pi-subagents 0.80.0 → 0.81.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 (87) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bundle/agents.js +18 -0
  3. package/bundle/background-work.js +16 -0
  4. package/bundle/capability-ceiling.js +22 -0
  5. package/bundle/child-tool-plan.js +11 -0
  6. package/bundle/chunk-26C3WLYQ.js +8388 -0
  7. package/bundle/chunk-2XYWN7QJ.js +128 -0
  8. package/bundle/chunk-2ZSNH26H.js +4164 -0
  9. package/bundle/chunk-3DC5KBVP.js +235 -0
  10. package/bundle/chunk-3IF2PGW7.js +380 -0
  11. package/bundle/chunk-4QFZZV3O.js +57 -0
  12. package/bundle/chunk-563BHAZ5.js +1365 -0
  13. package/bundle/chunk-5LFLVSTM.js +260 -0
  14. package/bundle/chunk-62OCY2S4.js +14 -0
  15. package/bundle/chunk-74RT5YQE.js +326 -0
  16. package/bundle/chunk-7F6RKT6W.js +132 -0
  17. package/bundle/chunk-7J4ZXMY7.js +18 -0
  18. package/bundle/chunk-ARRKBB2B.js +179 -0
  19. package/bundle/chunk-BJN5LUQY.js +458 -0
  20. package/bundle/chunk-BPS2AGBZ.js +5421 -0
  21. package/bundle/chunk-CEKS3TFO.js +1337 -0
  22. package/bundle/chunk-CTPAE36Y.js +94 -0
  23. package/bundle/chunk-D4EHT5SJ.js +14 -0
  24. package/bundle/chunk-EYNJ6VL6.js +526 -0
  25. package/bundle/chunk-G5DKFLLI.js +1185 -0
  26. package/bundle/chunk-KSWQVGDW.js +1027 -0
  27. package/bundle/chunk-L5ZHE4L4.js +946 -0
  28. package/bundle/chunk-LSRUTHT7.js +57 -0
  29. package/bundle/chunk-N2BN7BF5.js +159 -0
  30. package/bundle/chunk-NF7IEKBH.js +490 -0
  31. package/bundle/chunk-NIXWDZZV.js +9 -0
  32. package/bundle/chunk-NNLASTQE.js +4082 -0
  33. package/bundle/chunk-TUWLXTAM.js +935 -0
  34. package/bundle/chunk-UKG4V45O.js +228 -0
  35. package/bundle/chunk-V2JWDB5U.js +753 -0
  36. package/bundle/chunk-V3BIUPLU.js +9163 -0
  37. package/bundle/chunk-V6MR5TOQ.js +270 -0
  38. package/bundle/chunk-VJHOELQP.js +8245 -0
  39. package/bundle/chunk-WSBNENFS.js +732 -0
  40. package/bundle/chunk-XMFLCKMQ.js +16664 -0
  41. package/bundle/chunk-YFCK7H2S.js +129 -0
  42. package/bundle/chunk-YYREO77V.js +137 -0
  43. package/bundle/chunk-Z7KVHQHL.js +173 -0
  44. package/bundle/chunk-ZBC44JJ3.js +8606 -0
  45. package/bundle/chunk-ZP3HODFH.js +175 -0
  46. package/bundle/control-channel.js +11 -0
  47. package/bundle/delegation.js +14 -0
  48. package/bundle/external-job-provider.js +20 -0
  49. package/bundle/external-runs.js +21 -0
  50. package/bundle/index.js +30 -0
  51. package/bundle/inspectors.js +8 -0
  52. package/bundle/intercom-bridge.js +11 -0
  53. package/bundle/preflight.js +425 -0
  54. package/bundle/project-panes.js +28 -0
  55. package/bundle/required-child-extensions.js +6 -0
  56. package/bundle/shared-types.js +8 -0
  57. package/bundle/workflow-resources.js +7 -0
  58. package/docs/extension-api.md +27 -0
  59. package/docs/observability.md +6 -2
  60. package/package.json +18 -18
  61. package/src/agents/frontmatter.d.ts +2 -0
  62. package/src/agents/frontmatter.js +9 -2
  63. package/src/api/delegation.d.ts +20 -0
  64. package/src/extension/index.js +27 -7
  65. package/src/runs/background/run-child-session.js +18 -0
  66. package/src/runs/background/stale-run-reconciler.js +2 -0
  67. package/src/runs/foreground/execution.js +39 -2
  68. package/src/runs/shared/structured-output.d.ts +1 -0
  69. package/src/runs/shared/structured-output.js +1 -0
  70. package/src/shared/accessible-dir.js +4 -1
  71. package/src/shared/provider-diagnostics.d.ts +9 -0
  72. package/src/shared/provider-diagnostics.js +75 -0
  73. package/src/shared/types.d.ts +3 -0
  74. package/src/slash/delegation-adapters.d.ts +3 -1
  75. package/src/slash/delegation-adapters.js +7 -0
  76. package/src/slash/prompt-template-bridge.js +3 -1
  77. package/src/slash/subagents-admin.d.ts +1 -0
  78. package/src/slash/subagents-admin.js +1 -1
  79. package/src/tui/fleet-layout.d.ts +20 -0
  80. package/src/tui/fleet-layout.js +17 -0
  81. package/src/tui/fleet-viewport.d.ts +12 -0
  82. package/src/tui/fleet-viewport.js +35 -0
  83. package/src/tui/fleet.d.ts +5 -6
  84. package/src/tui/fleet.js +69 -51
  85. package/src/workflows/structured-workflow-scripts.d.ts +1 -0
  86. package/src/workflows/structured-workflow-scripts.js +5 -4
  87. package/src/workflows/workflow-resources.js +2 -2
