@pi-unipi/background-tasks 2.6.2 → 2.6.4

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 (59) hide show
  1. package/package.json +3 -2
  2. package/src/tools.ts +3 -1
  3. package/src/__tests__/anthropic-attribution.test.ts +0 -195
  4. package/src/__tests__/config.test.ts +0 -137
  5. package/src/__tests__/core.test.ts +0 -493
  6. package/src/__tests__/delegate-artifacts.test.ts +0 -528
  7. package/src/__tests__/delegate-budget.test.ts +0 -456
  8. package/src/__tests__/delegate-launch.test.ts +0 -676
  9. package/src/__tests__/delegate-result-package.test.ts +0 -350
  10. package/src/__tests__/delegate-seed.test.ts +0 -392
  11. package/src/__tests__/durable-fs.test.ts +0 -559
  12. package/src/__tests__/extension-api.test.ts +0 -579
  13. package/src/__tests__/fusion-artifacts.test.ts +0 -1039
  14. package/src/__tests__/fusion-budget.test.ts +0 -1356
  15. package/src/__tests__/fusion-claude-cache.test.ts +0 -320
  16. package/src/__tests__/fusion-config.test.ts +0 -335
  17. package/src/__tests__/fusion-context-prompts.test.ts +0 -670
  18. package/src/__tests__/fusion-evaluation.test.ts +0 -315
  19. package/src/__tests__/fusion-extraction-equivalence.test.ts +0 -58
  20. package/src/__tests__/fusion-golden-bytes.test.ts +0 -35
  21. package/src/__tests__/fusion-high-cardinality.test.ts +0 -192
  22. package/src/__tests__/fusion-model-selector.test.ts +0 -205
  23. package/src/__tests__/fusion-orchestrator.test.ts +0 -1194
  24. package/src/__tests__/fusion-rpc.test.ts +0 -369
  25. package/src/__tests__/fusion-sdk.test.ts +0 -1226
  26. package/src/__tests__/fusion-v5-core.test.ts +0 -219
  27. package/src/__tests__/fusion-validate-orchestrator.test.ts +0 -240
  28. package/src/__tests__/fusion-web-fetch.test.ts +0 -485
  29. package/src/__tests__/fusion-workflows.test.ts +0 -59
  30. package/src/__tests__/helpers/delegate-deterministic-seed.ts +0 -109
  31. package/src/__tests__/helpers/delegate-seed-subprocess.ts +0 -10
  32. package/src/__tests__/helpers/fusion-canonical-subprocess.ts +0 -21
  33. package/src/__tests__/helpers/fusion-canonical.ts +0 -140
  34. package/src/__tests__/helpers/fusion-fake-pi.ts +0 -279
  35. package/src/__tests__/helpers/fusion-golden-corpus.ts +0 -500
  36. package/src/__tests__/helpers/fusion-high-cardinality.ts +0 -140
  37. package/src/__tests__/helpers/normalize.ts +0 -22
  38. package/src/__tests__/helpers/pi-hook-contract-evidence.json +0 -18
  39. package/src/__tests__/pi-launch.test.ts +0 -202
  40. package/src/__tests__/registry.test.ts +0 -1580
  41. package/src/__tests__/scripted-provider/delegate-ambient-provider.test.ts +0 -130
  42. package/src/__tests__/scripted-provider/delegate-child-guard.test.ts +0 -631
  43. package/src/__tests__/scripted-provider/delegate-guard-provider.ts +0 -403
  44. package/src/__tests__/scripted-provider/follow-up.test.ts +0 -448
  45. package/src/__tests__/scripted-provider/fusion-output-recovery.test.ts +0 -132
  46. package/src/__tests__/scripted-provider/fusion-reason.test.ts +0 -310
  47. package/src/__tests__/scripted-provider/fusion-runtime-guard.test.ts +0 -163
  48. package/src/__tests__/scripted-provider/hook-contract-provider.ts +0 -179
  49. package/src/__tests__/scripted-provider/hook-probe-a.ts +0 -3
  50. package/src/__tests__/scripted-provider/hook-probe-b.ts +0 -3
  51. package/src/__tests__/scripted-provider/hook-probe-extension.ts +0 -126
  52. package/src/__tests__/scripted-provider/output-recovery-provider.ts +0 -153
  53. package/src/__tests__/scripted-provider/pi-hook-contract-evidence.json +0 -18
  54. package/src/__tests__/scripted-provider/pi-hook-contract.test.ts +0 -477
  55. package/src/__tests__/scripted-provider/runtime-guard-probe.ts +0 -28
  56. package/src/__tests__/scripted-provider/runtime-guard-provider.ts +0 -49
  57. package/src/__tests__/scripted-provider/scripted-provider-extension.ts +0 -408
  58. package/src/__tests__/task-manager.test.ts +0 -479
  59. package/src/__tests__/windows-taskkill.test.ts +0 -161
