@narumitw/pi-subagents 2.0.5 → 2.1.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 (178) hide show
  1. package/README.md +152 -50
  2. package/dist/chunks/{auto-transport-SY2VHUFH.ts → auto-transport-FUUKFDIG.ts} +6 -6
  3. package/dist/chunks/{capability-grant-CGEWOEKE.ts → capability-grant-PR72SWWS.ts} +4 -4
  4. package/dist/chunks/{chunk-DPPVEQAM.ts → chunk-4AQSF7AS.ts} +1 -1
  5. package/dist/chunks/chunk-4AQSF7AS.ts.map +7 -0
  6. package/dist/chunks/chunk-6H6TBBED.ts +108 -0
  7. package/dist/chunks/chunk-6H6TBBED.ts.map +7 -0
  8. package/dist/chunks/{chunk-DSOOH73Y.ts → chunk-7AAJEUSL.ts} +3 -3
  9. package/dist/chunks/{chunk-DSOOH73Y.ts.map → chunk-7AAJEUSL.ts.map} +2 -2
  10. package/dist/chunks/{chunk-7QPPBBXZ.ts → chunk-D4CR7T73.ts} +21 -3
  11. package/dist/chunks/chunk-D4CR7T73.ts.map +7 -0
  12. package/dist/chunks/{chunk-434NII74.ts → chunk-DIHBUR2E.ts} +54 -8
  13. package/dist/chunks/chunk-DIHBUR2E.ts.map +7 -0
  14. package/dist/chunks/{chunk-NIF42QMF.ts → chunk-FEVPWRMU.ts} +7 -4
  15. package/dist/chunks/chunk-FEVPWRMU.ts.map +7 -0
  16. package/dist/chunks/{chunk-YBUWBRF7.ts → chunk-FXI45N3J.ts} +5 -5
  17. package/dist/chunks/{chunk-S2IWVK3J.ts → chunk-ILEQ27AL.ts} +3 -2
  18. package/dist/chunks/chunk-ILEQ27AL.ts.map +7 -0
  19. package/dist/chunks/{chunk-DQMN4OYM.ts → chunk-ITVWPNU4.ts} +12 -106
  20. package/dist/chunks/chunk-ITVWPNU4.ts.map +7 -0
  21. package/dist/chunks/{chunk-QDVGH2X7.ts → chunk-IWC32VPY.ts} +2 -1
  22. package/dist/chunks/{chunk-QDVGH2X7.ts.map → chunk-IWC32VPY.ts.map} +2 -2
  23. package/dist/chunks/{chunk-I2FAK44T.ts → chunk-JSZIP73U.ts} +48 -10
  24. package/dist/chunks/chunk-JSZIP73U.ts.map +7 -0
  25. package/dist/chunks/{chunk-ZHTNCZIA.ts → chunk-JU6LUNLP.ts} +2 -2
  26. package/dist/chunks/{chunk-PSK43Y6A.ts → chunk-LASD73CM.ts} +2 -2
  27. package/dist/chunks/{chunk-C4L6P266.ts → chunk-LEOYDZI3.ts} +1 -1
  28. package/dist/chunks/{chunk-C4L6P266.ts.map → chunk-LEOYDZI3.ts.map} +2 -2
  29. package/dist/chunks/{chunk-H3BFJ7HJ.ts → chunk-LL4LP2T7.ts} +5 -5
  30. package/dist/chunks/chunk-N2T5IN4X.ts +18 -0
  31. package/dist/chunks/chunk-N2T5IN4X.ts.map +7 -0
  32. package/dist/chunks/{chunk-G3RSMSXJ.ts → chunk-NTRPLF46.ts} +1 -1
  33. package/dist/chunks/chunk-NTRPLF46.ts.map +7 -0
  34. package/dist/chunks/{chunk-CBP76ARQ.ts → chunk-PABJJYP6.ts} +174 -47
  35. package/dist/chunks/chunk-PABJJYP6.ts.map +7 -0
  36. package/dist/chunks/{chunk-EFBPISNT.ts → chunk-PBZMBTNJ.ts} +104 -103
  37. package/dist/chunks/chunk-PBZMBTNJ.ts.map +7 -0
  38. package/dist/chunks/{chunk-SCZ33MYW.ts → chunk-PGLSFLYW.ts} +2 -2
  39. package/dist/chunks/{chunk-O4VO6JUJ.ts → chunk-TM2R67J3.ts} +3 -3
  40. package/dist/chunks/{chunk-QBORTI4W.ts → chunk-TMZRHIIK.ts} +32 -3
  41. package/dist/chunks/chunk-TMZRHIIK.ts.map +7 -0
  42. package/dist/chunks/chunk-TZ34IQ3M.ts +59 -0
  43. package/dist/chunks/chunk-TZ34IQ3M.ts.map +7 -0
  44. package/dist/chunks/chunk-VBDGNNLM.ts +214 -0
  45. package/dist/chunks/chunk-VBDGNNLM.ts.map +7 -0
  46. package/dist/chunks/{chunk-7OBINKFM.ts → chunk-VDG7LTYE.ts} +11 -11
  47. package/dist/chunks/chunk-VDG7LTYE.ts.map +7 -0
  48. package/dist/chunks/{chunk-AEUYS7JC.ts → chunk-X4NMONPE.ts} +1 -1
  49. package/dist/chunks/chunk-X4NMONPE.ts.map +7 -0
  50. package/dist/chunks/{chunk-HKC4ES4B.ts → chunk-YPJEN6NU.ts} +2 -2
  51. package/dist/chunks/{completion-delivery-YPOWSSV3.ts → completion-delivery-JVLNQRWX.ts} +5 -4
  52. package/dist/chunks/{config-status-J4GPWP46.ts → config-status-FKGDECZ3.ts} +7 -6
  53. package/dist/chunks/{config-ui-2YUCUEBM.ts → config-ui-DDKERQHI.ts} +297 -231
  54. package/dist/chunks/config-ui-DDKERQHI.ts.map +7 -0
  55. package/dist/chunks/{consult-IV7UBNQQ.ts → consult-PQ6PRAKC.ts} +14 -13
  56. package/dist/chunks/consult-PQ6PRAKC.ts.map +7 -0
  57. package/dist/chunks/{create-stateful-transport-ZP3IXNV3.ts → create-stateful-transport-JWC2EFYL.ts} +6 -6
  58. package/dist/chunks/{delegation-contract-WJPT4FVS.ts → delegation-contract-LA56I5DT.ts} +2 -2
  59. package/dist/chunks/{discovery-K25T3SH4.ts → discovery-MIFB2U4Y.ts} +3 -3
  60. package/dist/chunks/{execution-LA7HN5YF.ts → execution-Q2JZLKJA.ts} +19 -16
  61. package/dist/chunks/execution-Q2JZLKJA.ts.map +7 -0
  62. package/dist/chunks/{in-process-transport-4RF3J6XK.ts → in-process-transport-HJ6TZXC3.ts} +9 -9
  63. package/dist/chunks/{inspect-XV2QALMR.ts → inspect-UH2TKH6E.ts} +25 -10
  64. package/dist/chunks/inspect-UH2TKH6E.ts.map +7 -0
  65. package/dist/chunks/{persistence-VSGXAW4P.ts → persistence-XHPJBZL7.ts} +11 -7
  66. package/dist/chunks/persistence-XHPJBZL7.ts.map +7 -0
  67. package/dist/chunks/{registry-E6XPJB7L.ts → registry-XDXPECWF.ts} +136 -15
  68. package/dist/chunks/registry-XDXPECWF.ts.map +7 -0
  69. package/dist/chunks/{retained-semantic-state-7WXSGRJU.ts → retained-semantic-state-GM75FZGE.ts} +3 -3
  70. package/dist/chunks/{rpc-transport-NEHBHWX4.ts → rpc-transport-7R7DVCEB.ts} +12 -16
  71. package/dist/chunks/rpc-transport-7R7DVCEB.ts.map +7 -0
  72. package/dist/chunks/{spawn-idempotency-QGHO2WKC.ts → spawn-idempotency-BNHOSMZW.ts} +2 -2
  73. package/dist/chunks/{subprocess-transport-NSHOGDC4.ts → subprocess-transport-VHZJRBTW.ts} +15 -14
  74. package/dist/chunks/subprocess-transport-VHZJRBTW.ts.map +7 -0
  75. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts +164 -0
  76. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts.map +7 -0
  77. package/dist/index.ts +427 -30
  78. package/dist/index.ts.map +3 -3
  79. package/docs/async-runtime-protocol.md +69 -0
  80. package/docs/implementation-notes/pi-subagents-capability-matrix.md +62 -0
  81. package/docs/implementation-notes/pi-subagents-current-direction.md +99 -0
  82. package/docs/implementation-notes/pi-subagents-rpc-v1.md +153 -0
  83. package/docs/pi-subagents-diagrams.md +183 -0
  84. package/package.json +9 -8
  85. package/src/agents/types.ts +2 -0
  86. package/src/async-subagent-benchmark.ts +532 -0
  87. package/src/completion-delivery.ts +71 -4
  88. package/src/completion-render.ts +1 -0
  89. package/src/completion-requirement.ts +308 -0
  90. package/src/config-registration.ts +5 -5
  91. package/src/config-status.ts +66 -70
  92. package/src/config-ui.ts +244 -150
  93. package/src/consult-resources.ts +1 -1
  94. package/src/consult.ts +3 -8
  95. package/src/delegation-contract.ts +55 -9
  96. package/src/execution-plan.ts +1 -1
  97. package/src/execution-ui.ts +40 -29
  98. package/src/execution.ts +2 -3
  99. package/src/inspect.ts +24 -1
  100. package/src/orchestration-metrics.ts +1 -1
  101. package/src/panel-execution.ts +2 -3
  102. package/src/panel-failure.ts +1 -1
  103. package/src/panel-render.ts +1 -1
  104. package/src/parallel-limit-ui.ts +6 -5
  105. package/src/params.ts +2 -1
  106. package/src/persistence.ts +9 -0
  107. package/src/process-control.ts +43 -0
  108. package/src/registry-types.ts +5 -0
  109. package/src/registry.ts +162 -18
  110. package/src/render.ts +2 -1
  111. package/src/rpc-transport.ts +1 -1
  112. package/src/runner-outcome.ts +1 -1
  113. package/src/runner-result.ts +1 -1
  114. package/src/runner-types.ts +102 -0
  115. package/src/runner.ts +11 -192
  116. package/src/settings/inspection.ts +27 -0
  117. package/src/settings/schema.ts +9 -0
  118. package/src/settings-reader.ts +7 -0
  119. package/src/settings.ts +20 -0
  120. package/src/spawn-idempotency.ts +5 -0
  121. package/src/stateful-agent-view.ts +15 -19
  122. package/src/stateful-guidance.ts +14 -4
  123. package/src/stateful-limit-ui.ts +23 -20
  124. package/src/stateful-limits.ts +10 -10
  125. package/src/stateful-registration.ts +126 -9
  126. package/src/stateful-render.ts +27 -2
  127. package/src/subagent-details.ts +43 -0
  128. package/src/subagents-extension.ts +66 -8
  129. package/src/subagents.ts +2 -0
  130. package/src/subprocess-transport.ts +2 -1
  131. package/src/supervision.ts +2 -1
  132. package/src/timeout-finalization.ts +1 -1
  133. package/src/tool-schema-compatibility.ts +73 -0
  134. package/src/transport-types.ts +6 -0
  135. package/src/transport-ui.ts +18 -46
  136. package/src/usage-recording-config.ts +13 -0
  137. package/src/usage-recording-store.ts +183 -0
  138. package/src/usage-recording.ts +478 -0
  139. package/src/verification-harness.ts +1 -1
  140. package/src/workflow-ui.ts +17 -9
  141. package/dist/chunks/chunk-434NII74.ts.map +0 -7
  142. package/dist/chunks/chunk-7OBINKFM.ts.map +0 -7
  143. package/dist/chunks/chunk-7QPPBBXZ.ts.map +0 -7
  144. package/dist/chunks/chunk-AEUYS7JC.ts.map +0 -7
  145. package/dist/chunks/chunk-CBP76ARQ.ts.map +0 -7
  146. package/dist/chunks/chunk-DPPVEQAM.ts.map +0 -7
  147. package/dist/chunks/chunk-DQMN4OYM.ts.map +0 -7
  148. package/dist/chunks/chunk-EFBPISNT.ts.map +0 -7
  149. package/dist/chunks/chunk-G3RSMSXJ.ts.map +0 -7
  150. package/dist/chunks/chunk-I2FAK44T.ts.map +0 -7
  151. package/dist/chunks/chunk-NIF42QMF.ts.map +0 -7
  152. package/dist/chunks/chunk-QBORTI4W.ts.map +0 -7
  153. package/dist/chunks/chunk-S2IWVK3J.ts.map +0 -7
  154. package/dist/chunks/config-ui-2YUCUEBM.ts.map +0 -7
  155. package/dist/chunks/consult-IV7UBNQQ.ts.map +0 -7
  156. package/dist/chunks/execution-LA7HN5YF.ts.map +0 -7
  157. package/dist/chunks/inspect-XV2QALMR.ts.map +0 -7
  158. package/dist/chunks/persistence-VSGXAW4P.ts.map +0 -7
  159. package/dist/chunks/registry-E6XPJB7L.ts.map +0 -7
  160. package/dist/chunks/rpc-transport-NEHBHWX4.ts.map +0 -7
  161. package/dist/chunks/subprocess-transport-NSHOGDC4.ts.map +0 -7
  162. /package/dist/chunks/{auto-transport-SY2VHUFH.ts.map → auto-transport-FUUKFDIG.ts.map} +0 -0
  163. /package/dist/chunks/{capability-grant-CGEWOEKE.ts.map → capability-grant-PR72SWWS.ts.map} +0 -0
  164. /package/dist/chunks/{chunk-YBUWBRF7.ts.map → chunk-FXI45N3J.ts.map} +0 -0
  165. /package/dist/chunks/{chunk-ZHTNCZIA.ts.map → chunk-JU6LUNLP.ts.map} +0 -0
  166. /package/dist/chunks/{chunk-PSK43Y6A.ts.map → chunk-LASD73CM.ts.map} +0 -0
  167. /package/dist/chunks/{chunk-H3BFJ7HJ.ts.map → chunk-LL4LP2T7.ts.map} +0 -0
  168. /package/dist/chunks/{chunk-SCZ33MYW.ts.map → chunk-PGLSFLYW.ts.map} +0 -0
  169. /package/dist/chunks/{chunk-O4VO6JUJ.ts.map → chunk-TM2R67J3.ts.map} +0 -0
  170. /package/dist/chunks/{chunk-HKC4ES4B.ts.map → chunk-YPJEN6NU.ts.map} +0 -0
  171. /package/dist/chunks/{completion-delivery-YPOWSSV3.ts.map → completion-delivery-JVLNQRWX.ts.map} +0 -0
  172. /package/dist/chunks/{config-status-J4GPWP46.ts.map → config-status-FKGDECZ3.ts.map} +0 -0
  173. /package/dist/chunks/{create-stateful-transport-ZP3IXNV3.ts.map → create-stateful-transport-JWC2EFYL.ts.map} +0 -0
  174. /package/dist/chunks/{delegation-contract-WJPT4FVS.ts.map → delegation-contract-LA56I5DT.ts.map} +0 -0
  175. /package/dist/chunks/{discovery-K25T3SH4.ts.map → discovery-MIFB2U4Y.ts.map} +0 -0
  176. /package/dist/chunks/{in-process-transport-4RF3J6XK.ts.map → in-process-transport-HJ6TZXC3.ts.map} +0 -0
  177. /package/dist/chunks/{retained-semantic-state-7WXSGRJU.ts.map → retained-semantic-state-GM75FZGE.ts.map} +0 -0
  178. /package/dist/chunks/{spawn-idempotency-QGHO2WKC.ts.map → spawn-idempotency-BNHOSMZW.ts.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import type { ConsultResourcePolicy } from "./agents/types.js";
2
2
  import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
3
3
  import { resolvePiPromptResources } from "./prompt-resources.js";
4
- import type { ChildLaunchPolicy } from "./runner.js";
4
+ import type { ChildLaunchPolicy } from "./runner-types.js";
5
5
 
6
6
  const MINIMAL_CONSULT_SYSTEM_PROMPT =
7
7
  "You are a read-only consultation assistant. Analyze the delegated task using only executor-provided capabilities and return a grounded answer.";
package/src/consult.ts CHANGED
@@ -34,20 +34,15 @@ import {
34
34
  DEFAULT_MAX_STDERR_BYTES,
35
35
  MAX_SUBAGENT_TIMEOUT_MS,
36
36
  } from "./limits.js";
37
- import {
38
- type ChildLaunchPolicy,
39
- getResultFinalOutput,
40
- isResultError,
41
- runSingleAgent,
42
- type SingleResult,
43
- type SubagentDetails,
44
- } from "./runner.js";
37
+ import { getResultFinalOutput, isResultError, runSingleAgent } from "./runner.js";
38
+ import type { ChildLaunchPolicy, SingleResult } from "./runner-types.js";
45
39
  import { boundedPrivateText, boundText, safeDisplayPath, safeTerminalLine } from "./safe-text.js";
46
40
  import {
47
41
  DEFAULT_CONSULT_RESOURCE_POLICY,
48
42
  DEFAULT_CONSULTATION_CWD_POLICY,
49
43
  } from "./settings/inspection.js";
50
44
  import { resolveSubagentThinkingLevel } from "./settings.js";
45
+ import type { SubagentDetails } from "./subagent-details.js";
51
46
 
52
47
  export { registerSubagentConsult } from "./consult-registration.js";
53
48
  export type {
@@ -39,14 +39,51 @@ export const DelegationContractSchema = Type.Object(
39
39
  requestedAuthority: Type.Optional(
40
40
  Type.Object(
41
41
  {
42
- capabilities: Type.Optional(BoundedItems),
43
- tools: Type.Optional(BoundedItems),
44
- readPaths: Type.Optional(BoundedItems),
45
- writePaths: Type.Optional(BoundedItems),
46
- network: Type.Optional(StringEnum(AUTHORITY_REQUIREMENTS)),
47
- secrets: Type.Optional(StringEnum(AUTHORITY_REQUIREMENTS)),
42
+ capabilities: Type.Optional(
43
+ Type.Array(BoundedItem, {
44
+ maxItems: MAX_ITEMS,
45
+ description: "Capabilities requested from the selected agent manifest.",
46
+ }),
47
+ ),
48
+ tools: Type.Optional(
49
+ Type.Array(BoundedItem, {
50
+ maxItems: MAX_ITEMS,
51
+ description:
52
+ "Tools that enforcement may narrow to the selected agent's available set.",
53
+ }),
54
+ ),
55
+ readPaths: Type.Optional(
56
+ Type.Array(BoundedItem, {
57
+ maxItems: MAX_ITEMS,
58
+ description:
59
+ 'Audit metadata only; enforcement "enforce" rejects non-empty readPaths because path scope is unsupported.',
60
+ }),
61
+ ),
62
+ writePaths: Type.Optional(
63
+ Type.Array(BoundedItem, {
64
+ maxItems: MAX_ITEMS,
65
+ description:
66
+ 'Audit metadata only; enforcement "enforce" rejects non-empty writePaths because path scope is unsupported.',
67
+ }),
68
+ ),
69
+ network: Type.Optional(
70
+ StringEnum(AUTHORITY_REQUIREMENTS, {
71
+ description:
72
+ 'Audit metadata only; enforcement "enforce" rejects denied or required network guarantees as unsupported.',
73
+ }),
74
+ ),
75
+ secrets: Type.Optional(
76
+ StringEnum(AUTHORITY_REQUIREMENTS, {
77
+ description:
78
+ 'Audit metadata only; enforcement "enforce" rejects denied or required secret guarantees as unsupported.',
79
+ }),
80
+ ),
81
+ },
82
+ {
83
+ additionalProperties: false,
84
+ description:
85
+ "Only capabilities and tools have executor enforcement; readPaths, writePaths, network, and secrets are unsupported guarantees.",
48
86
  },
49
- { additionalProperties: false },
50
87
  ),
51
88
  ),
52
89
  acceptanceCriteria: Type.Optional(BoundedItems),
@@ -76,9 +113,18 @@ export const DelegationContractSchema = Type.Object(
76
113
  ),
77
114
  ),
78
115
  sideEffectPolicy: Type.Optional(StringEnum(DELEGATION_SIDE_EFFECT_POLICIES)),
79
- enforcement: Type.Optional(StringEnum(DELEGATION_ENFORCEMENT_MODES)),
116
+ enforcement: Type.Optional(
117
+ StringEnum(DELEGATION_ENFORCEMENT_MODES, {
118
+ description:
119
+ "Defaults to audit; enforce rejects unsupported requestedAuthority guarantees before child launch.",
120
+ }),
121
+ ),
122
+ },
123
+ {
124
+ additionalProperties: false,
125
+ description:
126
+ "Advanced delegation contract. For ordinary delegation omit this object; use it only for explicit acceptance, authority, evidence, or admission semantics.",
80
127
  },
81
- { additionalProperties: false },
82
128
  );