@@ -0,0 +1,425 @@
1
+ import {
2
+ buildEffectiveSystemPrompt,
3
+ canPreferForkFromSnapshot,
4
+ resolveSubagentLaunchContext
5
+ } from "./chunk-ARRKBB2B.js";
6
+ import "./chunk-EYNJ6VL6.js";
7
+ import {
8
+ loadConfig
9
+ } from "./chunk-WSBNENFS.js";
10
+ import {
11
+ nestedResultsPath,
12
+ processTerminalCandidatePath,
13
+ processTerminalPath,
14
+ resultFilePath
15
+ } from "./chunk-VJHOELQP.js";
16
+ import {
17
+ applyIntercomBridgeToAgent,
18
+ resolveIntercomBridge,
19
+ validateIntercomBridgeConfig
20
+ } from "./chunk-ZP3HODFH.js";
21
+ import {
22
+ AGENT_DEFINITION_PROJECTION_VERSION,
23
+ resolveLaunchBinding,
24
+ stableJsonDigest
25
+ } from "./chunk-7F6RKT6W.js";
26
+ import {
27
+ applyThinkingSuffix,
28
+ resolvePiLaunchToolPlan
29
+ } from "./chunk-G5DKFLLI.js";
30
+ import {
31
+ assertRequiredChildExtensionsAdmitted,
32
+ resolveRequiredChildExtensions
33
+ } from "./chunk-CTPAE36Y.js";
34
+ import {
35
+ getArtifactPaths,
36
+ getArtifactsDir
37
+ } from "./chunk-3DC5KBVP.js";
38
+ import {
39
+ assertClaudeCodeModelScope,
40
+ assertThinkingWithinCeiling,
41
+ discoverAgentSnapshot,
42
+ findBlockingAgentDiagnostic,
43
+ formatUnknownAgentError,
44
+ inheritsParentModel,
45
+ intersectThinkingCeilings,
46
+ isClaudeCodeAdapterId,
47
+ normalizeExtensionBindings,
48
+ normalizeSingleOutputOverride,
49
+ normalizeSkillInput,
50
+ projectScopeRequiresTrustMessage,
51
+ resolveAgentName,
52
+ resolveClaudeCodeOverride,
53
+ resolveEffectiveSubagentModel,
54
+ resolveEffectiveThinking,
55
+ resolveExecutionAgentScope,
56
+ resolveModelOrigin,
57
+ resolveModelScopesForAgent,
58
+ resolveModelSelection,
59
+ resolvePermissionRules,
60
+ resolveSingleOutputPath,
61
+ resolveSkillsWithFallback,
62
+ resolveStepBehavior,
63
+ unknownAgentDiagnosticContext
64
+ } from "./chunk-ZBC44JJ3.js";
65
+ import {
66
+ DIRS,
67
+ SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
68
+ TEMP_ROOT_DIR
69
+ } from "./chunk-5LFLVSTM.js";
70
+ import "./chunk-YFCK7H2S.js";
71
+ import {
72
+ capabilityCeilingAgentRestrictionMessage,
73
+ intersectSubagentCapabilityCeilings
74
+ } from "./chunk-Z7KVHQHL.js";
75
+
76
+ // src/api/preflight.ts
77
+ import * as fs from "node:fs";
78
+ import * as path from "node:path";
79
+ import { fileURLToPath } from "node:url";
80
+ var SUBAGENT_LAUNCH_CONTRACT_VERSION = 3;
81
+ var PREFLIGHT_ORCHESTRATOR_TARGET = "preflight";
82
+ function claudeCodeFailureCode(message) {
83
+ if (message.includes("subagent model scope")) return "model_scope";
84
+ if (message.startsWith("Thinking level '")) return "thinking_ceiling";
85
+ return "unsupported_mode";
86
+ }
87
+ function packageVersion() {
88
+ const packagePath = path.resolve(path.dirname(fileURLToPath(new URL("../src/api/preflight.js", import.meta.url).href)), "..", "..", "package.json");
89
+ const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
90
+ if (typeof parsed.version !== "string" || !parsed.version.trim()) {
91
+ throw new Error(`Invalid package version in '${packagePath}'.`);
92
+ }
93
+ return parsed.version;
94
+ }
95
+ function digestContract(contract) {
96
+ return stableJsonDigest(contract);
97
+ }
98
+ function normalizeAvailableModels(models) {
99
+ return (models ?? []).map((model) => ({ ...model, fullId: model.fullId ?? `${model.provider}/${model.id}` }));
100
+ }
101
+ function resolveLaunchContractContext(input, agent) {
102
+ return resolveSubagentLaunchContext({
103
+ explicitContext: input.context,
104
+ agentDefaultContext: agent.defaultContext,
105
+ defaultSubagentContext: loadConfig().defaultSubagentContext,
106
+ canUseImplicitFork: canPreferForkFromSnapshot({
107
+ parentSessionFile: input.parentSessionFile,
108
+ leafId: input.parentLeafId
109
+ })
110
+ });
111
+ }
112
+ function taskWorkspaceScopeAuthorityDiagnostic(task) {
113
+ if (!task) return void 0;
114
+ const text = task.replace(/\s+/g, " ").trim();
115
+ if (!text) return void 0;
116
+ const createsWorkspacePackage = /\b(?:add|create|introduce|make|set up)\b.{0,80}\b(?:new\s+)?(?:workspace\s+)?package\b/i.test(text) || /\b(?:new\s+)?package\b.{0,80}\b(?:workspace|monorepo)\b/i.test(text);
117
+ if (!createsWorkspacePackage) return void 0;
118
+ const packageOnlyAuthority = /\b(?:only|solely)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:package(?:\s+directory)?|packages\/[\w.-]+)\b/i.test(text) || /\b(?:do not|don't|must not)\b.{0,40}\b(?:edit|change|modify|touch|write(?:\s+to)?)\b.{0,80}\b(?:root|workspace|lockfile|metadata)\b/i.test(text) || /\bwithout\b.{0,40}\b(?:root|workspace|lockfile|metadata)\b.{0,40}\b(?:edit|change|modification|write)s?\b/i.test(text);
119
+ if (!packageOnlyAuthority) return void 0;
120
+ return {
121
+ code: "workspace_scope_authority",
122
+ severity: "warning",
123
+ message: "Task asks for a workspace package change while limiting authority to package-scope edits. New workspace packages often need root workspace metadata or lockfile changes, so confirm that authority before launch."
124
+ };
125
+ }
126
+ function candidateList(inputAgent, selected, all) {
127
+ return [...all.builtin, ...all.package, ...all.user, ...all.project].filter((agent) => Boolean(resolveAgentName(inputAgent, [agent]).agent)).map((agent) => ({
128
+ name: agent.name,
129
+ ...agent.localName ? { localName: agent.localName } : {},
130
+ ...agent.packageName ? { packageName: agent.packageName } : {},
131
+ source: agent.source,
132
+ filePath: agent.filePath,
133
+ ...agent.disabled === true ? { disabled: true } : {},
134
+ selected: Boolean(selected && agent.filePath === selected.filePath && agent.name === selected.name)
135
+ }));
136
+ }
137
+ async function resolveSubagentLaunchContract(input) {
138
+ const diagnostics = [];
139
+ const authorityDiagnostic = taskWorkspaceScopeAuthorityDiagnostic(input.task);
140
+ if (authorityDiagnostic) diagnostics.push(authorityDiagnostic);
141
+ const effectiveCwd = path.resolve(input.cwd);
142
+ try {
143
+ if (!fs.statSync(effectiveCwd).isDirectory()) {
144
+ return { ok: false, code: "invalid_cwd", message: `cwd '${effectiveCwd}' is not a directory.`, diagnostics };
145
+ }
146
+ } catch (error) {
147
+ const detail = error instanceof Error ? ` ${error.message}` : "";
148
+ return { ok: false, code: "invalid_cwd", message: `cwd '${effectiveCwd}' is not a directory.${detail}`, diagnostics };
149
+ }
150
+ if (input.context !== void 0 && input.context !== "fresh" && input.context !== "fork") {
151
+ return { ok: false, code: "unsupported_mode", message: `Unsupported context '${String(input.context)}'; expected 'fresh' or 'fork'.`, diagnostics };
152
+ }
153
+ if (input.artifactDir !== void 0 && input.artifactDir !== "project" && input.artifactDir !== "session" && input.artifactDir !== "temp") {
154
+ return { ok: false, code: "invalid_artifact_dir", message: `Unsupported artifactDir '${String(input.artifactDir)}'; expected 'project', 'session', or 'temp'.`, diagnostics };
155
+ }
156
+ const bridgeOverride = input.intercomBridge === void 0 ? void 0 : validateIntercomBridgeConfig({ value: input.intercomBridge, label: "intercomBridge" });
157
+ if (bridgeOverride && !bridgeOverride.ok) {
158
+ return { ok: false, code: "invalid_intercom_bridge", message: bridgeOverride.error, diagnostics };
159
+ }
160
+ if (input.orchestratorTarget !== void 0 && (typeof input.orchestratorTarget !== "string" || !input.orchestratorTarget.trim())) {
161
+ return { ok: false, code: "invalid_intercom_bridge", message: "orchestratorTarget must be a non-empty string when provided.", diagnostics };
162
+ }
163
+ const scope = resolveExecutionAgentScope(input.agentScope);
164
+ if (input.projectTrusted === false && scope === "project") {
165
+ const message = projectScopeRequiresTrustMessage(effectiveCwd);
166
+ return { ok: false, code: "restricted_agent", message, diagnostics: [...diagnostics, { code: "restricted_agent", severity: "error", message }] };
167
+ }
168
+ const parentProvider = input.preferredProvider ?? input.parentModel?.provider;
169
+ const discovery = discoverAgentSnapshot(effectiveCwd, scope, parentProvider, { includeChains: false, projectTrusted: input.projectTrusted });
170
+ const discovered = discovery.effective;
171
+ const resolvedAgent = resolveAgentName(input.agent, discovered.agents);
172
+ const ambiguousCandidates = resolvedAgent.error ? discovered.agents.filter((agent2) => resolveAgentName(input.agent, [agent2]).agent) : resolvedAgent.agent;
173
+ const invalidAgent = findBlockingAgentDiagnostic(input.agent, ambiguousCandidates, discovered.agentDiagnostics);
174
+ if (invalidAgent) {
175
+ const message = `Agent '${input.agent}' has invalid configuration: ${invalidAgent.error}`;
176
+ return { ok: false, code: "missing_agent", message, diagnostics: [{ code: "missing_agent", severity: "error", message }] };
177
+ }
178
+ if (resolvedAgent.error) {
179
+ return { ok: false, code: "ambiguous_agent", message: resolvedAgent.error, diagnostics };
180
+ }
181
+ if (!resolvedAgent.agent) {
182
+ return { ok: false, code: "missing_agent", message: formatUnknownAgentError(input.agent, unknownAgentDiagnosticContext(discovered)), diagnostics };
183
+ }
184
+ const definitionAgent = resolvedAgent.agent;
185
+ let extensionBindings;
186
+ try {
187
+ extensionBindings = normalizeExtensionBindings(input.extensionBindings)?.value;
188
+ } catch (error) {
189
+ return { ok: false, code: "invalid_extension_bindings", message: error instanceof Error ? error.message : String(error), diagnostics };
190
+ }
191
+ if (extensionBindings !== void 0 && (definitionAgent.runner?.type === "external-cli" || definitionAgent.runner?.type === "external-job")) {
192
+ return { ok: false, code: "unsupported_mode", message: `extensionBindings is not supported for runner.type='${definitionAgent.runner.type}'.`, diagnostics };
193
+ }
194
+ const context = resolveLaunchContractContext(input, definitionAgent);
195
+ if (context === "fork") {
196
+ diagnostics.push({ code: "host_required", severity: "host-required", message: "Exact fork session branching requires Pi host session snapshots." });
197
+ }
198
+ const bridge = resolveIntercomBridge({
199
+ config: loadConfig().intercomBridge,
200
+ ...bridgeOverride ? { override: bridgeOverride.value } : {},
201
+ context,
202
+ orchestratorTarget: input.orchestratorTarget ?? PREFLIGHT_ORCHESTRATOR_TARGET
203
+ });
204
+ if (bridge.active && bridge.interpolatesOrchestratorTarget && input.orchestratorTarget === void 0) {
205
+ diagnostics.push({ code: "host_required", severity: "host-required", message: "The intercomBridge instruction file names the supervisor session; supply orchestratorTarget to bind the exact child prompt." });
206
+ }
207
+ const agent = applyIntercomBridgeToAgent(definitionAgent, bridge);
208
+ const effectiveCapabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
209
+ const restrictionMessage = capabilityCeilingAgentRestrictionMessage(agent.name, effectiveCapabilityCeiling);
210
+ if (restrictionMessage) return { ok: false, code: "restricted_agent", message: restrictionMessage, diagnostics };
211
+ const runId = input.runId ?? "preflight";
212
+ const skillInput = normalizeSkillInput(input.skill);
213
+ const outputOverride = normalizeSingleOutputOverride(input.output, agent.output);
214
+ const behavior = resolveStepBehavior(agent, {
215
+ ...outputOverride !== void 0 ? { output: outputOverride } : {},
216
+ ...input.outputMode !== void 0 ? { outputMode: input.outputMode } : {},
217
+ ...skillInput !== void 0 ? { skills: skillInput } : {},
218
+ ...input.model !== void 0 ? { model: input.model } : {},
219
+ ...input.outputSchema !== void 0 ? { outputSchema: input.outputSchema } : {}
220
+ });
221
+ const requestedSkills = behavior.skills === false ? [] : behavior.skills;
222
+ const resolvedSkills = resolveSkillsWithFallback(
223
+ requestedSkills,
224
+ effectiveCwd,
225
+ effectiveCwd,
226
+ agent.skillPath,
227
+ agent.filePath ? path.dirname(agent.filePath) : effectiveCwd
228
+ );
229
+ if (resolvedSkills.missing.includes("pi-subagents")) {
230
+ return { ok: false, code: "missing_skill", message: "The pi-subagents orchestration skill is not child-injectable.", diagnostics };
231
+ }
232
+ if (resolvedSkills.missing.length > 0) diagnostics.push({ code: "missing_skill", severity: "error", message: `Missing skills: ${resolvedSkills.missing.join(", ")}` });
233
+ const externalRunner = agent.runner?.type === "external-cli" || agent.runner?.type === "external-job";
234
+ try {
235
+ assertRequiredChildExtensionsAdmitted([resolveRequiredChildExtensions(input.parentSessionId)], { agent: agent.name, runnerType: agent.runner?.type });
236
+ } catch (error) {
237
+ const message = error instanceof Error ? error.message : String(error);
238
+ return { ok: false, code: "unsupported_mode", message, diagnostics };
239
+ }
240
+ if (externalRunner && behavior.outputSchema) {
241
+ return { ok: false, code: "unsupported_mode", message: `Agent '${agent.name}' uses runner.type='${agent.runner?.type}' and does not support: structured output.`, diagnostics };
242
+ }
243
+ const availableModels = normalizeAvailableModels(input.availableModels);
244
+ const preferredProvider = agent.modelProvider ?? input.preferredProvider ?? input.parentModel?.provider;
245
+ const modelScopes = resolveModelScopesForAgent(discovered.modelScope, agent.name, input.parentModel, input.scopedModelIds);
246
+ const modelOrigin = resolveModelOrigin({ explicitModel: input.model, agentModel: agent.model, parentModel: input.parentModel });
247
+ const primaryModel = externalRunner ? void 0 : resolveEffectiveSubagentModel(input.model, agent.model, input.parentModel, availableModels, preferredProvider, {
248
+ scope: modelScopes,
249
+ source: modelOrigin === "explicit" ? "explicit" : "inherited"
250
+ });
251
+ const effectiveThinkingConfig = input.thinking !== void 0 ? input.thinking : agent.thinking;
252
+ const thinkingCeiling = externalRunner ? void 0 : intersectThinkingCeilings(
253
+ discovered.maxThinking,
254
+ input.thinkingCeiling,
255
+ input.inheritedThinkingCeiling
256
+ );
257
+ const model = externalRunner ? void 0 : applyThinkingSuffix(resolveModelSelection(primaryModel, availableModels, preferredProvider, {
258
+ scope: modelScopes,
259
+ primaryModelFromParent: modelOrigin === "inherited" || inheritsParentModel(input.model, agent.model, input.parentModel),
260
+ origin: modelOrigin
261
+ }).model, effectiveThinkingConfig, input.thinking !== void 0);
262
+ if (!externalRunner) {
263
+ try {
264
+ assertThinkingWithinCeiling({ model, configThinking: effectiveThinkingConfig, ceiling: thinkingCeiling, agent: agent.name, runId });
265
+ } catch (error) {
266
+ const message = error instanceof Error ? error.message : String(error);
267
+ diagnostics.push({ code: "thinking_ceiling", severity: "error", message });
268
+ return { ok: false, code: "thinking_ceiling", message, diagnostics };
269
+ }
270
+ }
271
+ if (agent.runner?.type === "external-cli" && isClaudeCodeAdapterId(agent.runner.adapter)) {
272
+ try {
273
+ const override = resolveClaudeCodeOverride({
274
+ model: typeof input.model === "string" ? input.model : void 0,
275
+ agent,
276
+ thinking: typeof effectiveThinkingConfig === "string" ? effectiveThinkingConfig : void 0,
277
+ thinkingCeiling: intersectThinkingCeilings(discovered.maxThinking, input.thinkingCeiling, input.inheritedThinkingCeiling),
278
+ agentName: agent.name,
279
+ runId
280
+ });
281
+ assertClaudeCodeModelScope({ scopes: modelScopes, model: override?.model, agent: agent.name, runId });
282
+ } catch (error) {
283
+ const message = error instanceof Error ? error.message : String(error);
284
+ const code = claudeCodeFailureCode(message);
285
+ diagnostics.push({ code, severity: "error", message });
286
+ return { ok: false, code, message, diagnostics };
287
+ }
288
+ }
289
+ let toolPlan;
290
+ const permissionRules = resolvePermissionRules(loadConfig().permissions, agent.permissions);
291
+ const fast = input.fast ?? agent.fast;
292
+ const requiredExtensions = externalRunner ? [] : resolveRequiredChildExtensions(input.parentSessionId);
293
+ try {
294
+ toolPlan = resolvePiLaunchToolPlan({
295
+ tools: agent.tools,
296
+ excludeTools: agent.excludeTools,
297
+ allowNestedSubagents: agent.allowNestedSubagents,
298
+ extensions: agent.extensions,
299
+ subagentOnlyExtensions: agent.subagentOnlyExtensions,
300
+ requiredExtensions,
301
+ mcpDirectTools: agent.mcpDirectTools,
302
+ cwd: effectiveCwd,
303
+ requireReadTool: resolvedSkills.resolved.length > 0,
304
+ structuredOutput: Boolean(behavior.outputSchema),
305
+ fast,
306
+ model,
307
+ capabilityCeiling: effectiveCapabilityCeiling,
308
+ agentName: agent.name,
309
+ permissionRules,
310
+ runtimeSnapshotHost: input.runtimeSnapshotHost
311
+ });
312
+ } catch (error) {
313
+ const message = error instanceof Error ? error.message : String(error);
314
+ diagnostics.push({ code: "denied_required_tool", severity: "error", message });
315
+ return { ok: false, code: "denied_required_tool", message, diagnostics };
316
+ }
317
+ const artifactsEnabled = input.artifacts !== false;
318
+ const artifactsDir = artifactsEnabled ? getArtifactsDir(input.parentSessionFile ?? null, effectiveCwd, input.artifactDir) : void 0;
319
+ const artifactPaths = artifactsDir ? getArtifactPaths(artifactsDir, runId, agent.name, 0) : void 0;
320
+ const outputPath = resolveSingleOutputPath(behavior.output, effectiveCwd, effectiveCwd, artifactsDir ? path.join(artifactsDir, "outputs", runId) : void 0);
321
+ const sessionRoot = input.sessionDir ? path.join(path.resolve(input.sessionDir), runId) : input.sessionRoot ? path.join(path.resolve(input.sessionRoot), runId) : void 0;
322
+ const sessionDir = sessionRoot ? path.join(sessionRoot, "run-0") : void 0;
323
+ const lifecycleAsyncDir = input.nestedRootRunId ? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", input.nestedRootRunId, runId) : path.join(DIRS.async, runId);
324
+ const lifecycleResultPath = input.nestedRootRunId ? nestedResultsPath(input.nestedRootRunId, runId) : resultFilePath(DIRS.results, runId);
325
+ if (!sessionDir) diagnostics.push({ code: "host_required", severity: "host-required", message: "No sessionRoot/sessionDir was supplied; exact child session paths require the Pi host session-root policy." });
326
+ if (!externalRunner && input.availableModels === void 0 && (input.model || agent.model || input.parentModel)) {
327
+ diagnostics.push({ code: "host_required", severity: "host-required", message: "No availableModels snapshot was supplied; model resolution may differ from the active Pi host registry." });
328
+ }
329
+ if (resolvedSkills.missing.length > 0) {
330
+ return { ok: false, code: "missing_skill", message: `Missing skills: ${resolvedSkills.missing.join(", ")}`, diagnostics };
331
+ }
332
+ const effectiveThinking = resolveEffectiveThinking(model, effectiveThinkingConfig);
333
+ const binding = resolveLaunchBinding({
334
+ agent,
335
+ task: input.task ?? "",
336
+ model,
337
+ ...fast !== void 0 ? { fast } : {},
338
+ ...effectiveThinking ? { thinking: effectiveThinking } : {},
339
+ systemPrompt: buildEffectiveSystemPrompt({ agent, resolvedSkills: resolvedSkills.resolved, cwd: effectiveCwd }),
340
+ skills: requestedSkills,
341
+ toolPlan,
342
+ ...outputPath ? { outputPath } : {},
343
+ outputMode: behavior.outputMode,
344
+ ...behavior.outputSchema ? { structuredOutputSchema: behavior.outputSchema } : {},
345
+ ...extensionBindings ? { extensionBindings } : {}
346
+ });
347
+ const candidates = candidateList(input.agent, agent, discovery.all);
348
+ const shadowedCandidates = candidates.filter((candidate) => !candidate.selected);
349
+ const contractBase = {
350
+ version: SUBAGENT_LAUNCH_CONTRACT_VERSION,
351
+ runId,
352
+ agent: {
353
+ name: agent.name,
354
+ ...agent.localName ? { localName: agent.localName } : {},
355
+ ...agent.packageName ? { packageName: agent.packageName } : {},
356
+ source: agent.source,
357
+ filePath: agent.filePath,
358
+ definitionProjectionVersion: AGENT_DEFINITION_PROJECTION_VERSION,
359
+ definitionDigest: binding.definitionDigest,
360
+ shadowedCandidates
361
+ },
362
+ context,
363
+ ...model ? { model } : {},
364
+ ...effectiveThinking ? { thinking: effectiveThinking } : {},
365
+ ...thinkingCeiling ? { thinkingCeiling } : {},
366
+ systemPromptMode: agent.systemPromptMode,
367
+ inheritProjectContext: agent.inheritProjectContext,
368
+ inheritGlobalContext: agent.inheritGlobalContext,
369
+ inheritSkills: agent.inheritSkills,
370
+ skills: {
371
+ requested: requestedSkills,
372
+ resolved: resolvedSkills.resolved.map((skill) => ({ name: skill.name, path: skill.path, source: skill.source })),
373
+ missing: resolvedSkills.missing
374
+ },
375
+ tools: {
376
+ requestedBuiltin: toolPlan.requestedBuiltinTools,
377
+ declaredBuiltin: toolPlan.declaredBuiltinTools,
378
+ ...toolPlan.excludeTools.length > 0 ? { excludeTools: toolPlan.excludeTools } : {},
379
+ effectiveAllowlist: toolPlan.effectiveToolAllowlist,
380
+ explicitAllowlist: toolPlan.explicitToolAllowlist,
381
+ requiredChildTools: toolPlan.requiredChildTools,
382
+ internalTools: toolPlan.internalTools,
383
+ mcp: toolPlan.effectiveMcpSelections,
384
+ effectiveMcpTools: toolPlan.effectiveMcpTools,
385
+ toolExtensionPaths: toolPlan.toolExtensionPaths,
386
+ runtimeExtensions: toolPlan.runtimeExtensions,
387
+ configuredExtensions: toolPlan.configuredExtensions,
388
+ requiredExtensionIds: toolPlan.requiredExtensions.map(({ id }) => id),
389
+ // Required paths are private launch authority; preflight exposes their safe IDs above.
390
+ extensionArgs: toolPlan.extensionArgs.filter((extensionPath) => !requiredExtensions.some(({ path: path2 }) => path2 === extensionPath)),
391
+ disableAmbientExtensions: toolPlan.disableAmbientExtensions,
392
+ fanoutAuthorized: toolPlan.fanoutAuthorized,
393
+ ...toolPlan.capabilityCeiling ? { capabilityCeiling: toolPlan.capabilityCeiling } : {},
394
+ ...toolPlan.capabilityAudit ? { capabilityAudit: toolPlan.capabilityAudit } : {}
395
+ },
396
+ intercomBridge: bridge.active && bridge.mode !== "off" ? { active: true, mode: bridge.mode } : { active: false, mode: bridge.mode },
397
+ roots: {
398
+ cwd: effectiveCwd,
399
+ ...sessionRoot ? { sessionRoot } : {},
400
+ ...sessionDir ? { sessionDir, sessionFile: path.join(sessionDir, "session.jsonl") } : {},
401
+ ...artifactsDir ? { artifactsDir } : {},
402
+ ...artifactPaths ? { artifactPaths } : {},
403
+ ...outputPath ? { outputPath } : {},
404
+ lifecycle: {
405
+ asyncDir: lifecycleAsyncDir,
406
+ resultPath: lifecycleResultPath,
407
+ statusPath: path.join(lifecycleAsyncDir, "status.json"),
408
+ eventsPath: path.join(lifecycleAsyncDir, "events.jsonl"),
409
+ processTerminalPath: processTerminalPath(lifecycleAsyncDir),
410
+ processTerminalCandidatePath: processTerminalCandidatePath(lifecycleAsyncDir)
411
+ }
412
+ },
413
+ protocol: {
414
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
415
+ packageVersion: packageVersion()
416
+ },
417
+ diagnostics,
418
+ launchContractDigest: binding.launchContractDigest
419
+ };
420
+ return { ok: true, contract: { ...contractBase, digest: digestContract(contractBase) } };
421
+ }
422
+ export {
423
+ SUBAGENT_LAUNCH_CONTRACT_VERSION,
424
+ resolveSubagentLaunchContract
425
+ };
@@ -0,0 +1,28 @@
1
+ import {
2
+ PROJECT_PANES_API_VERSION,
3
+ PROJECT_PANE_TRUST_STATUS,
4
+ closeProjectPane,
5
+ createProjectPaneManager,
6
+ focusProjectPane,
7
+ getProjectPaneStatus,
8
+ openProjectPane,
9
+ projectPaneBindingPath,
10
+ readProjectPaneBinding
11
+ } from "./chunk-V2JWDB5U.js";
12
+ import "./chunk-7J4ZXMY7.js";
13
+ import "./chunk-3DC5KBVP.js";
14
+ import "./chunk-ZBC44JJ3.js";
15
+ import "./chunk-5LFLVSTM.js";
16
+ import "./chunk-YFCK7H2S.js";
17
+ import "./chunk-Z7KVHQHL.js";
18
+ export {
19
+ PROJECT_PANES_API_VERSION,
20
+ PROJECT_PANE_TRUST_STATUS,
21
+ closeProjectPane,
22
+ createProjectPaneManager,
23
+ focusProjectPane,
24
+ getProjectPaneStatus,
25
+ openProjectPane,
26
+ projectPaneBindingPath,
27
+ readProjectPaneBinding
28
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ registerRequiredChildExtensions
3
+ } from "./chunk-CTPAE36Y.js";
4
+ export {
5
+ registerRequiredChildExtensions
6
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ DEFAULT_FORK_PREAMBLE,
3
+ wrapForkTask
4
+ } from "./chunk-5LFLVSTM.js";
5
+ export {
6
+ DEFAULT_FORK_PREAMBLE,
7
+ wrapForkTask
8
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ registerWorkflowResource
3
+ } from "./chunk-NF7IEKBH.js";
4
+ import "./chunk-7F6RKT6W.js";
5
+ export {
6
+ registerWorkflowResource
7
+ };
@@ -353,6 +353,33 @@ Results:
353
353
 
