Skip to content

release 1.1.0: ship the v2 endpoint fix to installed users, unwrap context envelopes, make deletes settle - #10

Merged
tushar-hydradb merged 2 commits into
mainfrom
tushar/bump-plugin-1.1.0
Aug 28, 2026
Merged

tushar-hydradb merged 2 commits into
mainfrom
tushar/bump-plugin-1.1.0

Conversation

@tushar-hydradb

@tushar-hydradb tushar-hydradb commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Why this is urgent

Installed 1.0.0 snapshots call the removed v1 REST surface and 404 on every hook. Confirmed against prod:

endpoint status
POST /recall/recall_preferences 404
POST /memories/add_memory 404
POST /query (v2, used by 1.1.0) 200

The SDK wrapper on main already moves everything to /query + /context/* + /databases/*. But installs are keyed by manifest version (plugins/cache/hydradb/hydradb/<version>/), and .claude-plugin/plugin.json has said 1.0.0 since the initial commit — so claude plugin update reports "already latest" and users have to uninstall/reinstall by hand.

The version bump is the delivery mechanism for a fix that already exists. That is what this PR is for.

What ships as 1.1.0

Everything on main since d9185b9, plus three defects found while validating this release.

Minor, not patch — three new skills (/doctor, /ingest, /query). Not major — HYDRADB_TENANT_ID / HYDRADB_SUB_TENANT_ID still resolve with a one-shot deprecation warning.

Defects found by actually installing it

I installed the plugin from the marketplace into an isolated config dir and exercised it against live prod. Three things surfaced.

1. Injected context carried the envelope, not the prose.

Knowledge is ingested through appKnowledge, and the server indexes that envelope verbatim — so recall returned the whole JSON object as chunk content: internal ids, empty html_base64/csv_base64/markdown/files/layout slots, tenant_metadata duplicated as app_metadata, and the real text buried in content.text. Every prompt paid for that, and one chunk was truncated mid-JSON at the 7000-char budget — the envelope ate the text it was meant to deliver.

Unwrapped at the normalization seam in sanitize.mjs, so query, last-recall and the injected block are fixed together. Non-envelope content passes through untouched.

Before / after, same workspace:

Chunk 1                                    Chunk 1
Source: CLAUDE.md                          Source: CLAUDE.md
{"id":"claude-file:997e94…","tenant_id":   # Fixed Workspace
"claude-plugin","sub_tenant_id":"plugin-   The canary phrase for the post-fix
smoke-110","title":"CLAUDE.md","content":  run is: cobalt-meridian-7734.
{"text":"# Smoke Workspace\n\nThe canary…   Build command is `make verify`.

buildSection is renamed to buildContextString and exported, matching the name and shape of HydraDB's documented reference formatter (docs: essentials/v2/api-results) that the plugin was already approximating.

2. classifyDeletion treated "already gone" as retryable. (new in #9)

It checked the batch-level success rollup before the per-item results, so a success:false batch discarded the per-item reasons — which are exactly what separates a real failure from an id the server already removed (deleted:false, error:"Source not found"). Deletion is idempotent; the postcondition holds, so that is terminal success. Per-item detail now outranks the rollup, and a genuine failure alongside an already-absent id is still retained.

3. writeState could not express a removal. (pre-existing — state.mjs is untouched by #9)

files: { ...(current.files || {}), ...(state.files || {}) }

The merge re-reads disk so a concurrent single-file sync keeps its addition, but a spread only adds and overwrites — it can never drop a key. A file deleted from in-memory state was resurrected on every write. This is why fixing #2 alone reported deleted: 1 and changed nothing. Removals are now named explicitly via removedFilePaths, passed by the two reconciling sync call sites.

Together, #2 and #3 are why a deleted file stayed tracked forever and re-attempted its delete on every sync.

Verification

Not just tests — installed and driven against prod:

  • Install shape: claude plugin install reports 1.1.0, 14 skills, 4 hooks, pinned to the commit. Runtime imports only scripts/vendor/hydradb-sdk.mjs; nothing under scripts/ references @hydradb/sdk, so the plugin works whether or not the npm step succeeds.
  • Round trips: ingest → recall returns the canary; remember → search returns the codeword. All four hooks exit 0.
  • Delete, end to end: pass 1 deleted:1, state drops the file, errors:[]; pass 2 deleted:0, errors:[] — converged, no wedge. Previously this reported knowledge delete incomplete … retained for retry forever.
  • API-Version: 2 verified live, per CONTRACT.md §6.
  • Every fix is mutation-tested — reverting each one fails the suite. New coverage: the prod already-absent delete shape, mixed already-absent + real failure, envelope unwrapping, JSON-ish passthrough, and state removal.
  • npm run check and pack:dry-run green.

Also here: license drift

d9185b9 relicensed MIT → Apache 2.0 but only updated package.json and LICENSE. plugin.json and marketplace.json still advertised MIT — and those are what the marketplace and plugin UI show users. Both now read Apache-2.0.

Not addressed

repository in both manifests still points at usecortex/…, which 301s to hydra-db/…. Harmless for install; it does break gh pr create on fresh clones. Left out to keep this PR scoped.

…x license drift

The manifest has said 1.0.0 since the initial commit, so everything merged
since then ships to fresh marketplace installs under a version string that
never moved. Bump to 1.1.0 across all three version sites.

Minor, not patch: PR #9 added three user-facing skills (/doctor, /ingest,
/query) alongside the migration onto @hydradb/sdk. Not major: the legacy
HYDRADB_TENANT_ID / HYDRADB_SUB_TENANT_ID env names still resolve, with a
one-shot deprecation warning pointing at HYDRADB_DATABASE / HYDRADB_COLLECTION.

Also corrects a license drift that predates this bump. d9185b9 switched the
project from MIT to Apache 2.0 but only updated package.json and LICENSE;
plugin.json and marketplace.json still advertised MIT. Those two manifests are
what the marketplace and the plugin UI actually show users, so the wrong
license was the one on display. Both now read Apache-2.0, matching LICENSE.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SR9ddwF4t2s5ubMs7GZEzw
Signed-off-by: tushar-hydradb <tushar@hydradb.com>
@greptile-apps

greptile-apps Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

Greptile Summary

The PR releases version 1.1.0 and aligns the marketplace manifests with the Apache-2.0 license, while refining retrieval-envelope handling and deletion reconciliation.

  • Updates package and plugin manifest versions to 1.1.0.
  • Corrects the displayed license in both plugin manifests.
  • Unwraps application-knowledge envelopes before presenting recalled content.
  • Treats already-absent deletion results as terminal and explicitly removes reconciled paths from merged local state.
  • Extends conformance and runtime checks for retrieval and deletion behavior.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
scripts/lib/hydra/index.mjs Refines per-ID deletion classification so already-absent sources are considered reconciled before evaluating batch-level failure.
scripts/lib/state.mjs Adds explicit file-path removals to the state merge so confirmed deletions are not resurrected from disk.
scripts/lib/workspace-sync.mjs Includes absolute file paths in confirmed-deletion summaries for durable state cleanup.
scripts/plugin.mjs Propagates confirmed workspace removals into state writes for automatic and manual full-sync paths.
scripts/lib/sanitize.mjs Adds parsing of structured application-knowledge envelopes to recover their prose content.
scripts/lib/hydra-client.mjs Applies envelope unwrapping while normalizing recalled chunks and additional context.
scripts/lib/context-format.mjs Applies normalized envelope content when formatting bounded Claude-facing retrieval sections.
conformance/tests.mjs Adds coverage for already-absent and mixed per-ID deletion responses.
scripts/check.mjs Adds checks for envelope normalization and explicit state-file removal semantics.
.claude-plugin/plugin.json Updates the plugin version and displayed license.
.claude-plugin/marketplace.json Updates marketplace release metadata and displayed license.
package.json Bumps the package version to 1.1.0.
package-lock.json Synchronizes lockfile package metadata with version 1.1.0.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Workspace sync] --> B[HydraDB delete]
  B --> C{Per-ID result}
  C -->|Deleted or already absent| D[Confirmed deletion]
  C -->|Other failure| E[Retain for retry]
  D --> F[Remove local tracking path]
  E --> G[Keep local tracking path]
  H[HydraDB recall] --> I[Normalize response]
  I --> J[Unwrap appKnowledge envelope]
  J --> K[Bounded Claude context]
Loading

Reviews (2): Last reviewed commit: "fix: unwrap knowledge envelopes into con..." | Re-trigger Greptile

@openhack-agent

Copy link
Copy Markdown

⏚ Security Scan Complete

✅ No vulnerabilities detected!

This pull request has been scanned and no security issues were found.

View results on OpenHack


…y settle

Found by installing 1.1.0 from the marketplace and exercising it against prod.

Context injection carried the envelope, not the prose. Knowledge is ingested
through `appKnowledge`, and the server indexes that envelope verbatim, so recall
returns the whole JSON object as the chunk's content: internal ids, empty
html_base64/csv_base64/markdown/files/layout slots, and tenant_metadata
duplicated as app_metadata, with the actual text buried in `content.text`. Every
prompt paid for that scaffolding, and one chunk was truncated mid-JSON at the
7000-char budget — the envelope ate the text it was supposed to deliver.
Unwrapped at the normalization seam in sanitize.mjs, so query, last-recall and
the injected block are all fixed at once; anything that is not one of our
envelopes passes through untouched.

`buildSection` is renamed to `buildContextString` and exported, matching the
name and shape of HydraDB's documented reference formatter, which is what the
plugin was already approximating.

Deleting a synced file never settled, for two independent reasons.

classifyDeletion checked the batch-level `success` rollup before the per-item
results, so a batch reported success:false discarded the per-item reasons. Those
reasons are what distinguish a real failure from an id the server has already
removed, which comes back as deleted:false / "Source not found". Deletion is
idempotent: the caller's postcondition holds, so that is a terminal success, not
something to retry. Per-item detail now outranks the rollup, and an
already-absent id is confirmed — a genuine failure alongside it is still retained.

That alone changed nothing user-visible, because writeState could not express a
removal. It merges `files` with what is on disk so a concurrent single-file sync
keeps its addition, but a spread only adds and overwrites — it can never drop a
key. A file deleted from in-memory state was resurrected from disk on every
write. Removals are now named explicitly via `removedFilePaths`, which the two
reconciling sync call sites pass from the summary. state.mjs is untouched by
PR #9, so this predates 1.0.0.

Verified end to end against prod, not just in tests: injected context is now
clean prose with graph relations intact, and a deleted file drops from state on
the first full pass and stays gone on the second. Each fix is mutation-tested —
reverting it fails the suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SR9ddwF4t2s5ubMs7GZEzw
Signed-off-by: tushar-hydradb <tushar@hydradb.com>
@tushar-hydradb tushar-hydradb changed the title chore: release 1.1.0 + fix license drift in the plugin manifests release 1.1.0: ship the v2 endpoint fix to installed users, unwrap context envelopes, make deletes settle Aug 27, 2026
@openhack-agent

Copy link
Copy Markdown

🔍 Security Scan Results

⚠️ 1 potential vulnerability detected

View full results on OpenHack

  • 🟡 Medium: 1

Findings

🟡 Symlink-following arbitrary file read and exfiltration in workspace ingestion (candidateSummary)

Severity: MEDIUM

The PostToolUse hook (hooks.json matcher Write|Edit|MultiEdit|NotebookEdit) invokes plugin.mjs post-tool-use, which calls extractPathsFromToolInput(input.tool_input, input.cwd) with LLM/attacker-influenced paths, then syncWorkspace -> candidateSummary (scripts/lib/workspace-sync.mjs). candidateSummary validates that the path is inside projectRoot via path.relative() and rejects ../ prefixes, but uses fs.stat and fs.readFile, which follow symlinks. A symlink placed inside the project (or present in a cloned repo) pointing to an arbitrary file outside the project root (e.g. ~/.ssh/id_rsa, ~/.aws/credentials, /etc/passwd) passes the lexical ../ check because the symlink's own path is inside the project, and its target content is read and uploaded to the third-party HydraDB service. This is inconsistent with the full-workspace walk() path, which uses Dirent.isFile()/isDirectory() and correctly skips symlinks. The likelySensitive() blocklist is also incomplete (no .ssh/, id_rsa, .npmrc, .gitconfig, aws/credentials), so sensitive files reached via symlink are not filtered. Combined with prompt injection in repo content steering the LLM to read a path, this enables exfiltration of arbitrary local files.

Location: scripts/lib/workspace-sync.mjs:309

View details on OpenHack →

@tushar-hydradb
tushar-hydradb merged commit 5d77faf into main Aug 28, 2026
4 checks passed
@tushar-hydradb
tushar-hydradb deleted the tushar/bump-plugin-1.1.0 branch August 28, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants