@animalabs/agent-framework 0.14.0 → 0.15.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 (62) hide show
  1. package/dist/src/agent.d.ts +10 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +39 -0
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +5 -2
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +126 -36
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/modules/history/index.d.ts +69 -0
  10. package/dist/src/modules/history/index.d.ts.map +1 -0
  11. package/dist/src/modules/history/index.js +519 -0
  12. package/dist/src/modules/history/index.js.map +1 -0
  13. package/dist/src/modules/history/search-regex-worker.d.ts +23 -0
  14. package/dist/src/modules/history/search-regex-worker.d.ts.map +1 -0
  15. package/dist/src/modules/history/search-regex-worker.js +57 -0
  16. package/dist/src/modules/history/search-regex-worker.js.map +1 -0
  17. package/dist/src/modules/index.d.ts +1 -0
  18. package/dist/src/modules/index.d.ts.map +1 -1
  19. package/dist/src/modules/index.js +1 -0
  20. package/dist/src/modules/index.js.map +1 -1
  21. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  22. package/dist/src/modules/workspace/index.js +42 -1
  23. package/dist/src/modules/workspace/index.js.map +1 -1
  24. package/dist/src/modules/workspace/types.d.ts +5 -0
  25. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  26. package/dist/src/modules/workspace/types.js.map +1 -1
  27. package/dist/src/types/trace.d.ts +4 -0
  28. package/dist/src/types/trace.d.ts.map +1 -1
  29. package/dist/test/history-module.test.d.ts +2 -0
  30. package/dist/test/history-module.test.d.ts.map +1 -0
  31. package/dist/test/history-module.test.js +449 -0
  32. package/dist/test/history-module.test.js.map +1 -0
  33. package/dist/test/kv-unified-open-flight-retry.test.d.ts +2 -0
  34. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +1 -0
  35. package/dist/test/kv-unified-open-flight-retry.test.js +189 -0
  36. package/dist/test/kv-unified-open-flight-retry.test.js.map +1 -0
  37. package/dist/test/kv-unified-wiring.test.js +39 -0
  38. package/dist/test/kv-unified-wiring.test.js.map +1 -1
  39. package/dist/test/puppet-tool-call.test.js +113 -1
  40. package/dist/test/puppet-tool-call.test.js.map +1 -1
  41. package/dist/test/puppet-turn-reservation.test.d.ts +2 -0
  42. package/dist/test/puppet-turn-reservation.test.d.ts.map +1 -0
  43. package/dist/test/puppet-turn-reservation.test.js +139 -0
  44. package/dist/test/puppet-turn-reservation.test.js.map +1 -0
  45. package/dist/test/save-recent-image.test.js +29 -0
  46. package/dist/test/save-recent-image.test.js.map +1 -1
  47. package/dist/test/tool-result-spill.test.js +114 -0
  48. package/dist/test/tool-result-spill.test.js.map +1 -1
  49. package/dist/test/workspace-read-text.test.d.ts +2 -0
  50. package/dist/test/workspace-read-text.test.d.ts.map +1 -0
  51. package/dist/test/workspace-read-text.test.js +101 -0
  52. package/dist/test/workspace-read-text.test.js.map +1 -0
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +3 -3
  55. package/src/agent.ts +41 -0
  56. package/src/framework.ts +132 -43
  57. package/src/modules/history/index.ts +613 -0
  58. package/src/modules/history/search-regex-worker.ts +76 -0
  59. package/src/modules/index.ts +2 -0
  60. package/src/modules/workspace/index.ts +42 -1
  61. package/src/modules/workspace/types.ts +5 -0
  62. package/src/types/trace.ts +4 -0