83
129
 
84
130
  export interface DelegationDependency {
@@ -201,7 +201,7 @@ export function acknowledgeExecutionPlan(plan: ExecutionPlan): ExecutionAcknowle
201
201
  }
202
202
  if (plan.unsupportedGuarantees.length > 0) {
203
203
  reasonCodes.push("unsupported-guarantee");
204
- recoveryActions.push("stop");
204
+ recoveryActions.push("repair-contract", "stop");
205
205
  }
206
206
  return {
207
207
  version: EXECUTION_ACKNOWLEDGEMENT_VERSION,
@@ -8,8 +8,8 @@ export function executionAgentPickerScreen(agents: readonly AgentConfig[]) {
8
8
  const configured = readSubagentSettings()?.agents ?? {};
9
9
  return {
10
10
  kind: "actions" as const,
11
- title: "Agent Execution Defaults",
12
- lines: ["Choose an agent to edit its inherited model, thinking, or timeout."],
11
+ title: "Model, Thinking, and Time Limit",
12
+ lines: ["Choose a subagent. A specific request can override these starting values."],
13
13
  items: [
14
14
  ...agents.map((agent) => {
15
15
  const value = configured[agent.name];
@@ -17,7 +17,7 @@ export function executionAgentPickerScreen(agents: readonly AgentConfig[]) {
17
17
  id: agent.name,
18
18
  label: safeTerminalText(agent.name),
19
19
  description: safeTerminalText(
20
- `${agent.source} · model ${value?.model ?? agent.model ?? "inherited"} · thinking ${value?.thinkingLevel ?? agent.thinkingLevel ?? "inherited"} · timeout ${value?.timeoutMs ?? agent.timeoutMs ?? "inherited"}`,
20
+ `${agent.source} · model ${value?.model ?? agent.model ?? "default"} · thinking ${value?.thinkingLevel ?? agent.thinkingLevel ?? "default"} · time limit ${value?.timeoutMs ?? agent.timeoutMs ?? "default"}`,
21
21
  ),
22
22
  action: "pick-execution-agent" as const,
23
23
  };
@@ -32,8 +32,8 @@ export function executionAgentScreen(agent: AgentConfig | undefined) {
32
32
  if (!agent) {
33
33
  return {
34
34
  kind: "actions" as const,
35
- title: "Agent Execution Defaults",
36
- lines: ["No agent selected."],
35
+ title: "Model, Thinking, and Time Limit",
36
+ lines: ["No subagent selected."],
37
37
  items: [{ id: "back", label: "Back", action: "back" as const }],
38
38
  hint: "back" as const,
39
39
  };
@@ -41,21 +41,21 @@ export function executionAgentScreen(agent: AgentConfig | undefined) {
41
41
  const configured = readSubagentSettings()?.agents?.[agent.name];
42
42
  return {
43
43
  kind: "actions" as const,
44
- title: `${safeTerminalText(agent.name)} execution`,
44
+ title: `${safeTerminalText(agent.name)} defaults`,
45
45
  lines: [
46
- `Model: ${safeTerminalText(configured?.model ?? agent.model ?? "inherited")}`,
47
- `Thinking: ${configured?.thinkingLevel ?? agent.thinkingLevel ?? "inherited"}`,
48
- `Timeout: ${configured?.timeoutMs ?? agent.timeoutMs ?? "inherited"}`,
49
- "Explicit tool-call values remain authoritative.",
46
+ `Model: ${safeTerminalText(configured?.model ?? agent.model ?? "agent or Pi default")}`,
47
+ `Thinking: ${configured?.thinkingLevel ?? agent.thinkingLevel ?? "agent or Pi default"}`,
48
+ `Time limit: ${configured?.timeoutMs ?? agent.timeoutMs ?? "agent or Pi default"}`,
49
+ "A specific subagent request can override these defaults.",
50
50
  ],
51
51
  items: [
52
52
  { id: "thinking", label: "Thinking level", to: "execution-thinking" as const },
53
53
  { id: "model", label: "Model", to: "execution-model" as const },
54
- { id: "timeout", label: "Timeout", to: "execution-timeout" as const },
54
+ { id: "timeout", label: "Time limit", to: "execution-timeout" as const },
55
55
  {
56
56
  id: "reset",
57
- label: "Reset execution defaults",
58
- description: "Restore frontmatter or Pi inheritance without changing tools",
57
+ label: "Use agent or Pi defaults",
58
+ description: "Remove custom model, thinking, and time limit without changing tools",
59
59
  action: "reset-agent-execution" as const,
60
60
  },
61
61
  { id: "back", label: "Back", action: "back" as const },
@@ -74,9 +74,19 @@ export function executionThinkingScreen(agent: AgentConfig | undefined) {
74
74
  {
75
75
  id: "thinking",
76
76
  label: "Default thinking level",
77
- description: "Explicit spawn or blocking call values still win.",
78
- currentValue: configured?.thinkingLevel ?? agent.thinkingLevel ?? "Inherited",
79
- values: ["Inherited", "off", "minimal", "low", "medium", "high", "xhigh", "max"],
77
+ description: "A specific subagent request can override this value.",
78
+ currentValue:
79
+ configured?.thinkingLevel ?? agent.thinkingLevel ?? "Use agent or Pi default",
80
+ values: [
81
+ "Use agent or Pi default",
82
+ "off",
83
+ "minimal",
84
+ "low",
85
+ "medium",
86
+ "high",
87
+ "xhigh",
88
+ "max",
89
+ ],
80
90
  action: "set-agent-thinking" as const,
81
91
  },
82
92
  ]
@@ -96,15 +106,15 @@ export function executionModelScreen(agent: AgentConfig | undefined, ctx: Extens
96
106
  kind: "actions" as const,
97
107
  title: agent ? `${safeTerminalText(agent.name)} model` : "Agent model",
98
108
  lines: [
99
- `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "inherited")}`,
100
- "Choose a session-available model or enter a custom Pi model pattern.",
109
+ `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "agent or Pi default")}`,
110
+ "Choose an available model or enter a custom Pi model pattern.",
101
111
  ],
102
112
  items: agent
103
113
  ? [
104
114
  {
105
115
  id: "model:__inherited__",
106
- label: "Inherited",
107
- description: "Use frontmatter or active Pi model resolution",
116
+ label: "Use agent or Pi default",
117
+ description: "Remove the custom model setting",
108
118
  action: "set-agent-model" as const,
109
119
  },
110
120
  ...models.map((model) => ({
@@ -131,9 +141,9 @@ export function executionModelInputScreen(agent: AgentConfig | undefined) {
131
141
  kind: "input" as const,
132
142
  title: agent ? `${safeTerminalText(agent.name)} model` : "Agent model",
133
143
  lines: [
134
- `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "inherited")}`,
144
+ `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "agent or Pi default")}`,
135
145
  "Enter a Pi CLI model pattern, including an optional :thinking suffix.",
136
- "Use Reset execution defaults to restore inheritance.",
146
+ "Use agent or Pi defaults to remove this custom value.",
137
147
  ],
138
148
  placeholder: "provider/model or model pattern",
139
149
  action: "set-agent-model" as const,
@@ -145,13 +155,13 @@ export function executionTimeoutInputScreen(agent: AgentConfig | undefined) {
145
155
  const configured = agent ? readSubagentSettings()?.agents?.[agent.name] : undefined;
146
156
  return {
147
157
  kind: "input" as const,
148
- title: agent ? `${safeTerminalText(agent.name)} timeout` : "Agent timeout",
158
+ title: agent ? `${safeTerminalText(agent.name)} time limit` : "Agent time limit",
149
159
  lines: [
150
- `Current: ${configured?.timeoutMs ?? agent?.timeoutMs ?? "inherited"}`,
160
+ `Current: ${configured?.timeoutMs ?? agent?.timeoutMs ?? "agent or Pi default"}`,
151
161
  `Allowed: 1-${MAX_SUBAGENT_TIMEOUT_MS} milliseconds.`,
152
- "Use Reset execution defaults to restore inheritance.",
162
+ "Use agent or Pi defaults to remove this custom value.",
153
163
  ],
154
- placeholder: "Timeout in milliseconds",
164
+ placeholder: "Time limit in milliseconds",
155
165
  action: "set-agent-timeout" as const,
156
166
  hint: "back" as const,
157
167
  };
@@ -163,7 +173,8 @@ export function applyAgentThinking(
163
173
  ctx: ExtensionCommandContext,
164
174
  ) {
165
175
  if (!agent) return { kind: "rejected" as const };
166
- const thinkingLevel = value === "Inherited" ? undefined : (value as SubagentThinkingLevel);
176
+ const thinkingLevel =
177
+ value === "Use agent or Pi default" ? undefined : (value as SubagentThinkingLevel);
167
178
  if (
168
179
  thinkingLevel !== undefined &&
169
180
  !["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(thinkingLevel)
@@ -196,12 +207,12 @@ export function applyAgentTimeout(
196
207
  if (!agent) return { kind: "rejected" as const };
197
208
  const normalized = value?.trim() ?? "";
198
209
  if (!/^\d+$/u.test(normalized)) {
199
- ctx.ui.notify("Timeout must be a whole number of milliseconds.", "warning");
210
+ ctx.ui.notify("Time limit must be a whole number of milliseconds.", "warning");
200
211
  return { kind: "rejected" as const };
201
212
  }
202
213
  const timeoutMs = Number(normalized);
203
214
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_SUBAGENT_TIMEOUT_MS) {
204
- ctx.ui.notify(`Timeout must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}.`, "warning");
215
+ ctx.ui.notify(`Time limit must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}.`, "warning");
205
216
  return { kind: "rejected" as const };
206
217
  }
207
218
  return saveAgentPatch(agent.name, { timeoutMs }, ctx, "timeout");
package/src/execution.ts CHANGED
@@ -65,17 +65,16 @@ import {
65
65
  getResultFinalOutput,
66
66
  isResultError,
67
67
  mapWithConcurrencyLimit,
68
- type OnUpdateCallback,
69
68
  runSingleAgent,
70
- type SingleResult,
71
- type SubagentDetails,
72
69
  } from "./runner.js";
70
+ import type { SingleResult } from "./runner-types.js";
73
71
  import { safeTerminalLine } from "./safe-text.js";
74
72
  import {
75
73
  DEFAULT_DELEGATION_CWD_POLICY,
76
74
  resolveBlockingMaxParallelTasks,
77
75
  } from "./settings/inspection.js";
78
76
  import { readSubagentSettings, resolveSubagentThinkingLevel } from "./settings.js";
77
+ import type { OnUpdateCallback, SubagentDetails } from "./subagent-details.js";
79
78
  import { isRetryableResult, runHedgedAttempt, supervisionDelay } from "./supervision.js";
80
79
  import { TimeoutProgressJournal, TURN_TERMINATION_VERSION } from "./timeout-checkpoint.js";
81
80
  import type { TurnLimits } from "./turn-budget.js";
package/src/inspect.ts CHANGED
@@ -25,8 +25,10 @@ import {
25
25
  inspectStatefulLimitSettings,
26
26
  inspectStatefulTransportSettings,
27
27
  inspectSubagentSettings,
28
+ inspectUsageRecordingSettings,
28
29
  } from "./settings.js";
29
30
  import type { StatefulSubagentRuntimeStatus } from "./stateful.js";
31
+ import type { UsageRecordingStatus } from "./usage-recording.js";
30
32
  import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
31
33
  import { inspectSessionWorkflows } from "./work-item-persistence.js";
32
34
 
@@ -41,6 +43,7 @@ export interface SubagentInspectRuntime {
41
43
  getConsultResourcePolicy(): "project-context" | "none" | "all";
42
44
  getConsultationCwdPolicy(): ConsultationCwdPolicy;
43
45
  getDelegationCwdPolicy(): DelegationCwdPolicy;
46
+ getUsageRecordingStatus?(): UsageRecordingStatus;
44
47
  getRuntimeStatus(): StatefulSubagentRuntimeStatus;
45
48
  listRunInspection(includeClosed?: boolean): AgentRunInspectionSummary[];
46
49
  getRunInspection(agentId: string): AgentRunInspectionDetail | undefined;
@@ -344,6 +347,9 @@ function projectRunSummary(run: AgentRunInspectionSummary): Record<string, unkno
344
347
  unreadMessages: run.unreadMessages,
345
348
  turnGeneration: run.turnGeneration,
346
349
  pendingCompletionCount: run.pendingCompletionCount,
350
+ ...(run.pendingRequiredCompletionCount === undefined
351
+ ? {}
352
+ : { pendingRequiredCompletionCount: run.pendingRequiredCompletionCount }),
347
353
  };
348
354
  }
349
355
 
@@ -379,6 +385,9 @@ function projectRun(run: AgentRunInspectionDetail, ctx: ExtensionContext): Recor
379
385
  }
380
386
  : undefined,
381
387
  resultFormat: run.resultFormat ?? "text",
388
+ completionRequirements: (run.completionRequirements ?? []).map((requirement) => ({
389
+ ...requirement,
390
+ })),
382
391
  structuredResult: run.structuredResult,
383
392
  termination: run.termination,
384
393
  outcome: run.outcome,
@@ -577,6 +586,8 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
577
586
  const parallelLimit = inspectBlockingParallelLimitSettings();
578
587
  const detachedLimits = inspectStatefulLimitSettings();
579
588
  const transport = inspectStatefulTransportSettings();
589
+ const usageRecording = inspectUsageRecordingSettings();
590
+ const usageStatus = runtime.getUsageRecordingStatus?.();
580
591
  const configuredDetachedLimits = detachedLimits.values
581
592
  ? Object.fromEntries(
582
593
  Object.entries(detachedLimits.values).map(([field, snapshot]) => [field, snapshot.value]),
@@ -599,6 +610,16 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
599
610
  configuredStatefulLimitSources: configuredDetachedLimitSources,
600
611
  configuredCompletionDelivery: completion.value,
601
612
  configuredCompletionDeliverySource: completion.source,
613
+ usageRecording: usageStatus
614
+ ? {
615
+ enabled: usageStatus.enabled,
616
+ retentionDays: usageStatus.retentionDays,
617
+ recordedEvents: usageStatus.recordedEvents,
618
+ writeFailure: usageStatus.writeFailure,
619
+ }
620
+ : { enabled: false },
621
+ configuredUsageRecording: usageRecording.enabled,
622
+ configuredUsageRecordingSource: usageRecording.source,
602
623
  maxParallelTasks: runtime.getMaxParallelTasks(),
603
624
  configuredMaxParallelTasks: parallelLimit.value,
604
625
  configuredMaxParallelTasksSource: parallelLimit.source,
@@ -619,7 +640,8 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
619
640
  completion.error ||
620
641
  parallelLimit.error ||
621
642
  detachedLimits.error ||
622
- transport.error
643
+ transport.error ||
644
+ usageRecording.error
623
645
  ? boundedPrivateText(
624
646
  configured.error ??
625
647
  resources.error ??
@@ -628,6 +650,7 @@ function projectStatus(runtime: SubagentInspectRuntime): Record<string, unknown>
628
650
  parallelLimit.error ??
629
651
  detachedLimits.error ??
630
652
  transport.error ??
653
+ usageRecording.error ??
631
654
  "",
632
655
  2 * 1024,
633
656
  )
@@ -1,4 +1,4 @@
1
- import type { SingleResult } from "./runner.js";
1
+ import type { SingleResult } from "./runner-types.js";
2
2
  import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
3
3
 
4
4
  export interface OrchestrationMetrics {
@@ -31,15 +31,14 @@ import { buildPanelReviewerPrompt, buildPanelSynthesisPrompt } from "./panel-pro
31
31
  import { reconcilePanel } from "./panel-reconciliation.js";
32
32
  import type { SubagentParams } from "./params.js";
33
33
  import {
34
- type ChildLaunchPolicy,
35
34
  getResultFinalOutput,
36
35
  isResultError,
37
36
  mapWithConcurrencyLimit,
38
37
  runSingleAgent,
39
- type SingleResult,
40
- type SubagentDetails,
41
38
  } from "./runner.js";
39
+ import type { ChildLaunchPolicy, SingleResult } from "./runner-types.js";
42
40
  import { boundedPrivateText, boundText } from "./safe-text.js";
41
+ import type { SubagentDetails } from "./subagent-details.js";
43
42
  import { createSessionWorkItemPersistence } from "./work-item-persistence.js";
44
43
  import { assertWorkspaceIsolationReady } from "./workspace.js";
45
44
 
@@ -1,4 +1,4 @@
1
- import type { SingleResult } from "./runner.js";
1
+ import type { SingleResult } from "./runner-types.js";
2
2
 
3
3
  export const PANEL_FAILURE_KINDS = [
4
4
  "transient-launch",
@@ -2,7 +2,7 @@ import type { Theme } from "@earendil-works/pi-coding-agent";
2
2
  import { Text } from "@earendil-works/pi-tui";
3
3
  import type { SubagentParams } from "./params.js";
4
4
  import { safeBlock, safeLine } from "./render-common.js";
5
- import type { SubagentDetails } from "./runner.js";
5
+ import type { SubagentDetails } from "./subagent-details.js";
6
6
 
7
7
  export function renderPanelCall(args: SubagentParams, theme: Theme): Text | undefined {
8
8
  const panel = args.panel;
@@ -14,11 +14,12 @@ export function blockingParallelLimitScreen(runtime: BlockingParallelLimitRuntim
14
14
  const limit = inspectBlockingParallelLimitSettings();
15
15
  return {
16
16
  kind: "input" as const,
17
- title: "Maximum Parallel Workers",
17
+ title: "Blocking Worker Limit",
18
18
  lines: [
19
- `Current: ${runtime.getMaxParallelTasks()} worker tasks per blocking call`,
19
+ "This applies only to requests that make Pi wait for delegated work.",
20
+ `Current: ${runtime.getMaxParallelTasks()} subagents per blocking request`,
20
21
  `Allowed: 1-${MAX_CONFIGURABLE_PARALLEL_TASKS}`,
21
- `Parallel execution still starts at most ${MAX_BLOCKING_PARALLEL_CONCURRENCY} workers at once.`,
22
+ `Pi still starts at most ${MAX_BLOCKING_PARALLEL_CONCURRENCY} of them at once.`,
22
23
  safeTerminalText(limit.path),
23
24
  ],
24
25
  placeholder: "Enter a whole number",
@@ -89,7 +90,7 @@ export function applyBlockingParallelLimitSetting(
89
90
  }
90
91
  }
91
92
  ctx.ui.notify(
92
- `${settingsChanged ? "Saved and " : ""}applied: maximum ${next} parallel workers per blocking call.`,
93
+ `${settingsChanged ? "Saved and " : ""}applied: maximum ${next} subagents per blocking request.`,
93
94
  "info",
94
95
  );
95
96
  return { kind: "back" as const };
@@ -97,7 +98,7 @@ export function applyBlockingParallelLimitSetting(
97
98
 
98
99
  function notifyValidationError(ctx: ExtensionCommandContext): void {
99
100
  ctx.ui.notify(
100
- `Maximum parallel workers must be a whole number from 1 to ${MAX_CONFIGURABLE_PARALLEL_TASKS}.`,
101
+ `Blocking worker limit must be a whole number from 1 to ${MAX_CONFIGURABLE_PARALLEL_TASKS}.`,
101
102
  "warning",
102
103
  );
103
104
  }
package/src/params.ts CHANGED
@@ -5,6 +5,7 @@ import { DelegationContractSchema } from "./delegation-contract.js";
5
5
  import { MAX_CONFIGURABLE_PARALLEL_TASKS, MAX_SUBAGENT_TIMEOUT_MS } from "./limits.js";
6
6
  import { PANEL_PRESETS } from "./panel-presets.js";
7
7
  import { SUBAGENT_RESULT_FORMATS } from "./result-contract.js";
8
+ import { grammarSafeToolObject } from "./tool-schema-compatibility.js";
8
9
  import { MAX_SUBAGENT_TOOL_CALLS, MAX_SUBAGENT_TURNS } from "./turn-budget.js";
9
10
  import { VerifiedExecutionContractSchema } from "./verified-execution-schema.js";
10
11
 
@@ -202,7 +203,7 @@ const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
202
203
  default: "user",
203
204
  });
204
205
 
205
- export const SubagentParams = Type.Object({
206
+ export const SubagentParams = grammarSafeToolObject({
206
207
  agent: Type.Optional(
207
208
  Type.String({ description: "Name of the agent to invoke (for single mode)" }),
208
209
  ),
@@ -5,6 +5,7 @@ import { getAgentDir, withFileMutationQueue } from "@earendil-works/pi-coding-ag
5
5
  import { projectAgentRecords } from "./agent-projection.js";
6
6
  import { isThinkingLevel } from "./agents/types.js";
7
7
  import { isCapabilityGrant, revokeCapabilityGrant } from "./capability-grant.js";
8
+ import { isCompletionRequirementRecord } from "./completion-requirement.js";
8
9
  import { redactPrivateText } from "./context.js";
9
10
  import { normalizeDelegationContract } from "./delegation-contract.js";
10
11
  import { copyExecutionPlan, isExecutionPlan } from "./execution-plan.js";
@@ -171,6 +172,10 @@ function sanitizeAgent(agent: ManagedAgent): ManagedAgent {
171
172
  output: redactPrivateText(completion.output),
172
173
  error: completion.error ? redactPrivateText(completion.error) : undefined,
173
174
  })),
175
+ completionRequirements: (agent.completionRequirements ?? [])
176
+ .filter(isCompletionRequirementRecord)
177
+ .slice(-MAX_STORED_COMPLETIONS_PER_AGENT)
178
+ .map((record) => ({ ...record })),
174
179
  currentTimeoutMs: undefined,
175
180
  currentIdleTimeoutMs: undefined,
176
181
  currentMaxTurns: undefined,
@@ -261,6 +266,10 @@ function isStoredState(value: unknown): value is StoredState {
261
266
  (record.turnGeneration === undefined || isNonNegativeInteger(record.turnGeneration)) &&
262
267
  (record.pendingCompletions === undefined ||
263
268
  isCompletionOutbox(record.pendingCompletions, record.turnGeneration ?? 0)) &&
269
+ (record.completionRequirements === undefined ||
270
+ (Array.isArray(record.completionRequirements) &&
271
+ record.completionRequirements.length <= MAX_STORED_COMPLETIONS_PER_AGENT &&
272
+ record.completionRequirements.every(isCompletionRequirementRecord))) &&
264
273
  (record.spawnIdempotencyKey === undefined ||
265
274
  (typeof record.spawnIdempotencyKey === "string" &&
266
275
  record.spawnIdempotencyKey.length > 0 &&
@@ -0,0 +1,43 @@
1
+ import type { spawn } from "node:child_process";
2
+
3
+ export const KILL_GRACE_MS = 5000;
4
+
5
+ function signalProcess(proc: ReturnType<typeof spawn>, signal: NodeJS.Signals): void {
6
+ if (process.platform !== "win32" && proc.pid) {
7
+ try {
8
+ process.kill(-proc.pid, signal);
9
+ return;
10
+ } catch {
11
+ // Fall back to signaling the immediate child when process-group signaling is unavailable.
12
+ }
13
+ }
14
+ try {
15
+ proc.kill(signal);
16
+ } catch {
17
+ // The process may already have exited.
18
+ }
19
+ }
20
+
21
+ export function terminateProcess(
22
+ proc: ReturnType<typeof spawn>,
23
+ graceMs = KILL_GRACE_MS,
24
+ ): () => void {
25
+ const leaderExited = proc.exitCode !== null || proc.signalCode !== null;
26
+ const capturedOutputClosed = [proc.stdout, proc.stderr].every(
27
+ (stream) => !stream || stream.readableEnded || stream.destroyed,
28
+ );
29
+ let closed = leaderExited && capturedOutputClosed;
30
+ const onClose = () => {
31
+ closed = true;
32
+ };
33
+ proc.once("close", onClose);
34
+ if (!closed) signalProcess(proc, "SIGTERM");
35
+ const escalation = setTimeout(() => {
36
+ if (!closed) signalProcess(proc, "SIGKILL");
37
+ }, graceMs);
38
+ escalation.unref();
39
+ return () => {
40
+ clearTimeout(escalation);
41
+ proc.off("close", onClose);
42
+ };
43
+ }
@@ -1,5 +1,6 @@
1
1
  import type { SubagentThinkingLevel } from "./agents/types.js";
2
2
  import type { CapabilityGrant } from "./capability-grant.js";
3
+ import type { CompletionRequirementRecord } from "./completion-requirement.js";
3
4
  import type { TargetPolicyAudit } from "./cwd-policy.js";
4
5
  import type { DelegationContract } from "./delegation-contract.js";
5
6
  import type { ExecutionPlan } from "./execution-plan.js";
@@ -14,6 +15,7 @@ export type AgentLifecycleState =
14
15
  | "running"
15
16
  | "idle"
16
17
  | "completed"
18
+ | "partial"
17
19
  | "blocked"
18
20
  | "needs-input"
19
21
  | "abstained"
@@ -85,6 +87,7 @@ export interface ManagedAgent {
85
87
  currentRunId?: string;
86
88
  currentTurnGeneration?: number;
87
89
  pendingCompletions?: PersistedAgentCompletion[];
90
+ completionRequirements?: CompletionRequirementRecord[];
88
91
  history: AgentTurn[];
89
92
  error?: string;
90
93
  context?: string;
@@ -123,6 +126,7 @@ export interface AgentRunInspectionSummary {
123
126
  unreadMessages: number;
124
127
  turnGeneration: number;
125
128
  pendingCompletionCount: number;
129
+ pendingRequiredCompletionCount?: number;
126
130
  }
127
131
 
128
132
  export interface AgentRunInspectionDetail extends AgentRunInspectionSummary {
@@ -156,6 +160,7 @@ export interface AgentRunInspectionDetail extends AgentRunInspectionSummary {
156
160
  semanticSnapshot?: SemanticSnapshot;
157
161
  semanticCompatibility?: SemanticCompatibility;
158
162
  termination?: TurnTerminationReport;
163
+ completionRequirements?: CompletionRequirementRecord[];
159
164
  telemetry?: TransportTelemetry;
160
165
  }
161
166