@animalabs/agent-framework 0.6.0 → 0.6.3

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 (115) hide show
  1. package/dist/src/agent.d.ts +4 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +15 -0
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +235 -2
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +1141 -92
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +20 -1
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +60 -4
  12. package/dist/src/gate/event-gate.js.map +1 -1
  13. package/dist/src/index.d.ts +3 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +4 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/mcpl/channel-registry.d.ts +17 -1
  18. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  19. package/dist/src/mcpl/channel-registry.js +34 -3
  20. package/dist/src/mcpl/channel-registry.js.map +1 -1
  21. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  22. package/dist/src/mcpl/push-handler.js +4 -0
  23. package/dist/src/mcpl/push-handler.js.map +1 -1
  24. package/dist/src/mcpl/server-connection.d.ts +29 -0
  25. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  26. package/dist/src/mcpl/server-connection.js +102 -4
  27. package/dist/src/mcpl/server-connection.js.map +1 -1
  28. package/dist/src/mcpl/types.d.ts +17 -1
  29. package/dist/src/mcpl/types.d.ts.map +1 -1
  30. package/dist/src/mcpl/types.js.map +1 -1
  31. package/dist/src/module-registry.d.ts +15 -1
  32. package/dist/src/module-registry.d.ts.map +1 -1
  33. package/dist/src/module-registry.js +92 -4
  34. package/dist/src/module-registry.js.map +1 -1
  35. package/dist/src/types/agent.d.ts +37 -0
  36. package/dist/src/types/agent.d.ts.map +1 -1
  37. package/dist/src/types/framework.d.ts +40 -0
  38. package/dist/src/types/framework.d.ts.map +1 -1
  39. package/dist/src/types/module.d.ts +46 -0
  40. package/dist/src/types/module.d.ts.map +1 -1
  41. package/dist/src/types/trace.d.ts +51 -0
  42. package/dist/src/types/trace.d.ts.map +1 -1
  43. package/dist/test/agent-cache-ttl.test.d.ts +2 -0
  44. package/dist/test/agent-cache-ttl.test.d.ts.map +1 -0
  45. package/dist/test/agent-cache-ttl.test.js +25 -0
  46. package/dist/test/agent-cache-ttl.test.js.map +1 -0
  47. package/dist/test/conversation-fork-injection-scoping.test.d.ts +2 -0
  48. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +1 -0
  49. package/dist/test/conversation-fork-injection-scoping.test.js +184 -0
  50. package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -0
  51. package/dist/test/gather-context-timeout.test.d.ts +2 -0
  52. package/dist/test/gather-context-timeout.test.d.ts.map +1 -0
  53. package/dist/test/gather-context-timeout.test.js +120 -0
  54. package/dist/test/gather-context-timeout.test.js.map +1 -0
  55. package/dist/test/inference-exhaustion-rewind.test.d.ts +2 -0
  56. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +1 -0
  57. package/dist/test/inference-exhaustion-rewind.test.js +181 -0
  58. package/dist/test/inference-exhaustion-rewind.test.js.map +1 -0
  59. package/dist/test/inference-failure-observability.test.js +9 -0
  60. package/dist/test/inference-failure-observability.test.js.map +1 -1
  61. package/dist/test/maintenance-timer.test.d.ts +2 -0
  62. package/dist/test/maintenance-timer.test.d.ts.map +1 -0
  63. package/dist/test/maintenance-timer.test.js +114 -0
  64. package/dist/test/maintenance-timer.test.js.map +1 -0
  65. package/dist/test/mcpl-reconnect-events.test.d.ts +2 -0
  66. package/dist/test/mcpl-reconnect-events.test.d.ts.map +1 -0
  67. package/dist/test/mcpl-reconnect-events.test.js +119 -0
  68. package/dist/test/mcpl-reconnect-events.test.js.map +1 -0
  69. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +2 -0
  70. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +1 -0
  71. package/dist/test/mcpl-reconnect-reregistration.test.js +181 -0
  72. package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -0
  73. package/dist/test/mcpl-request-timeout.test.d.ts +2 -0
  74. package/dist/test/mcpl-request-timeout.test.d.ts.map +1 -0
  75. package/dist/test/mcpl-request-timeout.test.js +123 -0
  76. package/dist/test/mcpl-request-timeout.test.js.map +1 -0
  77. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +7 -0
  78. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +1 -0
  79. package/dist/test/mcpl-runtime-lifecycle.test.js +142 -0
  80. package/dist/test/mcpl-runtime-lifecycle.test.js.map +1 -0
  81. package/dist/test/ops-alert.test.d.ts +2 -0
  82. package/dist/test/ops-alert.test.d.ts.map +1 -0
  83. package/dist/test/ops-alert.test.js +152 -0
  84. package/dist/test/ops-alert.test.js.map +1 -0
  85. package/dist/test/refusal-rewind.test.d.ts +2 -0
  86. package/dist/test/refusal-rewind.test.d.ts.map +1 -0
  87. package/dist/test/refusal-rewind.test.js +108 -0
  88. package/dist/test/refusal-rewind.test.js.map +1 -0
  89. package/dist/test/state-scaling-e2e.test.d.ts +22 -0
  90. package/dist/test/state-scaling-e2e.test.d.ts.map +1 -0
  91. package/dist/test/state-scaling-e2e.test.js +200 -0
  92. package/dist/test/state-scaling-e2e.test.js.map +1 -0
  93. package/dist/test/state-scaling.test.d.ts +12 -0
  94. package/dist/test/state-scaling.test.d.ts.map +1 -0
  95. package/dist/test/state-scaling.test.js +243 -0
  96. package/dist/test/state-scaling.test.js.map +1 -0
  97. package/dist/test/tool-result-content.test.d.ts +2 -0
  98. package/dist/test/tool-result-content.test.d.ts.map +1 -0
  99. package/dist/test/tool-result-content.test.js +37 -0
  100. package/dist/test/tool-result-content.test.js.map +1 -0
  101. package/dist/tsconfig.tsbuildinfo +1 -1
  102. package/package.json +3 -2
  103. package/src/agent.ts +16 -0
  104. package/src/framework.ts +1339 -99
  105. package/src/gate/event-gate.ts +56 -4
  106. package/src/index.ts +10 -0
  107. package/src/mcpl/channel-registry.ts +34 -3
  108. package/src/mcpl/push-handler.ts +4 -0
  109. package/src/mcpl/server-connection.ts +110 -4
  110. package/src/mcpl/types.ts +19 -1
  111. package/src/module-registry.ts +113 -4
  112. package/src/types/agent.ts +38 -0
  113. package/src/types/framework.ts +44 -0
  114. package/src/types/module.ts +52 -0
  115. package/src/types/trace.ts +67 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Whole-store scaling sweep (scaling campaign Finding 2, end-to-end gate).