@@ -0,0 +1,189 @@
1
+ /**
2
+ * kv-unified receipt flight vs. the framework's error-policy retry.
3
+ *
4
+ * Field incident (devops agent on gpt-6-astra, 2026-09-16 10:42 local):
5
+ * [inference-failed] kv-unified submission devops:46:1789544019150:4 is still in flight
6
+ *
7
+ * Membrane fires the cache-wire receipt once per provider attempt, BEFORE the
8
+ * adapter call, and the framework only settles that flight on the attempt's
9
+ * usage event (accept) or in driveStream's `finally` (fail). A provider call
10
+ * that dies before its usage event therefore leaves the flight open until the
11
+ * stream is torn down — but the error-policy retry starts the successor stream
12
+ * from INSIDE the failed stream's event loop, before that `finally` runs. The
13
+ * successor's first receipt then hits the ledger's single-flight guard and the
14
+ * retry itself fails with "is still in flight", turning one transient provider
15
+ * error into a lost turn.
16
+ */
17
+ import { test } from 'node:test';
18
+ import assert from 'node:assert/strict';
19
+ import { mkdtempSync, rmSync } from 'node:fs';
20
+ import { tmpdir } from 'node:os';
21
+ import { join } from 'node:path';
22
+ import { AutobiographicalStrategy } from '@animalabs/context-manager';
23
+ import { AgentFramework } from '../src/index.js';
24
+ import { createMockResponse, MockMembrane } from './helpers/mock-membrane.js';
25
+ /** Mirrors membrane's yielding stream closely enough for the receipt seam:
26
+ * the wire receipt fires inside streamOnce, after `await applyBeforeRequestHook`,
27
+ * i.e. a microtask or two after the consumer's first pull, and before any
28
+ * usage event. A receipt hook that throws surfaces as an `error` event (the
29
+ * real stream's startInference catch), never as a thrown `next()`. */
30
+ class ScriptedStream {
31
+ onFirstPull;
32
+ script;
33
+ done = false;
34
+ constructor(onFirstPull, script) {
35
+ this.onFirstPull = onFirstPull;
36
+ this.script = script;
37
+ }
38
+ cancel() { this.done = true; }
39
+ get isWaitingForTools() { return false; }
40
+ get pendingToolCallIds() { return []; }
41
+ get toolDepth() { return 0; }
42
+ provideToolResults() { throw new Error('ScriptedStream is not waiting for tools'); }
43
+ async *[Symbol.asyncIterator]() {
44
+ await Promise.resolve();
45
+ await Promise.resolve();
46
+ try {
47
+ this.onFirstPull();
48
+ }
49
+ catch (error) {
50
+ yield { type: 'error', error: error };
51
+ return;
52
+ }
53
+ for (const event of this.script) {
54
+ if (this.done)
55
+ return;
56
+ yield event;
57
+ }
58
+ }
59
+ }
60
+ /** First provider attempt dies after the receipt fired and before any usage
61
+ * event (a transport error); every later attempt completes normally. */
62
+ class DeadThenAliveMembrane extends MockMembrane {
63
+ order = [];
64
+ attempts = 0;
65
+ streamYielding(request) {
66
+ this.calls.push(request);
67
+ const attempt = this.attempts++;
68
+ const hooks = request;
69
+ const fireReceipt = () => {
70
+ this.order.push(`pull:${attempt}`);
71
+ hooks.onCacheWireReceipt?.({ requestHash: `wire-${attempt}`, markers: [] });
72
+ this.order.push(`receipt:${attempt}`);
73
+ };
74
+ const script = attempt === 0
75
+ ? [{ type: 'error', error: new Error('socket hang up') }]
76
+ : [
77
+ { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } },
78
+ {
79
+ type: 'complete',
80
+ response: createMockResponse([{ type: 'text', text: 'recovered' }]),
81
+ },
82
+ ];
83
+ return new ScriptedStream(fireReceipt, script);
84
+ }
85
+ }
86
+ class WakeModule {
87
+ name = 'wake';
88
+ async start(_ctx) { }
89
+ async stop() { }
90
+ getTools() { return []; }
91
+ async handleToolCall(_call) {
92
+ return { success: false, error: 'no tools', isError: true };
93
+ }
94
+ async onProcess(event, _state) {
95
+ if (event.type !== 'external-message')
96
+ return {};
97
+ return {
98
+ addMessages: [{ participant: 'User', content: [{ type: 'text', text: String(event.content) }] }],
99
+ requestInference: true,
100
+ };
101
+ }
102
+ }
103
+ class RetryOncePolicy {
104
+ maxRetries = 1;
105
+ onInferenceError(_error, _agentName, attempt) {
106
+ return attempt < this.maxRetries ? { retry: true, delayMs: 0 } : { retry: false };
107
+ }
108
+ }
109
+ function kvUnifiedStrategy() {
110
+ return new AutobiographicalStrategy({
111
+ adaptiveResolution: true,
112
+ foldingStrategy: 'kv-unified',
113
+ headWindowTokens: 0,
114
+ recentWindowTokens: 100,
115
+ kvUnified: {
116
+ policy: {
117
+ alpha: 0.7,
118
+ budgetLowRatio: 0.5,
119
+ budgetHighRatio: 0.9,
120
+ budgetUnderLambda: 10,
121
+ budgetOverLambda: 10,
122
+ cacheLambda: 1,
123
+ cacheScale: 1000,
124
+ cacheReadPrice: 0.1,
125
+ cacheWritePrice: 1.25,
126
+ continuityLambda: 1,
127
+ continuityScale: 1000,
128
+ continuityRecencyHalfLifeTokens: 1000,
129
+ continuityRecencyFloor: 0.2,
130
+ continuityStableHalfLife: 10,
131
+ continuityStableFloor: 0.25,
132
+ },
133
+ tokenBucketSize: 100,
134
+ continuityBucketSize: 100,
135
+ fidelityBucketSize: 100,
136
+ labelCeiling: 10_000,
137
+ adoptEpsilon: 0,
138
+ treeifyNonContiguousSummaries: false,
139
+ preserveGapBearingSummaries: true,
140
+ },
141
+ });
142
+ }
143
+ test('error-policy retry settles the dead attempt\'s kv-unified flight before the successor submits', async () => {
144
+ const tempDir = mkdtempSync(join(tmpdir(), 'kv-open-flight-'));
145
+ const membrane = new DeadThenAliveMembrane();
146
+ const strategy = kvUnifiedStrategy();
147
+ // Observe the ledger from the outside: the framework reaches the strategy
148
+ // through getStrategy(), so instance-level wrapping sees every call.
149
+ const spied = strategy;
150
+ const origFail = spied.reportKvUnifiedFailed.bind(strategy);
151
+ spied.reportKvUnifiedFailed = (id) => { membrane.order.push(`fail:${id}`); origFail(id); };
152
+ const origAccept = spied.reportKvUnifiedAccepted.bind(strategy);
153
+ spied.reportKvUnifiedAccepted = (args) => {
154
+ membrane.order.push(`accept:${args.submissionId}`);
155
+ origAccept(args);
156
+ };
157
+ const framework = await AgentFramework.create({
158
+ storePath: join(tempDir, 'store.chronicle'),
159
+ membrane: membrane.asMembrane(),
160
+ agents: [{ name: 'devops', model: 'test-model', systemPrompt: 'Test', strategy }],
161
+ modules: [new WakeModule()],
162
+ errorPolicy: new RetryOncePolicy(),
163
+ syncIntervalMs: 0,
164
+ });
165
+ const traces = [];
166
+ framework.onTrace((event) => traces.push(event));
167
+ try {
168
+ framework.pushEvent({ type: 'external-message', source: 'test', content: 'hello', metadata: {} });
169
+ await framework.runUntilIdle();
170
+ const failures = traces
171
+ .filter((t) => t.type === 'inference:failed')
172
+ .map((t) => t.error);
173
+ assert.deepEqual(failures, ['socket hang up'], `only the provider error may fail: ${JSON.stringify(membrane.order)}`);
174
+ assert.ok(traces.some((t) => t.type === 'inference:completed'), `retry must complete the turn: ${JSON.stringify(membrane.order)}`);
175
+ assert.ok(!traces.some((t) => t.type === 'inference:exhausted'), 'a single transient provider error must not exhaust the turn');
176
+ // The dead attempt's flight is closed BEFORE the successor's first pull,
177
+ // so the successor's receipt never meets an open flight.
178
+ const failIndex = membrane.order.findIndex((entry) => entry.startsWith('fail:'));
179
+ const successorPull = membrane.order.indexOf('pull:1');
180
+ assert.ok(failIndex >= 0, `dead flight was never failed: ${JSON.stringify(membrane.order)}`);
181
+ assert.ok(failIndex < successorPull, `dead flight failed after the successor started: ${JSON.stringify(membrane.order)}`);
182
+ assert.ok(membrane.order.some((entry) => entry.startsWith('accept:')), 'successor flight must be accepted on its usage event');
183
+ }
184
+ finally {
185
+ await framework.stop();
186
+ rmSync(tempDir, { recursive: true, force: true });
187
+ }
188
+ });
189
+ //# sourceMappingURL=kv-unified-open-flight-retry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kv-unified-open-flight-retry.test.js","sourceRoot":"","sources":["../../test/kv-unified-open-flight-retry.test.ts"],"names":[],"mappings":"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"}
@@ -53,4 +53,43 @@ test('agent leaves marker ownership unchanged for non-kv strategies', async () =
53
53
  assert.equal(request.cacheMarkers, undefined);
54
54
  assert.equal(compileOptions, undefined);
55
55
  });
