@narumitw/pi-subagents 0.53.0 → 0.54.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.
Files changed (64) hide show
  1. package/README.md +75 -6
  2. package/package.json +1 -1
  3. package/src/agents/built-ins.ts +124 -0
  4. package/src/agents/catalog.ts +224 -0
  5. package/src/agents/discovery.ts +249 -0
  6. package/src/agents/types.ts +98 -0
  7. package/src/agents.ts +47 -670
  8. package/src/auto-transport.ts +2 -1
  9. package/src/automation.ts +7 -2
  10. package/src/capability-router.ts +1 -1
  11. package/src/completion-delivery.ts +2 -3
  12. package/src/config-status.ts +2 -2
  13. package/src/config-ui.ts +8 -8
  14. package/src/consult-resources.ts +1 -1
  15. package/src/consult.ts +9 -7
  16. package/src/create-stateful-transport.ts +2 -1
  17. package/src/cwd-policy.ts +1 -1
  18. package/src/execution/budget.ts +56 -0
  19. package/src/execution/runtime-policy.ts +19 -0
  20. package/src/execution-plan.ts +1 -1
  21. package/src/execution-profiles.ts +1 -1
  22. package/src/execution-ui.ts +1 -1
  23. package/src/execution.ts +269 -100
  24. package/src/in-process-transport.ts +3 -2
  25. package/src/inspect.ts +35 -8
  26. package/src/limits.ts +1 -0
  27. package/src/orchestration-metrics.ts +12 -5
  28. package/src/panel-execution.ts +1 -1
  29. package/src/panel-planning.ts +1 -1
  30. package/src/params.ts +3 -1
  31. package/src/persistence.ts +1 -1
  32. package/src/registry-types.ts +1 -1
  33. package/src/registry.ts +1 -1
  34. package/src/render.ts +1 -1
  35. package/src/retained-semantic-state.ts +1 -1
  36. package/src/rpc-transport-metadata.ts +1 -1
  37. package/src/rpc-transport.ts +2 -1
  38. package/src/runner.ts +6 -1
  39. package/src/settings/inspection.ts +275 -0
  40. package/src/settings/schema.ts +186 -0
  41. package/src/settings.ts +72 -420
  42. package/src/spawn-idempotency.ts +1 -1
  43. package/src/stateful-agent-view.ts +87 -0
  44. package/src/stateful-config.ts +1 -1
  45. package/src/stateful-guidance.ts +1 -1
  46. package/src/stateful-limits.ts +1 -1
  47. package/src/stateful-prompt.ts +2 -2
  48. package/src/stateful-safety.ts +2 -1
  49. package/src/stateful.ts +23 -103
  50. package/src/subagents.ts +8 -9
  51. package/src/subprocess-transport.ts +2 -6
  52. package/src/transport-types.ts +1 -1
  53. package/src/transport-ui.ts +1 -1
  54. package/src/verification-harness.ts +516 -0
  55. package/src/verification-receipt.ts +275 -0
  56. package/src/verified-execution-benchmark.ts +86 -0
  57. package/src/verified-execution-contract.ts +219 -0
  58. package/src/work-item-ledger.ts +510 -37
  59. package/src/work-item-persistence.ts +31 -0
  60. package/src/workflow-completion-controller.ts +397 -0
  61. package/src/workflow-plan-compiler.ts +1 -1
  62. package/src/workflow-plan-patch.ts +1 -1
  63. package/src/workflow-planning.ts +11 -1
  64. package/src/workflow-ui.ts +1 -1
package/src/settings.ts CHANGED
@@ -3,26 +3,44 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
5
  import lockfile from "proper-lockfile";
