@animalabs/agent-framework 0.5.7 → 0.6.2
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 +4 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +15 -0
- package/dist/src/agent.js.map +1 -1
- package/dist/src/framework.d.ts +243 -0
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +1378 -114
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +64 -2
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +212 -12
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/gate-script-worker.d.ts +2 -0
- package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
- package/dist/src/gate/gate-script-worker.js +69 -0
- package/dist/src/gate/gate-script-worker.js.map +1 -0
- package/dist/src/gate/gate-script.d.ts +44 -0
- package/dist/src/gate/gate-script.d.ts.map +1 -0
- package/dist/src/gate/gate-script.js +164 -0
- package/dist/src/gate/gate-script.js.map +1 -0
- package/dist/src/gate/types.d.ts +18 -3
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +60 -2
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +114 -9
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +15 -0
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +5 -0
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +64 -30
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +247 -210
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +98 -0
- package/dist/src/mcpl/transport.d.ts.map +1 -0
- package/dist/src/mcpl/transport.js +281 -0
- package/dist/src/mcpl/transport.js.map +1 -0
- package/dist/src/mcpl/types.d.ts +59 -3
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +17 -1
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +101 -6
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/prose-segments.d.ts +18 -0
- package/dist/src/prose-segments.d.ts.map +1 -0
- package/dist/src/prose-segments.js +39 -0
- package/dist/src/prose-segments.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
- package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
- package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog.js +106 -0
- package/dist/src/runtime/liveness-watchdog.js.map +1 -0
- package/dist/src/types/agent.d.ts +47 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +14 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +58 -0
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +42 -0
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/agent-cache-ttl.test.d.ts +2 -0
- package/dist/test/agent-cache-ttl.test.d.ts.map +1 -0
- package/dist/test/agent-cache-ttl.test.js +25 -0
- package/dist/test/agent-cache-ttl.test.js.map +1 -0
- package/dist/test/channel-registry-routing.test.js +108 -1
- package/dist/test/channel-registry-routing.test.js.map +1 -1
- package/dist/test/checkpoint-attribution.test.d.ts +2 -0
- package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
- package/dist/test/checkpoint-attribution.test.js +40 -0
- package/dist/test/checkpoint-attribution.test.js.map +1 -0
- package/dist/test/conversation-fork-injection-scoping.test.d.ts +2 -0
- package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +1 -0
- package/dist/test/conversation-fork-injection-scoping.test.js +184 -0
- package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.js +137 -0
- package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
- package/dist/test/event-gate-script.test.d.ts +2 -0
- package/dist/test/event-gate-script.test.d.ts.map +1 -0
- package/dist/test/event-gate-script.test.js +95 -0
- package/dist/test/event-gate-script.test.js.map +1 -0
- package/dist/test/event-gate-tags.test.d.ts +2 -0
- package/dist/test/event-gate-tags.test.d.ts.map +1 -0
- package/dist/test/event-gate-tags.test.js +76 -0
- package/dist/test/event-gate-tags.test.js.map +1 -0
- package/dist/test/gather-context-timeout.test.d.ts +2 -0
- package/dist/test/gather-context-timeout.test.d.ts.map +1 -0
- package/dist/test/gather-context-timeout.test.js +120 -0
- package/dist/test/gather-context-timeout.test.js.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.d.ts +2 -0
- package/dist/test/inference-exhaustion-rewind.test.d.ts.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.js +181 -0
- package/dist/test/inference-exhaustion-rewind.test.js.map +1 -0
- package/dist/test/inference-failure-observability.test.js +4 -0
- package/dist/test/inference-failure-observability.test.js.map +1 -1
- package/dist/test/liveness-watchdog.test.d.ts +2 -0
- package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
- package/dist/test/liveness-watchdog.test.js +78 -0
- package/dist/test/liveness-watchdog.test.js.map +1 -0
- package/dist/test/mcpl-reconnect-events.test.d.ts +2 -0
- package/dist/test/mcpl-reconnect-events.test.d.ts.map +1 -0
- package/dist/test/mcpl-reconnect-events.test.js +119 -0
- package/dist/test/mcpl-reconnect-events.test.js.map +1 -0
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts +2 -0
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +1 -0
- package/dist/test/mcpl-reconnect-reregistration.test.js +181 -0
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -0
- package/dist/test/mcpl-request-timeout.test.d.ts +2 -0
- package/dist/test/mcpl-request-timeout.test.d.ts.map +1 -0
- package/dist/test/mcpl-request-timeout.test.js +123 -0
- package/dist/test/mcpl-request-timeout.test.js.map +1 -0
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts +7 -0
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +1 -0
- package/dist/test/mcpl-runtime-lifecycle.test.js +142 -0
- package/dist/test/mcpl-runtime-lifecycle.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
- package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +155 -0
- package/dist/test/mcpl-ws-transport.test.js.map +1 -0
- package/dist/test/prose-segments.test.d.ts +2 -0
- package/dist/test/prose-segments.test.d.ts.map +1 -0
- package/dist/test/prose-segments.test.js +52 -0
- package/dist/test/prose-segments.test.js.map +1 -0
- package/dist/test/refusal-rewind.test.d.ts +2 -0
- package/dist/test/refusal-rewind.test.d.ts.map +1 -0
- package/dist/test/refusal-rewind.test.js +108 -0
- package/dist/test/refusal-rewind.test.js.map +1 -0
- package/dist/test/state-scaling-e2e.test.d.ts +22 -0
- package/dist/test/state-scaling-e2e.test.d.ts.map +1 -0
- package/dist/test/state-scaling-e2e.test.js +200 -0
- package/dist/test/state-scaling-e2e.test.js.map +1 -0
- package/dist/test/state-scaling.test.d.ts +12 -0
- package/dist/test/state-scaling.test.d.ts.map +1 -0
- package/dist/test/state-scaling.test.js +243 -0
- package/dist/test/state-scaling.test.js.map +1 -0
- package/dist/test/tool-result-content.test.d.ts +2 -0
- package/dist/test/tool-result-content.test.d.ts.map +1 -0
- package/dist/test/tool-result-content.test.js +37 -0
- package/dist/test/tool-result-content.test.js.map +1 -0
- package/dist/test/trunk-channel-routing.test.d.ts +19 -0
- package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
- package/dist/test/trunk-channel-routing.test.js +175 -0
- package/dist/test/trunk-channel-routing.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/agent.ts +16 -0
- package/src/framework.ts +1576 -121
- package/src/gate/event-gate.ts +209 -12
- package/src/gate/gate-script-worker.ts +73 -0
- package/src/gate/gate-script.ts +180 -0
- package/src/gate/types.ts +19 -3
- package/src/index.ts +10 -0
- package/src/mcpl/channel-registry.ts +143 -8
- package/src/mcpl/checkpoint-manager.ts +16 -0
- package/src/mcpl/push-handler.ts +5 -0
- package/src/mcpl/server-connection.ts +262 -227
- package/src/mcpl/transport.ts +307 -0
- package/src/mcpl/types.ts +68 -3
- package/src/module-registry.ts +126 -6
- package/src/prose-segments.ts +42 -0
- package/src/runtime/liveness-watchdog-worker.ts +66 -0
- package/src/runtime/liveness-watchdog.ts +122 -0
- package/src/types/agent.ts +48 -0
- package/src/types/framework.ts +15 -0
- package/src/types/module.ts +65 -0
- package/src/types/trace.ts +51 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { WebSocketServer } from 'ws';
|
|
4
|
+
import { McplServerConnection } from '../src/mcpl/server-connection.js';
|
|
5
|
+
import { buildWebSocketUrl, isWebSocketTransport } from '../src/mcpl/transport.js';
|
|
6
|
+
const HOST_CAPS = { version: '0.4', pushEvents: true, featureSets: true };
|
|
7
|
+
/**
|
|
8
|
+
* A minimal in-process MCPL server over WebSocket: answers the initialize
|
|
9
|
+
* handshake, tools/list, and an `echo` tools/call. One JSON-RPC message per WS
|
|
10
|
+
* frame (as McplServerConnection sends). `onSocket` lets a test grab the raw
|
|
11
|
+
* socket to simulate a mid-session drop.
|
|
12
|
+
*/
|
|
13
|
+
function startMockMcplWsServer(onSocket) {
|
|
14
|
+
const wss = new WebSocketServer({ port: 0, host: '127.0.0.1' });
|
|
15
|
+
wss.on('connection', (ws) => {
|
|
16
|
+
onSocket?.(ws);
|
|
17
|
+
ws.on('message', (data) => {
|
|
18
|
+
let msg;
|
|
19
|
+
try {
|
|
20
|
+
msg = JSON.parse(String(data));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const reply = (result) => ws.send(JSON.stringify({ jsonrpc: '2.0', id: msg.id, result }));
|
|
26
|
+
switch (msg.method) {
|
|
27
|
+
case 'initialize':
|
|
28
|
+
reply({ capabilities: { experimental: { mcpl: { version: '0.4', pushEvents: true } } } });
|
|
29
|
+
break;
|
|
30
|
+
case 'notifications/initialized':
|
|
31
|
+
break; // notification, no reply
|
|
32
|
+
case 'tools/list':
|
|
33
|
+
reply({ tools: [{ name: 'echo', description: 'Echo text back', inputSchema: { type: 'object', properties: { text: { type: 'string' } } } }] });
|
|
34
|
+
break;
|
|
35
|
+
case 'tools/call': {
|
|
36
|
+
const text = String(msg.params?.arguments?.text ?? '');
|
|
37
|
+
reply({ content: [{ type: 'text', text }] });
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
default:
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
return wss;
|
|
46
|
+
}
|
|
47
|
+
function wsUrl(wss) {
|
|
48
|
+
const port = wss.address().port;
|
|
49
|
+
return `ws://127.0.0.1:${port}/mcpl`;
|
|
50
|
+
}
|
|
51
|
+
function waitListening(wss) {
|
|
52
|
+
return new Promise((resolve) => (wss.address() ? resolve() : wss.once('listening', () => resolve())));
|
|
53
|
+
}
|
|
54
|
+
test('buildWebSocketUrl validates protocol and appends the token as a query param', () => {
|
|
55
|
+
assert.equal(buildWebSocketUrl({ id: 's', url: 'wss://example.com/mcpl' }), 'wss://example.com/mcpl');
|
|
56
|
+
assert.equal(buildWebSocketUrl({ id: 's', url: 'wss://example.com/mcpl', token: 'sekret' }), 'wss://example.com/mcpl?token=sekret');
|
|
57
|
+
// Preserves an existing query.
|
|
58
|
+
assert.equal(buildWebSocketUrl({ id: 's', url: 'wss://example.com/mcpl?v=2', token: 'abc' }), 'wss://example.com/mcpl?v=2&token=abc');
|
|
59
|
+
assert.throws(() => buildWebSocketUrl({ id: 's', url: 'http://example.com' }), /ws:\/\/ or wss:\/\//);
|
|
60
|
+
assert.throws(() => buildWebSocketUrl({ id: 's', command: 'x' }), /requires "url"/);
|
|
61
|
+
});
|
|
62
|
+
test('isWebSocketTransport selects the transport from config shape', () => {
|
|
63
|
+
assert.equal(isWebSocketTransport({ id: 's', url: 'wss://x/y' }), true);
|
|
64
|
+
assert.equal(isWebSocketTransport({ id: 's', url: 'wss://x/y', transport: 'websocket' }), true);
|
|
65
|
+
assert.equal(isWebSocketTransport({ id: 's', command: 'node' }), false);
|
|
66
|
+
// Explicit transport wins over shape.
|
|
67
|
+
assert.equal(isWebSocketTransport({ id: 's', command: 'node', transport: 'stdio' }), false);
|
|
68
|
+
assert.equal(isWebSocketTransport({ id: 's', url: 'wss://x', command: 'node' }), false); // url+command → stdio unless transport says otherwise
|
|
69
|
+
});
|
|
70
|
+
test('connects over WebSocket, negotiates capabilities, lists tools, and round-trips a call', async () => {
|
|
71
|
+
const wss = startMockMcplWsServer();
|
|
72
|
+
await waitListening(wss);
|
|
73
|
+
try {
|
|
74
|
+
const conn = await McplServerConnection.connect({ id: 'editor', url: wsUrl(wss) }, HOST_CAPS);
|
|
75
|
+
conn.ready();
|
|
76
|
+
// Handshake negotiated MCPL capabilities over WS.
|
|
77
|
+
assert.ok(conn.capabilities, 'expected MCPL capabilities from the handshake');
|
|
78
|
+
assert.equal(conn.capabilities?.pushEvents, true);
|
|
79
|
+
// tools/list round-trips.
|
|
80
|
+
const list = await conn.sendToolsList();
|
|
81
|
+
assert.deepEqual(list.tools.map((t) => t.name), ['echo']);
|
|
82
|
+
// tools/call round-trips.
|
|
83
|
+
const result = await conn.sendToolsCall('echo', { text: 'hello over wss' });
|
|
84
|
+
const block = result.content[0];
|
|
85
|
+
assert.equal(block.text, 'hello over wss');
|
|
86
|
+
await conn.close();
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
wss.close();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
test('a failed WebSocket dial rejects with a clear error (no reconnect)', async () => {
|
|
93
|
+
// Nothing is listening on this port.
|
|
94
|
+
await assert.rejects(McplServerConnection.connect({ id: 'dead', url: 'ws://127.0.0.1:1/mcpl' }, HOST_CAPS), /WebSocket connect (failed|timed out)/);
|
|
95
|
+
});
|
|
96
|
+
// A tiny stdio MCPL server: reads NDJSON on stdin, answers the same three
|
|
97
|
+
// methods on stdout, and writes one line to stderr on boot (to prove stderr
|
|
98
|
+
// still surfaces after the transport refactor). Kept inline so the test is
|
|
99
|
+
// self-contained.
|
|
100
|
+
const STDIO_SERVER = `
|
|
101
|
+
let buf = '';
|
|
102
|
+
process.stderr.write('mock stdio server up\\n');
|
|
103
|
+
process.stdin.on('data', (c) => {
|
|
104
|
+
buf += c.toString('utf8');
|
|
105
|
+
let i;
|
|
106
|
+
while ((i = buf.indexOf('\\n')) >= 0) {
|
|
107
|
+
const line = buf.slice(0, i); buf = buf.slice(i + 1);
|
|
108
|
+
if (!line.trim()) continue;
|
|
109
|
+
let m; try { m = JSON.parse(line); } catch { continue; }
|
|
110
|
+
const reply = (result) => process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: m.id, result }) + '\\n');
|
|
111
|
+
if (m.method === 'initialize') reply({ capabilities: { experimental: { mcpl: { version: '0.4', pushEvents: true } } } });
|
|
112
|
+
else if (m.method === 'tools/list') reply({ tools: [{ name: 'echo', description: 'e', inputSchema: { type: 'object' } }] });
|
|
113
|
+
else if (m.method === 'tools/call') reply({ content: [{ type: 'text', text: String((m.params && m.params.arguments && m.params.arguments.text) || '') }] });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
`;
|
|
117
|
+
test('stdio transport still handshakes, lists tools, round-trips a call, and surfaces stderr', async () => {
|
|
118
|
+
const conn = await McplServerConnection.connect({ id: 'stdiosrv', command: process.execPath, args: ['-e', STDIO_SERVER] }, HOST_CAPS);
|
|
119
|
+
const stderrLines = [];
|
|
120
|
+
conn.on('stderr', (p) => stderrLines.push(p.line));
|
|
121
|
+
conn.ready();
|
|
122
|
+
assert.ok(conn.capabilities, 'stdio handshake should negotiate capabilities');
|
|
123
|
+
const list = await conn.sendToolsList();
|
|
124
|
+
assert.deepEqual(list.tools.map((t) => t.name), ['echo']);
|
|
125
|
+
const result = await conn.sendToolsCall('echo', { text: 'over stdio' });
|
|
126
|
+
const block = result.content[0];
|
|
127
|
+
assert.equal(block.text, 'over stdio');
|
|
128
|
+
// stderr forwarding preserved (buffered pre-listener, flushed on subscribe).
|
|
129
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
130
|
+
assert.ok(stderrLines.some((l) => l.includes('mock stdio server up')), 'stderr line should surface');
|
|
131
|
+
await conn.close();
|
|
132
|
+
});
|
|
133
|
+
test('reconnect: an unexpected socket drop triggers a background reconnect', async () => {
|
|
134
|
+
let sockets = [];
|
|
135
|
+
const wss = startMockMcplWsServer((ws) => sockets.push(ws));
|
|
136
|
+
await waitListening(wss);
|
|
137
|
+
try {
|
|
138
|
+
const conn = await McplServerConnection.connect({ id: 'editor', url: wsUrl(wss), reconnect: true, reconnectIntervalMs: 100 }, HOST_CAPS);
|
|
139
|
+
conn.ready();
|
|
140
|
+
const reconnected = new Promise((resolve) => conn.once('reconnect', () => resolve()));
|
|
141
|
+
// Force-drop the server side of the current socket.
|
|
142
|
+
assert.equal(sockets.length, 1);
|
|
143
|
+
sockets[0].terminate();
|
|
144
|
+
await reconnected; // background reconnect must fire
|
|
145
|
+
// The new socket works: a call round-trips post-reconnect.
|
|
146
|
+
const result = await conn.sendToolsCall('echo', { text: 'after reconnect' });
|
|
147
|
+
const block = result.content[0];
|
|
148
|
+
assert.equal(block.text, 'after reconnect');
|
|
149
|
+
await conn.close();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
wss.close();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=mcpl-ws-transport.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpl-ws-transport.test.js","sourceRoot":"","sources":["../../test/mcpl-ws-transport.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAkB,MAAM,IAAI,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGnF,MAAM,SAAS,GAAyB,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAEhG;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,QAAkC;IAC/D,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE;QAC1B,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACf,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,GAAuG,CAAC;YAC5G,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACjC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,CAAC,MAAe,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YACnG,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;gBACnB,KAAK,YAAY;oBACf,KAAK,CAAC,EAAE,YAAY,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;oBAC1F,MAAM;gBACR,KAAK,2BAA2B;oBAC9B,MAAM,CAAC,yBAAyB;gBAClC,KAAK,YAAY;oBACf,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC/I,MAAM;gBACR,KAAK,YAAY,CAAC,CAAC,CAAC;oBAClB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;oBACvD,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC7C,MAAM;gBACR,CAAC;gBACD;oBACE,MAAM;YACV,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,KAAK,CAAC,GAAoB;IACjC,MAAM,IAAI,GAAI,GAAG,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAC;IACjD,OAAO,kBAAkB,IAAI,OAAO,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,GAAoB;IACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;AACxG,CAAC;AAED,IAAI,CAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAC7D,wBAAwB,CACzB,CAAC;IACF,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,wBAAwB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAC9E,qCAAqC,CACtC,CAAC;IACF,+BAA+B;IAC/B,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,4BAA4B,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAC/E,sCAAsC,CACvC,CAAC;IACF,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAE,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACtG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;AACtF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACxE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAChG,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACxE,sCAAsC;IACtC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5F,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,sDAAsD;AACjJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;IACvG,MAAM,GAAG,GAAG,qBAAqB,EAAE,CAAC;IACpC,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,kDAAkD;QAClD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,+CAA+C,CAAC,CAAC;QAC9E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAElD,0BAA0B;QAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAE1D,0BAA0B;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC5E,MAAM,KAAK,GAAI,MAAM,CAAC,OAAkD,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QAE3C,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,KAAK,EAAE,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;IACnF,qCAAqC;IACrC,MAAM,MAAM,CAAC,OAAO,CAClB,oBAAoB,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,uBAAuB,EAAE,EAAE,SAAS,CAAC,EACrF,sCAAsC,CACvC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,4EAA4E;AAC5E,2EAA2E;AAC3E,kBAAkB;AAClB,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;CAgBpB,CAAC;AAEF,IAAI,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;IACxG,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAC7C,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,EACzE,SAAS,CACV,CAAC;IACF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAmB,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,KAAK,EAAE,CAAC;IAEb,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,+CAA+C,CAAC,CAAC;IAE9E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;IACxC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IACxE,MAAM,KAAK,GAAI,MAAM,CAAC,OAAkD,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAEvC,6EAA6E;IAC7E,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;IAErG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;IACtF,IAAI,OAAO,GAAgB,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,qBAAqB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAC7C,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,mBAAmB,EAAE,GAAG,EAAE,EAC5E,SAAS,CACV,CAAC;QACF,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAE5F,oDAAoD;QACpD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QAEvB,MAAM,WAAW,CAAC,CAAC,iCAAiC;QACpD,2DAA2D;QAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC7E,MAAM,KAAK,GAAI,MAAM,CAAC,OAAkD,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAE5C,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,KAAK,EAAE,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prose-segments.test.d.ts","sourceRoot":"","sources":["../../test/prose-segments.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { splitProseSegments } from '../src/prose-segments.js';
|
|
4
|
+
const textBlock = (text) => ({ type: 'text', text });
|
|
5
|
+
const toolUse = (id) => ({ type: 'tool_use', id, name: 'workspace--write', input: {} });
|
|
6
|
+
const toolResult = (id) => ({ type: 'tool_result', toolUseId: id, content: 'done', isError: false });
|
|
7
|
+
const thinking = () => ({ type: 'thinking', thinking: '', signature: 'x' });
|
|
8
|
+
test('item 4: prose before a tool_use is not dropped or merged — it becomes its own ordered segment', () => {
|
|
9
|
+
// Shape of the real captured turn (docs/reports/item4-failing-transcript.jsonl),
|
|
10
|
+
// as the membrane accumulates it into response.content across tool rounds:
|
|
11
|
+
// [text FIRST, tool_use, tool_result, thinking, tool_use, tool_result, text SECOND]
|
|
12
|
+
const content = [
|
|
13
|
+
textBlock('FIRST_MESSAGE_BEFORE_TOOL.'),
|
|
14
|
+
toolUse('t1'),
|
|
15
|
+
toolResult('t1'),
|
|
16
|
+
thinking(),
|
|
17
|
+
toolUse('t2'),
|
|
18
|
+
toolResult('t2'),
|
|
19
|
+
textBlock('SECOND_MESSAGE_AFTER_TOOL.'),
|
|
20
|
+
];
|
|
21
|
+
const segments = splitProseSegments(content);
|
|
22
|
+
// Old behaviour joined all text into ONE trailing post
|
|
23
|
+
// ("FIRST_MESSAGE_BEFORE_TOOL.\nSECOND_MESSAGE_AFTER_TOOL."); the fix keeps
|
|
24
|
+
// them as two separate messages IN ORDER.
|
|
25
|
+
assert.deepEqual(segments, ['FIRST_MESSAGE_BEFORE_TOOL.', 'SECOND_MESSAGE_AFTER_TOOL.']);
|
|
26
|
+
});
|
|
27
|
+
test('three interleaved messages (msgA → [tool] → msgB → [tool] → msgC) all survive in order', () => {
|
|
28
|
+
const content = [
|
|
29
|
+
textBlock('A'),
|
|
30
|
+
toolUse('t1'), toolResult('t1'),
|
|
31
|
+
textBlock('B'),
|
|
32
|
+
toolUse('t2'), toolResult('t2'),
|
|
33
|
+
textBlock('C'),
|
|
34
|
+
];
|
|
35
|
+
assert.deepEqual(splitProseSegments(content), ['A', 'B', 'C']);
|
|
36
|
+
});
|
|
37
|
+
test('contiguous text blocks merge into one segment; whitespace-only runs are dropped', () => {
|
|
38
|
+
const content = [
|
|
39
|
+
textBlock('line one'),
|
|
40
|
+
textBlock('line two'),
|
|
41
|
+
toolUse('t1'), toolResult('t1'),
|
|
42
|
+
textBlock(' '),
|
|
43
|
+
];
|
|
44
|
+
assert.deepEqual(splitProseSegments(content), ['line one\nline two']);
|
|
45
|
+
});
|
|
46
|
+
test('a plain no-tool turn yields a single segment (unchanged behaviour)', () => {
|
|
47
|
+
assert.deepEqual(splitProseSegments([textBlock('just a reply')]), ['just a reply']);
|
|
48
|
+
});
|
|
49
|
+
test('a tool-only turn (no prose) yields no segments', () => {
|
|
50
|
+
assert.deepEqual(splitProseSegments([toolUse('t1'), toolResult('t1')]), []);
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=prose-segments.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prose-segments.test.js","sourceRoot":"","sources":["../../test/prose-segments.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusal-rewind.test.d.ts","sourceRoot":"","sources":["../../test/refusal-rewind.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { AgentFramework } from '../src/framework.js';
|
|
4
|
+
/**
|
|
5
|
+
* The refusal auto-rewind sheds the newest turn (in sequence, newest first,
|
|
6
|
+
* INCLUDING the agent's own turns) as a COMPLETE exchange — a tool_result is
|
|
7
|
+
* removed together with its paired tool_use turn, so no orphaned tool_use /
|
|
8
|
+
* signed thinking block is left behind. It maintains exactly ONE consolidated
|
|
9
|
+
* marker per episode. These exercise the private primitives on a prototype
|
|
10
|
+
* instance with a minimal in-memory ContextManager stub.
|
|
11
|
+
*/
|
|
12
|
+
function makeAgentHarness(messages) {
|
|
13
|
+
const removed = [];
|
|
14
|
+
const added = [];
|
|
15
|
+
const edited = [];
|
|
16
|
+
const cm = {
|
|
17
|
+
getAllMessages: () => messages,
|
|
18
|
+
removeMessage: (id) => {
|
|
19
|
+
removed.push(id);
|
|
20
|
+
const i = messages.findIndex((m) => m.id === id);
|
|
21
|
+
if (i >= 0)
|
|
22
|
+
messages.splice(i, 1);
|
|
23
|
+
},
|
|
24
|
+
addMessage: (participant, content, meta) => {
|
|
25
|
+
const id = `marker-${added.length}`;
|
|
26
|
+
added.push({ participant, content, meta });
|
|
27
|
+
messages.push({ id, participant, content, metadata: meta });
|
|
28
|
+
return id;
|
|
29
|
+
},
|
|
30
|
+
editMessage: (id, content) => {
|
|
31
|
+
edited.push({ id, content });
|
|
32
|
+
const m = messages.find((x) => x.id === id);
|
|
33
|
+
if (m)
|
|
34
|
+
m.content = content;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const fw = Object.create(AgentFramework.prototype);
|
|
38
|
+
fw.rewindEpisode = new Map();
|
|
39
|
+
const agent = { name: 'labclaude', getContextManager: () => cm };
|
|
40
|
+
return { fw, agent, removed, added, edited, messages };
|
|
41
|
+
}
|
|
42
|
+
test('shed: tool_result removes its paired tool_use turn too (complete exchange)', () => {
|
|
43
|
+
const msgs = [
|
|
44
|
+
{ id: 'u1', participant: 'simulect', content: [{ type: 'text', text: 'do it' }], metadata: { messageId: '1' } },
|
|
45
|
+
{ id: 'a1', participant: 'labclaude', content: [{ type: 'thinking', thinking: '…' }, { type: 'tool_use', name: 'shell', id: 't1' }] },
|
|
46
|
+
{ id: 'r1', participant: 'user', content: [{ type: 'tool_result', toolUseId: 't1', content: 'X'.repeat(320 * 1024) }] },
|
|
47
|
+
];
|
|
48
|
+
const { fw, agent, removed, added } = makeAgentHarness(msgs);
|
|
49
|
+
const rec = fw.shedNewestTurn(agent);
|
|
50
|
+
assert.equal(rec.kind, 'tool');
|
|
51
|
+
assert.deepEqual([...removed].sort(), ['a1', 'r1'], 'removes the tool_result AND its tool_use turn — no orphan');
|
|
52
|
+
assert.deepEqual([...rec.removedIds].sort(), ['a1', 'r1']);
|
|
53
|
+
assert.equal(added.length, 0, 'shedNewestTurn must not add a marker');
|
|
54
|
+
});
|
|
55
|
+
test('shed: human message → kind=human + discordRef, single removal', () => {
|
|
56
|
+
const msgs = [
|
|
57
|
+
{ id: 'a0', participant: 'labclaude', content: [{ type: 'text', text: 'hi' }] },
|
|
58
|
+
{ id: 'm2', participant: 'simulect', content: [{ type: 'text', text: 'sketchy' }], metadata: { messageId: '999', channelId: 'discord:g:c' } },
|
|
59
|
+
];
|
|
60
|
+
const { fw, agent, removed } = makeAgentHarness(msgs);
|
|
61
|
+
const rec = fw.shedNewestTurn(agent);
|
|
62
|
+
assert.equal(rec.kind, 'human');
|
|
63
|
+
assert.deepEqual(removed, ['m2']);
|
|
64
|
+
assert.deepEqual(rec.discordRef, { channelId: 'discord:g:c', messageId: '999' });
|
|
65
|
+
});
|
|
66
|
+
test('shed: agent turns ARE shed too (newest-first includes the agent own turns)', () => {
|
|
67
|
+
const msgs = [
|
|
68
|
+
{ id: 'u1', participant: 'simulect', content: [{ type: 'text', text: 'q' }], metadata: { messageId: '1' } },
|
|
69
|
+
{ id: 'a1', participant: 'labclaude', content: [{ type: 'text', text: 'my narration' }] },
|
|
70
|
+
];
|
|
71
|
+
const { fw, agent, removed } = makeAgentHarness(msgs);
|
|
72
|
+
const rec = fw.shedNewestTurn(agent);
|
|
73
|
+
assert.ok(rec, 'sheds the agent turn (no longer skipped)');
|
|
74
|
+
assert.deepEqual(removed, ['a1']);
|
|
75
|
+
});
|
|
76
|
+
test('shed: skips the episode marker and sheds the real turn beneath it', () => {
|
|
77
|
+
const msgs = [
|
|
78
|
+
{ id: 'm1', participant: 'simulect', content: [{ type: 'text', text: 'real msg' }], metadata: { messageId: '1' } },
|
|
79
|
+
{ id: 'mk', participant: 'user', content: [{ type: 'text', text: '[refusal-rewind] ...' }], metadata: { system: true, kind: 'refusal-rewind' } },
|
|
80
|
+
];
|
|
81
|
+
const { fw, agent, removed } = makeAgentHarness(msgs);
|
|
82
|
+
const rec = fw.shedNewestTurn(agent);
|
|
83
|
+
assert.ok(rec);
|
|
84
|
+
assert.deepEqual(removed, ['m1']);
|
|
85
|
+
});
|
|
86
|
+
test('shed: null when only the episode marker remains', () => {
|
|
87
|
+
const msgs = [{ id: 'mk', participant: 'user', content: [{ type: 'text', text: 'x' }], metadata: { system: true } }];
|
|
88
|
+
const { fw, agent } = makeAgentHarness(msgs);
|
|
89
|
+
assert.equal(fw.shedNewestTurn(agent), null);
|
|
90
|
+
});
|
|
91
|
+
test('marker: six rewinds produce ONE marker, updated in place (not six)', () => {
|
|
92
|
+
const msgs = [{ id: 'm1', participant: 'labclaude', content: [{ type: 'text', text: 'a' }] }];
|
|
93
|
+
const { fw, agent, added, edited } = makeAgentHarness(msgs);
|
|
94
|
+
const c1 = fw.updateRewindMarker(agent, 'cyber');
|
|
95
|
+
const c2 = fw.updateRewindMarker(agent, 'cyber');
|
|
96
|
+
fw.updateRewindMarker(agent, 'cyber');
|
|
97
|
+
fw.updateRewindMarker(agent, 'cyber');
|
|
98
|
+
fw.updateRewindMarker(agent, 'cyber');
|
|
99
|
+
const c6 = fw.updateRewindMarker(agent, 'cyber');
|
|
100
|
+
assert.equal(c1, 1);
|
|
101
|
+
assert.equal(c2, 2);
|
|
102
|
+
assert.equal(c6, 6);
|
|
103
|
+
assert.equal(added.length, 1, 'exactly ONE marker message added for the whole episode');
|
|
104
|
+
assert.equal(edited.length, 5, 'subsequent rewinds edit that one marker in place');
|
|
105
|
+
assert.match(edited[edited.length - 1].content[0].text, /6 most recent turn/, 'marker reflects the running total');
|
|
106
|
+
assert.equal(added[0].meta.kind, 'refusal-rewind');
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=refusal-rewind.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusal-rewind.test.js","sourceRoot":"","sources":["../../test/refusal-rewind.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=state-scaling-e2e.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-scaling-e2e.test.d.ts","sourceRoot":"","sources":["../../test/state-scaling-e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
|
|
@@ -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"}
|