56
+ test('a new activation closes the receipt flight its predecessor left open before it submits', async () => {
57
+ const order = [];
58
+ const strategy = {
59
+ isKvUnifiedEnabled: () => true,
60
+ beginKvUnifiedSubmission: (args) => { order.push(`begin:${args.submissionId}`); },
61
+ reportKvUnifiedFailed: (submissionId) => { order.push(`fail:${submissionId}`); },
62
+ };
63
+ const cm = {
64
+ getStrategy: () => strategy,
65
+ setToolDefinitions: () => { },
66
+ compile: async () => ({
67
+ messages: [{ participant: 'user', content: [{ type: 'text', text: 'hello' }] }],
68
+ systemInjections: [],
69
+ }),
70
+ };
71
+ const membrane = { streamYielding: () => ({}) };
72
+ const agent = new Agent({ name: 'devops', model: 'test', systemPrompt: 'system' }, cm, membrane);
73
+ // Activation 1: the provider attempt fires its receipt and then dies with no
74
+ // usage event (transport error / idle timeout / framework cancel). Nothing
75
+ // takes the submission; the stream's own teardown has not run yet.
76
+ const first = await agent.startStreamWithInjections([], undefined);
77
+ first.request
78
+ .onCacheWireReceipt?.({ requestHash: 'wire-1', markers: [] });
79
+ const firstId = order[0]?.slice('begin:'.length);
80
+ assert.ok(firstId, 'first activation must have begun a submission');
81
+ agent.reset();
82
+ // Activation 2 (retry, restart, or the next wake) must supersede that flight
83
+ // BEFORE its own receipt reaches the ledger.
84
+ const second = await agent.startStreamWithInjections([], undefined);
85
+ second.request
86
+ .onCacheWireReceipt?.({ requestHash: 'wire-2', markers: [] });
87
+ assert.equal(order.length, 3, JSON.stringify(order));
88
+ assert.equal(order[1], `fail:${firstId}`);
89
+ assert.ok(order[2].startsWith('begin:') && order[2] !== order[0]);
90
+ // The predecessor's late teardown finds nothing left to fail.
91
+ assert.deepEqual(first.drainKvSubmissionIds?.(), []);
92
+ // The successor's own flight is still live for its usage event.
93
+ assert.equal(second.takeKvSubmission?.()?.wireReceipt.requestHash, 'wire-2');
94
+ });
56
95
  //# sourceMappingURL=kv-unified-wiring.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kv-unified-wiring.test.js","sourceRoot":"","sources":["../../test/kv-unified-wiring.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKxC,IAAI,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;IACzF,IAAI,cAAuB,CAAC;IAC5B,IAAI,KAAc,CAAC;IACnB,MAAM,QAAQ,GAAG;QACf,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI;QAC9B,wBAAwB,EAAE,CAAC,IAAa,EAAE,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;KAC/D,CAAC;IACF,MAAM,EAAE,GAAG;QACT,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ;QAC3B,kBAAkB,EAAE,GAAG,EAAE,GAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,OAAgB,EAAE,WAAoB,EAAE,OAAgB,EAAE,EAAE;YAC1E,cAAc,GAAG,OAAO,CAAC;YACzB,OAAO;gBACL,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;gBAC/E,gBAAgB,EAAE,EAAE;aACrB,CAAC;QACJ,CAAC;KAC2B,CAAC;IAC/B,MAAM,QAAQ,GAAG;QACf,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAU;KACb,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,EAC3D,EAAE,EACF,QAAQ,CACT,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,yBAAyB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAyD,CAAC;IACpF,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,OAAQ,cAA4D,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAC;IAC1H,SAAS,CAAC,kBAAkB,EAAE,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,OAAO,UAAU,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC/D,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE;QACtB,YAAY,EAAE,UAAU,EAAE,YAAY;QACtC,WAAW,EAAE,WAAW;QACxB,UAAU,EAAG,KAAgC,CAAC,UAAU;KACzD,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAE,KAAgC,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;IAC/E,IAAI,cAAc,GAAY,OAAO,CAAC;IACtC,MAAM,EAAE,GAAG;QACT,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;QACxD,kBAAkB,EAAE,GAAG,EAAE,GAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,OAAgB,EAAE,WAAoB,EAAE,OAAgB,EAAE,EAAE;YAC1E,cAAc,GAAG,OAAO,CAAC;YACzB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;QAChD,CAAC;KAC2B,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,EAC3D,EAAE,EACF,EAAc,CACf,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,CAAE,OAAkD,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"kv-unified-wiring.test.js","sourceRoot":"","sources":["../../test/kv-unified-wiring.test.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,23 @@ function puppetHarness(opts) {
