@animalabs/connectome-host 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.env.example +12 -5
  2. package/.github/PULL_REQUEST_TEMPLATE.md +3 -2
  3. package/.github/workflows/changelog.yml +9 -4
  4. package/.github/workflows/ci.yml +5 -3
  5. package/.github/workflows/publish.yml +12 -6
  6. package/CHANGELOG.md +401 -10
  7. package/CONTRIBUTING.md +47 -19
  8. package/HEADLESS-FLEET-PLAN.md +22 -0
  9. package/README.md +39 -1
  10. package/bun.lock +27 -31
  11. package/changelog.d/README.md +28 -0
  12. package/docs/AGENT-ONBOARDING.md +1 -1
  13. package/docs/debug-context-api.md +2 -2
  14. package/docs/retrieval-traces.md +173 -0
  15. package/docs/webui-deployment.md +2 -1
  16. package/package.json +6 -6
  17. package/recipes/SETUP.md +11 -5
  18. package/recipes/TRIUMVIRATE-SETUP.md +68 -14
  19. package/recipes/knowledge-miner.json +0 -30
  20. package/recipes/mock-test.json +19 -0
  21. package/recipes/triumvirate.json +6 -1
  22. package/scripts/audit-module-optins.ts +288 -0
  23. package/scripts/release-changelog.ts +210 -21
  24. package/src/cache-keepalive-log.ts +41 -0
  25. package/src/commands.ts +96 -0
  26. package/src/framework-strategy.ts +50 -4
  27. package/src/gate-telemetry.ts +106 -0
  28. package/src/headless.ts +24 -0
  29. package/src/index.ts +179 -64
  30. package/src/mcpl-config.ts +99 -1
  31. package/src/modules/fleet-module.ts +60 -1
  32. package/src/modules/fleet-types.ts +30 -1
  33. package/src/modules/identity-module.ts +310 -2
  34. package/src/modules/instructions-module.ts +265 -0
  35. package/src/modules/mcpl-admin-module.ts +89 -13
  36. package/src/modules/retrieval-module.ts +249 -51
  37. package/src/modules/retrieval-trace-page.ts +254 -0
  38. package/src/modules/retrieval-trace.ts +904 -0
  39. package/src/modules/subagent-module.ts +18 -0
  40. package/src/modules/tts-relay-module.ts +33 -18
  41. package/src/modules/web-ui-module.ts +445 -894
  42. package/src/recipe.ts +787 -29
  43. package/src/retrieval-config.ts +39 -0
  44. package/src/strategies/frontdesk-strategy.ts +34 -125
  45. package/src/tui.ts +325 -54
  46. package/src/web/panel-data.ts +1206 -0
  47. package/src/web/protocol.ts +75 -10
  48. package/src/workspace-mounts.ts +73 -0
  49. package/test/audit-module-optins.test.ts +174 -0
  50. package/test/cache-keepalive-log.test.ts +83 -0
  51. package/test/conversations-recipe.test.ts +142 -0
  52. package/test/fleet-panel-request.test.ts +90 -0
  53. package/test/framework-fkm-composition.test.ts +35 -3
  54. package/test/framework-strategy-defaults.test.ts +41 -0
  55. package/test/frontdesk-strategy.test.ts +25 -37
  56. package/test/gate-telemetry-adapter.test.ts +84 -0
  57. package/test/gate-telemetry.test.ts +91 -0
  58. package/test/headless-panel-request.test.ts +201 -0
  59. package/test/identity-and-surfaces.test.ts +212 -1
  60. package/test/instructions-module.test.ts +258 -0
  61. package/test/mcpl-admin-module.test.ts +64 -0
  62. package/test/mcpl-agent-overlay.test.ts +51 -3
  63. package/test/mcpl-child-env.test.ts +64 -0
  64. package/test/mock-headless-child.ts +14 -0
  65. package/test/nudge-command.test.ts +47 -0
  66. package/test/recipe-cache-keepalive.test.ts +59 -0
  67. package/test/recipe-compression-fallback.test.ts +19 -0
  68. package/test/recipe-hybrid-prose-routing.test.ts +12 -0
  69. package/test/recipe-instructions.test.ts +176 -0
  70. package/test/recipe-kv-unified.test.ts +87 -0
  71. package/test/recipe-mcp-source.test.ts +54 -0
  72. package/test/recipe-openai-compatible.test.ts +54 -0
  73. package/test/recipe-path-resolution.test.ts +19 -8
  74. package/test/recipe-provider.test.ts +14 -0
  75. package/test/recipe-save-unresolved.test.ts +244 -0
  76. package/test/recipe-source-only.test.ts +38 -0
  77. package/test/release-changelog.test.ts +202 -0
  78. package/test/retrieval-auth-loopback.test.ts +49 -0
  79. package/test/retrieval-config.test.ts +74 -0
  80. package/test/retrieval-module.test.ts +821 -0
  81. package/test/subagent-prose-routing.test.ts +109 -0
  82. package/test/tui-format.test.ts +106 -0
  83. package/test/web-ui-context-coverage.test.ts +1 -1
  84. package/test/web-ui-module.test.ts +189 -3
  85. package/test/web-ui-observers.test.ts +8 -5
  86. package/test/web-ui-protocol.test.ts +0 -0
  87. package/test/workspace-mounts.test.ts +68 -0
  88. package/web/src/App.tsx +160 -44
  89. package/web/src/Context.tsx +35 -8
  90. package/web/src/ContextDocument.tsx +20 -5
  91. package/web/src/Files.tsx +2 -8
  92. package/web/src/Health.tsx +61 -1
  93. package/web/src/Lessons.tsx +2 -38
  94. package/web/src/Mcpl.tsx +80 -14
  95. package/web/src/Pins.tsx +5 -0
  96. package/web/src/Settings.tsx +5 -0
  97. package/web/vite.config.ts +8 -2