6
+ import type {
7
+ AgentConfig,
8
+ CompletionDelivery,
9
+ ConsultationCwdPolicy,
10
+ ConsultResourcePolicy,
11
+ DelegationCwdPolicy,
12
+ SubagentAgentConfig,
13
+ SubagentSettings,
14
+ SubagentThinkingLevel,
15
+ SubagentTransportKind,
16
+ } from "./agents/types.js";
17
+ import { MAX_CONFIGURABLE_PARALLEL_TASKS } from "./limits.js";
6
18
  import {
7
- type AgentConfig,
8
- CONSULT_RESOURCE_POLICIES,
9
- CONSULTATION_CWD_POLICIES,
10
- type CompletionDelivery,
11
- type ConsultationCwdPolicy,
12
- type ConsultResourcePolicy,
13
- DELEGATION_CWD_POLICIES,
14
- type DelegationCwdPolicy,
15
- isThinkingLevel,
16
- type SubagentAgentConfig,
17
- type SubagentSettings,
18
- type SubagentThinkingLevel,
19
- type SubagentTransportKind,
20
- } from "./agents.js";
19
+ type BlockingParallelLimitSettingsSnapshot,
20
+ buildBlockingParallelLimitSettingsSnapshot,
21
+ buildCompletionDeliverySettingsSnapshot,
22
+ buildConsultResourceSettingsSnapshot,
23
+ buildCwdPolicySettingsSnapshot,
24
+ buildDelegationWorkflowSettingsSnapshot,
25
+ buildStatefulLimitSettingsSnapshot,
26
+ buildStatefulTransportSettingsSnapshot,
27
+ buildSubagentSettingsSnapshot,
28
+ type CompletionDeliverySettingsSnapshot,
29
+ type ConsultResourceSettingsSnapshot,
30
+ type CwdPolicySettingsSnapshot,
31
+ type DelegationWorkflow,
32
+ type DelegationWorkflowSettingsSnapshot,
33
+ type InspectedSubagentSettingsDocument,
34
+ type StatefulLimitSettingsSnapshot,
35
+ type StatefulTransportSettingsSnapshot,
36
+ type SubagentSettingsSnapshot,
37
+ } from "./settings/inspection.js";
21
38
  import {
22
- DEFAULT_MAX_PARALLEL_TASKS,
23
- MAX_CONFIGURABLE_PARALLEL_TASKS,
24
- MAX_SUBAGENT_TIMEOUT_MS,
25
- } from "./limits.js";
39
+ hasOwn,
40
+ isPlainObject,
41
+ isPositiveInteger,
42
+ normalizeSubagentSettings,
43
+ } from "./settings/schema.js";
26
44
  import {
27
45
  isValidStatefulLimit,
28
46
  resolveStatefulLimits,
@@ -32,185 +50,32 @@ import {
32
50
  statefulLimitDefinition,
33
51
  } from "./stateful-limits.js";
34
52
 
35
- export function hasOwn(obj: object, key: PropertyKey): boolean {
36
- return Object.hasOwn(obj, key);
37
- }
38
-
39
- function isPlainObject(value: unknown): value is Record<string, unknown> {
40
- return typeof value === "object" && value !== null && !Array.isArray(value);
41
- }
42
-
43
- function isStringArray(value: unknown): value is string[] {
44
- return Array.isArray(value) && value.every((item) => typeof item === "string");
45
- }
46
-
47
- function isPositiveNumber(value: unknown): value is number {
48
- return typeof value === "number" && Number.isFinite(value) && value >= 1;
49
- }
50
-
51
- function isPositiveInteger(value: unknown): value is number {
52
- return isPositiveNumber(value) && Number.isSafeInteger(value);
53
- }
54
-
55
- export function normalizeAgentSettings(value: unknown): SubagentAgentConfig | undefined {
56
- if (!isPlainObject(value)) return undefined;
57
-
58
- const config: SubagentAgentConfig = {};
59
- let hasKnownField = false;
60
-
61
- if (hasOwn(value, "tools")) {
62
- if (!isStringArray(value.tools)) return undefined;
63
- config.tools = value.tools;
64
- hasKnownField = true;
65
- }
66
-
67
- if (hasOwn(value, "model")) {
68
- if (value.model !== null && typeof value.model !== "string") return undefined;
69
- config.model = value.model;
70
- hasKnownField = true;
71
- }
72
-
73
- if (hasOwn(value, "thinkingLevel")) {
74
- if (value.thinkingLevel !== null && !isThinkingLevel(value.thinkingLevel)) return undefined;
75
- config.thinkingLevel = value.thinkingLevel;
76
- hasKnownField = true;
77
- }
78
-
79
- if (hasOwn(value, "timeoutMs")) {
80
- if (
81
- value.timeoutMs !== null &&
82
- (!isPositiveNumber(value.timeoutMs) || value.timeoutMs > MAX_SUBAGENT_TIMEOUT_MS)
83
- ) {
84
- return undefined;
85
- }
86
- config.timeoutMs = value.timeoutMs;
87
- hasKnownField = true;
88
- }
89
-
90
- return hasKnownField ? config : undefined;
91
- }
92
-
93
- export function normalizeSubagentSettings(value: unknown): SubagentSettings | undefined {
94
- if (!isPlainObject(value)) return undefined;
95
- const settings: SubagentSettings = {};
96
- if (hasOwn(value, "agents")) {
97
- if (!isPlainObject(value.agents)) return undefined;
98
- const agents: Record<string, SubagentAgentConfig> = {};
99
- for (const [name, rawConfig] of Object.entries(value.agents)) {
100
- const config = normalizeAgentSettings(rawConfig);
101
- if (config) agents[name] = config;
102
- }
103
- if (Object.keys(agents).length > 0) settings.agents = agents;
104
- }
105
- if (hasOwn(value, "blocking")) {
106
- if (!isPlainObject(value.blocking)) return undefined;
107
- const blocking: NonNullable<SubagentSettings["blocking"]> = {};
108
- if (hasOwn(value.blocking, "enabled")) {
109
- if (typeof value.blocking.enabled !== "boolean") return undefined;
110
- blocking.enabled = value.blocking.enabled;
111
- }
112
- if (hasOwn(value.blocking, "maxParallelTasks")) {
113
- if (
114
- !isPositiveInteger(value.blocking.maxParallelTasks) ||
115
- value.blocking.maxParallelTasks > MAX_CONFIGURABLE_PARALLEL_TASKS
116
- ) {
117
- return undefined;
118
- }
119
- blocking.maxParallelTasks = value.blocking.maxParallelTasks;
120
- }
121
- settings.blocking = blocking;
122
- }
123
- if (hasOwn(value, "stateful")) {
124
- if (!isPlainObject(value.stateful)) return undefined;
125
- const runtime: NonNullable<SubagentSettings["stateful"]> = {};
126
- if (hasOwn(value.stateful, "transport")) {
127
- if (
128
- value.stateful.transport !== "subprocess" &&
129
- value.stateful.transport !== "in-process" &&
130
- value.stateful.transport !== "rpc" &&
131
- value.stateful.transport !== "auto"
132
- ) {
133
- return undefined;
134
- }
135
- runtime.transport = value.stateful.transport;
136
- }
137
- if (hasOwn(value.stateful, "completionDelivery")) {
138
- if (
139
- value.stateful.completionDelivery !== "next-turn" &&
140
- value.stateful.completionDelivery !== "auto-resume"
141
- ) {
142
- return undefined;
143
- }
144
- runtime.completionDelivery = value.stateful.completionDelivery;
145
- }
146
- for (const key of STATEFUL_LIMIT_FIELDS) {
147
- if (hasOwn(value.stateful, key)) {
148
- if (!isValidStatefulLimit(key, value.stateful[key])) return undefined;
149
- runtime[key] = value.stateful[key];
150
- }
151
- }
152
- for (const key of ["maxMailboxMessages", "maxMailboxMessageBytes", "idleTtlMs"] as const) {
153
- if (hasOwn(value.stateful, key)) {
154
- if (!isPositiveInteger(value.stateful[key])) return undefined;
155
- runtime[key] = value.stateful[key];
156
- }
157
- }
158
- if (hasOwn(value.stateful, "retentionDays")) {
159
- if (!isPositiveNumber(value.stateful.retentionDays)) return undefined;
160
- runtime.retentionDays = value.stateful.retentionDays;
161
- }
162
- if (hasOwn(value.stateful, "enabled")) {
163
- if (typeof value.stateful.enabled !== "boolean") return undefined;
164
- runtime.enabled = value.stateful.enabled;
165
- }
166
- settings.stateful = runtime;
167
- }
168
- if (hasOwn(value, "consult")) {
169
- if (!isPlainObject(value.consult)) return undefined;
170
- const consult: NonNullable<SubagentSettings["consult"]> = {};
171
- if (hasOwn(value.consult, "resources")) {
172
- if (
173
- typeof value.consult.resources !== "string" ||
174
- !CONSULT_RESOURCE_POLICIES.includes(value.consult.resources as ConsultResourcePolicy)
175
- ) {
176
- return undefined;
177
- }
178
- consult.resources = value.consult.resources as ConsultResourcePolicy;
179
- }
180
- settings.consult = consult;
181
- }
182
- if (hasOwn(value, "cwdPolicy")) {
183
- if (!isPlainObject(value.cwdPolicy)) return undefined;
184
- const cwdPolicy: NonNullable<SubagentSettings["cwdPolicy"]> = {};
185
- if (hasOwn(value.cwdPolicy, "consultation")) {
186
- if (
187
- typeof value.cwdPolicy.consultation !== "string" ||
188
- !CONSULTATION_CWD_POLICIES.includes(value.cwdPolicy.consultation as ConsultationCwdPolicy)
189
- ) {
190
- return undefined;
191
- }
192
- cwdPolicy.consultation = value.cwdPolicy.consultation as ConsultationCwdPolicy;
193
- }
194
- if (hasOwn(value.cwdPolicy, "delegation")) {
195
- if (
196
- typeof value.cwdPolicy.delegation !== "string" ||
197
- !DELEGATION_CWD_POLICIES.includes(value.cwdPolicy.delegation as DelegationCwdPolicy)
198
- ) {
199
- return undefined;
200
- }
201
- cwdPolicy.delegation = value.cwdPolicy.delegation as DelegationCwdPolicy;
202
- }
203
- settings.cwdPolicy = cwdPolicy;
204
- }
205
- return settings;
206
- }
53
+ export {
54
+ type BlockingParallelLimitSettingsSnapshot,
55
+ type CompletionDeliverySettingsSnapshot,
56
+ type ConsultResourceSettingsSnapshot,
57
+ type CwdPolicyFieldSnapshot,
58
+ type CwdPolicySettingsSnapshot,
59
+ DEFAULT_CONSULT_RESOURCE_POLICY,
60
+ DEFAULT_CONSULTATION_CWD_POLICY,
61
+ DEFAULT_DELEGATION_CWD_POLICY,
62
+ type DelegationWorkflow,
63
+ type DelegationWorkflowSettingsSnapshot,
64
+ resolveBlockingMaxParallelTasks,
65
+ resolveDelegationWorkflow,
66
+ type StatefulLimitFieldSnapshot,
67
+ type StatefulLimitSettingsSnapshot,
68
+ type StatefulTransportSettingsSnapshot,
69
+ type SubagentSettingsSnapshot,
70
+ } from "./settings/inspection.js";
71
+ export {
72
+ hasOwn,
73
+ normalizeAgentSettings,
74
+ normalizeSubagentSettings,
75
+ } from "./settings/schema.js";
207
76
 
208
77
  const SETTINGS_FILE = "pi-subagents.json";
209
78
  const LEGACY_SETTINGS_FILE = "pi-subagents-config.json";
210
- const DEFAULT_COMPLETION_DELIVERY: CompletionDelivery = "next-turn";
211
- export const DEFAULT_CONSULT_RESOURCE_POLICY: ConsultResourcePolicy = "project-context";
212
- export const DEFAULT_CONSULTATION_CWD_POLICY: ConsultationCwdPolicy = "anywhere";
213
- export const DEFAULT_DELEGATION_CWD_POLICY: DelegationCwdPolicy = "trusted-targets";
214
79
  const SETTINGS_LOCK_FS_ADAPTER = {
215
80
  mkdir: fs.mkdir,
216
81
  mkdirSync: fs.mkdirSync,
@@ -284,94 +149,11 @@ export function saveSubagentConfig(settings: SubagentSettings): void {
284
149
  writeSettingsObject(settings);
285
150
  }
286
151
 
287
- export type DelegationWorkflow = "all" | "async-only" | "blocking-only" | "disabled";
288
-
289
- export interface DelegationWorkflowSettingsSnapshot {
290
- path: string;
291
- value: DelegationWorkflow;
292
- source: "default" | "user settings";
293
- error?: string;
294
- }
295
-
296
- export interface CompletionDeliverySettingsSnapshot {
297
- path: string;
298
- value: CompletionDelivery;
299
- source: "default" | "user settings";
300
- error?: string;
301
- }
302
-
303
- export interface StatefulTransportSettingsSnapshot {
304
- path: string;
305
- value: SubagentTransportKind;
306
- source: "default" | "user settings";
307
- error?: string;
308
- }
309
-
310
- export interface BlockingParallelLimitSettingsSnapshot {
311
- path: string;
312
- value: number;
313
- source: "default" | "user settings";
314
- error?: string;
315
- }
316
-
317
- export interface StatefulLimitFieldSnapshot {
318
- value: number;
319
- source: "default" | "user settings";
320
- }
321
-
322
- export interface StatefulLimitSettingsSnapshot {
323
- path: string;
324
- writePath: string;
325
- values?: Record<StatefulLimitField, StatefulLimitFieldSnapshot>;
326
- error?: string;
327
- }
328
-
329
- export interface ConsultResourceSettingsSnapshot {
330
- path: string;
331
- value: ConsultResourcePolicy;
332
- source: "default" | "user settings";
333
- error?: string;
334
- }
335
-
336
- export interface CwdPolicyFieldSnapshot<T> {
337
- value: T;
338
- source: "default" | "user settings";
339
- }
340
-
341
- export interface CwdPolicySettingsSnapshot {
342
- path: string;
343
- consultation: CwdPolicyFieldSnapshot<ConsultationCwdPolicy>;
344
- delegation: CwdPolicyFieldSnapshot<DelegationCwdPolicy>;
345
- error?: string;
346
- }
347
-
348
- export interface SubagentSettingsSnapshot {
349
- path: string;
350
- settings?: SubagentSettings;
351
- source: "default" | "user settings";
352
- error?: string;
353
- }
354
-
355
152
  export function subagentSettingsFilePath(): string {
356
153
  return path.join(getAgentDir(), SETTINGS_FILE);
357
154
  }
358
155
 
359
- export function resolveDelegationWorkflow(
360
- blockingEnabled: boolean,
361
- statefulEnabled: boolean,
362
- ): DelegationWorkflow {
363
- if (blockingEnabled && statefulEnabled) return "all";
364
- if (statefulEnabled) return "async-only";
365
- if (blockingEnabled) return "blocking-only";
366
- return "disabled";
367
- }
368
-
369
- function inspectSubagentSettingsDocument(): {
370
- path: string;
371
- raw?: Record<string, unknown>;
372
- settings?: SubagentSettings;
373
- error?: string;
374
- } {
156
+ function inspectSubagentSettingsDocument(): InspectedSubagentSettingsDocument {
375
157
  const { canonicalPath, activePath } = resolveSubagentSettingsPaths();
376
158
  if (activePath === undefined) return { path: canonicalPath };
377
159
  const inspected = inspectSubagentSettingsPath(activePath);
@@ -380,12 +162,7 @@ function inspectSubagentSettingsDocument(): {
380
162
  : inspected;
381
163
  }
382
164
 
383
- function inspectSubagentSettingsPath(configPath: string): {
384
- path: string;
385
- raw?: Record<string, unknown>;
386
- settings?: SubagentSettings;
387
- error?: string;
388
- } {
165
+ function inspectSubagentSettingsPath(configPath: string): InspectedSubagentSettingsDocument {
389
166
  const fileName = path.basename(configPath);
390
167
  let contents: string;
391
168
  try {
@@ -411,163 +188,38 @@ function inspectSubagentSettingsPath(configPath: string): {
411
188
  }
412
189
 
413
190
  export function inspectSubagentSettings(): SubagentSettingsSnapshot {
414
- const inspected = inspectSubagentSettingsDocument();
415
- return {
416
- path: inspected.path,
417
- settings: inspected.settings,
418
- source: inspected.settings ? "user settings" : "default",
419
- ...(inspected.error ? { error: inspected.error } : {}),
420
- };
191
+ return buildSubagentSettingsSnapshot(inspectSubagentSettingsDocument());
421
192
  }
422
193
 
423
194
  export function inspectConsultResourceSettings(): ConsultResourceSettingsSnapshot {
424
- const inspected = inspectSubagentSettingsDocument();
425
- if (!inspected.raw || !inspected.settings) {
426
- return {
427
- path: inspected.path,
428
- value: DEFAULT_CONSULT_RESOURCE_POLICY,
429
- source: "default",
430
- ...(inspected.error ? { error: inspected.error } : {}),
431
- };
432
- }
433
- const explicit =
434
- isPlainObject(inspected.raw.consult) && hasOwn(inspected.raw.consult, "resources");
435
- return {
436
- path: inspected.path,
437
- value: inspected.settings.consult?.resources ?? DEFAULT_CONSULT_RESOURCE_POLICY,
438
- source: explicit ? "user settings" : "default",
439
- };
195
+ return buildConsultResourceSettingsSnapshot(inspectSubagentSettingsDocument());
440
196
  }
441
197
 
442
198
  export function inspectCwdPolicySettings(): CwdPolicySettingsSnapshot {
443
- const inspected = inspectSubagentSettingsDocument();
444
- if (!inspected.raw || !inspected.settings) {
445
- return {
446
- path: inspected.path,
447
- consultation: { value: DEFAULT_CONSULTATION_CWD_POLICY, source: "default" },
448
- delegation: { value: DEFAULT_DELEGATION_CWD_POLICY, source: "default" },
449
- ...(inspected.error ? { error: inspected.error } : {}),
450
- };
451
- }
452
- const rawPolicy = isPlainObject(inspected.raw.cwdPolicy) ? inspected.raw.cwdPolicy : undefined;
453
- return {
454
- path: inspected.path,
455
- consultation: {
456
- value: inspected.settings.cwdPolicy?.consultation ?? DEFAULT_CONSULTATION_CWD_POLICY,
457
- source: rawPolicy && hasOwn(rawPolicy, "consultation") ? "user settings" : "default",
458
- },
459
- delegation: {
460
- value: inspected.settings.cwdPolicy?.delegation ?? DEFAULT_DELEGATION_CWD_POLICY,
461
- source: rawPolicy && hasOwn(rawPolicy, "delegation") ? "user settings" : "default",
462
- },
463
- };
199
+ return buildCwdPolicySettingsSnapshot(inspectSubagentSettingsDocument());
464
200
  }
465
201
 
466
202
  export function inspectDelegationWorkflowSettings(): DelegationWorkflowSettingsSnapshot {
467
- const inspected = inspectSubagentSettingsDocument();
468
- if (!inspected.raw || !inspected.settings) {
469
- return {
470
- path: inspected.path,
471
- value: "all",
472
- source: "default",
473
- ...(inspected.error ? { error: inspected.error } : {}),
474
- };
475
- }
476
- const explicit =
477
- (isPlainObject(inspected.raw.blocking) && hasOwn(inspected.raw.blocking, "enabled")) ||
478
- (isPlainObject(inspected.raw.stateful) && hasOwn(inspected.raw.stateful, "enabled"));
479
- return {
480
- path: inspected.path,
481
- value: resolveDelegationWorkflow(
482
- inspected.settings.blocking?.enabled !== false,
483
- inspected.settings.stateful?.enabled !== false,
484
- ),
485
- source: explicit ? "user settings" : "default",
486
- };
203
+ return buildDelegationWorkflowSettingsSnapshot(inspectSubagentSettingsDocument());
487
204
  }
488
205
 
489
206
  export function inspectCompletionDeliverySettings(): CompletionDeliverySettingsSnapshot {
490
- const inspected = inspectSubagentSettingsDocument();
491
- if (!inspected.raw || !inspected.settings) {
492
- return {
493
- path: inspected.path,
494
- value: DEFAULT_COMPLETION_DELIVERY,
495
- source: "default",
496
- ...(inspected.error ? { error: inspected.error } : {}),
497
- };
498
- }
499
- const explicit =
500
- isPlainObject(inspected.raw.stateful) && hasOwn(inspected.raw.stateful, "completionDelivery");
501
- return {
502
- path: inspected.path,
503
- value: inspected.settings.stateful?.completionDelivery ?? DEFAULT_COMPLETION_DELIVERY,
504
- source: explicit ? "user settings" : "default",
505
- };
207
+ return buildCompletionDeliverySettingsSnapshot(inspectSubagentSettingsDocument());
506
208
  }
507
209
 
508
210
  export function inspectStatefulTransportSettings(): StatefulTransportSettingsSnapshot {
509
- const inspected = inspectSubagentSettingsDocument();
510
- if (!inspected.raw || !inspected.settings) {
511
- return {
512
- path: inspected.path,
513
- value: "subprocess",
514
- source: "default",
515
- ...(inspected.error ? { error: inspected.error } : {}),
516
- };
517
- }
518
- const explicit =
519
- isPlainObject(inspected.raw.stateful) && hasOwn(inspected.raw.stateful, "transport");
520
- return {
521
- path: inspected.path,
522
- value: inspected.settings.stateful?.transport ?? "subprocess",
523
- source: explicit ? "user settings" : "default",
524
- };
525
- }
526
-
527
- export function resolveBlockingMaxParallelTasks(settings?: SubagentSettings): number {
528
- return settings?.blocking?.maxParallelTasks ?? DEFAULT_MAX_PARALLEL_TASKS;
211
+ return buildStatefulTransportSettingsSnapshot(inspectSubagentSettingsDocument());
529
212
  }
530
213
 
531
214
  export function inspectBlockingParallelLimitSettings(): BlockingParallelLimitSettingsSnapshot {
532
- const inspected = inspectSubagentSettingsDocument();
533
- if (!inspected.raw || !inspected.settings) {
534
- return {
535
- path: inspected.path,
536
- value: DEFAULT_MAX_PARALLEL_TASKS,
537
- source: "default",
538
- ...(inspected.error ? { error: inspected.error } : {}),
539
- };
540
- }
541
- const explicit =
542
- isPlainObject(inspected.raw.blocking) && hasOwn(inspected.raw.blocking, "maxParallelTasks");
543
- return {
544
- path: inspected.path,
545
- value: resolveBlockingMaxParallelTasks(inspected.settings),
546
- source: explicit ? "user settings" : "default",
547
- };
215
+ return buildBlockingParallelLimitSettingsSnapshot(inspectSubagentSettingsDocument());
548
216
  }
549
217
 
550
218
  export function inspectStatefulLimitSettings(): StatefulLimitSettingsSnapshot {
551
- const inspected = inspectSubagentSettingsDocument();
552
- const writePath = subagentSettingsFilePath();
553
- if (inspected.error) {
554
- return { path: inspected.path, writePath, error: inspected.error };
555
- }
556
- const resolved = resolveStatefulLimits(inspected.settings?.stateful);
557
- const rawStateful = isPlainObject(inspected.raw?.stateful) ? inspected.raw.stateful : undefined;
558
- return {
559
- path: inspected.path,
560
- writePath,
561
- values: Object.fromEntries(
562
- STATEFUL_LIMIT_FIELDS.map((field) => [
563
- field,
564
- {
565
- value: resolved[field],
566
- source: rawStateful && hasOwn(rawStateful, field) ? "user settings" : "default",
567
- },
568
- ]),
569
- ) as unknown as Record<StatefulLimitField, StatefulLimitFieldSnapshot>,
570
- };
219
+ return buildStatefulLimitSettingsSnapshot(
220
+ inspectSubagentSettingsDocument(),
221
+ subagentSettingsFilePath(),
222
+ );
571
223
  }
572
224
 
573
225
  export function updateDelegationWorkflowSetting(
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import type { AgentScope, SubagentThinkingLevel } from "./agents.js";
2
+ import type { AgentScope, SubagentThinkingLevel } from "./agents/types.js";
3
3
  import type { DelegationContract } from "./delegation-contract.js";
4
4
  import type { SubagentResultFormat } from "./result-contract.js";
5
5
 
@@ -0,0 +1,87 @@
1
+ import { MAX_TOOL_MESSAGE_BYTES, truncateUtf8 } from "./limits.js";
2
+ import type { ManagedAgent } from "./registry.js";
3
+
4
+ export function formatStatefulAgentLine(agent: ManagedAgent, now = Date.now()): string {
5
+ const elapsedSeconds = Math.max(0, Math.floor((now - agent.updatedAt) / 1000));
6
+ const actions =
7
+ agent.state === "running" || agent.state === "starting"
8
+ ? "interrupt, close"
9
+ : agent.state === "closed"
10
+ ? "inspect"
11
+ : "send, close";
12
+ const task = agent.currentTask ? ` — ${sanitizeStatusLine(agent.currentTask, 80)}` : "";
13
+ const unread = agent.mailbox.filter((message) => !message.readAt).length;
14
+ const indent = " ".repeat(agent.depth);
15
+ const thinking = agent.thinkingLevel ? ` thinking:${agent.thinkingLevel}` : "";
16
+ const timeout = agent.currentTimeoutMs ?? agent.timeoutMs;
17
+ const timeoutText = timeout ? ` timeout:${timeout}ms` : "";
18
+ const idleTimeout = agent.currentIdleTimeoutMs ?? agent.idleTimeoutMs;
19
+ const idleText = idleTimeout ? ` idle:${idleTimeout}ms` : "";
20
+ const maxTurns = agent.currentMaxTurns ?? agent.maxTurns;
21
+ const turnsText = maxTurns ? ` turns:${maxTurns}` : "";
22
+ const maxToolCalls = agent.currentMaxToolCalls ?? agent.maxToolCalls;
23
+ const toolsText = maxToolCalls ? ` tools:${maxToolCalls}` : "";
24
+ const transport = agent.telemetry?.transport ? ` transport:${agent.telemetry.transport}` : "";
25
+ const phase = agent.telemetry?.phase ? ` phase:${agent.telemetry.phase}` : "";
26
+ const queued = agent.telemetry?.queuePosition ? ` queue:${agent.telemetry.queuePosition}` : "";
27
+ return `${indent}${sanitizeStatusLine(agent.id, 128)} ${sanitizeStatusLine(agent.agent, 128)} ${agent.state} ${elapsedSeconds}s${thinking}${timeoutText}${idleText}${turnsText}${toolsText}${transport}${phase}${queued} unread:${unread} [${actions}]${task}`;
28
+ }
29
+
30
+ export function summarizeStatefulAgent(agent: ManagedAgent) {
31
+ return {
32
+ id: agent.id,
33
+ agent: agent.agent,
34
+ parentId: agent.parentId,
35
+ rootId: agent.rootId,
36
+ depth: agent.depth,
37
+ children: [...agent.children],
38
+ state: agent.state,
39
+ createdAt: agent.createdAt,
40
+ updatedAt: agent.updatedAt,
41
+ cwd: agent.cwd,
42
+ workspaceMode: agent.workspaceMode ?? "shared",
43
+ thinkingLevel: agent.thinkingLevel,
44
+ timeoutMs: agent.timeoutMs,
45
+ currentTimeoutMs: agent.currentTimeoutMs,
46
+ idleTimeoutMs: agent.idleTimeoutMs,
47
+ currentIdleTimeoutMs: agent.currentIdleTimeoutMs,
48
+ maxTurns: agent.maxTurns,
49
+ currentMaxTurns: agent.currentMaxTurns,
50
+ maxToolCalls: agent.maxToolCalls,
51
+ currentMaxToolCalls: agent.currentMaxToolCalls,
52
+ currentTask: agent.currentTask
53
+ ? truncateUtf8(agent.currentTask, MAX_TOOL_MESSAGE_BYTES).text
54
+ : undefined,
55
+ historyCount: agent.history.length,
56
+ unreadMessages: agent.mailbox.filter((message) => !message.readAt).length,
57
+ context: {
58
+ turns: agent.contextTurns ?? 0,
59
+ sources: agent.contextSourceIds?.length ?? 0,
60
+ bytes: agent.contextBytes ?? 0,
61
+ truncated: agent.contextTruncated === true,
62
+ },
63
+ resultFormat: agent.resultFormat ?? "text",
64
+ structuredResult: agent.structuredResult,
65
+ termination: agent.termination,
66
+ outcome: agent.outcome,
67
+ executionPlan: agent.executionPlan,
68
+ capabilityGrant: agent.capabilityGrant,
69
+ semanticCompatibility: agent.semanticCompatibility,
70
+ semanticSnapshotDigest: agent.semanticSnapshot?.digest,
71
+ telemetry: agent.telemetry,
72
+ error: agent.error ? truncateUtf8(agent.error, MAX_TOOL_MESSAGE_BYTES).text : undefined,
73
+ target: agent.target,
74
+ policy: agent.policy,
75
+ };
76
+ }
77
+
78
+ function sanitizeStatusLine(value: string, maxLength: number): string {
79
+ return (
80
+ value
81
+ .slice(0, maxLength)
82
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.
83
+ .replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ")
84
+ .replace(/\s+/gu, " ")
85
+ .trim()
86
+ );
87
+ }
@@ -1,4 +1,4 @@
1
- import type { CompletionDelivery, SubagentTransportKind } from "./agents.js";
1
+ import type { CompletionDelivery, SubagentTransportKind } from "./agents/types.js";
2
2
 
3
3
  export function resolveStatefulTransportKind(
4
4
  value: SubagentTransportKind | undefined,
@@ -1,4 +1,4 @@
1
- import type { CompletionDelivery } from "./agents.js";
1
+ import type { CompletionDelivery } from "./agents/types.js";
2
2
 
3
3
  export function createSpawnPromptGuidelines(
4
4
  completionDelivery: CompletionDelivery,
@@ -1,4 +1,4 @@
1
- import type { SubagentRuntimeSettings } from "./agents.js";
1
+ import type { SubagentRuntimeSettings } from "./agents/types.js";
2
2
 
3
3
  export const STATEFUL_LIMIT_FIELDS = [
4
4
  "maxAgents",
@@ -1,7 +1,7 @@
1
- import type { AgentConfig } from "./agents.js";
1
+ import type { AgentConfig } from "./agents/types.js";
2
2
  import { redactPrivateText } from "./context.js";
3
3
  import { appendDelegationContract } from "./delegation-contract.js";
4
- import { resolveDefaultSubagentTimeoutMs } from "./execution.js";
4
+ import { resolveDefaultSubagentTimeoutMs } from "./execution/runtime-policy.js";
5
5
  import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
6
6
  import type { ManagedAgent } from "./registry.js";
7
7
  import { appendResultInstruction } from "./result-contract.js";