28
28
  framework.agents =
29
29
  new Map([['princess', agent]]);
30
30
  framework.toolImageLedgers = new Map();
31
+ // Turn-alive machinery the puppet reserves through (#145).
32
+ const fw = framework;
33
+ fw.activeTurnTokens = new Map();
34
+ fw.nextTurnToken = 1;
35
+ fw.deferredMessages = [];
36
+ fw.pendingAssistantBlocks = new Map();
37
+ fw.primaryAgentName = 'princess';
38
+ // Cross-turn writer path (what a channel message goes through): defers
39
+ // while a turn is alive, else stores — the real guard, reduced.
40
+ fw.addMessage = (participant, content, _m, opts) => {
41
+ if (fw.activeTurnTokens.has('princess')) {
42
+ fw.deferredMessages.push({ participant, content, forAgent: opts?.forAgent });
43
+ return '';
44
+ }
45
+ stored.push({ participant, content });
46
+ return `msg-${stored.length}`;
47
+ };
31
48
  framework.getToolsForAgent =
32
49
  () => surface.map((name) => ({ name }));
33
50
  framework.executeToolCall =
@@ -39,7 +56,22 @@ function puppetHarness(opts) {
39
56
  () => ({ cap: undefined });
40
57
  framework.emitTrace =
41
58
  (e) => { traces.push(e); };
42
- return { framework, stored, traces, executed };
59
+ const turn = fw;
60
+ return { framework, stored, traces, executed, fw: turn };
61
+ }
62
+ /** A harness whose executeToolCall stays open until `release()` is called. */
63
+ function heldHarness() {
64
+ const h = puppetHarness();
65
+ let release;
66
+ const gate = new Promise((r) => { release = r; });
67
+ const executed = [];
68
+ h.framework.executeToolCall =
69
+ async (call) => {
70
+ executed.push(call);
71
+ await gate;
72
+ return { success: true, data: [{ type: 'text', text: 'done' }], isError: false };
73
+ };
74
+ return { ...h, executed, release };
43
75
  }
