@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.
- package/dist/src/agent.d.ts +10 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +39 -0
- package/dist/src/agent.js.map +1 -1
- package/dist/src/framework.d.ts +5 -2
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +126 -36
- package/dist/src/framework.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +69 -0
- package/dist/src/modules/history/index.d.ts.map +1 -0
- package/dist/src/modules/history/index.js +519 -0
- package/dist/src/modules/history/index.js.map +1 -0
- package/dist/src/modules/history/search-regex-worker.d.ts +23 -0
- package/dist/src/modules/history/search-regex-worker.d.ts.map +1 -0
- package/dist/src/modules/history/search-regex-worker.js +57 -0
- package/dist/src/modules/history/search-regex-worker.js.map +1 -0
- package/dist/src/modules/index.d.ts +1 -0
- package/dist/src/modules/index.d.ts.map +1 -1
- package/dist/src/modules/index.js +1 -0
- package/dist/src/modules/index.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +42 -1
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +5 -0
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/types/trace.d.ts +4 -0
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/history-module.test.d.ts +2 -0
- package/dist/test/history-module.test.d.ts.map +1 -0
- package/dist/test/history-module.test.js +449 -0
- package/dist/test/history-module.test.js.map +1 -0
- package/dist/test/kv-unified-open-flight-retry.test.d.ts +2 -0
- package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +1 -0
- package/dist/test/kv-unified-open-flight-retry.test.js +189 -0
- package/dist/test/kv-unified-open-flight-retry.test.js.map +1 -0
- package/dist/test/kv-unified-wiring.test.js +39 -0
- package/dist/test/kv-unified-wiring.test.js.map +1 -1
- package/dist/test/puppet-tool-call.test.js +113 -1
- package/dist/test/puppet-tool-call.test.js.map +1 -1
- package/dist/test/puppet-turn-reservation.test.d.ts +2 -0
- package/dist/test/puppet-turn-reservation.test.d.ts.map +1 -0
- package/dist/test/puppet-turn-reservation.test.js +139 -0
- package/dist/test/puppet-turn-reservation.test.js.map +1 -0
- package/dist/test/save-recent-image.test.js +29 -0
- package/dist/test/save-recent-image.test.js.map +1 -1
- package/dist/test/tool-result-spill.test.js +114 -0
- package/dist/test/tool-result-spill.test.js.map +1 -1
- package/dist/test/workspace-read-text.test.d.ts +2 -0
- package/dist/test/workspace-read-text.test.d.ts.map +1 -0
- package/dist/test/workspace-read-text.test.js +101 -0
- package/dist/test/workspace-read-text.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/agent.ts +41 -0
- package/src/framework.ts +132 -43
- package/src/modules/history/index.ts +613 -0
- package/src/modules/history/search-regex-worker.ts +76 -0
- package/src/modules/index.ts +2 -0
- package/src/modules/workspace/index.ts +42 -1
- package/src/modules/workspace/types.ts +5 -0
- 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
|
-
|
|
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 @@
|
|
|
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-',
|