@wichayutdew/pi-workflows 0.2.3 → 1.0.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/README.md +66 -38
- package/dist/index.js +5903 -4782
- package/package.json +1 -1
- package/src/command-names.ts +7 -1
- package/src/commands.ts +126 -52
- package/src/config/catalog.ts +162 -0
- package/src/config/ceiling.ts +116 -116
- package/src/config/command-conflicts.ts +4 -4
- package/src/config/diagnostics.ts +28 -0
- package/src/config/load-settings.ts +37 -0
- package/src/config/load-types.ts +53 -0
- package/src/config/load-workflows.ts +135 -0
- package/src/config/load.ts +43 -311
- package/src/config/types.ts +137 -135
- package/src/config/validate.ts +12 -1261
- package/src/config/validation/permissions.ts +291 -0
- package/src/config/validation/prompt.ts +19 -0
- package/src/config/validation/settings.ts +122 -0
- package/src/config/validation/shared.ts +129 -0
- package/src/config/validation/shortcut.ts +118 -0
- package/src/config/validation/step.ts +228 -0
- package/src/config/validation/subagent.ts +288 -0
- package/src/config/validation/workflow.ts +156 -0
- package/src/config/yaml.ts +29 -0
- package/src/digest.ts +38 -6
- package/src/engine/checkpoint.ts +13 -9
- package/src/engine/create-run.ts +42 -0
- package/src/engine/gate-transitions.ts +184 -0
- package/src/engine/reconciliation-history.ts +88 -0
- package/src/engine/resume.ts +19 -7
- package/src/engine/run-advance.ts +122 -0
- package/src/engine/run-lifecycle.ts +124 -0
- package/src/engine/run-reconciliation.ts +116 -0
- package/src/engine/run-validation.ts +128 -0
- package/src/engine/state-types.ts +64 -0
- package/src/engine/state.ts +10 -193
- package/src/engine/transition-helpers.ts +28 -0
- package/src/engine/transition-types.ts +8 -0
- package/src/engine/transitions.ts +17 -471
- package/src/harness/action-context.ts +181 -0
- package/src/harness/catalog.ts +45 -0
- package/src/harness/context-idle.ts +21 -0
- package/src/harness/core-actions.ts +299 -0
- package/src/harness/delegation-control-actions.ts +250 -0
- package/src/harness/delegation-failure.ts +215 -0
- package/src/harness/delegation-plan.ts +219 -0
- package/src/harness/delegation-recovery-validation.ts +159 -0
- package/src/harness/delegation-response-actions.ts +295 -0
- package/src/harness/delegation-retry-policy.ts +118 -0
- package/src/harness/dependencies.ts +165 -0
- package/src/harness/gate-submission-action.ts +154 -0
- package/src/harness/lifecycle-actions.ts +154 -0
- package/src/harness/pause-actions.ts +120 -0
- package/src/harness/plannotator-result-actions.ts +123 -0
- package/src/harness/prompt-gate-actions.ts +270 -0
- package/src/harness/resume-action.ts +261 -0
- package/src/harness/start-actions.ts +183 -0
- package/src/harness/status-actions.ts +191 -0
- package/src/harness/step-execution-actions.ts +283 -0
- package/src/harness/types.ts +76 -0
- package/src/harness.ts +237 -2201
- package/src/index.ts +73 -11
- package/src/integrations/plannotator-requests.ts +127 -0
- package/src/integrations/plannotator-responses.ts +141 -0
- package/src/integrations/plannotator-types.ts +44 -0
- package/src/integrations/plannotator.ts +16 -235
- package/src/integrations/prompt-gate.ts +17 -10
- package/src/integrations/subagents/child-policy-envelope.ts +119 -0
- package/src/integrations/subagents/child-policy-paths.ts +66 -0
- package/src/integrations/subagents/child-policy-sections.ts +174 -0
- package/src/integrations/subagents/child-policy-types.ts +32 -0
- package/src/integrations/subagents/child-policy-validation.ts +163 -0
- package/src/integrations/subagents/child-runtime-completion.ts +56 -0
- package/src/integrations/subagents/child-runtime-dependencies.ts +47 -0
- package/src/integrations/subagents/child-runtime-files.ts +156 -0
- package/src/integrations/subagents/child-runtime-policy.ts +83 -0
- package/src/integrations/subagents/child-runtime-types.ts +30 -0
- package/src/integrations/subagents/child-runtime.ts +140 -301
- package/src/integrations/subagents/client-delegation.ts +181 -0
- package/src/integrations/subagents/client-messages.ts +66 -0
- package/src/integrations/subagents/client-types.ts +36 -0
- package/src/integrations/subagents/client.ts +127 -219
- package/src/integrations/subagents/delegated-result.ts +31 -0
- package/src/integrations/subagents/diagnostic-format.ts +40 -0
- package/src/integrations/subagents/diagnostic-text.ts +114 -0
- package/src/integrations/subagents/diagnostic-types.ts +77 -0
- package/src/integrations/subagents/diagnostics.ts +24 -799
- package/src/integrations/subagents/failure-correlation.ts +235 -0
- package/src/integrations/subagents/failure-transcript.ts +192 -0
- package/src/integrations/subagents/hidden-bash-failure.ts +98 -0
- package/src/integrations/subagents/protocol-events.ts +27 -0
- package/src/integrations/subagents/protocol.ts +30 -375
- package/src/integrations/subagents/replay-audit.ts +147 -0
- package/src/integrations/subagents/replay-safety.ts +82 -0
- package/src/integrations/subagents/session-diagnostics.ts +258 -0
- package/src/policy/approved-command-extraction.ts +71 -0
- package/src/policy/approved-commands.ts +15 -432
- package/src/policy/bash-authorization.ts +86 -0
- package/src/policy/bash-read-only.ts +161 -0
- package/src/policy/bash-types.ts +20 -0
- package/src/policy/bash.ts +4 -346
- package/src/policy/completion-batch.ts +21 -16
- package/src/policy/immutable-input.ts +5 -2
- package/src/policy/mcp-authorization.ts +71 -0
- package/src/policy/restricted-command.ts +141 -0
- package/src/policy/restricted-git.ts +30 -0
- package/src/policy/reviewed-artifact.ts +97 -0
- package/src/policy/reviewed-command-safety.ts +150 -0
- package/src/policy/reviewed-command-shape.ts +49 -0
- package/src/policy/reviewed-repository-contract.ts +138 -0
- package/src/policy/reviewed-repository-cwd.ts +102 -0
- package/src/policy/tool-call-authorization.ts +58 -0
- package/src/policy/tool-selection.ts +72 -0
- package/src/policy/tool-types.ts +14 -0
- package/src/policy/tools.ts +11 -150
- package/src/preflight.ts +96 -61
- package/src/prompt/main-workflow-notice.ts +41 -0
- package/src/prompt/retry-task.ts +83 -0
- package/src/prompt/step-contract.ts +101 -0
- package/src/prompt/step-sections.ts +75 -0
- package/src/prompt/step-task.ts +156 -0
- package/src/prompt/template.ts +81 -0
- package/src/prompt.ts +10 -245
- package/src/runtime/completion-tool.ts +3 -0
- package/src/runtime/main-step-completion.ts +78 -0
- package/src/runtime/main-step-lifecycle.ts +37 -0
- package/src/runtime/main-step-policy.ts +84 -0
- package/src/runtime/main-step-runtime-types.ts +64 -0
- package/src/runtime/main-step-runtime.ts +129 -200
- package/src/runtime/main-step-state.ts +85 -0
- package/src/runtime/serial-task-queue.ts +48 -13
- package/src/runtime/step-result.ts +48 -27
- package/src/workflow-list.ts +15 -6
- package/src/workflow-status/format-status.ts +52 -0
- package/src/workflow-status/formatting.ts +178 -0
- package/src/workflow-status/layout.ts +109 -0
- package/src/workflow-status/render-board.ts +101 -0
- package/src/workflow-status/render-path.ts +123 -0
- package/src/workflow-status/render-summary.ts +144 -0
- package/src/workflow-status/types.ts +40 -0
- package/src/workflow-status/view.ts +273 -0
- package/src/workflow-status.ts +20 -820
|
@@ -1,318 +1,149 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
realpathSync,
|
|
7
|
-
renameSync,
|
|
8
|
-
statSync,
|
|
9
|
-
unlinkSync,
|
|
10
|
-
writeFileSync,
|
|
11
|
-
} from 'node:fs';
|
|
12
|
-
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
13
|
-
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
14
|
-
import type { WorkflowStep } from '../../config/types.ts';
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
InputEvent,
|
|
4
|
+
InputEventResult,
|
|
5
|
+
} from '@earendil-works/pi-coding-agent';
|
|
15
6
|
import { invalidCompletionCallIds } from '../../policy/completion-batch.ts';
|
|
16
7
|
import { freezeToolInput } from '../../policy/immutable-input.ts';
|
|
17
8
|
import { authorizeToolCall, resolveActiveTools } from '../../policy/tools.ts';
|
|
18
9
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from './
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
prompt: { inline: 'Delegated workflow step' },
|
|
38
|
-
subagent: {
|
|
39
|
-
agent: policy.agent,
|
|
40
|
-
context: 'fresh',
|
|
41
|
-
timeoutMs: 900_000,
|
|
42
|
-
artifacts: false,
|
|
43
|
-
retryToolFailures: false,
|
|
44
|
-
},
|
|
45
|
-
permissions: policy.permissions,
|
|
46
|
-
requires: { tools: [], extensions: [], skills: [] },
|
|
47
|
-
transitions: {},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function childSystemPrompt(policy: ChildStepPolicy): string {
|
|
52
|
-
const hasPauseOutcome = policy.pauseOutcomes.length > 0;
|
|
53
|
-
return [
|
|
54
|
-
'# Pi Workflows delegated step',
|
|
55
|
-
'',
|
|
56
|
-
`Workflow: ${policy.workflowId}`,
|
|
57
|
-
`Run: ${policy.runId}`,
|
|
58
|
-
`Step: ${policy.stepId} (${policy.stepTitle})`,
|
|
59
|
-
'',
|
|
60
|
-
'The parent workflow harness owns orchestration and state transitions.',
|
|
61
|
-
'Perform only this delegated step. Its child-side tool policy is enforced.',
|
|
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.',
|
|
64
|
-
`Valid outcomes: ${policy.outcomes.join(', ')}`,
|
|
65
|
-
`Pause outcomes: ${policy.pauseOutcomes.join(', ') || '(none)'}`,
|
|
66
|
-
`Summary limit: ${policy.summaryMaxChars} characters`,
|
|
67
|
-
...(policy.gateSubmitOutcome
|
|
68
|
-
? [
|
|
69
|
-
`Outcome "${policy.gateSubmitOutcome}" requires the complete gate artifact.`,
|
|
70
|
-
]
|
|
71
|
-
: []),
|
|
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
|
-
: []),
|
|
106
|
-
].join('\n');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function writeResult(policy: ChildStepPolicy, result: unknown): void {
|
|
110
|
-
if (existsSync(policy.resultPath)) {
|
|
111
|
-
throw new Error('Delegated workflow step already produced a result');
|
|
112
|
-
}
|
|
113
|
-
const temporaryPath = `${policy.resultPath}.${randomUUID()}.tmp`;
|
|
114
|
-
try {
|
|
115
|
-
writeFileSync(temporaryPath, JSON.stringify(result), {
|
|
116
|
-
encoding: 'utf8',
|
|
117
|
-
flag: 'wx',
|
|
118
|
-
mode: 0o600,
|
|
119
|
-
});
|
|
120
|
-
renameSync(temporaryPath, policy.resultPath);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
try {
|
|
123
|
-
unlinkSync(temporaryPath);
|
|
124
|
-
} catch {
|
|
125
|
-
// The temporary file may not have been created.
|
|
126
|
-
}
|
|
127
|
-
throw error;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
10
|
+
CHILD_COMPLETION_TOOL,
|
|
11
|
+
CHILD_COORDINATION_TOOLS,
|
|
12
|
+
parseChildStructuredResult,
|
|
13
|
+
} from './child-runtime-completion.ts';
|
|
14
|
+
import { DEFAULT_CHILD_RUNTIME_DEPENDENCIES } from './child-runtime-dependencies.ts';
|
|
15
|
+
import {
|
|
16
|
+
repositoryMutationError,
|
|
17
|
+
verifyChildCapability,
|
|
18
|
+
writeChildResult,
|
|
19
|
+
} from './child-runtime-files.ts';
|
|
20
|
+
import { childPolicyStep, childSystemPrompt } from './child-runtime-policy.ts';
|
|
21
|
+
import type {
|
|
22
|
+
SubagentChildRuntimeDependencies,
|
|
23
|
+
SubagentChildRuntimeOptions,
|
|
24
|
+
} from './child-runtime-types.ts';
|
|
25
|
+
import { extractChildPolicy } from './child-policy-envelope.ts';
|
|
26
|
+
import type { ChildStepPolicy } from './child-policy-types.ts';
|
|
27
|
+
import { isSubagentRuntimeName } from './child-policy-validation.ts';
|
|
130
28
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
}
|
|
29
|
+
export { CHILD_COMPLETION_TOOL } from './child-runtime-completion.ts';
|
|
30
|
+
export type {
|
|
31
|
+
ChildRuntimeFileSystem,
|
|
32
|
+
ChildRuntimePathInspection,
|
|
33
|
+
SubagentChildRuntimeDependencies,
|
|
34
|
+
SubagentChildRuntimeOptions,
|
|
35
|
+
} from './child-runtime-types.ts';
|
|
167
36
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
let actual: Buffer;
|
|
176
|
-
try {
|
|
177
|
-
actual = Buffer.from(readFileSync(policy.capabilityPath, 'utf8'), 'utf8');
|
|
178
|
-
} catch {
|
|
179
|
-
throw new Error('delegated workflow capability is missing');
|
|
180
|
-
}
|
|
181
|
-
const expected = Buffer.from(policy.capabilityToken, 'utf8');
|
|
182
|
-
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
|
|
183
|
-
throw new Error('delegated workflow capability is invalid');
|
|
184
|
-
}
|
|
185
|
-
unlinkSync(policy.capabilityPath);
|
|
186
|
-
}
|
|
37
|
+
type ChildRuntimeState = {
|
|
38
|
+
readonly activePolicy: ChildStepPolicy | undefined;
|
|
39
|
+
readonly policyError: string | undefined;
|
|
40
|
+
readonly invalidCompletionCalls: ReadonlySet<string>;
|
|
41
|
+
readonly effectiveTools: ReadonlySet<string>;
|
|
42
|
+
};
|
|
187
43
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
}
|
|
44
|
+
const INITIAL_STATE: ChildRuntimeState = {
|
|
45
|
+
activePolicy: undefined,
|
|
46
|
+
policyError: undefined,
|
|
47
|
+
invalidCompletionCalls: new Set(),
|
|
48
|
+
effectiveTools: new Set(),
|
|
49
|
+
};
|
|
218
50
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
fromRoot !== '..' &&
|
|
223
|
-
!fromRoot.startsWith(`..${sep}`) &&
|
|
224
|
-
!isAbsolute(fromRoot)
|
|
225
|
-
);
|
|
226
|
-
}
|
|
51
|
+
const errorMessage = (error: unknown): string =>
|
|
52
|
+
error instanceof Error ? error.message : String(error);
|
|
227
53
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
try {
|
|
242
|
-
return realpathSync(candidate);
|
|
243
|
-
} catch {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
54
|
+
const invalidPolicyInput = (
|
|
55
|
+
pi: ExtensionAPI,
|
|
56
|
+
policyError: string,
|
|
57
|
+
images: InputEvent['images'],
|
|
58
|
+
): InputEventResult => {
|
|
59
|
+
pi.setActiveTools([]);
|
|
60
|
+
return {
|
|
61
|
+
action: 'transform' as const,
|
|
62
|
+
text: `Delegated workflow policy is invalid: ${policyError}`,
|
|
63
|
+
...(images ? { images } : {}),
|
|
64
|
+
};
|
|
65
|
+
};
|
|
248
66
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
67
|
+
const resolveChildAgent = (
|
|
68
|
+
options: SubagentChildRuntimeOptions,
|
|
69
|
+
dependencies: SubagentChildRuntimeDependencies,
|
|
70
|
+
): string | undefined =>
|
|
71
|
+
options.childAgent ?? dependencies.environmentChildAgent();
|
|
252
72
|
|
|
253
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Registers the policy-enforcing runtime used inside a delegated subagent.
|
|
75
|
+
*
|
|
76
|
+
* All file-system, identity, process, and capability-comparison boundaries can
|
|
77
|
+
* be supplied through `options.dependencies`.
|
|
78
|
+
*/
|
|
79
|
+
export const registerSubagentChildRuntime = (
|
|
254
80
|
pi: ExtensionAPI,
|
|
255
81
|
options: SubagentChildRuntimeOptions = {},
|
|
256
|
-
): void {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
let
|
|
261
|
-
const childAgent =
|
|
262
|
-
options.childAgent ?? process.env.PI_SUBAGENT_CHILD_AGENT?.trim();
|
|
82
|
+
): void => {
|
|
83
|
+
const dependencies =
|
|
84
|
+
options.dependencies ?? DEFAULT_CHILD_RUNTIME_DEPENDENCIES;
|
|
85
|
+
const childAgent = resolveChildAgent(options, dependencies);
|
|
86
|
+
let state = INITIAL_STATE;
|
|
263
87
|
|
|
264
88
|
pi.on('input', (event) => {
|
|
265
89
|
let extracted;
|
|
266
90
|
try {
|
|
267
|
-
extracted = extractChildPolicy(event.text
|
|
91
|
+
extracted = extractChildPolicy(event.text, {
|
|
92
|
+
temporaryDirectory: dependencies.temporaryDirectory,
|
|
93
|
+
});
|
|
268
94
|
} catch (error) {
|
|
269
|
-
policyError =
|
|
270
|
-
|
|
271
|
-
return
|
|
272
|
-
action: 'transform' as const,
|
|
273
|
-
text: `Delegated workflow policy is invalid: ${policyError}`,
|
|
274
|
-
...(event.images ? { images: event.images } : {}),
|
|
275
|
-
};
|
|
95
|
+
const policyError = errorMessage(error);
|
|
96
|
+
state = { ...state, policyError };
|
|
97
|
+
return invalidPolicyInput(pi, policyError, event.images);
|
|
276
98
|
}
|
|
277
99
|
if (!extracted) return;
|
|
278
|
-
if (activePolicy) {
|
|
279
|
-
policyError = 'child received more than one workflow policy';
|
|
280
|
-
|
|
281
|
-
return
|
|
282
|
-
action: 'transform' as const,
|
|
283
|
-
text: `Delegated workflow policy is invalid: ${policyError}`,
|
|
284
|
-
...(event.images ? { images: event.images } : {}),
|
|
285
|
-
};
|
|
100
|
+
if (state.activePolicy) {
|
|
101
|
+
const policyError = 'child received more than one workflow policy';
|
|
102
|
+
state = { ...state, policyError };
|
|
103
|
+
return invalidPolicyInput(pi, policyError, event.images);
|
|
286
104
|
}
|
|
287
105
|
|
|
288
106
|
try {
|
|
289
|
-
|
|
107
|
+
if (!isSubagentRuntimeName(childAgent)) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
'child agent does not match the delegated workflow policy',
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
verifyChildCapability({
|
|
113
|
+
policy: extracted.policy,
|
|
114
|
+
childAgent,
|
|
115
|
+
dependencies,
|
|
116
|
+
});
|
|
290
117
|
const profileTools = new Set(pi.getActiveTools());
|
|
291
118
|
if (!profileTools.has(CHILD_COMPLETION_TOOL)) {
|
|
292
119
|
throw new Error(
|
|
293
120
|
'pi-subagents structured_output completion is unavailable',
|
|
294
121
|
);
|
|
295
122
|
}
|
|
296
|
-
|
|
297
|
-
policyError = undefined;
|
|
298
|
-
effectiveTools = new Set(
|
|
123
|
+
const effectiveTools = new Set(
|
|
299
124
|
resolveActiveTools(
|
|
300
125
|
pi.getAllTools(),
|
|
301
|
-
|
|
126
|
+
childPolicyStep(extracted.policy),
|
|
302
127
|
CHILD_COMPLETION_TOOL,
|
|
303
128
|
).filter((toolName) => !CHILD_COORDINATION_TOOLS.has(toolName)),
|
|
304
129
|
);
|
|
130
|
+
state = {
|
|
131
|
+
...state,
|
|
132
|
+
activePolicy: extracted.policy,
|
|
133
|
+
policyError: undefined,
|
|
134
|
+
effectiveTools,
|
|
135
|
+
};
|
|
305
136
|
} catch (error) {
|
|
306
|
-
policyError =
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
text: `Delegated workflow policy is invalid: ${policyError}`,
|
|
312
|
-
...(event.images ? { images: event.images } : {}),
|
|
137
|
+
const policyError = errorMessage(error);
|
|
138
|
+
state = {
|
|
139
|
+
...state,
|
|
140
|
+
policyError,
|
|
141
|
+
effectiveTools: new Set(),
|
|
313
142
|
};
|
|
143
|
+
return invalidPolicyInput(pi, policyError, event.images);
|
|
314
144
|
}
|
|
315
|
-
|
|
145
|
+
|
|
146
|
+
pi.setActiveTools([...state.effectiveTools]);
|
|
316
147
|
return {
|
|
317
148
|
action: 'transform' as const,
|
|
318
149
|
text: extracted.task,
|
|
@@ -321,34 +152,32 @@ export function registerSubagentChildRuntime(
|
|
|
321
152
|
});
|
|
322
153
|
|
|
323
154
|
pi.on('before_agent_start', (event) => {
|
|
324
|
-
if (!activePolicy) {
|
|
325
|
-
if (policyError) pi.setActiveTools([]);
|
|
155
|
+
if (!state.activePolicy) {
|
|
156
|
+
if (state.policyError) pi.setActiveTools([]);
|
|
326
157
|
return;
|
|
327
158
|
}
|
|
328
159
|
return {
|
|
329
|
-
systemPrompt: `${event.systemPrompt}\n\n${childSystemPrompt(activePolicy)}`,
|
|
160
|
+
systemPrompt: `${event.systemPrompt}\n\n${childSystemPrompt(state.activePolicy)}`,
|
|
330
161
|
};
|
|
331
162
|
});
|
|
332
163
|
|
|
333
164
|
pi.on('turn_start', () => {
|
|
334
|
-
invalidCompletionCalls
|
|
165
|
+
state = { ...state, invalidCompletionCalls: new Set() };
|
|
335
166
|
});
|
|
336
167
|
|
|
337
168
|
pi.on('message_end', (event) => {
|
|
338
|
-
if (!activePolicy) return;
|
|
169
|
+
if (!state.activePolicy) return;
|
|
339
170
|
const invalid = invalidCompletionCallIds(
|
|
340
171
|
event.message,
|
|
341
172
|
CHILD_COMPLETION_TOOL,
|
|
342
173
|
);
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
(event.message as { role?: unknown }).role === 'assistant'
|
|
346
|
-
) {
|
|
347
|
-
invalidCompletionCalls = invalid;
|
|
174
|
+
if (invalid.size > 0 || event.message.role === 'assistant') {
|
|
175
|
+
state = { ...state, invalidCompletionCalls: invalid };
|
|
348
176
|
}
|
|
349
177
|
});
|
|
350
178
|
|
|
351
179
|
pi.on('tool_call', (event) => {
|
|
180
|
+
const { activePolicy, policyError } = state;
|
|
352
181
|
if (!activePolicy) {
|
|
353
182
|
if (!policyError) return;
|
|
354
183
|
return {
|
|
@@ -356,7 +185,7 @@ export function registerSubagentChildRuntime(
|
|
|
356
185
|
reason: policyError,
|
|
357
186
|
};
|
|
358
187
|
}
|
|
359
|
-
if (invalidCompletionCalls.has(event.toolCallId)) {
|
|
188
|
+
if (state.invalidCompletionCalls.has(event.toolCallId)) {
|
|
360
189
|
return {
|
|
361
190
|
block: true,
|
|
362
191
|
reason: `${CHILD_COMPLETION_TOOL} must be the only tool call in its message`,
|
|
@@ -370,14 +199,21 @@ export function registerSubagentChildRuntime(
|
|
|
370
199
|
};
|
|
371
200
|
}
|
|
372
201
|
try {
|
|
373
|
-
const result =
|
|
374
|
-
|
|
202
|
+
const result = parseChildStructuredResult({
|
|
203
|
+
input: event.input,
|
|
204
|
+
policy: activePolicy,
|
|
205
|
+
});
|
|
206
|
+
writeChildResult({
|
|
207
|
+
policy: activePolicy,
|
|
208
|
+
result,
|
|
209
|
+
dependencies,
|
|
210
|
+
});
|
|
375
211
|
freezeToolInput(event.input);
|
|
376
212
|
return;
|
|
377
213
|
} catch (error) {
|
|
378
214
|
return {
|
|
379
215
|
block: true,
|
|
380
|
-
reason:
|
|
216
|
+
reason: errorMessage(error),
|
|
381
217
|
};
|
|
382
218
|
}
|
|
383
219
|
}
|
|
@@ -388,10 +224,12 @@ export function registerSubagentChildRuntime(
|
|
|
388
224
|
'workflow children are non-interactive; use structured_output with a pause outcome and describe the unresolved contract in summary',
|
|
389
225
|
};
|
|
390
226
|
}
|
|
227
|
+
|
|
228
|
+
const input: Record<string, unknown> = { ...event.input };
|
|
391
229
|
const authorization = authorizeToolCall(
|
|
392
230
|
event.toolName,
|
|
393
|
-
|
|
394
|
-
|
|
231
|
+
input,
|
|
232
|
+
childPolicyStep(activePolicy),
|
|
395
233
|
pi.getAllTools(),
|
|
396
234
|
activePolicy.approvedBashCommands ?? [],
|
|
397
235
|
);
|
|
@@ -401,18 +239,19 @@ export function registerSubagentChildRuntime(
|
|
|
401
239
|
reason: authorization.reason ?? 'Tool blocked by workflow child policy',
|
|
402
240
|
};
|
|
403
241
|
}
|
|
404
|
-
if (!effectiveTools.has(event.toolName)) {
|
|
242
|
+
if (!state.effectiveTools.has(event.toolName)) {
|
|
405
243
|
return {
|
|
406
244
|
block: true,
|
|
407
245
|
reason: `tool "${event.toolName}" is allowed by the workflow but unavailable in this child runtime`,
|
|
408
246
|
};
|
|
409
247
|
}
|
|
410
248
|
|
|
411
|
-
const mutationError =
|
|
412
|
-
event.toolName,
|
|
413
|
-
|
|
414
|
-
activePolicy,
|
|
415
|
-
|
|
249
|
+
const mutationError = repositoryMutationError({
|
|
250
|
+
toolName: event.toolName,
|
|
251
|
+
input,
|
|
252
|
+
policy: activePolicy,
|
|
253
|
+
dependencies,
|
|
254
|
+
});
|
|
416
255
|
if (mutationError) {
|
|
417
256
|
return {
|
|
418
257
|
block: true,
|
|
@@ -421,4 +260,4 @@ export function registerSubagentChildRuntime(
|
|
|
421
260
|
}
|
|
422
261
|
freezeToolInput(event.input);
|
|
423
262
|
});
|
|
424
|
-
}
|
|
263
|
+
};
|