pi-subagents 0.65.0 → 0.66.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 (104) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +1 -1
  3. package/agents/researcher.md +23 -13
  4. package/docs/agents.md +17 -3
  5. package/docs/configuration.md +34 -0
  6. package/docs/extension-api.md +94 -0
  7. package/docs/models.md +58 -1
  8. package/docs/observability.md +42 -2
  9. package/docs/tool-reference.md +11 -5
  10. package/docs/workflows.md +22 -7
  11. package/package.json +4 -1
  12. package/runner-server-preload.mjs +13 -0
  13. package/skills/pi-subagents/SKILL.md +2 -1
  14. package/skills/pi-subagents/references/execution-controls.md +19 -2
  15. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  16. package/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  17. package/src/agents/advertised-agent-prompt.ts +63 -0
  18. package/src/agents/agent-management.ts +14 -1
  19. package/src/agents/agent-serializer.ts +2 -0
  20. package/src/agents/agents.ts +8 -0
  21. package/src/api/preflight.ts +5 -1
  22. package/src/api/shared-types.ts +1 -1
  23. package/src/api/workflow-resources.ts +6 -0
  24. package/src/extension/config.ts +4 -2
  25. package/src/extension/index.ts +71 -4
  26. package/src/extension/public-execution.ts +0 -1
  27. package/src/extension/rpc.ts +4 -21
  28. package/src/extension/schemas.ts +9 -7
  29. package/src/extension/tool-description.ts +10 -5
  30. package/src/integrations/pi-web-session-liveness.ts +73 -0
  31. package/src/intercom/native-supervisor-channel.ts +102 -88
  32. package/src/intercom/supervisor-ui.ts +3 -2
  33. package/src/missions/workflow-state.ts +37 -16
  34. package/src/runs/background/active-async-capacity.ts +18 -18
  35. package/src/runs/background/async-execution.ts +8 -1
  36. package/src/runs/background/async-job-tracker.ts +35 -3
  37. package/src/runs/background/async-resume.ts +3 -1
  38. package/src/runs/background/async-retention.ts +9 -0
  39. package/src/runs/background/async-status-snapshot.ts +10 -12
  40. package/src/runs/background/async-status.ts +17 -9
  41. package/src/runs/background/auto-drain.ts +40 -29
  42. package/src/runs/background/chain-root-attachment.ts +8 -0
  43. package/src/runs/background/control-channel.ts +78 -44
  44. package/src/runs/background/notify.ts +88 -12
  45. package/src/runs/background/owned-process-tree.ts +6 -6
  46. package/src/runs/background/process-terminal.ts +23 -23
  47. package/src/runs/background/retained-nested-route-tracker.ts +96 -0
  48. package/src/runs/background/run-child-session.ts +62 -33
  49. package/src/runs/background/run-status.ts +75 -5
  50. package/src/runs/background/runner-aliases.ts +46 -8
  51. package/src/runs/background/runner-child-launch.ts +86 -0
  52. package/src/runs/background/stale-run-reconciler.ts +3 -1
  53. package/src/runs/background/subagent-runner.ts +430 -208
  54. package/src/runs/background/subagent-wait.ts +3 -0
  55. package/src/runs/background/wait-completions.ts +4 -0
  56. package/src/runs/foreground/async-steering-action.ts +19 -0
  57. package/src/runs/foreground/execution.ts +116 -26
  58. package/src/runs/foreground/foreground-history.ts +3 -1
  59. package/src/runs/foreground/prompt-audit.ts +9 -5
  60. package/src/runs/foreground/subagent-executor.ts +531 -227
  61. package/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  62. package/src/runs/foreground/workflow-foreground-steering.ts +56 -2
  63. package/src/runs/shared/acceptance.ts +16 -3
  64. package/src/runs/shared/agent-contract.ts +1 -1
  65. package/src/runs/shared/async-status-projection.ts +47 -47
  66. package/src/runs/shared/child-hooks.ts +151 -2
  67. package/src/runs/shared/child-launch.ts +18 -13
  68. package/src/runs/shared/child-session.ts +55 -24
  69. package/src/runs/shared/child-tool-plan.ts +2 -2
  70. package/src/runs/shared/completion-evidence.ts +2 -2
  71. package/src/runs/shared/completion-guard.ts +1 -0
  72. package/src/runs/shared/host-step-status.ts +11 -11
  73. package/src/runs/shared/llm-intent-arbiter.ts +30 -20
  74. package/src/runs/shared/model-exclusions.ts +2 -1
  75. package/src/runs/shared/model-fallback.ts +41 -8
  76. package/src/runs/shared/nested-events.ts +8 -8
  77. package/src/runs/shared/orca-progress-tabs.ts +6 -0
  78. package/src/runs/shared/parallel-handoff.ts +57 -12
  79. package/src/runs/shared/parallel-utils.ts +3 -2
  80. package/src/runs/shared/readonly-drain-observation.ts +42 -0
  81. package/src/runs/shared/readonly-model-continuation.ts +69 -0
  82. package/src/runs/shared/readonly-session-evidence.ts +307 -0
  83. package/src/runs/shared/run-fanout-budget.ts +8 -8
  84. package/src/runs/shared/runtime-acknowledged-extensions.ts +3 -3
  85. package/src/runs/shared/subagent-prompt-runtime.ts +13 -3
  86. package/src/runs/shared/worktree-cleanup-plan.ts +6 -3
  87. package/src/runs/shared/worktree-setup-command.ts +190 -0
  88. package/src/runs/shared/worktree.ts +403 -210
  89. package/src/shared/model-response-aliases.ts +13 -0
  90. package/src/shared/types.ts +89 -60
  91. package/src/shared/utils.ts +10 -2
  92. package/src/shared/watch-strategy.ts +2 -0
  93. package/src/shared/workflow-child-permit.ts +18 -13
  94. package/src/tui/fleet-status.ts +1 -1
  95. package/src/tui/fleet.ts +11 -5
  96. package/src/tui/render.ts +44 -15
  97. package/src/workflows/chat-progress.ts +3 -3
  98. package/src/workflows/scripted-workflow.ts +70 -16
  99. package/src/workflows/workflow-checklist.ts +15 -18
  100. package/src/workflows/workflow-child-summary.ts +57 -8
  101. package/src/workflows/workflow-preflight.ts +19 -19
  102. package/src/workflows/workflow-receipt.ts +3 -3
  103. package/src/workflows/workflow-resources.ts +96 -21
  104. package/src/workflows/workflow-settlement.ts +3 -0