354
354
  Live update events are bounded progress snapshots, not patches, so consumers should replace the prior snapshot rather than merge it as a delta. Structured delegation coalesces heartbeats whose delegation-visible progress and recent output are unchanged; a duration-only heartbeat therefore does not produce another update. The terminal response remains authoritative for the complete result, error, and final usage details.
355
355
 
356
+ Both `:update` and terminal `:response` optionally include one bounded `providerDiagnostics` snapshot:
357
+
358
+ ```ts
359
+ {
360
+ errorCount: number,
361
+ lastError?: {
362
+ kind: "rate_limited" | "transient_other" | "unknown",
363
+ message: string,
364
+ at: number,
365
+ },
366
+ retry?: {
367
+ phase: "backoff" | "in_flight",
368
+ attempt: number,
369
+ maxAttempts: number,
370
+ delayMs: number,
371
+ scheduledAt: number,
372
+ },
373
+ }
374
+ ```
375
+
376
+ - Coverage is **observed assistant-request errors and Pi retries**, not every provider-internal HTTP attempt. Healthy long reasoning does not invent diagnostics; absence means no observation, not proof that no internal retry happened.
377
+ - `errorCount` increments once per failed assistant `message_end` (`stopReason: "error"`), including repeated identical errors and errors without an `errorMessage`. It does not count retry scheduling, tool failures, aborts, or compaction. The first error is reported immediately, even with no output or tokens and before Pi schedules any retry.
378
+ - Only the failed message's `errorMessage` is classified, never assistant content or tool text. `rate_limited` requires explicit throttling evidence. Text that mentions quota or billing, or says credits, balance, or an account or usage limit is exhausted, is reported as `unknown`, even when it also carries throttling or transient wording, so account exhaustion never looks like transient throttling. Other Pi-recognized transient errors use `transient_other`; anything else uses `unknown`. This classification is observational and does not change Pi's retry policy or the delegation's status/error behavior.
379
+ - `lastError.message` is a short fixed safe description, **not** the raw provider error/body. Raw diagnostics remain in existing child artifacts. No HTTP status or provider code is inferred or exposed here. `at` and `scheduledAt` are observation-time Unix timestamps in milliseconds.
380
+ - `auto_retry_start` schedules `backoff`; `attempt` is the 1-based retry attempt, `maxAttempts` is Pi's retry budget, and `delayMs` is Pi's planned delay, **not** a claimed server `Retry-After`. The next `agent_start`/`turn_start` changes the phase to `in_flight`. `auto_retry_end` ends the sequence after success, exhaustion, or cancellation; it is not a backoff-finished event.
381
+ - Cumulative error history survives recovery and unrelated replacement updates. `retry` is active-only and is absent from every terminal response, including cancellation and timeout. Historical errors alone do not turn a recovered completed result into a failure.
382
+
356
383
  Bounds:
