@animalabs/connectome-host 0.7.4 → 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.
- package/.env.example +12 -5
- package/.github/PULL_REQUEST_TEMPLATE.md +3 -2
- package/.github/workflows/changelog.yml +9 -4
- package/.github/workflows/ci.yml +5 -3
- package/.github/workflows/publish.yml +12 -6
- package/CHANGELOG.md +245 -0
- package/CONTRIBUTING.md +47 -19
- package/README.md +27 -0
- package/bun.lock +27 -31
- package/changelog.d/README.md +28 -0
- package/package.json +5 -5
- package/recipes/SETUP.md +11 -5
- package/recipes/TRIUMVIRATE-SETUP.md +68 -14
- package/recipes/knowledge-miner.json +0 -30
- package/recipes/mock-test.json +19 -0
- package/recipes/triumvirate.json +6 -1
- package/scripts/release-changelog.ts +210 -21
- package/src/cache-keepalive-log.ts +41 -0
- package/src/commands.ts +96 -0
- package/src/framework-strategy.ts +37 -0
- package/src/gate-telemetry.ts +106 -0
- package/src/headless.ts +10 -0
- package/src/index.ts +167 -55
- package/src/mcpl-config.ts +99 -1
- package/src/modules/identity-module.ts +310 -2
- package/src/modules/instructions-module.ts +265 -0
- package/src/modules/mcpl-admin-module.ts +58 -11
- package/src/modules/subagent-module.ts +18 -0
- package/src/recipe.ts +732 -25
- package/src/web/panel-data.ts +19 -0
- package/src/workspace-mounts.ts +73 -0
- package/test/audit-module-optins.test.ts +10 -3
- package/test/cache-keepalive-log.test.ts +83 -0
- package/test/conversations-recipe.test.ts +142 -0
- package/test/framework-fkm-composition.test.ts +35 -3
- package/test/framework-strategy-defaults.test.ts +19 -0
- package/test/gate-telemetry-adapter.test.ts +84 -0
- package/test/gate-telemetry.test.ts +91 -0
- package/test/identity-and-surfaces.test.ts +212 -1
- package/test/instructions-module.test.ts +258 -0
- package/test/mcpl-admin-module.test.ts +41 -0
- package/test/mcpl-agent-overlay.test.ts +51 -3
- package/test/mcpl-child-env.test.ts +64 -0
- package/test/nudge-command.test.ts +47 -0
- package/test/recipe-cache-keepalive.test.ts +59 -0
- package/test/recipe-compression-fallback.test.ts +19 -0
- package/test/recipe-hybrid-prose-routing.test.ts +12 -0
- package/test/recipe-instructions.test.ts +176 -0
- package/test/recipe-kv-unified.test.ts +87 -0
- package/test/recipe-mcp-source.test.ts +54 -0
- package/test/recipe-openai-compatible.test.ts +54 -0
- package/test/recipe-path-resolution.test.ts +19 -8
- package/test/recipe-provider.test.ts +14 -0
- package/test/recipe-save-unresolved.test.ts +244 -0
- package/test/recipe-source-only.test.ts +38 -0
- package/test/release-changelog.test.ts +202 -0
- package/test/subagent-prose-routing.test.ts +109 -0
- package/test/workspace-mounts.test.ts +68 -0
- package/web/src/App.tsx +1 -0
- package/web/src/Health.tsx +61 -1
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
|
|
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
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
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
|
-
- [ ]
|
|
23
|
-
|
|
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
|
|
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/'
|
|
29
|
-
|
|
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"
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -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,252 @@
|
|
|
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
|
+
|
|
5
250
|
## 0.7.4 — 2026-08-03
|
|
6
251
|
|
|
7
252
|
### Changed
|
package/CONTRIBUTING.md
CHANGED
|
@@ -43,7 +43,7 @@ plus, when applicable, **Not verified**, **Out of scope**, and
|
|
|
43
43
|
- **Tests accompany behavior changes.** Review scrutinizes test substance,
|
|
44
44
|
not mere presence — a test that can't fail on the unfixed code will be
|
|
45
45
|
called out.
|
|
46
|
-
- **Changelog
|
|
46
|
+
- **Changelog fragment** in `changelog.d/` for anything behavior-affecting
|
|
47
47
|
(see below).
|
|
48
48
|
|
|
49
49
|
Conventional-commit-style titles (`feat(recipe): …`, `fix(subagent): …`) are
|
|
@@ -80,33 +80,61 @@ that don't fail on unfixed code, or with claims the branch itself disproves.
|
|
|
80
80
|
|
|
81
81
|
## Changelog
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
(loosely [Keep a Changelog](https://keepachangelog.com/))
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
Changelog entries land as **fragment files** in
|
|
84
|
+
[`changelog.d/`](changelog.d/) — one file per change — and are folded into
|
|
85
|
+
`CHANGELOG.md` (loosely [Keep a Changelog](https://keepachangelog.com/)) at
|
|
86
|
+
release time. One file per change is what keeps concurrent work from
|
|
87
|
+
conflicting: when every PR edited the same `## Unreleased` section, any PR
|
|
88
|
+
that outlived another merge hit a conflict in `CHANGELOG.md`; distinct files
|
|
89
|
+
never do.
|
|
90
|
+
|
|
91
|
+
- **Format:** `changelog.d/<slug>.<breaking|added|changed|fixed>.md`, a flat
|
|
92
|
+
file directly in `changelog.d/`, containing one or more markdown bullets
|
|
93
|
+
(`- …`) written exactly as they should appear in `CHANGELOG.md`:
|
|
94
|
+
continuation lines indent two spaces, nested bullets are fine, headings
|
|
95
|
+
and horizontal rules are refused (even indented — a heading inside a
|
|
96
|
+
fragment would corrupt the section structure). The slug just has to be
|
|
97
|
+
unique among pending fragments and filesystem-safe — the PR number works,
|
|
98
|
+
and so does the branch name with `/` replaced by `-`
|
|
99
|
+
(`100-prose-routing.added.md`, `fix-retry-backoff.fixed.md`). The release
|
|
100
|
+
script scans the directory fail-closed: a subdirectory, an unrecognized
|
|
101
|
+
category suffix, or any other stray file aborts the release rather than
|
|
102
|
+
silently stranding an entry.
|
|
103
|
+
|
|
104
|
+
- **The fragment lands with the change** — same commit, or at least the same
|
|
88
105
|
PR. This binds direct pushes to `main` just as much as PRs. On PRs, CI
|
|
89
|
-
enforces it softly: touching `src/` without
|
|
90
|
-
the `changelog` check unless the `no-changelog`
|
|
106
|
+
enforces it softly: touching `src/` without adding a fragment (or editing
|
|
107
|
+
`CHANGELOG.md`) fails the `changelog` check unless the `no-changelog`
|
|
108
|
+
label is applied.
|
|
91
109
|
- **What needs an entry:** anything an operator, recipe author, or module
|
|
92
110
|
developer would notice — behavior, config/recipe schema, CLI, tool
|
|
93
111
|
surfaces, defaults. Internal refactors, test-only, and docs-only changes
|
|
94
112
|
don't.
|
|
95
|
-
- **Breaking entries are audience-scoped.**
|
|
96
|
-
|
|
113
|
+
- **Breaking entries are audience-scoped.** Open the bullet by naming who
|
|
114
|
+
needs to act (`- **Recipe authors:** …`) and cover: **who needs to act**,
|
|
97
115
|
**migration**, and **unchanged** (what readers might fear broke but
|
|
98
116
|
didn't). The fleet recipe-path entry in `CHANGELOG.md` is the canonical
|
|
99
117
|
example of the format.
|
|
118
|
+
- **Editing `## Unreleased` in `CHANGELOG.md` directly still works** and is
|
|
119
|
+
merged with the fragments at release time — it remains the right place to
|
|
120
|
+
restructure pending entries, and the escape hatch for anything the
|
|
121
|
+
fragment format can't express (e.g. an audience-qualified
|
|
122
|
+
`### Breaking (recipe authors only)` heading, which `breaking` fragments
|
|
123
|
+
will then join). Keep one `## Unreleased` heading — the release script
|
|
124
|
+
refuses more than one, since only the first is ever cut.
|
|
100
125
|
- **Releases** (maintainers): `npm version <patch|minor|major>` does the
|
|
101
|
-
whole cut — the `version` hook
|
|
102
|
-
`## X.Y.Z — YYYY-MM-DD`
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
whole cut — the `version` hook folds the pending fragments plus any
|
|
127
|
+
entries filed directly under `Unreleased` into `## X.Y.Z — YYYY-MM-DD`
|
|
128
|
+
(subsections emitted in `### Breaking` / `### Added` / `### Changed` /
|
|
129
|
+
`### Fixed` order), deletes the consumed fragments, keeps a fresh empty
|
|
130
|
+
`Unreleased` above, and refuses to release when there is nothing to
|
|
131
|
+
release; npm then commits and tags. `git push --follow-tags` triggers CI,
|
|
132
|
+
which refuses a tag with no matching changelog section, publishes
|
|
133
|
+
`@animalabs/connectome-host` to npm, and creates the GitHub release with
|
|
134
|
+
that section as its notes. The two release jobs are independent: some
|
|
135
|
+
consumers run github-clone checkouts, so release notes must exist even
|
|
136
|
+
when npm publish fails. Version bumps are a maintainer release-time
|
|
137
|
+
action, not part of feature PRs.
|
|
110
138
|
|
|
111
139
|
## Building and testing
|
|
112
140
|
|
package/README.md
CHANGED
|
@@ -118,6 +118,31 @@ device-code flow if needed. No `OPENAI_API_KEY` is used for this provider. Use
|
|
|
118
118
|
warns if the service reports that it fell back to Standard; Fast mode consumes
|
|
119
119
|
subscription credits at a higher rate when applied.
|
|
120
120
|
|
|
121
|
+
### OpenAI-compatible endpoints (Ollama, vLLM, Together, Groq, NanoGPT, ...)
|
|
122
|
+
|
|
123
|
+
Any server speaking the OpenAI chat-completions API works through the generic
|
|
124
|
+
`openai-compatible` provider — the recipe names the endpoint and the model:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"agent": {
|
|
129
|
+
"provider": "openai-compatible",
|
|
130
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
131
|
+
"model": "qwen3:32b",
|
|
132
|
+
"systemPrompt": "You are a helpful assistant."
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The API key is read from `OPENAI_COMPATIBLE_API_KEY` only — deliberately no
|
|
138
|
+
`OPENAI_API_KEY` fallback: `baseUrl` is recipe-controlled, and a real OpenAI
|
|
139
|
+
credential must never be sent silently to an arbitrary endpoint. Local
|
|
140
|
+
servers usually need none. `agent.model` is required —
|
|
141
|
+
there is no default model for an arbitrary endpoint. Tool calls use the
|
|
142
|
+
standard `tool_calls` format, so the endpoint must support function calling
|
|
143
|
+
for tool-using recipes. Provider-side prompt caching and cache accounting
|
|
144
|
+
depend on what the endpoint reports.
|
|
145
|
+
|
|
121
146
|
## What it provides
|
|
122
147
|
|
|
123
148
|
- **Web UI**: browser operator console (`modules.webui`) — live chat with full interiority (thinking, tool calls, streaming), agent/fleet tree, context makeup + compression coverage, call ledger with cache verdicts and billing-grade costs, health/ops alerts, Chronicle branch tree, lessons, MCPL config, workspace files; scoped read-only observer access via device keys
|
|
@@ -128,6 +153,7 @@ subscription credits at a higher rate when applied.
|
|
|
128
153
|
- **Session management**: Isolated sessions with auto-naming
|
|
129
154
|
- **MCPL support**: Connect any MCP/MCPL server; wake subscriptions for selective event triggering
|
|
130
155
|
- **File products**: Write reports and documents, materialize to disk
|
|
156
|
+
- **Shared instructions** (opt-in, `modules.instructions`): a living instructions document (CLAUDE.md analogue) kept in a workspace mount and injected into every agent's context on every turn — the resident agent and all ephemeral subagents. Edits take effect on the next turn; nothing is persisted to history. Defaults: path `instructions/AGENTS.md`, `position: "system"`, 32 KiB cap (reads are bounded to the cap); a missing file is fail-open (no injection, warn once), while a path naming a nonexistent mount fails at recipe load — including on the implicit default workspace (`input` + `products`), whose mount set can never satisfy the default path, so declare an `instructions` mount explicitly. **Who edits, and how it propagates**: the module reads *disk*; agent `workspace--write`/`edit` land in Chronicle and reach disk only on an `autoMaterialize: true` mount — validation therefore requires it on a read-write instructions mount. On a read-only mount the flow reverses: human/deploy edits to disk reach the injection, but not `workspace--read` (which serves Chronicle) — prefer routing human feedback through conversation and letting the agent make the edit. Symlinks that lead outside the mount are rejected (realpath containment), never injected. **Cache note**: at `position: "system"` the block lives in every agent's prompt-cache prefix, so each edit is a fleet-wide cache cold start on the next turn — curate in batches, or use `afterUser` for cache-cheap, lower-salience injection. Compared to **lessons** (`modules.lessons`): lessons are a structured, confidence-scored store with model-driven retrieval; instructions are one free-form curated document, always present verbatim
|
|
131
157
|
|
|
132
158
|
For `openai-responses` and `openai-codex`, an object-valued
|
|
133
159
|
`modules.retrieval` can set `reasoningEffort` (`none`, `minimal`, `low`,
|
|
@@ -156,6 +182,7 @@ npm install
|
|
|
156
182
|
|----------|---------|-------------|
|
|
157
183
|
| `ANTHROPIC_API_KEY` | (required) | Anthropic API key |
|
|
158
184
|
| `OPENAI_API_KEY` | — | OpenAI Platform key for `openai-responses` recipes |
|
|
185
|
+
| `OPENAI_COMPATIBLE_API_KEY` | — | Key for `openai-compatible` recipes (no `OPENAI_API_KEY` fallback by design); omit for local servers |
|
|
159
186
|
| `CODEX_BINARY` | `codex` | Codex CLI executable for `openai-codex` subscription auth |
|
|
160
187
|
| `CODEX_HOME` | `~/.codex` | Codex credential/config directory |
|
|
161
188
|
| `CODEX_BASE_URL` | ChatGPT Codex backend | Optional subscription transport override |
|