package/.env.example CHANGED
@@ -23,11 +23,18 @@ ANTHROPIC_API_KEY=sk-ant-...
23
23
  # GITLAB_TOKEN=glpat-...
24
24
  # GITLAB_API_URL=https://gitlab.example.com/api/v4
25
25
 
26
- # Notion (knowledge-miner.json: syncntn)
26
+ # Notion only if you add a Notion MCP server block back to
27
+ # knowledge-miner.json (none ships by default; see TRIUMVIRATE-SETUP.md Step 6)
27
28
  # NOTION_STORAGE_URL=http://localhost:8000
28
29
  # NOTION_WORKSPACE_ID=...
29
30
 
30
- # Scribe audio/video transcription (knowledge-miner.json: scribe)
31
- # GEMINI_API_KEY=... # REQUIRED if you keep the scribe server (recipe uses bare ${GEMINI_API_KEY}); powers transcription
32
- # NOTION_API_KEY=... # optional (recipe uses ${NOTION_API_KEY:-}); only scribe--scribe_notion_page needs it
33
- # SCRIBE_GLOSSARY_URL=... # optional (recipe uses ${SCRIBE_GLOSSARY_URL:-}); unset = transcribe without a glossary
31
+ # Web UI credentials (triumvirate.json: webui). Defaults to admin:admin —
32
+ # CHANGE THESE for anything reachable beyond your own machine.
33
+ # WEBUI_USERNAME=admin
34
+ # WEBUI_PASSWORD=admin
35
+
36
+ # Scribe — audio/video transcription. Only if you add a scribe block back to
37
+ # knowledge-miner.json (none ships by default; see TRIUMVIRATE-SETUP.md Step 6)
38
+ # GEMINI_API_KEY=... # required by the scribe block (bare ${GEMINI_API_KEY}); powers transcription
39
+ # NOTION_API_KEY=... # optional (block uses ${NOTION_API_KEY:-}); only scribe--scribe_notion_page needs it
40
+ # SCRIBE_GLOSSARY_URL=... # optional (block uses ${SCRIBE_GLOSSARY_URL:-}); unset = transcribe without a glossary
@@ -19,8 +19,9 @@
19
19
 
20
20
  ---
21
21
 
22
- - [ ] `CHANGELOG.md` updated under `## Unreleased` — or this change is
23
- internal-only / test-only / docs-only (apply the `no-changelog` label).
22
+ - [ ] Changelog fragment added `changelog.d/<slug>.<breaking|added|changed|fixed>.md`
23
+ (see `changelog.d/README.md`) or this change is internal-only /
24
+ test-only / docs-only (apply the `no-changelog` label).
24
25
 
25
26
  <!-- AI-assisted contributions are welcome and normal here — see
26
27
  CONTRIBUTING.md for the attribution convention (footer + Co-Authored-By). -->
@@ -14,19 +14,24 @@ jobs:
14
14
  if: "!contains(github.event.pull_request.labels.*.name, 'no-changelog')"
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@v6
17
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
18
18
  with:
19
19
  fetch-depth: 0
20
20
 
21
- - name: Require CHANGELOG.md update when src/ changes
21
+ - name: Require changelog fragment when src/ changes
22
22
  run: |
23
23
  base="${{ github.event.pull_request.base.sha }}"
24
24
  head="${{ github.event.pull_request.head.sha }}"
25
25
  changed=$(git diff --name-only "$base...$head")
26
+ # Fragments must be *added* — a deleted or renamed fragment also
27
+ # appears in --name-only and must not satisfy the check.
28
+ added=$(git diff --name-only --diff-filter=A "$base...$head")
26
29
  echo "Changed files:"
27
30
  echo "$changed"
28
- if echo "$changed" | grep -q '^src/' && ! echo "$changed" | grep -qx 'CHANGELOG.md'; then
29
- echo "::error::This PR touches src/ but not CHANGELOG.md. Add an entry under 'Unreleased' (see CONTRIBUTING.md), or apply the 'no-changelog' label if the change is internal-only."
31
+ if echo "$changed" | grep -q '^src/' \
32
+ && ! echo "$added" | grep -Eq '^changelog\.d/[^/]+\.(breaking|added|changed|fixed)\.md$' \
33
+ && ! echo "$changed" | grep -qx 'CHANGELOG.md'; then
34
+ echo "::error::This PR touches src/ but carries no changelog entry. Add a fragment changelog.d/<slug>.<breaking|added|changed|fixed>.md (see CONTRIBUTING.md), or apply the 'no-changelog' label if the change is internal-only."
30
35
  exit 1
