@narumitw/pi-subagents 0.49.2 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +313 -53
  2. package/package.json +11 -8
  3. package/src/adaptive-scheduler.ts +196 -0
  4. package/src/admission-benchmark.ts +95 -0
  5. package/src/admission-policy.ts +78 -0
  6. package/src/agent-projection.ts +53 -0
  7. package/src/agents.ts +58 -1
  8. package/src/auto-transport.ts +114 -0
  9. package/src/blocking-status.ts +63 -0
  10. package/src/capabilities.ts +145 -0
  11. package/src/capability-grant.ts +115 -0
  12. package/src/capability-router.ts +107 -0
  13. package/src/completion-delivery.ts +257 -0
  14. package/src/config-status.ts +221 -0
  15. package/src/config-ui.ts +215 -236
  16. package/src/consult-resources.ts +4 -27
  17. package/src/consult.ts +9 -1
  18. package/src/create-stateful-transport.ts +55 -0
  19. package/src/delegation-contract.ts +417 -0
  20. package/src/execution-plan.ts +322 -0
  21. package/src/execution-profiles.ts +95 -0
  22. package/src/execution-ui.ts +320 -0
  23. package/src/execution.ts +848 -158
  24. package/src/in-process-transport.ts +269 -25
  25. package/src/inspect-render.ts +101 -1
  26. package/src/inspect.ts +296 -3
  27. package/src/integration-controller.ts +98 -0
  28. package/src/limits.ts +3 -0
  29. package/src/orchestration-metrics.ts +78 -0
  30. package/src/outcome.ts +61 -0
  31. package/src/panel-child-group.ts +35 -0
  32. package/src/panel-contract.ts +343 -0
  33. package/src/panel-evidence.ts +59 -0
  34. package/src/panel-execution.ts +772 -0
  35. package/src/panel-failure.ts +56 -0
  36. package/src/panel-planning.ts +175 -0
  37. package/src/panel-prompts.ts +132 -0
  38. package/src/panel-reconciliation.ts +57 -0
  39. package/src/panel-render.ts +103 -0
  40. package/src/parallel-limit-ui.ts +112 -0
  41. package/src/params.ts +172 -3
  42. package/src/persistence.ts +182 -32
  43. package/src/prompt-resources.ts +38 -0
  44. package/src/registry-types.ts +175 -0
  45. package/src/registry.ts +466 -143
  46. package/src/render.ts +72 -6
  47. package/src/result-contract.ts +416 -0
  48. package/src/retained-semantic-state.ts +100 -0
  49. package/src/rpc-timeout-finalization.ts +207 -0
  50. package/src/rpc-transport-metadata.ts +65 -0
  51. package/src/rpc-transport.ts +990 -0
  52. package/src/rpc-turn-capture.ts +142 -0
  53. package/src/runner-result.ts +55 -0
  54. package/src/runner-usage.ts +48 -0
  55. package/src/runner.ts +325 -73
  56. package/src/semantic-snapshot.ts +214 -0
  57. package/src/settings.ts +254 -35
  58. package/src/spawn-idempotency.ts +61 -0
  59. package/src/stateful-config.ts +13 -0
  60. package/src/stateful-guidance.ts +1 -0
  61. package/src/stateful-lifecycle.ts +45 -2
  62. package/src/stateful-limit-ui.ts +246 -0
  63. package/src/stateful-limits.ts +96 -0
  64. package/src/stateful-prompt.ts +11 -2
  65. package/src/stateful-render.ts +48 -3
  66. package/src/stateful.ts +467 -357
  67. package/src/subagents.ts +114 -46
  68. package/src/subprocess-transport.ts +64 -5
  69. package/src/supervision.ts +103 -0
  70. package/src/timeout-checkpoint.ts +305 -0
  71. package/src/timeout-finalization.ts +75 -0
  72. package/src/transport-types.ts +68 -0
  73. package/src/transport-ui.ts +169 -0
  74. package/src/transport.ts +16 -4
  75. package/src/turn-budget.ts +109 -0
  76. package/src/verification-policy.ts +17 -0
  77. package/src/work-item-ledger.ts +682 -0
  78. package/src/work-item-persistence.ts +218 -0
  79. package/src/workflow-planning.ts +150 -0
  80. package/src/workflow-ui.ts +61 -0
  81. package/src/workspace.ts +69 -12
