@pi-unipi/background-tasks 2.6.3 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -5
- package/src/__tests__/anthropic-attribution.test.ts +0 -195
- package/src/__tests__/config.test.ts +0 -137
- package/src/__tests__/core.test.ts +0 -493
- package/src/__tests__/delegate-artifacts.test.ts +0 -528
- package/src/__tests__/delegate-budget.test.ts +0 -456
- package/src/__tests__/delegate-launch.test.ts +0 -676
- package/src/__tests__/delegate-result-package.test.ts +0 -350
- package/src/__tests__/delegate-seed.test.ts +0 -392
- package/src/__tests__/durable-fs.test.ts +0 -559
- package/src/__tests__/extension-api.test.ts +0 -579
- package/src/__tests__/fusion-artifacts.test.ts +0 -1039
- package/src/__tests__/fusion-budget.test.ts +0 -1356
- package/src/__tests__/fusion-claude-cache.test.ts +0 -320
- package/src/__tests__/fusion-config.test.ts +0 -335
- package/src/__tests__/fusion-context-prompts.test.ts +0 -670
- package/src/__tests__/fusion-evaluation.test.ts +0 -315
- package/src/__tests__/fusion-extraction-equivalence.test.ts +0 -58
- package/src/__tests__/fusion-golden-bytes.test.ts +0 -35
- package/src/__tests__/fusion-high-cardinality.test.ts +0 -192
- package/src/__tests__/fusion-model-selector.test.ts +0 -205
- package/src/__tests__/fusion-orchestrator.test.ts +0 -1194
- package/src/__tests__/fusion-rpc.test.ts +0 -369
- package/src/__tests__/fusion-sdk.test.ts +0 -1226
- package/src/__tests__/fusion-v5-core.test.ts +0 -219
- package/src/__tests__/fusion-validate-orchestrator.test.ts +0 -240
- package/src/__tests__/fusion-web-fetch.test.ts +0 -485
- package/src/__tests__/fusion-workflows.test.ts +0 -59
- package/src/__tests__/helpers/delegate-deterministic-seed.ts +0 -109
- package/src/__tests__/helpers/delegate-seed-subprocess.ts +0 -10
- package/src/__tests__/helpers/fusion-canonical-subprocess.ts +0 -21
- package/src/__tests__/helpers/fusion-canonical.ts +0 -140
- package/src/__tests__/helpers/fusion-fake-pi.ts +0 -279
- package/src/__tests__/helpers/fusion-golden-corpus.ts +0 -500
- package/src/__tests__/helpers/fusion-high-cardinality.ts +0 -140
- package/src/__tests__/helpers/normalize.ts +0 -22
- package/src/__tests__/helpers/pi-hook-contract-evidence.json +0 -18
- package/src/__tests__/pi-launch.test.ts +0 -202
- package/src/__tests__/registry.test.ts +0 -1580
- package/src/__tests__/scripted-provider/delegate-ambient-provider.test.ts +0 -130
- package/src/__tests__/scripted-provider/delegate-child-guard.test.ts +0 -631
- package/src/__tests__/scripted-provider/delegate-guard-provider.ts +0 -403
- package/src/__tests__/scripted-provider/follow-up.test.ts +0 -448
- package/src/__tests__/scripted-provider/fusion-output-recovery.test.ts +0 -132
- package/src/__tests__/scripted-provider/fusion-reason.test.ts +0 -310
- package/src/__tests__/scripted-provider/fusion-runtime-guard.test.ts +0 -163
- package/src/__tests__/scripted-provider/hook-contract-provider.ts +0 -179
- package/src/__tests__/scripted-provider/hook-probe-a.ts +0 -3
- package/src/__tests__/scripted-provider/hook-probe-b.ts +0 -3
- package/src/__tests__/scripted-provider/hook-probe-extension.ts +0 -126
- package/src/__tests__/scripted-provider/output-recovery-provider.ts +0 -153
- package/src/__tests__/scripted-provider/pi-hook-contract-evidence.json +0 -18
- package/src/__tests__/scripted-provider/pi-hook-contract.test.ts +0 -477
- package/src/__tests__/scripted-provider/runtime-guard-probe.ts +0 -28
- package/src/__tests__/scripted-provider/runtime-guard-provider.ts +0 -49
- package/src/__tests__/scripted-provider/scripted-provider-extension.ts +0 -408
- package/src/__tests__/task-manager.test.ts +0 -479
- package/src/__tests__/windows-taskkill.test.ts +0 -161
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
import type { AssistantMessage, ToolResultMessage, UserMessage } from '@earendil-works/pi-ai';
|
|
2
|
-
import { buildFusionCanonicalInput } from '../../fusion/context.js';
|
|
3
|
-
import { FusionBudget } from '../../fusion/budget.js';
|
|
4
|
-
import { canonicalJson } from '../../attested-pi-run.js';
|
|
5
|
-
import {
|
|
6
|
-
FUSION_NO_TOOLS_CAPABILITY,
|
|
7
|
-
FUSION_VALIDATE_CAPABILITY,
|
|
8
|
-
type FusionCapability,
|
|
9
|
-
type FusionSource,
|
|
10
|
-
type ResolvedFusionModel,
|
|
11
|
-
type ResolvedFusionModels,
|
|
12
|
-
} from '../../fusion/types.js';
|
|
13
|
-
import {
|
|
14
|
-
FUSION_REASON_WORKFLOW,
|
|
15
|
-
FUSION_VALIDATE_WORKFLOW,
|
|
16
|
-
type FusionWorkflowProfile,
|
|
17
|
-
} from '../../fusion/workflows.js';
|
|
18
|
-
import { assistantMessage, sessionWith, toolResultMessage, userMessage } from './fusion-canonical.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Frozen differential corpus for Fusion's conversation projection and budget
|
|
22
|
-
* planner.
|
|
23
|
-
*
|
|
24
|
-
* Every case here is rendered to raw bytes and compared against committed golden
|
|
25
|
-
* files. The corpus is deliberately exhaustive across the branches that could
|
|
26
|
-
* silently move during a refactor: run-boundary flushing, image coalescing in
|
|
27
|
-
* `projection_map`, tool-name ordering, `compactCounts` combinations, UTF-8 and
|
|
28
|
-
* lone-surrogate handling, empty-block accounting, and every budget stage.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
type ConversationMessage = UserMessage | AssistantMessage | ToolResultMessage;
|
|
32
|
-
|
|
33
|
-
export interface FusionGoldenCase {
|
|
34
|
-
readonly id: string;
|
|
35
|
-
readonly messages: readonly ConversationMessage[];
|
|
36
|
-
readonly source: FusionSource;
|
|
37
|
-
readonly request: string;
|
|
38
|
-
readonly systemPrompt: string;
|
|
39
|
-
readonly toolCallId?: string;
|
|
40
|
-
readonly toolName?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function thinking(text: string) {
|
|
44
|
-
return { type: 'thinking' as const, thinking: text, thinkingSignature: '' };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function toolCall(id: string, name: string, args: Record<string, unknown>) {
|
|
48
|
-
return { type: 'toolCall' as const, id, name, arguments: args };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function textBlock(text: string) {
|
|
52
|
-
return { type: 'text' as const, text };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function image(mimeType: string, data = 'AAAA') {
|
|
56
|
-
return { type: 'image' as const, mimeType, data };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const UNICODE_SAMPLE = [
|
|
60
|
-
'crlf\r\nline',
|
|
61
|
-
'quote " backslash \\ slash /',
|
|
62
|
-
'emoji 👩👩👧👦 family',
|
|
63
|
-
'combining e\u0301 accent',
|
|
64
|
-
'rtl \u05D0\u05D1\u05D2 hebrew',
|
|
65
|
-
'sep \u2028 para \u2029 end',
|
|
66
|
-
'zwj \u200D joiner',
|
|
67
|
-
'tab\tvertical\u000Bform\u000C',
|
|
68
|
-
'lone surrogate \uD800 end',
|
|
69
|
-
'nul \u0000 byte',
|
|
70
|
-
].join(' | ');
|
|
71
|
-
|
|
72
|
-
export const FUSION_GOLDEN_CASES: readonly FusionGoldenCase[] = [
|
|
73
|
-
{ id: 'empty-conversation', messages: [], source: 'tool', request: 'r', systemPrompt: 'sys' },
|
|
74
|
-
{
|
|
75
|
-
id: 'user-text-only',
|
|
76
|
-
messages: [userMessage('hello world')],
|
|
77
|
-
source: 'tool',
|
|
78
|
-
request: 'r',
|
|
79
|
-
systemPrompt: 'sys',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: 'empty-string-user-content',
|
|
83
|
-
messages: [userMessage(''), userMessage([textBlock('')]), userMessage('after')],
|
|
84
|
-
source: 'tool',
|
|
85
|
-
request: 'r',
|
|
86
|
-
systemPrompt: 'sys',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
id: 'omission-only',
|
|
90
|
-
messages: [assistantMessage([thinking('secret reasoning')])],
|
|
91
|
-
source: 'tool',
|
|
92
|
-
request: 'r',
|
|
93
|
-
systemPrompt: 'sys',
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: 'omission-at-start',
|
|
97
|
-
messages: [assistantMessage([thinking('lead')]), userMessage('visible tail')],
|
|
98
|
-
source: 'tool',
|
|
99
|
-
request: 'r',
|
|
100
|
-
systemPrompt: 'sys',
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
id: 'omission-at-end',
|
|
104
|
-
messages: [userMessage('visible head'), assistantMessage([thinking('trail')])],
|
|
105
|
-
source: 'tool',
|
|
106
|
-
request: 'r',
|
|
107
|
-
systemPrompt: 'sys',
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
id: 'singleton-run-between-text',
|
|
111
|
-
messages: [
|
|
112
|
-
userMessage('before'),
|
|
113
|
-
assistantMessage([thinking('mid')]),
|
|
114
|
-
userMessage('after'),
|
|
115
|
-
],
|
|
116
|
-
source: 'tool',
|
|
117
|
-
request: 'r',
|
|
118
|
-
systemPrompt: 'sys',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
id: 'multiple-separated-runs',
|
|
122
|
-
messages: [
|
|
123
|
-
userMessage('a'),
|
|
124
|
-
assistantMessage([thinking('t1'), toolCall('c1', 'read', { path: '/x' })]),
|
|
125
|
-
toolResultMessage('c1', 'read', [textBlock('payload one')]),
|
|
126
|
-
assistantMessage([textBlock('visible answer')]),
|
|
127
|
-
assistantMessage([thinking('t2'), toolCall('c2', 'grep', { pattern: 'p' })]),
|
|
128
|
-
toolResultMessage('c2', 'grep', [textBlock('payload two')]),
|
|
129
|
-
userMessage('z'),
|
|
130
|
-
],
|
|
131
|
-
source: 'tool',
|
|
132
|
-
request: 'r',
|
|
133
|
-
systemPrompt: 'sys',
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
id: 'cross-message-contiguous-run',
|
|
137
|
-
messages: [
|
|
138
|
-
assistantMessage([toolCall('c1', 'read', { path: '/a' })]),
|
|
139
|
-
toolResultMessage('c1', 'read', [textBlock('one')]),
|
|
140
|
-
assistantMessage([toolCall('c2', 'read', { path: '/b' })]),
|
|
141
|
-
toolResultMessage('c2', 'read', [textBlock('two')]),
|
|
142
|
-
],
|
|
143
|
-
source: 'tool',
|
|
144
|
-
request: 'r',
|
|
145
|
-
systemPrompt: 'sys',
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
id: 'all-count-kinds-in-one-run',
|
|
149
|
-
messages: [
|
|
150
|
-
assistantMessage([thinking('why'), toolCall('c1', 'ls', { path: '.' })]),
|
|
151
|
-
toolResultMessage('c1', 'ls', [textBlock('entries')]),
|
|
152
|
-
],
|
|
153
|
-
source: 'tool',
|
|
154
|
-
request: 'r',
|
|
155
|
-
systemPrompt: 'sys',
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
id: 'thinking-only-counts',
|
|
159
|
-
messages: [assistantMessage([thinking('a'), thinking('b')])],
|
|
160
|
-
source: 'tool',
|
|
161
|
-
request: 'r',
|
|
162
|
-
systemPrompt: 'sys',
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
id: 'tool-call-only-counts',
|
|
166
|
-
messages: [assistantMessage([toolCall('c1', 'find', {}), toolCall('c2', 'find', {})])],
|
|
167
|
-
source: 'tool',
|
|
168
|
-
request: 'r',
|
|
169
|
-
systemPrompt: 'sys',
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
id: 'tool-result-only-counts',
|
|
173
|
-
messages: [toolResultMessage('c1', 'read', [textBlock('only result')])],
|
|
174
|
-
source: 'tool',
|
|
175
|
-
request: 'r',
|
|
176
|
-
systemPrompt: 'sys',
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
id: 'single-user-image',
|
|
180
|
-
messages: [userMessage([image('image/png')])],
|
|
181
|
-
source: 'tool',
|
|
182
|
-
request: 'r',
|
|
183
|
-
systemPrompt: 'sys',
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
id: 'adjacent-user-images',
|
|
187
|
-
messages: [userMessage([image('image/png'), image('image/jpeg'), image('image/webp')])],
|
|
188
|
-
source: 'tool',
|
|
189
|
-
request: 'r',
|
|
190
|
-
systemPrompt: 'sys',
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
id: 'text-image-text',
|
|
194
|
-
messages: [userMessage([textBlock('left'), image('image/gif'), textBlock('right')])],
|
|
195
|
-
source: 'tool',
|
|
196
|
-
request: 'r',
|
|
197
|
-
systemPrompt: 'sys',
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: 'adjacent-tool-result-images-coalesce',
|
|
201
|
-
messages: [
|
|
202
|
-
toolResultMessage('c1', 'read', [image('image/png', 'IMG1'), image('image/png', 'IMG2')]),
|
|
203
|
-
toolResultMessage('c2', 'read', [image('image/png', 'IMG3')]),
|
|
204
|
-
],
|
|
205
|
-
source: 'tool',
|
|
206
|
-
request: 'r',
|
|
207
|
-
systemPrompt: 'sys',
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
id: 'image-splits-omission-run',
|
|
211
|
-
messages: [
|
|
212
|
-
assistantMessage([thinking('before image')]),
|
|
213
|
-
toolResultMessage('c1', 'read', [image('image/png', 'MID')]),
|
|
214
|
-
assistantMessage([thinking('after image')]),
|
|
215
|
-
],
|
|
216
|
-
source: 'tool',
|
|
217
|
-
request: 'r',
|
|
218
|
-
systemPrompt: 'sys',
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
id: 'non-adjacent-tool-result-images',
|
|
222
|
-
messages: [
|
|
223
|
-
toolResultMessage('c1', 'read', [image('image/png', 'A')]),
|
|
224
|
-
toolResultMessage('c2', 'read', [textBlock('text between')]),
|
|
225
|
-
toolResultMessage('c3', 'read', [image('image/png', 'B')]),
|
|
226
|
-
],
|
|
227
|
-
source: 'tool',
|
|
228
|
-
request: 'r',
|
|
229
|
-
systemPrompt: 'sys',
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
id: 'mixed-tool-result-text-and-image',
|
|
233
|
-
messages: [
|
|
234
|
-
toolResultMessage('c1', 'read', [
|
|
235
|
-
{ type: 'text', text: 'head' },
|
|
236
|
-
image('image/png', 'MIX'),
|
|
237
|
-
{ type: 'text', text: 'tail' },
|
|
238
|
-
]),
|
|
239
|
-
],
|
|
240
|
-
source: 'tool',
|
|
241
|
-
request: 'r',
|
|
242
|
-
systemPrompt: 'sys',
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
id: 'repeated-and-unsorted-tool-names',
|
|
246
|
-
messages: [
|
|
247
|
-
assistantMessage([
|
|
248
|
-
toolCall('c1', 'zeta', {}),
|
|
249
|
-
toolCall('c2', 'Alpha', {}),
|
|
250
|
-
toolCall('c3', 'alpha', {}),
|
|
251
|
-
toolCall('c4', 'zeta', {}),
|
|
252
|
-
toolCall('c5', '10_numeric', {}),
|
|
253
|
-
toolCall('c6', '2_numeric', {}),
|
|
254
|
-
toolCall('c7', 'Ünicode', {}),
|
|
255
|
-
toolCall('c8', 'a-dash', {}),
|
|
256
|
-
toolCall('c9', 'a_underscore', {}),
|
|
257
|
-
]),
|
|
258
|
-
],
|
|
259
|
-
source: 'tool',
|
|
260
|
-
request: 'r',
|
|
261
|
-
systemPrompt: 'sys',
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: 'unicode-heavy-visible-text',
|
|
265
|
-
messages: [userMessage(UNICODE_SAMPLE), assistantMessage([textBlock(UNICODE_SAMPLE)])],
|
|
266
|
-
source: 'tool',
|
|
267
|
-
request: UNICODE_SAMPLE,
|
|
268
|
-
systemPrompt: UNICODE_SAMPLE,
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
id: 'unicode-heavy-omitted-payload',
|
|
272
|
-
messages: [
|
|
273
|
-
assistantMessage([thinking(UNICODE_SAMPLE), toolCall('c1', 'read', { q: UNICODE_SAMPLE })]),
|
|
274
|
-
toolResultMessage('c1', 'read', [textBlock(UNICODE_SAMPLE)]),
|
|
275
|
-
],
|
|
276
|
-
source: 'tool',
|
|
277
|
-
request: 'r',
|
|
278
|
-
systemPrompt: 'sys',
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
id: 'command-source-authority',
|
|
282
|
-
messages: [userMessage('shared history'), assistantMessage([textBlock('shared reply')])],
|
|
283
|
-
source: 'command',
|
|
284
|
-
request: 'command request text',
|
|
285
|
-
systemPrompt: 'sys',
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
id: 'tool-source-with-tool-call-id',
|
|
289
|
-
messages: [userMessage('shared history'), assistantMessage([textBlock('shared reply')])],
|
|
290
|
-
source: 'tool',
|
|
291
|
-
request: 'tool request text',
|
|
292
|
-
systemPrompt: 'sys',
|
|
293
|
-
toolCallId: 'explicit-call-id',
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
id: 'tool-source-custom-tool-name',
|
|
297
|
-
messages: [userMessage('shared history')],
|
|
298
|
-
source: 'tool',
|
|
299
|
-
request: 'r',
|
|
300
|
-
systemPrompt: 'sys',
|
|
301
|
-
toolName: 'some_other_tool',
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
id: 'active-tool-call-leaf-excluded',
|
|
305
|
-
messages: [
|
|
306
|
-
userMessage('keep this'),
|
|
307
|
-
assistantMessage([textBlock('keep this too')]),
|
|
308
|
-
assistantMessage([
|
|
309
|
-
toolCall('active-call', 'fusion_reason', { prompt: 'p' }),
|
|
310
|
-
toolCall('sibling-call', 'read', { path: '/sibling' }),
|
|
311
|
-
]),
|
|
312
|
-
],
|
|
313
|
-
source: 'tool',
|
|
314
|
-
request: 'r',
|
|
315
|
-
systemPrompt: 'sys',
|
|
316
|
-
toolCallId: 'active-call',
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
id: 'many-runs-high-cardinality',
|
|
320
|
-
messages: Array.from({ length: 24 }, (_, index) =>
|
|
321
|
-
index % 3 === 0
|
|
322
|
-
? userMessage(`visible ${String(index)}`)
|
|
323
|
-
: index % 3 === 1
|
|
324
|
-
? assistantMessage([
|
|
325
|
-
thinking(`t${String(index)}`),
|
|
326
|
-
toolCall(`c${String(index)}`, `tool_${String(index % 5)}`, { i: index }),
|
|
327
|
-
])
|
|
328
|
-
: toolResultMessage(`c${String(index - 1)}`, `tool_${String((index - 1) % 5)}`, [textBlock(`payload ${String(index)}`)]),
|
|
329
|
-
),
|
|
330
|
-
source: 'tool',
|
|
331
|
-
request: 'r',
|
|
332
|
-
systemPrompt: 'sys',
|
|
333
|
-
},
|
|
334
|
-
];
|
|
335
|
-
|
|
336
|
-
function model(
|
|
337
|
-
provider: string,
|
|
338
|
-
id: string,
|
|
339
|
-
contextWindow: number,
|
|
340
|
-
source: 'current' | 'configured' = 'configured',
|
|
341
|
-
): ResolvedFusionModel {
|
|
342
|
-
return {
|
|
343
|
-
selection: `${provider}/${id}`,
|
|
344
|
-
source,
|
|
345
|
-
provider,
|
|
346
|
-
model: id,
|
|
347
|
-
qualifiedId: `${provider}/${id}`,
|
|
348
|
-
thinkingLevel: 'medium',
|
|
349
|
-
contextWindow,
|
|
350
|
-
maxOutputTokens: 32_768,
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export interface FusionGoldenModelSet {
|
|
355
|
-
readonly id: string;
|
|
356
|
-
readonly models: ResolvedFusionModels;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/** Route sets spanning equal, asymmetric, boundary, and just-above-minimum capacities. */
|
|
360
|
-
export const FUSION_GOLDEN_MODEL_SETS: readonly FusionGoldenModelSet[] = [
|
|
361
|
-
{
|
|
362
|
-
id: 'uniform-large',
|
|
363
|
-
models: {
|
|
364
|
-
candidates: [
|
|
365
|
-
model('anthropic', 'claude-a', 200_000),
|
|
366
|
-
model('anthropic', 'claude-b', 200_000),
|
|
367
|
-
model('anthropic', 'claude-c', 200_000),
|
|
368
|
-
],
|
|
369
|
-
evaluator: model('anthropic', 'claude-eval', 200_000),
|
|
370
|
-
merger: model('anthropic', 'claude-merge', 200_000),
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
id: 'asymmetric-small-candidate',
|
|
375
|
-
models: {
|
|
376
|
-
candidates: [
|
|
377
|
-
model('openai-codex', 'gpt-small', 60_000),
|
|
378
|
-
model('anthropic', 'claude-b', 400_000),
|
|
379
|
-
model('anthropic', 'claude-c', 400_000),
|
|
380
|
-
],
|
|
381
|
-
evaluator: model('anthropic', 'claude-eval', 400_000),
|
|
382
|
-
merger: model('anthropic', 'claude-merge', 400_000),
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
id: 'minimum-viable-window',
|
|
387
|
-
models: {
|
|
388
|
-
candidates: [
|
|
389
|
-
model('local', 'tiny-1', 53_248),
|
|
390
|
-
model('local', 'tiny-2', 53_248),
|
|
391
|
-
model('local', 'tiny-3', 53_248),
|
|
392
|
-
],
|
|
393
|
-
evaluator: model('local', 'tiny-eval', 53_248),
|
|
394
|
-
merger: model('local', 'tiny-merge', 53_248),
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
];
|
|
398
|
-
|
|
399
|
-
export interface FusionGoldenRecord {
|
|
400
|
-
readonly case_id: string;
|
|
401
|
-
readonly canonical_input: string;
|
|
402
|
-
readonly context_ledger: string;
|
|
403
|
-
readonly transcript_leaf_present: boolean;
|
|
404
|
-
readonly budget_plans: Readonly<Record<string, string>>;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** Recompute one golden record from the live implementation. */
|
|
408
|
-
export function computeFusionGoldenRecord(
|
|
409
|
-
testCase: FusionGoldenCase,
|
|
410
|
-
profile: FusionWorkflowProfile = FUSION_REASON_WORKFLOW,
|
|
411
|
-
candidateCapability: FusionCapability = FUSION_NO_TOOLS_CAPABILITY,
|
|
412
|
-
): FusionGoldenRecord {
|
|
413
|
-
const session = sessionWith(testCase.messages);
|
|
414
|
-
const options: Parameters<typeof buildFusionCanonicalInput>[1] = {
|
|
415
|
-
source: testCase.source,
|
|
416
|
-
request: testCase.request,
|
|
417
|
-
};
|
|
418
|
-
if (testCase.toolCallId !== undefined) options.toolCallId = testCase.toolCallId;
|
|
419
|
-
if (testCase.toolName !== undefined) options.toolName = testCase.toolName;
|
|
420
|
-
const built = buildFusionCanonicalInput(
|
|
421
|
-
{
|
|
422
|
-
cwd: '/tmp/project',
|
|
423
|
-
sessionManager: session,
|
|
424
|
-
getSystemPrompt: () => testCase.systemPrompt,
|
|
425
|
-
},
|
|
426
|
-
options,
|
|
427
|
-
);
|
|
428
|
-
const plans: Record<string, string> = {};
|
|
429
|
-
for (const set of FUSION_GOLDEN_MODEL_SETS) {
|
|
430
|
-
const budget = new FusionBudget(
|
|
431
|
-
set.models,
|
|
432
|
-
built.input.conversation_projection.policy.id,
|
|
433
|
-
candidateCapability,
|
|
434
|
-
profile,
|
|
435
|
-
);
|
|
436
|
-
plans[set.id] = canonicalJson(budget.plan(built.input));
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
case_id: testCase.id,
|
|
440
|
-
canonical_input: built.serialized,
|
|
441
|
-
context_ledger: canonicalJson(built.ledger),
|
|
442
|
-
transcript_leaf_present: built.transcriptLeafId !== null,
|
|
443
|
-
budget_plans: plans,
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export function computeFusionGoldenCorpus(): readonly FusionGoldenRecord[] {
|
|
448
|
-
return FUSION_GOLDEN_CASES.map((testCase) => computeFusionGoldenRecord(testCase));
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Validate-workflow record.
|
|
453
|
-
*
|
|
454
|
-
* A workflow selects stage framing only, so the canonical input and the omission
|
|
455
|
-
* ledger are provably identical to the reason/session-projection run for the same case.
|
|
456
|
-
* Pinning copies of those 7 MB of bytes would not add coverage; instead the equality is
|
|
457
|
-
* asserted directly by the gate, and this fixture pins only the budget plans,
|
|
458
|
-
* which are the bytes a workflow can legitimately move.
|
|
459
|
-
*/
|
|
460
|
-
export interface FusionValidateGoldenRecord {
|
|
461
|
-
readonly case_id: string;
|
|
462
|
-
/** Historical fixture field name; proven equal to the reason record rather than duplicated. */
|
|
463
|
-
readonly canonical_input_matches_brainstorm: boolean;
|
|
464
|
-
readonly context_ledger_matches_brainstorm: boolean;
|
|
465
|
-
readonly budget_plans: Readonly<Record<string, string>>;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
export function computeFusionValidateGoldenRecord(
|
|
469
|
-
testCase: FusionGoldenCase,
|
|
470
|
-
): FusionValidateGoldenRecord {
|
|
471
|
-
const reason = computeFusionGoldenRecord(testCase);
|
|
472
|
-
const validate = computeFusionGoldenRecord(
|
|
473
|
-
testCase,
|
|
474
|
-
FUSION_VALIDATE_WORKFLOW,
|
|
475
|
-
FUSION_VALIDATE_CAPABILITY,
|
|
476
|
-
);
|
|
477
|
-
return {
|
|
478
|
-
case_id: testCase.id,
|
|
479
|
-
canonical_input_matches_brainstorm:
|
|
480
|
-
reason.canonical_input === validate.canonical_input,
|
|
481
|
-
context_ledger_matches_brainstorm: reason.context_ledger === validate.context_ledger,
|
|
482
|
-
budget_plans: validate.budget_plans,
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export function computeFusionValidateGoldenCorpus(): readonly FusionValidateGoldenRecord[] {
|
|
487
|
-
return FUSION_GOLDEN_CASES.map(computeFusionValidateGoldenRecord);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
export function serializeFusionValidateGoldenCorpus(): string {
|
|
491
|
-
return `${JSON.stringify(computeFusionValidateGoldenCorpus(), null, 2)}\n`;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Stable serialization of the whole corpus. Compared byte-for-byte against the
|
|
496
|
-
* committed golden file; a single differing byte fails the gate.
|
|
497
|
-
*/
|
|
498
|
-
export function serializeFusionGoldenCorpus(): string {
|
|
499
|
-
return `${JSON.stringify(computeFusionGoldenCorpus(), null, 2)}\n`;
|
|
500
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import type { AssistantMessage, ToolResultMessage, UserMessage } from '@earendil-works/pi-ai';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* High-cardinality interleaved tool activity.
|
|
5
|
-
*
|
|
6
|
-
* The production incident that motivated compact omission receipts was NOT a
|
|
7
|
-
* few enormous tool results: it was many small, interleaved tool events. That
|
|
8
|
-
* shape produces one receipt per contiguous omitted run, so receipt metadata —
|
|
9
|
-
* not tool payload — dominated the projected prompt.
|
|
10
|
-
*
|
|
11
|
-
* Fixtures built from a handful of huge tool results cannot reproduce it, so
|
|
12
|
-
* this builder deliberately generates many short runs separated by retained
|
|
13
|
-
* conversation text.
|
|
14
|
-
*/
|
|
15
|
-
export interface HighCardinalityOptions {
|
|
16
|
-
/** Number of retained-text separated omitted runs. */
|
|
17
|
-
runs?: number;
|
|
18
|
-
/** Tool calls (and matching results) per run. */
|
|
19
|
-
callsPerRun?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Assistant thinking blocks per cycle. Retained assistant text separates
|
|
22
|
-
* thinking from the tool calls, so a non-zero value yields two receipts per
|
|
23
|
-
* cycle instead of one.
|
|
24
|
-
*/
|
|
25
|
-
thinkingPerRun?: number;
|
|
26
|
-
/** UTF-8 bytes per tool-call argument payload. */
|
|
27
|
-
argumentBytes?: number;
|
|
28
|
-
/** UTF-8 bytes per tool-result text payload. */
|
|
29
|
-
resultBytes?: number;
|
|
30
|
-
/**
|
|
31
|
-
* UTF-8 bytes of retained conversational text per visible entry. The default
|
|
32
|
-
* reproduces the production incident's total visible volume: that session
|
|
33
|
-
* carried 120,621 bytes of visible text, and this builder emits two visible
|
|
34
|
-
* entries per cycle, so 177 bytes per entry over 340 cycles matches it.
|
|
35
|
-
*/
|
|
36
|
-
visibleTextBytes?: number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface HighCardinalitySession {
|
|
40
|
-
messages: (UserMessage | AssistantMessage | ToolResultMessage)[];
|
|
41
|
-
/**
|
|
42
|
-
* Receipts produced per cycle. Retained assistant text sits between the
|
|
43
|
-
* thinking block and the tool calls, so each cycle yields two maximal
|
|
44
|
-
* contiguous omitted runs when thinking is present: one for thinking and one
|
|
45
|
-
* for the tool call/result group.
|
|
46
|
-
*/
|
|
47
|
-
expectedRuns: number;
|
|
48
|
-
expectedLedgerEvents: number;
|
|
49
|
-
expectedToolCalls: number;
|
|
50
|
-
expectedToolResults: number;
|
|
51
|
-
expectedThinking: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function usage() {
|
|
55
|
-
return {
|
|
56
|
-
input: 1,
|
|
57
|
-
output: 1,
|
|
58
|
-
cacheRead: 0,
|
|
59
|
-
cacheWrite: 0,
|
|
60
|
-
totalTokens: 2,
|
|
61
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Builds a session whose omitted events are numerous and short. Every run is
|
|
67
|
-
* separated by retained user/assistant text so runs cannot merge, which pins
|
|
68
|
-
* the receipt count exactly.
|
|
69
|
-
*/
|
|
70
|
-
export function buildHighCardinalitySession(
|
|
71
|
-
options: HighCardinalityOptions = {},
|
|
72
|
-
): HighCardinalitySession {
|
|
73
|
-
const runs = options.runs ?? 340;
|
|
74
|
-
const callsPerRun = options.callsPerRun ?? 2;
|
|
75
|
-
const thinkingPerRun = options.thinkingPerRun ?? 0;
|
|
76
|
-
const argumentBytes = options.argumentBytes ?? 512;
|
|
77
|
-
const resultBytes = options.resultBytes ?? 1024;
|
|
78
|
-
const visibleTextBytes = options.visibleTextBytes ?? 177;
|
|
79
|
-
|
|
80
|
-
const messages: (UserMessage | AssistantMessage | ToolResultMessage)[] = [];
|
|
81
|
-
let timestamp = 1;
|
|
82
|
-
|
|
83
|
-
for (let run = 0; run < runs; run++) {
|
|
84
|
-
// Retained text separates runs so contiguous omissions cannot merge.
|
|
85
|
-
messages.push({
|
|
86
|
-
role: 'user',
|
|
87
|
-
content: `USER-TURN-${String(run)} ${'q'.repeat(visibleTextBytes)}`,
|
|
88
|
-
timestamp: timestamp++,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const content: AssistantMessage['content'] = [];
|
|
92
|
-
for (let t = 0; t < thinkingPerRun; t++) {
|
|
93
|
-
content.push({ type: 'thinking', thinking: 'k'.repeat(64) });
|
|
94
|
-
}
|
|
95
|
-
content.push({
|
|
96
|
-
type: 'text',
|
|
97
|
-
text: `ASSISTANT-TURN-${String(run)} ${'w'.repeat(visibleTextBytes)}`,
|
|
98
|
-
});
|
|
99
|
-
for (let call = 0; call < callsPerRun; call++) {
|
|
100
|
-
content.push({
|
|
101
|
-
type: 'toolCall',
|
|
102
|
-
id: `call-${String(run)}-${String(call)}`,
|
|
103
|
-
name: call % 2 === 0 ? 'read' : 'bash',
|
|
104
|
-
arguments: { payload: 'g'.repeat(argumentBytes) },
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
messages.push({
|
|
108
|
-
role: 'assistant',
|
|
109
|
-
api: 'openai-codex-responses',
|
|
110
|
-
provider: 'openai-codex',
|
|
111
|
-
model: 'gpt-5.5',
|
|
112
|
-
usage: usage(),
|
|
113
|
-
stopReason: 'toolUse',
|
|
114
|
-
content,
|
|
115
|
-
timestamp: timestamp++,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
for (let call = 0; call < callsPerRun; call++) {
|
|
119
|
-
messages.push({
|
|
120
|
-
role: 'toolResult',
|
|
121
|
-
toolCallId: `call-${String(run)}-${String(call)}`,
|
|
122
|
-
toolName: call % 2 === 0 ? 'read' : 'bash',
|
|
123
|
-
content: [{ type: 'text', text: 'z'.repeat(resultBytes) }],
|
|
124
|
-
details: { ok: true },
|
|
125
|
-
isError: false,
|
|
126
|
-
timestamp: timestamp++,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const receiptsPerCycle = thinkingPerRun > 0 ? 2 : 1;
|
|
132
|
-
return {
|
|
133
|
-
messages,
|
|
134
|
-
expectedRuns: runs * receiptsPerCycle,
|
|
135
|
-
expectedLedgerEvents: runs * (thinkingPerRun + callsPerRun * 2),
|
|
136
|
-
expectedToolCalls: runs * callsPerRun,
|
|
137
|
-
expectedToolResults: runs * callsPerRun,
|
|
138
|
-
expectedThinking: runs * thinkingPerRun,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const isolatedTestEnv = {
|
|
2
|
-
PI_OFFLINE: '1',
|
|
3
|
-
PI_SKIP_VERSION_CHECK: '1',
|
|
4
|
-
PI_TELEMETRY: '0',
|
|
5
|
-
CI: '1',
|
|
6
|
-
} as const;
|
|
7
|
-
|
|
8
|
-
const ESCAPE = String.fromCharCode(27);
|
|
9
|
-
const ANSI_PATTERN = new RegExp(`${ESCAPE}\\[[0-?]*[ -/]*[@-~]`, 'g');
|
|
10
|
-
|
|
11
|
-
export function stripAnsi(value: string): string {
|
|
12
|
-
return value.replace(ANSI_PATTERN, '');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function normalizeVolatile(value: string): string {
|
|
16
|
-
return value
|
|
17
|
-
.replace(/b[0-9a-f]{8}/g, '<TASK_ID>')
|
|
18
|
-
.replace(/[0-9a-f]{8}-[0-9a-f-]{27,}/gi, '<UUID>')
|
|
19
|
-
.replace(/pid=?\s*\d+/gi, 'pid=<PID>')
|
|
20
|
-
.replace(/\.pi\/tasks\/[^\s)]+/g, '.pi/tasks/<RUN>/<FILE>')
|
|
21
|
-
.replace(/\/tmp\/[^\s)]+/g, '/tmp/<TEMP>');
|
|
22
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schema_version": "pi-background-tasks.delegate-hook-contract.v1",
|
|
3
|
-
"contract_id": "context-measure-abort-v1+tool-result-spill-v1",
|
|
4
|
-
"guarantees": {
|
|
5
|
-
"context_fires_before_every_model_call": true,
|
|
6
|
-
"context_result_messages_reach_provider": true,
|
|
7
|
-
"context_abort_blocks_provider_call": true,
|
|
8
|
-
"context_abort_skips_stream_invocation": false,
|
|
9
|
-
"context_abort_terminates_run": true,
|
|
10
|
-
"context_throw_blocks_provider_call": false,
|
|
11
|
-
"context_throw_isolated_to_throwing_handler": true,
|
|
12
|
-
"tool_result_fires_before_transcript_entry": true,
|
|
13
|
-
"tool_result_replacement_reaches_provider": true,
|
|
14
|
-
"tool_result_replacement_preserves_identity": true,
|
|
15
|
-
"tool_result_chains_in_load_order": true,
|
|
16
|
-
"handlers_run_in_extension_load_order": true
|
|
17
|
-
}
|
|
18
|
-
}
|