44
76
  test('puppetToolCall executes with agent provenance and stores the pair', async () => {
45
77
  const { framework, stored, traces, executed } = puppetHarness();
@@ -104,4 +136,84 @@ test('puppetToolCall stores an error result as isError, still paired', async ()
104
136
  console.log = quiet;
105
137
  }
106
138
  });
139
+ // ---------------------------------------------------------------------------
140
+ // #145 — the idle guard is a point in time; the reservation must span the
141
+ // awaits, or a wake starts a turn underneath the pair.
142
+ // ---------------------------------------------------------------------------
143
+ test('puppetToolCall holds the turn-alive marker across tool execution and releases it after the pair is stored', async () => {
144
+ const { framework, stored, fw, release, executed } = heldHarness();
145
+ const quiet = console.log;
146
+ console.log = () => { };
147
+ try {
148
+ assert.equal(fw.activeTurnTokens.has('princess'), false, 'nothing reserved before the call');
149
+ const p = framework.puppetToolCall('princess', 'mcpl--eido--look', {});
150
+ await new Promise((r) => setImmediate(r));
151
+ assert.equal(executed.length, 1, 'tool is executing');
152
+ assert.equal(fw.activeTurnTokens.has('princess'), true, 'while the tool runs the agent is turn-alive — the scheduler requeues wakes on exactly this');
153
+ release();
154
+ await p;
155
+ assert.equal(fw.activeTurnTokens.has('princess'), false, 'released once the pair is stored');
156
+ assert.equal(stored.length, 2, 'the pair, stored directly');
157
+ assert.equal(stored[0].content[0].type, 'tool_use');
158
+ assert.equal(stored[1].content[0].type, 'tool_result');
159
+ }
160
+ finally {
161
+ console.log = quiet;
162
+ }
163
+ });
164
+ test('puppetToolCall releases the reservation when the tool throws', async () => {
165
+ const { framework, fw } = puppetHarness();
166
+ framework.executeToolCall =
167
+ async () => { throw new Error('boom'); };
168
+ await assert.rejects(() => framework.puppetToolCall('princess', 'mcpl--eido--look', {}), /boom/);
169
+ assert.equal(fw.activeTurnTokens.has('princess'), false, 'no leaked token (the idle+turn-alive wedge)');
170
+ });
171
+ test('puppetToolCall refuses an idle agent whose turn is still alive (teardown pending)', async () => {
172
+ const { framework, stored, fw } = puppetHarness();
173
+ fw.activeTurnTokens.set('princess', 41);
174
+ await assert.rejects(() => framework.puppetToolCall('princess', 'mcpl--eido--look', {}), /idle\+turn-alive.*requires idle/);
175
+ assert.equal(stored.length, 0);
176
+ assert.equal(fw.activeTurnTokens.get('princess'), 41, 'someone else\'s token untouched');
177
+ });
178
+ test('a message arriving while the puppet holds the agent lands AFTER the pair, never between', async () => {
179
+ const { framework, stored, fw, release } = heldHarness();
180
+ const quiet = console.log;
181
+ console.log = () => { };
182
+ try {
183
+ const p = framework.puppetToolCall('princess', 'mcpl--eido--look', {});
184
+ await new Promise((r) => setImmediate(r));
185
+ // a channel message for the agent, mid-execution: the cross-turn writer defers
186
+ framework
187
+ .addMessage('user', [{ type: 'text', text: 'hey, you there?' }]);
188
+ assert.equal(stored.length, 0, 'deferred, not stored mid-puppet');
189
+ assert.equal(fw.deferredMessages.length, 1);
190
+ release();
191
+ await p;
192
+ assert.equal(fw.deferredMessages.length, 0, 'flushed at the puppet\'s end, like a turn\'s end');
193
+ assert.deepEqual(stored.map((m) => m.content[0].type), ['tool_use', 'tool_result', 'text'], 'pair first and adjacent; the deferred message follows');
194
+ }
195
+ finally {
196
+ console.log = quiet;
197
+ }
198
+ });
199
+ test('if a turn replaced the reservation mid-flight, the pair is queued as a unit behind it, not written into it', async () => {
200
+ const { framework, stored, traces, fw, release } = heldHarness();
201
+ const quiet = console.log;
202
+ console.log = () => { };
203
+ try {
204
+ const p = framework.puppetToolCall('princess', 'mcpl--eido--look', {});
205
+ await new Promise((r) => setImmediate(r));
206
+ // the provider-admission re-entry: a turn takes the marker without re-testing it
207
+ fw.activeTurnTokens.set('princess', 9999);
208
+ release();
209
+ await p;
210
+ assert.equal(stored.length, 0, 'nothing written under the live turn');
211
+ assert.equal(fw.activeTurnTokens.get('princess'), 9999, 'the turn\'s token is not clobbered');
212
+ assert.deepEqual(fw.deferredMessages.map((m) => [m.forAgent, m.content[0].type]), [['princess', 'tool_use'], ['princess', 'tool_result']], 'both halves queued, adjacent, addressed to the agent');
213
+ assert.equal(traces[0].deferred, true, 'trace says so');
214
+ }
215
+ finally {
216
+ console.log = quiet;
217
+ }
218
+ });
107
219
  //# sourceMappingURL=puppet-tool-call.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"puppet-tool-call.test.js","sourceRoot":"","sources":["../../test/puppet-tool-call.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"puppet-tool-call.test.js","sourceRoot":"","sources":["../../test/puppet-tool-call.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=puppet-turn-reservation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppet-turn-reservation.test.d.ts","sourceRoot":"","sources":["../../test/puppet-turn-reservation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * #145 end-to-end: a wake that arrives while a puppeted tool is executing