31
36
  fi
32
37
  echo "OK"
@@ -25,15 +25,17 @@ jobs:
25
25
  os: [ubuntu-latest, macos-latest]
26
26
 
27
27
  steps:
28
- - uses: actions/checkout@v6
28
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
29
+ with:
30
+ persist-credentials: false
29
31
 
30
32
  - name: Setup Node.js
31
- uses: actions/setup-node@v6
33
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
32
34
  with:
33
35
  node-version: 24
34
36
 
35
37
  - name: Setup Bun
36
- uses: oven-sh/setup-bun@v2
38
+ uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
37
39
  with:
38
40
  bun-version: 1.3.14
39
41
 
@@ -15,15 +15,17 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v6
18
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
19
+ with:
20
+ persist-credentials: false
19
21
 
20
22
  - name: Setup Node.js
21
- uses: actions/setup-node@v6
23
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
22
24
  with:
23
25
  node-version: 24
24
26
 
25
27
  - name: Setup Bun
26
- uses: oven-sh/setup-bun@v2
28
+ uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
27
29
  with:
28
30
  bun-version: 1.3.14
29
31
 
@@ -51,7 +53,9 @@ jobs:
51
53
  id-token: write
52
54
 
53
55
  steps:
54
- - uses: actions/checkout@v6
56
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
57
+ with:
58
+ persist-credentials: false
55
59
 
56
60
  - name: Require changelog section for this release
57
61
  run: |
@@ -63,7 +67,7 @@ jobs:
63
67
  fi
64
68
 
65
69
  - name: Setup Node.js
66
- uses: actions/setup-node@v6
70
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
67
71
  with:
68
72
  # node 24 ships npm >= 11.5.1, required for OIDC publishing.
69
73
  node-version: 24
@@ -91,7 +95,9 @@ jobs:
91
95
  contents: write
92
96
 
93
97
  steps:
94
- - uses: actions/checkout@v6
98
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
99
+ with:
100
+ persist-credentials: false
95
101
 
96
102
  - name: Mirror changelog section into release notes
97
103
  env:
package/CHANGELOG.md CHANGED
@@ -1,7 +1,408 @@
1
1
  # Changelog
2
2
 