@@ -1,485 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { createHash } from 'node:crypto';
3
- import * as http from 'node:http';
4
- import type { AddressInfo } from 'node:net';
5
- import { describe, it } from 'node:test';
6
-
7
- import {
8
- FUSION_WEB_FETCH_MAX_REDIRECTS,
9
- FUSION_WEB_FETCH_MAX_RESPONSE_BYTES,
10
- FUSION_WEB_FETCH_MAX_OUTPUT_BYTES,
11
- FUSION_WEB_FETCH_TIMEOUT_MS,
12
- FusionWebFetchError,
13
- type FusionWebFetchErrorCode,
14
- type FusionWebFetchOptions,
15
- fusionWebFetch,
16
- } from '../fusion/web-fetch.js';
17
-
18
- type RouteHandler = (request: http.IncomingMessage, response: http.ServerResponse) => void;
19
-
20
- async function withServer<T>(routes: Record<string, RouteHandler>, run: (baseUrl: string) => Promise<T>): Promise<T> {
21
- const server = http.createServer((request, response) => {
22
- const path = request.url?.split('?')[0] ?? '/';
23
- const handler = routes[path];
24
- if (handler === undefined) {
25
- response.writeHead(404, { 'content-type': 'text/plain' });
26
- response.end('missing route');
27
- return;
28
- }
29
- handler(request, response);
30
- });
31
-
32
- await new Promise<void>((resolve, reject) => {
33
- server.once('error', reject);
34
- server.listen(0, '127.0.0.1', () => {
35
- server.off('error', reject);
36
- resolve();
37
- });
38
- });
39
-
40
- try {
41
- const address = server.address();
42
- assert.ok(address !== null && typeof address === 'object');
43
- return await run(`http://public.test:${String((address as AddressInfo).port)}`);
44
- } finally {
45
- await new Promise<void>((resolve, reject) => {
46
- server.close((error) => {
47
- if (error !== undefined) reject(error);
48
- else resolve();
49
- });
50
- });
51
- }
52
- }
53
-
54
- function localOptions(extra: FusionWebFetchOptions = {}): FusionWebFetchOptions {
55
- return {
56
- lookup: async () => [{ address: '127.0.0.1', family: 4 }],
57
- allowBlockedAddressesForTests: true,
58
- ...extra,
59
- };
60
- }
61
-
62
- async function expectError(
63
- promise: Promise<unknown>,
64
- code: FusionWebFetchErrorCode,
65
- ): Promise<FusionWebFetchError> {
66
- let captured: FusionWebFetchError | undefined;
67
- await assert.rejects(promise, (error: unknown) => {
68
- assert.ok(error instanceof FusionWebFetchError);
69
- assert.equal(error.code, code);
70
- captured = error;
71
- return true;
72
- });
73
- assert.ok(captured !== undefined);
74
- return captured;
75
- }
76
-
77
- void describe('fusion_web_fetch core', () => {
78
- void it('pins the expanded research fetch envelope', () => {
79
- assert.equal(FUSION_WEB_FETCH_TIMEOUT_MS, 90_000);
80
- assert.equal(FUSION_WEB_FETCH_MAX_RESPONSE_BYTES, 4 * 1024 * 1024);
81
- assert.equal(FUSION_WEB_FETCH_MAX_OUTPUT_BYTES, 32 * 1024);
82
- assert.equal(FUSION_WEB_FETCH_MAX_REDIRECTS, 5);
83
- });
84
-
85
- void it('rejects unsupported schemes before network access', async () => {
86
- let lookupCalls = 0;
87
- await expectError(
88
- fusionWebFetch({ url: 'file:///tmp/page.html' }, { lookup: async () => {
89
- lookupCalls += 1;
90
- return [{ address: '93.184.216.34', family: 4 }];
91
- } }),
92
- 'unsupported_scheme',
93
- );
94
- assert.equal(lookupCalls, 0);
95
- });
96
-
97
- void it('rejects URL credentials before network access', async () => {
98
- let lookupCalls = 0;
99
- await expectError(
100
- fusionWebFetch({ url: 'https://user:pass@example.com/' }, { lookup: async () => {
101
- lookupCalls += 1;
102
- return [{ address: '93.184.216.34', family: 4 }];
103
- } }),
104
- 'invalid_url',
105
- );
106
- assert.equal(lookupCalls, 0);
107
- });
108
-
109
- void it('rejects blocked address classes from DNS answers', async () => {
110
- const blocked = [
111
- { address: '127.0.0.1', family: 4 },
112
- { address: '169.254.1.2', family: 4 },
113
- { address: '169.254.169.254', family: 4 },
114
- { address: '::ffff:127.0.0.1', family: 6 },
115
- { address: '64:ff9b::a00:1', family: 6 },
116
- { address: '64:ff9b:1::a00:1', family: 6 },
117
- { address: 'fe80::1', family: 6 },
118
- { address: 'fc00::1', family: 6 },
119
- { address: 'ff02::1', family: 6 },
120
- ] as const;
121
-
122
- for (const address of blocked) {
123
- await expectError(
124
- fusionWebFetch({ url: 'http://public.test/' }, { lookup: async () => [address] }),
125
- 'blocked_address',
126
- );
127
- }
128
- });
129
-
130
- void it('consults the resolver exactly once and pins the connection to that vetted address', async () => {
131
- // DNS-rebinding defence, expressed as the property that actually holds.
132
- //
133
- // Validating resolver answers is not sufficient on its own: a name can resolve again
134
- // between validation and connect, so the socket could land somewhere never vetted. That
135
- // is the window left open by implementations that validate and then delegate to global
136
- // fetch, which re-resolves internally. Here the vetted address is captured once and the
137
- // per-request lookup hook can return only that address, so a second resolver answer can
138
- // never influence where the socket terminates. The post-connect socket check is then
139
- // defence in depth rather than the primary control.
140
- //
141
- // A rebinding attempt is simulated by a resolver that would hand back loopback on every
142
- // call after the first. If the implementation re-queried at connect time, the request
143
- // would reach the loopback server and this assertion on the call count would fail.
144
- let calls = 0;
145
- await withServer(
146
- {
147
- '/': (_request, response) => {
148
- response.writeHead(200, { 'content-type': 'text/plain' });
149
- response.end('should never be reached');
150
- },
151
- },
152
- async (baseUrl) => {
153
- const rebindingLookup = async () => {
154
- calls += 1;
155
- return calls === 1
156
- ? [{ address: '127.0.0.1', family: 4 as const }]
157
- : [{ address: '10.0.0.1', family: 4 as const }];
158
- };
159
- const result = await fusionWebFetch(
160
- { url: `${baseUrl}/` },
161
- { lookup: rebindingLookup, allowBlockedAddressesForTests: true },
162
- );
163
- assert.equal(result.status, 200);
164
- },
165
- );
166
- assert.equal(calls, 1, 'the resolver must be consulted once; the vetted address is then pinned');
167
- });
168
-
169
- void it('rejects a DNS result that mixes public and blocked answers', async () => {
170
- await expectError(
171
- fusionWebFetch({ url: 'http://public.test/' }, {
172
- lookup: async () => [
173
- { address: '93.184.216.34', family: 4 },
174
- { address: '10.0.0.1', family: 4 },
175
- ],
176
- }),
177
- 'blocked_address',
178
- );
179
- });
180
-
181
- void it('follows a redirect chain', async () => {
182
- await withServer(
183
- {
184
- '/start': (_request, response) => {
185
- response.writeHead(302, { location: '/middle' });
186
- response.end();
187
- },
188
- '/middle': (_request, response) => {
189
- response.writeHead(301, { location: '/final' });
190
- response.end();
191
- },
192
- '/final': (_request, response) => {
193
- response.writeHead(200, { 'content-type': 'text/plain' });
194
- response.end('done');
195
- },
196
- },
197
- async (baseUrl) => {
198
- const result = await fusionWebFetch({ url: `${baseUrl}/start` }, localOptions());
199
- assert.equal(result.final_url, `${baseUrl}/final`);
200
- assert.equal(result.status, 200);
201
- assert.equal(result.content, 'done');
202
- },
203
- );
204
- });
205
-
206
- void it('destroys redirect responses without consuming unbounded bodies', async () => {
207
- let redirectClosed = false;
208
- await withServer(
209
- {
210
- '/start': (_request, response) => {
211
- const interval = setInterval(() => response.write('unbounded redirect body'), 5);
212
- response.once('close', () => {
213
- redirectClosed = true;
214
- clearInterval(interval);
215
- });
216
- response.writeHead(302, { location: '/final', 'content-type': 'text/plain' });
217
- response.flushHeaders();
218
- },
219
- '/final': (_request, response) => {
220
- response.writeHead(200, { 'content-type': 'text/plain' });
221
- response.end('done');
222
- },
223
- },
224
- async (baseUrl) => {
225
- const result = await fusionWebFetch({ url: `${baseUrl}/start` }, localOptions());
226
- assert.equal(result.content, 'done');
227
- },
228
- );
229
- assert.equal(redirectClosed, true);
230
- });
231
-
232
- void it('rejects a redirect to a blocked host', async () => {
233
- await withServer(
234
- {
235
- '/start': (_request, response) => {
236
- response.writeHead(302, { location: 'http://localhost/final' });
237
- response.end();
238
- },
239
- },
240
- async (baseUrl) => {
241
- await expectError(fusionWebFetch({ url: `${baseUrl}/start` }, localOptions()), 'redirect_blocked');
242
- },
243
- );
244
- });
245
-
246
- void it('rejects a redirect loop at the configured limit', async () => {
247
- await withServer(
248
- {
249
- '/loop': (_request, response) => {
250
- response.writeHead(302, { location: '/loop' });
251
- response.end();
252
- },
253
- },
254
- async (baseUrl) => {
255
- await expectError(
256
- fusionWebFetch({ url: `${baseUrl}/loop` }, localOptions({ maxRedirects: 2 })),
257
- 'redirect_limit',
258
- );
259
- },
260
- );
261
- assert.equal(FUSION_WEB_FETCH_MAX_REDIRECTS, 5);
262
- });
263
-
264
- void it('rejects an oversized Content-Length before reading body bytes', async () => {
265
- let bodyWriteCount = 0;
266
- await withServer(
267
- {
268
- '/large': (_request, response) => {
269
- response.writeHead(200, { 'content-type': 'text/plain', 'content-length': '10' });
270
- response.flushHeaders();
271
- setTimeout(() => {
272
- bodyWriteCount += 1;
273
- response.end('0123456789');
274
- }, 50);
275
- },
276
- },
277
- async (baseUrl) => {
278
- await expectError(
279
- fusionWebFetch({ url: `${baseUrl}/large` }, localOptions({ maxResponseBytes: 5 })),
280
- 'response_too_large',
281
- );
282
- },
283
- );
284
- assert.equal(bodyWriteCount, 0);
285
- });
286
-
287
- void it('aborts loudly when a streamed body crosses the response cap', async () => {
288
- await withServer(
289
- {
290
- '/stream': (_request, response) => {
291
- response.writeHead(200, { 'content-type': 'text/plain' });
292
- response.write('12345');
293
- response.end('67890');
294
- },
295
- },
296
- async (baseUrl) => {
297
- await expectError(
298
- fusionWebFetch({ url: `${baseUrl}/stream` }, localOptions({ maxResponseBytes: 8 })),
299
- 'response_too_large',
300
- );
301
- },
302
- );
303
- });
304
-
305
- void it('rejects unsupported content types', async () => {
306
- await withServer(
307
- {
308
- '/json': (_request, response) => {
309
- response.writeHead(200, { 'content-type': 'application/json' });
310
- response.end('{"ok":true}');
311
- },
312
- },
313
- async (baseUrl) => {
314
- await expectError(fusionWebFetch({ url: `${baseUrl}/json` }, localOptions()), 'unsupported_content_type');
315
- },
316
- );
317
- });
318
-
319
- void it('converts HTML to markdown with links, headings, tables, and code blocks', async () => {
320
- const html = `<!doctype html><html><body>
321
- <h1>Title</h1>
322
- <p>Visit <a href="https://example.com/docs">docs</a>.</p>
323
- <table><tr><th>Name</th><th>Value</th></tr><tr><td>alpha</td><td>one</td></tr></table>
324
- <pre><code>const x = 1;</code></pre>
325
- </body></html>`;
326
- await withServer(
327
- {
328
- '/html': (_request, response) => {
329
- response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
330
- response.end(html);
331
- },
332
- },
333
- async (baseUrl) => {
334
- const result = await fusionWebFetch({ url: `${baseUrl}/html` }, localOptions());
335
- assert.equal(result.format, 'markdown');
336
- assert.match(result.content, /# Title/u);
337
- assert.match(result.content, /\[docs\]\(https:\/\/example\.com\/docs\)/u);
338
- assert.match(result.content, /\| Name \| Value \|/u);
339
- assert.match(result.content, /\| alpha \| one \|/u);
340
- assert.match(result.content, /```/u);
341
- assert.match(result.content, /const x = 1;/u);
342
- },
343
- );
344
- });
345
-
346
- void it('strips script and style content before extraction', async () => {
347
- await withServer(
348
- {
349
- '/html': (_request, response) => {
350
- response.writeHead(200, { 'content-type': 'text/html' });
351
- response.end('<h1>Keep</h1><script>secret()</script><style>.secret{}</style><noscript>hidden</noscript>');
352
- },
353
- },
354
- async (baseUrl) => {
355
- const result = await fusionWebFetch({ url: `${baseUrl}/html` }, localOptions());
356
- assert.match(result.content, /Keep/u);
357
- assert.doesNotMatch(result.content, /secret|hidden/u);
358
- },
359
- );
360
- });
361
-
362
- void it('passes text/plain through and reports hash and byte count', async () => {
363
- const body = 'plain\ntext & symbols';
364
- await withServer(
365
- {
366
- '/plain': (_request, response) => {
367
- response.writeHead(200, { 'content-type': 'text/plain; charset=utf-8' });
368
- response.end(body);
369
- },
370
- },
371
- async (baseUrl) => {
372
- const result = await fusionWebFetch({ url: `${baseUrl}/plain`, extract: 'markdown' }, localOptions());
373
- assert.equal(result.format, 'text');
374
- assert.equal(result.content, body);
375
- assert.equal(result.response_bytes, Buffer.byteLength(body));
376
- assert.equal(result.content_sha256, createHash('sha256').update(body).digest('hex'));
377
- },
378
- );
379
- });
380
-
381
- void it('caps output without splitting UTF-8 characters', async () => {
382
- await withServer(
383
- {
384
- '/emoji': (_request, response) => {
385
- response.writeHead(200, { 'content-type': 'text/plain; charset=utf-8' });
386
- response.end('😀😀😀');
387
- },
388
- },
389
- async (baseUrl) => {
390
- const result = await fusionWebFetch({ url: `${baseUrl}/emoji` }, localOptions({ maxOutputBytes: 5 }));
391
- assert.equal(result.truncated, true);
392
- assert.equal(result.content, '😀');
393
- assert.equal(Buffer.byteLength(result.content), 4);
394
- },
395
- );
396
- });
397
-
398
- void it('includes content extraction inside the fetch deadline', async () => {
399
- await withServer(
400
- {
401
- '/slow-extraction': (_request, response) => {
402
- response.writeHead(200, { 'content-type': 'text/html' });
403
- response.end('<h1>ready</h1>');
404
- },
405
- },
406
- async (baseUrl) => {
407
- const start = Date.now();
408
- await expectError(
409
- fusionWebFetch(
410
- { url: `${baseUrl}/slow-extraction` },
411
- localOptions({
412
- timeoutMs: 20,
413
- extractContent: async () => {
414
- await new Promise((resolve) => setTimeout(resolve, 100));
415
- return { content: 'late extraction', format: 'markdown' };
416
- },
417
- }),
418
- ),
419
- 'request_timeout',
420
- );
421
- assert.ok(Date.now() - start < 90, 'extraction must be raced against remaining deadline');
422
- },
423
- );
424
- });
425
-
426
- void it('includes DNS resolution inside the fetch deadline', async () => {
427
- const start = Date.now();
428
- await expectError(
429
- fusionWebFetch(
430
- { url: 'https://dns-stall.test/' },
431
- { lookup: () => new Promise(() => undefined), timeoutMs: 20 },
432
- ),
433
- 'request_timeout',
434
- );
435
- assert.ok(Date.now() - start < 500, 'stalled DNS must be bounded by the request deadline');
436
- });
437
-
438
- void it('subtracts delayed successful DNS time from the HTTP deadline', async () => {
439
- await withServer(
440
- {
441
- '/slow-after-dns': (_request, response) => {
442
- setTimeout(() => {
443
- response.writeHead(200, { 'content-type': 'text/plain' });
444
- response.end('too late');
445
- }, 40);
446
- },
447
- },
448
- async (baseUrl) => {
449
- await expectError(
450
- fusionWebFetch(
451
- { url: `${baseUrl}/slow-after-dns` },
452
- {
453
- lookup: async () => {
454
- await new Promise((resolve) => setTimeout(resolve, 30));
455
- return [{ address: '127.0.0.1', family: 4 }];
456
- },
457
- allowBlockedAddressesForTests: true,
458
- timeoutMs: 50,
459
- },
460
- ),
461
- 'request_timeout',
462
- );
463
- },
464
- );
465
- });
466
-
467
- void it('fails with a typed timeout', async () => {
468
- await withServer(
469
- {
470
- '/slow': (_request, response) => {
471
- setTimeout(() => {
472
- response.writeHead(200, { 'content-type': 'text/plain' });
473
- response.end('late');
474
- }, 100);
475
- },
476
- },
477
- async (baseUrl) => {
478
- await expectError(
479
- fusionWebFetch({ url: `${baseUrl}/slow` }, localOptions({ timeoutMs: 20 })),
480
- 'request_timeout',
481
- );
482
- },
483
- );
484
- });
485
- });
@@ -1,59 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import {
4
- FUSION_INVESTIGATE,
5
- FUSION_INVESTIGATE_WORKFLOW,
6
- FUSION_REASON,
7
- FUSION_REASON_WORKFLOW,
8
- FUSION_RESEARCH,
9
- FUSION_RESEARCH_WORKFLOW,
10
- FUSION_VALIDATE,
11
- FUSION_VALIDATE_WORKFLOW,
12
- FUSION_WORKFLOW_PROFILES,
13
- assertWorkflowCapability,
14
- fusionWorkflowProfile,
15
- } from '../fusion/workflows.js';
16
- import { FUSION_WORKFLOW_IDS } from '../fusion/types.js';
17
-
18
- void describe('fusion fixed v1 workflows', () => {
19
- void it('exports exactly four public workflow profiles with fixed policies', () => {
20
- assert.deepEqual([...FUSION_WORKFLOW_IDS], ['reason', 'investigate', 'research', 'validate']);
21
- assert.deepEqual(FUSION_WORKFLOW_PROFILES.map((profile) => profile.publicName), ['fusion_reason', 'fusion_investigate', 'fusion_research', 'fusion_validate']);
22
- assert.equal(FUSION_REASON, FUSION_REASON_WORKFLOW);
23
- assert.equal(FUSION_INVESTIGATE, FUSION_INVESTIGATE_WORKFLOW);
24
- assert.equal(FUSION_RESEARCH, FUSION_RESEARCH_WORKFLOW);
25
- assert.equal(FUSION_VALIDATE, FUSION_VALIDATE_WORKFLOW);
26
- });
27
-
28
- void it('pins context and tool policies by workflow', () => {
29
- assert.equal(FUSION_REASON_WORKFLOW.contextKind, 'session_projection');
30
- assert.equal(FUSION_REASON_WORKFLOW.candidateCapability, 'reason');
31
- assert.deepEqual(FUSION_REASON_WORKFLOW.candidateTools, []);
32
- assert.equal(FUSION_INVESTIGATE_WORKFLOW.contextKind, 'clean_task');
33
- assert.equal(FUSION_INVESTIGATE_WORKFLOW.candidateCapability, 'inspect');
34
- assert.deepEqual(FUSION_INVESTIGATE_WORKFLOW.candidateTools, ['read', 'grep', 'find', 'ls']);
35
- assert.equal(FUSION_RESEARCH_WORKFLOW.contextKind, 'clean_task');
36
- assert.equal(FUSION_RESEARCH_WORKFLOW.candidateCapability, 'research');
37
- assert.ok(FUSION_RESEARCH_WORKFLOW.candidateTools.includes('fusion_web_fetch'));
38
- assert.equal(FUSION_VALIDATE_WORKFLOW.contextKind, 'clean_task');
39
- assert.equal(FUSION_VALIDATE_WORKFLOW.candidateCapability, 'inspect');
40
- for (const profile of FUSION_WORKFLOW_PROFILES) {
41
- assert.equal(profile.evaluatorCapability, 'reason');
42
- assert.equal(profile.mergeCapability, 'reason');
43
- assert.deepEqual(profile.evaluatorTools, []);
44
- assert.deepEqual(profile.mergeTools, []);
45
- }
46
- });
47
-
48
- void it('rejects mismatched caller capabilities instead of substituting', () => {
49
- assert.equal(assertWorkflowCapability(FUSION_REASON_WORKFLOW, undefined), 'reason');
50
- assert.equal(assertWorkflowCapability(FUSION_VALIDATE_WORKFLOW, 'inspect'), 'inspect');
51
- assert.throws(() => assertWorkflowCapability(FUSION_VALIDATE_WORKFLOW, 'reason'), /always runs candidates with the inspect capability/);
52
- assert.throws(() => assertWorkflowCapability(FUSION_RESEARCH_WORKFLOW, 'inspect'), /always runs candidates with the research capability/);
53
- });
54
-
55
- void it('resolves every declared workflow id and fails closed on unknown ids', () => {
56
- for (const id of FUSION_WORKFLOW_IDS) assert.equal(fusionWorkflowProfile(id).id, id);
57
- assert.throws(() => fusionWorkflowProfile('brainstorm' as never), /unknown fusion workflow/);
58
- });
59
- });
@@ -1,109 +0,0 @@
1
- import type { SessionMessageEntry } from '@earendil-works/pi-coding-agent';
2
- import type { AssistantMessage, ToolResultMessage, UserMessage } from '@earendil-works/pi-ai';
3
- import { buildDelegateSeed, type BuiltDelegateSeed } from '../../delegate/seed.js';
4
- import type { DelegateLimits, DelegatePinnedRoute } from '../../delegate/types.js';
5
-
6
- /**
7
- * Fully deterministic delegate seed fixture.
8
- *
9
- * Pi assigns random ids to session entries, so a `SessionManager` cannot be used
10
- * to prove cross-process byte stability: its leaf id legitimately differs every
11
- * time. This fixture supplies a hand-built session view with fixed entry ids, so
12
- * the only thing that can vary between two builds is the projection itself.
13
- */
14
-
15
- const ROUTE: DelegatePinnedRoute = {
16
- provider: 'anthropic',
17
- model: 'claude-test',
18
- qualified_id: 'anthropic/claude-test',
19
- context_window_tokens: 200_000,
20
- thinking_level: 'medium',
21
- origin: 'parent_current',
22
- };
23
-
24
- const LIMITS: DelegateLimits = {
25
- max_turns: 24,
26
- max_tool_calls: 120,
27
- timeout_seconds: 900,
28
- max_tool_result_bytes: 65_536,
29
- max_total_tool_output_bytes: 67_108_864,
30
- max_answer_bytes: 4_194_304,
31
- allowed_input_tokens: 171_712,
32
- };
33
-
34
- function messageEntry(
35
- id: string,
36
- parentId: string | null,
37
- message: UserMessage | AssistantMessage | ToolResultMessage,
38
- ): SessionMessageEntry {
39
- return { type: 'message', id, parentId, timestamp: '2024-01-01T00:00:00.000Z', message };
40
- }
41
-
42
- const ENTRIES: readonly SessionMessageEntry[] = [
43
- messageEntry('e0000001', null, {
44
- role: 'user',
45
- content: 'VISIBLE_USER_ONE about the failing test',
46
- timestamp: 1,
47
- }),
48
- messageEntry('e0000002', 'e0000001', {
49
- role: 'assistant',
50
- api: 'openai-codex-responses',
51
- provider: 'openai-codex',
52
- model: 'gpt-5.5',
53
- usage: {
54
- input: 1,
55
- output: 1,
56
- cacheRead: 0,
57
- cacheWrite: 0,
58
- totalTokens: 2,
59
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
60
- },
61
- stopReason: 'toolUse',
62
- content: [
63
- { type: 'text', text: 'VISIBLE_ASSISTANT_ONE here is my reading' },
64
- { type: 'thinking', thinking: 'SECRET_THINKING_PAYLOAD', thinkingSignature: '' },
65
- { type: 'toolCall', id: 'c1', name: 'read', arguments: { path: '/SECRET_TOOL_ARGUMENT' } },
66
- ],
67
- timestamp: 2,
68
- }),
69
- messageEntry('e0000003', 'e0000002', {
70
- role: 'toolResult',
71
- toolCallId: 'c1',
72
- toolName: 'read',
73
- content: [{ type: 'text', text: 'SECRET_TOOL_RESULT_PAYLOAD' }],
74
- isError: false,
75
- timestamp: 3,
76
- }),
77
- messageEntry('e0000004', 'e0000003', {
78
- role: 'user',
79
- content: 'VISIBLE_USER_TWO follow-up',
80
- timestamp: 4,
81
- }),
82
- ];
83
-
84
- /** Read-only session view with fixed ids and a fixed leaf. */
85
- export const DETERMINISTIC_SESSION = {
86
- getLeafId: (): string | null => 'e0000004',
87
- getLeafEntry: (): SessionMessageEntry | undefined => ENTRIES[ENTRIES.length - 1],
88
- getEntries: (): SessionMessageEntry[] => [...ENTRIES],
89
- };
90
-
91
- export function buildDeterministicFixtureSeed(): BuiltDelegateSeed {
92
- return buildDelegateSeed(
93
- {
94
- cwd: '/tmp/project',
95
- sessionManager: DETERMINISTIC_SESSION,
96
- getSystemPrompt: () => 'parent system prompt',
97
- },
98
- {
99
- taskId: 'd0123456789abcdef0123456789abcdef',
100
- launchNonce: 'ffeeddccbbaa99887766554433221100',
101
- toolCallId: 'delegate-call-1',
102
- directive: 'investigate the failing gate',
103
- capability: 'inspect',
104
- extensionMode: 'isolated',
105
- route: ROUTE,
106
- limits: LIMITS,
107
- },
108
- );
109
- }
@@ -1,10 +0,0 @@
1
- import { buildDeterministicFixtureSeed } from './delegate-deterministic-seed.js';
2
-
3
- /**
4
- * Cross-process determinism probe.
5
- *
6
- * Builds the shared deterministic seed fixture in a fresh Node process and
7
- * prints its digest, so the unit test can prove the seed bytes do not depend on
8
- * process-local state such as Map iteration order or module load order.
9
- */
10
- process.stdout.write(`${buildDeterministicFixtureSeed().sha256}\n`);
@@ -1,21 +0,0 @@
1
- import {
2
- assistantMessage,
3
- buildFrom,
4
- toolResultMessage,
5
- userMessage,
6
- } from './fusion-canonical.js';
7
-
8
- const messages = [
9
- userMessage('repeatable question'),
10
- assistantMessage([
11
- { type: 'thinking', thinking: 'hidden' },
12
- { type: 'text', text: 'visible' },
13
- { type: 'toolCall', id: 'c1', name: 'bash', arguments: { z: 1, a: 2 } },
14
- ]),
15
- toolResultMessage('c1', 'bash', [{ type: 'text', text: 'file listing' }]),
16
- ];
17
-
18
- const built = buildFrom(messages, { source: 'tool', request: 'again' });
19
- process.stdout.write(
20
- `${built.serialized}\n${built.input.conversation_projection.accounting.ledger_root_sha256}`,
21
- );