@wichayutdew/pi-workflows 0.2.2 → 0.3.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/LICENSE +21 -201
- package/README.md +207 -106
- package/agents/step.md +15 -1
- package/dist/index.js +1964 -463
- package/examples/mr-comments.workflow.yaml +4 -4
- package/examples/prompts/mr-comments/implement.md +10 -5
- package/examples/prompts/mr-comments/plan.md +10 -5
- package/examples/prompts/mr-comments/verify.md +5 -4
- package/examples/settings.yaml +3 -1
- package/package.json +10 -6
- package/schemas/settings.schema.json +8 -0
- package/schemas/workflow.schema.json +10 -2
- package/src/command-names.ts +0 -1
- package/src/commands.ts +0 -6
- package/src/config/ceiling.ts +8 -0
- package/src/config/load.ts +3 -9
- package/src/config/types.ts +15 -3
- package/src/config/validate.ts +147 -22
- package/src/engine/state.ts +7 -0
- package/src/engine/transitions.ts +52 -7
- package/src/harness.ts +804 -74
- package/src/index.ts +6 -2
- package/src/integrations/prompt-gate.ts +14 -13
- package/src/integrations/subagents/child-runtime.ts +187 -69
- package/src/integrations/subagents/client.ts +4 -3
- package/src/integrations/subagents/diagnostics.ts +977 -0
- package/src/integrations/subagents/protocol.ts +86 -15
- package/src/policy/approved-commands.ts +212 -5
- package/src/policy/bash.ts +0 -9
- package/src/prompt.ts +115 -7
- package/src/runtime/serial-task-queue.ts +5 -1
- package/src/workflow-status.ts +244 -35
package/src/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { defaultUserWorkflowDirectory, loadSettings } from './config/load.ts';
|
|
2
3
|
import { WorkflowHarness } from './harness.ts';
|
|
3
4
|
import { registerSubagentChildRuntime } from './integrations/subagents/child-runtime.ts';
|
|
4
5
|
import { isSubagentRuntimeName } from './integrations/subagents/protocol.ts';
|
|
5
6
|
|
|
6
|
-
export default function piWorkflowsExtension(
|
|
7
|
+
export default async function piWorkflowsExtension(
|
|
8
|
+
pi: ExtensionAPI,
|
|
9
|
+
): Promise<void> {
|
|
7
10
|
if (process.env.PI_SUBAGENT_CHILD === '1') {
|
|
8
11
|
const childAgent = process.env.PI_SUBAGENT_CHILD_AGENT?.trim();
|
|
9
12
|
if (isSubagentRuntimeName(childAgent)) {
|
|
@@ -11,5 +14,6 @@ export default function piWorkflowsExtension(pi: ExtensionAPI): void {
|
|
|
11
14
|
}
|
|
12
15
|
return;
|
|
13
16
|
}
|
|
14
|
-
|
|
17
|
+
const { settings } = await loadSettings(defaultUserWorkflowDirectory());
|
|
18
|
+
new WorkflowHarness(pi, settings.statusShortcut);
|
|
15
19
|
}
|
|
@@ -33,22 +33,23 @@ export async function requestPromptGateReview(
|
|
|
33
33
|
return { status: 'dismissed' };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
let feedback = await ui.input(
|
|
37
|
+
'Workflow review feedback',
|
|
38
|
+
'Describe the required changes',
|
|
39
|
+
...(signal ? [{ signal }] : []),
|
|
40
|
+
);
|
|
41
|
+
while (feedback !== undefined && !feedback.trim()) {
|
|
42
|
+
ui.notify('Feedback cannot be empty', 'warning');
|
|
43
|
+
feedback = await ui.input(
|
|
38
44
|
'Workflow review feedback',
|
|
39
45
|
'Describe the required changes',
|
|
40
46
|
...(signal ? [{ signal }] : []),
|
|
41
47
|
);
|
|
42
|
-
if (feedback === undefined) {
|
|
43
|
-
return { status: 'dismissed' };
|
|
44
|
-
}
|
|
45
|
-
if (feedback.trim()) {
|
|
46
|
-
return {
|
|
47
|
-
status: 'resolved',
|
|
48
|
-
approved: false,
|
|
49
|
-
feedback: feedback.trim(),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
ui.notify('Feedback cannot be empty', 'warning');
|
|
53
48
|
}
|
|
49
|
+
if (feedback === undefined) return { status: 'dismissed' };
|
|
50
|
+
return {
|
|
51
|
+
status: 'resolved',
|
|
52
|
+
approved: false,
|
|
53
|
+
feedback: feedback.trim(),
|
|
54
|
+
};
|
|
54
55
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import {
|
|
3
3
|
existsSync,
|
|
4
|
+
lstatSync,
|
|
4
5
|
readFileSync,
|
|
6
|
+
realpathSync,
|
|
5
7
|
renameSync,
|
|
8
|
+
statSync,
|
|
6
9
|
unlinkSync,
|
|
7
10
|
writeFileSync,
|
|
8
11
|
} from 'node:fs';
|
|
12
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
9
13
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
10
14
|
import type { WorkflowStep } from '../../config/types.ts';
|
|
11
15
|
import { invalidCompletionCallIds } from '../../policy/completion-batch.ts';
|
|
12
16
|
import { freezeToolInput } from '../../policy/immutable-input.ts';
|
|
13
17
|
import { authorizeToolCall, resolveActiveTools } from '../../policy/tools.ts';
|
|
14
|
-
import {
|
|
15
|
-
WORKFLOW_COMPLETION_PARAMETERS,
|
|
16
|
-
WORKFLOW_COMPLETION_TOOL,
|
|
17
|
-
} from '../../runtime/completion-tool.ts';
|
|
18
18
|
import {
|
|
19
19
|
extractChildPolicy,
|
|
20
20
|
isSubagentRuntimeName,
|
|
@@ -22,7 +22,14 @@ import {
|
|
|
22
22
|
type ChildStepPolicy,
|
|
23
23
|
} from './protocol.ts';
|
|
24
24
|
|
|
25
|
-
export const CHILD_COMPLETION_TOOL =
|
|
25
|
+
export const CHILD_COMPLETION_TOOL = 'structured_output';
|
|
26
|
+
const CHILD_COORDINATION_TOOLS = new Set([
|
|
27
|
+
'contact_supervisor',
|
|
28
|
+
'subagent_supervisor',
|
|
29
|
+
'intercom',
|
|
30
|
+
]);
|
|
31
|
+
const STRUCTURED_RESULT_KEYS = new Set(['outcome', 'summary', 'artifact']);
|
|
32
|
+
const FILE_MUTATION_TOOLS = new Set(['edit', 'write']);
|
|
26
33
|
|
|
27
34
|
function policyStep(policy: ChildStepPolicy): WorkflowStep {
|
|
28
35
|
return {
|
|
@@ -33,6 +40,7 @@ function policyStep(policy: ChildStepPolicy): WorkflowStep {
|
|
|
33
40
|
context: 'fresh',
|
|
34
41
|
timeoutMs: 900_000,
|
|
35
42
|
artifacts: false,
|
|
43
|
+
retryToolFailures: false,
|
|
36
44
|
},
|
|
37
45
|
permissions: policy.permissions,
|
|
38
46
|
requires: { tools: [], extensions: [], skills: [] },
|
|
@@ -41,6 +49,7 @@ function policyStep(policy: ChildStepPolicy): WorkflowStep {
|
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
function childSystemPrompt(policy: ChildStepPolicy): string {
|
|
52
|
+
const hasPauseOutcome = policy.pauseOutcomes.length > 0;
|
|
44
53
|
return [
|
|
45
54
|
'# Pi Workflows delegated step',
|
|
46
55
|
'',
|
|
@@ -50,15 +59,50 @@ function childSystemPrompt(policy: ChildStepPolicy): string {
|
|
|
50
59
|
'',
|
|
51
60
|
'The parent workflow harness owns orchestration and state transitions.',
|
|
52
61
|
'Perform only this delegated step. Its child-side tool policy is enforced.',
|
|
53
|
-
'When finished, call `
|
|
62
|
+
'When finished, call `structured_output` exactly once and as the only tool call in that message.',
|
|
63
|
+
'Pass the workflow result as its `value`: outcome, summary, and optional artifact.',
|
|
54
64
|
`Valid outcomes: ${policy.outcomes.join(', ')}`,
|
|
65
|
+
`Pause outcomes: ${policy.pauseOutcomes.join(', ') || '(none)'}`,
|
|
55
66
|
`Summary limit: ${policy.summaryMaxChars} characters`,
|
|
56
67
|
...(policy.gateSubmitOutcome
|
|
57
68
|
? [
|
|
58
69
|
`Outcome "${policy.gateSubmitOutcome}" requires the complete gate artifact.`,
|
|
59
70
|
]
|
|
60
71
|
: []),
|
|
61
|
-
|
|
72
|
+
...(hasPauseOutcome
|
|
73
|
+
? [
|
|
74
|
+
`If the workflow definition or environment is wrong, choose a pause outcome (${policy.pauseOutcomes.join(', ')}).`,
|
|
75
|
+
]
|
|
76
|
+
: [
|
|
77
|
+
'If the workflow definition or environment is wrong, do not fabricate success or call the completion tool; end with a concise declarative error so the parent pauses the step.',
|
|
78
|
+
]),
|
|
79
|
+
'This is a non-interactive workflow child. Never call contact_supervisor, subagent_supervisor, or intercom.',
|
|
80
|
+
...(policy.gateSubmitOutcome
|
|
81
|
+
? [
|
|
82
|
+
'Put every unresolved decision in the gate artifact with evidence, options, a recommendation, and an adopted default; do not ask a terminal question.',
|
|
83
|
+
]
|
|
84
|
+
: hasPauseOutcome
|
|
85
|
+
? [
|
|
86
|
+
'Treat the step instructions and incoming handoff as the final execution contract.',
|
|
87
|
+
'If that contract is missing, stale, or contradictory, finish with a pause outcome and describe the unresolved contract and evidence declaratively in the summary; do not ask a terminal question.',
|
|
88
|
+
]
|
|
89
|
+
: [
|
|
90
|
+
'Treat the step instructions and incoming handoff as the final execution contract.',
|
|
91
|
+
'If that contract is missing, stale, or contradictory, do not fabricate success or call the completion tool; end with a concise declarative error so the parent pauses the step. Do not ask a terminal question.',
|
|
92
|
+
]),
|
|
93
|
+
...(policy.repositoryCwd
|
|
94
|
+
? [
|
|
95
|
+
`Reviewed repository root: ${policy.repositoryCwd}`,
|
|
96
|
+
...(policy.bootstrapCwd
|
|
97
|
+
? [
|
|
98
|
+
`Bootstrap directory: ${policy.bootstrapCwd}`,
|
|
99
|
+
'The reviewed repository root does not exist yet. Run only its exact approved setup command first, then use absolute paths under the reviewed repository root for every edit and write. Never mutate the bootstrap directory.',
|
|
100
|
+
]
|
|
101
|
+
: [
|
|
102
|
+
'Keep every edit and write inside the reviewed repository root.',
|
|
103
|
+
]),
|
|
104
|
+
]
|
|
105
|
+
: []),
|
|
62
106
|
].join('\n');
|
|
63
107
|
}
|
|
64
108
|
|
|
@@ -84,6 +128,43 @@ function writeResult(policy: ChildStepPolicy, result: unknown): void {
|
|
|
84
128
|
}
|
|
85
129
|
}
|
|
86
130
|
|
|
131
|
+
function structuredResult(
|
|
132
|
+
input: unknown,
|
|
133
|
+
policy: ChildStepPolicy,
|
|
134
|
+
): ReturnType<typeof parseDelegatedStepResult> {
|
|
135
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
136
|
+
throw new Error('structured_output input must be an object');
|
|
137
|
+
}
|
|
138
|
+
const wrapper = input as Record<string, unknown>;
|
|
139
|
+
if (Object.keys(wrapper).length !== 1 || !Object.hasOwn(wrapper, 'value')) {
|
|
140
|
+
throw new Error('structured_output input must contain only value');
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
wrapper.value === null ||
|
|
144
|
+
typeof wrapper.value !== 'object' ||
|
|
145
|
+
Array.isArray(wrapper.value)
|
|
146
|
+
) {
|
|
147
|
+
throw new Error('structured_output value must be an object');
|
|
148
|
+
}
|
|
149
|
+
const value = wrapper.value as Record<string, unknown>;
|
|
150
|
+
const unknownKey = Object.keys(value).find(
|
|
151
|
+
(key) => !STRUCTURED_RESULT_KEYS.has(key),
|
|
152
|
+
);
|
|
153
|
+
if (unknownKey) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`structured_output value has unknown property "${unknownKey}"`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return parseDelegatedStepResult(
|
|
159
|
+
{
|
|
160
|
+
version: 1,
|
|
161
|
+
policyDigest: policy.policyDigest,
|
|
162
|
+
...value,
|
|
163
|
+
},
|
|
164
|
+
policy,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
87
168
|
function verifyCapability(
|
|
88
169
|
policy: ChildStepPolicy,
|
|
89
170
|
childAgent: string | undefined,
|
|
@@ -104,6 +185,67 @@ function verifyCapability(
|
|
|
104
185
|
unlinkSync(policy.capabilityPath);
|
|
105
186
|
}
|
|
106
187
|
|
|
188
|
+
function authorizeRepositoryMutation(
|
|
189
|
+
toolName: string,
|
|
190
|
+
input: Record<string, unknown>,
|
|
191
|
+
policy: ChildStepPolicy,
|
|
192
|
+
): string | undefined {
|
|
193
|
+
if (!policy.repositoryCwd || !FILE_MUTATION_TOOLS.has(toolName)) return;
|
|
194
|
+
if (typeof input.path !== 'string' || !input.path.trim()) {
|
|
195
|
+
return `${toolName} must name a path inside the reviewed repository root`;
|
|
196
|
+
}
|
|
197
|
+
const candidate = resolve(process.cwd(), input.path);
|
|
198
|
+
const root = resolve(policy.repositoryCwd);
|
|
199
|
+
if (!pathIsInside(root, candidate)) {
|
|
200
|
+
return `${toolName} path is outside the reviewed repository root "${policy.repositoryCwd}"`;
|
|
201
|
+
}
|
|
202
|
+
let canonicalRoot: string;
|
|
203
|
+
try {
|
|
204
|
+
if (!statSync(root).isDirectory()) throw new Error('not a directory');
|
|
205
|
+
canonicalRoot = realpathSync(root);
|
|
206
|
+
} catch {
|
|
207
|
+
return `reviewed repository root is not an existing directory: ${policy.repositoryCwd}`;
|
|
208
|
+
}
|
|
209
|
+
const canonicalAncestor = nearestCanonicalAncestor(candidate);
|
|
210
|
+
if (
|
|
211
|
+
canonicalAncestor === undefined ||
|
|
212
|
+
!pathIsInside(canonicalRoot, canonicalAncestor)
|
|
213
|
+
) {
|
|
214
|
+
return `${toolName} path is outside the reviewed repository root "${policy.repositoryCwd}"`;
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function pathIsInside(root: string, candidate: string): boolean {
|
|
220
|
+
const fromRoot = relative(root, candidate);
|
|
221
|
+
return (
|
|
222
|
+
fromRoot !== '..' &&
|
|
223
|
+
!fromRoot.startsWith(`..${sep}`) &&
|
|
224
|
+
!isAbsolute(fromRoot)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function nearestCanonicalAncestor(path: string): string | undefined {
|
|
229
|
+
let candidate = path;
|
|
230
|
+
while (true) {
|
|
231
|
+
try {
|
|
232
|
+
lstatSync(candidate);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const code = (error as { code?: unknown }).code;
|
|
235
|
+
if (code !== 'ENOENT') return undefined;
|
|
236
|
+
const parent = dirname(candidate);
|
|
237
|
+
if (parent === candidate) return undefined;
|
|
238
|
+
candidate = parent;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
return realpathSync(candidate);
|
|
243
|
+
} catch {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
107
249
|
export interface SubagentChildRuntimeOptions {
|
|
108
250
|
childAgent?: string;
|
|
109
251
|
}
|
|
@@ -116,61 +258,9 @@ export function registerSubagentChildRuntime(
|
|
|
116
258
|
let policyError: string | undefined;
|
|
117
259
|
let invalidCompletionCalls = new Set<string>();
|
|
118
260
|
let effectiveTools = new Set<string>();
|
|
119
|
-
let completionRegistered = false;
|
|
120
261
|
const childAgent =
|
|
121
262
|
options.childAgent ?? process.env.PI_SUBAGENT_CHILD_AGENT?.trim();
|
|
122
263
|
|
|
123
|
-
const registerCompletionTool = (): void => {
|
|
124
|
-
if (completionRegistered) return;
|
|
125
|
-
completionRegistered = true;
|
|
126
|
-
pi.registerTool({
|
|
127
|
-
name: CHILD_COMPLETION_TOOL,
|
|
128
|
-
label: 'Complete Delegated Workflow Step',
|
|
129
|
-
description:
|
|
130
|
-
'Return one validated result from a pi-workflows delegated child step',
|
|
131
|
-
promptSnippet: 'Complete the delegated workflow step',
|
|
132
|
-
promptGuidelines: [
|
|
133
|
-
'Call workflow_complete_step alone after all delegated work is complete.',
|
|
134
|
-
],
|
|
135
|
-
parameters: WORKFLOW_COMPLETION_PARAMETERS,
|
|
136
|
-
executionMode: 'sequential',
|
|
137
|
-
execute: async (_toolCallId, params) => {
|
|
138
|
-
if (!activePolicy) {
|
|
139
|
-
throw new Error('No delegated workflow policy is active');
|
|
140
|
-
}
|
|
141
|
-
if (policyError) throw new Error(policyError);
|
|
142
|
-
const result = parseDelegatedStepResult(
|
|
143
|
-
{
|
|
144
|
-
version: 1,
|
|
145
|
-
policyDigest: activePolicy.policyDigest,
|
|
146
|
-
outcome: params.outcome,
|
|
147
|
-
summary: params.summary,
|
|
148
|
-
...(params.artifact !== undefined
|
|
149
|
-
? { artifact: params.artifact }
|
|
150
|
-
: {}),
|
|
151
|
-
},
|
|
152
|
-
activePolicy,
|
|
153
|
-
);
|
|
154
|
-
writeResult(activePolicy, result);
|
|
155
|
-
return {
|
|
156
|
-
content: [
|
|
157
|
-
{
|
|
158
|
-
type: 'text' as const,
|
|
159
|
-
text: `Captured workflow step outcome "${result.outcome}".`,
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
details: {
|
|
163
|
-
workflowId: activePolicy.workflowId,
|
|
164
|
-
runId: activePolicy.runId,
|
|
165
|
-
stepId: activePolicy.stepId,
|
|
166
|
-
outcome: result.outcome,
|
|
167
|
-
},
|
|
168
|
-
terminate: true,
|
|
169
|
-
};
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
|
|
174
264
|
pi.on('input', (event) => {
|
|
175
265
|
let extracted;
|
|
176
266
|
try {
|
|
@@ -198,18 +288,19 @@ export function registerSubagentChildRuntime(
|
|
|
198
288
|
try {
|
|
199
289
|
verifyCapability(extracted.policy, childAgent);
|
|
200
290
|
const profileTools = new Set(pi.getActiveTools());
|
|
291
|
+
if (!profileTools.has(CHILD_COMPLETION_TOOL)) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
'pi-subagents structured_output completion is unavailable',
|
|
294
|
+
);
|
|
295
|
+
}
|
|
201
296
|
activePolicy = extracted.policy;
|
|
202
297
|
policyError = undefined;
|
|
203
|
-
registerCompletionTool();
|
|
204
298
|
effectiveTools = new Set(
|
|
205
299
|
resolveActiveTools(
|
|
206
300
|
pi.getAllTools(),
|
|
207
301
|
policyStep(activePolicy),
|
|
208
302
|
CHILD_COMPLETION_TOOL,
|
|
209
|
-
).filter(
|
|
210
|
-
(toolName) =>
|
|
211
|
-
toolName === CHILD_COMPLETION_TOOL || profileTools.has(toolName),
|
|
212
|
-
),
|
|
303
|
+
).filter((toolName) => !CHILD_COORDINATION_TOOLS.has(toolName)),
|
|
213
304
|
);
|
|
214
305
|
} catch (error) {
|
|
215
306
|
policyError = error instanceof Error ? error.message : String(error);
|
|
@@ -278,16 +369,25 @@ export function registerSubagentChildRuntime(
|
|
|
278
369
|
reason: policyError,
|
|
279
370
|
};
|
|
280
371
|
}
|
|
281
|
-
|
|
282
|
-
|
|
372
|
+
try {
|
|
373
|
+
const result = structuredResult(event.input, activePolicy);
|
|
374
|
+
writeResult(activePolicy, result);
|
|
375
|
+
freezeToolInput(event.input);
|
|
376
|
+
return;
|
|
377
|
+
} catch (error) {
|
|
378
|
+
return {
|
|
379
|
+
block: true,
|
|
380
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
283
383
|
}
|
|
284
|
-
if (
|
|
384
|
+
if (CHILD_COORDINATION_TOOLS.has(event.toolName)) {
|
|
285
385
|
return {
|
|
286
386
|
block: true,
|
|
287
|
-
reason:
|
|
387
|
+
reason:
|
|
388
|
+
'workflow children are non-interactive; use structured_output with a pause outcome and describe the unresolved contract in summary',
|
|
288
389
|
};
|
|
289
390
|
}
|
|
290
|
-
|
|
291
391
|
const authorization = authorizeToolCall(
|
|
292
392
|
event.toolName,
|
|
293
393
|
event.input as unknown as Record<string, unknown>,
|
|
@@ -301,6 +401,24 @@ export function registerSubagentChildRuntime(
|
|
|
301
401
|
reason: authorization.reason ?? 'Tool blocked by workflow child policy',
|
|
302
402
|
};
|
|
303
403
|
}
|
|
404
|
+
if (!effectiveTools.has(event.toolName)) {
|
|
405
|
+
return {
|
|
406
|
+
block: true,
|
|
407
|
+
reason: `tool "${event.toolName}" is allowed by the workflow but unavailable in this child runtime`,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const mutationError = authorizeRepositoryMutation(
|
|
412
|
+
event.toolName,
|
|
413
|
+
event.input as unknown as Record<string, unknown>,
|
|
414
|
+
activePolicy,
|
|
415
|
+
);
|
|
416
|
+
if (mutationError) {
|
|
417
|
+
return {
|
|
418
|
+
block: true,
|
|
419
|
+
reason: mutationError,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
304
422
|
freezeToolInput(event.input);
|
|
305
423
|
});
|
|
306
424
|
}
|
|
@@ -41,6 +41,7 @@ const DELEGATION_STATUSES = new Set<SubagentDelegationStatus>([
|
|
|
41
41
|
'interrupted',
|
|
42
42
|
'turn_budget_exhausted',
|
|
43
43
|
'tool_budget_exhausted',
|
|
44
|
+
'structured_output_failed',
|
|
44
45
|
'acceptance_failed',
|
|
45
46
|
'invalid_request',
|
|
46
47
|
'unavailable_context',
|
|
@@ -105,9 +106,9 @@ export class SubagentDelegationClient {
|
|
|
105
106
|
return Promise.reject(new Error('subagent delegation was cancelled'));
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
let start
|
|
109
|
-
let requestCancellation
|
|
110
|
-
let resolveTerminal
|
|
109
|
+
let start!: () => void;
|
|
110
|
+
let requestCancellation!: () => void;
|
|
111
|
+
let resolveTerminal!: () => void;
|
|
111
112
|
const terminal = new Promise<void>((resolve) => {
|
|
112
113
|
resolveTerminal = resolve;
|
|
113
114
|
});
|