3
+ Entries land with the change that causes them, as fragment files in
4
+ [`changelog.d/`](changelog.d/) that are folded into a version section at
5
+ release time — see [CONTRIBUTING.md](CONTRIBUTING.md#changelog).
6
+
3
7
  ## Unreleased
4
8
 
9
+ ## 0.8.0 — 2026-09-05
10
+
11
+ ### Added
12
+
13
+ - Recipes accept `agent.proseRouting: "disabled"` for tool-only external publication when paired with a supporting Agent Framework release.
14
+ - **`modules.instructions`** — a shared living-instructions file (a CLAUDE.md
15
+ analogue kept in a workspace mount) injected into every agent's context on
16
+ every turn, ephemeral subagents included. `true` for defaults
17
+ (`instructions/AGENTS.md`, 32 KB cap, `system` position) or
18
+ `{ path, header, maxBytes, position }`. Reads resolve through the workspace
19
+ mount (scoping + traversal guard apply), are bounded to `maxBytes`, reject
20
+ symlinks leading outside the mount (realpath containment), are cached by
21
+ `(realpath, mtime, size)`, and fail open — a missing file never blocks
22
+ inference. Recipe validation cross-checks the path's mount prefix against
23
+ the effective workspace mounts (explicit and implicit alike) at load time,
24
+ requires `autoMaterialize: true` on a read-write instructions mount so
25
+ agent curation edits actually reach the disk-side injection, and rejects
26
+ the host-managed `_config` mount (it materializes only on branch-changing
27
+ commands). Validation and the runtime share one mount builder
28
+ (`src/workspace-mounts.ts`), so the two cannot drift.
29
+
30
+ - Recipes accept the default-off `agent.strategy.compressionSourceOnly` flag and pass it through to Context Manager's residence-scoped L1 compression request builder (#103).
31
+
32
+ - Gate telemetry stamps why the turn fired: `x-gate-origin` (heartbeat |
33
+ event | mail | operator | raw reason), `x-gate-channel` and
34
+ `x-gate-counterparty` (adapter-namespaced ids, never content or display
35
+ names) ride the stream lane under the same `GATE_TELEMETRY=1` + base-URL
36
+ gate as the debt stamp; background calls on the complete lane carry debt
37
+ only (#113).
38
+
39
+ - Gate-bound Anthropic calls carry an `x-gate-debt-chunks` header with the
40
+ live compression-debt pending-chunk count (membrane `dynamicHeaders`,
41
+ antra-tess/membrane#65) — the gateway records it per ledger row and strips
42
+ it before the vendor. Double-gated on `GATE_TELEMETRY=1` AND a configured
43
+ `ANTHROPIC_BASE_URL`, so the stamp can never reach a vendor endpoint;
44
+ unreadable state sends no header rather than a guess (#109).
45
+
46
+ - Health tab renders the per-agent compression-debt reduction (state, pending
47
+ chunks, oldest age, merge queue) and says "not reported by this stack" when
48
+ absent — the queue is now distinct from context composition, and the top
49
+ line reads "inference queued" (#110).
50
+
51
+ - Add complete fail-closed recipe validation and strategy passthrough for `foldingStrategy: "kv-unified"`; partial policies, invalid occupancy bands, unsafe approximation grids, and implicit treeification are rejected at load time.
52
+
53
+ - **`agent.provider: 'openai-compatible'`** — run an agent against any
54
+ OpenAI chat-completions endpoint (Ollama, vLLM, Together, Groq, NanoGPT,
55
+ ...) via membrane's existing `OpenAICompatibleAdapter`, which no host ever
56
+ wired. The recipe names the endpoint (`agent.baseUrl`, validated as an
57
+ absolute http(s) URL at load) and the model (required — no default for an
58
+ arbitrary endpoint); the key comes from `OPENAI_COMPATIBLE_API_KEY`
59
+ only (no `OPENAI_API_KEY` fallback — `baseUrl` is recipe-controlled, so a
60
+ fallback would silently send a real OpenAI credential to an arbitrary
61
+ endpoint) and may be absent for local servers.
62
+ `agent.baseUrl` with any other provider is rejected at load time.
63
+
64
+ - Recipes can pass the Context Manager source-only compression controls through Host/FKM, including the new default-off L1 and merge final-fallback modes, with boolean validation and cross-agent isolation.
65
+
66
+ ### Added
67
+
68
+ - **`agent.cacheKeepalive` — hold an idle agent's 1h prompt cache warm.** With
69
+ `cacheTtl: "1h"`, an idle agent's cached prefix expires after an hour and its
70
+ next wake pays a **2x cache write** over the entire context. Reading an entry
71
+ restarts its clock, so membrane now replays the last request with
72
+ `max_tokens: 0` (prefill only) to refresh it at cache-**read** price (0.1x).
73
+ On by default for the anthropic provider; `{ "enabled": false }` opts out.
74
+ - **Cost is proportional to actual idleness, not to `maxIdleHours`.** A poke
75
+ fires only when the entry is genuinely near expiry, so a busy agent never
76
+ fires one — its own traffic already refreshed the TTL. Measured on mythos
77
+ llm-calls over 36h: 563 of 637 gaps were under 5 minutes, only 5 exceeded
78
+ 1h.
79
+ - Knobs: `maxIdleHours` (default 24, measured from the last **real** request
80
+ so pokes cannot extend their own mandate) and `refreshAfterMinutes`
81
+ (default 45). Recipe validation **rejects `refreshAfterMinutes >= the cache
82
+ TTL`** — such a keepalive always fires after the entry has already expired,
83
+ paying a full cache write on every poke while still looking like a healthy
84
+ successful call.
85
+ - Events land in `service-stderr.log`, warn-level for `ineffective` and
86
+ `disabled`, so a background spender is legible without opening a billing
87
+ dashboard.
88
+ - Sizing, from fable-cm's 11-day log (~500k-token prefix): 49.7M tokens of
89
+ `cache_creation` landed on turns following a >1h idle gap — ~$944 of write
90
+ premium at fable-5 rates that this converts to ~$308 of reads.
91
+
92
+ - **`provider: "mock"` — run the whole host with zero provider spend and no
93
+ credentials.** Wires membrane's existing `MockAdapter` (previously
94
+ unreachable from any recipe) as a first-class provider: echoes the last
95
+ user message by default, or returns `agent.mock.defaultResponse` with
96
+ `agent.mock.echoMode: false` for deterministic scripted output. No API
97
+ key is required or read. Mock calls still ride the generic logging
98
+ decorator, so `llm-calls.*.jsonl` receipts work exactly as they do for
99
+ real providers. `recipes/mock-test.json` is a ready-made offline smoke
100
+ recipe (loopback webui, everything else off).
101
+
102
+ ### Added
103
+
104
+ - **Hybrid prose routing.** Recipes may set `agent.proseRouting: "hybrid"`: unprefixed text keeps ordinary frozen-locus delivery, while a leading `>>>destination` publication envelope routes through Agent Framework’s existing authorized cross-surface resolver. Source text retains the envelope; recipients see only the body; success/failure returns to resident context.
105
+
106
+ - **`conversations` recipe block — per-channel conversation forks.** Maps to
107
+ agent-framework's `ConversationRouter`: the recipe's agent becomes a dormant
108
+ trunk template, and qualifying incoming channel messages spawn per-channel
109
+ fork agents seeded from the trunk's current context. Recipe surface: `bind` /
110
+ `trigger` rules per channel kind (`dm`/`groupDm`/`channel`), `idleTtlMs`
111
+ (default 12h), `closurePrompt`, and `agentPrefix`. The host fills
112
+ `templateAgent` from the recipe and creates a fresh stateful strategy instance
113
+ for each fork. Absent block means no routing and no behavior change.
114
+
115
+ - **Protective reaction-suppression baseline for Discord adapters.** Stdio
116
+ MCPL children now receive `DISCORD_SUPPRESSED_REACTIONS_BASELINE` — the
117
+ agent-framework's exported refusal-annotation set (`REFUSAL_REACTION_BASELINE`,
118
+ comma-joined) — so a never-configured Discord adapter defaults to
119
+ suppressing exactly the markers this host's framework stamps, instead of
120
+ defaulting to nothing. An operator-set value on the server entry
121
+ supersedes the house baseline, and the adapter's own precedence (filters-file
122
+ key including explicit `[]` → legacy operator env → baseline) governs
123
+ enforcement; lost configuration stays stale rather than re-defaulting.
124
+ Requires an agent-framework release carrying the `REFUSAL_REACTION_BASELINE`
125
+ export.
126
+
127
+ - **Standing autobiographical production target.** Recipes may set `agent.strategy.productionBudgetTokens` to keep the summary forest deep enough for a later live context-budget descent without a fold storm. This is a context-token target passed through to Context Manager, not a provider-spend ceiling; omission preserves Context Manager defaults.
128
+
129
+ - **`mcpl_list` reports manifest freshness.** Each loaded server now shows the
130
+ last validated manifest revision plus fetch and grant-negotiation timestamps.
131
+ Older Agent Framework versions remain legible as `manifest=unknown`, and the
132
+ server-authored revision is quoted and bounded before reaching model-facing
133
+ text.
134
+
135
+ - **`BEDROCK_BASE_URL` env hook** for the bedrock provider — mirrors
136
+ `ANTHROPIC_BASE_URL`, routing bedrock-runtime calls through an inference
137
+ gateway (gate.animalabs.ai/bedrock/<credSet>). The gate reads the agent
138
+ token from the SigV4 Credential (`AWS_ACCESS_KEY_ID` slot), discards the
139
+ client signature, and re-signs with real AWS creds. First user: Princess,
140
+ moved off the first-party Anthropic API (classifier "bio" false-positive
141
+ streak) onto Bedrock Sonnet 4.5 via gate apse1 — needs membrane ≥1dcd4e3
142
+ for `global.` inference-profile id pass-through.
143
+
144
+ ### Changed
145
+
146
+ - **Dependency floor: agent-framework `^0.10.0`, chronicle `^0.3.0`,
147
+ membrane `^0.5.78`.** af 0.10.0 brings `ConversationRouter` (the
148
+ per-channel conversation-fork machinery this release’s `conversations`
149
+ recipe surface targets, and includes the current `hybrid` prose router) and exports `nudgeAgent`, which `/nudge` has
150
+ called since it landed — on every published af before 0.9.0 that call
151
+ was a guaranteed `TypeError`, so the floor also makes `/nudge` actually
152
+ work. Chronicle `^0.3.0` aligns the whole tree on one chronicle copy
153
+ (previously context-manager `0.6.3` nested its own `0.3.0` next to the
154
+ host's `0.2.x`). Operators: run a clean `npm ci` — a stale
155
+ `node_modules` predating the lock is the known failure mode here.
156
+
157
+ - **The public triumvirate recipes boot from a fresh clone.**
158
+ `knowledge-miner.json` no longer ships a `syncntn` (Notion) block pointing at
159
+ an org-internal adapter that isn't publicly available — with `NOTION_*` env
160
+ vars unset the block failed recipe load, and with them set it died at spawn
161
+ on the dangling `../syncntn` path. The `scribe` block is dropped for the
162
+ same reason: it hard-required `GEMINI_API_KEY` and a `../scribe-mcp`
163
+ sibling checkout, neither mentioned anywhere in the setup guides — a
164
+ guide-following fresh install always got a crashed miner. Notion and
165
+ Scribe are now add-a-block opt-ins, documented in SETUP.md and
166
+ TRIUMVIRATE-SETUP.md (the miner prompt's tool-name contracts are
167
+ unchanged). `triumvirate.json` declares
168
+ webui Basic-Auth defaulting to `admin`/`admin` (override via
169
+ `WEBUI_USERNAME` / `WEBUI_PASSWORD` in `.env`) instead of bare
170
+ `"webui": true`, which the non-loopback bind guard refuses to start.
171
+
172
+ - **agent-framework `^0.11.0`** (was `^0.10.0`). Activates `proseRouting:
173
+ "disabled"` for recipes that set it (#100 accepted the key; the runtime now
174
+ implements it — generated prose is never published externally, only explicit
175
+ tools speak), plus AF 0.11's Windows workspace-mount fix and the
176
+ org-acceleration 429 cooldown. Clears the last two standing cross-package
177
+ `tsc` errors — the typecheck is fully clean at this lock.
178
+
179
+ - Changelog entries now land as per-change fragment files in `changelog.d/`
180
+ (`<slug>.<breaking|added|changed|fixed>.md`), folded into the version
181
+ section at release time — concurrent PRs no longer conflict in
182
+ `CHANGELOG.md`. Editing `## Unreleased` directly still works and is merged
183
+ at the same point.
184
+
185
+ - **membrane `^0.5.80`** (was `^0.5.78`, lockfile-resolved 0.5.79). Two
186
+ latent cache behaviors the host already configures become ACTIVE with this
187
+ relock: the prompt-cache keepalive (`agent.cacheKeepalive`, on by default —
188
+ previously passed to an adapter version with no such field and silently
189
+ ignored, so idle gaps over the 1h TTL repaid a full cache write on wake)
190
+ and the floating cache marker (incremental prompt caching inside the native
191
+ tool loop, membrane's default-on). Both reduce cost; neither changes
192
+ visible agent behavior. Also clears two of the four standing cross-package
193
+ `tsc` errors (the membrane-typing pair).
194
+
195
+ - Depend on `@animalabs/agent-framework` ^0.12.0 and `@animalabs/membrane` ^0.5.82 —
196
+ the published versions that implement the active-turn trigger and the
197
+ lane-aware `dynamicHeaders` the wake-cause stamp (#113) relies on; the
198
+ compatibility cast and optional lookup are gone, and an adapter-level test
199
+ proves a stream call carries the origin trio while a complete call carries
200
+ debt only.
201
+
202
+ ### Fixed
203
+
204
+ - **Prompt-cache keepalive events all go to stderr**, so every one of them lands
205
+ in `service-stderr.log` beside `[inference-refusal]` instead of being split by
206
+ severity across two sinks. Routine `refreshed` events previously went to
207
+ stdout — which the host unit leaves on the journal — so the log an operator
208
+ actually greps showed nothing. Observed on fable-cm 2026-08-23: the keepalive
209
+ refreshed a 523,102-token prefix three times, correctly and with zero cache
210
+ writes, while a monitor tailing `service-stderr.log` reported no activity for
211
+ three hours. A background spender that can't be found in the operator's log is
212
+ indistinguishable from one that never ran.
213
+
214
+ - Plumb `agent.strategy.compressionRecallBudgetTokens` through recipe validation and Framework strategy construction, with positive-integer validation instead of silently accepting an inert key.
215
+
216
+ - **Saved recipe snapshots no longer contain resolved secrets.** `loadRecipe`
217
+ substitutes every `${VAR}` — API tokens included — and the host then wrote
218
+ that fully resolved recipe to `$DATA_DIR/.recipe.json` at default file mode:
219
+ plaintext credentials in the exact directory deployments bind-mount and back
220
+ up (found by an external recipe review that verified live tokens in a backed
221
+ up `data/` directory on a production VM). The snapshot now keeps the
222
+ pre-substitution form — `${VAR}` references literal, a URL `systemPrompt`
223
+ kept as the URL — and a resumed session re-runs substitution, validation,
224
+ and the prompt fetch against the *current* environment, so secret rotation
225
+ and remote prompt updates take effect on restart without re-cooking. The
226
+ file is written 0600 and re-chmod'd 0600 on every save. Legacy resolved
227
+ snapshots (no `$unresolved` marker) still load verbatim, with no
228
+ substitution, so a literal `${...}` surviving in prose cannot fail them;
229
+ resuming an unresolved snapshot whose required env var has since disappeared
230
+ fails loudly naming the variable instead of silently starting the default
231
+ recipe.
232
+
233
+ - **Ephemeral subagents inherit the caller's `proseRouting` mode.** They
234
+ previously always ran AF's `'locus'` default regardless of the recipe, so a
235
+ resident running `proseRouting: "disabled"` still spawned subagents whose
236
+ between-tool-calls prose published live into its open channel as parent
237
+ speech (field-confirmed on a deployed resident, 2026-08-26 — including
238
+ after the recipe adopted `"disabled"`, which reached only the resident).
239
+
240
+ ### Fixed
241
+
242
+ - **`mcpServers.<id>.source` accepts cook's npm registry form.**
243
+ `validateRecipe` demanded `source.url`, but connectome-cook's source grammar
244
+ also has `{ "npm": "pkg@version" }` — which the shipped knowledge-miner
245
+ recipe uses for its gitlab server, so that recipe failed to load
246
+ (`mcpServers.gitlab.source.url must be a non-empty string`). Exactly one of
247
+ `url` / `npm` is now required; the field remains build-tooling metadata,
248
+ ignored at runtime.
249
+
250
+ ## 0.7.4 — 2026-08-03
251
+
252
+ ### Changed
253
+
254
+ - **Frontdesk agents ride the adaptive path.** `frontdesk` strategies now
255
+ default to adaptive resolution + kv-stable folding, same as
256
+ `autobiographical` (a recipe can pin `adaptiveResolution: false` to keep the
257
+ old hierarchical renderer). The hierarchical renderer reserves nothing for
258
+ the raw tail and cannot shed summary mass, so a long-lived frontdesk agent
259
+ eventually saturates its fixed context budget into a terminal
260
+ `UncoveredDropError` refusal loop — the 2026-08-03 boter clerk outage.
261
+ Details and deltas:
262
+ - Topic-aware chunking now rides context-manager's `chunkBoundaryHint` seam
263
+ (requires CM ≥0.6.3) instead of a fork of `rebuildChunks` that silently
264
+ bypassed chunk-record persistence and the fail-closed orphan guard.
265
+ - Existing frontdesk stores carry no chunk records (the fork never wrote
266
+ them); context-manager's `migrateChunkRecords` backfills them from L1
267
+ `sourceIds` on first load, so upgraded stores do not re-compress lived
268
+ history. First boot re-plans folds (one-time KV churn, possibly a burst of
269
+ L1 production for the un-summarized frontier).
270
+ - The salience-biased L1 emission order is retired (it was a hierarchical-
271
+ renderer concept); unanswered questions/@mentions are still preserved
272
+ verbatim through the compression prompt.
273
+ - Witnessed chunks now get the base witnessed compression prompt; the fork
274
+ predated witnessed prompts and overrode them.
275
+
276
+ ### Added
277
+
278
+ - **Every WebUI inspection panel now works per fleet child.** One persistent
279
+ scope dropdown in the sidebar header ("inspecting: …") replaces the
280
+ per-tab pill rows Lessons/Files carried — MCPL, Context, Settings, Pins,
281
+ Health, and the main-pane Context document all follow it, instead of the
282
+ previous split where only Lessons/Files could switch (statefully, via
283
+ duplicated pickers), Context 404'd by mis-sending the child name as an
284
+ `?agent=` param, and MCPL/Settings/Pins/Health were silently locked to the
285
+ fleetmaster. Backed by one generic fleet IPC verb pair
286
+ (`panel-request`/`panel-response`) dispatching into a shared panel layer
287
+ (`src/web/panel-data.ts`) that both the WebUI host and headless children
288
+ run — a new panel op needs no protocol change to work fleet-wide.
289
+ Details:
290
+ - `/debug/context/{,makeup,coverage,curve,preview,maintenance}` and
291
+ `/healthz` accept `?scope=<child>` — the host proxies to the child over
292
+ the fleet IPC and answers with its JSON verbatim (still curl-able;
293
+ connectome-doctor / fleet hub can now watch children through the host).
294
+ `/curve?scope=<child>` passes through to the scoped JSON.
295
+ - The MCPL tab shows the scoped process's **live** loaded servers
296
+ (connection status, tool counts — the long-missing fleet mcpl snapshot)
297
+ above the shared registry file; registry edits stay host-scope (the
298
+ file is one cwd-shared registry, so a "child-local edit" would be a
299
+ lie) and the panel says so instead of hiding the fact.
300
+ - Settings mutations, dry-run previews (single-flight guard now lives in
301
+ the target process), and pin add/remove run inside the scoped child;
302
+ child pins snapshots ship picker candidates (real store ids) since the
303
+ SPA has no window into a child's message store.
304
+ - Scoped WS responses (`lessons-list`, `workspace-*`, `mcpl-list`,
305
+ `settings-state`, `pins-list`) now echo their `scope`, and the SPA
306
+ drops replies that arrive after the operator switched — fixing a
307
+ pre-existing race where a slow child's lessons/files could render under
308
+ another child's header.
309
+ - Child health snapshots include the child's recent provider-call ledger.
310
+ - **TUI: context budget gauge.** The status bar's `ctx:` readout and the fleet
311
+ tree's per-agent readouts show `142k/180k` against the *live* runtime budget
312
+ (runtime overrides win over the recipe), and the status segment goes yellow at
313
+ 75% / red at 90% — "how close to compression/trouble" at a glance instead of a
314
+ bare number.
315
+ - **TUI: fleet view viewport.** The tree now scrolls with the cursor
316
+ (`┈ N lines above/below ┈` markers) instead of clipping past the bottom of the
317
+ terminal — previously a large fleet let the cursor walk below the fold and
318
+ Del:stop targeted rows the operator couldn't see.
319
+ - **TUI: fleet view opens with a summary header** — agent counts
320
+ (running/done/failed/cancelled across local subagents *and* fleet children),
321
+ children up/crashed, session cost — plus the active ops alerts in full (the
322
+ status bar only has room for a count).
323
+ - **TUI: event timestamps.** Alerts, tool batches, subagent results, wake
324
+ triggers, branch switches, errors and user messages get an `HH:MM` prefix, so
325
+ scrollback read an hour later still answers "when".
326
+ - **TUI: root-agent tool completions are visible.** Verbose shows every
327
+ `✓ tool (1.2s)`; terse shows the slow ones (≥2s). Slow *running* tools show a
328
+ live elapsed in the status bar after 5s — "still executing" and "stuck" no
329
+ longer look identical.
330
+ - **TUI: the status bar names the worst active alert** (`⚠ 2 ·
331
+ compression-quarantine`), with quarantine and inference-exhausted outranking
332
+ the merely-recent.
333
+
334
+ ### Changed
335
+
336
+ - **TUI: thinking honors the Ctrl+V verbose toggle.** Terse mode collapses live
337
+ thinking to a counting one-liner (`💭 thinking… ~1.2k tok`) and replayed
338
+ history thinking to one truncated line per block — the toggle's label always
339
+ claimed this.
340
+ - **TUI: session-history replay caps at the last 50 messages** (marker points at
341
+ the web UI for the rest) instead of flooding scrollback with the whole session.
342
+ - **TUI: elapsed times are humane everywhere** — `5m48s`, not `348s`; the fleet
343
+ tree and both peek views now agree.
344
+ - **TUI: peek-proc renders child `ops:alert` events properly** (red `⚠ kind:
345
+ message`, cyan for `-clear`) instead of a dim `· ops:alert` dot line, and no
346
+ longer prints dot lines for per-block/per-round bookkeeping events.
347
+ - **TUI: the status-left segment truncates to fit** the terminal width instead of
348
+ shoving the tokens/mem segment off the row.
349
+
350
+ ### Fixed
351
+
352
+ - `cancel-subagent-result` was missing from the headless runtime's
353
+ subscription-filter exemptions: a parent that narrowed the event stream
354
+ could never see its own cancel confirmations.
355
+ - The Vite dev server proxies `/debug`, `/healthz`, `/curve`, and `/files`
356
+ to the running host — previously every HTTP panel fetch 404'd under
357
+ `bun run dev`.
358
+ - **TUI: "Branch switched" announcements survive.** The line was printed *before*
359
+ `refreshFromStore()` cleared the scrollbox, so it was destroyed unread.
360
+
361
+ ### Upgrade notes
362
+
363
+ - **subagents/lessons/retrieval are now opt-in** (they were opt-out, and
364
+ DEFAULT_RECIPE enabled all three). A recipe that omits them ran them under
365
+ v0.7.2 and stops running them on this upgrade — that is the fix for
366
+ "lessons injected despite following the onboarding guide" (Discord issue
367
+ #32) working as intended. A recipe that *explicitly* enables them keeps
368
+ them, deliberately: a defaults change cannot tell old boilerplate from a
369
+ real choice. Before upgrading an existing deployment, run
370
+
371
+ bun scripts/audit-module-optins.ts <recipes-and-data-dirs...>
372
+
373
+ It reports every explicit enable, every omission that changes behavior,
374
+ and every retrieval-without-lessons combination that would go silently
375
+ inert — and modifies nothing; the decisions stay with the operator.
376
+ Persisted `data/.recipe.json` files are launch-time snapshots, not
377
+ authoritative sources — the audit lists them separately as pointers back
378
+ to the source recipe. Retrieved-lesson injection also moved from the
379
+ system prompt to after the last user message, which keeps the stable
380
+ prefix KV-cacheable.
381
+
382
+ ### Added
383
+
384
+ - **Operator retrieval traces.** The Web UI now exposes operator-only,
385
+ process-memory retrieval traces at `/debug/retrieval` and a readable
386
+ lesson-selection viewer at `/debug/retrieval/view`, including invoking-agent
387
+ attribution, mechanical candidates, relevance decisions, cache provenance,
388
+ and the exact injected lesson block. Exact conversation/model inputs remain
389
+ opt-in via literal `includeInputs=1`.
390
+
391
+ ### Fixed
392
+
393
+ - **OpenAI retrieval reasoning effort.** Recipes using `openai-responses` or
394
+ `openai-codex` can set `modules.retrieval.reasoningEffort` independently of
395
+ the primary agent. Unsupported providers fail recipe validation instead of
396
+ receiving an invalid OpenAI-shaped request, and reasoning-enabled retrieval
397
+ requires an explicit model instead of falling through to the Claude default.
398
+
399
+ - **`mcpl_list` reports the live MCPL policy boundary.** Each server now shows
400
+ connected/retrying state, whether its initial policy was established, its
401
+ effective grant, host-masked and deny-by-default capability paths, and the
402
+ separate host-owned `host/command` authority. During a rolling upgrade,
403
+ fields unavailable from an older agent-framework render as `unknown` rather
404
+ than as a misleading empty grant.
405
+
5
406
  ## 0.7.3 — 2026-08-01
6
407
 
7
408
  ### Changed
@@ -68,8 +469,6 @@
68
469
  write, output, breakpoints, duration and verdict, with refusals and errors
69
470
  highlighted. Cumulative totals for the session remain in the Usage panel.
70
471
 
71
- ## Unreleased
72
-
73
472
  ## 0.7.0 — 2026-07-26
74
473
 
75
474
  ### Added
@@ -89,8 +488,6 @@
89
488
  `/debug/context/makeup` this costs nothing and makes no `count_tokens` network
90
489
  call, so it is safe on the 15s health poll.
91
490
 
92
- ## Unreleased
93
-
94
491
  ## 0.6.1 — 2026-07-26
95
492
 
96
493
  ### Fixed
@@ -105,8 +502,6 @@
105
502
  text/id filter. Caught by checking the endpoint against a real store before
106
503
  anyone used the panel.
107
504
 
108
- ## Unreleased
109
-
110
505
  ## 0.6.0 — 2026-07-26
111
506
 
112
507
  ### Added
@@ -170,8 +565,6 @@
170
565
  measured cost, and notes runs are serialized so a second click is refused
171
566
  rather than queueing another pause.
172
567
 
173
- ## Unreleased
174
-
175
568
  ## 0.5.3 — 2026-07-26
176
569
 
177
570
  ### Added
@@ -294,8 +687,6 @@
294
687
  - `compressionMaxTokens` recipe passthrough — cap compression output for
295
688
  models with low output ceilings (2c78936).
296
689
 
297
- ## Unreleased
298
-
299
690
  ### Fixed
300
691
 
301
692
  - **TUI bug sweep** (#64): operator-safety and observability fixes.