@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
|
@@ -120,7 +120,8 @@ export class McplAdminModule implements Module {
|
|
|
120
120
|
description:
|
|
121
121
|
'List all MCPL servers: connection/retry state, whether policy was established, ' +
|
|
122
122
|
'the effective grant, masked/denied capability paths, host-command authority, ' +
|
|
123
|
-
'tool count, target,
|
|
123
|
+
'validated manifest revision/fetch/negotiation freshness, tool count, target, ' +
|
|
124
|
+
'and config source.',
|
|
124
125
|
inputSchema: { type: 'object', properties: {} },
|
|
125
126
|
},
|
|
126
127
|
{
|
|
@@ -130,7 +131,14 @@ export class McplAdminModule implements Module {
|
|
|
130
131
|
'restarts) and hot-connect it now — its tools become available immediately. ' +
|
|
131
132
|
'If a server with this id is already running it is restarted with the new ' +
|
|
132
133
|
'config. Provide either `command` (stdio, spawned as the host user) or `url` ' +
|
|
133
|
-
'(WebSocket). Relative ./ args resolve against the host working directory.'
|
|
134
|
+
'(WebSocket). Relative ./ args resolve against the host working directory. ' +
|
|
135
|
+
'Sensible defaults: omit (or pass empty) the list fields and every feature ' +
|
|
136
|
+
'set and tool the server offers is available; an empty array means ' +
|
|
137
|
+
'"unspecified", never deny-all (deny-all is disabledTools/' +
|
|
138
|
+
'disabledFeatureSets: ["*"]). Self-deployed servers get channels + tools ' +
|
|
139
|
+
'only — consequential capabilities (context hooks around your inference, ' +
|
|
140
|
+
'server-initiated inference, inference lifecycle) are host-masked; a server ' +
|
|
141
|
+
'that genuinely needs one is an operator conversation, not a deploy flag.',
|
|
134
142
|
inputSchema: {
|
|
135
143
|
type: 'object',
|
|
136
144
|
properties: {
|
|
@@ -142,10 +150,10 @@ export class McplAdminModule implements Module {
|
|
|
142
150
|
token: { type: 'string', description: 'Bearer token for WebSocket auth (only when the operator hands you one — prefer `access`).' },
|
|
143
151
|
access: { type: 'string', description: 'Name of a host-managed access grant (e.g. "eidoverse"): the host attaches your standing credentials to the connection automatically. Nothing for you to obtain or handle.' },
|
|
144
152
|
toolPrefix: { type: 'string', description: 'Tool namespace prefix. Default: mcpl--<id>.' },
|
|
145
|
-
reconnect: { type: 'boolean', description: 'Auto-reconnect on transport failure (
|
|
146
|
-
enabledFeatureSets: { type: 'array', items: { type: 'string' } },
|
|
147
|
-
disabledFeatureSets: { type: 'array', items: { type: 'string' } },
|
|
148
|
-
enabledTools: { type: 'array', items: { type: 'string' }, description: 'Tool allow-list (bare names, * wildcard).' },
|
|
153
|
+
reconnect: { type: 'boolean', description: 'Auto-reconnect on transport failure. Default: true for websocket URLs (a bounced server comes back on its own), false for stdio. Note: does NOT respawn a crashed child — use mcpl_restart for that.' },
|
|
154
|
+
enabledFeatureSets: { type: 'array', items: { type: 'string' }, description: 'Feature-set allowlist (* wildcard). Omit or pass [] for all offered.' },
|
|
155
|
+
disabledFeatureSets: { type: 'array', items: { type: 'string' }, description: 'Feature-set deny-list; wins over enabled.' },
|
|
156
|
+
enabledTools: { type: 'array', items: { type: 'string' }, description: 'Tool allow-list (bare names, * wildcard). Omit or pass [] for all offered.' },
|
|
149
157
|
disabledTools: { type: 'array', items: { type: 'string' }, description: 'Tool deny-list; wins over enabledTools.' },
|
|
150
158
|
},
|
|
151
159
|
required: ['id'],
|
|
@@ -239,6 +247,11 @@ export class McplAdminModule implements Module {
|
|
|
239
247
|
maskedCapabilities?: string[];
|
|
240
248
|
deniedCapabilities?: string[];
|
|
241
249
|
allowHostCommands?: boolean;
|
|
250
|
+
manifestState?: {
|
|
251
|
+
lastValidatedRevision: string | null;
|
|
252
|
+
lastFetchedAt: number | null;
|
|
253
|
+
lastNegotiatedAt: number | null;
|
|
254
|
+
};
|
|
242
255
|
}
|
|
243
256
|
>;
|
|
244
257
|
const overlay = readAgentOverlay(this.overlayPath);
|
|
@@ -262,7 +275,8 @@ export class McplAdminModule implements Module {
|
|
|
262
275
|
`grant=${formatCapabilityList(s.effectiveGrant)}, ` +
|
|
263
276
|
`masked=${formatCapabilityList(s.maskedCapabilities)}, ` +
|
|
264
277
|
`denied=${formatCapabilityList(s.deniedCapabilities)}, ` +
|
|
265
|
-
`hostCommands=${hostCommands}
|
|
278
|
+
`hostCommands=${hostCommands}, ` +
|
|
279
|
+
`manifest=${formatManifestState(s.manifestState)}; ${s.toolCount} tools, ` +
|
|
266
280
|
`prefix=${s.toolPrefix}, source=${source}, ${target}`,
|
|
267
281
|
);
|
|
268
282
|
}
|
|
@@ -314,10 +328,17 @@ export class McplAdminModule implements Module {
|
|
|
314
328
|
}
|
|
315
329
|
if (typeof input.toolPrefix === 'string') entry.toolPrefix = input.toolPrefix;
|
|
316
330
|
if (typeof input.reconnect === 'boolean') entry.reconnect = input.reconnect;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
331
|
+
// Empty arrays are NOT persisted: OpenAI-style strict function calling
|
|
332
|
+
// forces every schema property, so callers emit `[]` meaning
|
|
333
|
+
// "unspecified" — and a persisted empty ALLOWLIST is deny-all under the
|
|
334
|
+
// §5.3 pin (Mica's silently eventless eidoverse, 2026-08-04).
|
|
335
|
+
// resolveOverlayEntry drops them at read time too; this keeps the file
|
|
336
|
+
// itself from carrying the trap. Deny-all is spelled ["*"] on the
|
|
337
|
+
// deny-lists.
|
|
338
|
+
if (Array.isArray(input.enabledFeatureSets) && input.enabledFeatureSets.length) entry.enabledFeatureSets = input.enabledFeatureSets.map(String);
|
|
339
|
+
if (Array.isArray(input.disabledFeatureSets) && input.disabledFeatureSets.length) entry.disabledFeatureSets = input.disabledFeatureSets.map(String);
|
|
340
|
+
if (Array.isArray(input.enabledTools) && input.enabledTools.length) entry.enabledTools = input.enabledTools.map(String);
|
|
341
|
+
if (Array.isArray(input.disabledTools) && input.disabledTools.length) entry.disabledTools = input.disabledTools.map(String);
|
|
321
342
|
|
|
322
343
|
// Persist to the overlay first — a connect failure still leaves the entry
|
|
323
344
|
// in place so the agent can fix the server and mcpl_restart it.
|
|
@@ -407,3 +428,29 @@ function formatCapabilityList(paths: string[] | undefined): string {
|
|
|
407
428
|
if (paths === undefined) return 'unknown';
|
|
408
429
|
return `[${paths.join(',')}]`;
|
|
409
430
|
}
|
|
431
|
+
|
|
432
|
+
function formatManifestState(state: {
|
|
433
|
+
lastValidatedRevision: string | null;
|
|
434
|
+
lastFetchedAt: number | null;
|
|
435
|
+
lastNegotiatedAt: number | null;
|
|
436
|
+
} | undefined): string {
|
|
437
|
+
if (state === undefined) return 'unknown';
|
|
438
|
+
return `{revision=${formatManifestRevision(state.lastValidatedRevision)},` +
|
|
439
|
+
`fetchedAt=${formatManifestTimestamp(state.lastFetchedAt)},` +
|
|
440
|
+
`negotiatedAt=${formatManifestTimestamp(state.lastNegotiatedAt)}}`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Bound and quote the server-authored, equality-only revision before putting
|
|
444
|
+
* it on a model-facing text surface. Conforming revisions fit well below the
|
|
445
|
+
* limit; malformed peers cannot inject control lines or unbounded text. */
|
|
446
|
+
function formatManifestRevision(revision: string | null): string {
|
|
447
|
+
if (revision === null) return 'none';
|
|
448
|
+
const bounded = revision.length <= 64 ? revision : `${revision.slice(0, 61)}...`;
|
|
449
|
+
return JSON.stringify(bounded);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function formatManifestTimestamp(timestamp: number | null): string {
|
|
453
|
+
if (timestamp === null) return 'none';
|
|
454
|
+
const date = new Date(timestamp);
|
|
455
|
+
return Number.isNaN(date.getTime()) ? 'invalid' : date.toISOString();
|
|
456
|
+
}
|
|
@@ -1376,6 +1376,18 @@ export class SubagentModule implements Module {
|
|
|
1376
1376
|
* 3. parent agent's `maxTokens` (by default, subagents inherit their caller's budget)
|
|
1377
1377
|
* 4. last-resort framework fallback (4096) — only reached if there's no parent at all
|
|
1378
1378
|
*/
|
|
1379
|
+
/** Ephemeral agents inherit the caller's prose-routing mode. AF's Agent
|
|
1380
|
+
* defaults to 'locus' (ambient locus capture publishes plain prose to the
|
|
1381
|
+
* open channel), so a resident running proseRouting 'disabled' would still
|
|
1382
|
+
* spawn divers whose between-tool-calls prose leaks into its live channel
|
|
1383
|
+
* as parent speech — field-confirmed 2026-08-26. Falls back to the
|
|
1384
|
+
* configured parent agent, then to AF's own default. */
|
|
1385
|
+
private resolveProseRouting(callerAgentName?: string): 'locus' | 'explicit' | 'hybrid' | 'disabled' | undefined {
|
|
1386
|
+
const parentName = callerAgentName ?? this.config.parentAgentName;
|
|
1387
|
+
if (!parentName) return undefined;
|
|
1388
|
+
return this.framework?.getAgent(parentName)?.proseRouting;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1379
1391
|
private resolveMaxTokens(callMaxTokens: number | undefined, parentAgentName?: string): number {
|
|
1380
1392
|
if (callMaxTokens !== undefined) return callMaxTokens;
|
|
1381
1393
|
if (this.config.defaultMaxTokens !== undefined) return this.config.defaultMaxTokens;
|
|
@@ -1703,6 +1715,9 @@ export class SubagentModule implements Module {
|
|
|
1703
1715
|
model,
|
|
1704
1716
|
systemPrompt: input.systemPrompt,
|
|
1705
1717
|
maxTokens: this.resolveMaxTokens(input.maxTokens, _callerAgentName),
|
|
1718
|
+
...(this.resolveProseRouting(_callerAgentName) !== undefined
|
|
1719
|
+
? { proseRouting: this.resolveProseRouting(_callerAgentName) }
|
|
1720
|
+
: {}),
|
|
1706
1721
|
maxStreamTokens: 500_000,
|
|
1707
1722
|
strategy: new KnowledgeStrategy({
|
|
1708
1723
|
headWindowTokens: 2_000,
|
|
@@ -1855,6 +1870,9 @@ export class SubagentModule implements Module {
|
|
|
1855
1870
|
model,
|
|
1856
1871
|
systemPrompt,
|
|
1857
1872
|
maxTokens: this.resolveMaxTokens(input.maxTokens, callerAgentName),
|
|
1873
|
+
...(this.resolveProseRouting(callerAgentName) !== undefined
|
|
1874
|
+
? { proseRouting: this.resolveProseRouting(callerAgentName) }
|
|
1875
|
+
: {}),
|
|
1858
1876
|
maxStreamTokens: 500_000,
|
|
1859
1877
|
strategy: new KnowledgeStrategy({
|
|
1860
1878
|
headWindowTokens: 2_000,
|