3
+ * must not start a turn. Before the fix the scheduler saw an idle agent with
4
+ * no turn alive, started streaming, and the puppet's tool_use/tool_result
5
+ * pair then landed inside that turn.
6
+ */
7
+ import { describe, it, beforeEach, afterEach } from 'node:test';
8
+ import assert from 'node:assert/strict';
9
+ import { tmpdir } from 'node:os';
10
+ import { mkdtempSync, rmSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { AgentFramework } from '../src/index.js';
13
+ import { MockMembrane, createMockResponse } from './helpers/mock-membrane.js';
14
+ const settle = () => new Promise((r) => setTimeout(r, 30));
15
+ describe('puppetToolCall vs a concurrent wake (#145)', () => {
16
+ let tempDir;
17
+ let membrane;
18
+ beforeEach(() => {
19
+ tempDir = mkdtempSync(join(tmpdir(), 'puppet-reservation-'));
20
+ membrane = new MockMembrane();
21
+ });
22
+ afterEach(() => {
23
+ rmSync(tempDir, { recursive: true, force: true });
24
+ });
25
+ it('a wake during a held puppet execution is requeued; the pair lands before the turn it would have corrupted', async () => {
26
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'ok, I see it' }]));
27
+ const framework = await AgentFramework.create({
28
+ storePath: join(tempDir, 'test.chronicle'),
29
+ membrane: membrane.asMembrane(),
30
+ agents: [{ name: 'scout', model: 'test-model', systemPrompt: 'You are scout.' }],
31
+ modules: [],
32
+ });
33
+ const i = framework;
34
+ let release;
35
+ const gate = new Promise((r) => { release = r; });
36
+ i.executeToolCall = async () => {
37
+ await gate;
38
+ return { success: true, data: 'settings snapshot', isError: false };
39
+ };
40
+ const quiet = console.log;
41
+ console.log = () => { };
42
+ try {
43
+ const puppet = framework.puppetToolCall('scout', 'agent_settings', { action: 'get' });
44
+ await new Promise((r) => setImmediate(r));
45
+ assert.equal(i.activeTurnTokens.has('scout'), true, 'reserved while the tool runs');
46
+ // the race: a wake arrives mid-execution and the scheduler runs
47
+ i.pendingRequests.push({ agentName: 'scout', reason: 'mcpl:channel-incoming', source: 'test', timestamp: Date.now() });
48
+ await i.processInferenceRequests();
49
+ assert.equal(membrane.calls.length, 0, 'no turn started under the puppet');
50
+ assert.equal(i.pendingRequests.length, 1, 'the wake is requeued, not dropped');
51
+ release();
52
+ await puppet;
53
+ assert.equal(i.activeTurnTokens.has('scout'), false, 'reservation released');
54
+ await i.processInferenceRequests();
55
+ await framework.runUntilIdle();
56
+ assert.equal(membrane.calls.length, 1, 'the requeued wake ran once the puppet was done');
57
+ const all = framework.getAgent('scout').getContextManager().getAllMessages();
58
+ const types = all.map((m) => m.content[0]?.type);
59
+ const use = types.indexOf('tool_use');
60
+ assert.ok(use >= 0, 'pair stored');
61
+ assert.equal(types[use + 1], 'tool_result', 'pair adjacent');
62
+ const turnText = types.lastIndexOf('text');
63
+ assert.ok(turnText > use + 1, 'the turn the wake started comes AFTER the pair, and saw it');
64
+ }
65
+ finally {
66
+ console.log = quiet;
67
+ await framework.stop();
68
+ }
69
+ });
70
+ it('a turn parked on provider admission that resumes during a puppet is requeued, not started over the reservation (review of #148)', async () => {
71
+ // The one gap the scheduler's busy test does not cover: a wake passed it,
72
+ // acquired primary admission, and parked behind an auxiliary call. If the
73
+ // auxiliary settles while a puppet holds the agent, the continuation used
74
+ // to re-enter startAgentStream and replace the puppet's token; a fast
75
+ // replacement turn then ended (and flushed) before the puppeted tool
76
+ // returned, and the pair queued behind it was never stored. The public
77
+ // promise still resolved "ok".
78
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'replacement turn' }]));
79
+ const framework = await AgentFramework.create({
80
+ storePath: join(tempDir, 'test.chronicle'),
81
+ membrane: membrane.asMembrane(),
82
+ agents: [{ name: 'scout', model: 'test-model', systemPrompt: 'You are scout.' }],
83
+ modules: [],
84
+ });
85
+ const i = framework;
86
+ let releaseTool;
87
+ const toolGate = new Promise((r) => { releaseTool = r; });
88
+ i.executeToolCall = async () => { await toolGate; return { success: true, data: 'settings snapshot', isError: false }; };
89
+ let releaseAux;
90
+ const auxiliary = i.withAuxiliaryAdmission('scout', () => new Promise((r) => { releaseAux = r; }));
91
+ const quiet = console.log, quietErr = console.error;
92
+ console.log = () => { };
93
+ console.error = () => { };
94
+ try {
95
+ // 1. a wake passes the busy test and parks on provider admission
96
+ i.pendingRequests.push({ agentName: 'scout', reason: 'mcpl:channel-incoming', source: 'test', timestamp: Date.now() });
97
+ await i.processInferenceRequests();
98
+ await settle();
99
+ assert.equal(membrane.calls.length, 0, 'parked behind the auxiliary call');
100
+ assert.equal(i.providerGates.get('scout')?.primaryDepth, 1, 'admission held while parked');
101
+ assert.equal(i.activeTurnTokens.has('scout'), false, 'no token yet — the park is before the token');
102
+ // 2. a puppet takes the agent while the turn is parked
103
+ const puppet = framework.puppetToolCall('scout', 'agent_settings', { action: 'get' });
104
+ await settle();
105
+ assert.equal(i.activeTurnTokens.has('scout'), true, 'puppet holds the reservation');
106
+ const puppetToken = i.activeTurnTokens.get('scout');
107
+ // 3. the auxiliary settles: the parked turn would now resume
108
+ releaseAux();
109
+ await auxiliary;
110
+ await settle();
111
+ assert.equal(membrane.calls.length, 0, 'no provider call starts during the puppeted tool');
112
+ assert.equal(i.activeTurnTokens.get('scout'), puppetToken, 'the reservation is not replaced');
113
+ assert.equal(i.pendingRequests.length, 1, 'the wake is requeued for the scheduler, not dropped');
114
+ assert.equal(i.providerGates.get('scout')?.primaryDepth, 0, 'admission given back');
115
+ // 4. the tool returns: the pair is persisted before the promise resolves
116
+ releaseTool();
117
+ await puppet;
118
+ const stored = () => framework.getAgent('scout').getContextManager().getAllMessages()
119
+ .map((m) => m.content[0]?.type);
120
+ assert.deepEqual(stored().filter((t) => t === 'tool_use' || t === 'tool_result'), ['tool_use', 'tool_result'], 'pair persisted when puppetToolCall resolves');
121
+ assert.equal(i.deferredMessages.length, 0, 'nothing stranded in the deferred queue');
122
+ assert.equal(i.activeTurnTokens.has('scout'), false, 'reservation released');
123
+ // 5. the retained wake runs afterwards and sees the pair
124
+ await i.processInferenceRequests();
125
+ await framework.runUntilIdle();
126
+ assert.equal(membrane.calls.length, 1, 'the requeued wake ran once the puppet was done');
127
+ const types = stored();
128
+ assert.ok(types.lastIndexOf('text') > types.indexOf('tool_result'), 'the turn comes after the pair');
129
+ }
130
+ finally {
131
+ console.log = quiet;
132
+ console.error = quietErr;
133
+ releaseAux();
134
+ releaseTool();
135
+ await framework.stop();
136
+ }
137
+ });
138
+ });
139
+ //# sourceMappingURL=puppet-turn-reservation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppet-turn-reservation.test.js","sourceRoot":"","sources":["../../test/puppet-turn-reservation.test.ts"],"names":[],"mappings":"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"}
@@ -464,6 +464,35 @@ describe('save_recent_image provenance (issue #104)', () => {
464
464
  rmSync(h.tempDir, { recursive: true, force: true });
465
465
  }
466
466
  });