@@ -0,0 +1,320 @@
1
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import type { AgentConfig, SubagentThinkingLevel } from "./agents.js";
3
+ import {
4
+ applyExecutionProfile,
5
+ EXECUTION_PROFILES,
6
+ type ExecutionProfile,
7
+ executionProfileDescription,
8
+ executionProfileLabel,
9
+ executionProfilePreview,
10
+ inspectExecutionProfile,
11
+ } from "./execution-profiles.js";
12
+ import { MAX_SUBAGENT_TIMEOUT_MS } from "./limits.js";
13
+ import { safeTerminalLine as safeTerminalText } from "./safe-text.js";
14
+ import { readSubagentSettings, updateAgentSettingsPatch } from "./settings.js";
15
+
16
+ export function executionProfileScreen() {
17
+ const current = inspectExecutionProfile();
18
+ return {
19
+ kind: "actions" as const,
20
+ title: "Execution Profiles",
21
+ lines: [
22
+ `Current built-in mapping: ${current === "custom" ? "Custom or inherited" : executionProfileLabel(current)}`,
23
+ "Profiles change only built-in agent thinking defaults.",
24
+ "Models, timeouts, tools, transport, context, and explicit tool-call values are preserved.",
25
+ ],
26
+ items: [
27
+ ...EXECUTION_PROFILES.map((profile) => ({
28
+ id: profile,
29
+ label: executionProfileLabel(profile),
30
+ description: executionProfileDescription(profile),
31
+ action: "apply-execution-profile" as const,
32
+ })),
33
+ { id: "back", label: "Back", action: "back" as const },
34
+ ],
35
+ hint: "back" as const,
36
+ };
37
+ }
38
+
39
+ export async function applyExecutionProfileFromUi(
40
+ profileValue: string,
41
+ ctx: ExtensionCommandContext,
42
+ signal: AbortSignal,
43
+ isCurrent: () => boolean,
44
+ ) {
45
+ if (!EXECUTION_PROFILES.includes(profileValue as ExecutionProfile)) {
46
+ return { kind: "rejected" as const };
47
+ }
48
+ const profile = profileValue as ExecutionProfile;
49
+ const before = executionSettingsFingerprint();
50
+ const confirmed = await ctx.ui.confirm(
51
+ `Apply ${executionProfileLabel(profile)} profile?`,
52
+ [
53
+ executionProfileDescription(profile),
54
+ ...executionProfilePreview(profile),
55
+ "Existing model, timeout, and tool overrides remain unchanged.",
56
+ ].join("\n"),
57
+ { signal },
58
+ );
59
+ if (signal.aborted || !isCurrent()) return { kind: "close" as const };
60
+ if (!confirmed) return { kind: "rejected" as const };
61
+ if (before !== executionSettingsFingerprint()) {
62
+ ctx.ui.notify("Agent execution settings changed while confirming; review again.", "warning");
63
+ return { kind: "rejected" as const };
64
+ }
65
+ try {
66
+ applyExecutionProfile(profile);
67
+ ctx.ui.notify(`Applied ${executionProfileLabel(profile)} profile.`, "info");
68
+ return { kind: "stay" as const };
69
+ } catch (error) {
70
+ ctx.ui.notify(`Execution profile was not saved: ${formatError(error)}`, "error");
71
+ return { kind: "rejected" as const };
72
+ }
73
+ }
74
+
75
+ export function executionAgentPickerScreen(agents: readonly AgentConfig[]) {
76
+ const configured = readSubagentSettings()?.agents ?? {};
77
+ return {
78
+ kind: "actions" as const,
79
+ title: "Agent Execution Defaults",
80
+ lines: ["Choose an agent to edit its inherited model, thinking, or timeout."],
81
+ items: [
82
+ ...agents.map((agent) => {
83
+ const value = configured[agent.name];
84
+ return {
85
+ id: agent.name,
86
+ label: safeTerminalText(agent.name),
87
+ description: safeTerminalText(
88
+ `${agent.source} · model ${value?.model ?? agent.model ?? "inherited"} · thinking ${value?.thinkingLevel ?? agent.thinkingLevel ?? "inherited"} · timeout ${value?.timeoutMs ?? agent.timeoutMs ?? "inherited"}`,
89
+ ),
90
+ action: "pick-execution-agent" as const,
91
+ };
92
+ }),
93
+ { id: "back", label: "Back", action: "back" as const },
94
+ ],
95
+ hint: "back" as const,
96
+ };
97
+ }
98
+
99
+ export function executionAgentScreen(agent: AgentConfig | undefined) {
100
+ if (!agent) {
101
+ return {
102
+ kind: "actions" as const,
103
+ title: "Agent Execution Defaults",
104
+ lines: ["No agent selected."],
105
+ items: [{ id: "back", label: "Back", action: "back" as const }],
106
+ hint: "back" as const,
107
+ };
108
+ }
109
+ const configured = readSubagentSettings()?.agents?.[agent.name];
110
+ return {
111
+ kind: "actions" as const,
112
+ title: `${safeTerminalText(agent.name)} execution`,
113
+ lines: [
114
+ `Model: ${safeTerminalText(configured?.model ?? agent.model ?? "inherited")}`,
115
+ `Thinking: ${configured?.thinkingLevel ?? agent.thinkingLevel ?? "inherited"}`,
116
+ `Timeout: ${configured?.timeoutMs ?? agent.timeoutMs ?? "inherited"}`,
117
+ "Explicit tool-call values remain authoritative.",
118
+ ],
119
+ items: [
120
+ { id: "thinking", label: "Thinking level", to: "execution-thinking" as const },
121
+ { id: "model", label: "Model", to: "execution-model" as const },
122
+ { id: "timeout", label: "Timeout", to: "execution-timeout" as const },
123
+ {
124
+ id: "reset",
125
+ label: "Reset execution defaults",
126
+ description: "Restore frontmatter or Pi inheritance without changing tools",
127
+ action: "reset-agent-execution" as const,
128
+ },
129
+ { id: "back", label: "Back", action: "back" as const },
130
+ ],
131
+ hint: "back" as const,
132
+ };
133
+ }
134
+
135
+ export function executionThinkingScreen(agent: AgentConfig | undefined) {
136
+ const configured = agent ? readSubagentSettings()?.agents?.[agent.name] : undefined;
137
+ return {
138
+ kind: "settings" as const,
139
+ title: agent ? `${safeTerminalText(agent.name)} thinking` : "Agent thinking",
140
+ items: agent
141
+ ? [
142
+ {
143
+ id: "thinking",
144
+ label: "Default thinking level",
145
+ description: "Explicit spawn or blocking call values still win.",
146
+ currentValue: configured?.thinkingLevel ?? agent.thinkingLevel ?? "Inherited",
147
+ values: ["Inherited", "off", "minimal", "low", "medium", "high", "xhigh", "max"],
148
+ action: "set-agent-thinking" as const,
149
+ },
150
+ ]
151
+ : [],
152
+ hint: "back" as const,
153
+ };
154
+ }
155
+
156
+ export function executionModelScreen(agent: AgentConfig | undefined, ctx: ExtensionCommandContext) {
157
+ const configured = agent ? readSubagentSettings()?.agents?.[agent.name] : undefined;
158
+ const models = ctx.modelRegistry
159
+ .getAvailable()
160
+ .map((model) => `${model.provider}/${model.id}`)
161
+ .sort((left, right) => left.localeCompare(right))
162
+ .slice(0, 100);
163
+ return {
164
+ kind: "actions" as const,
165
+ title: agent ? `${safeTerminalText(agent.name)} model` : "Agent model",
166
+ lines: [
167
+ `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "inherited")}`,
168
+ "Choose a session-available model or enter a custom Pi model pattern.",
169
+ ],
170
+ items: agent
171
+ ? [
172
+ {
173
+ id: "model:__inherited__",
174
+ label: "Inherited",
175
+ description: "Use frontmatter or active Pi model resolution",
176
+ action: "set-agent-model" as const,
177
+ },
178
+ ...models.map((model) => ({
179
+ id: `model:${model}`,
180
+ label: safeTerminalText(model),
181
+ action: "set-agent-model" as const,
182
+ })),
183
+ {
184
+ id: "custom",
185
+ label: "Custom model pattern",
186
+ description: "Enter provider/model, a model pattern, or an optional :thinking suffix",
187
+ to: "execution-model-input" as const,
188
+ },
189
+ { id: "back", label: "Back", action: "back" as const },
190
+ ]
191
+ : [],
192
+ hint: "back" as const,
193
+ };
194
+ }
195
+
196
+ export function executionModelInputScreen(agent: AgentConfig | undefined) {
197
+ const configured = agent ? readSubagentSettings()?.agents?.[agent.name] : undefined;
198
+ return {
199
+ kind: "input" as const,
200
+ title: agent ? `${safeTerminalText(agent.name)} model` : "Agent model",
201
+ lines: [
202
+ `Current: ${safeTerminalText(configured?.model ?? agent?.model ?? "inherited")}`,
203
+ "Enter a Pi CLI model pattern, including an optional :thinking suffix.",
204
+ "Use Reset execution defaults to restore inheritance.",
205
+ ],
206
+ placeholder: "provider/model or model pattern",
207
+ action: "set-agent-model" as const,
208
+ hint: "back" as const,
209
+ };
210
+ }
211
+
212
+ export function executionTimeoutInputScreen(agent: AgentConfig | undefined) {
213
+ const configured = agent ? readSubagentSettings()?.agents?.[agent.name] : undefined;
214
+ return {
215
+ kind: "input" as const,
216
+ title: agent ? `${safeTerminalText(agent.name)} timeout` : "Agent timeout",
217
+ lines: [
218
+ `Current: ${configured?.timeoutMs ?? agent?.timeoutMs ?? "inherited"}`,
219
+ `Allowed: 1-${MAX_SUBAGENT_TIMEOUT_MS} milliseconds.`,
220
+ "Use Reset execution defaults to restore inheritance.",
221
+ ],
222
+ placeholder: "Timeout in milliseconds",
223
+ action: "set-agent-timeout" as const,
224
+ hint: "back" as const,
225
+ };
226
+ }
227
+
228
+ export function applyAgentThinking(
229
+ agent: AgentConfig | undefined,
230
+ value: string | undefined,
231
+ ctx: ExtensionCommandContext,
232
+ ) {
233
+ if (!agent) return { kind: "rejected" as const };
234
+ const thinkingLevel = value === "Inherited" ? undefined : (value as SubagentThinkingLevel);
235
+ if (
236
+ thinkingLevel !== undefined &&
237
+ !["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(thinkingLevel)
238
+ ) {
239
+ return { kind: "rejected" as const };
240
+ }
241
+ return saveAgentPatch(agent.name, { thinkingLevel }, ctx, "thinking level");
242
+ }
243
+
244
+ export function applyAgentModel(
245
+ agent: AgentConfig | undefined,
246
+ value: string | undefined,
247
+ ctx: ExtensionCommandContext,
248
+ ) {
249
+ if (!agent) return { kind: "rejected" as const };
250
+ if (value === undefined) return saveAgentPatch(agent.name, { model: undefined }, ctx, "model");
251
+ const model = value.trim();
252
+ if (!model || Buffer.byteLength(model, "utf8") > 1_024 || hasTerminalControl(model)) {
253
+ ctx.ui.notify("Model must contain 1-1024 UTF-8 bytes on one line.", "warning");
254
+ return { kind: "rejected" as const };
255
+ }
256
+ return saveAgentPatch(agent.name, { model }, ctx, "model");
257
+ }
258
+
259
+ export function applyAgentTimeout(
260
+ agent: AgentConfig | undefined,
261
+ value: string | undefined,
262
+ ctx: ExtensionCommandContext,
263
+ ) {
264
+ if (!agent) return { kind: "rejected" as const };
265
+ const normalized = value?.trim() ?? "";
266
+ if (!/^\d+$/u.test(normalized)) {
267
+ ctx.ui.notify("Timeout must be a whole number of milliseconds.", "warning");
268
+ return { kind: "rejected" as const };
269
+ }
270
+ const timeoutMs = Number(normalized);
271
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_SUBAGENT_TIMEOUT_MS) {
272
+ ctx.ui.notify(`Timeout must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}.`, "warning");
273
+ return { kind: "rejected" as const };
274
+ }
275
+ return saveAgentPatch(agent.name, { timeoutMs }, ctx, "timeout");
276
+ }
277
+
278
+ export function resetAgentExecution(agent: AgentConfig | undefined, ctx: ExtensionCommandContext) {
279
+ if (!agent) return { kind: "rejected" as const };
280
+ return saveAgentPatch(
281
+ agent.name,
282
+ { model: undefined, thinkingLevel: undefined, timeoutMs: undefined },
283
+ ctx,
284
+ "execution defaults",
285
+ );
286
+ }
287
+
288
+ function saveAgentPatch(
289
+ name: string,
290
+ patch: Parameters<typeof updateAgentSettingsPatch>[0][string],
291
+ ctx: ExtensionCommandContext,
292
+ label: string,
293
+ ) {
294
+ try {
295
+ updateAgentSettingsPatch({ [name]: patch });
296
+ ctx.ui.notify(`${safeTerminalText(name)} ${label} saved.`, "info");
297
+ return { kind: "back" as const };
298
+ } catch (error) {
299
+ ctx.ui.notify(
300
+ `${safeTerminalText(name)} ${label} was not saved: ${formatError(error)}`,
301
+ "error",
302
+ );
303
+ return { kind: "rejected" as const };
304
+ }
305
+ }
306
+
307
+ function hasTerminalControl(value: string): boolean {
308
+ return [...value].some((character) => {
309
+ const code = character.codePointAt(0) ?? 0;
310
+ return code <= 0x1f || (code >= 0x7f && code <= 0x9f);
311
+ });
312
+ }
313
+
314
+ function executionSettingsFingerprint(): string {
315
+ return JSON.stringify(readSubagentSettings()?.agents ?? {});
316
+ }
317
+
318
+ function formatError(error: unknown): string {
319
+ return safeTerminalText(error instanceof Error ? error.message : String(error));
320
+ }