357
384
 
358
385
  - Schemas are capped at 64 KiB; tasks and returned text/structured values are capped at 1 MiB, with smaller bounds on identity/configuration strings and a maximum `timeoutMs` of 2,147,483,647.
@@ -116,8 +116,8 @@ FleetView and the under-editor async widget are both enabled by default; set `as
116
116
  Default keys:
117
117
 
118
118
  - `↑`/`↓` or `j`/`k` — select a child
119
- - `Shift+K`/`Shift+J` — scroll one line
120
- - `PgUp`/`PgDn` — scroll one page
119
+ - `K`/`J` (Shift+k / Shift+j) — scroll output one line
120
+ - `PgUp`/`PgDn` — scroll output one body viewport page (structured headers stay pinned)
121
121
  - `x`/`Ctrl+O` — toggle tool details
122
122
  - `r` — refresh
123
123
  - `Esc` — close
@@ -126,6 +126,10 @@ Default keys:
126
126
  - `D` — stop a selected child's top-level async run after confirmation
127
127
  - `H` — open the selected active async child through the available Inspect plugin
128
128
 
129
+ In fullscreen mode with Pi mouse reporting enabled, primary-click a visible roster row to select that child. Wheel or trackpad input over the output pane (including its pinned header) scrolls the same viewport as the keyboard. Owned wheel events stay inside the inspector, even at the top and bottom. New output follows the bottom until you scroll away; reaching the bottom resumes following. Steering/redo drafts and stop confirmation retain input ownership, and Prompt Audit blocks roster clicks. Footer shortcuts remain text hints, not buttons.
130
+
131
+ In regular (non-fullscreen) mode, the terminal retains mouse scrolling and scrollback ownership; internal-pane wheel scrolling and roster clicking are not promised. Use the keyboard controls above to navigate clipped inspector output in either mode. The extension does not enable terminal mouse tracking.
132
+
129
133
  Set `fleetKeybindings` in the extension config to replace inspector-level keys when a terminal intercepts keys such as `PgUp`, `PgDn`, `Home`, or `End`. Prompt modes keep fixed keys such as `Esc`, `Enter`, `Tab`, and stop-confirmation `Y`/`N`.
130
134
 
131
135
  Enter and `H` use the available Inspect plugin. Inside tmux, this includes the bundled tmux plugin: it splits the focused window, runs the inspector pane there, and records a binding, so `status` and `close` are available and ordinary tmux keys such as the prefix keep working in the pane. On macOS with Ghostty 1.3+ (TERM_PROGRAM=ghostty), this includes the other bundled open-only plugin using Ghostty's preview AppleScript API; status and close are unavailable because no binding is written. In a child-specific inspector, type ordinary guidance and press Enter to send it through the acknowledged steer channel; `steer <message>`, `status`, and `stop` remain available as explicit controls. The bundled Herdr plugin uses Herdr 0.7.5+.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "pi-subagents",
4
- "version": "0.80.0",
4
+ "version": "0.81.0",
5
5
  "description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
6
6
  "author": "Nico Bailon",
7
7
  "license": "MIT",
@@ -61,72 +61,72 @@
61
61
  "exports": {
62
62
  ".": {
63
63
  "types": "./index.d.ts",
64
- "default": "./index.js"
64
+ "default": "./bundle/index.js"
65
65
  },
66
66
  "./background-work": {
67
67
  "types": "./src/api/background-work.d.ts",
68
- "default": "./src/api/background-work.js"
68
+ "default": "./bundle/background-work.js"
69
69
  },
70
70
  "./external-job-provider": {
71
71
  "types": "./src/api/external-job-provider.d.ts",
72
- "default": "./src/api/external-job-provider.js"
72
+ "default": "./bundle/external-job-provider.js"
73
73
  },
74
74
  "./external-runs": {
75
75
  "types": "./src/api/external-runs.d.ts",
76
- "default": "./src/api/external-runs.js"
76
+ "default": "./bundle/external-runs.js"
77
77
  },
78
78
  "./agents": {
79
79
  "types": "./src/api/agents.d.ts",
80
- "default": "./src/api/agents.js"
80
+ "default": "./bundle/agents.js"
81
81
  },
82
82
  "./inspectors": {
83
83
  "types": "./src/api/inspectors.d.ts",
84
- "default": "./src/api/inspectors.js"
84
+ "default": "./bundle/inspectors.js"
85
85
  },
86
86
  "./delegation": {
87
87
  "types": "./src/api/delegation.d.ts",
88
- "default": "./src/api/delegation.js"
88
+ "default": "./bundle/delegation.js"
89
89
  },
90
90
  "./capability-ceiling": {
91
91
  "types": "./src/api/capability-ceiling.d.ts",
92
- "default": "./src/api/capability-ceiling.js"
92
+ "default": "./bundle/capability-ceiling.js"
93
93
  },
94
94
  "./workflow-resources": {
95
95
  "types": "./src/api/workflow-resources.d.ts",
96
- "default": "./src/api/workflow-resources.js"
96
+ "default": "./bundle/workflow-resources.js"
97
97
  },
98
98
  "./required-child-extensions": {
99
99
  "types": "./src/api/required-child-extensions.d.ts",
100
- "default": "./src/api/required-child-extensions.js"
100
+ "default": "./bundle/required-child-extensions.js"
101
101
  },
102
102
  "./preflight": {
103
103
  "types": "./src/api/preflight.d.ts",
104
- "default": "./src/api/preflight.js"
104
+ "default": "./bundle/preflight.js"
105
105
  },
106
106
  "./control-channel": {
107
107
  "types": "./src/api/control-channel.d.ts",
108
- "default": "./src/api/control-channel.js"
108
+ "default": "./bundle/control-channel.js"
109
109
  },
110
110
  "./intercom-bridge": {
111
111
  "types": "./src/api/intercom-bridge.d.ts",
112
- "default": "./src/api/intercom-bridge.js"
112
+ "default": "./bundle/intercom-bridge.js"
113
113
  },
114
114
  "./child-tool-plan": {
115
115
  "types": "./src/api/child-tool-plan.d.ts",
116
- "default": "./src/api/child-tool-plan.js"
116
+ "default": "./bundle/child-tool-plan.js"
117
117
  },
118
118
  "./shared-types": {
119
119
  "types": "./src/api/shared-types.d.ts",
120
- "default": "./src/api/shared-types.js"
120
+ "default": "./bundle/shared-types.js"
121
121
  },
122
122
  "./project-panes": {
123
123
  "types": "./src/api/project-panes.d.ts",
124
- "default": "./src/api/project-panes.js"
124
+ "default": "./bundle/project-panes.js"
125
125
  }
126
126
  },
127
127
  "pi": {
128
128
  "extensions": [
129
- "./index.js"
129
+ "./bundle/index.js"
130
130
  ],
131
131
  "skills": [
132
132
  "./skills"
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Normalize a simple-scalar frontmatter list from comma-separated or block-list syntax.
3
3
  * Only the standard `- item` marker is removed; ordinary hyphenated values stay intact.
4
+ * A trailing ` #...` comment is dropped from unquoted `- item` lines. Single-line values are left
5
+ * alone because parseFrontmatter has already removed their quotes, so `"notes #1.md"` must survive.
4
6
  */
5
7
  export declare function parseFrontmatterList(raw: string | undefined): string[] | undefined;
6
8
  /**