@narumitw/pi-subagents 0.49.3 → 0.52.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 +362 -53
- package/package.json +10 -7
- package/src/adaptive-scheduler.ts +224 -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 +1098 -158
- package/src/in-process-transport.ts +269 -25
- package/src/inspect-render.ts +101 -1
- package/src/inspect.ts +321 -3
- package/src/integration-controller.ts +98 -0
- package/src/limits.ts +3 -0
- package/src/orchestration-metrics.ts +109 -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 +770 -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 +179 -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 +67 -0
- package/src/work-item-ledger.ts +931 -0
- package/src/work-item-persistence.ts +223 -0
- package/src/workflow-planning.ts +162 -0
- package/src/workflow-tree-identity.ts +289 -0
- package/src/workflow-ui.ts +61 -0
- package/src/workflow-verification.ts +296 -0
- package/src/workspace.ts +69 -12
package/src/registry.ts
CHANGED
|
@@ -1,119 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentRegistry intentionally owns its full state machine in one module so queue, tree,
|
|
3
|
+
* mailbox, generation, grant, transport, persistence, and completion transitions are atomic.
|
|
4
|
+
*/
|
|
1
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { projectAgentRecords } from "./agent-projection.js";
|
|
2
7
|
import type { SubagentThinkingLevel } from "./agents.js";
|
|
8
|
+
import {
|
|
9
|
+
type CapabilityGrant,
|
|
10
|
+
isCapabilityGrantActive,
|
|
11
|
+
revokeCapabilityGrant,
|
|
12
|
+
} from "./capability-grant.js";
|
|
3
13
|
import type { TargetPolicyAudit } from "./cwd-policy.js";
|
|
4
|
-
import {
|
|
14
|
+
import type { DelegationContract } from "./delegation-contract.js";
|
|
15
|
+
import {
|
|
16
|
+
copyExecutionPlan,
|
|
17
|
+
type ExecutionPlan,
|
|
18
|
+
rotateExecutionPlanGeneration,
|
|
19
|
+
} from "./execution-plan.js";
|
|
20
|
+
import {
|
|
21
|
+
DEFAULT_MAX_CONTEXT_BYTES,
|
|
22
|
+
DEFAULT_MAX_OUTPUT_BYTES,
|
|
23
|
+
MAX_SUBAGENT_TIMEOUT_MS,
|
|
24
|
+
truncateUtf8,
|
|
25
|
+
} from "./limits.js";
|
|
26
|
+
import { classifyStructuredOutcome } from "./outcome.js";
|
|
27
|
+
import type {
|
|
28
|
+
AgentInspectionCounts,
|
|
29
|
+
AgentLifecycleState,
|
|
30
|
+
AgentMailboxMessage,
|
|
31
|
+
AgentRegistryOptions,
|
|
32
|
+
AgentRunInspectionDetail,
|
|
33
|
+
AgentRunInspectionSummary,
|
|
34
|
+
AgentTurnCompletion,
|
|
35
|
+
ManagedAgent,
|
|
36
|
+
} from "./registry-types.js";
|
|
37
|
+
import {
|
|
38
|
+
type AnyStructuredSubagentResult,
|
|
39
|
+
parseAnyStructuredSubagentResult,
|
|
40
|
+
type SubagentResultFormat,
|
|
41
|
+
} from "./result-contract.js";
|
|
42
|
+
import type { SemanticCompatibility, SemanticSnapshot } from "./semantic-snapshot.js";
|
|
43
|
+
import { resolveStatefulLimits } from "./stateful-limits.js";
|
|
44
|
+
import { copyTurnTerminationReport } from "./timeout-checkpoint.js";
|
|
5
45
|
import { type AgentTurnRunner, normalizeTransport, type SubagentTransport } from "./transport.js";
|
|
46
|
+
import type { TransportTelemetry } from "./transport-types.js";
|
|
47
|
+
import { type TurnLimits, validateTurnLimits } from "./turn-budget.js";
|
|
6
48
|
|
|
7
|
-
|
|
8
|
-
| "starting"
|
|
9
|
-
| "running"
|
|
10
|
-
| "idle"
|
|
11
|
-
| "completed"
|
|
12
|
-
| "interrupted"
|
|
13
|
-
| "failed"
|
|
14
|
-
| "closed";
|
|
15
|
-
|
|
16
|
-
export interface AgentTurn {
|
|
17
|
-
task: string;
|
|
18
|
-
output: string;
|
|
19
|
-
startedAt: number;
|
|
20
|
-
completedAt: number;
|
|
21
|
-
exitCode: number;
|
|
22
|
-
truncated?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface AgentMailboxMessage {
|
|
26
|
-
id: string;
|
|
27
|
-
senderId: string;
|
|
28
|
-
recipientId: string;
|
|
29
|
-
content: string;
|
|
30
|
-
createdAt: number;
|
|
31
|
-
readAt?: number;
|
|
32
|
-
deduplicationKey?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface ManagedAgent {
|
|
36
|
-
id: string;
|
|
37
|
-
agent: string;
|
|
38
|
-
parentId?: string;
|
|
39
|
-
rootId: string;
|
|
40
|
-
depth: number;
|
|
41
|
-
children: string[];
|
|
42
|
-
state: AgentLifecycleState;
|
|
43
|
-
createdAt: number;
|
|
44
|
-
updatedAt: number;
|
|
45
|
-
cwd: string;
|
|
46
|
-
agentScope?: "user" | "project" | "both";
|
|
47
|
-
thinkingLevel?: SubagentThinkingLevel;
|
|
48
|
-
currentTask?: string;
|
|
49
|
-
history: AgentTurn[];
|
|
50
|
-
error?: string;
|
|
51
|
-
context?: string;
|
|
52
|
-
contextSourceIds?: string[];
|
|
53
|
-
contextTruncated?: boolean;
|
|
54
|
-
workspaceMode?: "worktree";
|
|
55
|
-
target?: TargetPolicyAudit;
|
|
56
|
-
policy?: { inherited: string[]; overridden: string[]; unsupported: string[] };
|
|
57
|
-
mailbox: AgentMailboxMessage[];
|
|
58
|
-
currentMailboxMessageIds?: string[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface AgentRunInspectionSummary {
|
|
62
|
-
id: string;
|
|
63
|
-
agent: string;
|
|
64
|
-
state: AgentLifecycleState;
|
|
65
|
-
createdAt: number;
|
|
66
|
-
updatedAt: number;
|
|
67
|
-
historyCount: number;
|
|
68
|
-
unreadMessages: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface AgentRunInspectionDetail extends AgentRunInspectionSummary {
|
|
72
|
-
cwd: string;
|
|
73
|
-
thinkingLevel?: SubagentThinkingLevel;
|
|
74
|
-
currentTask?: string;
|
|
75
|
-
error?: string;
|
|
76
|
-
workspaceMode?: "worktree";
|
|
77
|
-
target?: TargetPolicyAudit;
|
|
78
|
-
policy?: { inherited: string[]; overridden: string[]; unsupported: string[] };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface AgentInspectionCounts {
|
|
82
|
-
activeAgents: number;
|
|
83
|
-
retainedAgents: number;
|
|
84
|
-
}
|
|
49
|
+
const DEFAULT_STATEFUL_LIMITS = resolveStatefulLimits();
|
|
85
50
|
|
|
86
|
-
export
|
|
87
|
-
output: string;
|
|
88
|
-
exitCode: number;
|
|
89
|
-
aborted?: boolean;
|
|
90
|
-
truncated?: boolean;
|
|
91
|
-
error?: string;
|
|
92
|
-
policy?: ManagedAgent["policy"];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface AgentTurnCompletion {
|
|
96
|
-
agent: ManagedAgent;
|
|
97
|
-
task: string;
|
|
98
|
-
output: string;
|
|
99
|
-
error?: string;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface AgentRegistryOptions {
|
|
103
|
-
maxAgents?: number;
|
|
104
|
-
maxActiveTurns?: number;
|
|
105
|
-
maxHistoryTurns?: number;
|
|
106
|
-
maxDepth?: number;
|
|
107
|
-
maxChildrenPerAgent?: number;
|
|
108
|
-
maxMailboxMessages?: number;
|
|
109
|
-
maxMailboxMessageBytes?: number;
|
|
110
|
-
maxTaskBytes?: number;
|
|
111
|
-
maxTurnOutputBytes?: number;
|
|
112
|
-
idleTtlMs?: number;
|
|
113
|
-
now?: () => number;
|
|
114
|
-
onChange?: (agents: ManagedAgent[]) => void | Promise<void>;
|
|
115
|
-
onTurnComplete?: (completion: AgentTurnCompletion) => void | Promise<void>;
|
|
116
|
-
}
|
|
51
|
+
export type * from "./registry-types.js";
|
|
117
52
|
|
|
118
53
|
function positiveInteger(value: number, label: string): number {
|
|
119
54
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
@@ -129,6 +64,22 @@ function nonNegativeInteger(value: number, label: string): number {
|
|
|
129
64
|
return value;
|
|
130
65
|
}
|
|
131
66
|
|
|
67
|
+
function validateTurnTimeout(value: number): number {
|
|
68
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > MAX_SUBAGENT_TIMEOUT_MS) {
|
|
69
|
+
throw new Error(`Subagent timeoutMs must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}`);
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clearCurrentTurn(agent: ManagedAgent): void {
|
|
75
|
+
agent.currentTask = undefined;
|
|
76
|
+
agent.currentTimeoutMs = undefined;
|
|
77
|
+
agent.currentIdleTimeoutMs = undefined;
|
|
78
|
+
agent.currentMaxTurns = undefined;
|
|
79
|
+
agent.currentMaxToolCalls = undefined;
|
|
80
|
+
agent.currentMailboxMessageIds = undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
132
83
|
function waitAbortError(): Error {
|
|
133
84
|
const error = new Error("Subagent wait was aborted");
|
|
134
85
|
error.name = "AbortError";
|
|
@@ -163,12 +114,21 @@ export class AgentRegistry {
|
|
|
163
114
|
private readonly options: AgentRegistryOptions = {},
|
|
164
115
|
) {
|
|
165
116
|
this.transport = normalizeTransport(transport);
|
|
166
|
-
this.maxAgents = positiveInteger(
|
|
167
|
-
|
|
117
|
+
this.maxAgents = positiveInteger(
|
|
118
|
+
options.maxAgents ?? DEFAULT_STATEFUL_LIMITS.maxAgents,
|
|
119
|
+
"maxAgents",
|
|
120
|
+
);
|
|
121
|
+
this.maxActiveTurns = positiveInteger(
|
|
122
|
+
options.maxActiveTurns ?? DEFAULT_STATEFUL_LIMITS.maxActiveTurns,
|
|
123
|
+
"maxActiveTurns",
|
|
124
|
+
);
|
|
168
125
|
this.maxHistoryTurns = positiveInteger(options.maxHistoryTurns ?? 20, "maxHistoryTurns");
|
|
169
|
-
this.maxDepth = nonNegativeInteger(
|
|
126
|
+
this.maxDepth = nonNegativeInteger(
|
|
127
|
+
options.maxDepth ?? DEFAULT_STATEFUL_LIMITS.maxDepth,
|
|
128
|
+
"maxDepth",
|
|
129
|
+
);
|
|
170
130
|
this.maxChildrenPerAgent = positiveInteger(
|
|
171
|
-
options.maxChildrenPerAgent ??
|
|
131
|
+
options.maxChildrenPerAgent ?? DEFAULT_STATEFUL_LIMITS.maxChildrenPerAgent,
|
|
172
132
|
"maxChildrenPerAgent",
|
|
173
133
|
);
|
|
174
134
|
this.maxMailboxMessages = positiveInteger(
|
|
@@ -193,10 +153,10 @@ export class AgentRegistry {
|
|
|
193
153
|
|
|
194
154
|
restore(records: readonly ManagedAgent[]): void {
|
|
195
155
|
const candidates = new Map(
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
|
|
156
|
+
projectAgentRecords(
|
|
157
|
+
records.filter((record) => record.id && record.state !== "closed"),
|
|
158
|
+
{ maxAgents: this.maxAgents, maxDepth: this.maxDepth },
|
|
159
|
+
).map((record) => [record.id, record]),
|
|
200
160
|
);
|
|
201
161
|
for (const record of candidates.values()) {
|
|
202
162
|
if (record.parentId && !candidates.has(record.parentId)) continue;
|
|
@@ -218,13 +178,22 @@ export class AgentRegistry {
|
|
|
218
178
|
if (cyclic || depth > this.maxDepth) continue;
|
|
219
179
|
this.agents.set(record.id, {
|
|
220
180
|
...record,
|
|
221
|
-
state:
|
|
181
|
+
state:
|
|
182
|
+
record.state === "running" || record.state === "starting" ? "interrupted" : record.state,
|
|
222
183
|
rootId,
|
|
223
184
|
depth,
|
|
224
185
|
currentTask: undefined,
|
|
186
|
+
currentTimeoutMs: undefined,
|
|
187
|
+
currentIdleTimeoutMs: undefined,
|
|
188
|
+
currentMaxTurns: undefined,
|
|
189
|
+
currentMaxToolCalls: undefined,
|
|
225
190
|
currentMailboxMessageIds: undefined,
|
|
226
191
|
children: [],
|
|
227
192
|
contextSourceIds: [...(record.contextSourceIds ?? [])],
|
|
193
|
+
capabilityGrant:
|
|
194
|
+
record.capabilityGrant?.state === "active"
|
|
195
|
+
? revokeCapabilityGrant(record.capabilityGrant, "restore-boundary", this.now())
|
|
196
|
+
: record.capabilityGrant,
|
|
228
197
|
mailbox: (record.mailbox ?? [])
|
|
229
198
|
.slice(-this.maxMailboxMessages)
|
|
230
199
|
.map((message) => ({ ...message, recipientId: record.id })),
|
|
@@ -244,14 +213,35 @@ export class AgentRegistry {
|
|
|
244
213
|
cwd: string;
|
|
245
214
|
agentScope?: "user" | "project" | "both";
|
|
246
215
|
thinkingLevel?: SubagentThinkingLevel;
|
|
216
|
+
timeoutMs?: number;
|
|
217
|
+
idleTimeoutMs?: number;
|
|
218
|
+
maxTurns?: number;
|
|
219
|
+
maxToolCalls?: number;
|
|
247
220
|
parentId?: string;
|
|
248
221
|
context?: string;
|
|
249
222
|
contextSourceIds?: string[];
|
|
250
223
|
contextTruncated?: boolean;
|
|
224
|
+
contextTurns?: number;
|
|
225
|
+
contextBytes?: number;
|
|
251
226
|
workspaceMode?: "worktree";
|
|
227
|
+
spawnIdempotencyKey?: string;
|
|
228
|
+
spawnRequestHash?: string;
|
|
229
|
+
contract?: DelegationContract;
|
|
230
|
+
resultFormat?: SubagentResultFormat;
|
|
231
|
+
executionPlan?: ExecutionPlan;
|
|
232
|
+
capabilityGrant?: CapabilityGrant;
|
|
233
|
+
semanticSnapshot?: SemanticSnapshot;
|
|
234
|
+
semanticCompatibility?: SemanticCompatibility;
|
|
252
235
|
target?: TargetPolicyAudit;
|
|
253
236
|
}): Promise<ManagedAgent> {
|
|
254
237
|
if (!input.task.trim()) throw new Error("Subagent tasks cannot be empty");
|
|
238
|
+
if (input.timeoutMs !== undefined) validateTurnTimeout(input.timeoutMs);
|
|
239
|
+
validateTurnLimits(input);
|
|
240
|
+
const existing = this.findBySpawnIdempotencyKey(
|
|
241
|
+
input.spawnIdempotencyKey,
|
|
242
|
+
input.spawnRequestHash,
|
|
243
|
+
);
|
|
244
|
+
if (existing) return existing;
|
|
255
245
|
const task = truncateUtf8(input.task, this.maxTaskBytes).text;
|
|
256
246
|
const expired = this.evictExpired();
|
|
257
247
|
let expiryReleaseError: unknown;
|
|
@@ -287,13 +277,31 @@ export class AgentRegistry {
|
|
|
287
277
|
cwd: input.cwd,
|
|
288
278
|
agentScope: input.agentScope,
|
|
289
279
|
thinkingLevel: input.thinkingLevel,
|
|
280
|
+
timeoutMs: input.timeoutMs,
|
|
281
|
+
currentTimeoutMs: input.timeoutMs,
|
|
282
|
+
idleTimeoutMs: input.idleTimeoutMs,
|
|
283
|
+
currentIdleTimeoutMs: input.idleTimeoutMs,
|
|
284
|
+
maxTurns: input.maxTurns,
|
|
285
|
+
currentMaxTurns: input.maxTurns,
|
|
286
|
+
maxToolCalls: input.maxToolCalls,
|
|
287
|
+
currentMaxToolCalls: input.maxToolCalls,
|
|
290
288
|
currentTask: task,
|
|
291
289
|
history: [],
|
|
292
290
|
mailbox: [],
|
|
293
291
|
context: input.context,
|
|
294
292
|
contextSourceIds: input.contextSourceIds,
|
|
295
293
|
contextTruncated: input.contextTruncated,
|
|
294
|
+
contextTurns: input.contextTurns,
|
|
295
|
+
contextBytes: input.contextBytes,
|
|
296
296
|
workspaceMode: input.workspaceMode,
|
|
297
|
+
spawnIdempotencyKey: input.spawnIdempotencyKey,
|
|
298
|
+
spawnRequestHash: input.spawnRequestHash,
|
|
299
|
+
contract: input.contract,
|
|
300
|
+
resultFormat: input.resultFormat,
|
|
301
|
+
executionPlan: input.executionPlan,
|
|
302
|
+
capabilityGrant: input.capabilityGrant,
|
|
303
|
+
semanticSnapshot: input.semanticSnapshot,
|
|
304
|
+
semanticCompatibility: input.semanticCompatibility,
|
|
297
305
|
target: input.target,
|
|
298
306
|
};
|
|
299
307
|
this.agents.set(record.id, record);
|
|
@@ -302,22 +310,76 @@ export class AgentRegistry {
|
|
|
302
310
|
parent.updatedAt = now;
|
|
303
311
|
}
|
|
304
312
|
await this.changed();
|
|
305
|
-
this.startTurn(record, task);
|
|
313
|
+
this.startTurn(record, task, input);
|
|
306
314
|
return this.copy(record);
|
|
307
315
|
}
|
|
308
316
|
|
|
309
|
-
|
|
317
|
+
findBySpawnIdempotencyKey(
|
|
318
|
+
key: string | undefined,
|
|
319
|
+
requestHash: string | undefined,
|
|
320
|
+
): ManagedAgent | undefined {
|
|
321
|
+
if (!key) return undefined;
|
|
322
|
+
const existing = [...this.agents.values()].find(
|
|
323
|
+
(agent) => agent.state !== "closed" && agent.spawnIdempotencyKey === key,
|
|
324
|
+
);
|
|
325
|
+
if (!existing) return undefined;
|
|
326
|
+
if (!requestHash || existing.spawnRequestHash !== requestHash) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
"The subagent_spawn idempotencyKey was already used with different parameters",
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return this.copy(existing);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async followUp(
|
|
335
|
+
id: string,
|
|
336
|
+
task: string,
|
|
337
|
+
options: TurnLimits & { timeoutMs?: number } = {},
|
|
338
|
+
): Promise<ManagedAgent> {
|
|
310
339
|
if (!task.trim()) throw new Error("Subagent tasks cannot be empty");
|
|
340
|
+
if (options.timeoutMs !== undefined) validateTurnTimeout(options.timeoutMs);
|
|
341
|
+
validateTurnLimits(options);
|
|
311
342
|
const boundedTask = truncateUtf8(task, this.maxTaskBytes).text;
|
|
312
343
|
const agent = this.require(id);
|
|
313
|
-
if (
|
|
344
|
+
if (
|
|
345
|
+
![
|
|
346
|
+
"idle",
|
|
347
|
+
"completed",
|
|
348
|
+
"blocked",
|
|
349
|
+
"needs-input",
|
|
350
|
+
"abstained",
|
|
351
|
+
"stale",
|
|
352
|
+
"interrupted",
|
|
353
|
+
"failed",
|
|
354
|
+
].includes(agent.state)
|
|
355
|
+
) {
|
|
314
356
|
throw new Error(`Agent ${id} cannot accept follow-up while ${agent.state}`);
|
|
315
357
|
}
|
|
316
358
|
const unread = agent.mailbox.filter((message) => !message.readAt);
|
|
317
359
|
const readAt = this.now();
|
|
318
360
|
for (const message of unread) message.readAt = readAt;
|
|
319
361
|
agent.currentMailboxMessageIds = unread.map((message) => message.id);
|
|
320
|
-
this.startTurn(agent, boundedTask);
|
|
362
|
+
this.startTurn(agent, boundedTask, options);
|
|
363
|
+
return this.copy(agent);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async updateSemanticState(
|
|
367
|
+
id: string,
|
|
368
|
+
executionPlan: ExecutionPlan,
|
|
369
|
+
capabilityGrant: CapabilityGrant,
|
|
370
|
+
snapshot: SemanticSnapshot,
|
|
371
|
+
compatibility: SemanticCompatibility,
|
|
372
|
+
): Promise<ManagedAgent> {
|
|
373
|
+
const agent = this.require(id);
|
|
374
|
+
if (agent.state === "running" || agent.state === "starting" || agent.state === "closed") {
|
|
375
|
+
throw new Error(`Agent ${id} cannot update semantic state while ${agent.state}`);
|
|
376
|
+
}
|
|
377
|
+
agent.executionPlan = copyExecutionPlan(executionPlan);
|
|
378
|
+
agent.capabilityGrant = structuredClone(capabilityGrant);
|
|
379
|
+
agent.semanticSnapshot = structuredClone(snapshot);
|
|
380
|
+
agent.semanticCompatibility = structuredClone(compatibility);
|
|
381
|
+
agent.updatedAt = this.now();
|
|
382
|
+
await this.changed();
|
|
321
383
|
return this.copy(agent);
|
|
322
384
|
}
|
|
323
385
|
|
|
@@ -410,13 +472,22 @@ export class AgentRegistry {
|
|
|
410
472
|
const agent = this.require(id);
|
|
411
473
|
if (agent.state !== "running" && agent.state !== "starting")
|
|
412
474
|
throw new Error(`Agent ${id} is not running`);
|
|
475
|
+
if (agent.capabilityGrant?.state === "active") {
|
|
476
|
+
agent.capabilityGrant = revokeCapabilityGrant(
|
|
477
|
+
agent.capabilityGrant,
|
|
478
|
+
"interrupted",
|
|
479
|
+
this.now(),
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
if (agent.executionPlan) {
|
|
483
|
+
agent.executionPlan = rotateExecutionPlanGeneration(agent.executionPlan);
|
|
484
|
+
}
|
|
413
485
|
if (agent.state === "starting") {
|
|
414
486
|
const index = this.queue.findIndex((entry) => entry.agent.id === id);
|
|
415
487
|
if (index >= 0) {
|
|
416
488
|
const [entry] = this.queue.splice(index, 1);
|
|
417
489
|
agent.state = "interrupted";
|
|
418
|
-
agent
|
|
419
|
-
agent.currentMailboxMessageIds = undefined;
|
|
490
|
+
clearCurrentTurn(agent);
|
|
420
491
|
agent.updatedAt = this.now();
|
|
421
492
|
const completion: AgentTurnCompletion = {
|
|
422
493
|
agent: this.copy(agent),
|
|
@@ -462,6 +533,14 @@ export class AgentRegistry {
|
|
|
462
533
|
if (agent.children.some((childId) => this.agents.get(childId)?.state !== "closed")) {
|
|
463
534
|
throw new Error(`Agent ${id} has active descendants; close the subtree instead`);
|
|
464
535
|
}
|
|
536
|
+
if (agent.state === "starting" || agent.state === "running") {
|
|
537
|
+
if (agent.capabilityGrant?.state === "active") {
|
|
538
|
+
agent.capabilityGrant = revokeCapabilityGrant(agent.capabilityGrant, "closed", this.now());
|
|
539
|
+
}
|
|
540
|
+
if (agent.executionPlan) {
|
|
541
|
+
agent.executionPlan = rotateExecutionPlanGeneration(agent.executionPlan);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
465
544
|
if (agent.state === "starting") {
|
|
466
545
|
const index = this.queue.findIndex((entry) => entry.agent.id === id);
|
|
467
546
|
if (index >= 0) {
|
|
@@ -478,8 +557,7 @@ export class AgentRegistry {
|
|
|
478
557
|
const parent = this.agents.get(agent.parentId);
|
|
479
558
|
if (parent) parent.children = parent.children.filter((childId) => childId !== id);
|
|
480
559
|
}
|
|
481
|
-
agent
|
|
482
|
-
agent.currentMailboxMessageIds = undefined;
|
|
560
|
+
clearCurrentTurn(agent);
|
|
483
561
|
let releaseError: unknown;
|
|
484
562
|
try {
|
|
485
563
|
await this.transport.release?.(this.copy(agent));
|
|
@@ -507,19 +585,42 @@ export class AgentRegistry {
|
|
|
507
585
|
|
|
508
586
|
async shutdown(): Promise<void> {
|
|
509
587
|
for (const entry of this.queue.splice(0)) {
|
|
510
|
-
entry.agent.state
|
|
511
|
-
|
|
512
|
-
|
|
588
|
+
if (entry.agent.capabilityGrant?.state === "active") {
|
|
589
|
+
entry.agent.capabilityGrant = revokeCapabilityGrant(
|
|
590
|
+
entry.agent.capabilityGrant,
|
|
591
|
+
"shutdown",
|
|
592
|
+
this.now(),
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
if (entry.agent.executionPlan) {
|
|
596
|
+
entry.agent.executionPlan = rotateExecutionPlanGeneration(entry.agent.executionPlan);
|
|
597
|
+
}
|
|
598
|
+
entry.agent.state = "interrupted";
|
|
599
|
+
clearCurrentTurn(entry.agent);
|
|
513
600
|
entry.resolve(entry.agent);
|
|
514
601
|
this.running.delete(entry.agent.id);
|
|
515
602
|
}
|
|
603
|
+
for (const id of this.controllers.keys()) {
|
|
604
|
+
const agent = this.agents.get(id);
|
|
605
|
+
if (agent?.capabilityGrant?.state === "active") {
|
|
606
|
+
agent.capabilityGrant = revokeCapabilityGrant(
|
|
607
|
+
agent.capabilityGrant,
|
|
608
|
+
"shutdown",
|
|
609
|
+
this.now(),
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
if (agent?.executionPlan) {
|
|
613
|
+
agent.executionPlan = rotateExecutionPlanGeneration(agent.executionPlan);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
516
616
|
for (const controller of this.controllers.values()) controller.abort();
|
|
517
617
|
await Promise.all([...this.running.values()].map((turn) => turn.catch(() => undefined)));
|
|
518
618
|
for (const agent of this.agents.values()) {
|
|
519
619
|
if (agent.state !== "closed") {
|
|
520
|
-
agent.state
|
|
521
|
-
|
|
522
|
-
|
|
620
|
+
if (agent.state === "running" || agent.state === "starting") {
|
|
621
|
+
agent.state = "interrupted";
|
|
622
|
+
}
|
|
623
|
+
clearCurrentTurn(agent);
|
|
523
624
|
}
|
|
524
625
|
}
|
|
525
626
|
let shutdownError: unknown;
|
|
@@ -556,9 +657,23 @@ export class AgentRegistry {
|
|
|
556
657
|
...this.inspectSummary(agent),
|
|
557
658
|
cwd: agent.cwd,
|
|
558
659
|
thinkingLevel: agent.thinkingLevel,
|
|
660
|
+
timeoutMs: agent.timeoutMs,
|
|
661
|
+
currentTimeoutMs: agent.currentTimeoutMs,
|
|
662
|
+
idleTimeoutMs: agent.idleTimeoutMs,
|
|
663
|
+
currentIdleTimeoutMs: agent.currentIdleTimeoutMs,
|
|
664
|
+
maxTurns: agent.maxTurns,
|
|
665
|
+
currentMaxTurns: agent.currentMaxTurns,
|
|
666
|
+
maxToolCalls: agent.maxToolCalls,
|
|
667
|
+
currentMaxToolCalls: agent.currentMaxToolCalls,
|
|
559
668
|
currentTask: agent.currentTask,
|
|
560
669
|
error: agent.error,
|
|
561
670
|
workspaceMode: agent.workspaceMode,
|
|
671
|
+
contextTurns: agent.contextTurns,
|
|
672
|
+
contextBytes: agent.contextBytes,
|
|
673
|
+
contextSources: agent.contextSourceIds?.length,
|
|
674
|
+
contextTruncated: agent.contextTruncated,
|
|
675
|
+
contract: agent.contract ? structuredClone(agent.contract) : undefined,
|
|
676
|
+
resultFormat: agent.resultFormat,
|
|
562
677
|
target: agent.target ? { ...agent.target, trust: { ...agent.target.trust } } : undefined,
|
|
563
678
|
policy: agent.policy
|
|
564
679
|
? {
|
|
@@ -567,6 +682,20 @@ export class AgentRegistry {
|
|
|
567
682
|
unsupported: [...agent.policy.unsupported],
|
|
568
683
|
}
|
|
569
684
|
: undefined,
|
|
685
|
+
structuredResult: agent.structuredResult
|
|
686
|
+
? copyStructuredResult(agent.structuredResult)
|
|
687
|
+
: undefined,
|
|
688
|
+
termination: agent.termination ? copyTurnTerminationReport(agent.termination) : undefined,
|
|
689
|
+
outcome: agent.outcome ? structuredClone(agent.outcome) : undefined,
|
|
690
|
+
executionPlan: agent.executionPlan ? copyExecutionPlan(agent.executionPlan) : undefined,
|
|
691
|
+
capabilityGrant: agent.capabilityGrant ? structuredClone(agent.capabilityGrant) : undefined,
|
|
692
|
+
semanticSnapshot: agent.semanticSnapshot
|
|
693
|
+
? structuredClone(agent.semanticSnapshot)
|
|
694
|
+
: undefined,
|
|
695
|
+
semanticCompatibility: agent.semanticCompatibility
|
|
696
|
+
? structuredClone(agent.semanticCompatibility)
|
|
697
|
+
: undefined,
|
|
698
|
+
telemetry: agent.telemetry ? copyTelemetry(agent.telemetry) : undefined,
|
|
570
699
|
};
|
|
571
700
|
}
|
|
572
701
|
|
|
@@ -583,6 +712,16 @@ export class AgentRegistry {
|
|
|
583
712
|
return agent ? this.copy(agent) : undefined;
|
|
584
713
|
}
|
|
585
714
|
|
|
715
|
+
markCompletionDelivered(id: string, deliveredAt: number): void {
|
|
716
|
+
const agent = this.agents.get(id);
|
|
717
|
+
if (!agent?.telemetry) return;
|
|
718
|
+
agent.telemetry = {
|
|
719
|
+
...agent.telemetry,
|
|
720
|
+
updatedAt: deliveredAt,
|
|
721
|
+
timing: { ...agent.telemetry.timing, completionDeliveredAt: deliveredAt },
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
|
|
586
725
|
async sweepExpired(): Promise<number> {
|
|
587
726
|
const removed = this.evictExpired();
|
|
588
727
|
let releaseError: unknown;
|
|
@@ -596,17 +735,35 @@ export class AgentRegistry {
|
|
|
596
735
|
return removed.length;
|
|
597
736
|
}
|
|
598
737
|
|
|
599
|
-
private startTurn(
|
|
738
|
+
private startTurn(
|
|
739
|
+
agent: ManagedAgent,
|
|
740
|
+
task: string,
|
|
741
|
+
limits: TurnLimits & { timeoutMs?: number } = {},
|
|
742
|
+
): void {
|
|
600
743
|
agent.state = "starting";
|
|
601
744
|
agent.error = undefined;
|
|
602
745
|
agent.currentTask = task;
|
|
746
|
+
agent.currentTimeoutMs = limits.timeoutMs ?? agent.timeoutMs;
|
|
747
|
+
agent.currentIdleTimeoutMs = limits.idleTimeoutMs ?? agent.idleTimeoutMs;
|
|
748
|
+
agent.currentMaxTurns = limits.maxTurns ?? agent.maxTurns;
|
|
749
|
+
agent.currentMaxToolCalls = limits.maxToolCalls ?? agent.maxToolCalls;
|
|
750
|
+
agent.structuredResult = undefined;
|
|
751
|
+
agent.termination = undefined;
|
|
752
|
+
agent.outcome = undefined;
|
|
603
753
|
agent.updatedAt = this.now();
|
|
754
|
+
agent.telemetry = {
|
|
755
|
+
phase: "queued",
|
|
756
|
+
queuePosition: this.queue.length + 1,
|
|
757
|
+
updatedAt: agent.updatedAt,
|
|
758
|
+
timing: { queuedAt: agent.updatedAt },
|
|
759
|
+
};
|
|
604
760
|
let resolveQueued!: (agent: ManagedAgent) => void;
|
|
605
761
|
const completion = new Promise<ManagedAgent>((resolve) => {
|
|
606
762
|
resolveQueued = resolve;
|
|
607
763
|
});
|
|
608
764
|
this.running.set(agent.id, completion);
|
|
609
765
|
this.queue.push({ agent, task, resolve: resolveQueued });
|
|
766
|
+
this.updateQueuePositions();
|
|
610
767
|
void this.changed();
|
|
611
768
|
this.pumpQueue();
|
|
612
769
|
}
|
|
@@ -616,6 +773,7 @@ export class AgentRegistry {
|
|
|
616
773
|
const next = this.queue.shift();
|
|
617
774
|
if (!next) return;
|
|
618
775
|
this.runQueuedTurn(next.agent, next.task, next.resolve);
|
|
776
|
+
this.updateQueuePositions();
|
|
619
777
|
}
|
|
620
778
|
}
|
|
621
779
|
|
|
@@ -624,17 +782,48 @@ export class AgentRegistry {
|
|
|
624
782
|
task: string,
|
|
625
783
|
resolveQueued: (agent: ManagedAgent) => void,
|
|
626
784
|
): void {
|
|
785
|
+
if (
|
|
786
|
+
agent.capabilityGrant &&
|
|
787
|
+
agent.executionPlan &&
|
|
788
|
+
!isCapabilityGrantActive(agent.capabilityGrant, agent.executionPlan, this.now())
|
|
789
|
+
) {
|
|
790
|
+
agent.state = "failed";
|
|
791
|
+
agent.error = "Capability grant expired or no longer matches the accepted plan";
|
|
792
|
+
agent.outcome = classifyStructuredOutcome("failed", "capability-grant-invalid");
|
|
793
|
+
agent.currentTask = undefined;
|
|
794
|
+
agent.currentTimeoutMs = undefined;
|
|
795
|
+
agent.updatedAt = this.now();
|
|
796
|
+
resolveQueued(agent);
|
|
797
|
+
this.running.delete(agent.id);
|
|
798
|
+
void this.notifyTurnComplete({
|
|
799
|
+
agent: this.copy(agent),
|
|
800
|
+
task,
|
|
801
|
+
output: "",
|
|
802
|
+
error: agent.error,
|
|
803
|
+
}).then(() => this.changed());
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
627
806
|
const controller = new AbortController();
|
|
628
807
|
this.controllers.set(agent.id, controller);
|
|
629
808
|
agent.state = "running";
|
|
630
809
|
agent.updatedAt = this.now();
|
|
631
810
|
const startedAt = this.now();
|
|
632
811
|
const completionKey = `completion:${agent.id}:${randomUUID()}`;
|
|
812
|
+
const acceptedPlanId = agent.executionPlan?.id;
|
|
633
813
|
let completionContent = "";
|
|
634
814
|
let completionOutput = "";
|
|
635
815
|
let completionError: string | undefined;
|
|
636
816
|
void this.transport
|
|
637
|
-
.runTurn(this.copy(agent), task, controller.signal)
|
|
817
|
+
.runTurn(this.copy(agent), task, controller.signal, (progress) => {
|
|
818
|
+
agent.telemetry = {
|
|
819
|
+
...progress,
|
|
820
|
+
queuePosition: undefined,
|
|
821
|
+
timing: {
|
|
822
|
+
queuedAt: agent.telemetry?.timing.queuedAt,
|
|
823
|
+
...progress.timing,
|
|
824
|
+
},
|
|
825
|
+
};
|
|
826
|
+
})
|
|
638
827
|
.then(async (outcome) => {
|
|
639
828
|
const output = truncateUtf8(outcome.output, this.maxTurnOutputBytes).text;
|
|
640
829
|
const error = outcome.error
|
|
@@ -647,22 +836,90 @@ export class AgentRegistry {
|
|
|
647
836
|
completedAt: this.now(),
|
|
648
837
|
exitCode: outcome.exitCode,
|
|
649
838
|
truncated: outcome.truncated,
|
|
839
|
+
termination: outcome.termination
|
|
840
|
+
? copyTurnTerminationReport(outcome.termination)
|
|
841
|
+
: undefined,
|
|
650
842
|
});
|
|
651
843
|
agent.history = agent.history.slice(-this.maxHistoryTurns);
|
|
652
|
-
agent.
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
844
|
+
agent.structuredResult =
|
|
845
|
+
outcome.structuredResult ?? parseAnyStructuredSubagentResult(output, agent.resultFormat);
|
|
846
|
+
agent.outcome =
|
|
847
|
+
outcome.outcome ??
|
|
848
|
+
(outcome.aborted
|
|
849
|
+
? classifyStructuredOutcome("interrupted", "transport-aborted")
|
|
850
|
+
: agent.structuredResult?.version === "pi-subagents:result:v2"
|
|
851
|
+
? classifyStructuredOutcome(
|
|
852
|
+
agent.structuredResult.status,
|
|
853
|
+
agent.structuredResult.reasonCode,
|
|
854
|
+
)
|
|
855
|
+
: agent.resultFormat !== undefined &&
|
|
856
|
+
agent.resultFormat !== "text" &&
|
|
857
|
+
agent.structuredResult === undefined
|
|
858
|
+
? classifyStructuredOutcome("contract-invalid", "malformed-structured-result")
|
|
859
|
+
: undefined);
|
|
860
|
+
const staleGeneration = Boolean(
|
|
861
|
+
acceptedPlanId && agent.executionPlan?.id !== acceptedPlanId,
|
|
862
|
+
);
|
|
863
|
+
if (staleGeneration) {
|
|
864
|
+
agent.outcome = classifyStructuredOutcome("stale", "cancelled-generation");
|
|
865
|
+
}
|
|
866
|
+
agent.state = staleGeneration
|
|
867
|
+
? "stale"
|
|
868
|
+
: outcome.aborted
|
|
869
|
+
? "interrupted"
|
|
870
|
+
: outcome.exitCode !== 0
|
|
871
|
+
? "failed"
|
|
872
|
+
: lifecycleStateForOutcome(agent.outcome?.status);
|
|
657
873
|
agent.error = error;
|
|
874
|
+
if (agent.capabilityGrant?.state === "active") {
|
|
875
|
+
agent.capabilityGrant = revokeCapabilityGrant(
|
|
876
|
+
agent.capabilityGrant,
|
|
877
|
+
"turn-settled",
|
|
878
|
+
this.now(),
|
|
879
|
+
);
|
|
880
|
+
}
|
|
658
881
|
agent.policy = outcome.policy;
|
|
882
|
+
agent.telemetry = outcome.telemetry
|
|
883
|
+
? {
|
|
884
|
+
...outcome.telemetry,
|
|
885
|
+
timing: {
|
|
886
|
+
queuedAt: agent.telemetry?.timing.queuedAt,
|
|
887
|
+
...outcome.telemetry.timing,
|
|
888
|
+
},
|
|
889
|
+
}
|
|
890
|
+
: agent.telemetry;
|
|
891
|
+
agent.termination = outcome.termination
|
|
892
|
+
? copyTurnTerminationReport(outcome.termination)
|
|
893
|
+
: undefined;
|
|
659
894
|
completionOutput = output;
|
|
660
895
|
completionError = error;
|
|
661
896
|
completionContent = output || error || `${agent.id} ${agent.state}`;
|
|
662
897
|
return agent;
|
|
663
898
|
})
|
|
664
899
|
.catch((error) => {
|
|
665
|
-
|
|
900
|
+
const staleGeneration = Boolean(
|
|
901
|
+
acceptedPlanId && agent.executionPlan?.id !== acceptedPlanId,
|
|
902
|
+
);
|
|
903
|
+
agent.state = staleGeneration
|
|
904
|
+
? "stale"
|
|
905
|
+
: controller.signal.aborted
|
|
906
|
+
? "interrupted"
|
|
907
|
+
: "failed";
|
|
908
|
+
agent.outcome = classifyStructuredOutcome(
|
|
909
|
+
staleGeneration ? "stale" : controller.signal.aborted ? "interrupted" : "failed",
|
|
910
|
+
staleGeneration
|
|
911
|
+
? "cancelled-generation"
|
|
912
|
+
: controller.signal.aborted
|
|
913
|
+
? "transport-aborted"
|
|
914
|
+
: "transport-error",
|
|
915
|
+
);
|
|
916
|
+
if (agent.capabilityGrant?.state === "active") {
|
|
917
|
+
agent.capabilityGrant = revokeCapabilityGrant(
|
|
918
|
+
agent.capabilityGrant,
|
|
919
|
+
"turn-failed",
|
|
920
|
+
this.now(),
|
|
921
|
+
);
|
|
922
|
+
}
|
|
666
923
|
agent.error = truncateUtf8(
|
|
667
924
|
error instanceof Error ? error.message : String(error),
|
|
668
925
|
this.maxTurnOutputBytes,
|
|
@@ -675,6 +932,16 @@ export class AgentRegistry {
|
|
|
675
932
|
exitCode: controller.signal.aborted ? 130 : 1,
|
|
676
933
|
});
|
|
677
934
|
agent.history = agent.history.slice(-this.maxHistoryTurns);
|
|
935
|
+
agent.telemetry = {
|
|
936
|
+
...(agent.telemetry ?? {
|
|
937
|
+
phase: "failed",
|
|
938
|
+
updatedAt: this.now(),
|
|
939
|
+
timing: { queuedAt: startedAt },
|
|
940
|
+
}),
|
|
941
|
+
phase: controller.signal.aborted ? "interrupted" : "failed",
|
|
942
|
+
failurePhase: agent.telemetry?.phase ?? "running",
|
|
943
|
+
updatedAt: this.now(),
|
|
944
|
+
};
|
|
678
945
|
completionError = agent.error;
|
|
679
946
|
completionContent = agent.error;
|
|
680
947
|
return agent;
|
|
@@ -692,8 +959,7 @@ export class AgentRegistry {
|
|
|
692
959
|
this.enqueueMessage(parent, completionContent, agent.id, completionKey);
|
|
693
960
|
}
|
|
694
961
|
}
|
|
695
|
-
agent
|
|
696
|
-
agent.currentMailboxMessageIds = undefined;
|
|
962
|
+
clearCurrentTurn(agent);
|
|
697
963
|
agent.updatedAt = this.now();
|
|
698
964
|
this.controllers.delete(agent.id);
|
|
699
965
|
this.running.delete(agent.id);
|
|
@@ -704,6 +970,17 @@ export class AgentRegistry {
|
|
|
704
970
|
});
|
|
705
971
|
}
|
|
706
972
|
|
|
973
|
+
private updateQueuePositions(): void {
|
|
974
|
+
for (const [index, entry] of this.queue.entries()) {
|
|
975
|
+
if (!entry.agent.telemetry) continue;
|
|
976
|
+
entry.agent.telemetry = {
|
|
977
|
+
...entry.agent.telemetry,
|
|
978
|
+
queuePosition: index + 1,
|
|
979
|
+
updatedAt: this.now(),
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
707
984
|
private enqueueMessage(
|
|
708
985
|
recipient: ManagedAgent,
|
|
709
986
|
content: string,
|
|
@@ -851,6 +1128,7 @@ export class AgentRegistry {
|
|
|
851
1128
|
: undefined,
|
|
852
1129
|
history: agent.history.map((turn) => ({ ...turn })),
|
|
853
1130
|
mailbox: agent.mailbox.map((message) => ({ ...message })),
|
|
1131
|
+
contract: agent.contract ? structuredClone(agent.contract) : undefined,
|
|
854
1132
|
target: agent.target ? { ...agent.target, trust: { ...agent.target.trust } } : undefined,
|
|
855
1133
|
policy: agent.policy
|
|
856
1134
|
? {
|
|
@@ -859,6 +1137,51 @@ export class AgentRegistry {
|
|
|
859
1137
|
unsupported: [...agent.policy.unsupported],
|
|
860
1138
|
}
|
|
861
1139
|
: undefined,
|
|
1140
|
+
structuredResult: agent.structuredResult
|
|
1141
|
+
? copyStructuredResult(agent.structuredResult)
|
|
1142
|
+
: undefined,
|
|
1143
|
+
termination: agent.termination ? copyTurnTerminationReport(agent.termination) : undefined,
|
|
1144
|
+
outcome: agent.outcome ? structuredClone(agent.outcome) : undefined,
|
|
1145
|
+
executionPlan: agent.executionPlan ? copyExecutionPlan(agent.executionPlan) : undefined,
|
|
1146
|
+
capabilityGrant: agent.capabilityGrant ? structuredClone(agent.capabilityGrant) : undefined,
|
|
1147
|
+
semanticSnapshot: agent.semanticSnapshot
|
|
1148
|
+
? structuredClone(agent.semanticSnapshot)
|
|
1149
|
+
: undefined,
|
|
1150
|
+
semanticCompatibility: agent.semanticCompatibility
|
|
1151
|
+
? structuredClone(agent.semanticCompatibility)
|
|
1152
|
+
: undefined,
|
|
1153
|
+
telemetry: agent.telemetry ? copyTelemetry(agent.telemetry) : undefined,
|
|
862
1154
|
};
|
|
863
1155
|
}
|
|
864
1156
|
}
|
|
1157
|
+
|
|
1158
|
+
function lifecycleStateForOutcome(
|
|
1159
|
+
status: import("./result-contract.js").SubagentOutcomeStatus | undefined,
|
|
1160
|
+
): AgentLifecycleState {
|
|
1161
|
+
switch (status) {
|
|
1162
|
+
case "blocked":
|
|
1163
|
+
case "needs-input":
|
|
1164
|
+
case "abstained":
|
|
1165
|
+
case "stale":
|
|
1166
|
+
return status;
|
|
1167
|
+
case "failed":
|
|
1168
|
+
case "contract-invalid":
|
|
1169
|
+
return "failed";
|
|
1170
|
+
case "interrupted":
|
|
1171
|
+
return "interrupted";
|
|
1172
|
+
default:
|
|
1173
|
+
return "completed";
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
function copyStructuredResult(value: AnyStructuredSubagentResult): AnyStructuredSubagentResult {
|
|
1178
|
+
return structuredClone(value);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
function copyTelemetry(value: TransportTelemetry): TransportTelemetry {
|
|
1182
|
+
return {
|
|
1183
|
+
...value,
|
|
1184
|
+
timing: { ...value.timing },
|
|
1185
|
+
usage: value.usage ? { ...value.usage } : undefined,
|
|
1186
|
+
};
|
|
1187
|
+
}
|