pi-subagents 0.67.0 → 0.69.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 (122) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +1 -1
  3. package/docs/agents.md +41 -12
  4. package/docs/configuration.md +61 -19
  5. package/docs/extension-api.md +5 -1
  6. package/docs/missions.md +2 -2
  7. package/docs/models.md +11 -79
  8. package/docs/observability.md +18 -8
  9. package/docs/standalone-background.md +13 -3
  10. package/docs/tool-reference.md +38 -14
  11. package/docs/watchdog.md +10 -12
  12. package/docs/workflows.md +59 -1
  13. package/index.ts +5 -2
  14. package/package.json +4 -2
  15. package/runner-peer-loader.mjs +24 -0
  16. package/runner-peer-preload.mjs +25 -11
  17. package/skills/pi-subagents/SKILL.md +18 -21
  18. package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
  19. package/skills/pi-subagents/references/execution-controls.md +6 -4
  20. package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
  21. package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  22. package/skills/pi-subagents/references/prompting-and-roles.md +16 -12
  23. package/skills/pi-subagents/references/review-and-validation.md +3 -3
  24. package/src/agents/agent-management.ts +57 -58
  25. package/src/agents/agent-serializer.ts +4 -3
  26. package/src/agents/agents.ts +185 -72
  27. package/src/agents/chain-serializer.ts +5 -0
  28. package/src/agents/runtime-agent-registry.ts +7 -6
  29. package/src/agents/skills.ts +1 -1
  30. package/src/api/preflight.ts +20 -16
  31. package/src/api/required-child-extensions.ts +6 -0
  32. package/src/extension/config.ts +10 -37
  33. package/src/extension/fanout-child.ts +3 -0
  34. package/src/extension/herdr-pi-bridge.ts +160 -0
  35. package/src/extension/index.ts +42 -31
  36. package/src/extension/public-execution.ts +3 -3
  37. package/src/extension/schemas.ts +23 -6
  38. package/src/extension/tool-description.ts +8 -7
  39. package/src/inspectors/ghostty/plugin.ts +13 -1
  40. package/src/intercom/native-supervisor-channel.ts +22 -18
  41. package/src/policy/authority.ts +4 -0
  42. package/src/profiles/profiles.ts +12 -6
  43. package/src/runs/background/active-run-index.ts +17 -1
  44. package/src/runs/background/async-execution.ts +309 -126
  45. package/src/runs/background/async-job-tracker.ts +8 -6
  46. package/src/runs/background/async-resume.ts +13 -4
  47. package/src/runs/background/async-status.ts +15 -4
  48. package/src/runs/background/auto-drain.ts +20 -10
  49. package/src/runs/background/binary-bootstrap.ts +5 -0
  50. package/src/runs/background/chain-append.ts +1 -1
  51. package/src/runs/background/chain-root-attachment.ts +14 -33
  52. package/src/runs/background/notify.ts +74 -6
  53. package/src/runs/background/result-files.ts +8 -4
  54. package/src/runs/background/result-watcher.ts +19 -2
  55. package/src/runs/background/run-child-session.ts +20 -29
  56. package/src/runs/background/runner-aliases.ts +4 -33
  57. package/src/runs/background/runner-child-launch.ts +4 -1
  58. package/src/runs/background/runner-child-sessions.ts +2 -2
  59. package/src/runs/background/runner-http-dispatcher.ts +119 -0
  60. package/src/runs/background/scheduled-runs.ts +11 -5
  61. package/src/runs/background/stale-run-reconciler.ts +35 -11
  62. package/src/runs/background/subagent-runner.ts +413 -276
  63. package/src/runs/background/subagent-wait.ts +128 -23
  64. package/src/runs/background/wait-completions.ts +75 -27
  65. package/src/runs/background/wait-subscriptions.ts +9 -3
  66. package/src/runs/background/wait-tool.ts +4 -2
  67. package/src/runs/foreground/async-stop-action.ts +93 -3
  68. package/src/runs/foreground/execution.ts +115 -219
  69. package/src/runs/foreground/foreground-history.ts +2 -1
  70. package/src/runs/foreground/subagent-executor.ts +281 -80
  71. package/src/runs/shared/acceptance.ts +194 -37
  72. package/src/runs/shared/async-status-projection.ts +123 -33
  73. package/src/runs/shared/child-launch-plan.ts +15 -3
  74. package/src/runs/shared/child-launch.ts +19 -6
  75. package/src/runs/shared/child-runtime-config.ts +5 -0
  76. package/src/runs/shared/child-session.ts +94 -50
  77. package/src/runs/shared/child-tool-plan.ts +28 -16
  78. package/src/runs/shared/dynamic-fanout.ts +2 -2
  79. package/src/runs/shared/external-cli-contract.ts +11 -1
  80. package/src/runs/shared/external-cli-preflight.ts +6 -2
  81. package/src/runs/shared/herdr-connection.ts +134 -0
  82. package/src/runs/shared/herdr-external-adapters.ts +169 -0
  83. package/src/runs/shared/herdr-machine.ts +279 -0
  84. package/src/runs/shared/herdr-pi-protocol.ts +59 -0
  85. package/src/runs/shared/herdr-placed-run.ts +263 -0
  86. package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
  87. package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -237
  88. package/src/runs/shared/model-scope.ts +1 -1
  89. package/src/runs/shared/nested-events.ts +11 -2
  90. package/src/runs/shared/parallel-utils.ts +7 -2
  91. package/src/runs/shared/pi-spawn.ts +1 -1
  92. package/src/runs/shared/subagent-prompt-runtime.ts +4 -2
  93. package/src/runs/shared/worktree-setup-command.ts +27 -4
  94. package/src/runs/shared/worktree.ts +30 -8
  95. package/src/shared/child-cache-retention.ts +43 -0
  96. package/src/shared/launch-contract.ts +6 -9
  97. package/src/shared/pruned-fork.ts +1 -1
  98. package/src/shared/required-child-extensions.ts +81 -0
  99. package/src/shared/settings.ts +5 -2
  100. package/src/shared/shortcuts.ts +0 -4
  101. package/src/shared/types.ts +81 -29
  102. package/src/slash/slash-commands.ts +0 -6
  103. package/src/slash/subagents-admin.ts +13 -9
  104. package/src/tui/render.ts +20 -10
  105. package/src/watchdog/child-status.ts +28 -36
  106. package/src/watchdog/lsp-diagnostics.ts +1 -1
  107. package/src/watchdog/model-selection.ts +1 -1
  108. package/src/watchdog/register-child.ts +10 -3
  109. package/src/watchdog/register-main.ts +20 -20
  110. package/src/watchdog/render.ts +1 -1
  111. package/src/watchdog/review.ts +14 -30
  112. package/src/watchdog/rules.ts +1 -1
  113. package/src/watchdog/runtime.ts +23 -12
  114. package/src/watchdog/settings.ts +3 -6
  115. package/src/watchdog/types.ts +3 -5
  116. package/src/watchdog/warning-format.ts +1 -1
  117. package/src/workflows/scripted-workflow.ts +68 -7
  118. package/src/workflows/workflow-receipt.ts +21 -3
  119. package/src/workflows/workflow-resources.ts +13 -2
  120. package/src/runs/shared/model-exclusions.ts +0 -374
  121. package/src/runs/shared/readonly-model-continuation.ts +0 -69
  122. package/src/runs/shared/readonly-session-evidence.ts +0 -307
