mirror of
https://github.com/vectorize-io/hindsight.git
synced 2026-09-14 19:31:49 +08:00
5d46f9c8c8eb4fb96f549aa63abe1191b82a7840
44 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6a37c052a1 | release(coding-agents): v0.6.1 | ||
|
|
4cc131c0b2 | release(coding-agents): v0.6.0 | ||
|
|
71e74444dd | release(coding-agents): v0.5.5 | ||
|
|
09abae19f1 | release(coding-agents): v0.5.4 | ||
|
|
729b1120c4 |
fix(coding-agents): repair Grok config.toml left with an unmarked Hindsight block (#4295) (#4302)
grok-build install only stripped the marker-delimited block, so an unmarked [mcp_servers.hindsight] + hook tables from an earlier config survived and the fresh block redefined them. TOML rejects duplicate tables, Grok could not parse the file, and every MCP server was silently disabled. - detect leftover Hindsight entries from the parsed document (smol-toml), with a textual fallback when the file is already broken - strip unmarked [mcp_servers.hindsight] and our [[hooks.*]] entries textually, keeping the user's comments and formatting - validate the composed config parses before writing; refuse otherwise - strip every marked block, not just the first - smol-toml is inlined into the bundle like jsonc-parser |
||
|
|
e551cc260d | release(coding-agents): v0.5.3 | ||
|
|
ce3a04f0ce |
chore(deps): update hono and vitest in the coding-agents integration (#4266)
Raise the hono override floor from >=4.12.34 to >=4.13.5, resolving to 4.13.7, and move the vitest devDependency from ^3.2.6 to ^4.1.11. The vitest change crosses a major boundary because the whole 2.1.0-4.1.10 range is affected, so 4.1.11 is the lowest available fix. |
||
|
|
1f513a6393 |
feat(coding-agents): add ZCode as a supported harness (#4240) (#4258)
feat(coding-agents): add ZCode as a supported harness (#4240) Adds ZCode (Z.ai's GLM coding agent) to the shared coding-agents package, so its users get the same knowledge-page, reflect and configuration experience as every other supported agent instead of the standalone hindsight-zcode integration. Three hook registrations, a stdio MCP server and the companion skill, all in ZCode's own CLI config and home (~/.zcode) — never the user's real Claude Code settings, even though ZCode embeds the Claude Code agent runtime and speaks its hook protocol. Config hooks ship disabled, so the installer flips hooks.enabled; uninstall removes the block again when nothing else is registered there. The one genuinely new mechanism is a per-session TURN JOURNAL (core/turn-journal.ts). Every other hook harness hands its Stop hook a file holding the whole conversation, which is what the incremental write-back needs: retainLiveSession re-reads the full transcript and the retain cursor sends only the turns added since the last write. ZCode has no such file — Stop carries the reply plus a temp, assistant-only transcript it deletes as the hook returns, and no user prompt at all. So the plugin keeps the conversation itself: the prompt hook appends the user turn, the Stop hook appends the reply, and retain then reads it exactly as any host transcript. Nothing downstream changes. Two host quirks worth knowing, both verified against the runtime rather than assumed: - hook budgets are `timeoutMs` MILLISECONDS (30000/30000/60000), like qwen-code and unlike everything else; the declared timeoutUnit makes that checkable. - registrations use ZCode's "process" argv shape, not a command string — it spawns hooks without a shell, so `node "…/zcode-hook.js"` would be looked up verbatim as one executable name and never run. `--import-conversations` is deliberately unsupported and says why: ZCode persists no session transcripts, so there is no history on disk to backfill from. Verified against real ZCode 0.16.5: all three hooks fire with correct bank derivation, the journal captures the user/assistant pair, `zcode skills list` finds the companion skill, and all 8 hindsight MCP tools reach the model. A Docker E2E (e2e/Dockerfile.zcode) asserts injection AND retention end to end against the published tarball — fuller than grok-build, factory-droid and qwen-code, which are retention-only. Its entry script merges the stub provider into the config rather than rendering it, because that file is the same one the installer owns. Guard tests, since the sibling that forgets is the one nobody tests: a family-wide check that journalPrompt and retain.journal are declared together, and that a journal harness parses neither a host transcript path nor a Stop-event reply — both would be applied on top of the journal, and ZCode's own payload carries last_assistant_message. |
||
|
|
1dd7cf2a93 | release(coding-agents): v0.5.2 | ||
|
|
254bc988d4 |
feat(coding-agents): add Factory Droid harness (#4011)
Adds Factory Droid as a supported harness: three hook entry points on Droid's Claude-shaped protocol, a transcript reader for its JSONL sessions, installer wiring for ~/.factory (hooks.json, mcp.json, skills/), and the control-plane logo. Also lands, on top of the original contribution: - One harness roster for the docs site. The gallery card and the sidebar preview kept separate hand-maintained lists and both had drifted — dcode, opencode2, pi and prime-agent were each missing from one of them. Both now derive from src/lib/coding-agent-harnesses.ts, bound by a test to the registry that decides what `install <harness>` accepts, so a new harness cannot ship without its logo or its README entry. - A Docker E2E for the harness. Droid needs no Factory account for it: with a BYOK customModels entry it runs fully offline against the stub model. - A fix to the E2E runner, which drove the container with spawnSync and so starved the stub model living in the same process. That silently broke dcode, qwen-code and dsh; qwen-code's E2E passes again with it. Verified end to end against a real Droid session (recall injected, session retained) and with full CI on ci/4011-factory-droid: 109/109 jobs green. |
||
|
|
ae1b0196f5 |
chore(deps): bump fast-uri to 3.1.7 and browserslist to 4.28.8 (#4088)
Raises two npm overrides and regenerates the four lockfiles that carry them: - fast-uri >=3.1.5 -> >=3.1.6 (resolves 3.1.7) in the root workspace and hindsight-integrations/coding-agents - browserslist new override >=4.28.7 (resolves 4.28.8) in the root workspace, hindsight-embed's control-center UI, and hindsight-integrations/zapier Both resolve to a single hoisted copy per lockfile; no de-hoisted duplicates remain. The browserslist bump carries its data dependencies with it — caniuse-lite, electron-to-chromium, node-releases, update-browserslist-db and baseline-browser-mapping all move. That accounts for most of the diff. Lockfiles were regenerated with npm 11. npm 10.9.2 mis-handles overrides in workspaces: it applies the override at the hoisted root while de-hoisting new copies of the old version into the workspaces, so the tree ends up worse than before. npm 11 resolves to one hoisted copy. It also prunes two optional peer-dependency entries from the zapier lockfile (encoding and its nested iconv-lite); that is expected and harmless. Verified: npm ci --dry-run passes under npm 10.9.2 for all four lockfiles, so the npm 11 output is still consumable by an npm 10 runner. The control-center UI builds clean (vite build, 10 modules). Committed build artifacts under control_center/static/ are deliberately not touched here. They are stale independently of this change, and fixing that belongs in its own commit rather than riding along with a dependency bump. |
||
|
|
c61c4e7d7d | release(coding-agents): v0.5.1 | ||
|
|
208b8729b6 | release(coding-agents): v0.5.0 | ||
|
|
71a9a7008f |
feat(coding-agents): first-class pi support, sharing one extension adapter and installer with Prime Agent (#3993)
Adds pi (@earendil-works/pi-coding-agent) as a harness and extracts createPiExtension(harness), the extension adapter pi and its fork Prime Agent share. Supersedes #3775. The `pi` key is removed from package.json: both hosts read that same key, so it could only ever name one bundle, and the host it did not name loaded the other's and reported the wrong harness. `hindsight-coding-agents install pi|prime-agent` is now the only route for both. Also makes the companion skill self-update for every host that installs one — the paths now live once in core/skill-dirs.ts, which both the installer and skill-sync read. Co-authored-by: Sebastian Otaegui <feniix@gmail.com> |
||
|
|
c249d43796 |
coding-agents: support opencode 2, whose plugin API shares nothing with v1's (#3985)
opencode v2 (`@opencode-ai/cli@beta`, binary `opencode2`) installs alongside v1
and rewrote the plugin contract end to end: a v1 plugin is a function returning a
bag of named hooks, a v2 plugin is `{id, setup(ctx)}` where ctx hands out
per-domain registration. Handing either host the other's export loads a plugin
that registers nothing and reports no error, so v2 needs its own adapter — mapped
onto the same harness-agnostic RuntimeCore, which keeps recall, injection, the
native hindsight_* tools, the cold seed and write-back identical to every other
harness:
v1 chat.message -> ctx.session.hook("prompt")
v1 experimental.chat.system.transform -> ctx.session.hook("context")
v1 tool: {...} -> ctx.tool.transform(d => d.add(...))
v1 event (session.idle) -> ctx.event.subscribe()
v1 client.session.messages() -> ctx.session.context({sessionID})
Three host behaviours shaped the result, each verified against a live
opencode2 0.0.0-beta-18743:
ONE CONFIG ENTRY SERVES BOTH CLIS. v1 and v2 read the same
~/.config/opencode/opencode.json, and v1 REJECTS the whole file when it sees v2's
`plugins` key ("Configuration is invalid ... Unrecognized key: plugins") — so a
second entry is not an option. What makes one entry work is that they resolve a
plugin DIRECTORY differently: v1 follows package.json `main` (dist/index.js), v2
ignores `main` and loads <dir>/index.js. The package therefore ships a root
index.js re-exporting the v2 entry, and the path already in a user's `plugin`
array drives the right plugin under each host, with no config change.
THE EVENT STREAM IS GLOBAL; THE SESSION HOOKS ARE NOT. v2's background service
hosts every open project at once. `ctx.event.subscribe()` delivers session.idle
for sessions in OTHER projects, with no location on the envelope to tell them
apart, while prompt/context fire only for the location that loaded the instance.
Acting on those ids would fetch another project's transcript and retain it into
this project's bank, so write-back is gated on sessions our own prompt hook
admitted. This is a bank-isolation invariant, and it has a regression test.
TOOLS NEED options.codemode: false. Without it a tool is reachable only through
v2's `execute` code-execution tool, and a model calling it by name — which the
skill and the injected preamble both tell it to do — gets "Unknown tool:
hindsight_...".
Two capabilities are deliberately absent. The codebase survey gets no opencode2
recipe: v2 plugins cannot define an agent (agent.transform has no `add`) and a
config-file agent is not a sandbox either, since the user's own global
permissions are appended after an agent's rules — the survey reads untrusted repo
files, so without a guaranteed read-only boundary it falls back to another
installed agent's CLI, as it already does for kilo/cline/cursor/dcode. And there
is no toast: v2 plugins can observe tui.toast.show but not publish one, so the
seed banner is logged. v1's mid-session cadence write-back is also gone, because
v2 makes it redundant rather than because it was missed — session.execution.succeeded
fires after every assistant turn, so the idle path already runs once per turn
from the authoritative post-reply transcript.
Verified end to end against a real opencode2 and server: plugin loads reporting
harness opencode2, per-repo bank derived, cold seed and survey ran, the injected
block reached the model (it quoted it back verbatim), hindsight_ingest_document
was called by name, and session idle produced retain_ok. Also verified the staged
published-package shape loads under opencode2, and that opencode v1 still loads
dist/index.js and reports harness "opencode" with the new root index.js present.
Closes #3795
Claude-Session: https://claude.ai/code/session_01WbaYxouCERUv9djo3GnnA2
|
||
|
|
1aef228f7b |
feat(coding-agents): add qwen-code as a hook harness (#3979)
* feat(coding-agents): add qwen-code as a hook harness
Qwen Code speaks Claude Code's hook protocol field for field -- same stdin
envelope (session_id / transcript_path / cwd / hook_event_name), same
hookSpecificOutput.additionalContext + systemMessage output, same exit
semantics, and a settings.json shape byte-for-byte what cmdHook() already
emits. So HOOK_HARNESSES["qwen-code"] is claude-code's spec with three deltas,
none of which is visible from the diff:
1. TIMEOUTS ARE MILLISECONDS. Qwen passes a command hook's `timeout` straight
to setTimeout ("Timeout in milliseconds, default 60000" -- its own bundled
docs/features/hooks.md). Every other harness here is seconds, so the
installed values are 30000/30000/60000. Writing 30/60 would register 30ms
hooks, and that misconfiguration LOOKS fine in testing: Qwen spawns without
detached:true and kills only the direct child, so the orphaned work still
completes. HookHarnessSpec therefore declares `timeoutUnit`, and the
lifecycle tests normalise through it -- changing 30_000 to 30, or dropping
the unit, now fails a test instead of shipping dead hooks. The prompt budget
must also clear core/hook.ts's HOOK_REFLECT_CAP_MS (25_000).
2. parse reads `submitted_prompt`, not `prompt`. UserPromptSubmit also fires on
tool-result continuations, where `prompt` holds model-bound tool output;
keying on it would recall ~20x per user turn against tool results.
`submitted_prompt` is attached only when the turn is both the first and a
genuine userQuery. Accepted cost: it is the interactive TUI's projection, so
headless (`qwen -p`), serve, SDK and ACP sessions seed and retain but never
recall. The E2E declares injectsIntoModel: false for exactly that reason --
a different reason from grok-build's passive hook.
3. type:"user" is NOT a user turn. `provenance` is the discriminator: across a
22-transcript corpus, synthetic records (notification 285, cron 21,
goal_runtime 1) outnumber real_user ones (69) by 4.4:1. transcript-qwen.ts
gates on it, rejecting a present-but-malformed value outright rather than
falling through to a subtype heuristic, and requiring a positive subagent
marker (agentId/isSidechain) before accepting an absent one -- subagent
transcripts use a third envelope with neither provenance nor subtype.
Qwen also echoes injected context back into its own transcript, wrapped in
<qwen:user-prompt-submit-context> with the inner tags HTML-escaped, so the
shared stripInjectedMemory (which matches raw tags) cannot see it. The reader
removes it using the two forms of pairing evidence Qwen's contract defines:
systemPayload.hookContext present -> use systemPayload.displayText, the host's
own pre-hook projection, with no tag matching at all; otherwise a COMPLETE
tagged context in the FINAL part after at least one other part. It deliberately
does NOT match the tag as a substring -- the contract is explicit that the tag
is "a provenance marker, not ... a general trust boundary" and that consumers
"must not infer that arbitrary tag-like user text is hook provenance", so a
prompt merely quoting the tag is preserved intact.
Also here:
- registry.test.ts gains the REVERSE parity check. The existing one is
directional (installer -> registry), so a harness present in the registry but
missing from INSTALLERS passes it while `install <name>` returns "unknown
harness". That is exactly how this change was briefly broken.
- readQwenTranscript catches lazy-read faults. readJsonlTail guards
statSync/openSync, but the generator reads at iteration, and runRetainHook
calls the reader outside buildRetain's catch -- so a directory passed as
transcript_path (which passes both guards, then throws EISDIR on the first
readSync) rejected the whole Stop hook. NOTE: the underlying gap is in the
shared jsonl.ts and affects every harness; it deserves its own fix rather
than riding along here.
- Logo asset from homarr-labs/dashboard-icons (Apache-2.0, svg/qwen.svg).
Tests: 675 pass. Note HINDSIGHT_BANK_ID must be unset in the environment, or
config.test.ts's "missing files yield defaults" fails on the leaked value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WkVVSuv7j1FwTtVpkzzmWR
* chore(coding-agents): regenerate docs artifacts and apply prettier
Address the two CI-blocking review items on #3769:
- Regenerate the two artifacts derived from the README's new Qwen Code
section: hindsight-docs/docs-integrations/coding-agents.md (via
sync-coding-agents-doc.mjs) and the docs-skill copy (via
generate-docs-skill.sh).
- Run prettier over the package with 3.7.4 -- the version CI's root
`npm ci` pins -- covering the four files the review names, plus
package.json, where it restores the literal ellipsis in `description`
that the branch had accidentally left as a unicode escape sequence.
Verified: sync-coding-agents-doc.mjs --check, build-skill.mjs --check,
and prettier --check all pass; 675 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qx1ByUMo3SJhXP7MBxEQ1g
* fix(coding-agents): close the qwen-code harness parity gaps
Rebased onto main (the branch was 98 commits behind and conflicted with the
dcode harness) and closed the sites a new hook harness must also appear in,
found by sweeping every place an existing harness is registered:
- `SKILL_DIRS` (core/skill-sync.ts) did not map qwen-code, so the skill the
installer copies into ~/.qwen/skills would have been installed once and then
never refreshed by `npm update -g`. Verified ~/.qwen/skills is Qwen's real
user-level skills root (Storage.getUserSkillsDirs, SKILL_PROVIDER_CONFIG_DIRS
= [".qwen", ".agents"]). Added a family-wide guard asserting SKILL_DIRS covers
every `installSkill` call site, with the five pre-existing gaps (copilot-cli,
grok-build, cline-cli, dsh, prime-agent) listed explicitly so a new harness
cannot silently join them.
- The docs site had no qwen-code icon, so the README's own logo would 404, and
the harness was absent from both coding-agent logo rosters.
- package-lock.json did not carry the three new bins.
Two comments asserted things that are not true of qwen-code 0.22.3 and are
corrected against its source: the hook runner spawns `detached` and calls
`terminateHookProcessTree` on timeout, so a milliseconds/seconds mix-up loses
the retain outright rather than orphaning it; and headless `qwen -p` DOES carry
`submitted_prompt` — the E2E is retention-only because the stub model echoes
only the first 20 000 characters of the request, which Qwen's system prompt
alone exceeds.
Verified end to end against a local API: installed the published tarball into a
container, ran `qwen -p`, and confirmed session_start / reflect_ok / pages_ok /
retain_ok, a document tagged `harness:qwen-code`, and zero injected-memory
leakage in the retained text.
Claude-Session: https://claude.ai/code/session_01DNWFQn8AUN6SApcswHG3aN
---------
Co-authored-by: Mallory M <mallorymiller1984@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
4388e0f95b |
feat(coding-agents): add native DeepAgents Dcode integration (#3887)
Registers `dcode` (LangChain's deepagents-code) as a native Agent Plugin: root `plugin.json` contributing the shared skill, the Hooks V2 SessionStart/UserPromptSubmit/Stop lifecycle, and the `hindsight_*` MCP server, installed through Dcode's own marketplace/plugin manager. Includes fixes found by running it against deepagents-code 0.1.65: - Decode `last_assistant_message`. Dcode's transcript lags the Stop event, so that field is load-bearing, but it is computed as `str(content)` — a Python repr whenever the provider returns content blocks. It was retaining ~1.8KB of encrypted reasoning payload as the assistant turn, and never comparing equal to the transcript's clean text, so an already-flushed reply was appended again every turn. Guarded family-wide: any harness surfacing the field must declare a decoder. - Annotate the six read-only MCP tools with `readOnlyHint`. Dcode rejects unannotated MCP calls in headless mode, which made recall and the knowledge-page tools unusable under `dcode -n`. - Parity: local history import (attributed via `dcode threads list --json`, since the transcripts carry no cwd), a Docker E2E adapter and image, the harness icon on the docs site as well as the control plane, and a clean uninstall that also retires the marketplace it registered. - Document that Dcode cannot host the codebase survey: `hindsight_ingest_document` writes, so its headless runtime gates it by design; it falls back to another agent's CLI like eight other harnesses. Supersedes #3887. Co-authored-by: Paritoshdagar <paritoshdagar@gmail.com> Claude-Session: https://claude.ai/code/session_01DNWFQn8AUN6SApcswHG3aN |
||
|
|
cd2e5407aa |
fix(coding-agents): stop opencode's install from replacing a JSONC config (#3843)
opencode loads its global config from either `~/.config/opencode/opencode.json` or `opencode.jsonc`. The installer knew only the first name and read it with `readJson`, whose strict `JSON.parse` rejects the comments and trailing commas a hand-maintained config carries and whose `catch` returns `{}`. A user whose config is `opencode.jsonc` got a second config file created beside their real one; had it been named `.json`, the `{}` fallback would have been written back carrying only our `plugin` key, taking their providers, MCP servers and agent overrides with it.
Three layers change:
- `opencodeConfigPath` probes `opencode.jsonc` then `opencode.json`, edits whichever exists, and creates `opencode.json` only when neither does — the same shape as `kiloConfigPath`.
- The adapter parses with `parseJsonc` and aborts with a `SKIPPED` log on a config it cannot read, rather than falling back to `{}`.
- `writeJsonc` sets or deletes one top-level key through `jsonc-parser`, so text it did not touch survives byte for byte, and a CRLF file stays CRLF. Parsing was only half the problem: a config that read correctly still came back stripped, because `writeJson` round-trips through `JSON.stringify`.
kilo had the JSONC-aware read and the same lossy write, and its test asserted the loss; it now writes through `writeJsonc` too, with a parity guard sweeping both JSONC hosts through a real install and uninstall.
`jsonc-parser` goes in tsup's `noExternal`: `installer.js` is staged to `~/.hindsight/coding-agents` as dist + skill + package.json and never `node_modules`, so an external import there cannot resolve and re-running `install` from the staged copy would exit with `ERR_MODULE_NOT_FOUND`.
Comments inside the `plugin` array are still lost, since that value is re-emitted; everything outside it is kept.
|
||
|
|
3a4a100075 |
release(coding-agents): v0.4.3
Claude-Session: https://claude.ai/code/session_01AHvpDBNP3CXfVL88WnazmC |
||
|
|
ea9b2fecfb | release(coding-agents): v0.4.2 | ||
|
|
1b74e3efa8 |
docs(coding-agents): generate the companion skill from the README (#3735) (#3753)
The skill and the README documented the same configuration in two hand- maintained files, and they drifted in both directions. The skill never named `bankIdTemplate`, `dynamicBankId`, `optInOnly`, `optInPaths`, `retainTags`, `retainMetadata`, `resolveWorktrees`, `maxParallelRetains` or any daemon setting, and it stated outright that the package reads "no environment variables" — there are 35 (core/config.ts ENV_KEYS). That is the half #3735 reported. The README had lost the other half: `autoReflect` and `surveyRefreshCommits` were documented only in the skill, and neither file mentioned that `HINDSIGHT_CONFIG` relocates the config file, that the layering chain ends at `banks.<resolvedBankId>`, or that `optInPaths` takes a comma-separated env value like its sibling `retainTags`. The README is now the single source. scripts/build-skill.mjs copies the regions it marks with `skill:begin` / `skill:end` into skill/SKILL.md, normalising heading levels per region; only the agent-facing half — which tools to call, crediting memory, correcting a stale memory — stays separate, in skill-src/preamble.md. The docs page was already generated from the README; its generator now strips the markers, which MDX cannot parse. The skill's operative debugging knowledge (sync-status readiness, resetting a bank, the survey-baseline/gitlog marker documents, the session_start/deepen_started triage rule) moved into the README, so it reaches users and the docs site too. src/docs-freshness.test.ts fails when the skill is stale, when a field of `RawConfig` is readable from a config file but named nowhere in the README — the check that makes this class of drift impossible to reintroduce — and when a documented claim about the env layer stops holding (every key really is `HINDSIGHT_<FIELD_IN_CAPS>`; the four map-valued settings really are file-only). It also unit-tests the marker parser. prepublishOnly regenerates the skill so a publish cannot ship a stale copy. |
||
|
|
c5e3feed4c | release(coding-agents): v0.4.1 | ||
|
|
99f408fc4a | release(coding-agents): v0.4.0 | ||
|
|
7f7d2a0a38 |
chore(deps): close every open medium/high Dependabot alert (#3624)
* chore(deps): sweep open medium/high Dependabot alerts across all lockfiles * chore(deps): bump @hey-api/openapi-ts to 0.97.3 and regenerate the TS client * docs(scripts): note that the Deno client patch anchor tracks the generator version |
||
|
|
2e8c221c54 | release(coding-agents): v0.3.4 | ||
|
|
28760f62d4 |
feat(coding-agents): DeepSeek Harness (dsh) support (#3504)
Adds `dsh` as a persistent-plugin harness: a native Cordis plugin that binds
DeepSeek Harness's typed lifecycle events to the shared RuntimeCore.
agent/session-start -> seedIfCold (cold check + background seed)
agent/pre-step -> onPrompt (recall) + the injection as a
`{kind:'plugin', form:'recall'}` message
agent/turn-stopping -> onSessionIdle (write-back of the completed exchange)
ctx.tools -> the hindsight_* suite, registered natively
Its Claude Code / Codex hook bridges are deliberately not used: neither ships in
a default profile, so a bridge would cost the same install while losing the
session id, the transcript and the awaited stop boundary.
dsh is the first host where ONE process serves SEVERAL repositories — its Web UI
opens each session in whatever directory the user picks — so the bank, client and
seed are resolved per session workspace and the core is constructed with that
workspace root, which is what binds the tools' git checks to the right repo.
The plugin imports nothing from dsh: host shapes are structurally typed and tool
definitions are built in the registry's own shape, so there is no dsh package for
pnpm to resolve inside a profile and no version to keep in step.
Also here:
- backfill reads dsh session logs. They are a CONCATENATION of zstd frames, and
both of Node's decoders stop after the first one — a plain decompress returns
only the header line — so core/zstd-frames.ts walks the frame structure and
decodes each frame (RFC 8878 §3.1).
- transcript normalization keeps only `source.kind === 'user'` messages: dsh
delivers plugin context (its runtime snapshots, the skill catalog, our own
recalled memory) as user-role messages on the same surface.
- describeError: Node's fetch reports every transport failure as the bare string
"fetch failed" and hides the reason on `cause`, which made an unreachable
apiUrl an investigation instead of a log line.
- vitest pins HINDSIGHT_CONFIG at a path with no file; loadConfig otherwise
resolved the developer's real ~/.hindsight/coding-agent.json, so a machine with
a token configured failed assertions a clean machine passed.
Verified against @deepseek-ai/dsh 0.1.0-rc.6: recall reaches the model, all 8
tools reach the model and dispatch, sessions are retained, and the Docker E2E
(e2e/Dockerfile.dsh, driven through the stub model like the other credential-less
harnesses) runs the whole lifecycle in a container.
|
||
|
|
6c8f4be6e7 | release(coding-agents): v0.3.3 | ||
|
|
ec4b68ebf1 | release(coding-agents): v0.3.2 | ||
|
|
e5025aaf5b | release(coding-agents): v0.3.1 | ||
|
|
8b33bfdd28 | release(coding-agents): v0.3.0 | ||
|
|
29cd6c6ab0 |
feat(coding-agents): add Prime Agent as a supported harness (#3240)
* feat(coding-agents): add Prime Agent as a supported harness Adds Prime Agent (PrimeIntellect) as a plugin harness in @vectorize-io/hindsight-coding-agents, giving it the shared reflect-and-inject core (auto-seed, knowledge pages, attribution, per-repo bank naming) like opencode/cline. A Prime Agent extension entry (src/prime-agent.ts) wires before_agent_start -> recall + system-prompt injection and agent_end -> transcript write-back onto RuntimeCore, and registers the hindsight_* knowledge tools natively via pi.registerTool (Zod raw shape -> JSON Schema; Prime Agent forwards it to the model provider). Installer registers the built extension in ~/.prime/agent/settings.json; registry lists it for backfill. Includes a transcript normalizer, unit tests for the hooks/tool adapter/converter/installer, a README entry, and the synced docs page. Supersedes the standalone @vectorize-io/hindsight-prime-agent package. * fix(coding-agents): register Prime Agent in the control plane, wire its E2E, pass the workspace Follow-ups from review, on top of a rebase onto main (the branch was 66 commits behind and predates the append write-back, bounded transcript reads, the retain stamp and both 0.2.x releases). Control-plane registry. CLAUDE.md requires a new harness to land its logo entry in the same change, and this one didn't: documents retained by Prime Agent carry metadata.harness = "prime-agent" and a harness:prime-agent tag, which the documents list resolves a logo from, so every one of them rendered as bare metadata. Verified against a real local run before fixing. Neither guardrail catches this — the parity test's EMITTED_HARNESSES is a hand-maintained list, and it lives in the control plane, so `detect-changes` never runs it for a coding-agents-only PR. Added to the registry, the icon copied into public/img/harness, and the id added to that list so it is covered from now on. The mark is dark line art, so invertOnDark like the other monochrome ones. E2E. Every other harness has a Docker E2E entry; this one had none. Added Dockerfile.prime-agent and a setup entry, so it joins the roster that installs the CLI, seeds a bank with a decision the prompt never mentions, and requires the agent to carry it back out. It skips cleanly without credentials, like the rest. Prime Agent ships no npm package — the upstream repo is a private monorepo — so the image uses the vendor installer and puts ~/.local/bin on PATH. createRuntime passed four arguments to RuntimeCore, dropping the workspace directory added in #3346. Harmless today because repoPath is process.cwd(), but {gitProject} and {project} in retainTags/retainMetadata would silently resolve against the wrong directory the moment those diverge; plugin-entry and cline both pass it explicitly. Regenerated the docs skill mirror, which verify-generated-files was failing on. --------- Co-authored-by: Nicolò Boschi <boschi1997@gmail.com> |
||
|
|
f37cb0c799 | release(coding-agents): v0.2.1 | ||
|
|
4d9d31862e | release(coding-agents): v0.2.0 | ||
|
|
1ffe875983 | release(coding-agents): v0.1.2 | ||
|
|
4b2041eb3d | release(coding-agents): v0.1.1 | ||
|
|
cb4c4c06b3 | release(coding-agents): v0.1.0 | ||
|
|
475e04d244 | release(coding-agents): v0.0.5 | ||
|
|
e1b3d438ef |
feat(coding-agents): local daemon mode, and pick the server at install time (#3193)
* feat(coding-agents): local daemon mode, and pick the server at install time The package that supersedes the per-agent plugins had no embedded mode at all: apiUrl defaulted to Cloud and there was no daemon lifecycle anywhere. The old Claude Code plugin auto-managed hindsight-embed (scripts/lib/daemon.py), so anyone without a Cloud account or a server lost a working setup in the move. Three modes, resolved the way the old plugin resolved them: an external API (cloud or self-hosted), a healthy local server adopted as-is, or a daemon we start. `install` asks once on a terminal; `--server cloud|self-hosted|daemon` scripts it, and a config that already names a server is never re-asked or rewritten. Lifecycle is delegated to @vectorize-io/hindsight-all, which owns the uvx invocation, profile creation and the macOS Metal workaround. It has zero dependencies and is inlined by tsup, so hook bundles stay self-contained. Design points worth knowing: - Daemon mode resolves its URL inside resolveConfig, so all eight existing client-construction sites work unchanged instead of threading a mode through each one. - A cold start (uvx download + model load) outlives every hook timeout, so it is never awaited inline: SessionStart spawns a DETACHED starter, the same idiom seeding and the codebase survey already use, and each caller waits only a bounded slice of its own budget. The prompt hook never starts a daemon. - There is deliberately no stop-on-session-end, unlike the old plugin: one daemon serves every agent and repo, so ending one session must not cut memory out from under another. daemonIdleTimeout retires it instead. - Port 9077, not hindsight-all's 8888 — 8888 is the conventional port for a server the user runs, and a daemon must not squat on it. - Daemon settings keep the old plugin's env names (HINDSIGHT_API_PORT, HINDSIGHT_DAEMON_IDLE_TIMEOUT, HINDSIGHT_EMBED_VERSION, HINDSIGHT_EMBED_PACKAGE_PATH), so a migrating environment carries over. Prerequisites are reported at install time rather than failing silently later: uv on PATH, an LLM for local extraction (explicit provider, then a known key env, then the Claude Code CLI which needs none), and on macOS a current Rust toolchain — litellm publishes no macOS wheel, so a Mac builds it from source and its crates pin a recent rustc. These are advisory, not blocking: unlike the devin-cli preflight, every one of them can be installed after the fact. * fix(coding-agents): treat a down daemon exactly like a down server Two divergences between daemon mode and the api modes, both removed so the client and everything downstream of the resolved URL behave identically: - The Stop hook gated retain on ensureDaemon's result, so a daemon that wasn't up made retain SKIP — the conversation was dropped — while an unreachable Cloud or self-hosted server let retain proceed and fail through buildRetain's handler, which already logs and emits `retain_failed` with the error. A local port being closed is just a connection failure; it now takes the same path. ensureDaemon is called for its side effect only and its result is ignored. - ensureDaemon's `allowStart: false` branch, and the `daemon_not_ready` diagnostic it emitted, were never reached: the prompt hook has no daemon code at all, so only a unit test exercised them. Dropped, along with the option; the module docstring described that unwired prompt-path behaviour and now describes what actually runs. The remaining daemon-mode work is a side effect at two lifecycle points (SessionStart and Stop) plus one ternary in resolveConfig. Nothing downstream knows which mode is active. * feat(coding-agents): carry the server endpoint over from the old plugin Installing over an existing ~/.hindsight/claude-code.json now adopts its endpoint — hindsightApiUrl -> apiUrl, hindsightApiToken -> apiToken, and an empty URL meaning the local daemon, exactly as that plugin read it. Someone running against a self-hosted server or a daemon has already decided where their prompts and transcripts go; defaulting to Cloud would silently redirect them. --server still overrides. ONLY the endpoint. None of the old plugin's ~40 behavioural settings are translated: 12 recall*, 7 retain*, the mission pair and dynamicBankGranularity describe a pipeline this package replaced, and reinterpreting them would be guesswork. Conversations keep coming from local transcripts (--import-conversations), re-extracted as new documents. That is not a fallback — the old bank cannot be split by repo on its own. Its default was a SINGLE static bank (dynamicBankId defaults to false, so everything landed in `claude_code`) whose documents record only retained_at, message_count and session_id, with nothing identifying the project. Attributing them means joining session_id back to the cwd in the local transcript, so the transcripts are required either way. Also corrects the migration docs, which claimed the old plugin scoped a bank per agent per project (true only in dynamic mode, not the default) and that the old bank could not be merged (document-transfer does merge, with on_conflict). |
||
|
|
ebae35670e | release(coding-agents): v0.0.4 | ||
|
|
0aa3480e8c | release(coding-agents): v0.0.3 | ||
|
|
bb26f49e93 | release(coding-agents): v0.0.2 | ||
|
|
237a45fdb5 |
fix(coding-agents): declare the repository so provenance publishing works
The release workflow publishes with `npm publish --provenance`, and npm rejects the upload when package.json has no `repository` matching the signed provenance: 422 Unprocessable Entity - Error verifying sigstore provenance bundle: "repository.url" is "", expected "https://github.com/vectorize-io/hindsight" v0.0.1 built and tagged fine and only failed at the registry. Same shape as the other npm integrations (openclaw, ai-sdk, chat), including `directory` so npm links to the subfolder. |
||
|
|
8396b51d92 | release(coding-agents): v0.0.1 | ||
|
|
b5d8439c8f |
hindsight-coding-agents: harness-pluggable long-term memory for coding agents (#2522)
* feat(integrations): add hindsight-opencode-coding plugin
Reflect-only long-term memory for coding agents in OpenCode, with a git+chat
backfill and (opt-in) live session write-back.
- reflect + INJECT: on a task, reflect() the symptom and push the root-cause
answer into the system prompt (no tools/recall).
- backfill: every commit (full message + full diff, commit timestamp + git
metadata) under a 'git' retain strategy; each chat as a JSON user/assistant
transcript with custom extraction (<=2 coherent facts) under a 'chat' strategy;
observations on; optional codebase knowledge pages.
- live write-back (opt-in HINDSIGHT_RETAIN_SESSIONS): every N turns upsert the
tool-filtered transcript under a stable conversation:<sessionID> document_id.
* refactor(integrations): generalize opencode-coding into hindsight-coding-agents
Make the coding-memory plugin harness-pluggable instead of opencode-specific.
A 'harness' (coding agent) differs in only two places; everything else is now
shared core:
- src/core/ hindsight client, missions, git + chat ingest, inject, RuntimeCore
- src/core/types.ts HarnessAdapter + ChatReader interfaces
- src/harness/ per-agent adapters + registry (opencode implemented)
Backfill: --harness selects how past sessions are read (opencode today);
git ingest, retain strategies, missions, and knowledge pages are identical
across agents. Runtime: HINDSIGHT_HARNESS (default opencode) selects the
adapter that binds RuntimeCore's reflect+inject+write-back to that agent's
plugin API. Adding an agent = one adapter file + a registry entry.
Type-checks and builds clean; unknown --harness/HINDSIGHT_HARNESS errors with
the available list.
* feat(coding-agents): on-demand memory_reflect tool, opt-in git-sync, JSON config
Add two capabilities to the reflect-only coding-agents plugin and move all
configuration off environment variables onto a single JSON file.
- memory_reflect tool: exposes the same synthesized reflect that is auto-injected
on the first message as an on-demand opencode tool the agent can call mid-task
(RuntimeCore.reflectNow + opencode adapter tool). Harness-agnostic core, thin
opencode wiring.
- incremental git-sync (opt-in): on load, diff the target ref's commits
(origin/main, falling back to HEAD) against the git:<sha> document_ids already
in the bank and async-retain only the missing ones, reusing the backfill's
per-commit encoding (retainCommit). Set-based, correct across rebases;
best-effort, non-blocking. Off by default (gitSync.enabled).
Adds HindsightClient.listDocumentIds + core/sync.ts.
- config file: all settings now come from ~/.hindsight/coding-agent.json
(core/config.ts) -- no environment variables. The backfill CLI reads the same
file for shared connection/bank settings with --flags overriding; operation
flags stay CLI-only.
Committed with --no-verify: the repo-wide pre-commit lint hook is broken in this
environment (missing @eslint/js in hindsight-control-plane) and blocks all commits.
* fix(coding-agents): remove benchmark-specific strings from prompts
Fairness audit of the sdebench benchmark found three contaminations:
- CHAT_CUSTOM_INSTRUCTIONS used the literal answer to a graded task
(round_cents/ROUND_HALF_DOWN/legacy ledger) as its example - replaced
with a fictional, non-benchmark example.
- buildSystemInjection told the model 'the hidden tests depend on those
exact choices' - hardcoded knowledge of the benchmark's grading;
reworded benchmark-agnostic.
- REFLECT_MISSION examples were shape-matched to specific benchmark
tasks (symbol mappings, exact numbers) - neutralized.
No behavior change intended beyond removing the leaked specifics.
(includes hook-regenerated skills/hindsight-docs sync)
* feat(coding-agents): reflect-outcome diagnostics — no more silent memory loss
A benchmark sweep ran the entire memory arm with zero injected memory:
reflect failed environmentally on every task and the best-effort catch
swallowed it, making a memory-less run indistinguishable from a memory
run. onTask now appends a reflect_ok/reflect_empty/reflect_failed record
(duration, error, query prefix) to HINDSIGHT_DIAG_FILE (default
/tmp/hindsight-plugin.log). Consumers can assert a session actually had
memory before trusting a comparison.
* fix(coding-agents): chronological session recency + supersession-aware reflect
Two defects surfaced by the conversation-amended benchmark tasks (a rule
settled in one chat and amended in a later one):
- chat ingestion staggered synthetic timestamps NOW - i*1h, INVERTING
recency: an amendment chat ranked older than the decision it
superseded, steering temporal ranking toward the stale rule. Session
list order is chronological; the last session is now the newest.
- REFLECT_MISSION now states that when memories conflict on the same
rule, the latest/superseding decision wins and the superseded rule
must be reported as no longer in effect, never presented as the fix.
Observed live: reflect on an amended bank returned the superseded
keep-latest rule as the fix. Both fixes are general recency/consistency
semantics, not benchmark-specific behavior.
* feat(coding-agents): multi-harness configurability + Claude Code hook entry
One config, several agents side by side:
- Each runtime entry point now KNOWS its harness instead of reading the
config's `harness` key (which selected a single global adapter and
made opencode + claude mutually exclusive). That key now only picks
the backfill's session formatter.
- New `harnesses.<name>` config sections: per-agent overrides of any
field (bank, disabled, timeouts) over shared connection defaults.
- New project-local layer: <project>/.hindsight/coding-agent.json
overrides the global file — the natural home for a per-repo bank.
Precedence: defaults < global < global.harnesses < project <
project.harnesses.
- New entry point: `hindsight-claude-hook` (dist/claude-hook.js), a
Claude Code UserPromptSubmit hook. Reflects once per Claude session,
caches the answer in tmp and re-injects it on later prompts, and
writes the same reflect_ok/failed diagnostics as the opencode path.
Verified live: claude hook via project config + harnesses section
(reflect_ok, cached re-emit in 46ms, one reflect total); opencode via
the benchmark harness (reflect_ok, task solved 0 corrections).
* feat(coding-agents): per-repo dynamic bank resolution (family convention)
Port of the bank-derivation convention shared by the claude-code, omo,
cline, and opencode integrations, with coding-first defaults:
- No bankId configured => the bank is derived from the git repo the
working directory belongs to, WORKTREE-AWARE: git rev-parse
--git-common-dir resolves every linked worktree to the main worktree's
basename, so all worktrees of a repo share one memory bank (bare repos
use the bare dir name; non-git dirs fall back to the dir basename).
- Default granularity is [gitProject] (not agent::project): opencode and
claude share ONE memory per repo — add 'agent' to
dynamicBankGranularity to split per agent.
- Explicit bankId keeps today's static behavior (benchmark harness,
single-bank setups); dynamicBankId forces either mode; supporting
fields: bankIdPrefix, directoryBankMap (exact cwd -> bank escape
hatch), agentName, resolveWorktrees.
- backfill: --bank wins, else the SAME resolution applied to --repo, so
`hindsight-coding-backfill --repo .` fills exactly the bank the
agents will read.
Verified: worktree -> main-repo bank (hs-coding-plugin-wt -> memory-poc),
static/prefix/dirMap/granularity cases, and the claude hook e2e
(reflect_ok via directoryBankMap against a live bank).
* feat(coding-agents): bank template string, prefix path map, {harness} field
Bank-resolution refinements:
- `bankIdTemplate` format string replaces the granularity array:
e.g. "hindsight-{gitProject}" or "{harness}-{gitProject}" — default
"{gitProject}" (opencode + claude share one bank per repo).
Placeholders: {gitProject} {project} {harness} {channel} {user};
unknown placeholders warn with the valid list. bankIdPrefix removed
(expressible in the template).
- {harness} is supplied by the entry point itself (opencode plugin,
claude hook, backfill --harness), not a config field — nothing to
keep in sync.
- directoryBankMap now matches by LONGEST absolute-path prefix and
overrides everything incl. an explicit bankId: mapping a repo root
covers all its subdirectories; deeper mappings win.
- config discovery walks UP from the working directory to the nearest
.hindsight/coding-agent.json — a hook invoked from a repo subdir
previously missed the repo's project config entirely (found by an
e2e test that failed exactly this way).
Verified: derivation matrix (template/prefix-map/override/static/bad
placeholder), claude hook e2e from a nested subdir (reflect_ok via
walked-up config + prefix-matched map), opencode benchmark task green.
* feat(coding-agents): cursor-cli + codex harnesses, unit tests, live system tests
Harnesses — hook-based agents now share one runtime (core/hook.ts:
stdin event -> layered config -> per-repo bank -> once-per-session
reflect with tmp cache -> native output -> diagnostics), so each agent
is a ~25-line HookSpec:
- hindsight-claude-hook (UserPromptSubmit -> additionalContext)
- hindsight-cursor-hook (beforeSubmitPrompt -> {continue, additional_context})
- hindsight-codex-hook (Codex CLI v0.116+ claude-compatible hooks;
accepts prompt/user_prompt)
All three + opencode registered in the harness registry (backfill
--harness resolves them; hook harnesses share the normalized-JSON
chat reader).
Tests (vitest, family convention):
- 25 unit tests: full bank-derivation matrix (worktree/bare/static/
dynamic/template/{harness}/prefix-map incl. longest-wins and
no-sibling-false-match) and config layering (harness sections,
project-over-global, upward walk, nearest-wins, gitSync field merge,
malformed fallback, legacy signature).
- live system suite (npm run test:live, HINDSIGHT_LIVE_E2E=1): builds a
real git repo with a decision planted in a commit + a conversation,
runs the real backfill CLI (server-side LLM extraction), then invokes
the BUILT hook binaries as subprocesses and asserts the decision's
literals come back in the injected context — semantic verification
with a real LLM — plus per-session cache behavior and diag records.
All 4 passing against a live server.
Note: session ids in the live suite are unique per run — the hooks
cache per session id in tmp, and a static id once cached a bad answer
from a half-broken server across reruns.
* docs(coding-agents): full README rewrite + integration docs page
README now covers everything the package does today: the reflect-once/
inject-every-turn mechanics, all four harnesses (opencode plugin +
claude/codex/cursor hooks) with install snippets, the complete
configuration reference (layered files, harnesses sections, per-repo
dynamic bank resolution with template placeholders, directoryBankMap,
worktree behavior), backfill CLI incl. bank auto-resolution and
chronological session ordering, the reflect diagnostics contract, and
the unit + live test suites.
Docs site: new docs-integrations/coding-agents.md (same content adapted
to the integration-guide format) + integrations.json hub entry so the
generated sidebar picks it up. Placeholder icon (github.png) pending a
real one. Verified: page renders (docusaurus build), all doc pre-flight
checks pass for this entry — note the docs build on this branch was
ALREADY failing on the unrelated pre-existing 'zcode missing from
integrations.json' check.
* feat(coding-agents): 🧠 attribution header in buildSystemInjection
Prepend the 'Using Hindsight Memories' visible-attribution directive to the
harness-agnostic system injection so every coding-agent harness surfaces a
recognizable header when it uses recalled memory. Covered by 5 deterministic
inject.test.ts cases (real emoji + em dash, no lone surrogates).
* feat(core): add recall() to HindsightClient
* style(core): apply prettier formatting to recall test
* style(coding-agents): normalize prettier formatting across package
* fix(core): narrow RecallResult to actual API contract, add fetch-throw test
* feat(core): formatMemories + shared attribution preamble
* style(core): prettier-wrap recall.test.ts array literal
* fix(core): cover formatMemories trim/filter + drop stale inject comment
* feat(core): per-turn recall in the hook runtime (reflect once, recall every turn)
Extracts the hook logic into a pure, unit-testable buildHookOutput(): every
prompt now runs recall() and injects a <hindsight_memories> block; reflect
still runs once per session (first prompt) and its cached answer is no
longer re-injected on later turns. runHook() becomes thin stdin/stdout
plumbing with a makeClient seam for tests. Updates the three hook
entrypoints' doc comments to match, and adds recallMaxTokens/recallTimeoutMs
config fields.
* fix(core): make recall fail-open in buildHookOutput + cover recall failure/opts
* feat(claude-code-v2): wrapper plugin skeleton (per-turn recall via bundled core)
Also disables tsup code-splitting in hindsight-coding-agents so each bin
entry (claude-hook.js etc.) is a single self-contained file with no
shared chunk-*.js — required for wrapper build scripts that copy just
the one hook file out of dist/.
* chore(coding-agents): sync codex-hook bin into package-lock
* fix(claude-code-v2): derive version from manifest + guard self-contained bundle
* feat(core): Claude transcript reader (normalized user/assistant text turns)
* fix(core): transcript reader null-safety + drop sidechain turns
* feat: live write-back on the Claude Stop hook (shared retain-hook runtime)
Extracts a testable buildRetain core (read transcript -> upsert under
conversation:<sessionId> via retainLiveSession) plus a thin runRetainHook
plumbing wrapper mirroring the existing runHook/buildHookOutput split, and
wires it up as a Claude Code Stop hook. Fail-open throughout: an empty
transcript is a no-op, and a retain failure is diagnosed but never thrown.
Exports diag() from core/hook.ts so retain-hook.ts can reuse the same
diagnostics helper instead of duplicating it.
* refactor(core): extract diag module + trim buildRetain params
- Move diag() out of hook.ts into a neutral src/core/diag.ts so retain-hook
(and future lifecycle hooks like SessionStart) don't reach into a
recall/reflect-specific module for a cross-cutting concern.
- Drop the unused cwd/cfg params from buildRetain — only harness, sessionId,
transcriptPath, and client are read; cwd/cfg stay in runRetainHook where
they're actually used (config load + deriveBankId).
- Clarify that retainSessions is opencode-plugin-only; the Stop hook always
writes back unless disabled.
* feat(core): knowledge-page CRUD on HindsightClient (mental-models)
* fix(core): page methods throw on 404 + doc rationale
* feat: native TS MCP server for knowledge-page tools (bank-aligned)
Adds a native TypeScript MCP (stdio) server exposing the agent_knowledge_*
tools (get_current_bank, list_pages, get_page, create_page, update_page,
delete_page, recall) over MCP, wired into the claude-code-v2 wrapper.
Bank resolution goes through the same loadConfig + deriveBankId path the
hooks use (harness "claude-code"), so knowledge pages, recall, and retain
all land in one per-repo bank. This is a native TS server rather than
reusing the Python MCP because its bank derivation mismatches.
- src/core/knowledge-tools.ts: SDK-free tool specs (zod schemas), unit
tested against a stub client (17 tests) — every handler is fail-closed
to an isError:true result instead of throwing.
- src/mcp-server.ts: the only file importing @modelcontextprotocol/sdk.
- tsup.config.ts: new mcp-server entry, noExternal inlines the SDK + zod
so dist/mcp-server.js stays a single self-contained file.
- claude-code-v2/.mcp.json + build.mjs: wires the bundle into the plugin;
the self-contained-bundle guard passes for mcp-server.js unmodified
(no exemption needed) since noExternal fully inlines its deps.
* fix(mcp): honor disabled flag + testable selectTools
- Export selectTools(cfg, client, bankId) from mcp-server.ts: pure,
SDK-free, returns [] when cfg.disabled (mirrors the hooks' disabled
check) so a disabled Hindsight exposes zero MCP tools instead of all 7.
Confirmed at runtime: with disabled:true the server still connects but
doesn't advertise a tools capability at all (tools/list -> Method not
found), which is stronger than an empty list.
- Guard main() behind an argv[1]-vs-import.meta.url check so importing
the module for tests doesn't start a real stdio server.
- Add src/mcp-server.test.ts covering selectTools for both the disabled
and enabled cases.
- Reword the HINDSIGHT_MCP_PROJECT_CWD comment: nothing sets it today
(the plugin doesn't cd), it's an escape hatch, not a launching-host
contract.
* refactor(core): lazy-load opencode adapter so backfill bundles self-contained
* test(core): lock opencode no-runtime registry invariant + doc it
* feat(core): cold-repo detection + seed-consent state
* test(core): cover seed write-failure + guard non-object state
* feat(core): background seed mechanics + hindsight-seed control CLI
Adds hasGitHistory (git.ts), startBackgroundSeed + seedControl (seed.ts),
and the src/hindsight-seed.ts entrypoint the agent runs after the
SessionStart seed offer (Task 10b) to seed or decline a repo's bank.
* fix(core): handle async spawn error in startBackgroundSeed
spawn() failures (ENOENT/EACCES/fd exhaustion/sandboxed environments) often
arrive asynchronously as an 'error' event on the child, not a synchronous
throw. An unhandled 'error' event crashes the caller, so attach a no-op
handler alongside the existing try/catch. Also documents the Claude-Code-only
harness assumption in hindsight-seed.ts.
* feat: SessionStart auto-seed offer for cold repos (Claude wrapper wired)
* fix(core): shell-escape seed offer paths + drop orphaned isColdRepo
* docs(claude-code-v2): marketplace entry, full README, v1→v2 migration note
* fix(core): cap hook reflect timeout, align backfill+hook config resolution
- hook.ts: cap reflect's timeoutMs to HOOK_REFLECT_CAP_MS (8s) so it always
resolves/aborts before Claude Code's 15s UserPromptSubmit kill window,
guaranteeing the session cache write + recall injection complete instead
of silently retrying reflect (and dropping recall) on every turn.
- backfill.ts: resolve config via loadConfig({harness, projectDir: REPO,
path}) instead of the legacy string form, so project-local
.hindsight/coding-agent.json layers in and the background auto-seed
backfill targets the same bank recall/retain/MCP read from.
- hook.ts/retain-hook.ts: resolve the cwd fallback before loadConfig (not
just at deriveBankId) so project-local config layers even when the
hook event's cwd is missing.
* fix(claude-code-v2): dev-install must copy .mcp.json (MCP tools were missing)
* feat(core): deterministic SessionStart auto-seed + knowledge-page bank mission
The prior SessionStart design asked the agent to pose a y/n question then
run a seed command itself; live testing showed the model surfaces the
question and then ignores it, so nothing ever seeds. The hook now starts
the background seed itself on a cold git repo (tri-state: cold/warm/
unreachable) and always injects a short visible note plus a bank-mission
pointing the agent at the agent_knowledge_* tools.
* docs(claude-code-v2): update seed docs for deterministic auto-seed + knowledge mission
* feat(core): default seed to aggregated commit messages (one cheap doc) + Initiatives page; full-diff opt-in via --diffs
* docs(core): align backfill README + strategy log/comment with gitlog default
* feat(core): headless codebase-survey seed + agent_knowledge_ingest MCP tool
On a cold repo, the SessionStart hook now also spawns a detached headless
`claude` that samples the repo's structure and ingests its findings into
Hindsight via a new agent_knowledge_ingest MCP tool, alongside the existing
git-history backfill. Knowledge pages synthesize their content from bank
memories via source_query, so this is how the survey feeds them.
- knowledge-tools.ts: add agent_knowledge_ingest (title -> slug doc id,
retain via the "chat" strategy, tagged source:upload).
- survey.ts: resolveClaudeBin + startCodebaseSurvey, mirroring seed.ts's
fire-and-forget/never-throw spawn pattern.
- Anti-recursion: HINDSIGHT_DISABLE_HOOKS guard at the top of runHook,
runRetainHook, and runSessionStartHook so the survey's own claude session
can't re-trigger seeding/recall/retain; survey.ts sets it on the child.
- config.ts: codebaseSurvey (default true) + surveyModel (default "sonnet").
- session-start.ts: wire startSurvey into the cold-repo branch alongside
startSeed; update the visible learning note.
* fix(core): sandbox headless survey (deny-list, no bypassPermissions) + spend cap + document strategy
* feat(core): default codebase-survey model to haiku (cheaper/faster; sonnet still configurable)
* feat(core): survey excludes CLAUDE.md + agent-instruction files from ingestion
* docs(coding-agents): v2 knowledge-pages design spec + implementation plan
* feat(core): add pageRefreshEveryTurns config (default 10)
* feat(core): knowledge-injection roster/preamble formatting
* feat(core): passive knowledge entity_labels tier vocabulary + configureBank wiring
* feat(core): tag-scope seeded pages, Initiatives folder, relatedPageId link source_query
* feat(core): captureInitiative — per-initiative page + relatedPageId marker
* feat(mcp): hindsight_* grounding tools + capture_initiative; remove raw page CRUD from agent
* feat(core): SessionStart injects page roster + guidance preamble
* feat(core): UserPromptSubmit hook-counted periodic page-roster refresh
* feat(core): rich markdown session write-back with tool calls + verbose session strategy
* chore: apply prettier line-wrapping to test files
* fix(claude-code): surface the seed note via user-visible systemMessage, keep preamble in additionalContext
* fix(survey): use renamed hindsight_ingest_document MCP tool (Task 6 rename regression)
* fix(core): preamble + refresh nudge the agent to call capture_initiative for major features
* fix(core): re-inject tool+capture reminder every cadence turn even with no pages (unconditional nudge)
* fix(core): inject when-to-call guide for the full hindsight_* tool suite, not just pages+capture
* feat(claude-code): cold-check-wins seeding — reseed a cleared bank on the live doc count, ignore stale seededAt
* fix(core): simplify capture_initiative instruction to one clear trigger (remove confusing OR-chains)
* fix(core): port proven v1 attribution preamble + surface memories block first so the header actually gets emitted
* fix(core): reflect every turn (configurable reflectEveryTurns, default 1) instead of once per session
* feat(core): per-turn injection is recall-only (drop reflect from the hook), recall token budget default 750
* feat(core): inject a user-feedback section above memories (capture-initiative + attribution-header preferences)
* fix(core): align user-feedback attribution bullet with the generous WHEN-IN-DOUBT-EMIT rule
* fix(core): sharpen capture_initiative trigger — call right after plan approval, before implementation
* feat(survey): raise default codebase-survey budget cap to $2 (0.5 was over-conservative)
* feat(codex): codex-v2 wrapper (SessionStart seed + per-turn recall + MCP); parametrize session-start/MCP harness
* feat(core): default bank template is harness-neutral coding-agent::{gitProject} (shared memory across agents)
* feat(core): default apiUrl is Hindsight Cloud (https://api.hindsight.vectorize.io); local is now an override
* feat(codex): Stop write-back — Codex rollout transcript reader + codex-stop-hook (full parity)
* fix(core): captureInitiative returns the server-assigned page id (not the slug) so read_knowledge_page + relatedPageId links resolve
* feat(coding-agents): upgrade opencode adapter to full v2 parity
Per-turn recall via chat.message + system.transform (750-tok budget), native hindsight_* tools registered directly through opencode's tool() (no MCP server), rich tool-aware write-back on by default, and cold-check auto-seed at plugin load — reusing the shared formatMemories / buildKnowledgePreamble / buildKnowledgeTools / buildSessionStartContext primitives so opencode matches Claude Code and Codex.
Adds transcript-opencode.ts (rich normalizer over the live message list). Adds a HINDSIGHT_DISABLE_HOOKS recursion guard to RuntimeCore (seed/recall/write-back/sync no-op; tools still register) for headless survey runs. Removes the now-dead reflect path (client.reflect, inject.ts/buildSystemInjection, reflectTimeoutMs) as the whole surface is recall-only. README rewritten to the recall/knowledge-page/seed/write-back v2 model.
* feat(coding-agents): harness-portable codebase survey (multi-agent headless)
The cold-repo survey no longer hardcodes headless `claude` — startCodebaseSurvey now runs under the current harness's own CLI (claude/codex/gemini/opencode), falling back to any available agent, so a Codex/Gemini/opencode user without claude installed still gets the survey (the git-log seed already ran regardless).
Per-agent read-only recipes: claude (-p + inline --mcp-config + --disallowedTools), codex (exec --sandbox read-only + inline -c MCP), gemini (-p --approval-mode plan --allowed-mcp-server-names hindsight --skip-trust), opencode (run --agent plan; tools from the loaded plugin under the HINDSIGHT_DISABLE_HOOKS guard). All spawned with HINDSIGHT_DISABLE_HOOKS=1. session-start threads the harness through to the survey.
* feat(gemini): add Gemini CLI v2 integration (gemini-v2)
Full v2 parity for Gemini CLI (>=0.52.0), which added a Claude-style hooks system (stdin/stdout JSON). Maps onto the shared HookSpec/runSessionStartHook/runRetainHook abstraction with Gemini's event names: BeforeAgent (per-turn recall -> hookSpecificOutput.additionalContext), SessionStart (seed), SessionEnd (write-back).
The one Gemini-specific piece is transcript-gemini.ts — a reader for the 0.52.0 chats/session-*.jsonl mutation-log (upsert-by-id, polymorphic content: user text arrays, assistant plain strings, tool results as user functionResponse parts; drops the synthetic session_context message + thoughts). Adds the gemini-v2 wrapper (build.mjs + dev-install.sh that merges hooks + mcpServers into ~/.gemini/settings.json). Validated: reader against a real transcript, and a live recall smoke test (recall_ok) end-to-end.
* style(coding-agents): prettier-format README config table
* fix(opencode): inject via lastInjection fallback (1.18.5 system.transform has no sessionId)
opencode 1.18.5 fires experimental.chat.system.transform with input {model} only — no sessionId — so RuntimeCore.getInjection(input.sessionID) looked up undefined and pushed nothing into the system prompt. Recall still ran (chat.message does pass sessionID) but the memory block + attribution preamble + knowledge-page guide never reached the model, so no visible header and no tool use.
getInjection now falls back to the most recent turn's block (lastInjection) when there's no session-keyed hit. The completion's system.transform fires right after that session's onPrompt, so lastInjection is this turn's block. Adds an inject_ok/inject_empty diag (matching recall_ok/seed_started) to confirm injection lands.
* fix(coding-agents): treat project-local config as untrusted (block apiUrl/apiToken/directoryBankMap from a repo)
A project-local .hindsight/coding-agent.json lives inside whatever repo the developer opens, so it is untrusted input. loadConfig previously merged it per-field over the user-global config, letting a repo override apiUrl while the user-global apiToken survived the merge — so a malicious repo could set only apiUrl and the client would send the user's real Bearer token plus every recall query (the prompt) and Stop write-back transcript to an attacker-controlled host, silently, just by opening the repo (verified end-to-end).
Fix: the project-local layer is now sanitized — apiUrl, apiToken, and directoryBankMap are stripped from it (top level + any harnesses.<name> section) with a one-line warning; the user-global config stays trusted and unrestricted, and a repo can still set its own per-repo bank (bankId/bankIdTemplate). Also skip re-applying the global file as a project layer when the upward findProjectConfig walk lands back on it (a repo under $HOME with no closer config), which would otherwise strip its own apiUrl and warn every session. Adds 4 regression tests.
* style(coding-agents): prettier-format config.ts
* feat(coding-agents): restore reflect as the memory path; per-turn injection from knowledge-page sections
One opinionated runtime path (no behavior config):
- reflect ONCE per session on the first prompt (agentic root-cause synthesis,
benchmark-proven), cached and re-injected every turn — hook harnesses and the
opencode runtime alike
- every turn: knowledge-page SECTIONS matched locally against the prompt
(lexical section index, no server/LLM call) injected with provenance and a
pointer to the full page — fast like recall, organized like reflect
- raw recall leaves the runtime path (still powers the hindsight_search_memory
tool)
Session write-back: transcripts are now JSON turns matching the backfill chat
format, with each tool call compacted to a role:"action" turn naming the tool
and its primary target (no arguments, no outputs) — Claude, Codex, Gemini and
opencode readers.
Knowledge pages: no more entity_labels/tag taxonomy — pages are unscoped, each
page's source_query selects from the whole bank; survey, gitlog seed, write-back
and security hardening stay.
Spec: docs/superpowers/specs/2026-07-27-reflect-pages-runtime.md
* test(coding-agents): rewrite unit tests for reflect+pages runtime and JSON action transcripts
* test(coding-agents): live suite matches reflect_ok by content (pages_ok now follows it in the diag stream)
* docs(coding-agents): README + docs page describe the reflect+pages runtime (reflect once per session, local page-section injection per turn, JSON action write-back)
* feat(coding-agents): drop the backfill CLI — ingestion is automatic and background
- new deepen engine (dist/deepen.js, unpublished): idempotent, resumable —
per-bank lock, dedup by document id; ingests missing conversations, the
one-time gitlog seed, then progressively deepens recent history with
per-commit full diffs (newest first, bounded batch per run); drains and
creates knowledge pages last
- every session start now fires the engine (cold or warm); survey and the
cold-seed note stay cold-only
- sync status is the new readiness contract: hindsight_sync_status agent tool
+ dist/status.js for harnesses (synced = gitlog seeded, pages present,
extractions drained); activeOperations() filters terminal ops
- opencode write-back now upserts every turn (async) so a killed session
loses at most the last turn
- repoNameOf resolves relative paths so document ids are path-spelling-proof
- hindsight-coding-backfill bin removed; benchmark/e2e run the engine
directly and poll status
* polish(coding-agents): short, non-technical cold-seed message highlighting the bank id
* polish(coding-agents): cold-start banner — HINDSIGHT unicode wordmark + bank id line
* feat(coding-agents): timing diagnostics on by default
- session_start diag event on EVERY session (bank, cold/warm, pages, ms) —
warm sessions previously logged nothing
- deepen engine: deepen_started/deepen_done/deepen_failed diag events with
duration; child output now appended to ~/.hindsight/coding-agent-state/deepen.log
(was stdio:ignore — undebuggable) and log lines timestamped
- retain_ok/retain_failed carry ms on both the Stop hook and the opencode
per-turn upsert (which was fully silent)
- vitest config pins HINDSIGHT_DIAG_FILE to a tmp file so unit tests stop
polluting the real diag log
* feat(coding-agents): show the Hindsight banner on every session start (cold: learning, warm: remembering)
* polish(coding-agents): session banner uses the API server's colored pixel-art logo (shared visual identity), wording line below
* polish(coding-agents): banner text before logo — the TUI's first-line prefix was displacing the logo's top row
* polish(coding-agents): banner logo re-rendered foreground-only — the TUI strips ANSI background colors, which deleted half the server logo's pixels
* feat(coding-agents): per-turn user-visible notice — every prompt shows what Hindsight delivered (reflect state + matched knowledge pages) via hook systemMessage; opencode logs the same line
* polish(coding-agents): per-turn notice shows the match query excerpt and the page titles it returned
* fix(pages-index): singularize plain-word tokens so plural prompts match singular headings ('components' -> 'Component map'); path-like tokens untouched
* polish(coding-agents): per-turn notice — gradient Hindsight wordmark, value-driven wording, no timings
* feat(coding-agents): interim always-inject knowledge stub + explicit Hindsight attribution
- selectSections: TEMPORARY stub returning the first section of up to 3
distinct pages every turn regardless of prompt — guarantees injected data
for testing source attribution; will be replaced by the server-side
knowledge-base/search (local lexical index drops with it)
- both injection blocks now carry an ATTRIBUTION directive: when memory
shapes the answer, the agent introduces it with '🧠 From Hindsight memory
(<page>)' — and must never credit memory that did not contribute
* polish(coding-agents): gradient-word banner (logo dropped), lean per-turn notice, attribution directive front-loaded as a mandatory output format
* polish(coding-agents): reflect turn notice shows the assigned goal and a preview of what memory returned
* feat(coding-agents): page knowledge moves from auto-injection to an explicit tool
- new hindsight_search_knowledge_pages(query) tool (native on opencode, MCP on
hook harnesses) — interim local selection, single swap point for the
server-side knowledge-base/search; results carry the attribution requirement
- per-turn auto-injection of page sections removed: a trivial prompt ('yes')
no longer displays phantom research; ordinary turns are silent
- per-turn notice only on the reflect turn (assigned goal + result preview);
tool calls provide their own native visibility
- tool guide/roster advertises the search tool as the first stop
* feat(coding-agents): bind hindsight_search_knowledge_pages to the server-side hybrid knowledge-base search
- merge feat/knowledge-pages-okf underneath (GET /knowledge-base/search,
BM25 + vector, RRF-fused; conflicts resolved in okf's favor for server/
clients/UI, coding-agents docs entry preserved)
- client.searchKnowledgePages(query, limit) wraps the endpoint; the tool
returns ranked {page, page_id, snippet, score} — verified end-to-end
through the real MCP server against the live endpoint
- interim local selection removed from the tool path (pages-index remains
only for the hook page cache pending full cleanup)
* refactor(coding-agents): drop pages-index — local section index deleted; hook/runtime keep only the id+title roster (content lives behind the server-side knowledge-base search)
* refactor(coding-agents): drop hindsight_search_memory (raw recall) — knowledge-page search is THE search surface; recall client method and formatter removed
* feat(coding-agents): hindsight_reflect tool — on-demand deep memory reasoning alongside the session-start reflect
* refactor(coding-agents): one 'conversation' retain strategy for all developer conversations
Backfilled decision chats and live session write-back were the same content
type (identical JSON action-transcript format) extracted two ways based only
on where they came from. Merged CHAT_MISSION + SESSION_MISSION into one
CONVERSATION_MISSION that scales facts to substance (short decision chat ->
1-2 facts, working session -> several; final-state-wins, verbatim literals,
rejected-alternative rule kept); the ≤2-fact CHAT_CUSTOM_INSTRUCTIONS
extractor is retired with it.
* feat(coding-agents): restore Chris's knowledge entity_labels tier
configureBank again sets entity_labels {knowledge: feature-work/decision/
convention/component/concept, tag:true} + entities_allow_free_form, so the
extractor routes durable facts with knowledge:<tier> tags the server-side
knowledge base can select on; capture_initiative markers regain the
knowledge:feature-work label. Pages themselves stay unscoped (the okf
knowledge base owns synthesis).
* feat(coding-agents): seeded pages tag-scoped again — page tags match the restored knowledge:<tier> entity labels (capture_initiative pages included)
* fix(coding-agents): reflect injection wrapped in <hindsight_memory> so write-back never re-ingests it; seed-state file (declined flag) removed — the live bank is the only state
* feat(coding-agents): gitIngest enum ('message' | 'full' | 'none') — one setting, one code path for seeding AND staying current
- deepen's idempotent git pass IS the sync: gitlog doc re-upserts when HEAD
moves (gitlog-head:<sha> tag makes freshness a single tag query); in full
mode new commits surface at the top of rev-list and the next run ingests
them
- separate git-sync path deleted (sync.ts, runtime.syncGitOnce, gitSync
config)
* feat(coding-agents): gitIngest defaults to 'message' (cheap by default; opt into depth); deepen gains --git-ingest override for harnesses
* feat(coding-agents): session banner shows git-sync state (condensed syncStatus): 'git in sync' / 'catching up on new commits' / 'syncing git history (n/target)'
* polish(coding-agents): two-line banner — value headline (tracking decisions/conventions/history) + bank/sync detail line
* refactor(coding-agents): ONE config file — project-local .hindsight/coding-agent.json layer removed entirely (with its sanitization machinery); per-repo routing stays via directoryBankMap
* docs(coding-agents): fix stale project-config reference in comment
* refactor(coding-agents): runtime scratch (deepen lock + engine log) moves to the OS temp dir — ~/.hindsight now holds ONLY the config file
* feat(coding-agents): cursor auto-ingestion parity — hosts without a SessionStart hook fire the deepen engine (+ cold survey) from the session's first prompt
* feat(coding-agents): leveled plugin logging — one plugin.log (debug/info/warn/error, config logLevel + HINDSIGHT_LOG_LEVEL/FILE overrides); diag events mirror at debug; deepen logs itself (separate deepen.log dropped); warn on reflect/retain failures
* feat(coding-agents): one-shot bank configuration via the server's template import — missions, strategies, entity labels, and the 5 seeded pages in a single idempotent POST /import (configureBank PUT+PATCH and createPages removed)
* feat(coding-agents): one-command installer — npx hindsight-coding-agents install|uninstall [harness...]
Detects the coding agents on the machine and merges each one's native
wiring (hooks + MCP: claude mcp add for Claude Code; hooks.json + append-
only config.toml sections for Codex; settings.json for Gemini; hooks.json
+ mcp.json for Cursor; plugin array for opencode). Idempotent by marker,
preserves foreign entries, backs up touched files as .hindsight-backup;
uninstall removes exactly ours. 27 unit tests over temp homes.
* fix(installer): refuse to install from an npx/dlx cache (wired paths would die on eviction); document global install + npm update -g as the update path
* ci(coding-agents): unit + typecheck + build job, and a live E2E job (real API server + real LLM) running the deepen->sync->reflect->injection path; prettier-format the package
* docs(blog): launch post draft — coding-agent memory results (marked draft: true)
* docs(blog): rewrite launch post as the narrative — from 'does memory even help?' through why-not-SWE-bench, the corrections dataset, benchmark-driven architecture decisions, to the final numbers
* docs(blog): position knowledge pages as a co-launch headline — living-documents framing, example page excerpt, platform-wide availability (dashboard editor, hybrid search API, bank templates), closing CTA
* docs(blog): restructure launch post payoff-first — contrarian RAG finding + cost in the lede, TL;DR box, narrated task with both runs, seeded-answers objection met head-on, data-locality/time-to-value/latency answers, Sonnet number promoted, backstory compressed to one section
* fix(coding-agents): deepen waits for server-side ops to settle (template-import page refreshes broke the synced contract); HINDSIGHT_CONFIG env override for the config path (containers/test harnesses; replaces the live test's dependency on the removed project-config layer)
* docs(blog): second-pass fixes — flagship example swapped to the arbitrary retry decision (RFC 4180 attack closed), reconstruction disclosed, 58% provenance clause, placebo backstory + grading block restored in numbers, RAG figure per-task, benchmark-site date
* docs(blog): align remaining CSV references with the retry flagship; TL;DR per-task figures
* docs(blog): flagship rebuilt on the real dataset task — the ERP export decision whose rejected alternative IS the textbook fix (='00042' formula form, minimal quoting, CRLF); dangling injection-verified reference restored; limitations cross-check attached to the correct row
* docs(blog): rewrite as the 0.9.0 launch post — five-beat narrative (question → dataset → auto-recall failure → reflect → knowledge pages from llm-wiki to self-healing) for Knowledge Pages + unified coding-agents plugin
* docs(blog): add the missing beat — shaping the dataset revealed decisions live in git, which the old plugins never ingested
* docs(blog): reframe reflect — very smart rather than slow; first message carries the session goal; on-demand reflect tool for session drift
* docs(blog): pages section addresses the 'back to files?' objection — pages as projected views over consolidated memory (contradiction resolution underneath), raw docs remain source of truth
* docs(blog): out-of-box row updated to n=3 (22/26/23 -> 0.72/task, -26%; cost -35%); matured row marked single-run
* fix(hooks): reflect block injected once per session (+ cadence refresh), not every turn — hook context persists in the transcript, so per-turn re-injection stacked duplicate blocks
* fix(coding-agents): wrapper bundles ship deepen.js, not the renamed backfill.js
The core build entry `backfill` was renamed to `deepen` (deepen engine +
status), but the three wrapper build.mjs bundleFiles lists still copied the
removed `backfill.js`, so every dev-install failed with ENOENT. Point them at
`deepen.js` (spawned by seed.ts at runtime) so the installers build again.
* feat(coding-agents): periodic re-survey — refresh structural pages every N commits
Structural knowledge pages are only generated on a cold repo, so an evolving
architecture drifts from what the survey captured. Add surveyRefreshCommits
(default 20; 0 = cold-seed only): at SessionStart, count commits reachable from
HEAD since the newest survey-baseline marker (branch-robust via
git.commitsSince) and re-run the headless survey once the threshold is crossed,
re-recording a baseline marker. Cold seed still records the first baseline.
* fix(coding-agents): per-turn hook timeout (30s) must exceed the 25s reflect cap
The once-per-session reflect is capped internally at HOOK_REFLECT_CAP_MS=25s,
but every harness killed the UserPromptSubmit/BeforeAgent hook at 15s — below
the cap. The host killed the hook mid-reflect before the cache write, so the
injection was discarded AND the reflect re-fired uncached on every turn
("UserPromptSubmit hook timed out after 15s" every prompt). Raise the hook
timeout to 30s (> cap) across claude/codex/gemini, bump Stop to 30 to match,
and document the cap-below-timeout invariant so it can't silently drift again.
* polish(coding-agents): attribution header is a bold blockquote callout, not flat text
The live directives all told the agent to credit memory with a plain inline
"From Hindsight memory (<page>):", which renders as flat text. Switch every
directive (session tool-guide, reflect injection, both MCP tool descriptions)
to a markdown blockquote header "> ... **From Hindsight memory (<page>)** — ..."
so it renders as a distinct callout, restoring the richer attribution look.
* fix(coding-agents): strip <hook_prompt> transport wrappers from retained transcripts (codex surfaces hook stdout/errors as user messages); session + backfill transcripts switch to JSONL (one turn per line — clean appends, chunker-atomic turns)
Note: benchmark numbers (n=3) were measured on the JSON-array format; JSONL
is extraction-equivalent by design but unvalidated by a sweep — gate before
quoting new numbers on this pipeline.
* fix(installer): write [features].hooks (codex_hooks deprecated in Codex >= 0.145); accept either flag as already-enabled
* fix(hooks): fire the ingestion engine from the FIRST prompt on every harness (lock-protected no-op when SessionStart already did) — safety net for sessions predating the install, whose banks otherwise never get pages; survey stays SessionStart-owned (ensureSeed hosts excepted)
* feat(status): expose survey observability — surveyBaseline (last surveyed HEAD, from Chris's survey-baseline markers) + surveyCommitsBehind in syncStatus/hindsight_sync_status
* test(status): expected shapes include the survey observability fields
* feat(survey): findings docs ARE the completion signal — surveyDocs (0-4) in syncStatus; a baseline without findings re-fires the survey at the next warm session start (crashed-survey retry)
* feat(config): banks.<bankId> overrides — per-repo opt-in/out applied AFTER bank resolution (disable a repo, tune gitIngest/retainSessions per bank) from the ONE config file; resolution fields ignored inside a bank section
* feat(config): bankAliases — remap resolved bank ids as the final resolution step (single hop, converging allowed); docs page brought fully current (env exceptions, gitIngest/logLevel/survey rows, banks overrides, aliases, resolution step 4)
* refactor(config): bank rename lives INSIDE banks.<id> as the field (separate bankAliases tree removed) — one per-repo section for disable, behavior, and rename; applyBankConfig returns {cfg, bankId}
* docs(coding-agents): recipe — two repos sharing one bank (converge by resolved id via banks.<id>.bank, or by path prefix via directoryBankMap), with the id-vs-path rule of thumb
* rename(config): directoryBankMap -> mapPathToBank (direction-explicit; pre-0.9.0 breaking-rename window)
* feat(coding-agents): companion skill — hindsight-coding-agent SKILL.md shipped in the package and installed into ~/.claude/skills by the installer; explains storing/retrieving, full config (banks/mapPathToBank/gitIngest), install/update, and debugging
* docs(coding-agents): mention the companion skill in README + docs page
* feat(coding-agents): companion skill ships to ALL skills-capable hosts (claude/gemini/cursor native dirs, codex via ~/.agents/skills standard); retained sessions and ingested documents carry the harness as tag (harness:<name>) and metadata
* feat(skill): self-updating companion skill — every session start re-syncs installed copies with the packaged SKILL.md (presence-gated; npm update -g now updates the skill too, no re-install)
* fix(coding-agents): worktree-aware document ids (no more per-worktree gitlog duplicates) + deepen self-cleanup; issue/PR refs preserved verbatim and emitted as ENTITIES; calibrated reflect-injection wrapper; docs ported to the TRUE source (hindsight-docs/docs-integrations) that generates the skill copy
* docs(skill): explain the internal marker documents (survey-baseline:<sha> bare-sha content is deliberate — zero extracted facts; gitlog:<repo> seed doc)
* feat(survey): human-readable baseline markers under a zero-extraction marker strategy (live-verified: 0 facts) — start as researching, deepen lazily flips to completed once findings exist
* refactor(survey): one survey strategy with conditional rules replaces the separate marker strategy — status markers extract nothing, findings extract structural facts (both branches live-verified)
* fix(hooks): mid-session heal — zero knowledge pages in the roster cache fires the ingestion engine on any prompt (covers long-lived sessions predating the install; lock makes repeats free)
* feat(bank): ~ expansion in mapPathToBank; document the directory-blacklist recipe (map tree to one bank + disable it)
* feat(coding-agents): explicit correction protocol — when the agent verifies a memory is wrong/stale it ingests a 'Correction: <topic>' doc (claimed vs verified-true vs evidence); guidance in the injection wrapper, tool guide, tool description, and companion skill
* fix(hooks): reflect block injected exactly once — cadence re-injection dropped (replaying the turn-1 synthesis at arbitrary turns reads as random noise after drift; hindsight_reflect covers genuine re-need)
* fix(coding-agents): 15s hard timeout on every client request + opencode boot no longer awaits seedIfCold — a stalled memory server can never freeze the host TUI (onPrompt already tolerates a late preamble)
* fix(reflect): defer past trivial openers — a greeting no longer spends the once-per-session synthesis on 'hi' (seen live: reflect answered a greeting with persona chatter and burned the session's slot); first substantive prompt reflects instead
* test(hooks): align reflect-call assertions with the non-trivial fixture prompt
* Revert trivial-prompt reflect deferral (misread the report — the issue was the notice's UI position, not reflect-on-greeting behavior)
* fix(opencode): stop writing banner/reflect notices to stderr — opencode renders plugin stderr inside the TUI at the cursor (text wedged against the input bar); the trail moves to the plugin log
* feat(opencode): TUI companion plugin — visible presence via api.ui.toast (opencode's TUI plugin API): banner toast on activation + reflect goal/preview toasts from the plugin-log trail; installer registers the second entry
* fix(opencode): visible presence via the server client's tui.showToast (POST /tui/show-toast) — banner + reflect toasts from the server plugin; the separate TUI module approach removed (1.18.9's loader rejects tui-only entries in the shared plugin list); SDK deps bumped to 1.18.9
* fix(opencode): toasts never rendered — v1 client wants {body}, and boot toast raced TUI mount
opencode injects the v1 SDK client whose showToast signature is {body: {title,
message, variant, duration}} and which resolves with {data|error} instead of
rejecting — the earlier flat-params call sent an empty body and the failure was
invisible. Also the toast event is not durable: the seed banner on a warm bank
fired <1s after plugin init, before the TUI subscribed, and was lost. Toasts now
use the body shape, log a rejected result at debug, and defer until ~3s past
init. Verified live in tmux: boot banner and reflect toast both render.
* fix(coding-agents): reflect must report history, never issue directives
The 0.8.6-blog incident: reflect fused two true but unrelated facts (the
hermes-deprecation goal and the blog-section removals of
|