@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,403 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
-
import {
|
|
3
|
-
createAssistantMessageEventStream,
|
|
4
|
-
type Api,
|
|
5
|
-
type AssistantMessage,
|
|
6
|
-
type AssistantMessageEventStream,
|
|
7
|
-
type Context,
|
|
8
|
-
type Model,
|
|
9
|
-
type TextContent,
|
|
10
|
-
type ToolCall,
|
|
11
|
-
} from '@earendil-works/pi-ai';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Deterministic provider used by the delegate child-guard gate.
|
|
15
|
-
*
|
|
16
|
-
* Scenarios drive the guard through spill, budget refusal, route drift, turn
|
|
17
|
-
* limits, and the clean commit path, all inside a real Pi agent loop.
|
|
18
|
-
*/
|
|
19
|
-
const PROVIDER = 'pi-bg-delegate';
|
|
20
|
-
const MODEL_ID = 'delegate-model';
|
|
21
|
-
const API = 'pi-bg-delegate-api';
|
|
22
|
-
|
|
23
|
-
const USAGE = {
|
|
24
|
-
input: 12,
|
|
25
|
-
output: 6,
|
|
26
|
-
cacheRead: 0,
|
|
27
|
-
cacheWrite: 0,
|
|
28
|
-
totalTokens: 18,
|
|
29
|
-
cost: { input: 0.001, output: 0.002, cacheRead: 0, cacheWrite: 0, total: 0.003 },
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type JsonObject = Record<PropertyKey, unknown>;
|
|
33
|
-
|
|
34
|
-
interface ScriptedToolCall extends Omit<ToolCall, 'arguments'> {
|
|
35
|
-
arguments: JsonObject;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type ScriptedBlock = TextContent | ScriptedToolCall;
|
|
39
|
-
|
|
40
|
-
interface ScriptedAssistantMessage extends Omit<AssistantMessage, 'content' | 'stopReason'> {
|
|
41
|
-
content: ScriptedBlock[];
|
|
42
|
-
stopReason: 'stop' | 'toolUse' | 'length';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function scenario(): string {
|
|
46
|
-
return process.env['UNIPI_BG_DELEGATE_SCENARIO'] ?? 'plain-answer';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function assistant(
|
|
50
|
-
content: ScriptedBlock[],
|
|
51
|
-
stopReason: 'stop' | 'toolUse' | 'length',
|
|
52
|
-
overrides: { provider?: string; model?: string } = {},
|
|
53
|
-
): ScriptedAssistantMessage {
|
|
54
|
-
return {
|
|
55
|
-
role: 'assistant',
|
|
56
|
-
content,
|
|
57
|
-
api: API,
|
|
58
|
-
provider: overrides.provider ?? PROVIDER,
|
|
59
|
-
model: overrides.model ?? MODEL_ID,
|
|
60
|
-
usage: USAGE,
|
|
61
|
-
stopReason,
|
|
62
|
-
timestamp: Date.now(),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function pushMessage(stream: AssistantMessageEventStream, message: ScriptedAssistantMessage): void {
|
|
67
|
-
const partial: AssistantMessage = { ...message, content: [] };
|
|
68
|
-
stream.push({ type: 'start', partial: { ...partial } });
|
|
69
|
-
message.content.forEach((block, contentIndex) => {
|
|
70
|
-
if (block.type === 'text') {
|
|
71
|
-
const partialText: TextContent = { type: 'text', text: '' };
|
|
72
|
-
partial.content = [...partial.content, partialText];
|
|
73
|
-
stream.push({ type: 'text_start', contentIndex, partial: { ...partial } });
|
|
74
|
-
partialText.text = block.text;
|
|
75
|
-
stream.push({ type: 'text_delta', contentIndex, delta: block.text, partial: { ...partial } });
|
|
76
|
-
stream.push({ type: 'text_end', contentIndex, content: block.text, partial: { ...partial } });
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const partialToolCall: ToolCall = {
|
|
80
|
-
type: 'toolCall',
|
|
81
|
-
id: block.id,
|
|
82
|
-
name: block.name,
|
|
83
|
-
arguments: {},
|
|
84
|
-
};
|
|
85
|
-
partial.content = [...partial.content, partialToolCall];
|
|
86
|
-
stream.push({ type: 'toolcall_start', contentIndex, partial: { ...partial } });
|
|
87
|
-
stream.push({
|
|
88
|
-
type: 'toolcall_delta',
|
|
89
|
-
contentIndex,
|
|
90
|
-
delta: JSON.stringify(block.arguments),
|
|
91
|
-
partial: { ...partial },
|
|
92
|
-
});
|
|
93
|
-
partialToolCall.arguments = block.arguments;
|
|
94
|
-
stream.push({ type: 'toolcall_end', contentIndex, toolCall: block, partial: { ...partial } });
|
|
95
|
-
});
|
|
96
|
-
stream.push({ type: 'done', reason: message.stopReason, message });
|
|
97
|
-
stream.end(message);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const GuardProbeParams = {
|
|
101
|
-
type: 'object',
|
|
102
|
-
properties: {
|
|
103
|
-
size: { type: 'number' },
|
|
104
|
-
marker: { type: 'string' },
|
|
105
|
-
},
|
|
106
|
-
additionalProperties: false,
|
|
107
|
-
} as const;
|
|
108
|
-
|
|
109
|
-
const GuardImageParams = {
|
|
110
|
-
type: 'object',
|
|
111
|
-
properties: {},
|
|
112
|
-
additionalProperties: false,
|
|
113
|
-
} as const;
|
|
114
|
-
|
|
115
|
-
function findSpillReceiptPath(context: Context): string | undefined {
|
|
116
|
-
for (const message of context.messages) {
|
|
117
|
-
if (message.role !== 'toolResult') continue;
|
|
118
|
-
const content = message.content;
|
|
119
|
-
if (!Array.isArray(content)) continue;
|
|
120
|
-
for (const part of content) {
|
|
121
|
-
if (part.type !== 'text') continue;
|
|
122
|
-
const match = /spill\/[A-Za-z0-9._-]+\.bin/.exec(part.text);
|
|
123
|
-
if (match) return match[0];
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export default function delegateGuardProviderExtension(pi: ExtensionAPI): void {
|
|
130
|
-
let calls = 0;
|
|
131
|
-
|
|
132
|
-
pi.registerTool<typeof GuardProbeParams, { bytes: number }>({
|
|
133
|
-
name: 'guard_probe',
|
|
134
|
-
label: 'Guard Probe',
|
|
135
|
-
description: 'Delegate guard characterisation probe that emits a payload of a requested size.',
|
|
136
|
-
parameters: GuardProbeParams,
|
|
137
|
-
execute(_toolCallId, input) {
|
|
138
|
-
const size = typeof input.size === 'number' ? input.size : 16;
|
|
139
|
-
const marker = typeof input.marker === 'string' ? input.marker : 'HUGEPAYLOAD';
|
|
140
|
-
// A repeated marker makes leakage into the transcript unambiguous.
|
|
141
|
-
const unit = marker.padEnd(16, '.');
|
|
142
|
-
const text = unit.repeat(Math.ceil(size / unit.length)).slice(0, size);
|
|
143
|
-
return Promise.resolve({
|
|
144
|
-
content: [{ type: 'text' as const, text }],
|
|
145
|
-
details: { bytes: text.length },
|
|
146
|
-
});
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
pi.registerTool<typeof GuardImageParams, { bytes: number }>({
|
|
151
|
-
name: 'guard_image_probe',
|
|
152
|
-
label: 'Guard Image Probe',
|
|
153
|
-
description: 'Delegate guard probe that emits image-bearing tool content.',
|
|
154
|
-
parameters: GuardImageParams,
|
|
155
|
-
execute() {
|
|
156
|
-
const data = Buffer.from('IMAGE_SENTINEL_BYTES'.repeat(256), 'utf8').toString('base64');
|
|
157
|
-
return Promise.resolve({
|
|
158
|
-
content: [
|
|
159
|
-
{ type: 'text' as const, text: 'image preface' },
|
|
160
|
-
{ type: 'image' as const, data, mimeType: 'image/png' },
|
|
161
|
-
],
|
|
162
|
-
details: { bytes: Buffer.byteLength(data, 'utf8') },
|
|
163
|
-
});
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
pi.registerProvider(PROVIDER, {
|
|
168
|
-
name: 'Pi Background Tasks Delegate Guard Provider',
|
|
169
|
-
baseUrl: 'http://localhost:0',
|
|
170
|
-
apiKey: 'UNIPI_BG_DELEGATE_API_KEY',
|
|
171
|
-
api: API,
|
|
172
|
-
models: [
|
|
173
|
-
{
|
|
174
|
-
id: MODEL_ID,
|
|
175
|
-
name: 'Delegate Guard Model',
|
|
176
|
-
reasoning: false,
|
|
177
|
-
input: ['text'],
|
|
178
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
179
|
-
contextWindow: 200_000,
|
|
180
|
-
maxTokens: 4096,
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
streamSimple(_model: Model<Api>, context: Context): AssistantMessageEventStream {
|
|
184
|
-
calls += 1;
|
|
185
|
-
const current = scenario();
|
|
186
|
-
const stream = createAssistantMessageEventStream();
|
|
187
|
-
const call = calls;
|
|
188
|
-
queueMicrotask(() => {
|
|
189
|
-
pushMessage(stream, responseFor(current, call, context));
|
|
190
|
-
});
|
|
191
|
-
return stream;
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function responseFor(
|
|
197
|
-
current: string,
|
|
198
|
-
call: number,
|
|
199
|
-
context: Context,
|
|
200
|
-
): ScriptedAssistantMessage {
|
|
201
|
-
if (current === 'invalid-unicode-tool-result') {
|
|
202
|
-
if (call === 1) {
|
|
203
|
-
return assistant(
|
|
204
|
-
[
|
|
205
|
-
{
|
|
206
|
-
type: 'toolCall',
|
|
207
|
-
id: 'guard-call-invalid-unicode',
|
|
208
|
-
name: 'guard_probe',
|
|
209
|
-
arguments: { size: 16, marker: '\uD800' },
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
'toolUse',
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
return assistant([{ type: 'text', text: 'MUST_NOT_COMMIT' }], 'stop');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (current === 'intermediate-narration') {
|
|
219
|
-
if (call === 1) {
|
|
220
|
-
return assistant(
|
|
221
|
-
[
|
|
222
|
-
{ type: 'text', text: 'INTERMEDIATE_NARRATION_MUST_NOT_BE_COMMITTED' },
|
|
223
|
-
{
|
|
224
|
-
type: 'toolCall',
|
|
225
|
-
id: 'guard-call-narration',
|
|
226
|
-
name: 'guard_probe',
|
|
227
|
-
arguments: { size: 16, marker: 'evidence' },
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
'toolUse',
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (current === 'image-tool-result') {
|
|
237
|
-
if (call === 1) {
|
|
238
|
-
return assistant(
|
|
239
|
-
[
|
|
240
|
-
{
|
|
241
|
-
type: 'toolCall',
|
|
242
|
-
id: 'guard-call-image',
|
|
243
|
-
name: 'guard_image_probe',
|
|
244
|
-
arguments: {},
|
|
245
|
-
},
|
|
246
|
-
],
|
|
247
|
-
'toolUse',
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (current === 'huge-tool-result') {
|
|
254
|
-
if (call === 1) {
|
|
255
|
-
return assistant(
|
|
256
|
-
[
|
|
257
|
-
{
|
|
258
|
-
type: 'toolCall',
|
|
259
|
-
id: 'guard-call-huge',
|
|
260
|
-
name: 'guard_probe',
|
|
261
|
-
arguments: { size: 2 * 1024 * 1024, marker: 'HUGEPAYLOAD' },
|
|
262
|
-
},
|
|
263
|
-
],
|
|
264
|
-
'toolUse',
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (current === 'split-utf8-range') {
|
|
271
|
-
if (call === 1) {
|
|
272
|
-
return assistant(
|
|
273
|
-
[
|
|
274
|
-
{
|
|
275
|
-
type: 'toolCall',
|
|
276
|
-
id: 'guard-call-utf8',
|
|
277
|
-
name: 'guard_probe',
|
|
278
|
-
arguments: { size: 2048, marker: 'é' },
|
|
279
|
-
},
|
|
280
|
-
],
|
|
281
|
-
'toolUse',
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
const artifact = findSpillReceiptPath(context);
|
|
285
|
-
if (call === 2 && artifact !== undefined) {
|
|
286
|
-
return assistant(
|
|
287
|
-
[
|
|
288
|
-
{
|
|
289
|
-
type: 'toolCall',
|
|
290
|
-
id: 'guard-call-split-byte',
|
|
291
|
-
name: 'delegate_read_artifact',
|
|
292
|
-
arguments: { artifact, offset: 1, length: 1 },
|
|
293
|
-
},
|
|
294
|
-
],
|
|
295
|
-
'toolUse',
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (current === 'spill-then-read') {
|
|
302
|
-
if (call === 1) {
|
|
303
|
-
return assistant(
|
|
304
|
-
[
|
|
305
|
-
{
|
|
306
|
-
type: 'toolCall',
|
|
307
|
-
id: 'guard-call-spill',
|
|
308
|
-
name: 'guard_probe',
|
|
309
|
-
arguments: { size: 8192, marker: 'RANGEMARKER' },
|
|
310
|
-
},
|
|
311
|
-
],
|
|
312
|
-
'toolUse',
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
const artifact = findSpillReceiptPath(context);
|
|
316
|
-
if (call === 2 && artifact !== undefined) {
|
|
317
|
-
return assistant(
|
|
318
|
-
[
|
|
319
|
-
{
|
|
320
|
-
type: 'toolCall',
|
|
321
|
-
id: 'guard-call-range',
|
|
322
|
-
name: 'delegate_read_artifact',
|
|
323
|
-
arguments: { artifact, offset: 0, length: 11 },
|
|
324
|
-
},
|
|
325
|
-
],
|
|
326
|
-
'toolUse',
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
if (call === 3 && artifact !== undefined) {
|
|
330
|
-
// Deliberately over-long: the bounded reader must refuse rather than
|
|
331
|
-
// return a short read.
|
|
332
|
-
return assistant(
|
|
333
|
-
[
|
|
334
|
-
{
|
|
335
|
-
type: 'toolCall',
|
|
336
|
-
id: 'guard-call-overlong',
|
|
337
|
-
name: 'delegate_read_artifact',
|
|
338
|
-
arguments: { artifact, offset: 8000, length: 100_000 },
|
|
339
|
-
},
|
|
340
|
-
],
|
|
341
|
-
'toolUse',
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (current === 'subthreshold-growth') {
|
|
348
|
-
if (call <= 8) {
|
|
349
|
-
return assistant(
|
|
350
|
-
[
|
|
351
|
-
{
|
|
352
|
-
type: 'toolCall',
|
|
353
|
-
id: `guard-call-growth-${String(call)}`,
|
|
354
|
-
name: 'guard_probe',
|
|
355
|
-
arguments: { size: 32 * 1024, marker: `GROWTH${String(call)}` },
|
|
356
|
-
},
|
|
357
|
-
],
|
|
358
|
-
'toolUse',
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (current === 'truncated-answer') {
|
|
365
|
-
// A response cut short by the output-token limit. Its bytes are intact but
|
|
366
|
-
// the answer is incomplete, so it must never be committed as a result.
|
|
367
|
-
return assistant([{ type: 'text', text: 'PARTIAL ANSWER CUT OFF' }], 'length');
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (current === 'whitespace-answer') {
|
|
371
|
-
return assistant([{ type: 'text', text: ' \n ' }], 'stop');
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (current === 'guard-throw') {
|
|
375
|
-
return assistant([{ type: 'text', text: 'GUARD_THROW_SENTINEL_ANSWER' }], 'stop');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (current === 'route-drift') {
|
|
379
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop', {
|
|
380
|
-
provider: 'someone-else',
|
|
381
|
-
model: 'other-model',
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (current === 'many-turns') {
|
|
386
|
-
if (call <= 4) {
|
|
387
|
-
return assistant(
|
|
388
|
-
[
|
|
389
|
-
{
|
|
390
|
-
type: 'toolCall',
|
|
391
|
-
id: `guard-call-loop-${String(call)}`,
|
|
392
|
-
name: 'guard_probe',
|
|
393
|
-
arguments: { size: 16, marker: 'LOOP' },
|
|
394
|
-
},
|
|
395
|
-
],
|
|
396
|
-
'toolUse',
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return assistant([{ type: 'text', text: 'DELEGATE_FINAL_ANSWER' }], 'stop');
|
|
403
|
-
}
|