@narumitw/pi-subagents 2.0.6 → 2.1.1

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 (180) hide show
  1. package/README.md +171 -60
  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-CBP76ARQ.ts → chunk-2LMJU25E.ts} +172 -74
  5. package/dist/chunks/chunk-2LMJU25E.ts.map +7 -0
  6. package/dist/chunks/{chunk-DPPVEQAM.ts → chunk-4AQSF7AS.ts} +1 -1
  7. package/dist/chunks/chunk-4AQSF7AS.ts.map +7 -0
  8. package/dist/chunks/chunk-6H6TBBED.ts +108 -0
  9. package/dist/chunks/chunk-6H6TBBED.ts.map +7 -0
  10. package/dist/chunks/{chunk-YBUWBRF7.ts → chunk-6NSJVPXX.ts} +6 -16
  11. package/dist/chunks/{chunk-YBUWBRF7.ts.map → chunk-6NSJVPXX.ts.map} +2 -2
  12. package/dist/chunks/{chunk-DSOOH73Y.ts → chunk-7AAJEUSL.ts} +3 -3
  13. package/dist/chunks/{chunk-DSOOH73Y.ts.map → chunk-7AAJEUSL.ts.map} +2 -2
  14. package/dist/chunks/{chunk-7QPPBBXZ.ts → chunk-D4CR7T73.ts} +21 -3
  15. package/dist/chunks/chunk-D4CR7T73.ts.map +7 -0
  16. package/dist/chunks/{chunk-NIF42QMF.ts → chunk-FEVPWRMU.ts} +7 -4
  17. package/dist/chunks/chunk-FEVPWRMU.ts.map +7 -0
  18. package/dist/chunks/{chunk-S2IWVK3J.ts → chunk-ILEQ27AL.ts} +3 -2
  19. package/dist/chunks/chunk-ILEQ27AL.ts.map +7 -0
  20. package/dist/chunks/{chunk-DQMN4OYM.ts → chunk-ITVWPNU4.ts} +12 -106
  21. package/dist/chunks/chunk-ITVWPNU4.ts.map +7 -0
  22. package/dist/chunks/{chunk-QDVGH2X7.ts → chunk-IWC32VPY.ts} +2 -1
  23. package/dist/chunks/{chunk-QDVGH2X7.ts.map → chunk-IWC32VPY.ts.map} +2 -2
  24. package/dist/chunks/{chunk-I2FAK44T.ts → chunk-JSZIP73U.ts} +48 -10
  25. package/dist/chunks/chunk-JSZIP73U.ts.map +7 -0
  26. package/dist/chunks/{chunk-ZHTNCZIA.ts → chunk-JU6LUNLP.ts} +2 -2
  27. package/dist/chunks/{chunk-PSK43Y6A.ts → chunk-LASD73CM.ts} +2 -2
  28. package/dist/chunks/{chunk-C4L6P266.ts → chunk-LEOYDZI3.ts} +1 -1
  29. package/dist/chunks/{chunk-C4L6P266.ts.map → chunk-LEOYDZI3.ts.map} +2 -2
  30. package/dist/chunks/{chunk-H3BFJ7HJ.ts → chunk-LL4LP2T7.ts} +5 -5
  31. package/dist/chunks/chunk-N2T5IN4X.ts +18 -0
  32. package/dist/chunks/chunk-N2T5IN4X.ts.map +7 -0
  33. package/dist/chunks/chunk-NLT67IZS.ts +322 -0
  34. package/dist/chunks/chunk-NLT67IZS.ts.map +7 -0
  35. package/dist/chunks/{chunk-G3RSMSXJ.ts → chunk-NTRPLF46.ts} +1 -1
  36. package/dist/chunks/chunk-NTRPLF46.ts.map +7 -0
  37. package/dist/chunks/{chunk-EFBPISNT.ts → chunk-PBZMBTNJ.ts} +104 -103
  38. package/dist/chunks/chunk-PBZMBTNJ.ts.map +7 -0
  39. package/dist/chunks/{chunk-SCZ33MYW.ts → chunk-PGLSFLYW.ts} +2 -2
  40. package/dist/chunks/{chunk-O4VO6JUJ.ts → chunk-TM2R67J3.ts} +3 -3
  41. package/dist/chunks/{chunk-QBORTI4W.ts → chunk-TMZRHIIK.ts} +32 -3
  42. package/dist/chunks/chunk-TMZRHIIK.ts.map +7 -0
  43. package/dist/chunks/chunk-TZ34IQ3M.ts +59 -0
  44. package/dist/chunks/chunk-TZ34IQ3M.ts.map +7 -0
  45. package/dist/chunks/{chunk-7OBINKFM.ts → chunk-VDG7LTYE.ts} +11 -11
  46. package/dist/chunks/chunk-VDG7LTYE.ts.map +7 -0
  47. package/dist/chunks/{chunk-AEUYS7JC.ts → chunk-X4NMONPE.ts} +1 -1
  48. package/dist/chunks/chunk-X4NMONPE.ts.map +7 -0
  49. package/dist/chunks/{chunk-HKC4ES4B.ts → chunk-YPJEN6NU.ts} +2 -2
  50. package/dist/chunks/{chunk-434NII74.ts → chunk-YU53SHA7.ts} +54 -8
  51. package/dist/chunks/chunk-YU53SHA7.ts.map +7 -0
  52. package/dist/chunks/{completion-delivery-YPOWSSV3.ts → completion-delivery-RSJU6BXL.ts} +5 -4
  53. package/dist/chunks/{config-status-J4GPWP46.ts → config-status-FKGDECZ3.ts} +7 -6
  54. package/dist/chunks/{config-ui-2YUCUEBM.ts → config-ui-ABHYNGQ7.ts} +297 -231
  55. package/dist/chunks/config-ui-ABHYNGQ7.ts.map +7 -0
  56. package/dist/chunks/{consult-IV7UBNQQ.ts → consult-LJU3IQY5.ts} +14 -13
  57. package/dist/chunks/consult-LJU3IQY5.ts.map +7 -0
  58. package/dist/chunks/{create-stateful-transport-ZP3IXNV3.ts → create-stateful-transport-JWC2EFYL.ts} +6 -6
  59. package/dist/chunks/{delegation-contract-WJPT4FVS.ts → delegation-contract-LA56I5DT.ts} +2 -2
  60. package/dist/chunks/{discovery-K25T3SH4.ts → discovery-MIFB2U4Y.ts} +3 -3
  61. package/dist/chunks/{execution-LA7HN5YF.ts → execution-Q2JZLKJA.ts} +19 -16
  62. package/dist/chunks/execution-Q2JZLKJA.ts.map +7 -0
  63. package/dist/chunks/{in-process-transport-4RF3J6XK.ts → in-process-transport-HJ6TZXC3.ts} +9 -9
  64. package/dist/chunks/{inspect-XV2QALMR.ts → inspect-UH2TKH6E.ts} +25 -10
  65. package/dist/chunks/inspect-UH2TKH6E.ts.map +7 -0
  66. package/dist/chunks/{persistence-VSGXAW4P.ts → persistence-UY3PY6E5.ts} +11 -7
  67. package/dist/chunks/persistence-UY3PY6E5.ts.map +7 -0
  68. package/dist/chunks/{registry-E6XPJB7L.ts → registry-BT54L6CY.ts} +136 -15
  69. package/dist/chunks/registry-BT54L6CY.ts.map +7 -0
  70. package/dist/chunks/{retained-semantic-state-7WXSGRJU.ts → retained-semantic-state-GM75FZGE.ts} +3 -3
  71. package/dist/chunks/{rpc-transport-NEHBHWX4.ts → rpc-transport-7R7DVCEB.ts} +12 -16
  72. package/dist/chunks/rpc-transport-7R7DVCEB.ts.map +7 -0
  73. package/dist/chunks/{spawn-idempotency-QGHO2WKC.ts → spawn-idempotency-BNHOSMZW.ts} +2 -2
  74. package/dist/chunks/{subprocess-transport-NSHOGDC4.ts → subprocess-transport-VHZJRBTW.ts} +15 -14
  75. package/dist/chunks/subprocess-transport-VHZJRBTW.ts.map +7 -0
  76. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts +164 -0
  77. package/dist/chunks/usage-recording-store-CW3EH2SZ.ts.map +7 -0
  78. package/dist/index.ts +682 -82
  79. package/dist/index.ts.map +3 -3
  80. package/docs/async-runtime-protocol.md +83 -0
  81. package/docs/implementation-notes/pi-subagents-capability-matrix.md +62 -0
  82. package/docs/implementation-notes/pi-subagents-current-direction.md +99 -0
  83. package/docs/implementation-notes/pi-subagents-rpc-v1.md +153 -0
  84. package/docs/pi-subagents-diagrams.md +183 -0
  85. package/package.json +9 -8
  86. package/src/agents/types.ts +2 -0
  87. package/src/async-subagent-benchmark.ts +532 -0
  88. package/src/completion-delivery.ts +71 -4
  89. package/src/completion-render.ts +1 -0
  90. package/src/completion-requirement.ts +479 -0
  91. package/src/config-registration.ts +5 -5
  92. package/src/config-status.ts +66 -70
  93. package/src/config-ui.ts +244 -150
  94. package/src/consult-registration.ts +4 -12
  95. package/src/consult-resources.ts +1 -1
  96. package/src/consult.ts +3 -8
  97. package/src/delegation-contract.ts +55 -9
  98. package/src/execution-plan.ts +1 -1
  99. package/src/execution-ui.ts +40 -29
  100. package/src/execution.ts +2 -3
  101. package/src/inspect.ts +24 -1
  102. package/src/orchestration-metrics.ts +1 -1
  103. package/src/panel-execution.ts +2 -3
  104. package/src/panel-failure.ts +1 -1
  105. package/src/panel-render.ts +1 -1
  106. package/src/parallel-limit-ui.ts +6 -5
  107. package/src/params.ts +2 -1
  108. package/src/persistence.ts +9 -0
  109. package/src/process-control.ts +43 -0
  110. package/src/registry-types.ts +5 -0
  111. package/src/registry.ts +162 -18
  112. package/src/render.ts +2 -1
  113. package/src/rpc-transport.ts +1 -1
  114. package/src/runner-outcome.ts +1 -1
  115. package/src/runner-result.ts +1 -1
  116. package/src/runner-types.ts +102 -0
  117. package/src/runner.ts +11 -192
  118. package/src/session-guidance-contract.ts +309 -0
  119. package/src/settings/inspection.ts +27 -0
  120. package/src/settings/schema.ts +9 -0
  121. package/src/settings-reader.ts +7 -0
  122. package/src/settings.ts +20 -0
  123. package/src/spawn-idempotency.ts +5 -0
  124. package/src/stateful-agent-view.ts +15 -19
  125. package/src/stateful-guidance.ts +11 -18
  126. package/src/stateful-limit-ui.ts +23 -20
  127. package/src/stateful-limits.ts +10 -10
  128. package/src/stateful-registration.ts +126 -36
  129. package/src/stateful-render.ts +27 -2
  130. package/src/subagent-details.ts +43 -0
  131. package/src/subagents-extension.ts +116 -68
  132. package/src/subagents.ts +2 -0
  133. package/src/subprocess-transport.ts +2 -1
  134. package/src/supervision.ts +2 -1
  135. package/src/timeout-finalization.ts +1 -1
  136. package/src/tool-schema-compatibility.ts +73 -0
  137. package/src/transport-types.ts +6 -0
  138. package/src/transport-ui.ts +18 -46
  139. package/src/usage-recording-config.ts +13 -0
  140. package/src/usage-recording-store.ts +183 -0
  141. package/src/usage-recording.ts +478 -0
  142. package/src/verification-harness.ts +1 -1
  143. package/src/workflow-ui.ts +17 -9
  144. package/dist/chunks/chunk-434NII74.ts.map +0 -7
  145. package/dist/chunks/chunk-7OBINKFM.ts.map +0 -7
  146. package/dist/chunks/chunk-7QPPBBXZ.ts.map +0 -7
  147. package/dist/chunks/chunk-AEUYS7JC.ts.map +0 -7
  148. package/dist/chunks/chunk-CBP76ARQ.ts.map +0 -7
  149. package/dist/chunks/chunk-DPPVEQAM.ts.map +0 -7
  150. package/dist/chunks/chunk-DQMN4OYM.ts.map +0 -7
  151. package/dist/chunks/chunk-EFBPISNT.ts.map +0 -7
  152. package/dist/chunks/chunk-G3RSMSXJ.ts.map +0 -7
  153. package/dist/chunks/chunk-I2FAK44T.ts.map +0 -7
  154. package/dist/chunks/chunk-NIF42QMF.ts.map +0 -7
  155. package/dist/chunks/chunk-QBORTI4W.ts.map +0 -7
  156. package/dist/chunks/chunk-S2IWVK3J.ts.map +0 -7
  157. package/dist/chunks/config-ui-2YUCUEBM.ts.map +0 -7
  158. package/dist/chunks/consult-IV7UBNQQ.ts.map +0 -7
  159. package/dist/chunks/execution-LA7HN5YF.ts.map +0 -7
  160. package/dist/chunks/inspect-XV2QALMR.ts.map +0 -7
  161. package/dist/chunks/persistence-VSGXAW4P.ts.map +0 -7
  162. package/dist/chunks/registry-E6XPJB7L.ts.map +0 -7
  163. package/dist/chunks/rpc-transport-NEHBHWX4.ts.map +0 -7
  164. package/dist/chunks/subprocess-transport-NSHOGDC4.ts.map +0 -7
  165. /package/dist/chunks/{auto-transport-SY2VHUFH.ts.map → auto-transport-FUUKFDIG.ts.map} +0 -0
  166. /package/dist/chunks/{capability-grant-CGEWOEKE.ts.map → capability-grant-PR72SWWS.ts.map} +0 -0
  167. /package/dist/chunks/{chunk-ZHTNCZIA.ts.map → chunk-JU6LUNLP.ts.map} +0 -0
  168. /package/dist/chunks/{chunk-PSK43Y6A.ts.map → chunk-LASD73CM.ts.map} +0 -0
  169. /package/dist/chunks/{chunk-H3BFJ7HJ.ts.map → chunk-LL4LP2T7.ts.map} +0 -0
  170. /package/dist/chunks/{chunk-SCZ33MYW.ts.map → chunk-PGLSFLYW.ts.map} +0 -0
  171. /package/dist/chunks/{chunk-O4VO6JUJ.ts.map → chunk-TM2R67J3.ts.map} +0 -0
  172. /package/dist/chunks/{chunk-HKC4ES4B.ts.map → chunk-YPJEN6NU.ts.map} +0 -0
  173. /package/dist/chunks/{completion-delivery-YPOWSSV3.ts.map → completion-delivery-RSJU6BXL.ts.map} +0 -0
  174. /package/dist/chunks/{config-status-J4GPWP46.ts.map → config-status-FKGDECZ3.ts.map} +0 -0
  175. /package/dist/chunks/{create-stateful-transport-ZP3IXNV3.ts.map → create-stateful-transport-JWC2EFYL.ts.map} +0 -0
  176. /package/dist/chunks/{delegation-contract-WJPT4FVS.ts.map → delegation-contract-LA56I5DT.ts.map} +0 -0
  177. /package/dist/chunks/{discovery-K25T3SH4.ts.map → discovery-MIFB2U4Y.ts.map} +0 -0
  178. /package/dist/chunks/{in-process-transport-4RF3J6XK.ts.map → in-process-transport-HJ6TZXC3.ts.map} +0 -0
  179. /package/dist/chunks/{retained-semantic-state-7WXSGRJU.ts.map → retained-semantic-state-GM75FZGE.ts.map} +0 -0
  180. /package/dist/chunks/{spawn-idempotency-QGHO2WKC.ts.map → spawn-idempotency-BNHOSMZW.ts.map} +0 -0