467
+ it('[save, snap] — a save dispatched BEFORE its sibling snapshot still waits for it (#140 follow-up)', async () => {
468
+ // The mirror of the test above, in the order a model is at least as
469
+ // likely to emit. The round loop dispatches synchronously, so the save
470
+ // starts first and finds no snapshot in the inventory; the sibling
471
+ // barrier's first poll must still yield to the snap that follows it in
472
+ // the same batch. Pinned separately because it was verified by reading
473
+ // at review time and never by a test.
474
+ const h = await startTurn({
475
+ prefix: 'sri-same-batch-rev-',
476
+ world: (world) => { world.snapDelayMs = 150; },
477
+ responses: [
478
+ [saveCall('call_save', { path: 'files/batched-rev.png', index: 0 }), snapCall('call_snap')],
479
+ done,
480
+ ],
481
+ });
482
+ try {
483
+ const receipt = saved(await waitForToolResult(h.framework, 'call_save'));
484
+ assert.strictEqual(receipt[0].source, 'tool-result');
485
+ assert.strictEqual(receipt[0].toolCallId, 'call_snap', 'the sibling snapshot, not the older attachment');
486
+ assert.strictEqual(receipt[0].sha256, sha(PNG));
487
+ assert.ok((await fileBytes(h, 'files/batched-rev.png'))?.equals(PNG));
488
+ const snap = await waitForToolResult(h.framework, 'call_snap');
489
+ assert.match(snap.content, new RegExp(`ref ${receipt[0].ref}\\]`));
490
+ }
491
+ finally {
492
+ await h.framework.stop();
493
+ rmSync(h.tempDir, { recursive: true, force: true });
494
+ }
495
+ });
467
496
  it('same-batch siblings are classified like the commit path: withheld data is no slot, a uri image is a reference slot', async () => {
468
497
  const h = await startTurn({
469
498
  prefix: 'sri-same-batch-class-',