@narumitw/pi-subagents 0.49.2 → 0.51.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 +313 -53
- package/package.json +11 -8
- package/src/adaptive-scheduler.ts +196 -0
- package/src/admission-benchmark.ts +95 -0
- package/src/admission-policy.ts +78 -0
- package/src/agent-projection.ts +53 -0
- package/src/agents.ts +58 -1
- package/src/auto-transport.ts +114 -0
- package/src/blocking-status.ts +63 -0
- package/src/capabilities.ts +145 -0
- package/src/capability-grant.ts +115 -0
- package/src/capability-router.ts +107 -0
- package/src/completion-delivery.ts +257 -0
- package/src/config-status.ts +221 -0
- package/src/config-ui.ts +215 -236
- package/src/consult-resources.ts +4 -27
- package/src/consult.ts +9 -1
- package/src/create-stateful-transport.ts +55 -0
- package/src/delegation-contract.ts +417 -0
- package/src/execution-plan.ts +322 -0
- package/src/execution-profiles.ts +95 -0
- package/src/execution-ui.ts +320 -0
- package/src/execution.ts +848 -158
- package/src/in-process-transport.ts +269 -25
- package/src/inspect-render.ts +101 -1
- package/src/inspect.ts +296 -3
- package/src/integration-controller.ts +98 -0
- package/src/limits.ts +3 -0
- package/src/orchestration-metrics.ts +78 -0
- package/src/outcome.ts +61 -0
- package/src/panel-child-group.ts +35 -0
- package/src/panel-contract.ts +343 -0
- package/src/panel-evidence.ts +59 -0
- package/src/panel-execution.ts +772 -0
- package/src/panel-failure.ts +56 -0
- package/src/panel-planning.ts +175 -0
- package/src/panel-prompts.ts +132 -0
- package/src/panel-reconciliation.ts +57 -0
- package/src/panel-render.ts +103 -0
- package/src/parallel-limit-ui.ts +112 -0
- package/src/params.ts +172 -3
- package/src/persistence.ts +182 -32
- package/src/prompt-resources.ts +38 -0
- package/src/registry-types.ts +175 -0
- package/src/registry.ts +466 -143
- package/src/render.ts +72 -6
- package/src/result-contract.ts +416 -0
- package/src/retained-semantic-state.ts +100 -0
- package/src/rpc-timeout-finalization.ts +207 -0
- package/src/rpc-transport-metadata.ts +65 -0
- package/src/rpc-transport.ts +990 -0
- package/src/rpc-turn-capture.ts +142 -0
- package/src/runner-result.ts +55 -0
- package/src/runner-usage.ts +48 -0
- package/src/runner.ts +325 -73
- package/src/semantic-snapshot.ts +214 -0
- package/src/settings.ts +254 -35
- package/src/spawn-idempotency.ts +61 -0
- package/src/stateful-config.ts +13 -0
- package/src/stateful-guidance.ts +1 -0
- package/src/stateful-lifecycle.ts +45 -2
- package/src/stateful-limit-ui.ts +246 -0
- package/src/stateful-limits.ts +96 -0
- package/src/stateful-prompt.ts +11 -2
- package/src/stateful-render.ts +48 -3
- package/src/stateful.ts +467 -357
- package/src/subagents.ts +114 -46
- package/src/subprocess-transport.ts +64 -5
- package/src/supervision.ts +103 -0
- package/src/timeout-checkpoint.ts +305 -0
- package/src/timeout-finalization.ts +75 -0
- package/src/transport-types.ts +68 -0
- package/src/transport-ui.ts +169 -0
- package/src/transport.ts +16 -4
- package/src/turn-budget.ts +109 -0
- package/src/verification-policy.ts +17 -0
- package/src/work-item-ledger.ts +682 -0
- package/src/work-item-persistence.ts +218 -0
- package/src/workflow-planning.ts +150 -0
- package/src/workflow-ui.ts +61 -0
- package/src/workspace.ts +69 -12
package/src/inspect.ts
CHANGED
|
@@ -13,26 +13,38 @@ import {
|
|
|
13
13
|
type DelegationCwdPolicy,
|
|
14
14
|
discoverAgents,
|
|
15
15
|
} from "./agents.js";
|
|
16
|
+
import { projectCapabilityManifest } from "./capabilities.js";
|
|
16
17
|
import { resolveConsultTools } from "./consult-policy.js";
|
|
18
|
+
import { buildContextSnapshot, type ContextMode } from "./context.js";
|
|
17
19
|
import { renderInspectCall, renderInspectResult } from "./inspect-render.js";
|
|
20
|
+
import { DEFAULT_MAX_CONTEXT_BYTES } from "./limits.js";
|
|
21
|
+
import { resolvePiInvocation } from "./pi-invocation.js";
|
|
18
22
|
import type { AgentRunInspectionDetail, AgentRunInspectionSummary } from "./registry.js";
|
|
19
23
|
import { boundedPrivateText, boundText, safeDisplayPath, safeTerminalLine } from "./safe-text.js";
|
|
20
24
|
import {
|
|
25
|
+
inspectBlockingParallelLimitSettings,
|
|
21
26
|
inspectCompletionDeliverySettings,
|
|
22
27
|
inspectConsultResourceSettings,
|
|
23
28
|
inspectCwdPolicySettings,
|
|
24
29
|
inspectDelegationWorkflowSettings,
|
|
30
|
+
inspectStatefulLimitSettings,
|
|
31
|
+
inspectStatefulTransportSettings,
|
|
25
32
|
inspectSubagentSettings,
|
|
26
33
|
resolveDelegationWorkflow,
|
|
27
34
|
} from "./settings.js";
|
|
28
35
|
import type { StatefulSubagentRuntimeStatus } from "./stateful.js";
|
|
36
|
+
import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
|
|
37
|
+
import { inspectSessionWorkflows } from "./work-item-persistence.js";
|
|
29
38
|
|
|
30
39
|
const INSPECT_ACTIONS = [
|
|
31
40
|
"list_agents",
|
|
32
41
|
"get_agent",
|
|
33
42
|
"list_runs",
|
|
34
43
|
"get_run",
|
|
44
|
+
"list_workflows",
|
|
45
|
+
"get_workflow",
|
|
35
46
|
"list_models",
|
|
47
|
+
"preview_context",
|
|
36
48
|
"status",
|
|
37
49
|
"diagnose",
|
|
38
50
|
] as const;
|
|
@@ -43,6 +55,10 @@ const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
|
|
|
43
55
|
});
|
|
44
56
|
|
|
45
57
|
const LimitSchema = Type.Number({ minimum: 1, maximum: 100, multipleOf: 1 });
|
|
58
|
+
const ContextModeSchema = Type.Union([
|
|
59
|
+
StringEnum(["none", "all", "summary"] as const),
|
|
60
|
+
Type.Number({ minimum: 1, multipleOf: 1 }),
|
|
61
|
+
]);
|
|
46
62
|
const MAX_DETAILS_LIST_BYTES = 40 * 1024;
|
|
47
63
|
|
|
48
64
|
export const SubagentInspectParams = Type.Object(
|
|
@@ -50,9 +66,12 @@ export const SubagentInspectParams = Type.Object(
|
|
|
50
66
|
action: StringEnum(INSPECT_ACTIONS),
|
|
51
67
|
agent: Type.Optional(Type.String({ minLength: 1 })),
|
|
52
68
|
agentId: Type.Optional(Type.String({ minLength: 1 })),
|
|
69
|
+
workflowId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
|
|
53
70
|
agentScope: Type.Optional(AgentScopeSchema),
|
|
54
71
|
limit: Type.Optional(LimitSchema),
|
|
55
72
|
includeClosed: Type.Optional(Type.Boolean({ default: false })),
|
|
73
|
+
context: Type.Optional(ContextModeSchema),
|
|
74
|
+
contextEntryIds: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
56
75
|
},
|
|
57
76
|
{ additionalProperties: false },
|
|
58
77
|
);
|
|
@@ -61,6 +80,7 @@ export type SubagentInspectParams = Static<typeof SubagentInspectParams>;
|
|
|
61
80
|
|
|
62
81
|
export interface SubagentInspectRuntime {
|
|
63
82
|
getBlockingEnabled(): boolean;
|
|
83
|
+
getMaxParallelTasks(): number;
|
|
64
84
|
getConsultResourcePolicy(): "project-context" | "none" | "all";
|
|
65
85
|
getConsultationCwdPolicy(): ConsultationCwdPolicy;
|
|
66
86
|
getDelegationCwdPolicy(): DelegationCwdPolicy;
|
|
@@ -79,7 +99,10 @@ type ValidatedInspectOperation =
|
|
|
79
99
|
| { action: "get_agent"; agent: string; agentScope: AgentScope }
|
|
80
100
|
| { action: "list_runs"; includeClosed: boolean; limit: number }
|
|
81
101
|
| { action: "get_run"; agentId: string }
|
|
102
|
+
| { action: "list_workflows"; limit: number }
|
|
103
|
+
| { action: "get_workflow"; workflowId: string }
|
|
82
104
|
| { action: "list_models"; limit: number }
|
|
105
|
+
| { action: "preview_context"; context: ContextMode; contextEntryIds?: string[] }
|
|
83
106
|
| { action: "status" }
|
|
84
107
|
| { action: "diagnose" };
|
|
85
108
|
|
|
@@ -88,7 +111,7 @@ export function registerSubagentInspect(pi: ExtensionAPI, runtime: SubagentInspe
|
|
|
88
111
|
name: "subagent_inspect",
|
|
89
112
|
label: "Inspect Subagents",
|
|
90
113
|
description:
|
|
91
|
-
"Inspect available subagent definitions, models, retained runs, runtime status, and diagnostics without changing subagent or workspace state. This tool never starts a child, sends or acknowledges messages, interrupts or closes runs, changes settings, or modifies files.",
|
|
114
|
+
"Inspect available subagent definitions, models, retained runs, persisted blocking workflows, runtime status, and diagnostics without changing subagent or workspace state. This tool never starts a child, sends or acknowledges messages, interrupts or closes runs, changes settings, or modifies files.",
|
|
92
115
|
promptSnippet: "Inspect subagent metadata and runtime state without changing it",
|
|
93
116
|
parameters: SubagentInspectParams,
|
|
94
117
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx): Promise<InspectToolResult> {
|
|
@@ -118,7 +141,10 @@ export function validateInspectParams(params: unknown): ValidatedInspectOperatio
|
|
|
118
141
|
get_agent: ["action", "agent", "agentScope"],
|
|
119
142
|
list_runs: ["action", "includeClosed", "limit"],
|
|
120
143
|
get_run: ["action", "agentId"],
|
|
144
|
+
list_workflows: ["action", "limit"],
|
|
145
|
+
get_workflow: ["action", "workflowId"],
|
|
121
146
|
list_models: ["action", "limit"],
|
|
147
|
+
preview_context: ["action", "context", "contextEntryIds"],
|
|
122
148
|
status: ["action"],
|
|
123
149
|
diagnose: ["action"],
|
|
124
150
|
};
|
|
@@ -148,9 +174,24 @@ export function validateInspectParams(params: unknown): ValidatedInspectOperatio
|
|
|
148
174
|
if (action === "get_run") {
|
|
149
175
|
return { action, agentId: requiredString(values.agentId, action, "agentId") };
|
|
150
176
|
}
|
|
177
|
+
if (action === "list_workflows") {
|
|
178
|
+
return { action, limit: optionalLimit(values.limit, 50) };
|
|
179
|
+
}
|
|
180
|
+
if (action === "get_workflow") {
|
|
181
|
+
return { action, workflowId: requiredString(values.workflowId, action, "workflowId") };
|
|
182
|
+
}
|
|
151
183
|
if (action === "list_models") {
|
|
152
184
|
return { action, limit: optionalLimit(values.limit, 50) };
|
|
153
185
|
}
|
|
186
|
+
if (action === "preview_context") {
|
|
187
|
+
const context = optionalContextMode(values.context);
|
|
188
|
+
const contextEntryIds = optionalStringArray(values.contextEntryIds, "contextEntryIds");
|
|
189
|
+
return {
|
|
190
|
+
action,
|
|
191
|
+
context: values.context === undefined && contextEntryIds ? "all" : context,
|
|
192
|
+
...(contextEntryIds ? { contextEntryIds } : {}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
154
195
|
return { action };
|
|
155
196
|
}
|
|
156
197
|
|
|
@@ -204,9 +245,59 @@ async function executeSubagentInspect(
|
|
|
204
245
|
}
|
|
205
246
|
return inspectResult({ action: operation.action, run: projectRun(run, ctx) });
|
|
206
247
|
}
|
|
248
|
+
if (operation.action === "list_workflows" || operation.action === "get_workflow") {
|
|
249
|
+
const owner =
|
|
250
|
+
ctx.sessionManager.getSessionId?.() ??
|
|
251
|
+
ctx.sessionManager.getSessionFile?.() ??
|
|
252
|
+
`ephemeral:${ctx.cwd}`;
|
|
253
|
+
const inspected = inspectSessionWorkflows(owner, {
|
|
254
|
+
maxStoredWorkflows: operation.action === "list_workflows" ? operation.limit : 64,
|
|
255
|
+
});
|
|
256
|
+
if (operation.action === "list_workflows") {
|
|
257
|
+
const selected = boundedProjection(
|
|
258
|
+
inspected.workflows,
|
|
259
|
+
operation.limit,
|
|
260
|
+
projectWorkflowSummary,
|
|
261
|
+
);
|
|
262
|
+
return inspectResult({
|
|
263
|
+
action: operation.action,
|
|
264
|
+
workflows: selected.items,
|
|
265
|
+
returned: selected.items.length,
|
|
266
|
+
omitted: inspected.omitted + selected.omitted,
|
|
267
|
+
invalid: inspected.invalid,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const workflow = inspected.workflows.find(
|
|
271
|
+
(candidate) => candidate.workflowId === operation.workflowId,
|
|
272
|
+
);
|
|
273
|
+
if (!workflow) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Unknown persisted workflow: ${boundedPrivateText(operation.workflowId, 256)}`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return inspectResult({ action: operation.action, workflow: projectWorkflow(workflow) });
|
|
279
|
+
}
|
|
207
280
|
if (operation.action === "list_models") {
|
|
208
281
|
return inspectResult({ action: operation.action, ...projectModels(ctx, operation.limit) });
|
|
209
282
|
}
|
|
283
|
+
if (operation.action === "preview_context") {
|
|
284
|
+
const snapshot = buildContextSnapshot(
|
|
285
|
+
ctx.sessionManager.getBranch(),
|
|
286
|
+
operation.context,
|
|
287
|
+
DEFAULT_MAX_CONTEXT_BYTES,
|
|
288
|
+
operation.contextEntryIds,
|
|
289
|
+
);
|
|
290
|
+
return inspectResult({
|
|
291
|
+
action: operation.action,
|
|
292
|
+
preview: {
|
|
293
|
+
mode: operation.context,
|
|
294
|
+
turns: snapshot.turns,
|
|
295
|
+
sourceCount: snapshot.sourceIds.length,
|
|
296
|
+
bytes: Buffer.byteLength(snapshot.text, "utf8"),
|
|
297
|
+
truncated: snapshot.truncated,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
210
301
|
if (operation.action === "status") {
|
|
211
302
|
return inspectResult({ action: operation.action, status: projectStatus(runtime) });
|
|
212
303
|
}
|
|
@@ -215,6 +306,8 @@ async function executeSubagentInspect(
|
|
|
215
306
|
const userDiscovery = discoverAgents(ctx.cwd, "user", settings.settings);
|
|
216
307
|
const modelCount = availableModelCount(ctx);
|
|
217
308
|
const runtimeStatus = runtime.getRuntimeStatus();
|
|
309
|
+
const rpcCapability = inspectRpcCapability();
|
|
310
|
+
const inProcessCapability = await inspectInProcessCapability();
|
|
218
311
|
const checks = [
|
|
219
312
|
{
|
|
220
313
|
name: "settings",
|
|
@@ -244,6 +337,20 @@ async function executeSubagentInspect(
|
|
|
244
337
|
? "Stateful runtime initialized."
|
|
245
338
|
: "Stateful runtime not initialized.",
|
|
246
339
|
},
|
|
340
|
+
{
|
|
341
|
+
name: "in-process-sdk",
|
|
342
|
+
status: inProcessCapability.error ? "fail" : "pass",
|
|
343
|
+
message: inProcessCapability.error
|
|
344
|
+
? boundedPrivateText(inProcessCapability.error, 2 * 1024)
|
|
345
|
+
: "Required public Pi in-process session APIs are available.",
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "rpc-cli",
|
|
349
|
+
status: rpcCapability.error ? "fail" : "pass",
|
|
350
|
+
message: rpcCapability.error
|
|
351
|
+
? boundedPrivateText(rpcCapability.error, 2 * 1024)
|
|
352
|
+
: "The exact loaded Pi CLI is available for persistent RPC transport.",
|
|
353
|
+
},
|
|
247
354
|
{
|
|
248
355
|
name: "consultation",
|
|
249
356
|
status: runtime.getBlockingEnabled() && modelCount > 0 ? "pass" : "fail",
|
|
@@ -280,6 +387,7 @@ function projectAgent(
|
|
|
280
387
|
: safeDisplayPath(agent.filePath, ctx.cwd),
|
|
281
388
|
model: agent.model ? boundedPrivateText(agent.model, 256) : undefined,
|
|
282
389
|
thinkingLevel: agent.thinkingLevel,
|
|
390
|
+
capabilityManifest: projectCapabilityManifest(agent.capabilityManifest),
|
|
283
391
|
...(includeTools
|
|
284
392
|
? { tools, toolCount: agent.tools?.length }
|
|
285
393
|
: { toolCount: agent.tools?.length }),
|
|
@@ -305,6 +413,67 @@ function projectRun(run: AgentRunInspectionDetail, ctx: ExtensionContext): Recor
|
|
|
305
413
|
cwd: safeDisplayPath(run.cwd, ctx.cwd),
|
|
306
414
|
workspaceMode: run.workspaceMode ?? "shared",
|
|
307
415
|
thinkingLevel: run.thinkingLevel,
|
|
416
|
+
timeoutMs: run.timeoutMs,
|
|
417
|
+
currentTimeoutMs: run.currentTimeoutMs,
|
|
418
|
+
idleTimeoutMs: run.idleTimeoutMs,
|
|
419
|
+
currentIdleTimeoutMs: run.currentIdleTimeoutMs,
|
|
420
|
+
maxTurns: run.maxTurns,
|
|
421
|
+
currentMaxTurns: run.currentMaxTurns,
|
|
422
|
+
maxToolCalls: run.maxToolCalls,
|
|
423
|
+
currentMaxToolCalls: run.currentMaxToolCalls,
|
|
424
|
+
context: {
|
|
425
|
+
turns: run.contextTurns ?? 0,
|
|
426
|
+
sources: run.contextSources ?? 0,
|
|
427
|
+
bytes: run.contextBytes ?? 0,
|
|
428
|
+
truncated: run.contextTruncated === true,
|
|
429
|
+
},
|
|
430
|
+
contract: run.contract
|
|
431
|
+
? {
|
|
432
|
+
version: run.contract.version,
|
|
433
|
+
level: run.contract.level,
|
|
434
|
+
taskId: boundedPrivateText(run.contract.taskId, 256),
|
|
435
|
+
enforcement: run.contract.enforcement,
|
|
436
|
+
dependencies: run.contract.dependencies.length,
|
|
437
|
+
acceptanceCriteria: run.contract.acceptanceCriteria.length,
|
|
438
|
+
requiredEvidence: run.contract.requiredEvidence.length,
|
|
439
|
+
}
|
|
440
|
+
: undefined,
|
|
441
|
+
resultFormat: run.resultFormat ?? "text",
|
|
442
|
+
structuredResult: run.structuredResult,
|
|
443
|
+
termination: run.termination,
|
|
444
|
+
outcome: run.outcome,
|
|
445
|
+
capabilityGrant: run.capabilityGrant
|
|
446
|
+
? {
|
|
447
|
+
version: run.capabilityGrant.version,
|
|
448
|
+
id: run.capabilityGrant.id,
|
|
449
|
+
executionPlanId: run.capabilityGrant.executionPlanId,
|
|
450
|
+
taskGeneration: run.capabilityGrant.taskGeneration,
|
|
451
|
+
issuedAt: run.capabilityGrant.issuedAt,
|
|
452
|
+
expiresAt: run.capabilityGrant.expiresAt,
|
|
453
|
+
state: run.capabilityGrant.state,
|
|
454
|
+
revokedAt: run.capabilityGrant.revokedAt,
|
|
455
|
+
revocationReason: run.capabilityGrant.revocationReason,
|
|
456
|
+
}
|
|
457
|
+
: undefined,
|
|
458
|
+
executionPlan: run.executionPlan
|
|
459
|
+
? {
|
|
460
|
+
...run.executionPlan,
|
|
461
|
+
target: {
|
|
462
|
+
...run.executionPlan.target,
|
|
463
|
+
cwd: safeDisplayPath(run.executionPlan.target.cwd, ctx.cwd),
|
|
464
|
+
trust: { ...run.executionPlan.target.trust, sourcePath: undefined },
|
|
465
|
+
},
|
|
466
|
+
}
|
|
467
|
+
: undefined,
|
|
468
|
+
semanticSnapshot: run.semanticSnapshot
|
|
469
|
+
? {
|
|
470
|
+
version: run.semanticSnapshot.version,
|
|
471
|
+
digest: run.semanticSnapshot.digest,
|
|
472
|
+
components: { ...run.semanticSnapshot.components },
|
|
473
|
+
}
|
|
474
|
+
: undefined,
|
|
475
|
+
semanticCompatibility: run.semanticCompatibility,
|
|
476
|
+
telemetry: run.telemetry,
|
|
308
477
|
currentTask: run.currentTask ? boundedPrivateText(run.currentTask, 2 * 1024) : undefined,
|
|
309
478
|
error: run.error ? boundedPrivateText(run.error, 2 * 1024) : undefined,
|
|
310
479
|
target: run.target
|
|
@@ -333,6 +502,52 @@ function projectRun(run: AgentRunInspectionDetail, ctx: ExtensionContext): Recor
|
|
|
333
502
|
};
|
|
334
503
|
}
|
|
335
504
|
|
|
505
|
+
function projectWorkflowSummary(workflow: WorkItemLedgerSnapshot): Record<string, unknown> {
|
|
506
|
+
return {
|
|
507
|
+
workflowId: boundedPrivateText(workflow.workflowId, 256),
|
|
508
|
+
generation: workflow.generation,
|
|
509
|
+
itemCount: workflow.items.length,
|
|
510
|
+
states: Object.fromEntries(
|
|
511
|
+
[...new Set(workflow.items.map((item) => item.state))]
|
|
512
|
+
.sort()
|
|
513
|
+
.map((state) => [state, workflow.items.filter((item) => item.state === state).length]),
|
|
514
|
+
),
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function projectWorkflow(workflow: WorkItemLedgerSnapshot): Record<string, unknown> {
|
|
519
|
+
const projected = boundedProjection(workflow.items, 64, (item) => ({
|
|
520
|
+
id: boundedPrivateText(item.id, 256),
|
|
521
|
+
state: item.state,
|
|
522
|
+
generation: item.generation,
|
|
523
|
+
taskGeneration: item.taskGeneration,
|
|
524
|
+
dependencies: item.dependencies.map((value) => boundedPrivateText(value, 256)),
|
|
525
|
+
assignedAgentId: item.assignedAgentId
|
|
526
|
+
? boundedPrivateText(item.assignedAgentId, 256)
|
|
527
|
+
: undefined,
|
|
528
|
+
acceptedExecutionPlanId: item.acceptedExecutionPlanId,
|
|
529
|
+
artifacts: item.artifacts.map((artifact) => ({
|
|
530
|
+
id: boundedPrivateText(artifact.id, 256),
|
|
531
|
+
kind: boundedPrivateText(artifact.kind, 256),
|
|
532
|
+
version: boundedPrivateText(artifact.version, 256),
|
|
533
|
+
producerTaskId: artifact.producerTaskId
|
|
534
|
+
? boundedPrivateText(artifact.producerTaskId, 256)
|
|
535
|
+
: undefined,
|
|
536
|
+
generation: artifact.generation,
|
|
537
|
+
verified: artifact.verified,
|
|
538
|
+
})),
|
|
539
|
+
verificationAccepted: item.verificationAccepted,
|
|
540
|
+
outcomeReason: item.outcomeReason
|
|
541
|
+
? boundedPrivateText(item.outcomeReason, 2 * 1024)
|
|
542
|
+
: undefined,
|
|
543
|
+
}));
|
|
544
|
+
return {
|
|
545
|
+
...projectWorkflowSummary(workflow),
|
|
546
|
+
items: projected.items,
|
|
547
|
+
omittedItems: projected.omitted,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
336
551
|
function projectModels(ctx: ExtensionContext, limit: number): Record<string, unknown> {
|
|
337
552
|
const scoped = ctx.scopedModels ?? [];
|
|
338
553
|
const candidates =
|
|
@@ -365,13 +580,34 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
|
|
|
365
580
|
const resources = inspectConsultResourceSettings();
|
|
366
581
|
const cwdPolicy = inspectCwdPolicySettings();
|
|
367
582
|
const completion = inspectCompletionDeliverySettings();
|
|
583
|
+
const parallelLimit = inspectBlockingParallelLimitSettings();
|
|
584
|
+
const detachedLimits = inspectStatefulLimitSettings();
|
|
585
|
+
const transport = inspectStatefulTransportSettings();
|
|
586
|
+
const configuredDetachedLimits = detachedLimits.values
|
|
587
|
+
? Object.fromEntries(
|
|
588
|
+
Object.entries(detachedLimits.values).map(([field, snapshot]) => [field, snapshot.value]),
|
|
589
|
+
)
|
|
590
|
+
: undefined;
|
|
591
|
+
const configuredDetachedLimitSources = detachedLimits.values
|
|
592
|
+
? Object.fromEntries(
|
|
593
|
+
Object.entries(detachedLimits.values).map(([field, snapshot]) => [field, snapshot.source]),
|
|
594
|
+
)
|
|
595
|
+
: undefined;
|
|
368
596
|
return {
|
|
369
597
|
workflow,
|
|
370
598
|
configuredWorkflow: configured.value,
|
|
371
599
|
configuredWorkflowSource: configured.source,
|
|
372
600
|
stateful,
|
|
601
|
+
statefulLimits: stateful.limits,
|
|
602
|
+
configuredTransport: transport.value,
|
|
603
|
+
configuredTransportSource: transport.source,
|
|
604
|
+
configuredStatefulLimits: configuredDetachedLimits,
|
|
605
|
+
configuredStatefulLimitSources: configuredDetachedLimitSources,
|
|
373
606
|
configuredCompletionDelivery: completion.value,
|
|
374
607
|
configuredCompletionDeliverySource: completion.source,
|
|
608
|
+
maxParallelTasks: runtime.getMaxParallelTasks(),
|
|
609
|
+
configuredMaxParallelTasks: parallelLimit.value,
|
|
610
|
+
configuredMaxParallelTasksSource: parallelLimit.source,
|
|
375
611
|
consultResources: runtime.getConsultResourcePolicy(),
|
|
376
612
|
consultationCwdPolicy: runtime.getConsultationCwdPolicy(),
|
|
377
613
|
configuredConsultationCwdPolicy: cwdPolicy.consultation.value,
|
|
@@ -383,15 +619,54 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
|
|
|
383
619
|
consultResourcesSource: resources.source,
|
|
384
620
|
settingsPath: safeDisplayPath(resources.path, process.cwd()),
|
|
385
621
|
settingsError:
|
|
386
|
-
configured.error ||
|
|
622
|
+
configured.error ||
|
|
623
|
+
resources.error ||
|
|
624
|
+
cwdPolicy.error ||
|
|
625
|
+
completion.error ||
|
|
626
|
+
parallelLimit.error ||
|
|
627
|
+
detachedLimits.error ||
|
|
628
|
+
transport.error
|
|
387
629
|
? boundedPrivateText(
|
|
388
|
-
configured.error ??
|
|
630
|
+
configured.error ??
|
|
631
|
+
resources.error ??
|
|
632
|
+
cwdPolicy.error ??
|
|
633
|
+
completion.error ??
|
|
634
|
+
parallelLimit.error ??
|
|
635
|
+
detachedLimits.error ??
|
|
636
|
+
transport.error ??
|
|
637
|
+
"",
|
|
389
638
|
2 * 1024,
|
|
390
639
|
)
|
|
391
640
|
: undefined,
|
|
392
641
|
};
|
|
393
642
|
}
|
|
394
643
|
|
|
644
|
+
async function inspectInProcessCapability(): Promise<{ error?: string }> {
|
|
645
|
+
try {
|
|
646
|
+
const moduleSpecifier = "@earendil-works/pi-coding-agent";
|
|
647
|
+
const core = await import(moduleSpecifier);
|
|
648
|
+
for (const name of [
|
|
649
|
+
"createAgentSessionServices",
|
|
650
|
+
"createAgentSessionFromServices",
|
|
651
|
+
"resolveCliModel",
|
|
652
|
+
] as const) {
|
|
653
|
+
if (typeof core[name] !== "function") return { error: `Pi core does not export ${name}()` };
|
|
654
|
+
}
|
|
655
|
+
return {};
|
|
656
|
+
} catch (error) {
|
|
657
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function inspectRpcCapability(): { error?: string } {
|
|
662
|
+
try {
|
|
663
|
+
resolvePiInvocation(["--mode", "rpc", "--no-session"]);
|
|
664
|
+
return {};
|
|
665
|
+
} catch (error) {
|
|
666
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
395
670
|
function availableModelCount(ctx: ExtensionContext): number {
|
|
396
671
|
return (ctx.scopedModels?.length ?? 0) > 0
|
|
397
672
|
? ctx.scopedModels.length
|
|
@@ -450,6 +725,24 @@ function requiredString(value: unknown, action: string, field: string): string {
|
|
|
450
725
|
return value;
|
|
451
726
|
}
|
|
452
727
|
|
|
728
|
+
function optionalContextMode(value: unknown): ContextMode {
|
|
729
|
+
if (value === undefined) return "none";
|
|
730
|
+
if (value === "none" || value === "all" || value === "summary") return value;
|
|
731
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 1) return value;
|
|
732
|
+
throw new Error("subagent_inspect context must be none, all, summary, or a positive integer");
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function optionalStringArray(value: unknown, field: string): string[] | undefined {
|
|
736
|
+
if (value === undefined) return undefined;
|
|
737
|
+
if (
|
|
738
|
+
!Array.isArray(value) ||
|
|
739
|
+
!value.every((item) => typeof item === "string" && item.length > 0)
|
|
740
|
+
) {
|
|
741
|
+
throw new Error(`subagent_inspect ${field} must be an array of non-empty strings`);
|
|
742
|
+
}
|
|
743
|
+
return [...value];
|
|
744
|
+
}
|
|
745
|
+
|
|
453
746
|
function optionalLimit(value: unknown, defaultValue: number): number {
|
|
454
747
|
if (value === undefined) return defaultValue;
|
|
455
748
|
if (!Number.isSafeInteger(value) || (value as number) < 1 || (value as number) > 100) {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
export interface ManagedIntegrationExpectation {
|
|
4
|
+
taskId: string;
|
|
5
|
+
taskGeneration: number;
|
|
6
|
+
baseRepositoryGeneration: string;
|
|
7
|
+
dependencyVersions: Record<string, string>;
|
|
8
|
+
readSetVersions: Record<string, string>;
|
|
9
|
+
executionPlanId: string;
|
|
10
|
+
allowedScopes: string[];
|
|
11
|
+
patchDigest: string;
|
|
12
|
+
requiredEvidence: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ManagedIntegrationCandidate extends ManagedIntegrationExpectation {
|
|
16
|
+
changedPaths: string[];
|
|
17
|
+
evidence: Record<string, string>;
|
|
18
|
+
verifier: {
|
|
19
|
+
freshContext: boolean;
|
|
20
|
+
exactIntegratedTree: boolean;
|
|
21
|
+
status: "accepted" | "rework" | "rejected";
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ManagedIntegrationAcceptance {
|
|
26
|
+
status: "accepted";
|
|
27
|
+
taskId: string;
|
|
28
|
+
taskGeneration: number;
|
|
29
|
+
patchDigest: string;
|
|
30
|
+
executionPlanId: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function verifyManagedIntegration(
|
|
34
|
+
expected: ManagedIntegrationExpectation,
|
|
35
|
+
candidate: ManagedIntegrationCandidate,
|
|
36
|
+
): ManagedIntegrationAcceptance {
|
|
37
|
+
if (
|
|
38
|
+
candidate.taskId !== expected.taskId ||
|
|
39
|
+
candidate.taskGeneration !== expected.taskGeneration
|
|
40
|
+
) {
|
|
41
|
+
throw new Error("Managed integration rejected stale task generation");
|
|
42
|
+
}
|
|
43
|
+
if (candidate.baseRepositoryGeneration !== expected.baseRepositoryGeneration) {
|
|
44
|
+
throw new Error("Managed integration rejected stale base repository generation");
|
|
45
|
+
}
|
|
46
|
+
if (candidate.executionPlanId !== expected.executionPlanId) {
|
|
47
|
+
throw new Error("Managed integration rejected stale execution plan identity");
|
|
48
|
+
}
|
|
49
|
+
if (!sameRecord(candidate.dependencyVersions, expected.dependencyVersions)) {
|
|
50
|
+
throw new Error("Managed integration rejected stale dependency versions");
|
|
51
|
+
}
|
|
52
|
+
if (!sameRecord(candidate.readSetVersions, expected.readSetVersions)) {
|
|
53
|
+
throw new Error("Managed integration rejected stale read-set versions");
|
|
54
|
+
}
|
|
55
|
+
if (candidate.patchDigest !== expected.patchDigest) {
|
|
56
|
+
throw new Error("Managed integration rejected patch digest mismatch");
|
|
57
|
+
}
|
|
58
|
+
const allowedScopes = expected.allowedScopes.map(normalizedPath);
|
|
59
|
+
if (
|
|
60
|
+
!candidate.changedPaths.every((changedPath) => {
|
|
61
|
+
const normalizedChangedPath = normalizedPath(changedPath);
|
|
62
|
+
return allowedScopes.some(
|
|
63
|
+
(scope) =>
|
|
64
|
+
normalizedChangedPath === scope ||
|
|
65
|
+
normalizedChangedPath.startsWith(`${scope}${path.sep}`),
|
|
66
|
+
);
|
|
67
|
+
})
|
|
68
|
+
) {
|
|
69
|
+
throw new Error("Managed integration rejected a path outside the accepted scope");
|
|
70
|
+
}
|
|
71
|
+
if (expected.requiredEvidence.some((id) => !candidate.evidence[id])) {
|
|
72
|
+
throw new Error("Managed integration rejected missing required evidence");
|
|
73
|
+
}
|
|
74
|
+
if (!candidate.verifier.freshContext || !candidate.verifier.exactIntegratedTree) {
|
|
75
|
+
throw new Error("Managed integration requires a fresh verifier on the exact integrated tree");
|
|
76
|
+
}
|
|
77
|
+
if (candidate.verifier.status !== "accepted") {
|
|
78
|
+
throw new Error(`Managed integration verifier returned ${candidate.verifier.status}`);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
status: "accepted",
|
|
82
|
+
taskId: expected.taskId,
|
|
83
|
+
taskGeneration: expected.taskGeneration,
|
|
84
|
+
patchDigest: expected.patchDigest,
|
|
85
|
+
executionPlanId: expected.executionPlanId,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizedPath(value: string): string {
|
|
90
|
+
if (!value || value.includes("\0")) throw new Error("Managed integration scope is invalid");
|
|
91
|
+
return path.resolve(path.sep, value.replaceAll("\\", path.sep));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function sameRecord(left: Record<string, string>, right: Record<string, string>): boolean {
|
|
95
|
+
const leftEntries = Object.entries(left).sort(([a], [b]) => a.localeCompare(b));
|
|
96
|
+
const rightEntries = Object.entries(right).sort(([a], [b]) => a.localeCompare(b));
|
|
97
|
+
return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
|
|
98
|
+
}
|
package/src/limits.ts
CHANGED
|
@@ -4,6 +4,9 @@ export const DEFAULT_MAX_OUTPUT_BYTES = DEFAULT_MAX_BYTES;
|
|
|
4
4
|
export const DEFAULT_MAX_STDERR_BYTES = 16 * 1024;
|
|
5
5
|
export const DEFAULT_MAX_CONTEXT_BYTES = DEFAULT_MAX_BYTES;
|
|
6
6
|
export const MAX_SUBAGENT_TIMEOUT_MS = 2_147_483_647;
|
|
7
|
+
export const DEFAULT_MAX_PARALLEL_TASKS = 8;
|
|
8
|
+
export const MAX_CONFIGURABLE_PARALLEL_TASKS = 64;
|
|
9
|
+
export const MAX_BLOCKING_PARALLEL_CONCURRENCY = 4;
|
|
7
10
|
export const DEFAULT_MAX_MESSAGES = 200;
|
|
8
11
|
export const TRUNCATION_MARKER = "\n… [truncated by pi-subagents]";
|
|
9
12
|
export const TAIL_TRUNCATION_MARKER = "… [truncated by pi-subagents]\n";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { SingleResult } from "./runner.js";
|
|
2
|
+
import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
|
|
3
|
+
|
|
4
|
+
export interface OrchestrationMetrics {
|
|
5
|
+
workItems: number;
|
|
6
|
+
completed: number;
|
|
7
|
+
failedOrBlocked: number;
|
|
8
|
+
invalidated: number;
|
|
9
|
+
requiredTransfers: number;
|
|
10
|
+
resolvedTransfers: number;
|
|
11
|
+
transferCoverage: number;
|
|
12
|
+
attempts: number;
|
|
13
|
+
hedgedTasks: number;
|
|
14
|
+
requestedTools: number;
|
|
15
|
+
effectiveRequestedTools: number;
|
|
16
|
+
permissionPrecision: number;
|
|
17
|
+
panelValidReviews?: number;
|
|
18
|
+
panelFailedReviews?: number;
|
|
19
|
+
panelBlockingObjections?: number;
|
|
20
|
+
panelDissent?: number;
|
|
21
|
+
panelSynthesisState?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function calculateOrchestrationMetrics(
|
|
25
|
+
workflow: WorkItemLedgerSnapshot | undefined,
|
|
26
|
+
results: SingleResult[],
|
|
27
|
+
panel?: {
|
|
28
|
+
validReviewCount: number;
|
|
29
|
+
failedReviewCount: number;
|
|
30
|
+
blockingObjectionCount: number;
|
|
31
|
+
dissentCount: number;
|
|
32
|
+
state: string;
|
|
33
|
+
},
|
|
34
|
+
): OrchestrationMetrics {
|
|
35
|
+
const items = workflow?.items ?? [];
|
|
36
|
+
const requiredTransfers = items.reduce((sum, item) => sum + item.inputArtifacts.length, 0);
|
|
37
|
+
const resolvedTransfers = items.reduce(
|
|
38
|
+
(sum, item) => sum + Object.keys(item.inputArtifactVersions).length,
|
|
39
|
+
0,
|
|
40
|
+
);
|
|
41
|
+
const requestedTools = results.reduce(
|
|
42
|
+
(sum, result) => sum + (result.executionPlan?.requestedTools.length ?? 0),
|
|
43
|
+
0,
|
|
44
|
+
);
|
|
45
|
+
const effectiveRequestedTools = results.reduce((sum, result) => {
|
|
46
|
+
const plan = result.executionPlan;
|
|
47
|
+
if (!plan) return sum;
|
|
48
|
+
return (
|
|
49
|
+
sum +
|
|
50
|
+
plan.requestedTools.filter((tool) => plan.effectiveTools?.includes(tool) === true).length
|
|
51
|
+
);
|
|
52
|
+
}, 0);
|
|
53
|
+
return {
|
|
54
|
+
workItems: items.length,
|
|
55
|
+
completed: items.filter((item) => item.state === "completed").length,
|
|
56
|
+
failedOrBlocked: items.filter((item) =>
|
|
57
|
+
["failed", "blocked", "needs-input", "interrupted"].includes(item.state),
|
|
58
|
+
).length,
|
|
59
|
+
invalidated: items.filter((item) => ["stale", "invalidated"].includes(item.state)).length,
|
|
60
|
+
requiredTransfers,
|
|
61
|
+
resolvedTransfers,
|
|
62
|
+
transferCoverage: requiredTransfers === 0 ? 1 : resolvedTransfers / requiredTransfers,
|
|
63
|
+
attempts: results.reduce((sum, result) => sum + (result.attemptCount ?? 1), 0),
|
|
64
|
+
hedgedTasks: results.filter((result) => result.hedged).length,
|
|
65
|
+
requestedTools,
|
|
66
|
+
effectiveRequestedTools,
|
|
67
|
+
permissionPrecision: requestedTools === 0 ? 1 : effectiveRequestedTools / requestedTools,
|
|
68
|
+
...(panel
|
|
69
|
+
? {
|
|
70
|
+
panelValidReviews: panel.validReviewCount,
|
|
71
|
+
panelFailedReviews: panel.failedReviewCount,
|
|
72
|
+
panelBlockingObjections: panel.blockingObjectionCount,
|
|
73
|
+
panelDissent: panel.dissentCount,
|
|
74
|
+
panelSynthesisState: panel.state,
|
|
75
|
+
}
|
|
76
|
+
: {}),
|
|
77
|
+
};
|
|
78
|
+
}
|