@narumitw/pi-subagents 0.53.0 → 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 +85 -15
- package/package.json +1 -1
- package/src/agents/built-ins.ts +124 -0
- package/src/agents/catalog.ts +224 -0
- package/src/agents/discovery.ts +249 -0
- package/src/agents/types.ts +98 -0
- package/src/agents.ts +47 -670
- package/src/auto-transport.ts +2 -1
- package/src/automation.ts +7 -2
- package/src/capability-router.ts +1 -1
- package/src/completion-delivery.ts +82 -11
- package/src/config-status.ts +2 -2
- package/src/config-ui.ts +8 -8
- package/src/consult-resources.ts +1 -1
- package/src/consult.ts +9 -7
- package/src/create-stateful-transport.ts +2 -1
- package/src/cwd-policy.ts +1 -1
- package/src/execution/budget.ts +56 -0
- package/src/execution/runtime-policy.ts +19 -0
- package/src/execution-plan.ts +1 -1
- package/src/execution-profiles.ts +1 -1
- package/src/execution-ui.ts +1 -1
- package/src/execution.ts +269 -100
- package/src/in-process-transport.ts +3 -2
- package/src/inspect.ts +39 -8
- package/src/limits.ts +1 -0
- package/src/orchestration-metrics.ts +12 -5
- package/src/panel-execution.ts +1 -1
- package/src/panel-planning.ts +1 -1
- package/src/params.ts +3 -1
- package/src/persistence.ts +106 -7
- package/src/registry-types.ts +22 -5
- package/src/registry.ts +205 -37
- package/src/render.ts +1 -1
- package/src/retained-semantic-state.ts +1 -1
- package/src/rpc-transport-metadata.ts +1 -1
- package/src/rpc-transport.ts +2 -1
- package/src/runner.ts +6 -1
- package/src/settings/inspection.ts +275 -0
- package/src/settings/schema.ts +186 -0
- package/src/settings.ts +72 -420
- package/src/spawn-idempotency.ts +1 -1
- package/src/stateful-agent-view.ts +87 -0
- package/src/stateful-config.ts +1 -1
- package/src/stateful-guidance.ts +2 -2
- package/src/stateful-limits.ts +1 -1
- package/src/stateful-prompt.ts +2 -2
- package/src/stateful-render.ts +0 -1
- package/src/stateful-safety.ts +2 -1
- package/src/stateful-tool-params.ts +13 -20
- package/src/stateful.ts +36 -117
- package/src/subagents.ts +8 -9
- package/src/subprocess-transport.ts +2 -6
- package/src/transport-types.ts +1 -1
- package/src/transport-ui.ts +1 -1
- package/src/verification-harness.ts +516 -0
- package/src/verification-receipt.ts +275 -0
- package/src/verified-execution-benchmark.ts +86 -0
- package/src/verified-execution-contract.ts +219 -0
- package/src/work-item-ledger.ts +510 -37
- package/src/work-item-persistence.ts +31 -0
- package/src/workflow-completion-controller.ts +397 -0
- package/src/workflow-plan-compiler.ts +1 -1
- package/src/workflow-plan-patch.ts +1 -1
- package/src/workflow-planning.ts +11 -1
- package/src/workflow-ui.ts +1 -1
package/src/execution.ts
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
* Blocking execution stays in one module so preflight, confirmation, cancellation generation,
|
|
3
3
|
* launch, and settlement retain one ordered lifecycle owner across every mode.
|
|
4
4
|
*/
|
|
5
|
+
|
|
5
6
|
import type { AgentToolResult, AgentToolUpdateCallback } from "@earendil-works/pi-agent-core";
|
|
6
7
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
8
|
import { AdaptiveScheduler } from "./adaptive-scheduler.js";
|
|
8
9
|
import { evaluateDelegationAdmission } from "./admission-policy.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "./agents.js";
|
|
10
|
+
import { discoverAgents } from "./agents/discovery.js";
|
|
11
|
+
import type {
|
|
12
|
+
AgentConfig,
|
|
13
|
+
AgentScope,
|
|
14
|
+
SubagentSettings,
|
|
15
|
+
SubagentThinkingLevel,
|
|
16
|
+
} from "./agents/types.js";
|
|
16
17
|
import {
|
|
17
18
|
chainStatus,
|
|
18
19
|
fanInStatus,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
singleStatus,
|
|
21
22
|
startSubagentStatus,
|
|
22
23
|
} from "./blocking-status.js";
|
|
23
|
-
import { issueCapabilityGrant } from "./capability-grant.js";
|
|
24
|
+
import { issueCapabilityGrant, revokeCapabilityGrant } from "./capability-grant.js";
|
|
24
25
|
import { redactPrivateText } from "./context.js";
|
|
25
26
|
import {
|
|
26
27
|
assertDelegationTargetAllowed,
|
|
@@ -33,6 +34,15 @@ import {
|
|
|
33
34
|
type DelegationContract,
|
|
34
35
|
normalizeDelegationContract,
|
|
35
36
|
} from "./delegation-contract.js";
|
|
37
|
+
import {
|
|
38
|
+
calculateExecutionBudget,
|
|
39
|
+
mergeTurnLimits,
|
|
40
|
+
resolveConfiguredTimeout,
|
|
41
|
+
} from "./execution/budget.js";
|
|
42
|
+
import {
|
|
43
|
+
assertSubagentDepthAllowed,
|
|
44
|
+
resolveDefaultSubagentTimeoutMs,
|
|
45
|
+
} from "./execution/runtime-policy.js";
|
|
36
46
|
import {
|
|
37
47
|
acknowledgeExecutionPlan,
|
|
38
48
|
createExecutionPlan,
|
|
@@ -63,10 +73,9 @@ import {
|
|
|
63
73
|
import { safeTerminalLine } from "./safe-text.js";
|
|
64
74
|
import {
|
|
65
75
|
DEFAULT_DELEGATION_CWD_POLICY,
|
|
66
|
-
readSubagentSettings,
|
|
67
76
|
resolveBlockingMaxParallelTasks,
|
|
68
|
-
|
|
69
|
-
} from "./settings.js";
|
|
77
|
+
} from "./settings/inspection.js";
|
|
78
|
+
import { readSubagentSettings, resolveSubagentThinkingLevel } from "./settings.js";
|
|
70
79
|
import { isRetryableResult, runHedgedAttempt, supervisionDelay } from "./supervision.js";
|
|
71
80
|
import { TimeoutProgressJournal, TURN_TERMINATION_VERSION } from "./timeout-checkpoint.js";
|
|
72
81
|
import type { TurnLimits } from "./turn-budget.js";
|
|
@@ -74,11 +83,16 @@ import {
|
|
|
74
83
|
requiresIndependentVerification,
|
|
75
84
|
validateWorkflowVerificationGraph,
|
|
76
85
|
} from "./verification-policy.js";
|
|
86
|
+
import { prepareVerifiedWorkflow } from "./verified-execution-contract.js";
|
|
77
87
|
import type { WorkItemLedger } from "./work-item-ledger.js";
|
|
78
88
|
import {
|
|
79
89
|
createSessionWorkItemPersistence,
|
|
80
90
|
type WorkItemPersistence,
|
|
81
91
|
} from "./work-item-persistence.js";
|
|
92
|
+
import {
|
|
93
|
+
WorkflowCompletionController,
|
|
94
|
+
workflowCompletionFailureReason,
|
|
95
|
+
} from "./workflow-completion-controller.js";
|
|
82
96
|
import { createBlockingWorkLedger, resolveWorkflowTasks } from "./workflow-planning.js";
|
|
83
97
|
import { captureWorkflowTreeIdentity, sameWorkflowTreeIdentity } from "./workflow-tree-identity.js";
|
|
84
98
|
import {
|
|
@@ -86,25 +100,12 @@ import {
|
|
|
86
100
|
workflowVerificationInstruction,
|
|
87
101
|
} from "./workflow-verification.js";
|
|
88
102
|
|
|
89
|
-
export
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function resolveDefaultSubagentTimeoutMs(): number {
|
|
98
|
-
return parsePositiveInteger(process.env.PI_SUBAGENT_TIMEOUT_MS) ?? FALLBACK_TIMEOUT_MS;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function assertSubagentDepthAllowed(): void {
|
|
102
|
-
const depth = Number.parseInt(process.env.PI_SUBAGENT_DEPTH ?? "0", 10) || 0;
|
|
103
|
-
const maxDepth = parsePositiveInteger(process.env.PI_SUBAGENT_MAX_DEPTH) ?? 1;
|
|
104
|
-
if (depth >= maxDepth) {
|
|
105
|
-
throw new Error(`Subagent recursion depth limit reached (${maxDepth})`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
103
|
+
export {
|
|
104
|
+
assertSubagentDepthAllowed,
|
|
105
|
+
FALLBACK_TIMEOUT_MS,
|
|
106
|
+
parsePositiveInteger,
|
|
107
|
+
resolveDefaultSubagentTimeoutMs,
|
|
108
|
+
} from "./execution/runtime-policy.js";
|
|
108
109
|
|
|
109
110
|
export async function executeSubagent(
|
|
110
111
|
toolCallId: string,
|
|
@@ -124,50 +125,54 @@ export async function executeSubagent(
|
|
|
124
125
|
const maxParallelTasks = resolveBlockingMaxParallelTasks(config);
|
|
125
126
|
const discovery = discoverAgents(ctx.cwd, agentScope, config);
|
|
126
127
|
const agents = discovery.agents;
|
|
127
|
-
|
|
128
|
+
let resolvedWorkflowTasks = resolveWorkflowTasks(params, agents);
|
|
129
|
+
const verifiedWorkflow = params.workflow?.verifiedExecution
|
|
130
|
+
? prepareVerifiedWorkflow(resolvedWorkflowTasks, params.workflow.verifiedExecution)
|
|
131
|
+
: undefined;
|
|
132
|
+
if (verifiedWorkflow) {
|
|
133
|
+
const verifierAgent = agents.find(
|
|
134
|
+
(agent) => agent.name === params.workflow?.verifiedExecution?.verifierAgent,
|
|
135
|
+
);
|
|
136
|
+
if (!verifierAgent) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Unknown verified execution agent: ${params.workflow?.verifiedExecution?.verifierAgent}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (
|
|
142
|
+
!verifierAgent.capabilityManifest?.verificationRoles.includes("independent-review") ||
|
|
143
|
+
!verifierAgent.capabilityManifest.resultFormats.includes("structured-v2")
|
|
144
|
+
) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Verified execution agent ${verifierAgent.name} lacks independent structured-v2 review capability`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
resolvedWorkflowTasks = verifiedWorkflow.tasks;
|
|
150
|
+
}
|
|
128
151
|
const confirmProjectAgents = params.confirmProjectAgents ?? true;
|
|
129
152
|
const resolveTimeoutMs = (agentName: string, localTimeoutMs?: number) =>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
resolveConfiguredTimeout(
|
|
154
|
+
agents,
|
|
155
|
+
agentName,
|
|
156
|
+
localTimeoutMs,
|
|
157
|
+
params.timeoutMs,
|
|
158
|
+
resolveDefaultSubagentTimeoutMs(),
|
|
159
|
+
);
|
|
134
160
|
const resolveThinkingLevel = (agentName: string, localThinkingLevel?: SubagentThinkingLevel) =>
|
|
135
161
|
resolveSubagentThinkingLevel(agents, agentName, params.thinkingLevel, localThinkingLevel);
|
|
136
162
|
let orchestrationDeadline: number | undefined;
|
|
137
|
-
const resolveTurnLimits = (local?: TurnLimits): TurnLimits =>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
| undefined => {
|
|
152
|
-
const requested = resolveTimeoutMs(agentName, localTimeoutMs);
|
|
153
|
-
if (orchestrationDeadline === undefined) {
|
|
154
|
-
return {
|
|
155
|
-
timeoutMs: requested,
|
|
156
|
-
workTimeoutReason: "work_timeout",
|
|
157
|
-
workTimeoutReportLimit: requested,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
const remaining = Math.floor(orchestrationDeadline - Date.now());
|
|
161
|
-
if (remaining < 1) return undefined;
|
|
162
|
-
const orchestrationLimited = remaining < requested;
|
|
163
|
-
return {
|
|
164
|
-
timeoutMs: Math.min(requested, remaining),
|
|
165
|
-
workTimeoutReason: orchestrationLimited ? "orchestration_timeout" : "work_timeout",
|
|
166
|
-
workTimeoutReportLimit: orchestrationLimited
|
|
167
|
-
? Math.floor(params.totalTimeoutMs as number)
|
|
168
|
-
: requested,
|
|
169
|
-
};
|
|
170
|
-
};
|
|
163
|
+
const resolveTurnLimits = (local?: TurnLimits): TurnLimits =>
|
|
164
|
+
mergeTurnLimits(local, {
|
|
165
|
+
idleTimeoutMs: params.idleTimeoutMs,
|
|
166
|
+
maxTurns: params.maxTurns,
|
|
167
|
+
maxToolCalls: params.maxToolCalls,
|
|
168
|
+
});
|
|
169
|
+
const resolveExecutionBudget = (agentName: string, localTimeoutMs?: number) =>
|
|
170
|
+
calculateExecutionBudget({
|
|
171
|
+
requestedTimeoutMs: resolveTimeoutMs(agentName, localTimeoutMs),
|
|
172
|
+
orchestrationDeadline,
|
|
173
|
+
totalTimeoutMs: params.totalTimeoutMs,
|
|
174
|
+
now: Date.now(),
|
|
175
|
+
});
|
|
171
176
|
|
|
172
177
|
const hasChain = (params.chain?.length ?? 0) > 0;
|
|
173
178
|
const hasTasks = (params.tasks?.length ?? 0) > 0;
|
|
@@ -386,7 +391,17 @@ export async function executeSubagent(
|
|
|
386
391
|
verificationTargetIds,
|
|
387
392
|
);
|
|
388
393
|
}
|
|
389
|
-
workLedger = createBlockingWorkLedger(
|
|
394
|
+
workLedger = createBlockingWorkLedger(
|
|
395
|
+
params,
|
|
396
|
+
resolvedWorkflowTasks,
|
|
397
|
+
aggregator,
|
|
398
|
+
verifiedWorkflow
|
|
399
|
+
? {
|
|
400
|
+
id: verifiedWorkflow.targetTaskId,
|
|
401
|
+
maxReworkCycles: verifiedWorkflow.maxReworkCycles,
|
|
402
|
+
}
|
|
403
|
+
: undefined,
|
|
404
|
+
);
|
|
390
405
|
let workflowPersistence: WorkItemPersistence | undefined;
|
|
391
406
|
if (hasWorkflow && workLedger) {
|
|
392
407
|
const owner =
|
|
@@ -698,13 +713,34 @@ export async function executeSubagent(
|
|
|
698
713
|
const deadline = orchestrationDeadline;
|
|
699
714
|
const cancelWorkflowGeneration = () => {
|
|
700
715
|
for (const item of workLedger.snapshot().items) {
|
|
701
|
-
if (
|
|
716
|
+
if (
|
|
717
|
+
item.state === "running" ||
|
|
718
|
+
item.state === "awaiting-verification" ||
|
|
719
|
+
(item.state === "completed" && item.acceptanceState === "pending")
|
|
720
|
+
) {
|
|
702
721
|
workLedger.invalidate(item.id, "parent-aborted");
|
|
703
722
|
}
|
|
704
723
|
}
|
|
705
724
|
};
|
|
706
|
-
|
|
725
|
+
let completionController: WorkflowCompletionController | undefined;
|
|
726
|
+
let abortListenerAttached = false;
|
|
707
727
|
try {
|
|
728
|
+
completionController = verifiedWorkflow
|
|
729
|
+
? new WorkflowCompletionController({
|
|
730
|
+
ledger: workLedger,
|
|
731
|
+
cwd:
|
|
732
|
+
workflowTargets[
|
|
733
|
+
resolvedWorkflowTasks.findIndex((task) => task.id === verifiedWorkflow.targetTaskId)
|
|
734
|
+
]?.cwd ?? ctx.cwd,
|
|
735
|
+
targetTaskId: verifiedWorkflow.targetTaskId,
|
|
736
|
+
verifierTaskId: verifiedWorkflow.verifierTaskId,
|
|
737
|
+
checks: verifiedWorkflow.checks,
|
|
738
|
+
signal,
|
|
739
|
+
deadlineAt: deadline,
|
|
740
|
+
})
|
|
741
|
+
: undefined;
|
|
742
|
+
signal?.addEventListener("abort", cancelWorkflowGeneration, { once: true });
|
|
743
|
+
abortListenerAttached = signal !== undefined;
|
|
708
744
|
while (true) {
|
|
709
745
|
const snapshot = workLedger.snapshot();
|
|
710
746
|
const remainingBudgetMs =
|
|
@@ -732,6 +768,8 @@ export async function executeSubagent(
|
|
|
732
768
|
const dependencies = (task.dependsOn ?? [])
|
|
733
769
|
.map((dependency) => resultsById.get(dependency))
|
|
734
770
|
.filter((result): result is SingleResult => result !== undefined);
|
|
771
|
+
const managedVerifier =
|
|
772
|
+
completionController !== undefined && workItemId === verifiedWorkflow?.verifierTaskId;
|
|
735
773
|
const verifierDependency = task.verifierFor
|
|
736
774
|
? resultsById.get(task.verifierFor)
|
|
737
775
|
: undefined;
|
|
@@ -739,15 +777,17 @@ export async function executeSubagent(
|
|
|
739
777
|
verifierDependency?.structuredResult?.version === "pi-subagents:result:v2"
|
|
740
778
|
? verifierDependency.structuredResult
|
|
741
779
|
: undefined;
|
|
742
|
-
const dependencyContext =
|
|
743
|
-
?
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
:
|
|
780
|
+
const dependencyContext = managedVerifier
|
|
781
|
+
? ""
|
|
782
|
+
: task.verifierFor
|
|
783
|
+
? verifierStructuredResult
|
|
784
|
+
? `\n\nStaged target result:\n${redactPrivateText(
|
|
785
|
+
JSON.stringify(verifierStructuredResult),
|
|
786
|
+
)}`
|
|
787
|
+
: ""
|
|
788
|
+
: dependencies.length
|
|
789
|
+
? `\n\nDependency results:\n${buildFanInContext(dependencies)}`
|
|
790
|
+
: "";
|
|
751
791
|
const displayTask = task.task;
|
|
752
792
|
const target = workflowTargets[index];
|
|
753
793
|
const thinkingLevel = resolveThinkingLevel(task.agent, task.thinkingLevel);
|
|
@@ -782,8 +822,15 @@ export async function executeSubagent(
|
|
|
782
822
|
const verificationTargetContract = normalizeDelegationContract(
|
|
783
823
|
verificationTargetTask?.contract,
|
|
784
824
|
);
|
|
785
|
-
const
|
|
786
|
-
|
|
825
|
+
const managedVerificationInstruction = managedVerifier
|
|
826
|
+
? completionController?.verifierPrompt()
|
|
827
|
+
: undefined;
|
|
828
|
+
if (managedVerifier && !managedVerificationInstruction) {
|
|
829
|
+
throw new Error("Verified execution verifier instruction is unavailable");
|
|
830
|
+
}
|
|
831
|
+
const verificationSuffix = managedVerifier
|
|
832
|
+
? `\n\n${managedVerificationInstruction}`
|
|
833
|
+
: task.verifierFor && verifierTreeIdentity
|
|
787
834
|
? `\n\n${workflowVerificationInstruction(task.verifierFor, verifierTreeIdentity, {
|
|
788
835
|
acceptanceCriteria: [
|
|
789
836
|
...(verificationTargetTask?.acceptanceCriteria ?? []),
|
|
@@ -792,12 +839,19 @@ export async function executeSubagent(
|
|
|
792
839
|
requiredEvidence: verificationTargetContract?.requiredEvidence ?? [],
|
|
793
840
|
})}`
|
|
794
841
|
: "";
|
|
842
|
+
const reworkSuffix =
|
|
843
|
+
completionController &&
|
|
844
|
+
workItemId === verifiedWorkflow?.targetTaskId &&
|
|
845
|
+
(workLedger.get(workItemId)?.taskGeneration ?? 1) > 1
|
|
846
|
+
? `\n\n${completionController.reworkPrompt()}`
|
|
847
|
+
: "";
|
|
795
848
|
const baseTask = `${task.task}${dependencyContext}`;
|
|
849
|
+
const requiredSuffix = `${reworkSuffix}${verificationSuffix}`;
|
|
796
850
|
const baseBudget = Math.max(
|
|
797
851
|
0,
|
|
798
|
-
DEFAULT_MAX_CONTEXT_BYTES - Buffer.byteLength(
|
|
852
|
+
DEFAULT_MAX_CONTEXT_BYTES - Buffer.byteLength(requiredSuffix, "utf8"),
|
|
799
853
|
);
|
|
800
|
-
const taskWithContext = `${truncateUtf8(baseTask, baseBudget).text}${
|
|
854
|
+
const taskWithContext = `${truncateUtf8(baseTask, baseBudget).text}${requiredSuffix}`;
|
|
801
855
|
const prepared = prepareTask(taskWithContext, task);
|
|
802
856
|
const startedItem = startWorkItem(workItemId, task.agent);
|
|
803
857
|
const acceptedTaskGeneration = startedItem?.taskGeneration ?? 0;
|
|
@@ -807,6 +861,17 @@ export async function executeSubagent(
|
|
|
807
861
|
if (!budget) {
|
|
808
862
|
return Promise.resolve(exhaustedResult(task.agent, displayTask, thinkingLevel));
|
|
809
863
|
}
|
|
864
|
+
const childPolicy = launchPolicy(
|
|
865
|
+
target,
|
|
866
|
+
prepared,
|
|
867
|
+
displayTask,
|
|
868
|
+
task.agent,
|
|
869
|
+
thinkingLevel,
|
|
870
|
+
budget.timeoutMs,
|
|
871
|
+
acceptedTaskGeneration,
|
|
872
|
+
budget,
|
|
873
|
+
resolveTurnLimits(task),
|
|
874
|
+
);
|
|
810
875
|
return runSingleAgent(
|
|
811
876
|
ctx.cwd,
|
|
812
877
|
agents,
|
|
@@ -820,17 +885,15 @@ export async function executeSubagent(
|
|
|
820
885
|
undefined,
|
|
821
886
|
makeDetails("workflow"),
|
|
822
887
|
undefined,
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
resolveTurnLimits(task),
|
|
833
|
-
),
|
|
888
|
+
managedVerifier
|
|
889
|
+
? {
|
|
890
|
+
...childPolicy,
|
|
891
|
+
disableExtensions: true,
|
|
892
|
+
disableSkills: true,
|
|
893
|
+
disablePromptTemplates: true,
|
|
894
|
+
disableContextFiles: true,
|
|
895
|
+
}
|
|
896
|
+
: childPolicy,
|
|
834
897
|
);
|
|
835
898
|
};
|
|
836
899
|
const maxAttempts = task.retryPolicy?.maxAttempts ?? 1;
|
|
@@ -875,6 +938,72 @@ export async function executeSubagent(
|
|
|
875
938
|
resultsById.set(workItemId, result);
|
|
876
939
|
if (result.outcome?.status === "stale") {
|
|
877
940
|
// Cancellation or replacement already rotated and invalidated this generation.
|
|
941
|
+
} else if (managedVerifier && completionController) {
|
|
942
|
+
const structured =
|
|
943
|
+
result.structuredResult?.version === "pi-subagents:result:v2"
|
|
944
|
+
? result.structuredResult
|
|
945
|
+
: undefined;
|
|
946
|
+
try {
|
|
947
|
+
if (!structured || !result.executionPlan?.id) {
|
|
948
|
+
throw new Error(
|
|
949
|
+
"Verified execution verifier did not return a current structured-v2 result",
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
const completion = await completionController.completeVerifier({
|
|
953
|
+
taskGeneration: acceptedTaskGeneration,
|
|
954
|
+
executionPlanId: result.executionPlan.id,
|
|
955
|
+
verifierAgent: task.agent,
|
|
956
|
+
result: structured,
|
|
957
|
+
sourceTruncated: result.truncated,
|
|
958
|
+
});
|
|
959
|
+
const targetItem = workLedger.get(verifiedWorkflow?.targetTaskId ?? "");
|
|
960
|
+
if (
|
|
961
|
+
completion.decision === "rework" &&
|
|
962
|
+
targetItem?.acceptanceState === "rework-requested"
|
|
963
|
+
) {
|
|
964
|
+
const targetResult = resultsById.get(targetItem.id);
|
|
965
|
+
if (targetResult?.capabilityGrant?.state === "active") {
|
|
966
|
+
targetResult.capabilityGrant = revokeCapabilityGrant(
|
|
967
|
+
targetResult.capabilityGrant,
|
|
968
|
+
"verification-rework",
|
|
969
|
+
Date.now(),
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
if (result.capabilityGrant?.state === "active") {
|
|
973
|
+
result.capabilityGrant = revokeCapabilityGrant(
|
|
974
|
+
result.capabilityGrant,
|
|
975
|
+
"verification-rework",
|
|
976
|
+
Date.now(),
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
completionController.beginRework();
|
|
980
|
+
} else if (completion.decision !== "accept" && targetItem) {
|
|
981
|
+
const targetResult = resultsById.get(targetItem.id);
|
|
982
|
+
if (targetResult) {
|
|
983
|
+
targetResult.outcome = {
|
|
984
|
+
status: "failed",
|
|
985
|
+
reasonCode: targetItem.outcomeReason ?? "verification-rejected",
|
|
986
|
+
recoveryActions: ["stop"],
|
|
987
|
+
retryable: false,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
} catch (error) {
|
|
992
|
+
if (signal?.aborted) throw error;
|
|
993
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
994
|
+
const reasonCode = workflowCompletionFailureReason(error);
|
|
995
|
+
result.outcome = {
|
|
996
|
+
status: "failed",
|
|
997
|
+
reasonCode,
|
|
998
|
+
recoveryActions: ["revalidate"],
|
|
999
|
+
retryable: false,
|
|
1000
|
+
};
|
|
1001
|
+
result.errorMessage = message;
|
|
1002
|
+
const verifierItem = workLedger.get(workItemId);
|
|
1003
|
+
if (verifierItem?.state === "running") {
|
|
1004
|
+
workLedger.failVerification(workItemId, reasonCode);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
878
1007
|
} else if (task.verifierFor) {
|
|
879
1008
|
const staged = workLedger.get(task.verifierFor);
|
|
880
1009
|
const structured =
|
|
@@ -963,6 +1092,34 @@ export async function executeSubagent(
|
|
|
963
1092
|
result.errorMessage = failureReason;
|
|
964
1093
|
workLedger.failVerification(workItemId, reasonCode);
|
|
965
1094
|
}
|
|
1095
|
+
} else if (
|
|
1096
|
+
completionController &&
|
|
1097
|
+
workItemId === verifiedWorkflow?.targetTaskId &&
|
|
1098
|
+
!isResultError(result)
|
|
1099
|
+
) {
|
|
1100
|
+
try {
|
|
1101
|
+
if (!result.executionPlan?.id) {
|
|
1102
|
+
throw new Error("Verified execution producer has no accepted ExecutionPlan");
|
|
1103
|
+
}
|
|
1104
|
+
await completionController.stageTarget({
|
|
1105
|
+
taskGeneration: acceptedTaskGeneration,
|
|
1106
|
+
executionPlanId: result.executionPlan.id,
|
|
1107
|
+
artifacts: artifactsFromResult(result),
|
|
1108
|
+
});
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
if (signal?.aborted) throw error;
|
|
1111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1112
|
+
result.outcome = {
|
|
1113
|
+
status: "failed",
|
|
1114
|
+
reasonCode: "verification-checks-unavailable",
|
|
1115
|
+
recoveryActions: ["revalidate"],
|
|
1116
|
+
retryable: false,
|
|
1117
|
+
};
|
|
1118
|
+
result.errorMessage = message;
|
|
1119
|
+
if (workLedger.get(workItemId)?.state === "running") {
|
|
1120
|
+
settleWorkItem(workItemId, result, acceptedTaskGeneration);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
966
1123
|
} else if (verificationTargetIds.has(workItemId) && !isResultError(result)) {
|
|
967
1124
|
try {
|
|
968
1125
|
const treeIdentity = await captureWorkflowTreeIdentity(target.cwd, { signal });
|
|
@@ -1001,7 +1158,8 @@ export async function executeSubagent(
|
|
|
1001
1158
|
if (
|
|
1002
1159
|
item.state !== "pending" &&
|
|
1003
1160
|
item.state !== "ready" &&
|
|
1004
|
-
item.state !== "awaiting-verification"
|
|
1161
|
+
item.state !== "awaiting-verification" &&
|
|
1162
|
+
!(item.state === "completed" && item.acceptanceState === "pending")
|
|
1005
1163
|
) {
|
|
1006
1164
|
continue;
|
|
1007
1165
|
}
|
|
@@ -1009,7 +1167,10 @@ export async function executeSubagent(
|
|
|
1009
1167
|
workLedger.settle(item.id, "interrupted", "parent-aborted");
|
|
1010
1168
|
} else if (deadline !== undefined && Date.now() >= deadline) {
|
|
1011
1169
|
workLedger.settle(item.id, "blocked", "budget-exhausted");
|
|
1012
|
-
} else if (
|
|
1170
|
+
} else if (
|
|
1171
|
+
item.state === "awaiting-verification" ||
|
|
1172
|
+
(item.state === "completed" && item.acceptanceState === "pending")
|
|
1173
|
+
) {
|
|
1013
1174
|
workLedger.settle(item.id, "blocked", "verification-not-completed");
|
|
1014
1175
|
} else {
|
|
1015
1176
|
const dependencyBlocked = item.dependencies.some(
|
|
@@ -1027,7 +1188,12 @@ export async function executeSubagent(
|
|
|
1027
1188
|
const completed = resultsById.get(task.id);
|
|
1028
1189
|
const item = workLedger.get(task.id);
|
|
1029
1190
|
if (completed) {
|
|
1030
|
-
if (
|
|
1191
|
+
if (
|
|
1192
|
+
item &&
|
|
1193
|
+
(item.state !== "completed" ||
|
|
1194
|
+
(item.acceptanceRequired && item.acceptanceState !== "accepted")) &&
|
|
1195
|
+
!isResultError(completed)
|
|
1196
|
+
) {
|
|
1031
1197
|
completed.outcome = {
|
|
1032
1198
|
status:
|
|
1033
1199
|
item.state === "needs-input"
|
|
@@ -1099,7 +1265,10 @@ export async function executeSubagent(
|
|
|
1099
1265
|
isError: isError || undefined,
|
|
1100
1266
|
};
|
|
1101
1267
|
} finally {
|
|
1102
|
-
|
|
1268
|
+
if (abortListenerAttached) {
|
|
1269
|
+
signal?.removeEventListener("abort", cancelWorkflowGeneration);
|
|
1270
|
+
}
|
|
1271
|
+
completionController?.dispose();
|
|
1103
1272
|
try {
|
|
1104
1273
|
await persistWorkLedger();
|
|
1105
1274
|
} finally {
|
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
SessionManager,
|
|
8
8
|
SettingsManager,
|
|
9
9
|
} from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import {
|
|
10
|
+
import { discoverAgents } from "./agents/discovery.js";
|
|
11
|
+
import type { AgentConfig, SubagentThinkingLevel } from "./agents/types.js";
|
|
11
12
|
import { redactPrivateText } from "./context.js";
|
|
12
13
|
import { appendDelegationContract } from "./delegation-contract.js";
|
|
13
|
-
import { resolveDefaultSubagentTimeoutMs } from "./execution.js";
|
|
14
|
+
import { resolveDefaultSubagentTimeoutMs } from "./execution/runtime-policy.js";
|
|
14
15
|
import { DEFAULT_MAX_CONTEXT_BYTES, DEFAULT_MAX_OUTPUT_BYTES, truncateUtf8 } from "./limits.js";
|
|
15
16
|
import { resolvePiPromptResources } from "./prompt-resources.js";
|
|
16
17
|
import type { AgentTurn, ManagedAgent, TurnOutcome } from "./registry.js";
|
package/src/inspect.ts
CHANGED
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
type ExtensionContext,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { type Static, Type } from "typebox";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "./agents.js";
|
|
9
|
+
import { discoverAgents } from "./agents/discovery.js";
|
|
10
|
+
import type {
|
|
11
|
+
AgentConfig,
|
|
12
|
+
AgentScope,
|
|
13
|
+
ConsultationCwdPolicy,
|
|
14
|
+
DelegationCwdPolicy,
|
|
15
|
+
} from "./agents/types.js";
|
|
16
16
|
import { projectCapabilityManifest } from "./capabilities.js";
|
|
17
17
|
import { resolveConsultTools } from "./consult-policy.js";
|
|
18
18
|
import { buildContextSnapshot, type ContextMode } from "./context.js";
|
|
@@ -21,6 +21,7 @@ import { DEFAULT_MAX_CONTEXT_BYTES } from "./limits.js";
|
|
|
21
21
|
import { resolvePiInvocation } from "./pi-invocation.js";
|
|
22
22
|
import type { AgentRunInspectionDetail, AgentRunInspectionSummary } from "./registry.js";
|
|
23
23
|
import { boundedPrivateText, boundText, safeDisplayPath, safeTerminalLine } from "./safe-text.js";
|
|
24
|
+
import { resolveDelegationWorkflow } from "./settings/inspection.js";
|
|
24
25
|
import {
|
|
25
26
|
inspectBlockingParallelLimitSettings,
|
|
26
27
|
inspectCompletionDeliverySettings,
|
|
@@ -30,7 +31,6 @@ import {
|
|
|
30
31
|
inspectStatefulLimitSettings,
|
|
31
32
|
inspectStatefulTransportSettings,
|
|
32
33
|
inspectSubagentSettings,
|
|
33
|
-
resolveDelegationWorkflow,
|
|
34
34
|
} from "./settings.js";
|
|
35
35
|
import type { StatefulSubagentRuntimeStatus } from "./stateful.js";
|
|
36
36
|
import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
|
|
@@ -404,6 +404,8 @@ function projectRunSummary(run: AgentRunInspectionSummary): Record<string, unkno
|
|
|
404
404
|
updatedAt: run.updatedAt,
|
|
405
405
|
historyCount: run.historyCount,
|
|
406
406
|
unreadMessages: run.unreadMessages,
|
|
407
|
+
turnGeneration: run.turnGeneration,
|
|
408
|
+
pendingCompletionCount: run.pendingCompletionCount,
|
|
407
409
|
};
|
|
408
410
|
}
|
|
409
411
|
|
|
@@ -474,6 +476,8 @@ function projectRun(run: AgentRunInspectionDetail, ctx: ExtensionContext): Recor
|
|
|
474
476
|
: undefined,
|
|
475
477
|
semanticCompatibility: run.semanticCompatibility,
|
|
476
478
|
telemetry: run.telemetry,
|
|
479
|
+
currentRunId: run.currentRunId,
|
|
480
|
+
currentTurnGeneration: run.currentTurnGeneration,
|
|
477
481
|
currentTask: run.currentTask ? boundedPrivateText(run.currentTask, 2 * 1024) : undefined,
|
|
478
482
|
error: run.error ? boundedPrivateText(run.error, 2 * 1024) : undefined,
|
|
479
483
|
target: run.target
|
|
@@ -537,6 +541,33 @@ function projectWorkflow(workflow: WorkItemLedgerSnapshot): Record<string, unkno
|
|
|
537
541
|
verified: artifact.verified,
|
|
538
542
|
})),
|
|
539
543
|
verificationAccepted: item.verificationAccepted,
|
|
544
|
+
acceptanceStateVersion: item.acceptanceStateVersion,
|
|
545
|
+
acceptanceRequired: item.acceptanceRequired,
|
|
546
|
+
acceptanceState: item.acceptanceState,
|
|
547
|
+
reworkCount: item.reworkCount,
|
|
548
|
+
maxReworkCycles: item.maxReworkCycles,
|
|
549
|
+
acceptanceReceipt: item.acceptanceReceipt
|
|
550
|
+
? {
|
|
551
|
+
version: item.acceptanceReceipt.version,
|
|
552
|
+
decision: item.acceptanceReceipt.decision,
|
|
553
|
+
targetTaskId: boundedPrivateText(item.acceptanceReceipt.targetTaskId, 256),
|
|
554
|
+
targetTaskGeneration: item.acceptanceReceipt.targetTaskGeneration,
|
|
555
|
+
targetExecutionPlanId: item.acceptanceReceipt.targetExecutionPlanId,
|
|
556
|
+
verifierTaskId: boundedPrivateText(item.acceptanceReceipt.verifierTaskId, 256),
|
|
557
|
+
verifierTaskGeneration: item.acceptanceReceipt.verifierTaskGeneration,
|
|
558
|
+
verifierExecutionPlanId: item.acceptanceReceipt.verifierExecutionPlanId,
|
|
559
|
+
verifierAgent: boundedPrivateText(item.acceptanceReceipt.verifierAgent, 256),
|
|
560
|
+
beforeTreeIdentity: item.acceptanceReceipt.beforeTreeIdentity,
|
|
561
|
+
afterTreeIdentity: item.acceptanceReceipt.afterTreeIdentity,
|
|
562
|
+
patchDigest: item.acceptanceReceipt.patchDigest,
|
|
563
|
+
changedPathCount: item.acceptanceReceipt.changedPaths.length,
|
|
564
|
+
checkCount: item.acceptanceReceipt.checks.length,
|
|
565
|
+
requiredEvidenceCount: item.acceptanceReceipt.requiredEvidenceIds.length,
|
|
566
|
+
createdAt: item.acceptanceReceipt.createdAt,
|
|
567
|
+
sourceTruncated: item.acceptanceReceipt.sourceTruncated,
|
|
568
|
+
}
|
|
569
|
+
: undefined,
|
|
570
|
+
acceptanceReceiptHistoryCount: item.acceptanceReceiptHistory.length,
|
|
540
571
|
stagedTreeIdentity: item.stagedTreeIdentity
|
|
541
572
|
? {
|
|
542
573
|
version: item.stagedTreeIdentity.version,
|
package/src/limits.ts
CHANGED
|
@@ -8,6 +8,7 @@ export const DEFAULT_MAX_PARALLEL_TASKS = 8;
|
|
|
8
8
|
export const MAX_CONFIGURABLE_PARALLEL_TASKS = 64;
|
|
9
9
|
export const MAX_BLOCKING_PARALLEL_CONCURRENCY = 4;
|
|
10
10
|
export const DEFAULT_MAX_MESSAGES = 200;
|
|
11
|
+
export const MAX_TOOL_MESSAGE_BYTES = 2 * 1024;
|
|
11
12
|
export const TRUNCATION_MARKER = "\n… [truncated by pi-subagents]";
|
|
12
13
|
export const TAIL_TRUNCATION_MARKER = "… [truncated by pi-subagents]\n";
|
|
13
14
|
|
|
@@ -81,10 +81,15 @@ export function calculateOrchestrationMetrics(
|
|
|
81
81
|
).length,
|
|
82
82
|
executorAcceptedVerification: items.filter((item) => item.verificationAccepted).length,
|
|
83
83
|
verificationRework: items.filter(
|
|
84
|
-
(item) =>
|
|
84
|
+
(item) =>
|
|
85
|
+
!item.verifierFor &&
|
|
86
|
+
(item.verificationReceipt?.decision === "rework" ||
|
|
87
|
+
item.acceptanceReceiptHistory.some((receipt) => receipt.decision === "rework")),
|
|
85
88
|
).length,
|
|
86
89
|
verificationRejected: items.filter(
|
|
87
|
-
(item) =>
|
|
90
|
+
(item) =>
|
|
91
|
+
!item.verifierFor &&
|
|
92
|
+
(item.verificationReceipt?.decision === "reject" || item.acceptanceState === "rejected"),
|
|
88
93
|
).length,
|
|
89
94
|
verificationInvalid: items.filter(
|
|
90
95
|
(item) => !item.verifierFor && item.outcomeReason === "verification-receipt-invalid",
|
|
@@ -92,9 +97,11 @@ export function calculateOrchestrationMetrics(
|
|
|
92
97
|
verificationTreeMismatch: items.filter(
|
|
93
98
|
(item) =>
|
|
94
99
|
!item.verifierFor &&
|
|
95
|
-
[
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
[
|
|
101
|
+
"verification-tree-mismatch",
|
|
102
|
+
"verification-tree-unavailable",
|
|
103
|
+
"verification-tree-drift",
|
|
104
|
+
].includes(item.outcomeReason ?? ""),
|
|
98
105
|
).length,
|
|
99
106
|
...(panel
|
|
100
107
|
? {
|