@@ -7,11 +7,12 @@
7
7
  * this extension and are not installed next to it. pi's own extension loader
8
8
  * aliases those specifiers to the copies shipped inside the installed pi
9
9
  * package; the parent computes the same map and hands it to the runner
10
- * through `JITI_ALIAS`, so the runner's child sessions and hooks share one
11
- * copy of every host package.
10
+ * through `JITI_ALIAS`, so child sessions and hooks retain host API identity.
11
+ * Only Pi 0.85.0's missing server exports may come from this extension.
12
12
  */
13
13
  import * as fs from "node:fs";
14
14
  import * as path from "node:path";
15
+ import { fileURLToPath } from "node:url";
15
16
 
16
17
  export const JITI_ALIAS_ENV = "JITI_ALIAS";
17
18
 
@@ -19,6 +20,9 @@ export const JITI_ALIAS_ENV = "JITI_ALIAS";
19
20
  export const HOST_PEER_ALIASES: ReadonlyArray<{ specifier: string; pkg: string; subpath: string }> = [
20
21
  { specifier: "@earendil-works/pi-coding-agent", pkg: "@earendil-works/pi-coding-agent", subpath: "." },
21
22
  { specifier: "@earendil-works/pi-agent-core", pkg: "@earendil-works/pi-agent-core", subpath: "." },
23
+ { specifier: "@earendil-works/pi-agent-core/node", pkg: "@earendil-works/pi-agent-core", subpath: "./node" },
24
+ { specifier: "@earendil-works/chord", pkg: "@earendil-works/chord", subpath: "." },
25
+ { specifier: "@earendil-works/chord/context", pkg: "@earendil-works/chord", subpath: "./context" },
22
26
  { specifier: "@earendil-works/pi-tui", pkg: "@earendil-works/pi-tui", subpath: "." },
23
27
  { specifier: "@earendil-works/pi-ai", pkg: "@earendil-works/pi-ai", subpath: "./compat" },
24
28
  { specifier: "@earendil-works/pi-ai/compat", pkg: "@earendil-works/pi-ai", subpath: "./compat" },
@@ -29,8 +33,16 @@ export const HOST_PEER_ALIASES: ReadonlyArray<{ specifier: string; pkg: string;
29
33
  { specifier: "typebox/value", pkg: "typebox", subpath: "./value" },
30
34
  ];
31
35
 
36
+ /** Experimental dependencies accidentally published in exactly Pi 0.85.0. */
37
+ const PI0850_PEER_ALIASES = [
38
+ { specifier: "@earendil-works/pi-server", pkg: "@earendil-works/pi-server", subpath: "." },
39
+ { specifier: "@earendil-works/pi-server/unix", pkg: "@earendil-works/pi-server", subpath: "./unix" },
40
+ { specifier: "@earendil-works/pi-client/unix", pkg: "@earendil-works/pi-client", subpath: "./unix" },
41
+ ];
42
+
32
43
  interface PackageManifest {
33
44
  name?: unknown;
45
+ version?: unknown;
34
46
  main?: unknown;
35
47
  exports?: unknown;
36
48
  }
@@ -95,7 +107,11 @@ export function resolvePackageSubpath(packageDir: string, subpath: string): stri
95
107
 
96
108
  /** Find `pkg` as the pi package itself, one of its dependencies, or a sibling in a hoisted install. */
97
109
  export function findHostPeerPackageDir(piPackageRoot: string, pkg: string): string | undefined {
98
- if (readManifest(piPackageRoot)?.name === pkg) return piPackageRoot;
110
+ return findPeerPackageDir(piPackageRoot, pkg, readManifest(piPackageRoot)?.name);
111
+ }
112
+
113
+ function findPeerPackageDir(piPackageRoot: string, pkg: string, hostName: unknown): string | undefined {
114
+ if (hostName === pkg) return piPackageRoot;
99
115
  const candidates = [path.join(piPackageRoot, "node_modules", pkg)];
100
116
  let dir = piPackageRoot;
101
117
  for (;;) {
@@ -112,14 +128,36 @@ export function findHostPeerPackageDir(piPackageRoot: string, pkg: string): stri
112
128
  }
113
129
 
114
130
  /** The alias map the runner needs, or the specifiers that could not be resolved. */
115
- export function resolveHostPeerAliases(piPackageRoot: string): { aliases: Record<string, string>; missing: string[] } {
131
+ export function resolveHostPeerAliases(
132
+ piPackageRoot: string,
133
+ extensionRoot = fileURLToPath(new URL("../../../", import.meta.url)),
134
+ ): { aliases: Record<string, string>; missing: string[]; supplemental: string[] } {
116
135
  const aliases: Record<string, string> = {};
117
136
  const missing: string[] = [];
118
- for (const { specifier, pkg, subpath } of HOST_PEER_ALIASES) {
119
- const packageDir = findHostPeerPackageDir(piPackageRoot, pkg);
120
- const target = packageDir ? resolvePackageSubpath(packageDir, subpath) : undefined;
137
+ const supplemental: string[] = [];
138
+ const hostManifest = readManifest(piPackageRoot);
139
+ const isPi0850 = hostManifest?.version === "0.85.0";
140
+ const required = isPi0850 ? [...HOST_PEER_ALIASES, ...PI0850_PEER_ALIASES] : HOST_PEER_ALIASES;
141
+ for (const { specifier, pkg, subpath } of required) {
142
+ const packageDir = findPeerPackageDir(piPackageRoot, pkg, hostManifest?.name);
143
+ let target = packageDir ? resolvePackageSubpath(packageDir, subpath) : undefined;
144
+ // Pi 0.85.0 omitted this runtime dependency. Never replace working host
145
+ // exports or extend this exact version contract to other peers/hosts.
146
+ if ((!target || !fs.existsSync(target))
147
+ && (specifier === "@earendil-works/pi-server" || specifier === "@earendil-works/pi-server/unix")
148
+ && isPi0850
149
+ && (!packageDir || readManifest(packageDir)?.version === "0.85.0")) {
150
+ const localDir = findHostPeerPackageDir(extensionRoot, pkg);
151
+ if (localDir && readManifest(localDir)?.version === "0.85.0") {
152
+ const localTarget = resolvePackageSubpath(localDir, subpath);
153
+ if (localTarget && fs.existsSync(localTarget)) {
154
+ target = localTarget;
155
+ supplemental.push(specifier);
156
+ }
157
+ }
158
+ }
121
159
  if (target && fs.existsSync(target)) aliases[specifier] = target;
122
160
  else missing.push(specifier);
123
161
  }
124
- return { aliases, missing };
162
+ return { aliases, missing, supplemental };
125
163
  }
@@ -0,0 +1,86 @@
1
+ /** Native runner construction, separate from its executable entrypoint and attempt loop. */
2
+ import * as path from "node:path";
3
+ import { buildInProcessChildLaunch, type BuildInProcessChildLaunchInput, type InheritedChildRuntime } from "../shared/child-launch.ts";
4
+ import { deriveForkPromptCacheKey } from "../shared/child-tool-plan.ts";
5
+ import { normalizeExtensionBindings } from "../shared/extension-bindings.ts";
6
+ import type { RunnerSubagentStep } from "../shared/parallel-utils.ts";
7
+ import { formatAcceptancePrompt } from "../shared/acceptance.ts";
8
+ import { isAgentContract } from "../shared/agent-contract.ts";
9
+
10
+ export interface RunnerChildLaunchContext {
11
+ cwd: string;
12
+ id: string;
13
+ flatIndex: number;
14
+ artifactsDir?: string;
15
+ childIntercomTarget?: string;
16
+ orchestratorIntercomTarget?: string;
17
+ nestedRoute?: BuildInProcessChildLaunchInput["nestedRoute"];
18
+ runFanoutBudget?: BuildInProcessChildLaunchInput["runFanoutBudget"];
19
+ capabilityCeiling?: BuildInProcessChildLaunchInput["capabilityCeiling"];
20
+ inheritedChildRuntime?: InheritedChildRuntime;
21
+ }
22
+
23
+ export function buildRunnerChildLaunch(step: RunnerSubagentStep, ctx: RunnerChildLaunchContext, attempt: {
24
+ model?: string;
25
+ sessionEnabled: boolean;
26
+ sessionDir?: string;
27
+ sessionName?: string;
28
+ structuredOutput?: BuildInProcessChildLaunchInput["structuredOutput"];
29
+ childWatchdog?: BuildInProcessChildLaunchInput["childWatchdog"];
30
+ watchdogStatus: NonNullable<BuildInProcessChildLaunchInput["watchdogStatus"]>;
31
+ }) {
32
+ // Resource-backed system instructions survive SDK split-turn compaction verbatim.
33
+ const acceptancePrompt = step.effectiveAcceptance
34
+ ? formatAcceptancePrompt(step.effectiveAcceptance, { reportOptional: isAgentContract(step.agentContract), structuredOutput: Boolean(step.structuredOutput?.acceptanceReportPath) })
35
+ : "";
36
+ return buildInProcessChildLaunch({
37
+ parentSessionId: step.parentSessionId,
38
+ forkCacheKey: step.context === "fork" ? deriveForkPromptCacheKey(step.parentSessionId) : undefined,
39
+ sessionEnabled: attempt.sessionEnabled,
40
+ sessionDir: attempt.sessionDir,
41
+ sessionFile: step.sessionFile,
42
+ model: attempt.model,
43
+ inheritProjectContext: step.inheritProjectContext,
44
+ inheritGlobalContext: step.inheritGlobalContext,
45
+ inheritSkills: step.inheritSkills,
46
+ requireReadTool: Boolean(step.skills?.length),
47
+ tools: step.tools,
48
+ excludeTools: step.excludeTools,
49
+ allowNestedSubagents: step.allowNestedSubagents,
50
+ extensions: step.extensions,
51
+ subagentOnlyExtensions: step.subagentOnlyExtensions,
52
+ fast: step.fast,
53
+ modelCandidates: step.modelCandidates,
54
+ systemPrompt: acceptancePrompt ? `${step.systemPrompt ?? ""}\n${acceptancePrompt}` : step.systemPrompt ?? "",
55
+ systemPromptMode: step.systemPromptMode,
56
+ mcpDirectTools: step.mcpDirectTools,
57
+ extensionBindings: normalizeExtensionBindings(step.extensionBindings)?.value,
58
+ capabilityCeiling: step.capabilityCeiling ?? ctx.capabilityCeiling,
59
+ cwd: step.cwd ?? ctx.cwd,
60
+ intercomSessionName: ctx.childIntercomTarget,
61
+ sessionName: attempt.sessionName,
62
+ orchestratorIntercomTarget: ctx.orchestratorIntercomTarget,
63
+ runId: ctx.id,
64
+ childAgentName: step.agent,
65
+ childIndex: ctx.flatIndex,
66
+ nestedRoute: ctx.nestedRoute,
67
+ runFanoutBudget: ctx.runFanoutBudget ? {
68
+ ...ctx.runFanoutBudget,
69
+ ...(step.runFanoutPath ? { parentPath: `${ctx.runFanoutBudget.parentPath ? `${ctx.runFanoutBudget.parentPath}/` : ""}${step.runFanoutPath}` } : {}),
70
+ } : undefined,
71
+ structuredOutput: attempt.structuredOutput,
72
+ toolBudget: step.toolBudget,
73
+ permissionRules: step.permissionRules,
74
+ permissionAuditPath: step.permissionRules && ctx.artifactsDir
75
+ ? path.join(ctx.artifactsDir, "permission-audit", `${ctx.id}-${ctx.flatIndex}.jsonl`) : undefined,
76
+ childWatchdog: attempt.childWatchdog,
77
+ // registerChildWatchdog returns before reading the sink when unconfigured.
78
+ ...(attempt.childWatchdog ? { watchdogStatus: attempt.watchdogStatus } : {}),
79
+ waitToolEnabled: step.waitToolEnabled,
80
+ waitToolDefaultTimeoutMs: step.waitToolDefaultTimeoutMs,
81
+ thinkingCeiling: step.thinkingCeiling,
82
+ maxSubagentDepth: step.maxSubagentDepth,
83
+ inherited: ctx.inheritedChildRuntime,
84
+ host: "runner",
85
+ });
86
+ }
@@ -9,6 +9,7 @@ import { resolveEffectiveThinking } from "../../shared/model-info.ts";
9
9
  import { normalizeParallelGroups } from "./parallel-groups.ts";
10
10
  import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent, type NestedRoute } from "../shared/nested-events.ts";
11
11
  import { assertWorkflowGraphHostSteps } from "../shared/host-step-status.ts";
12
+ import type { RawDrainStatusObserver } from "../shared/readonly-drain-observation.ts";
12
13
 
13
14
  export type PidLiveness = "alive" | "dead" | "unknown";
14
15
 
@@ -352,9 +353,10 @@ export function checkPidLiveness(pid: number, kill: KillFn = process.kill): PidL
352
353
  }
353
354
  }
354
355
 
355
- export function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOptions = {}): ReconcileAsyncRunResult {
356
+ export function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOptions = {}, observeStatus?: RawDrainStatusObserver): ReconcileAsyncRunResult {
356
357
  const now = options.now?.() ?? Date.now();
357
358
  const status = readStatus(asyncDir);
359
+ observeStatus?.(status);
358
360
  const startedStatus = !status && options.startedRun ? buildStartedStatus(asyncDir, options.startedRun, now) : undefined;
359
361
  const effectiveStatus = status ?? startedStatus;
360
362
  if (!effectiveStatus) return { status: null, repaired: false };