package/src/config-ui.ts CHANGED
@@ -44,6 +44,7 @@ import {
44
44
  inspectConsultResourceSettings,
45
45
  inspectCwdPolicySettings,
46
46
  inspectDelegationWorkflowSettings,
47
+ inspectUsageRecordingSettings,
47
48
  readSubagentSettings,
48
49
  sameToolSet,
49
50
  uniqueToolNames,
@@ -52,6 +53,7 @@ import {
52
53
  updateConsultResourceSetting,
53
54
  updateCwdPolicySetting,
54
55
  updateDelegationWorkflowSetting,
56
+ updateUsageRecordingSetting,
55
57
  } from "./settings.js";
56
58
  import { formatStatefulAgentLine, type StatefulSubagentRuntimeStatus } from "./stateful.js";
57
59
  import {
@@ -62,17 +64,15 @@ import {
62
64
  statefulLimitListScreen,
63
65
  } from "./stateful-limit-ui.js";
64
66
  import { isStatefulLimitField, type StatefulLimitField } from "./stateful-limits.js";
65
- import {
66
- applyTransportSetting,
67
- responsivenessSetupScreen,
68
- transportSettingsScreen,
69
- } from "./transport-ui.js";
67
+ import { applyTransportSetting, transportLabel, transportSettingsScreen } from "./transport-ui.js";
68
+ import type { UsageRecordingStatus } from "./usage-recording.js";
69
+ import { USAGE_RECORDING_RETENTION_DAYS } from "./usage-recording-config.js";
70
70
  import { showWorkflowPreview, workflowLabel } from "./workflow-ui.js";
71
71
 
72
72
  const SUBCOMMANDS = [
73
- { value: "settings", label: "settings", description: "Configure subagent user settings" },
74
- { value: "status", label: "status", description: "Show effective subagent settings" },
75
- { value: "help", label: "help", description: "Show subagent settings help" },
73
+ { value: "settings", label: "settings", description: "Open grouped subagent settings" },
74
+ { value: "status", label: "status", description: "Show detailed subagent diagnostics" },
75
+ { value: "help", label: "help", description: "Show subagent first steps and safety help" },
76
76
  ];
77
77
  const TOOL_VIEWPORT_SIZE = 10;
78
78
 
@@ -83,6 +83,9 @@ export interface SubagentSettingsRuntime {
83
83
  getConsultResourcePolicy(): ConsultResourcePolicy;
84
84
  getConsultationCwdPolicy(): ConsultationCwdPolicy;
85
85
  getDelegationCwdPolicy(): DelegationCwdPolicy;
86
+ getUsageRecordingEnabled?(): boolean;
87
+ getUsageRecordingStatus?(): UsageRecordingStatus;
88
+ setUsageRecordingEnabled?(value: boolean): Promise<void>;
86
89
  setMaxParallelTasks(value: number): void;
87
90
  setCompletionDelivery(value: CompletionDelivery): void;
88
91
  setConsultResourcePolicy(value: ConsultResourcePolicy): void;
@@ -135,7 +138,7 @@ function registerSubagentPrimaryCommand(
135
138
  owner: SubagentMenuOwner,
136
139
  ) {
137
140
  pi.registerCommand("subagents", {
138
- description: "Manage current-session subagents and user settings",
141
+ description: "Manage subagents, settings, diagnostics, and help",
139
142
  getArgumentCompletions(prefix: string) {
140
143
  const normalized = prefix.trim().toLowerCase();
141
144
  const matches = SUBCOMMANDS.filter((item) => item.value.startsWith(normalized));
@@ -149,7 +152,7 @@ function registerSubagentPrimaryCommand(
149
152
  }
150
153
  switch (subcommand) {
151
154
  case "settings":
152
- await showSubagentSettings(ctx, runtime, owner);
155
+ await showSubagentSettings(pi, ctx, runtime, owner);
153
156
  return;
154
157
  case "status":
155
158
  showSubagentStatus(ctx, runtime);
@@ -171,6 +174,7 @@ export async function showSubagentManager(
171
174
  ctx: ExtensionCommandContext,
172
175
  runtime: SubagentSettingsRuntime,
173
176
  owner: SubagentMenuOwner,
177
+ start: "main" | "settings-hub" = "main",
174
178
  ) {
175
179
  if (ctx.mode !== "tui") {
176
180
  showSubagentStatus(ctx, runtime);
@@ -188,10 +192,11 @@ export async function showSubagentManager(
188
192
  | "main"
189
193
  | "workflow"
190
194
  | "agents"
191
- | "settings"
192
- | "advanced"
193
- | "performance"
194
- | "responsiveness"
195
+ | "settings-hub"
196
+ | "access-settings"
197
+ | "behavior-settings"
198
+ | "agent-settings"
199
+ | "runtime-settings"
195
200
  | "transport"
196
201
  | "execution-agent-picker"
197
202
  | "execution-agent"
@@ -222,6 +227,7 @@ export async function showSubagentManager(
222
227
  | "set-consult-resources"
223
228
  | "set-consultation-cwd"
224
229
  | "set-delegation-cwd"
230
+ | "set-usage-recording"
225
231
  | "load-agent-picker"
226
232
  | "pick-agent"
227
233
  | "toggle-tool"
@@ -229,7 +235,7 @@ export async function showSubagentManager(
229
235
  | "discard-tools"
230
236
  | "back";
231
237
  const menu = defineMenu<undefined, Screen, Action, ExtensionCommandContext>({
232
- start: "main",
238
+ start,
233
239
  screens: {
234
240
  main: () => {
235
241
  const status = runtime.getRuntimeStatus();
@@ -241,29 +247,34 @@ export async function showSubagentManager(
241
247
  items: [
242
248
  {
243
249
  id: "workflow",
244
- label: "Change delegation",
245
- description: "Choose async only (recommended) or a compatibility workflow",
250
+ label: "How subagents run",
251
+ description: "Choose whether Pi stays available while subagents work",
246
252
  to: "workflow",
247
253
  },
248
254
  {
249
255
  id: "agents",
250
- label: "Current agents",
251
- description: `${status.activeAgents} active · ${status.retainedAgents} retained`,
256
+ label: "Current subagents",
257
+ description: `${status.activeAgents} working · ${status.retainedAgents} saved for follow-up`,
252
258
  to: "agents",
253
259
  },
254
260
  {
255
261
  id: "settings",
256
262
  label: "Settings",
257
- description: "Configure targets, trusted resources, and async completion",
258
- to: "settings",
263
+ description: "Folders, completion, privacy, agent defaults, and advanced options",
264
+ to: "settings-hub",
265
+ },
266
+ {
267
+ id: "status",
268
+ label: "Diagnostics",
269
+ description: "Detailed runtime values, setting sources, and file paths",
270
+ to: "status",
259
271
  },
260
272
  {
261
- id: "advanced",
262
- label: "Advanced settings",
263
- description: "Agent permissions, parallel limit, runtime details, and settings path",
264
- to: "advanced",
273
+ id: "help",
274
+ label: "Help",
275
+ description: "First steps, settings behavior, commands, and safety",
276
+ to: "help",
265
277
  },
266
- { id: "help", label: "Help", to: "help" },
267
278
  ],
268
279
  hint: "close",
269
280
  };
@@ -273,11 +284,13 @@ export async function showSubagentManager(
273
284
  const active = currentWorkflow(runtime, runtime.getRuntimeStatus());
274
285
  return {
275
286
  kind: "actions",
276
- title: "Change Delegation",
287
+ title: "How Subagents Run",
277
288
  lines: [
278
289
  `Current: ${workflowLabel(active)}`,
279
- "Recommended: Async only keeps the main agent responsive and omits blocking delegation and consultation.",
280
- "Final-answer-dependent detached work needs automatic resume.",
290
+ "Choose a workflow based on the tools and blocking behavior you need.",
291
+ "The blocking subagent tool is deprecated and remains available only for compatibility.",
292
+ "subagent_consult and subagent_await remain supported blocking helpers.",
293
+ "For results needed in the current answer, enable automatic continuation in Settings.",
281
294
  ...(snapshot.value !== active
282
295
  ? [`Configured after reload: ${workflowLabel(snapshot.value)}`]
283
296
  : []),
@@ -292,21 +305,23 @@ export async function showSubagentManager(
292
305
  ? []
293
306
  : [
294
307
  {
295
- id: "async-only",
296
- label: "Async only · Recommended",
297
- description: "Detached lifecycle plus inspection; omit blocking and consultation",
308
+ id: "all",
309
+ label: workflowLabel("all"),
310
+ description:
311
+ "Background agents plus deprecated subagent and supported blocking helpers",
298
312
  action: "set-workflow" as const,
299
313
  },
300
314
  {
301
- id: "all",
302
- label: "All delegation methods",
303
- description: "Compatibility: async, blocking, inspection, and consultation",
315
+ id: "async-only",
316
+ label: workflowLabel("async-only"),
317
+ description: "Subagents work in the background while you continue using Pi",
304
318
  action: "set-workflow" as const,
305
319
  },
306
320
  {
307
321
  id: "blocking-only",
308
- label: "Blocking only",
309
- description: "Compatibility: blocking and consultation without async lifecycle",
322
+ label: workflowLabel("blocking-only"),
323
+ description:
324
+ "No background agents; deprecated subagent plus read-only consultation",
310
325
  action: "set-workflow" as const,
311
326
  },
312
327
  ],
@@ -318,17 +333,20 @@ export async function showSubagentManager(
318
333
  const status = runtime.getRuntimeStatus();
319
334
  return {
320
335
  kind: "actions",
321
- title: "Current-session Subagents",
336
+ title: "Current Subagents",
322
337
  lines: agents.length
323
- ? agents.map(formatStatefulAgentLine)
338
+ ? [
339
+ "Working subagents and subagents saved for follow-up in this session.",
340
+ ...agents.map(formatStatefulAgentLine),
341
+ ]
324
342
  : [formatEmptyStatefulRuntime(status)],
325
343
  items: [
326
344
  ...(agents.length > 0
327
345
  ? [
328
346
  {
329
347
  id: "clear",
330
- label: "Clear current-session agents",
331
- description: "Close and delete retained agents for this session",
348
+ label: "Clear current subagents",
349
+ description: "Stop running work and remove subagents saved for follow-up",
332
350
  action: "clear-agents" as const,
333
351
  },
334
352
  ]
@@ -338,29 +356,82 @@ export async function showSubagentManager(
338
356
  hint: "back",
339
357
  };
340
358
  },
341
- settings: () => subagentSettingsScreen(runtime),
342
- advanced: () => {
359
+ "settings-hub": () => ({
360
+ kind: "actions",
361
+ title: "Subagent Settings",
362
+ lines: [
363
+ "Changes are saved immediately.",
364
+ "Transport and background-agent limits take effect after /reload.",
365
+ ],
366
+ items: [
367
+ {
368
+ id: "access",
369
+ label: "Folders and trusted resources",
370
+ description: "Where subagents can start and what trusted consultations can load",
371
+ to: "access-settings",
372
+ },
373
+ {
374
+ id: "behavior",
375
+ label: "Completion and privacy",
376
+ description: "What Pi does when work finishes and optional local recording",
377
+ to: "behavior-settings",
378
+ },
379
+ {
380
+ id: "agents",
381
+ label: "Agent defaults",
382
+ description: "Tools, model, thinking effort, and time limit for each subagent",
383
+ to: "agent-settings",
384
+ },
385
+ {
386
+ id: "runtime",
387
+ label: "Advanced runtime settings",
388
+ description: "Transport and capacity controls that most users can leave unchanged",
389
+ to: "runtime-settings",
390
+ },
391
+ { id: "back", label: "Back", action: "back" },
392
+ ],
393
+ hint: "back",
394
+ }),
395
+ "access-settings": () => subagentAccessSettingsScreen(runtime),
396
+ "behavior-settings": () => subagentBehaviorSettingsScreen(runtime),
397
+ "agent-settings": () => ({
398
+ kind: "actions",
399
+ title: "Agent Defaults",
400
+ lines: ["Choose the starting settings for each subagent."],
401
+ items: [
402
+ {
403
+ id: "agent-tools",
404
+ label: "Tool permissions",
405
+ description: "Choose which tools each subagent may use",
406
+ action: "load-agent-picker",
407
+ },
408
+ {
409
+ id: "execution",
410
+ label: "Model, thinking, and time limit",
411
+ description: "Choose how each subagent starts unless a request overrides it",
412
+ to: "execution-agent-picker",
413
+ },
414
+ { id: "back", label: "Back", action: "back" },
415
+ ],
416
+ hint: "back",
417
+ }),
418
+ "runtime-settings": () => {
343
419
  const limit = inspectBlockingParallelLimitSettings();
344
420
  return {
345
421
  kind: "actions",
346
- title: "Advanced Subagent Settings",
422
+ title: "Advanced Runtime Settings",
423
+ lines: ["Most users can leave these settings unchanged."],
347
424
  items: [
348
425
  {
349
- id: "agent-tools",
350
- label: "Agent tool permissions",
351
- description: "Customize persistent per-agent tool allow-lists",
352
- action: "load-agent-picker",
353
- },
354
- {
355
- id: "status",
356
- label: "Runtime details",
357
- description: "Show transport, configured source, and settings path",
358
- to: "status",
426
+ id: "transport",
427
+ label: "Transport",
428
+ description: `How Pi hosts background subagents · Current: ${transportLabel(runtime.getRuntimeStatus().transport)}`,
429
+ to: "transport",
359
430
  },
360
431
  {
361
432
  id: "parallel-limit",
362
- label: "Maximum parallel workers",
363
- description: `Current: ${runtime.getMaxParallelTasks()} per blocking call`,
433
+ label: "Blocking worker limit",
434
+ description: `Maximum subagents per blocking request · Current: ${runtime.getMaxParallelTasks()}`,
364
435
  to: "parallel-limit",
365
436
  disabled: limit.error !== undefined,
366
437
  disabledReason: limit.error
@@ -369,43 +440,15 @@ export async function showSubagentManager(
369
440
  },
370
441
  {
371
442
  id: "stateful-limits",
372
- label: "Detached agent limits",
443
+ label: "Background agent limits",
373
444
  description: formatDetachedLimitSummary(runtime.getRuntimeStatus()),
374
445
  to: "stateful-limits",
375
446
  },
376
- {
377
- id: "performance",
378
- label: "Performance and execution",
379
- description: "Transport, responsiveness, and agent defaults",
380
- to: "performance",
381
- },
382
447
  { id: "back", label: "Back", action: "back" },
383
448
  ],
384
449
  hint: "back",
385
450
  };
386
451
  },
387
- performance: () => ({
388
- kind: "actions",
389
- title: "Performance and Execution",
390
- items: [
391
- {
392
- id: "responsiveness",
393
- label: "Responsiveness setup",
394
- description: "Preview automatic retained transport and completion guidance",
395
- to: "responsiveness",
396
- },
397
- { id: "transport", label: "Detached transport", to: "transport" },
398
- {
399
- id: "agents",
400
- label: "Agent execution defaults",
401
- description: "Model, thinking level, and timeout",
402
- to: "execution-agent-picker",
403
- },
404
- { id: "back", label: "Back", action: "back" },
405
- ],
406
- hint: "back",
407
- }),
408
- responsiveness: () => responsivenessSetupScreen(runtime),
409
452
  transport: () => transportSettingsScreen(runtime),
410
453
  "execution-agent-picker": () => executionAgentPickerScreen(availableAgents),
411
454
  "execution-agent": () => executionAgentScreen(selectedExecutionAgent),
@@ -418,13 +461,13 @@ export async function showSubagentManager(
418
461
  "stateful-limit-input": () => statefulLimitInputScreen(selectedStatefulLimit, runtime),
419
462
  status: () => ({
420
463
  kind: "detail",
421
- title: "Subagent runtime details",
464
+ title: "Subagent Diagnostics",
422
465
  lines: statusLines(runtime),
423
466
  hint: "back",
424
467
  }),
425
468
  help: () => ({
426
469
  kind: "detail",
427
- title: "Subagents help",
470
+ title: "Subagents Help",
428
471
  lines: helpLines(runtime),
429
472
  hint: "back",
430
473
  }),
@@ -433,8 +476,8 @@ export async function showSubagentManager(
433
476
  const configured = settings.agents ?? {};
434
477
  return {
435
478
  kind: "actions",
436
- title: "Subagent Tool Configuration",
437
- lines: ["Select an agent to configure its allowed tools."],
479
+ title: "Tool Permissions",
480
+ lines: ["Choose a subagent to change which tools it may use."],
438
481
  items: availableAgents.map((agent) => {
439
482
  const override = configured[agent.name];
440
483
  const hasOverride = override ? hasOwn(override, "tools") : false;
@@ -495,7 +538,7 @@ export async function showSubagentManager(
495
538
  if (snapshot.error) return { kind: "rejected" };
496
539
  const active = currentWorkflow(runtime, runtime.getRuntimeStatus());
497
540
  if (itemId === active && itemId === snapshot.value) {
498
- ctx.ui.notify(`Delegation already uses ${workflowLabel(itemId)}.`, "info");
541
+ ctx.ui.notify(`Subagents already use ${workflowLabel(itemId)}.`, "info");
499
542
  return { kind: "stay" };
500
543
  }
501
544
  const requiresReload = itemId !== active;
@@ -513,7 +556,7 @@ export async function showSubagentManager(
513
556
  updateDelegationWorkflowSetting(itemId);
514
557
  } catch (error) {
515
558
  ctx.ui.notify(
516
- `Delegation settings were not saved: ${formatError(error)}. The current workflow is unchanged.`,
559
+ `How subagents run was not saved: ${formatError(error)}. The current choice is unchanged.`,
517
560
  "error",
518
561
  );
519
562
  return { kind: "rejected" };
@@ -536,8 +579,8 @@ export async function showSubagentManager(
536
579
  const agents = runtime.listAgents();
537
580
  if (agents.length === 0) return { kind: "stay" };
538
581
  const confirmed = await ctx.ui.confirm(
539
- "Clear current-session subagents?",
540
- `Close and delete ${agents.length} retained agent${agents.length === 1 ? "" : "s"}?`,
582
+ "Clear current subagents?",
583
+ `Stop work and remove ${agents.length} subagent${agents.length === 1 ? "" : "s"} saved for follow-up?`,
541
584
  { signal },
542
585
  );
543
586
  if (signal.aborted || !isCurrent()) return { kind: "close" };
@@ -549,17 +592,14 @@ export async function showSubagentManager(
549
592
  .join("\0") !== agents.map((agent) => agent.id).join("\0")
550
593
  ) {
551
594
  ctx.ui.notify(
552
- "Detached agents changed while confirming; review the list again.",
595
+ "Current subagents changed while confirming; review the list again.",
553
596
  "warning",
554
597
  );
555
598
  return { kind: "rejected" };
556
599
  }
557
600
  const cleared = await runtime.clearAgents();
558
601
  if (signal.aborted || !isCurrent()) return { kind: "close" };
559
- ctx.ui.notify(
560
- `Cleared ${cleared} current-session subagent${cleared === 1 ? "" : "s"}.`,
561
- "info",
562
- );
602
+ ctx.ui.notify(`Cleared ${cleared} current subagent${cleared === 1 ? "" : "s"}.`, "info");
563
603
  return { kind: "stay" };
564
604
  },
565
605
  "set-transport": async ({ itemId, signal }) =>
@@ -600,6 +640,8 @@ export async function showSubagentManager(
600
640
  applyConsultResourceSetting(value, ctx, runtime),
601
641
  "set-consultation-cwd": async ({ value }) => applyConsultationCwdSetting(value, ctx, runtime),
602
642
  "set-delegation-cwd": async ({ value }) => applyDelegationCwdSetting(value, ctx, runtime),
643
+ "set-usage-recording": async ({ value, signal }) =>
644
+ applyUsageRecordingSetting(value, ctx, runtime, { signal, isCurrent }),
603
645
  "load-agent-picker": async () => {
604
646
  availableAgents = discoverAgents(ctx.cwd, "user", readSubagentSettings() ?? {}).agents;
605
647
  if (availableAgents.length === 0) {
@@ -676,6 +718,7 @@ export async function showSubagentManager(
676
718
  }
677
719
 
678
720
  export async function showSubagentSettings(
721
+ pi: ExtensionAPI,
679
722
  ctx: ExtensionCommandContext,
680
723
  runtime: SubagentSettingsRuntime,
681
724
  owner: SubagentMenuOwner,
@@ -690,45 +733,20 @@ export async function showSubagentSettings(
690
733
  }
691
734
  return;
692
735
  }
693
- const generation = owner.generation;
694
- const isCurrent = () => generation === owner.generation && !owner.controller.signal.aborted;
695
- const { defineMenu, runMenu } = await import("@narumitw/pi-tui-kit");
696
- if (!isCurrent()) return;
697
- type SettingsAction =
698
- | "set-completion"
699
- | "set-consult-resources"
700
- | "set-consultation-cwd"
701
- | "set-delegation-cwd";
702
- const menu = defineMenu<undefined, "settings", SettingsAction, ExtensionCommandContext>({
703
- start: "settings",
704
- screens: { settings: () => subagentSettingsScreen(runtime) },
705
- actions: {
706
- "set-completion": async ({ value }) => applyCompletionSetting(value, ctx, runtime),
707
- "set-consult-resources": async ({ value }) =>
708
- applyConsultResourceSetting(value, ctx, runtime),
709
- "set-consultation-cwd": async ({ value }) => applyConsultationCwdSetting(value, ctx, runtime),
710
- "set-delegation-cwd": async ({ value }) => applyDelegationCwdSetting(value, ctx, runtime),
711
- },
712
- });
713
- await runMenu(ctx, menu, {
714
- getState: () => undefined,
715
- signal: owner.controller.signal,
716
- isCurrent,
717
- });
736
+ await showSubagentManager(pi, ctx, runtime, owner, "settings-hub");
718
737
  }
719
738
 
720
- function subagentSettingsScreen(runtime: SubagentSettingsRuntime) {
721
- const completion = inspectCompletionDeliverySettings();
739
+ function subagentAccessSettingsScreen(runtime: SubagentSettingsRuntime) {
722
740
  const consult = inspectConsultResourceSettings();
723
741
  const cwdPolicy = inspectCwdPolicySettings();
724
- const error = completion.error ?? consult.error ?? cwdPolicy.error;
742
+ const error = consult.error ?? cwdPolicy.error;
725
743
  return {
726
744
  kind: "settings" as const,
727
- title: error ? "Subagent User Settings · Read only" : "Subagent User Settings",
745
+ title: error ? "Folders and Trusted Resources · Read only" : "Folders and Trusted Resources",
728
746
  lines: [
729
- "Applies now and to future sessions",
730
- "Target and trust settings control startup resources, not filesystem access or sandboxing.",
731
- "Manage folder trust with Pi /trust; restart Pi after changing it.",
747
+ "Choose where subagents may start and what read-only consultations may load.",
748
+ "These settings do not restrict files, shell commands, network access, or OS permissions.",
749
+ "Manage saved folder trust with Pi /trust, then restart Pi.",
732
750
  safeTerminalText(consult.path),
733
751
  ...(error ? [`Settings cannot be edited: ${safeTerminalText(error)}`] : []),
734
752
  ],
@@ -737,48 +755,124 @@ function subagentSettingsScreen(runtime: SubagentSettingsRuntime) {
737
755
  : [
738
756
  {
739
757
  id: "consultationCwd",
740
- label: "Read-only consultation target",
758
+ label: "Where read-only consultations can start",
741
759
  description:
742
- "Untrusted external targets inherit no target/project resources; agent and package read-only prompts remain.",
760
+ "Use only this workspace, or allow other folders without loading untrusted project resources.",
743
761
  currentValue: consultationCwdLabel(runtime.getConsultationCwdPolicy()),
744
- values: ["Anywhere · untrusted targets inherit nothing", "Current workspace only"],
762
+ values: ["Any folder · no project resources when untrusted", "This workspace only"],
745
763
  action: "set-consultation-cwd" as const,
746
764
  },
747
765
  {
748
766
  id: "delegationCwd",
749
- label: "General delegation target",
767
+ label: "Where subagents can start",
750
768
  description:
751
- "Controls starting directories, not absolute paths, shell commands, or OS permissions.",
769
+ "Limit subagents to this workspace, saved-trusted folders, or any folder Pi can access.",
752
770
  currentValue: delegationCwdLabel(runtime.getDelegationCwdPolicy()),
753
771
  values: [
754
- "Current or saved-trusted folders",
755
- "Current workspace only",
756
- "Anywhere · normal Pi permissions",
772
+ "This workspace or saved-trusted folders",
773
+ "This workspace only",
774
+ "Any folder Pi can access",
757
775
  ],
758
776
  action: "set-delegation-cwd" as const,
759
777
  },
760
778
  {
761
779
  id: "consultResources",
762
- label: "Consultation resources for trusted targets",
780
+ label: "Resources for trusted read-only consultations",
763
781
  description:
764
- "Choose which trusted context, system, skill, and prompt resources a consultation inherits.",
782
+ "Choose how much project context, skills, and prompt guidance a trusted consultation may load.",
765
783
  currentValue: consultResourceLabel(runtime.getConsultResourcePolicy()),
766
- values: ["Project context only", "No inherited resources", "All trusted resources"],
784
+ values: ["Project context only", "No project resources", "All trusted resources"],
767
785
  action: "set-consult-resources" as const,
768
786
  },
787
+ ],
788
+ };
789
+ }
790
+
791
+ function subagentBehaviorSettingsScreen(runtime: SubagentSettingsRuntime) {
792
+ const completion = inspectCompletionDeliverySettings();
793
+ const usageRecording = inspectUsageRecordingSettings();
794
+ const error = completion.error ?? usageRecording.error;
795
+ return {
796
+ kind: "settings" as const,
797
+ title: error ? "Completion and Privacy · Read only" : "Completion and Privacy",
798
+ lines: [
799
+ "These changes apply immediately and to future sessions.",
800
+ safeTerminalText(completion.path),
801
+ ...(error ? [`Settings cannot be edited: ${safeTerminalText(error)}`] : []),
802
+ ],
803
+ items: error
804
+ ? []
805
+ : [
769
806
  {
770
807
  id: "completionDelivery",
771
- label: "When async work finishes",
808
+ label: "When background work finishes",
772
809
  description:
773
- "Wait for your next turn, or request one synthesis turn after the root settles.",
810
+ "Wait for your next message, or steer results into active work and continue automatically from idle.",
774
811
  currentValue: completionLabel(runtime.getCompletionDelivery()),
775
- values: ["Wait until my next turn", "Resume automatically when finished"],
812
+ values: ["Wait for my next message", "Continue automatically when work finishes"],
776
813
  action: "set-completion" as const,
777
814
  },
815
+ {
816
+ id: "usageRecording",
817
+ label: "Local usage recording",
818
+ description: `Optionally keep content-free timing and lifecycle events on this device for ${USAGE_RECORDING_RETENTION_DAYS} days.`,
819
+ currentValue: runtime.getUsageRecordingEnabled?.() ? "On · local only" : "Off",
820
+ values: ["Off", "On · local only"],
821
+ action: "set-usage-recording" as const,
822
+ },
778
823
  ],
779
824
  };
780
825
  }
781
826
 
827
+ async function applyUsageRecordingSetting(
828
+ value: string | undefined,
829
+ ctx: ExtensionCommandContext,
830
+ runtime: SubagentSettingsRuntime,
831
+ options: { signal: AbortSignal; isCurrent: () => boolean },
832
+ ) {
833
+ if (options.signal.aborted || !options.isCurrent()) return { kind: "close" as const };
834
+ const previous = runtime.getUsageRecordingEnabled?.() ?? false;
835
+ const next = value === "On · local only";
836
+ if (next === previous) return { kind: "stay" as const };
837
+ if (!runtime.setUsageRecordingEnabled) {
838
+ ctx.ui.notify("Usage recording is unavailable in this session.", "error");
839
+ return { kind: "rejected" as const };
840
+ }
841
+ try {
842
+ updateUsageRecordingSetting(next);
843
+ } catch (error) {
844
+ ctx.ui.notify(`Subagent settings were not saved: ${formatError(error)}`, "error");
845
+ return { kind: "rejected" as const };
846
+ }
847
+ try {
848
+ await runtime.setUsageRecordingEnabled(next);
849
+ if (options.signal.aborted || !options.isCurrent()) return { kind: "close" as const };
850
+ ctx.ui.notify(
851
+ next
852
+ ? `Local content-free usage recording enabled. Records stay on this device for ${USAGE_RECORDING_RETENTION_DAYS} days.`
853
+ : "Local usage recording disabled. Existing records expire under the retention policy.",
854
+ "info",
855
+ );
856
+ return { kind: "stay" as const };
857
+ } catch (error) {
858
+ if (options.signal.aborted || !options.isCurrent()) return { kind: "close" as const };
859
+ try {
860
+ updateUsageRecordingSetting(previous);
861
+ await runtime.setUsageRecordingEnabled(previous);
862
+ if (options.signal.aborted || !options.isCurrent()) return { kind: "close" as const };
863
+ } catch (rollbackError) {
864
+ if (options.signal.aborted || !options.isCurrent()) return { kind: "close" as const };
865
+ ctx.ui.notify(
866
+ `Usage recording could not be applied or rolled back: ${formatError(new AggregateError([error, rollbackError]))}`,
867
+ "error",
868
+ );
869
+ return { kind: "rejected" as const };
870
+ }
871
+ ctx.ui.notify(`Subagent settings were not applied: ${formatError(error)}`, "error");
872
+ return { kind: "rejected" as const };
873
+ }
874
+ }
875
+
782
876
  function applyCompletionSetting(
783
877
  value: string | undefined,
784
878
  ctx: ExtensionCommandContext,
@@ -786,7 +880,7 @@ function applyCompletionSetting(
786
880
  ) {
787
881
  const previous = runtime.getCompletionDelivery();
788
882
  const next: CompletionDelivery =
789
- value === "Resume automatically when finished" ? "auto-resume" : "next-turn";
883
+ value === "Continue automatically when work finishes" ? "auto-resume" : "next-turn";
790
884
  if (next === previous) return { kind: "stay" as const };
791
885
  try {
792
886
  updateCompletionDeliverySetting(next);
@@ -806,7 +900,7 @@ function applyConsultResourceSetting(
806
900
  ) {
807
901
  const previous = runtime.getConsultResourcePolicy();
808
902
  const next: ConsultResourcePolicy =
809
- value === "No inherited resources"
903
+ value === "No project resources"
810
904
  ? "none"
811
905
  : value === "All trusted resources"
812
906
  ? "all"
@@ -830,7 +924,7 @@ function applyConsultationCwdSetting(
830
924
  ) {
831
925
  const previous = runtime.getConsultationCwdPolicy();
832
926
  const next: ConsultationCwdPolicy =
833
- value === "Current workspace only" ? "current-workspace" : "anywhere";
927
+ value === "This workspace only" ? "current-workspace" : "anywhere";
834
928
  if (next === previous) return { kind: "stay" as const };
835
929
  try {
836
930
  updateCwdPolicySetting("consultation", next);
@@ -850,9 +944,9 @@ function applyDelegationCwdSetting(
850
944
  ) {
851
945
  const previous = runtime.getDelegationCwdPolicy();
852
946
  const next: DelegationCwdPolicy =
853
- value === "Current workspace only"
947
+ value === "This workspace only"
854
948
  ? "current-workspace"
855
- : value === "Anywhere · normal Pi permissions"
949
+ : value === "Any folder Pi can access"
856
950
  ? "anywhere"
857
951
  : "trusted-targets";
858
952
  if (next === previous) return { kind: "stay" as const };
@@ -874,7 +968,7 @@ function blockReloadWithRetainedAgents(
874
968
  const status = runtime.getRuntimeStatus();
875
969
  if (status.retainedAgents === 0) return false;
876
970
  ctx.ui.notify(
877
- `Cannot reload while ${status.retainedAgents} detached subagent${status.retainedAgents === 1 ? " is" : "s are"} retained (${status.activeAgents} active). Open Current agents and clear them after their work is safe to discard, then change delegation.`,
971
+ `Cannot reload while ${status.retainedAgents} subagent${status.retainedAgents === 1 ? " is" : "s are"} saved for follow-up (${status.activeAgents} working). Open Current subagents and clear them after their work is safe to discard, then change how subagents run.`,
878
972
  "warning",
879
973
  );
880
974
  return true;