@@ -1,7 +1,12 @@
1
+ import * as fs from "node:fs";
1
2
  import * as path from "node:path";
2
3
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
3
- import type { Details, SubagentState } from "../../shared/types.ts";
4
+ import { writeAtomicJson } from "../../shared/atomic-json.ts";
5
+ import { DIRS, type AsyncStatus, type Details, type SubagentState } from "../../shared/types.ts";
6
+ import { updateActiveRunIndex } from "../background/active-run-index.ts";
4
7
  import { deliverStopRequest } from "../background/control-channel.ts";
8
+ import { readProcessTerminal } from "../background/process-terminal.ts";
9
+ import { resultFilePath, resultPayloadPathForSessionRun, writeAsyncResultFile } from "../background/result-files.ts";
5
10
  import { reconcileAsyncRun } from "../background/stale-run-reconciler.ts";
6
11
  import { isStoppableAsyncStatusStep, resolveAsyncStatusChild, type ResolvedAsyncStatusChild } from "../shared/child-identity.ts";
7
12
 
@@ -21,6 +26,80 @@ function getAsyncStopTarget(
21
26
  return direct ? { asyncId: direct.asyncId, asyncDir: direct.asyncDir } : undefined;
22
27
  }
23
28
 
29
+ const STOP_MESSAGE = "Subagent stopped by user.";
30
+
31
+ function sealPausedRun(asyncDir: string, status: AsyncStatus): string | undefined {
32
+ const runnerProcessInstanceId = status.processTerminal?.runnerProcessInstanceId;
33
+ if (!runnerProcessInstanceId) return "runner process identity is missing";
34
+ const proof = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId });
35
+ if (proof?.state !== "observed" || proof.runId !== status.runId || proof.runnerProcessInstanceId !== runnerProcessInstanceId) {
36
+ return `process-terminal proof is ${proof?.state === "unknown" ? `unknown (${proof.reason})` : proof?.state ?? "missing"}`;
37
+ }
38
+ if (!status.sessionId) return "session identity is missing";
39
+ const existingResultPath = resultPayloadPathForSessionRun(DIRS.results, status.sessionId, status.runId);
40
+ if (!existingResultPath) return "paused result is missing";
41
+ let existingResult: Record<string, unknown>;
42
+ try {
43
+ const parsed: unknown = JSON.parse(fs.readFileSync(existingResultPath, "utf-8"));
44
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("result is not an object");
45
+ existingResult = parsed as Record<string, unknown>;
46
+ } catch (error) {
47
+ return `paused result is unavailable: ${error instanceof Error ? error.message : String(error)}`;
48
+ }
49
+ const resultRunId = typeof existingResult.runId === "string" ? existingResult.runId : existingResult.id;
50
+ if (resultRunId !== status.runId || existingResult.sessionId !== status.sessionId) {
51
+ return "paused result identity does not match the run";
52
+ }
53
+
54
+ const now = Date.now();
55
+ const steps = (status.steps ?? []).map((step) => {
56
+ if (step.status !== "pending" && step.status !== "running" && step.status !== "paused") return step;
57
+ const { activityState: _activityState, ...rest } = step;
58
+ return {
59
+ ...rest,
60
+ status: "stopped" as const,
61
+ error: STOP_MESSAGE,
62
+ exitCode: 1,
63
+ stopped: true,
64
+ endedAt: now,
65
+ durationMs: step.startedAt ? now - step.startedAt : 0,
66
+ lastActivityAt: now,
67
+ };
68
+ });
69
+ const results = Array.isArray(existingResult.results)
70
+ ? existingResult.results.map((entry, index) => {
71
+ const stepState = status.steps?.[index]?.status;
72
+ if ((stepState !== "pending" && stepState !== "running" && stepState !== "paused") || !entry || typeof entry !== "object" || Array.isArray(entry)) return entry;
73
+ const { interrupted: _interrupted, ...result } = entry as Record<string, unknown>;
74
+ return { ...result, output: STOP_MESSAGE, error: STOP_MESSAGE, success: false, exitCode: 1, stopped: true };
75
+ })
76
+ : existingResult.results;
77
+ const stoppedResult = {
78
+ ...existingResult,
79
+ success: false,
80
+ state: "stopped",
81
+ summary: STOP_MESSAGE,
82
+ error: STOP_MESSAGE,
83
+ stopped: true,
84
+ exitCode: 1,
85
+ timestamp: now,
86
+ ...(Array.isArray(existingResult.results) ? { results } : {}),
87
+ };
88
+ const stoppedStatus: AsyncStatus = {
89
+ ...status,
90
+ state: "stopped",
91
+ stopped: true,
92
+ error: STOP_MESSAGE,
93
+ endedAt: now,
94
+ lastUpdate: now,
95
+ steps,
96
+ };
97
+ delete stoppedStatus.activityState;
98
+ writeAsyncResultFile(resultFilePath(DIRS.results, status.runId), stoppedResult);
99
+ writeAtomicJson(path.join(asyncDir, "status.json"), stoppedStatus);
100
+ updateActiveRunIndex(asyncDir, "stopped", status.toolCallId, { retryCapacityErrors: true, terminalIndexBeforeRelease: true });
101
+ }
102
+
24
103
  export function stopAsyncRun(
25
104
  state: SubagentState,
26
105
  runId: string | undefined,
@@ -38,7 +117,8 @@ export function stopAsyncRun(
38
117
  details: { mode: "management", results: [] },
39
118
  };
40
119
  }