3
+ *
4
+ * The unit gates in state-scaling.test.ts pin the four known writers by
5
+ * calling them directly. This test validates the REGIME instead: it drives a
6
+ * synthetic workload through the framework's public surface — external
7
+ * messages with tool rounds, ephemeral spawn-and-dispose cycles, process
8
+ * logging on — then sweeps EVERY state family the store accumulated and
9
+ * asserts none of them grows superlinearly. A quadratic writer added anywhere
10
+ * (the way logProcessEvent silently duplicated logInference's bug) fails this
11
+ * test the day it's introduced, without anyone having to know to test it.
12
+ *
13
+ * Family classification:
14
+ * - capped families (per-agent turn-checkpoint slots) legitimately ramp to
15
+ * a fixed cap and plateau — small-N slope fits are noisy on that shape
16
+ * (a ramp-heavy window fits as "rising"), so they get a hard max-record-
17
+ * size bound instead, which is the stronger invariant anyway.
18
+ * - everything else with enough records gets a log-log slope fit of
19
+ * bytes-per-record vs index; flat ≈ healthy, ≈1 ≈ quadratic aggregate.
20
+ */
21
+ import { describe, it, beforeEach, afterEach } from 'node:test';
22
+ import assert from 'node:assert';
23
+ import { tmpdir } from 'node:os';
24
+ import { mkdtempSync, rmSync } from 'node:fs';
25
+ import { join } from 'node:path';
26
+ import { AgentFramework } from '../src/index.js';
27
+ import { MockMembrane, createMockResponse } from './helpers/mock-membrane.js';
28
+ const SLOPE_LIMIT = 0.3;
29
+ const MIN_RECORDS_FOR_FIT = 8;
30
+ /** Bounded-by-design families get a hard per-record ceiling instead of a
31
+ * slope fit: the checkpoint tree's ops are O(key) and its periodic
32
+ * consolidation snapshots are O(live keys) — both far under this, while a
33
+ * whole-map rewrite under fleet load blows past it. */
34
+ const CAPPED_MAX_RECORD_BYTES = 16 * 1024;
35
+ const CAPPED_FAMILIES = [/^framework\/turn-checkpoints\//];
36
+ /** OLS slope of ln(bytes) on ln(index) — same fit latency.mjs uses. */
37
+ function logLogSlope(sizes) {
38
+ const pts = sizes.map((y, i) => ({ x: i + 1, y })).filter((p) => p.y > 0);
39
+ const lx = pts.map((p) => Math.log(p.x));
40
+ const ly = pts.map((p) => Math.log(p.y));
41
+ const mx = lx.reduce((a, b) => a + b, 0) / lx.length;
42
+ const my = ly.reduce((a, b) => a + b, 0) / ly.length;
43
+ let num = 0, den = 0;
44
+ for (let i = 0; i < lx.length; i++) {
45
+ num += (lx[i] - mx) * (ly[i] - my);
46
+ den += (lx[i] - mx) ** 2;
47
+ }
48
+ return den === 0 ? 0 : num / den;
49
+ }
50
+ /** All state_update payload sizes in append order, grouped by state id. */
51
+ function sweepStateFamilies(store) {
52
+ const families = new Map();
53
+ for (const id of store.getRecordIdsByType('state_update')) {
54
+ const record = store.getRecord(id);
55
+ if (!record)
56
+ continue;
57
+ const update = JSON.parse(record.payload.toString());
58
+ let sizes = families.get(update.state_id);
59
+ if (!sizes)
60
+ families.set(update.state_id, (sizes = []));
61
+ sizes.push(record.payload.length);
62
+ }
63
+ return families;
64
+ }
65
+ /** Minimal module providing one tool, so turns exercise real tool rounds. */
66
+ class EchoModule {
67
+ name = 'echo';
68
+ async start(_ctx) { }
69
+ async stop() { }
70
+ getTools() {
71
+ return [{
72
+ name: 'echo',
73
+ description: 'Echo the input back',
74
+ inputSchema: { type: 'object', properties: { message: { type: 'string' } } },
75
+ }];
76
+ }
77
+ async handleToolCall(call) {
78
+ return { success: true, data: call.input.message ?? '' };
79
+ }
80
+ async onProcess(event, _state) {
81
+ if (event.type === 'external-message') {
82
+ return {
83
+ addMessages: [{
84
+ participant: 'User',
85
+ content: [{ type: 'text', text: String(event.content) }],
86
+ }],
87
+ requestInference: true,
88
+ };
89
+ }
90
+ return {};
91
+ }
92
+ }
93
+ describe('whole-store scaling sweep (e2e)', () => {
94
+ let tempDir;
95
+ beforeEach(() => {
96
+ tempDir = mkdtempSync(join(tmpdir(), 'af-scaling-e2e-'));
97
+ });
98
+ afterEach(() => {
99
+ rmSync(tempDir, { recursive: true, force: true });
100
+ });
101
+ it('no state family grows superlinearly under a synthetic fleet workload', async () => {
102
+ const membrane = new MockMembrane();
103
+ const framework = await AgentFramework.create({
104
+ storePath: join(tempDir, 'e2e.chronicle'),
105
+ membrane: membrane.asMembrane(),
106
+ agents: [{ name: 'assistant', model: 'test-model', systemPrompt: 'You are a test assistant.' }],
107
+ modules: [new EchoModule()],
108
+ // Exercise the process log — it's registered append_log and had the
109
+ // same Set-rewrite bug as the inference log.
110
+ processLogging: { persist: true },
111
+ });
112
+ // Phase 1: persistent-agent turns, each with one tool round. Every turn
113
+ // writes: turn checkpoint, process log entries, inference log entry,
114
+ // message appends — the production write set.
115
+ const TURNS = 40;
116
+ for (let t = 0; t < TURNS; t++) {
117
+ membrane.pushResponse(createMockResponse([
118
+ { type: 'text', text: `Working on request ${t}.` },
119
+ { type: 'tool_use', id: `call-${t}`, name: 'echo--echo', input: { message: `payload ${t}` } },
120
+ ], 'tool_use'));
121
+ membrane.pushResponse(createMockResponse([
122
+ { type: 'text', text: `Done with request ${t}.` },
123
+ ]));
124
+ framework.pushEvent({
125
+ type: 'external-message',
126
+ source: 'e2e',
127
+ content: `Request number ${t}, please handle it.`,
128
+ metadata: {},
129
+ });
130
+ await framework.runUntilIdle();
131
+ }
132
+ // Phase 2: spawn-and-dispose ephemeral agents — the fleet regime that
133
+ // grew the old checkpoint map without bound.
134
+ const SPAWNS = 12;
135
+ for (let s = 0; s < SPAWNS; s++) {
136
+ membrane.pushResponse(createMockResponse([
137
+ { type: 'text', text: `Scout ${s} reporting: nothing to see.` },
138
+ ]));
139
+ const { agent, contextManager, cleanup } = await framework.createEphemeralAgent({
140
+ name: `e2e-scout-${s}`,
141
+ model: 'test-model',
142
+ systemPrompt: 'You are a scout.',
143
+ });
144
+ contextManager.addMessage('user', [{ type: 'text', text: `Scout task ${s}` }]);
145
+ const run = framework.runEphemeralToCompletion(agent, contextManager);
146
+ await framework.runUntilIdle();
147
+ await run;
148
+ cleanup();
149
+ }
150
+ const store = framework.getStore();
151
+ const families = sweepStateFamilies(store);
152
+ // The workload must actually have exercised the families this test
153
+ // exists to watch — otherwise a routing change could green-wash it.
154
+ const inferenceLog = families.get('framework/inference-log') ?? [];
155
+ const processLog = families.get('framework/process-log') ?? [];
156
+ assert.ok(inferenceLog.length >= TURNS, `inference log exercised (${inferenceLog.length} records)`);
157
+ assert.ok(processLog.length >= TURNS, `process log exercised (${processLog.length} records)`);
158
+ const checkpointTree = families.get('framework/turn-checkpoints/tree') ?? [];
159
+ assert.ok(checkpointTree.length >= TURNS + SPAWNS, `checkpoint tree exercised (${checkpointTree.length} records)`);
160
+ // Disposal actually removed the scouts' keys — the tree stays O(live
161
+ // agents), and no per-agent state registration leaked into chronicle's
162
+ // state index (which is rewritten + fsynced on every sync tick).
163
+ for (let s = 0; s < SPAWNS; s++) {
164
+ assert.strictEqual(store.treeGet('framework/turn-checkpoints/tree', `e2e-scout-${s}`), null, `disposed scout ${s} evicted from checkpoint tree`);
165
+ }
166
+ assert.ok(store.treeGet('framework/turn-checkpoints/tree', 'assistant'), 'persistent agent keeps its checkpoints');
167
+ const stateCount = store.listStates().length;
168
+ const checkpointStates = store.listStates().filter((s) => s.id.startsWith('framework/turn-checkpoints')).length;
169
+ assert.strictEqual(checkpointStates, 1, `exactly one checkpoint state registered (index has ${stateCount} states total)`);
170
+ // The sweep: every family, classified, no exemptions beyond the rules —
171
+ // and no silent caps: families too small for a slope fit are listed.
172
+ const violations = [];
173
+ const skipped = [];
174
+ for (const [stateId, sizes] of families) {
175
+ if (CAPPED_FAMILIES.some((re) => re.test(stateId))) {
176
+ const max = Math.max(...sizes);
177
+ if (max > CAPPED_MAX_RECORD_BYTES) {
178
+ violations.push(`${stateId}: capped family record hit ${max} B (limit ${CAPPED_MAX_RECORD_BYTES})`);
179
+ }
180
+ continue;
181
+ }
182
+ if (sizes.length < MIN_RECORDS_FOR_FIT) {
183
+ skipped.push(`${stateId} (${sizes.length})`);
184
+ continue;
185
+ }
186
+ const slope = logLogSlope(sizes);
187
+ if (slope >= SLOPE_LIMIT) {
188
+ violations.push(`${stateId}: bytes/record slope ${slope.toFixed(2)} over ${sizes.length} records ` +
189
+ `(${sizes[0]} B → ${sizes[sizes.length - 1]} B) — superlinear growth`);
190
+ }
191
+ }
192
+ if (skipped.length > 0) {
193
+ console.log(`[state-scaling-e2e] ${skipped.length} families below the ${MIN_RECORDS_FOR_FIT}-record fit threshold ` +
194
+ `(unaudited by the slope gate): ${skipped.join(', ')}`);
195
+ }
196
+ assert.deepStrictEqual(violations, [], `superlinear state families:\n ${violations.join('\n ')}`);
197
+ await framework.stop();
198
+ });
199
+ });
200
+ //# sourceMappingURL=state-scaling-e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-scaling-e2e.test.js","sourceRoot":"","sources":["../../test/state-scaling-e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Regression gates for the set-rewrite quadratic-states fixes (2026-07-08
3
+ * scaling campaign, Finding 2): persisted record size must be independent of
4
+ * accumulated content.
5
+ *
6
+ * Families covered:
7
+ * - framework/inference-log — appended, not Set-rewritten
8
+ * - framework/turn-checkpoints/* — per-agent slots, not one global map
9
+ * - modules/<name>/<log> — ModuleContext log-state API
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=state-scaling.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-scaling.test.d.ts","sourceRoot":"","sources":["../../test/state-scaling.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Regression gates for the set-rewrite quadratic-states fixes (2026-07-08
3
+ * scaling campaign, Finding 2): persisted record size must be independent of
4
+ * accumulated content.
5
+ *
6
+ * Families covered:
7
+ * - framework/inference-log — appended, not Set-rewritten
8
+ * - framework/turn-checkpoints/* — per-agent slots, not one global map
9
+ * - modules/<name>/<log> — ModuleContext log-state API
10
+ */
11
+ import { describe, it, beforeEach, afterEach } from 'node:test';
12
+ import assert from 'node:assert';
13
+ import { tmpdir } from 'node:os';
14
+ import { mkdtempSync, rmSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ import { JsStore } from '@animalabs/chronicle';
17
+ import { AgentFramework } from '../src/index.js';
18
+ import { MockMembrane } from './helpers/mock-membrane.js';
19
+ const INFERENCE_LOG_ID = 'framework/inference-log';
20
+ const TURN_CHECKPOINTS_ID = 'framework/turn-checkpoints';
21
+ const TURN_CHECKPOINTS_TREE_ID = 'framework/turn-checkpoints/tree';
22
+ /**
23
+ * Payload sizes of all state_update records for states matching `match`,
24
+ * in append order.
25
+ */
26
+ function stateUpdateSizes(store, match) {
27
+ const sizes = [];
28
+ for (const id of store.getRecordIdsByType('state_update')) {
29
+ const record = store.getRecord(id);
30
+ if (!record)
31
+ continue;
32
+ const update = JSON.parse(record.payload.toString());
33
+ if (match(update.state_id))
34
+ sizes.push(record.payload.length);
35
+ }
36
+ return sizes;
37
+ }
38
+ /** Assert the record family isn't growing: last record ≈ first record. */
39
+ function assertFlat(sizes, label, tolerance = 1.5) {
40
+ assert.ok(sizes.length >= 2, `${label}: expected ≥2 records, got ${sizes.length}`);
41
+ const first = sizes[0];
42
+ const last = sizes[sizes.length - 1];
43
+ assert.ok(last <= first * tolerance, `${label}: record size grew ${first} → ${last} bytes over ${sizes.length} records — ` +
44
+ `looks like accumulated content is being rewritten per update`);
45
+ }
46
+ describe('state scaling regression gates', () => {
47
+ let tempDir;
48
+ let membrane;
49
+ beforeEach(() => {
50
+ tempDir = mkdtempSync(join(tmpdir(), 'af-state-scaling-'));
51
+ membrane = new MockMembrane();
52
+ });
53
+ afterEach(() => {
54
+ rmSync(tempDir, { recursive: true, force: true });
55
+ });
56
+ async function createFramework(storePath) {
57
+ return AgentFramework.create({
58
+ storePath,
59
+ membrane: membrane.asMembrane(),
60
+ agents: [{ name: 'assistant', model: 'test-model', systemPrompt: 'test' }],
61
+ modules: [],
62
+ });
63
+ }
64
+ it('inference log: Nth record size is independent of N', async () => {
65
+ const storePath = join(tempDir, 'inference.chronicle');
66
+ const framework = await createFramework(storePath);
67
+ const store = framework.getStore();
68
+ const N = 30;
69
+ for (let i = 0; i < N; i++) {
70
+ framework.logInference({
71
+ timestamp: Date.now(),
72
+ agentName: 'assistant',
73
+ requestId: `req-${i}`,
74
+ success: true,
75
+ durationMs: 100,
76
+ request: { i },
77
+ response: { ok: true },
78
+ });
79
+ }
80
+ const entries = store.getStateJson(INFERENCE_LOG_ID);
81
+ assert.strictEqual(entries.length, N, 'all entries readable back');
82
+ const sizes = stateUpdateSizes(store, (id) => id === INFERENCE_LOG_ID);
83
+ assertFlat(sizes, INFERENCE_LOG_ID);
84
+ await framework.stop();
85
+ });
86
+ it('inference log: appends compose with a legacy Set-written array', async () => {
87
+ const storePath = join(tempDir, 'legacy-log.chronicle');
88
+ // Write the state the old way: whole-array Set, as pre-fix stores did.
89
+ {
90
+ const store = JsStore.openOrCreate({ path: storePath });
91
+ store.registerState({
92
+ id: INFERENCE_LOG_ID,
93
+ strategy: 'append_log',
94
+ deltaSnapshotEvery: 100,
95
+ fullSnapshotEvery: 20,
96
+ });
97
+ store.setStateJson(INFERENCE_LOG_ID, [
98
+ { timestamp: 1, agentName: 'old', requestId: 'legacy-0', success: true },
99
+ { timestamp: 2, agentName: 'old', requestId: 'legacy-1', success: false },
100
+ ]);
101
+ store.close();
102
+ }
103
+ const framework = await createFramework(storePath);
104
+ framework.logInference({
105
+ timestamp: 3,
106
+ agentName: 'assistant',
107
+ requestId: 'new-0',
108
+ success: true,
109
+ durationMs: 5,
110
+ });
111
+ const entries = framework.getStore().getStateJson(INFERENCE_LOG_ID);
112
+ assert.strictEqual(entries.length, 3);
113
+ assert.strictEqual(entries[0].requestId, 'legacy-0');
114
+ assert.strictEqual(entries[2].requestId, 'new-0');
115
+ await framework.stop();
116
+ });
117
+ it('turn checkpoints: record size and state count independent of agents-ever-spawned', async () => {
118
+ const storePath = join(tempDir, 'checkpoints.chronicle');
119
+ const framework = await createFramework(storePath);
120
+ const store = framework.getStore();
121
+ // M spawn-and-dispose subagents, several turns each.
122
+ const M = 25;
123
+ for (let m = 0; m < M; m++) {
124
+ const name = `spawn-task-d1-${1000 + m}`;
125
+ for (let turn = 0; turn < 3; turn++) {
126
+ framework.recordTurnCheckpoint(name);
127
+ }
128
+ framework.evictTurnCheckpoints(name);
129
+ }
130
+ // A persistent agent keeps taking turns throughout.
131
+ for (let turn = 0; turn < 5; turn++) {
132
+ framework.recordTurnCheckpoint('assistant');
133
+ }
134
+ // Every write is one tree op (path + blob hash): flat across the whole
135
+ // run even though M unique agents came and went.
136
+ const sizes = stateUpdateSizes(store, (id) => id === TURN_CHECKPOINTS_TREE_ID);
137
+ assertFlat(sizes, TURN_CHECKPOINTS_TREE_ID, 4);
138
+ // Disposal removed the key outright — the tree holds only live agents...
139
+ for (let m = 0; m < M; m++) {
140
+ assert.strictEqual(store.treeGet(TURN_CHECKPOINTS_TREE_ID, `spawn-task-d1-${1000 + m}`), null, 'disposed agent key removed from checkpoint tree');
141
+ }
142
+ assert.strictEqual(framework.getTurnCheckpoints('assistant').length, 5);
143
+ // ...and no per-agent state registration leaked into the state index —
144
+ // chronicle has no deregistration and rewrites + fsyncs the index on
145
+ // every sync tick, so index entries must not scale with spawns-ever.
146
+ const checkpointStates = store.listStates().filter((s) => s.id.startsWith(TURN_CHECKPOINTS_ID));
147
+ assert.strictEqual(checkpointStates.length, 1, 'exactly one checkpoint state registered');
148
+ await framework.stop();
149
+ });
150
+ it('turn checkpoints: legacy single-map stores are readable, migrate on save, and evict without ghosts', async () => {
151
+ const storePath = join(tempDir, 'legacy-checkpoints.chronicle');
152
+ {
153
+ const store = JsStore.openOrCreate({ path: storePath });
154
+ store.registerState({ id: TURN_CHECKPOINTS_ID, strategy: 'snapshot' });
155
+ store.setStateJson(TURN_CHECKPOINTS_ID, {
156
+ assistant: [
157
+ { agentName: 'assistant', turnIndex: 0, sequenceBefore: 1, branchName: 'main', timestamp: 1 },
158
+ ],
159
+ 'spawn-ghost-d1-1234': [
160
+ { agentName: 'spawn-ghost-d1-1234', turnIndex: 3, sequenceBefore: 9, branchName: 'main', timestamp: 2 },
161
+ ],
162
+ });
163
+ store.close();
164
+ }
165
+ const framework = await createFramework(storePath);
166
+ const store = framework.getStore();
167
+ // Legacy entry visible through the fallback read...
168
+ const before = framework.getTurnCheckpoints('assistant');
169
+ assert.strictEqual(before.length, 1);
170
+ assert.strictEqual(before[0].turnIndex, 0);
171
+ // ...and the first new turn migrates the list into the tree.
172
+ framework.recordTurnCheckpoint('assistant');
173
+ const entry = store.treeGet(TURN_CHECKPOINTS_TREE_ID, 'assistant');
174
+ assert.ok(entry, 'tree entry created on first save');
175
+ const migrated = framework.getTurnCheckpoints('assistant');
176
+ assert.strictEqual(migrated.length, 2);
177
+ assert.strictEqual(migrated[0].turnIndex, 0);
178
+ // Evicting an agent whose checkpoints live only in the legacy map writes
179
+ // an empty tombstone — a later agent reusing the name must NOT inherit
180
+ // the dead agent's checkpoints through the fallback.
181
+ framework.evictTurnCheckpoints('spawn-ghost-d1-1234');
182
+ assert.deepStrictEqual(framework.getTurnCheckpoints('spawn-ghost-d1-1234'), []);
183
+ // Idempotent: a second evict doesn't rewrite the tombstone (blob "[]" is
184
+ // content-addressed, but the tree op record would still be new).
185
+ const recordsBefore = store.getRecordIdsByType('state_update').length;
186
+ framework.evictTurnCheckpoints('spawn-ghost-d1-1234');
187
+ assert.strictEqual(store.getRecordIdsByType('state_update').length, recordsBefore);
188
+ await framework.stop();
189
+ });
190
+ it('module log states: append/edit records are O(item), state is reconstructable', async () => {
191
+ const storePath = join(tempDir, 'module-log.chronicle');
192
+ let capturedCtx = null;
193
+ const logModule = {
194
+ name: 'logtest',
195
+ async start(ctx) {
196
+ capturedCtx = ctx;
197
+ ctx.registerLogState('log');
198
+ },
199
+ async stop() { },
200
+ getTools() { return []; },
201
+ async handleToolCall(_call) {
202
+ return { success: true };
203
+ },
204
+ async onProcess(_event, _state) {
205
+ return {};
206
+ },
207
+ };
208
+ const framework = await AgentFramework.create({
209
+ storePath,
210
+ membrane: membrane.asMembrane(),
211
+ agents: [{ name: 'assistant', model: 'test-model', systemPrompt: 'test' }],
212
+ modules: [logModule],
213
+ });
214
+ const ctx = capturedCtx;
215
+ const store = framework.getStore();
216
+ const N = 30;
217
+ for (let i = 0; i < N; i++) {
218
+ ctx.appendToLog('log', { id: `item-${i}`, content: 'x'.repeat(50) });
219
+ }
220
+ assert.strictEqual(ctx.getLogLength('log'), N);
221
+ // Point-edit keeps the record O(item), not O(log).
222
+ ctx.editLogItem('log', 3, { id: 'item-3', content: 'edited' });
223
+ const items = ctx.getLog('log');
224
+ assert.strictEqual(items.length, N);
225
+ assert.strictEqual(items[3].content, 'edited');
226
+ assert.strictEqual(items[4].id, 'item-4');
227
+ const sizes = stateUpdateSizes(store, (id) => id === 'modules/logtest/log');
228
+ assertFlat(sizes, 'modules/logtest/log');
229
+ // Idempotent re-registration must not throw (restart path).
230
+ ctx.registerLogState('log');
231
+ // Reserved / invalid names are rejected.
232
+ assert.throws(() => ctx.registerLogState('state'));
233
+ assert.throws(() => ctx.appendToLog('nested/name', {}));
234
+ // Writes to a never-registered name fail immediately — chronicle would
235
+ // accept the append but never snapshot the state, turning a typo into a
236
+ // silent full-chain-replay-on-restore trap.
237
+ assert.throws(() => ctx.appendToLog('lgo', { oops: true }), /not registered/);
238
+ assert.throws(() => ctx.editLogItem('lgo', 0, {}), /not registered/);
239
+ assert.strictEqual(ctx.getLogLength('log'), N, 'registered log still intact');
240
+ await framework.stop();
241
+ });
242
+ });
243
+ //# sourceMappingURL=state-scaling.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-scaling.test.js","sourceRoot":"","sources":["../../test/state-scaling.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tool-result-content.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-result-content.test.d.ts","sourceRoot":"","sources":["../../test/tool-result-content.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * toMembraneToolResult (6.6): JSON.stringify(undefined) returns the VALUE
3
+ * undefined, not a string — a tool returning `{ success: true }` with no data
4
+ * plus a configured maxChars cap used to throw a TypeError on content.length
5
+ * mid-turn. It must degrade to a safe empty string instead.
6
+ */
7
+ import { test } from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+ import { AgentFramework } from '../src/framework.js';
10
+ function makeFw() {
11
+ return Object.create(AgentFramework.prototype);
12
+ }
13
+ test('undefined data + maxChars cap: no throw, content is a safe string', () => {
14
+ const fw = makeFw();
15
+ const result = fw.toMembraneToolResult('call-1', { success: true }, 1000);
16
+ assert.equal(result.toolUseId, 'call-1');
17
+ assert.equal(result.isError, false);
18
+ assert.equal(result.content, '');
19
+ });
20
+ test('undefined data without a cap also yields a string (never the value undefined)', () => {
21
+ const fw = makeFw();
22
+ const result = fw.toMembraneToolResult('call-2', { success: true, data: undefined });
23
+ assert.equal(typeof result.content, 'string');
24
+ });
25
+ test('normal data is still stringified and truncated by the cap', () => {
26
+ const fw = makeFw();
27
+ const result = fw.toMembraneToolResult('call-3', { success: true, data: { v: 'y'.repeat(500) } }, 100);
28
+ assert.equal(typeof result.content, 'string');
29
+ assert.match(result.content, /\[truncated — original was \d+ chars\]/);
30
+ });
31
+ test('error results pass through unchanged', () => {
32
+ const fw = makeFw();
33
+ const result = fw.toMembraneToolResult('call-4', { success: false, error: 'boom', isError: true }, 100);
34
+ assert.equal(result.isError, true);
35
+ assert.equal(result.content, 'boom');
36
+ });
37
+ //# sourceMappingURL=tool-result-content.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-result-content.test.js","sourceRoot":"","sources":["../../test/tool-result-content.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,SAAS,MAAM;IACb,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAQ,CAAC;AACxD,CAAC;AAED,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;IAC7E,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,EAAE,CAAC,oBAAoB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+EAA+E,EAAE,GAAG,EAAE;IACzF,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,EAAE,CAAC,oBAAoB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACrF,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,EAAE,CAAC,oBAAoB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IACvG,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,wCAAwC,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,EAAE,CAAC,oBAAoB,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IACxG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC"}