@animalabs/agent-framework 0.6.3 → 0.6.5

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 (66) hide show
  1. package/README.md +5 -0
  2. package/dist/src/agent.d.ts +3 -1
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +27 -2
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/framework.d.ts +46 -1
  7. package/dist/src/framework.d.ts.map +1 -1
  8. package/dist/src/framework.js +280 -146
  9. package/dist/src/framework.js.map +1 -1
  10. package/dist/src/index.d.ts +1 -0
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +1 -0
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/modules/discord/index.d.ts +1 -0
  15. package/dist/src/modules/discord/index.d.ts.map +1 -1
  16. package/dist/src/modules/discord/index.js +4 -1
  17. package/dist/src/modules/discord/index.js.map +1 -1
  18. package/dist/src/modules/discord/types.d.ts +2 -0
  19. package/dist/src/modules/discord/types.d.ts.map +1 -1
  20. package/dist/src/modules/health/index.d.ts +2 -0
  21. package/dist/src/modules/health/index.d.ts.map +1 -1
  22. package/dist/src/modules/health/index.js +15 -4
  23. package/dist/src/modules/health/index.js.map +1 -1
  24. package/dist/src/timezone.d.ts +14 -0
  25. package/dist/src/timezone.d.ts.map +1 -0
  26. package/dist/src/timezone.js +74 -0
  27. package/dist/src/timezone.js.map +1 -0
  28. package/dist/src/types/agent.d.ts +5 -2
  29. package/dist/src/types/agent.d.ts.map +1 -1
  30. package/dist/src/types/framework.d.ts +15 -0
  31. package/dist/src/types/framework.d.ts.map +1 -1
  32. package/dist/test/activation-request-sanitize.test.d.ts +2 -0
  33. package/dist/test/activation-request-sanitize.test.d.ts.map +1 -0
  34. package/dist/test/activation-request-sanitize.test.js +89 -0
  35. package/dist/test/activation-request-sanitize.test.js.map +1 -0
  36. package/dist/test/agent-cache-ttl.test.js +18 -2
  37. package/dist/test/agent-cache-ttl.test.js.map +1 -1
  38. package/dist/test/ephemeral-settle.test.d.ts +2 -0
  39. package/dist/test/ephemeral-settle.test.d.ts.map +1 -0
  40. package/dist/test/ephemeral-settle.test.js +351 -0
  41. package/dist/test/ephemeral-settle.test.js.map +1 -0
  42. package/dist/test/health-snapshot.test.d.ts +2 -0
  43. package/dist/test/health-snapshot.test.d.ts.map +1 -0
  44. package/dist/test/health-snapshot.test.js +74 -0
  45. package/dist/test/health-snapshot.test.js.map +1 -0
  46. package/dist/test/maintenance-timer.test.js +49 -0
  47. package/dist/test/maintenance-timer.test.js.map +1 -1
  48. package/dist/test/overbudget-drain-breaker.test.d.ts +2 -0
  49. package/dist/test/overbudget-drain-breaker.test.d.ts.map +1 -0
  50. package/dist/test/overbudget-drain-breaker.test.js +157 -0
  51. package/dist/test/overbudget-drain-breaker.test.js.map +1 -0
  52. package/dist/test/timezone.test.d.ts +2 -0
  53. package/dist/test/timezone.test.d.ts.map +1 -0
  54. package/dist/test/timezone.test.js +18 -0
  55. package/dist/test/timezone.test.js.map +1 -0
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +1 -1
  58. package/src/agent.ts +30 -3
  59. package/src/framework.ts +313 -146
  60. package/src/index.ts +1 -0
  61. package/src/modules/discord/index.ts +4 -1
  62. package/src/modules/discord/types.ts +3 -0
  63. package/src/modules/health/index.ts +20 -4
  64. package/src/timezone.ts +90 -0
  65. package/src/types/agent.ts +6 -2
  66. package/src/types/framework.ts +17 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Live-path empty-text sanitize (PR #58, 8a89da0): the Anthropic API rejects
3
+ * empty text blocks with a 400, and one such block in the compiled window used
4
+ * to mute the agent entirely — [inference-failed] on every activation until
5
+ * the offending message aged out (field-observed 2026-07-10). The sanitize in
6
+ * buildActivationRequest strips empty/whitespace/non-string text blocks and
7
+ * drops messages left with no content, BEFORE the trailing-assistant check.
8
+ */
9
+ import { describe, it } from 'node:test';
10
+ import assert from 'node:assert/strict';
11
+ import { Agent } from '../src/agent.js';
12
+ /** buildActivationRequest only needs compile(); membrane is never touched. */
13
+ function agentCompiling(messages) {
14
+ const cm = {
15
+ compile: async () => ({ messages, systemInjections: [] }),
16
+ };
17
+ return new Agent({ name: 'tester', model: 'test-model', systemPrompt: 'sys' }, cm, {});
18
+ }
19
+ describe('buildActivationRequest empty-text sanitize', () => {
20
+ it('strips empty and whitespace-only text blocks, keeps real text', async () => {
21
+ const agent = agentCompiling([
22
+ {
23
+ participant: 'human',
24
+ content: [
25
+ { type: 'text', text: '' },
26
+ { type: 'text', text: ' \n\t ' },
27
+ { type: 'text', text: 'hello' },
28
+ ],
29
+ },
30
+ ]);
31
+ const request = await agent.buildActivationRequest([]);
32
+ assert.equal(request.messages.length, 1);
33
+ assert.deepEqual(request.messages[0].content, [{ type: 'text', text: 'hello' }]);
34
+ });
35
+ it('strips text blocks whose `text` is not a string (untyped history from disk)', async () => {
36
+ const agent = agentCompiling([
37
+ {
38
+ participant: 'human',
39
+ content: [
40
+ { type: 'text', text: null },
41
+ { type: 'text', text: 42 },
42
+ { type: 'text', text: 'kept' },
43
+ ],
44
+ },
45
+ ]);
46
+ const request = await agent.buildActivationRequest([]);
47
+ assert.deepEqual(request.messages[0].content, [{ type: 'text', text: 'kept' }]);
48
+ });
49
+ it('drops a message left with no content at all', async () => {
50
+ const agent = agentCompiling([
51
+ { participant: 'human', content: [{ type: 'text', text: '' }] },
52
+ { participant: 'human', content: [{ type: 'text', text: 'still here' }] },
53
+ ]);
54
+ const request = await agent.buildActivationRequest([]);
55
+ assert.equal(request.messages.length, 1);
56
+ assert.deepEqual(request.messages[0].content, [{ type: 'text', text: 'still here' }]);
57
+ });
58
+ it('passes non-text blocks through untouched — tool pairing is unaffected', async () => {
59
+ const toolUse = { type: 'tool_use', id: 't1', name: 'shell', input: {} };
60
+ const toolResult = { type: 'tool_result', toolUseId: 't1', content: 'ok' };
61
+ const agent = agentCompiling([
62
+ { participant: 'human', content: [{ type: 'text', text: 'run it' }] },
63
+ // Tool-only assistant turn whose accompanying text block is empty: the
64
+ // exact shape that produced the field incident.
65
+ { participant: 'tester', content: [{ type: 'text', text: '' }, toolUse] },
66
+ { participant: 'user', content: [toolResult] },
67
+ { participant: 'human', content: [{ type: 'text', text: 'and?' }] },
68
+ ]);
69
+ const request = await agent.buildActivationRequest([]);
70
+ assert.equal(request.messages.length, 4);
71
+ assert.deepEqual(request.messages[1].content, [toolUse]);
72
+ assert.deepEqual(request.messages[2].content, [toolResult]);
73
+ });
74
+ it('runs BEFORE the trailing-assistant check: a sanitize-exposed trailing assistant still gets [Continue]', async () => {
75
+ const agent = agentCompiling([
76
+ { participant: 'human', content: [{ type: 'text', text: 'hi' }] },
77
+ { participant: 'tester', content: [{ type: 'text', text: 'ok' }] },
78
+ // Empty trailing user message: dropped by the sanitize, which leaves the
79
+ // assistant turn last — the [Continue] guard must see THAT shape.
80
+ { participant: 'human', content: [{ type: 'text', text: ' ' }] },
81
+ ]);
82
+ const request = await agent.buildActivationRequest([]);
83
+ assert.equal(request.messages.length, 3);
84
+ const last = request.messages[request.messages.length - 1];
85
+ assert.equal(last.participant, 'user');
86
+ assert.deepEqual(last.content, [{ type: 'text', text: '[Continue]' }]);
87
+ });
88
+ });
89
+ //# sourceMappingURL=activation-request-sanitize.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activation-request-sanitize.test.js","sourceRoot":"","sources":["../../test/activation-request-sanitize.test.ts"],"names":[],"mappings":"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"}
@@ -15,11 +15,27 @@ describe('Agent cacheTtl forwarding', () => {
15
15
  assert.equal(request.promptCaching, true);
16
16
  assert.equal(request.cacheTtl, '1h');
17
17
  });
18
- it('omits cacheTtl entirely when unset, leaving the provider default', async () => {
18
+ it('defaults cacheTtl to 1h when unset', async () => {
19
19
  const agent = createAgent({});
20
20
  const request = await agent.buildActivationRequest([]);
21
21
  assert.equal(request.promptCaching, true);
22
- assert.equal('cacheTtl' in request, false);
22
+ assert.equal(request.cacheTtl, '1h');
23
+ });
24
+ it('preserves an explicit 5m override', async () => {
25
+ const agent = createAgent({ cacheTtl: '5m' });
26
+ const request = await agent.buildActivationRequest([]);
27
+ assert.equal(request.cacheTtl, '5m');
28
+ });
29
+ });
30
+ describe('Agent provider parameter forwarding', () => {
31
+ it('forwards stateless Responses reasoning and compaction settings unchanged', async () => {
32
+ const providerParams = {
33
+ reasoning: { effort: 'high', context: 'all_turns' },
34
+ context_management: [{ type: 'compaction', compact_threshold: 850000 }],
35
+ };
36
+ const agent = createAgent({ providerParams });
37
+ const request = await agent.buildActivationRequest([]);
38
+ assert.deepEqual(request.providerParams, providerParams);
23
39
  });
24
40
  });
25
41
  //# sourceMappingURL=agent-cache-ttl.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-cache-ttl.test.js","sourceRoot":"","sources":["../../test/agent-cache-ttl.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKxC,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG;IACzB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;CACjC,CAAC;AAE/B,SAAS,WAAW,CAAC,MAA4B;IAC/C,OAAO,IAAI,KAAK,CACd,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,EACvE,kBAAkB,EAClB,EAAc,CACf,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,OAAO,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"agent-cache-ttl.test.js","sourceRoot":"","sources":["../../test/agent-cache-ttl.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKxC,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG;IACzB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;CACjC,CAAC;AAE/B,SAAS,WAAW,CAAC,MAA4B;IAC/C,OAAO,IAAI,KAAK,CACd,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,EACvE,kBAAkB,EAClB,EAAc,CACf,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,cAAc,GAAG;YACrB,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE;YACnD,kBAAkB,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;SACxE,CAAC;QACF,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ephemeral-settle.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ephemeral-settle.test.d.ts","sourceRoot":"","sources":["../../test/ephemeral-settle.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,351 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join, relative } from 'node:path';
6
+ import { AgentFramework } from '../src/index.js';
7
+ import { createMockResponse, MockMembrane, MockYieldingStream } from './helpers/mock-membrane.js';
8
+ class EphemeralToolModule {
9
+ endTurnTool;
10
+ name = 'test';
11
+ calls = [];
12
+ constructor(endTurnTool = 'finish') {
13
+ this.endTurnTool = endTurnTool;
14
+ }
15
+ async start(_ctx) { }
16
+ async stop() { }
17
+ getTools() {
18
+ return [
19
+ {
20
+ name: 'echo',
21
+ description: 'Echo input',
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: { message: { type: 'string' } },
25
+ },
26
+ },
27
+ {
28
+ name: this.endTurnTool,
29
+ description: 'Finish the turn',
30
+ inputSchema: { type: 'object', properties: {} },
31
+ },
32
+ ];
33
+ }
34
+ async handleToolCall(call) {
35
+ this.calls.push(call);
36
+ const baseName = call.name.includes('--')
37
+ ? call.name.slice(call.name.lastIndexOf('--') + 2)
38
+ : call.name;
39
+ if (baseName === this.endTurnTool) {
40
+ return { success: true, data: { finished: true }, endTurn: true };
41
+ }
42
+ return { success: true, data: { echoed: call.input } };
43
+ }
44
+ async onProcess(_event, _state) {
45
+ return {};
46
+ }
47
+ }
48
+ class ThrowingMembrane extends MockMembrane {
49
+ errors;
50
+ constructor(errors) {
51
+ super();
52
+ this.errors = [...errors];
53
+ }
54
+ streamYielding(request, _options) {
55
+ this.calls.push(request);
56
+ throw this.errors.shift() ?? new Error('unexpected stream attempt');
57
+ }
58
+ }
59
+ class RetryOncePolicy {
60
+ maxRetries = 1;
61
+ onInferenceError(_error, _agentName, attempt) {
62
+ if (attempt < this.maxRetries) {
63
+ return { retry: true, delayMs: 0 };
64
+ }
65
+ return { retry: false };
66
+ }
67
+ }
68
+ function tempStorePath(prefix) {
69
+ const tempDir = mkdtempSync(join(tmpdir(), prefix));
70
+ return { tempDir, storePath: join(tempDir, 'store.chronicle') };
71
+ }
72
+ async function createEphemeral(framework, name = 'worker') {
73
+ const created = await framework.createEphemeralAgent({
74
+ name,
75
+ model: 'test-model',
76
+ systemPrompt: 'Do the task.',
77
+ allowedTools: 'all',
78
+ });
79
+ created.contextManager.addMessage('user', [{ type: 'text', text: 'Run once.' }]);
80
+ return created;
81
+ }
82
+ async function runStartedEphemeral(framework, agent, contextManager) {
83
+ const promise = framework.runEphemeralToCompletion(agent, contextManager);
84
+ framework.start();
85
+ return promise;
86
+ }
87
+ async function stopAndRemove(framework, tempDir) {
88
+ await framework.stop();
89
+ rmSync(tempDir, { recursive: true, force: true });
90
+ }
91
+ describe('runEphemeralToCompletion settle signal', () => {
92
+ it('resolves plain text completion and deregisters the agent', async () => {
93
+ const { tempDir, storePath } = tempStorePath('ephemeral-plain-');
94
+ const membrane = new MockMembrane();
95
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Plain final answer' }]));
96
+ const framework = await AgentFramework.create({
97
+ storePath,
98
+ membrane: membrane.asMembrane(),
99
+ agents: [],
100
+ modules: [new EphemeralToolModule()],
101
+ syncIntervalMs: 0,
102
+ });
103
+ try {
104
+ const { agent, contextManager } = await createEphemeral(framework);
105
+ const result = await runStartedEphemeral(framework, agent, contextManager);
106
+ assert.deepStrictEqual(result, {
107
+ speech: 'Plain final answer',
108
+ toolCallsCount: 0,
109
+ });
110
+ assert.strictEqual(framework.getAgent(agent.name), null);
111
+ }
112
+ finally {
113
+ await stopAndRemove(framework, tempDir);
114
+ }
115
+ });
116
+ it('resolves after tool rounds with final speech and dispatched call count', async () => {
117
+ const { tempDir, storePath } = tempStorePath('ephemeral-tools-');
118
+ const membrane = new MockMembrane();
119
+ const tools = new EphemeralToolModule();
120
+ membrane.pushResponse(createMockResponse([
121
+ { type: 'text', text: 'Using the first tool.' },
122
+ { type: 'tool_use', id: 'call_1', name: 'test--echo', input: { message: 'one' } },
123
+ ], 'tool_use'));
124
+ membrane.pushResponse(createMockResponse([
125
+ { type: 'text', text: 'Using the second tool.' },
126
+ { type: 'tool_use', id: 'call_2', name: 'test--echo', input: { message: 'two' } },
127
+ ], 'tool_use'));
128
+ membrane.pushResponse(createMockResponse([
129
+ { type: 'text', text: 'Post-tool final answer' },
130
+ ]));
131
+ const framework = await AgentFramework.create({
132
+ storePath,
133
+ membrane: membrane.asMembrane(),
134
+ agents: [],
135
+ modules: [tools],
136
+ syncIntervalMs: 0,
137
+ });
138
+ try {
139
+ const { agent, contextManager } = await createEphemeral(framework);
140
+ const result = await runStartedEphemeral(framework, agent, contextManager);
141
+ assert.deepStrictEqual(result, {
142
+ speech: 'Post-tool final answer',
143
+ toolCallsCount: 2,
144
+ });
145
+ assert.strictEqual(tools.calls.length, 2);
146
+ assert.strictEqual(framework.getAgent(agent.name), null);
147
+ }
148
+ finally {
149
+ await stopAndRemove(framework, tempDir);
150
+ }
151
+ });
152
+ it('resolves through the endTurn tool-result path', async () => {
153
+ const { tempDir, storePath } = tempStorePath('ephemeral-endturn-');
154
+ const membrane = new MockMembrane();
155
+ const tools = new EphemeralToolModule();
156
+ membrane.pushResponse(createMockResponse([
157
+ { type: 'tool_use', id: 'call_finish', name: 'test--finish', input: {} },
158
+ ], 'tool_use'));
159
+ const framework = await AgentFramework.create({
160
+ storePath,
161
+ membrane: membrane.asMembrane(),
162
+ agents: [],
163
+ modules: [tools],
164
+ syncIntervalMs: 0,
165
+ });
166
+ try {
167
+ const { agent, contextManager } = await createEphemeral(framework);
168
+ const result = await runStartedEphemeral(framework, agent, contextManager);
169
+ assert.deepStrictEqual(result, {
170
+ speech: '',
171
+ toolCallsCount: 1,
172
+ });
173
+ assert.strictEqual(tools.calls.length, 1);
174
+ assert.strictEqual(framework.getAgent(agent.name), null);
175
+ }
176
+ finally {
177
+ await stopAndRemove(framework, tempDir);
178
+ }
179
+ });
180
+ it('rejects only after error policy exhausts retries', async () => {
181
+ const { tempDir, storePath } = tempStorePath('ephemeral-errors-');
182
+ const membrane = new ThrowingMembrane([
183
+ new Error('temporary failure'),
184
+ new Error('final failure'),
185
+ ]);
186
+ const framework = await AgentFramework.create({
187
+ storePath,
188
+ membrane: membrane.asMembrane(),
189
+ agents: [],
190
+ modules: [new EphemeralToolModule()],
191
+ errorPolicy: new RetryOncePolicy(),
192
+ syncIntervalMs: 0,
193
+ });
194
+ try {
195
+ const { agent, contextManager } = await createEphemeral(framework);
196
+ const run = runStartedEphemeral(framework, agent, contextManager);
197
+ await assert.rejects(run, /final failure/);
198
+ assert.strictEqual(membrane.calls.length, 2, 'the retry attempt should run before settling');
199
+ assert.strictEqual(framework.getAgent(agent.name), null);
200
+ }
201
+ finally {
202
+ await stopAndRemove(framework, tempDir);
203
+ }
204
+ });
205
+ it('does not register a trace listener for completion control flow', async () => {
206
+ const { tempDir, storePath } = tempStorePath('ephemeral-trace-spy-');
207
+ const membrane = new MockMembrane();
208
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Trace independent' }]));
209
+ const framework = await AgentFramework.create({
210
+ storePath,
211
+ membrane: membrane.asMembrane(),
212
+ agents: [],
213
+ modules: [new EphemeralToolModule()],
214
+ syncIntervalMs: 0,
215
+ });
216
+ let onTraceCalls = 0;
217
+ const originalOnTrace = framework.onTrace.bind(framework);
218
+ framework.onTrace = ((listener) => {
219
+ onTraceCalls++;
220
+ return originalOnTrace(listener);
221
+ });
222
+ try {
223
+ const { agent, contextManager } = await createEphemeral(framework);
224
+ const result = await runStartedEphemeral(framework, agent, contextManager);
225
+ assert.strictEqual(result.speech, 'Trace independent');
226
+ assert.strictEqual(onTraceCalls, 0);
227
+ assert.strictEqual(framework.getAgent(agent.name), null);
228
+ }
229
+ finally {
230
+ await stopAndRemove(framework, tempDir);
231
+ }
232
+ });
233
+ it('startup watchdog rejects and deregisters when inference never starts', async () => {
234
+ const { tempDir, storePath } = tempStorePath('ephemeral-startup-');
235
+ const membrane = new MockMembrane();
236
+ const framework = await AgentFramework.create({
237
+ storePath,
238
+ membrane: membrane.asMembrane(),
239
+ agents: [],
240
+ modules: [new EphemeralToolModule()],
241
+ syncIntervalMs: 0,
242
+ });
243
+ try {
244
+ const { agent, contextManager } = await createEphemeral(framework);
245
+ // Framework deliberately NOT started: the inference request is queued
246
+ // but nothing drives it, so the (injected, short) startup watchdog is
247
+ // the only thing that can end the run.
248
+ const run = framework.runEphemeralToCompletion(agent, contextManager, {
249
+ startupTimeoutMs: 50,
250
+ });
251
+ await assert.rejects(run, /failed to start inference/);
252
+ assert.strictEqual(framework.getAgent(agent.name), null);
253
+ }
254
+ finally {
255
+ await stopAndRemove(framework, tempDir);
256
+ }
257
+ });
258
+ it('a context-budget restart mid-run neither rejects nor loses the tool count', async () => {
259
+ const { tempDir, storePath } = tempStorePath('ephemeral-budget-');
260
+ // Serve exactly one queued response per stream: the budget restart opens
261
+ // a SECOND stream, and the stock mock hands all remaining responses to
262
+ // the first one.
263
+ class SequentialStreamMembrane extends MockMembrane {
264
+ streamIdx = 0;
265
+ streamYielding(request, _options) {
266
+ this.calls.push(request);
267
+ const stream = new MockYieldingStream(this.responses.slice(this.streamIdx, ++this.streamIdx));
268
+ this.lastStream = stream;
269
+ return stream;
270
+ }
271
+ }
272
+ const membrane = new SequentialStreamMembrane();
273
+ const tools = new EphemeralToolModule();
274
+ // Stream 1: a tool round whose usage event (10 input tokens) exceeds the
275
+ // agent's maxStreamTokens below → after the tool result lands, the
276
+ // framework cancels the stream and queues a context_budget_restart.
277
+ membrane.pushResponse(createMockResponse([
278
+ { type: 'text', text: 'Working…' },
279
+ { type: 'tool_use', id: 'call_1', name: 'test--echo', input: { message: 'one' } },
280
+ ], 'tool_use'));
281
+ // Stream 2 (post-restart): the final answer.
282
+ membrane.pushResponse(createMockResponse([
283
+ { type: 'text', text: 'Recovered final answer' },
284
+ ]));
285
+ const framework = await AgentFramework.create({
286
+ storePath,
287
+ membrane: membrane.asMembrane(),
288
+ agents: [],
289
+ modules: [tools],
290
+ syncIntervalMs: 0,
291
+ });
292
+ try {
293
+ const created = await framework.createEphemeralAgent({
294
+ name: 'worker',
295
+ model: 'test-model',
296
+ systemPrompt: 'Do the task.',
297
+ allowedTools: 'all',
298
+ maxStreamTokens: 5,
299
+ });
300
+ created.contextManager.addMessage('user', [{ type: 'text', text: 'Run once.' }]);
301
+ // Without the framework-cancel marker, the restart's aborted event
302
+ // settles the run as exhausted ("Stream aborted: user") instead.
303
+ const result = await runStartedEphemeral(framework, created.agent, created.contextManager);
304
+ assert.deepStrictEqual(result, {
305
+ speech: 'Recovered final answer',
306
+ toolCallsCount: 1,
307
+ });
308
+ assert.strictEqual(membrane.calls.length, 2, 'restart should open a second stream');
309
+ assert.strictEqual(framework.getAgent(created.agent.name), null);
310
+ }
311
+ finally {
312
+ await stopAndRemove(framework, tempDir);
313
+ }
314
+ });
315
+ });
316
+ describe('trace control-flow guard', () => {
317
+ it('does not use this.onTrace inside src control flow', () => {
318
+ const root = process.cwd();
319
+ const srcRoot = join(root, 'src');
320
+ // Structural allowance, not an exact source line (which breaks on any
321
+ // reformat and turns the guard into a boy-who-cried-wolf): re-EXPOSING
322
+ // the subscription API — forwarding a caller-supplied listener, as the
323
+ // ModuleContext plumbing does — is fine. What the guard forbids is the
324
+ // framework subscribing its OWN listener to drive control flow.
325
+ const isSubscriptionPlumbing = (line) => /onTrace:\s*\([^)]*\)\s*=>\s*this\.onTrace\(/.test(line);
326
+ const matches = [];
327
+ const visit = (dir) => {
328
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
329
+ const path = join(dir, entry.name);
330
+ if (entry.isDirectory()) {
331
+ visit(path);
332
+ continue;
333
+ }
334
+ if (!entry.isFile() || !path.endsWith('.ts'))
335
+ continue;
336
+ const rel = relative(root, path);
337
+ const lines = readFileSync(path, 'utf8').split('\n');
338
+ lines.forEach((line, index) => {
339
+ if (!line.includes('this.onTrace('))
340
+ return;
341
+ if (isSubscriptionPlumbing(line))
342
+ return;
343
+ matches.push(`${rel}:${index + 1}: ${line.trim()}`);
344
+ });
345
+ }
346
+ };
347
+ visit(srcRoot);
348
+ assert.deepStrictEqual(matches, []);
349
+ });
350
+ });
351
+ //# sourceMappingURL=ephemeral-settle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ephemeral-settle.test.js","sourceRoot":"","sources":["../../test/ephemeral-settle.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=health-snapshot.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-snapshot.test.d.ts","sourceRoot":"","sources":["../../test/health-snapshot.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { HealthModule } from '../src/modules/health/index.js';
4
+ /**
5
+ * buildSnapshot only needs queryInferenceLogs (store/ctx paths are skipped
6
+ * when includeSubagents=false and no ctx is bound), so a minimal framework
7
+ * stub exercises the projectSummary presentation path directly.
8
+ */
9
+ function frameworkStub(entries) {
10
+ return {
11
+ queryInferenceLogs: () => ({ entries, total: entries.length, hasMore: false }),
12
+ };
13
+ }
14
+ function snapshotCall() {
15
+ return {
16
+ id: 'call-1',
17
+ name: 'snapshot',
18
+ input: { includeSubagents: false, includeTokens: false },
19
+ };
20
+ }
21
+ describe('HealthModule snapshot timestamp resilience', () => {
22
+ it('degrades gracefully when a legacy record lacks a timestamp', async () => {
23
+ const entries = [
24
+ {
25
+ sequence: 0,
26
+ // Legacy persisted record: no timestamp field at all.
27
+ entry: { agentName: 'legacy', requestId: 'r0', success: false, error: 'boom', request: {}, durationMs: 1 },
28
+ },
29
+ {
30
+ sequence: 1,
31
+ entry: { timestamp: 1751900000000, agentName: 'ok', requestId: 'r1', success: true, request: {}, durationMs: 1 },
32
+ },
33
+ ];
34
+ const health = new HealthModule({ timeZone: 'UTC' });
35
+ health.bind(frameworkStub(entries), {});
36
+ const result = await health.handleToolCall(snapshotCall());
37
+ // One bad record must not sink the whole snapshot.
38
+ assert.equal(result.success, true, `snapshot failed: ${result.error}`);
39
+ const data = result.data;
40
+ assert.equal(data.inferences.successCount, 1);
41
+ assert.equal(data.inferences.errorCount, 1);
42
+ // The bad record's timestamp degrades to null (undefined ?? null).
43
+ assert.equal(data.inferences.recentErrors[0].timestamp, null);
44
+ });
45
+ it('emits the raw value for a non-numeric timestamp instead of throwing', async () => {
46
+ const entries = [
47
+ {
48
+ sequence: 0,
49
+ entry: { timestamp: 'not-a-date', agentName: 'weird', requestId: 'r2', success: false, error: 'x', request: {}, durationMs: 1 },
50
+ },
51
+ ];
52
+ const health = new HealthModule({ timeZone: 'UTC' });
53
+ health.bind(frameworkStub(entries), {});
54
+ const result = await health.handleToolCall(snapshotCall());
55
+ assert.equal(result.success, true, `snapshot failed: ${result.error}`);
56
+ const data = result.data;
57
+ assert.equal(data.inferences.recentErrors[0].timestamp, 'not-a-date');
58
+ });
59
+ it('still formats valid timestamps in the configured zone', async () => {
60
+ const entries = [
61
+ {
62
+ sequence: 0,
63
+ entry: { timestamp: Date.UTC(2026, 0, 2, 3, 4, 5), agentName: 'ok', requestId: 'r3', success: false, error: 'y', request: {}, durationMs: 1 },
64
+ },
65
+ ];
66
+ const health = new HealthModule({ timeZone: 'UTC' });
67
+ health.bind(frameworkStub(entries), {});
68
+ const result = await health.handleToolCall(snapshotCall());
69
+ assert.equal(result.success, true, `snapshot failed: ${result.error}`);
70
+ const data = result.data;
71
+ assert.equal(data.inferences.recentErrors[0].timestamp, '2026-01-02T03:04:05.000+00:00 [UTC]');
72
+ });
73
+ });
74
+ //# sourceMappingURL=health-snapshot.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-snapshot.test.js","sourceRoot":"","sources":["../../test/health-snapshot.test.ts"],"names":[],"mappings":"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"}