41
- if (!status || (status.state !== "running" && status.state !== "queued")) {
120
+ const pausedWholeRun = status?.state === "paused" && childId === undefined;
121
+ if (!status || (status.state !== "running" && status.state !== "queued" && !pausedWholeRun)) {
42
122
  return {
43
123
  content: [{ type: "text", text: `No running or queued async run was found for '${runId ?? "current"}'.` }],
44
124
  isError: true,
@@ -66,13 +146,23 @@ export function stopAsyncRun(
66
146
  }
67
147
  try {
68
148
  deliverStopRequest({ asyncDir: target.asyncDir, pid: typeof status.pid === "number" ? status.pid : undefined, kill, source: "stop-action", targetIndex: child?.index, childId: child?.id ?? childId });
149
+ if (pausedWholeRun) {
150
+ const failure = sealPausedRun(target.asyncDir, status);
151
+ if (failure) {
152
+ return {
153
+ content: [{ type: "text", text: `Stop request persisted for paused async run ${target.asyncId}, but terminal proof is not ready (${failure}). Retry stop after runner shutdown is observed.` }],
154
+ isError: true,
155
+ details: { mode: "management", results: [] },
156
+ };
157
+ }
158
+ }
69
159
  const tracked = state.asyncJobs.get(target.asyncId);
70
160
  if (tracked) {
71
161
  tracked.activityState = undefined;
72
162
  tracked.updatedAt = Date.now();
73
163
  }
74
164
  return {
75
- content: [{ type: "text", text: child ? `Stop requested for child ${child.id} in async run ${target.asyncId}.` : `Stop requested for async run ${target.asyncId}.` }],
165
+ content: [{ type: "text", text: pausedWholeRun ? `Stopped paused async run ${target.asyncId}.` : child ? `Stop requested for child ${child.id} in async run ${target.asyncId}.` : `Stop requested for async run ${target.asyncId}.` }],
76
166
  details: { mode: "management", results: [] },
77
167
  };
78
168
  } catch (error) {