pi-subagents 0.81.0 → 0.82.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 (152) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +1 -1
  3. package/bundle/agents.js +3 -3
  4. package/bundle/child-tool-plan.js +2 -2
  5. package/bundle/{chunk-L5ZHE4L4.js → chunk-3JCDWF4V.js} +4 -4
  6. package/bundle/{chunk-LSRUTHT7.js → chunk-43SCHIMD.js} +1 -1
  7. package/bundle/{chunk-VJHOELQP.js → chunk-46XKCPJR.js} +193 -81
  8. package/bundle/{chunk-G5DKFLLI.js → chunk-4SDU2BZX.js} +15 -10
  9. package/bundle/{chunk-2ZSNH26H.js → chunk-4Y2ROR7L.js} +7 -7
  10. package/bundle/{chunk-V2JWDB5U.js → chunk-52UBALJL.js} +2 -2
  11. package/bundle/{chunk-3DC5KBVP.js → chunk-5TYMSZX3.js} +1 -1
  12. package/bundle/{chunk-NNLASTQE.js → chunk-6XJB7PIX.js} +20 -6
  13. package/bundle/{chunk-ZBC44JJ3.js → chunk-CIZQXJ5Q.js} +22 -9
  14. package/bundle/{chunk-KSWQVGDW.js → chunk-FXUXCX2K.js} +225 -18
  15. package/bundle/{chunk-563BHAZ5.js → chunk-G4GZD3OJ.js} +35 -26
  16. package/bundle/{chunk-ARRKBB2B.js → chunk-GJECHSAR.js} +2 -2
  17. package/bundle/{chunk-V3BIUPLU.js → chunk-IVAE5Z6Q.js} +256 -52
  18. package/bundle/{chunk-V6MR5TOQ.js → chunk-JYUKW2SY.js} +25 -19
  19. package/bundle/{chunk-BJN5LUQY.js → chunk-OYF6AFDY.js} +1 -1
  20. package/bundle/{chunk-CEKS3TFO.js → chunk-PBK2I7WY.js} +6 -6
  21. package/bundle/{chunk-TUWLXTAM.js → chunk-PDZWJCHD.js} +681 -320
  22. package/bundle/{chunk-EYNJ6VL6.js → chunk-PLCCBLPY.js} +2 -2
  23. package/bundle/{chunk-XMFLCKMQ.js → chunk-QE4AGIMG.js} +339 -132
  24. package/bundle/{chunk-74RT5YQE.js → chunk-RMQFMMAT.js} +123 -33
  25. package/bundle/{chunk-WSBNENFS.js → chunk-UCPP4GNU.js} +14 -4
  26. package/bundle/{chunk-2XYWN7QJ.js → chunk-UTT254JE.js} +2 -2
  27. package/bundle/{chunk-BPS2AGBZ.js → chunk-VSUSLDNW.js} +93 -52
  28. package/bundle/{chunk-ZP3HODFH.js → chunk-XSWOTIA3.js} +1 -1
  29. package/bundle/{chunk-3IF2PGW7.js → chunk-YKEWIB4M.js} +1 -1
  30. package/bundle/{chunk-26C3WLYQ.js → chunk-ZIV4J4HG.js} +498 -274
  31. package/bundle/control-channel.js +2 -2
  32. package/bundle/index.js +1 -1
  33. package/bundle/intercom-bridge.js +2 -2
  34. package/bundle/preflight.js +8 -8
  35. package/bundle/project-panes.js +3 -3
  36. package/docs/agents.md +37 -7
  37. package/docs/configuration.md +9 -1
  38. package/docs/tool-reference.md +3 -1
  39. package/docs/workflows.md +16 -0
  40. package/package.json +2 -2
  41. package/skills/council-mode/SKILL.md +1 -4
  42. package/skills/pi-subagents/SKILL.md +130 -107
  43. package/skills/pi-subagents/references/orchestration.md +124 -0
  44. package/skills/pi-subagents/references/prompting-and-roles.md +96 -295
  45. package/src/agents/agent-management.js +3 -3
  46. package/src/agents/agent-memory.js +3 -1
  47. package/src/agents/agents.js +6 -6
  48. package/src/agents/frontmatter.d.ts +2 -0
  49. package/src/agents/frontmatter.js +15 -1
  50. package/src/agents/skills.js +2 -1
  51. package/src/extension/config.js +3 -0
  52. package/src/extension/herdr-pi-bridge.js +17 -10
  53. package/src/extension/index.js +82 -2
  54. package/src/extension/reply-workflow-script.js +5 -2
  55. package/src/extension/schemas.js +3 -3
  56. package/src/extension/subagent-guide.js +1 -2
  57. package/src/intercom/native-supervisor-channel.js +20 -0
  58. package/src/missions/lifecycle.js +7 -1
  59. package/src/runs/background/active-async-capacity.d.ts +1 -0
  60. package/src/runs/background/active-async-capacity.js +9 -3
  61. package/src/runs/background/async-execution.js +20 -10
  62. package/src/runs/background/async-job-tracker.d.ts +6 -1
  63. package/src/runs/background/async-job-tracker.js +9 -4
  64. package/src/runs/background/async-status.d.ts +4 -0
  65. package/src/runs/background/async-status.js +22 -3
  66. package/src/runs/background/await-async-run.js +3 -7
  67. package/src/runs/background/boot-id.d.ts +12 -0
  68. package/src/runs/background/boot-id.js +47 -0
  69. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  70. package/src/runs/background/interrupted-elsewhere.js +68 -0
  71. package/src/runs/background/notify.d.ts +2 -0
  72. package/src/runs/background/notify.js +13 -0
  73. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  74. package/src/runs/background/result-delivery-ownership.js +19 -1
  75. package/src/runs/background/result-watcher.d.ts +3 -1
  76. package/src/runs/background/result-watcher.js +24 -8
  77. package/src/runs/background/run-child-session.d.ts +1 -6
  78. package/src/runs/background/run-child-session.js +1 -1
  79. package/src/runs/background/run-status.js +2 -1
  80. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  81. package/src/runs/background/stale-run-reconciler.js +52 -13
  82. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  83. package/src/runs/background/subagent-runner.d.ts +4 -0
  84. package/src/runs/background/subagent-runner.js +13 -5
  85. package/src/runs/background/subagent-wait.d.ts +1 -1
  86. package/src/runs/background/subagent-wait.js +6 -1
  87. package/src/runs/background/terminal-run-index.d.ts +2 -0
  88. package/src/runs/background/terminal-run-index.js +44 -3
  89. package/src/runs/foreground/async-stop-action.js +3 -1
  90. package/src/runs/foreground/execution.js +6 -3
  91. package/src/runs/foreground/foreground-history.d.ts +16 -2
  92. package/src/runs/foreground/foreground-history.js +133 -31
  93. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  94. package/src/runs/foreground/subagent-executor.js +277 -74
  95. package/src/runs/shared/child-launch.d.ts +2 -2
  96. package/src/runs/shared/child-session.d.ts +3 -2
  97. package/src/runs/shared/child-session.js +51 -8
  98. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  99. package/src/runs/shared/child-tool-plan.js +21 -6
  100. package/src/runs/shared/herdr-connection.d.ts +17 -8
  101. package/src/runs/shared/herdr-connection.js +19 -7
  102. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  103. package/src/runs/shared/herdr-external-adapters.js +1 -1
  104. package/src/runs/shared/herdr-machine.d.ts +12 -2
  105. package/src/runs/shared/herdr-machine.js +55 -9
  106. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  107. package/src/runs/shared/herdr-pane-host.js +155 -0
  108. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  109. package/src/runs/shared/herdr-placed-run.js +48 -395
  110. package/src/runs/shared/local-machine-access.d.ts +5 -0
  111. package/src/runs/shared/local-machine-access.js +28 -0
  112. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  113. package/src/runs/shared/orca-progress-tabs.js +1 -24
  114. package/src/runs/shared/parallel-utils.d.ts +1 -1
  115. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  116. package/src/runs/shared/placed-run-owner.js +93 -0
  117. package/src/runs/shared/placement-host.d.ts +99 -0
  118. package/src/runs/shared/placement-host.js +157 -0
  119. package/src/runs/shared/rex-machine.d.ts +25 -0
  120. package/src/runs/shared/rex-machine.js +83 -0
  121. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  122. package/src/runs/shared/rex-pane-host.js +327 -0
  123. package/src/shared/completion-owner.d.ts +5 -0
  124. package/src/shared/completion-owner.js +5 -0
  125. package/src/shared/session-identity.d.ts +2 -0
  126. package/src/shared/session-identity.js +26 -0
  127. package/src/shared/types.d.ts +45 -6
  128. package/src/tui/fleet.d.ts +1 -0
  129. package/src/tui/fleet.js +25 -15
  130. package/src/tui/render.d.ts +6 -0
  131. package/src/tui/render.js +3 -3
  132. package/src/tui/subagent-messages.js +1 -0
  133. package/src/workflows/scripted-workflow.d.ts +1 -1
  134. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  135. package/src/workflows/workflow-auto-resume.js +102 -0
  136. package/src/workflows/workflow-launch-record.d.ts +27 -0
  137. package/src/workflows/workflow-launch-record.js +48 -0
  138. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  139. package/src/workflows/workflow-resume-claim.js +210 -0
  140. package/src/workflows/workflow-resume.d.ts +56 -0
  141. package/src/workflows/workflow-resume.js +106 -0
  142. package/src/workflows/workflow-reuse.d.ts +15 -9
  143. package/src/workflows/workflow-reuse.js +77 -14
  144. package/src/workflows/workflow-revival.d.ts +2 -0
  145. package/src/workflows/workflow-revival.js +2 -1
  146. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  147. package/src/workflows/workflow-stop-cause.js +8 -0
  148. package/skills/pi-subagents/references/constraints-and-recipes.md +0 -74
  149. package/skills/pi-subagents/references/execution-controls.md +0 -563
  150. package/skills/pi-subagents/references/management-authoring-rpc.md +0 -165
  151. package/skills/pi-subagents/references/multi-lane-orchestration.md +0 -61
  152. package/skills/pi-subagents/references/review-and-validation.md +0 -73
@@ -0,0 +1,155 @@
1
+ import { createHash } from "node:crypto";
2
+ import { connectHerdrMachine, sshMachineAccess } from "./herdr-connection.js";
3
+ import { HERDR_PI_MODE_ENV, HERDR_PI_RUN_ENV, HERDR_PI_RUNTIME_DIR_ENV } from "./herdr-pi-protocol.js";
4
+ import { createPlacedRuntimeDir, discoverPlacedBridgeManifest, PlacedRunOwner, removePlacedRuntimeDir } from "./placed-run-owner.js";
5
+ import { herdrPaneAllocationKey, withHerdrPaneAllocationLock } from "./placement-host.js";
6
+ function herdrRunState(status, fallback = "unknown") {
7
+ return status === "working" ? "working" : status === "blocked" ? "blocked" : status === "idle" || status === "done" ? "settled" : status === "unknown" ? "unknown" : fallback;
8
+ }
9
+ export function herdrStatusSubscriptions(paneId) {
10
+ return [{ type: "pane.agent_status_changed", pane_id: paneId }, { type: "pane.closed" }, { type: "pane.moved" }];
11
+ }
12
+ export function reconcileHerdrPlacedRun(identity, candidate, proveEvidence) {
13
+ if (candidate.endpoint.session !== identity.session)
14
+ return { ok: false, reason: "Herdr session changed during reconnect." };
15
+ const matches = candidate.agents.filter((agent) => agent.terminal_id === identity.terminalId);
16
+ if (matches.length !== 1 || typeof matches[0].pane_id !== "string")
17
+ return { ok: false, reason: matches.length ? "Terminal identity is ambiguous." : "Owned terminal is missing." };
18
+ const proof = proveEvidence();
19
+ if (!proof.ok)
20
+ return proof;
21
+ return { ok: true, state: herdrRunState(matches[0].agent_status), paneId: matches[0].pane_id, cursor: proof.cursor };
22
+ }
23
+ function idOf(value, key) { const found = value && typeof value === "object" ? value[key] : undefined; if (typeof found !== "string" || !found)
24
+ throw new Error(`Herdr did not return ${key}.`); return found; }
25
+ function list(value, key) { if (Array.isArray(value))
26
+ return value; const found = value && typeof value === "object" ? value[key] : undefined; return Array.isArray(found) ? found : []; }
27
+ function tagged(value, type, key) { if (!value || typeof value !== "object" || value.type !== type || !(key in value) || typeof value[key] !== "object")
28
+ throw new Error(`Herdr returned an invalid ${type} envelope.`); return value[key]; }
29
+ export function parseHerdrCreated(value, type) { const envelope = value; if (envelope?.type !== type)
30
+ throw new Error(`Herdr returned an invalid ${type} envelope.`); const tab = envelope.tab; const pane = envelope.root_pane; return { ...(type === "workspace_created" ? { workspaceId: idOf(envelope.workspace, "workspace_id") } : {}), tabId: idOf(tab, "tab_id"), paneId: idOf(pane, "pane_id") }; }
31
+ export function parseHerdrAgent(value, type) { return tagged(value, type, "agent"); }
32
+ export function parseHerdrSessionSnapshot(value) { return tagged(value, "session_snapshot", "snapshot"); }
33
+ export function ownsHerdrPane(agent, terminalId, paneId) { return agent.terminal_id === terminalId && agent.pane_id === paneId; }
34
+ function snapshotAgents(snapshot) { return list(snapshot, "agents").filter((agent) => Boolean(agent && typeof agent === "object")).map((agent) => ({ terminal_id: typeof agent.terminal_id === "string" ? agent.terminal_id : undefined, pane_id: typeof agent.pane_id === "string" ? agent.pane_id : undefined, agent_status: typeof agent.agent_status === "string" ? agent.agent_status : undefined })); }
35
+ export const HERDR_MAX_OWNED_PANES = 20;
36
+ export async function provisionHerdrPane(client, cwd, runId, runtimeDir, environment, allocationKey = cwd) {
37
+ return withHerdrPaneAllocationLock(allocationKey, async () => {
38
+ const snapshot = parseHerdrSessionSnapshot(await client.call("session.snapshot"));
39
+ const panes = list(snapshot, "panes").filter((item) => Boolean(item && typeof item === "object")), workspaces = list(snapshot, "workspaces").filter((item) => Boolean(item && typeof item === "object"));
40
+ const ownedLabel = `pi-subagents-${createHash("sha256").update(cwd).digest("hex").slice(0, 10)}`, owned = workspaces.filter((item) => item.label === ownedLabel);
41
+ if (owned.length > 1)
42
+ throw new Error("Herdr deterministic owned workspace identity is ambiguous.");
43
+ const cwdWorkspaceIds = new Set(panes.filter((item) => item.cwd === cwd || item.foreground_cwd === cwd).map((item) => item.workspace_id).filter((id) => typeof id === "string")), cwdWorkspaces = workspaces.filter((item) => typeof item.workspace_id === "string" && cwdWorkspaceIds.has(item.workspace_id));
44
+ const selected = owned[0] ?? (cwdWorkspaces.length === 1 ? cwdWorkspaces[0] : undefined), selectedId = selected ? idOf(selected, "workspace_id") : undefined, actual = selectedId ? panes.filter((pane) => pane.workspace_id === selectedId).length : 0;
45
+ if (actual >= HERDR_MAX_OWNED_PANES)
46
+ throw new Error(`Herdr workspace pane limit of ${HERDR_MAX_OWNED_PANES} is reached; close an existing pane before launching another placed run.`);
47
+ const env = environment ?? { [HERDR_PI_MODE_ENV]: "1", [HERDR_PI_RUN_ENV]: runId, [HERDR_PI_RUNTIME_DIR_ENV]: runtimeDir };
48
+ if (selectedId) {
49
+ const tab = parseHerdrCreated(await client.call("tab.create", { workspace_id: selectedId, cwd, label: `subagent-${runId.slice(-8)}`, env, focus: false }), "tab_created");
50
+ return { workspaceId: selectedId, tabId: tab.tabId, paneId: tab.paneId };
51
+ }
52
+ const workspace = parseHerdrCreated(await client.call("workspace.create", { cwd, label: ownedLabel, env, focus: false }), "workspace_created");
53
+ return { workspaceId: workspace.workspaceId, tabId: workspace.tabId, paneId: workspace.paneId };
54
+ });
55
+ }
56
+ function provisionHerdrOwnedPane(connection, machine, runId, runtimeDir, environment) { return provisionHerdrPane(connection.client, machine.cwd, runId, runtimeDir, environment, herdrPaneAllocationKey(machine.target, connection.endpoint.session, machine.cwd)); }
57
+ function asHerdrMachine(machine) { if (machine.provider !== "herdr")
58
+ throw new Error("The Herdr pane host only places runs on Herdr saved machines."); return machine; }
59
+ async function startHerdrAgent(connection, owned, kind, name, args, options = {}) {
60
+ const clock = options.clock ?? { now: () => Date.now(), wait: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) }, deadline = clock.now() + (options.timeoutMs ?? 5_000), pollMs = options.pollMs ?? 50;
61
+ let response;
62
+ for (;;) {
63
+ try {
64
+ response = await connection.client.call("agent.start", { name, kind, pane_id: owned.paneId, args, timeout_ms: 45_000 }, 60_000);
65
+ break;
66
+ }
67
+ catch (error) {
68
+ if (!(error instanceof Error) || !error.message.includes("is not an available shell"))
69
+ throw error;
70
+ const pane = tagged(await connection.client.call("pane.get", { pane_id: owned.paneId }), "pane_info", "pane");
71
+ if (pane.pane_id !== owned.paneId || pane.workspace_id !== owned.workspaceId || pane.tab_id !== owned.tabId)
72
+ throw new Error("Herdr startup pane identity changed after a transient start rejection.", { cause: error });
73
+ if (pane.agent != null || pane.agent_session != null)
74
+ throw new Error("Herdr startup pane became occupied after a transient start rejection.", { cause: error });
75
+ if (clock.now() >= deadline)
76
+ throw new Error("Herdr owned pane remained unavailable until the startup deadline.", { cause: error });
77
+ await clock.wait(Math.min(pollMs, deadline - clock.now()));
78
+ }
79
+ }
80
+ const started = parseHerdrAgent(response, "agent_started"), terminalId = idOf(started, "terminal_id"), paneId = idOf(started, "pane_id");
81
+ if (paneId !== owned.paneId)
82
+ throw new Error("Herdr started the native agent in an unexpected pane.");
83
+ return { terminalId, paneId };
84
+ }
85
+ async function closeOwnedHerdrPane(connection, owned, terminalId, agentName) {
86
+ let closed = false;
87
+ if (terminalId && agentName) {
88
+ const current = parseHerdrAgent(await connection.client.call("agent.get", { target: agentName }), "agent_info");
89
+ if (ownsHerdrPane(current, terminalId, owned.paneId)) {
90
+ await connection.client.call("pane.close", { pane_id: owned.paneId });
91
+ closed = true;
92
+ }
93
+ }
94
+ if (!closed) {
95
+ const snapshot = parseHerdrSessionSnapshot(await connection.client.call("session.snapshot"));
96
+ const occupants = snapshotAgents(snapshot).filter((agent) => agent.pane_id === owned.paneId);
97
+ if (occupants.length !== 0)
98
+ throw new Error("Refusing cleanup because startup pane ownership is uncertain.");
99
+ await connection.client.call("pane.close", { pane_id: owned.paneId });
100
+ }
101
+ }
102
+ /** Herdr pane host: Herdr RPC over the SSH-forwarded Herdr socket. Ownership is the Herdr terminal_id/pane_id pair; run state comes from agent_status. */
103
+ export const herdrPaneHost = {
104
+ connect(machine) { return connectHerdrMachine(asHerdrMachine(machine)); },
105
+ sessionOf(connection) { return connection.endpoint.session; },
106
+ async launch(connection, machine, runId, runtimeDir, request, onPane, options) { const owned = await provisionHerdrOwnedPane(connection, asHerdrMachine(machine), runId, runtimeDir, request.environment); await onPane(owned); const { terminalId } = await startHerdrAgent(connection, owned, request.kind, request.name, request.args, options); return { owned, terminalId }; },
107
+ async prompt(connection, identity, text) { await connection.client.call("agent.prompt", { target: identity.agentName, text }); },
108
+ subscribe(connection, paneId, listener, onDisconnect) { return connection.client.subscribe(herdrStatusSubscriptions(paneId), listener, onDisconnect); },
109
+ paneEvent(event, fallback) { const data = event && typeof event === "object" ? event.data : undefined; return data ? { terminalId: data.terminal_id, paneId: data.pane_id, state: herdrRunState(data.agent_status, fallback) } : undefined; },
110
+ async panes(connection) { return { endpoint: connection.endpoint, agents: snapshotAgents(parseHerdrSessionSnapshot(await connection.client.call("session.snapshot"))) }; },
111
+ reconcile: reconcileHerdrPlacedRun,
112
+ closeOwnedPane: closeOwnedHerdrPane,
113
+ };
114
+ /** SSH entry points for the host-neutral runtime-dir and manifest helpers. */
115
+ export function discoverBridgeManifest(machine, runId, runtimeDir, options = {}) { return discoverPlacedBridgeManifest(sshMachineAccess(machine, options), runId, runtimeDir); }
116
+ export function createRemoteRuntimeDir(machine, runId, options = {}) { return createPlacedRuntimeDir(sshMachineAccess(machine, options), runId); }
117
+ export function removeRemoteRuntimeDir(machine, runtimeDir, options = {}) { removePlacedRuntimeDir(sshMachineAccess(machine, options), runtimeDir); }
118
+ /** The placed-run owner bound to Herdr and SSH access, plus the separate provision and start steps the external CLI adapters use. */
119
+ export class HerdrPlacedRunOwner extends PlacedRunOwner {
120
+ constructor(machine, runId, runtimeDir, connection, removeRuntime) { super(machine, runId, runtimeDir, connection, herdrPaneHost, sshMachineAccess(machine), removeRuntime); }
121
+ static async create(machine, runId, beforePane) { const runtimeDir = await createRemoteRuntimeDir(machine, runId); let connection; try {
122
+ connection = await herdrPaneHost.connect(machine);
123
+ const owner = new HerdrPlacedRunOwner(machine, runId, runtimeDir, connection);
124
+ await beforePane?.(owner);
125
+ return owner;
126
+ }
127
+ catch (error) {
128
+ const cleanupErrors = [];
129
+ try {
130
+ await connection?.close();
131
+ }
132
+ catch (cleanupError) {
133
+ cleanupErrors.push(cleanupError);
134
+ }
135
+ try {
136
+ removeRemoteRuntimeDir(machine, runtimeDir);
137
+ }
138
+ catch (cleanupError) {
139
+ cleanupErrors.push(cleanupError);
140
+ }
141
+ if (cleanupErrors.length)
142
+ throw new AggregateError([error, ...cleanupErrors], "Herdr owner creation failed and cleanup also failed.", { cause: error });
143
+ throw error;
144
+ } }
145
+ async provision(environment) { this.owned = await provisionHerdrOwnedPane(this.connection, this.machine, this.runId, this.runtimeDir, environment); return this.owned; }
146
+ async start(kind, name, args, options = {}) {
147
+ if (!this.owned)
148
+ throw new Error("Herdr placement owner has not provisioned its pane.");
149
+ const { terminalId, paneId } = await startHerdrAgent(this.connection, this.owned, kind, name, args, options), argv = [kind, ...args];
150
+ this.agentName = name;
151
+ this.terminalId = terminalId;
152
+ this.startedArgv = argv;
153
+ return { terminalId, paneId, argv };
154
+ }
155
+ }
@@ -1,10 +1,11 @@
1
1
  import * as net from "node:net";
2
2
  import type { AgentMessage } from "@earendil-works/pi-agent-core";
3
- import type { HerdrMachineReference, HerdrRemoteGitStatus } from "../../shared/types.ts";
3
+ import type { HerdrRemoteGitStatus, PlacementMachineReference } from "../../shared/types.ts";
4
4
  import type { ChildSession, ChildSessionEvent, ChildSessionFactory, ChildSessionLaunch } from "./child-session.ts";
5
5
  import type { ChildRuntimeConfig } from "./child-runtime-config.ts";
6
- import { connectHerdrMachine, type HerdrForwardedConnection, type HerdrRpcClient } from "./herdr-connection.ts";
7
6
  import { HerdrPiFrameDecoder, type HerdrPiFrame } from "./herdr-pi-protocol.ts";
7
+ import { type PlacedBridgeManifest, type PlacedRunOwner } from "./placed-run-owner.ts";
8
+ import type { PlacementBridgeIdentity, PlacementPaneHost, PlacementReconnectCheck, PlacementRunState, PlacementSockets } from "./placement-host.ts";
8
9
  export interface HerdrRunIdentity {
9
10
  runId: string;
10
11
  machineId: string;
@@ -21,82 +22,30 @@ export interface HerdrRunIdentity {
21
22
  }
22
23
  export interface HerdrRunSnapshot {
23
24
  connection: "connected" | "unknown" | "closed";
24
- state: "ready" | "working" | "blocked" | "settled" | "unknown";
25
+ state: PlacementRunState;
25
26
  identity?: HerdrRunIdentity;
26
27
  revision: number;
27
28
  }
28
- export interface HerdrReconnectCandidate {
29
- endpoint: {
30
- session: string | null;
31
- protocol: number;
32
- version: string;
33
- };
34
- agents: Array<{
35
- terminal_id?: string;
36
- pane_id?: string;
37
- agent_status?: string;
38
- }>;
39
- bridge: {
40
- runId?: string;
41
- nativeSessionId?: string;
42
- packageVersion?: string;
43
- protocol?: number;
44
- evidenceCursor?: number;
45
- evidenceFloor?: number;
46
- };
47
- }
48
- export declare function herdrStatusSubscriptions(paneId: string): unknown[];
49
- export declare function reconcileHerdrPlacedRun(identity: HerdrRunIdentity, candidate: HerdrReconnectCandidate, proveEvidence: () => {
50
- ok: true;
51
- cursor: number;
52
- } | {
53
- ok: false;
54
- reason: string;
55
- }): {
56
- ok: true;
57
- state: HerdrRunSnapshot["state"];
58
- paneId: string;
59
- cursor: number;
60
- } | {
61
- ok: false;
62
- reason: string;
63
- };
64
- export declare function reconcileHerdrReconnect(identity: HerdrRunIdentity, candidate: HerdrReconnectCandidate, lastEvidenceCursor: number): {
65
- ok: true;
66
- state: HerdrRunSnapshot["state"];
67
- paneId: string;
68
- cursor: number;
69
- } | {
70
- ok: false;
71
- reason: string;
72
- };
73
- export declare function boundedHerdrReconnect(identity: HerdrRunIdentity, lastEvidenceCursor: number, attempt: (number: number) => Promise<HerdrReconnectCandidate>, options?: {
29
+ /** The pane half comes from the host; the bridge half (run, package, protocol, native session, evidence cursor) is host-neutral. */
30
+ export declare function reconcileHerdrReconnect(identity: HerdrRunIdentity, candidate: {
31
+ bridge: PlacementBridgeIdentity;
32
+ }, lastEvidenceCursor: number, host?: Pick<PlacementPaneHost, "reconcile">): PlacementReconnectCheck;
33
+ export declare function boundedHerdrReconnect(identity: HerdrRunIdentity, lastEvidenceCursor: number, attempt: (number: number) => Promise<{
34
+ bridge: PlacementBridgeIdentity;
35
+ }>, options?: {
74
36
  attempts?: number;
75
37
  deadlineMs?: number;
76
38
  disposed?: () => boolean;
39
+ host?: Pick<PlacementPaneHost, "reconcile">;
77
40
  }): Promise<{
78
41
  ok: true;
79
- state: HerdrRunSnapshot["state"];
42
+ state: PlacementRunState;
80
43
  paneId: string;
81
44
  cursor: number;
82
45
  } | {
83
46
  ok: false;
84
47
  reason: string;
85
48
  }>;
86
- export declare function parseHerdrCreated(value: unknown, type: "workspace_created" | "tab_created"): {
87
- workspaceId?: string;
88
- tabId: string;
89
- paneId: string;
90
- };
91
- export declare function parseHerdrAgent(value: unknown, type: "agent_started" | "agent_info" | "agent_prompted"): Record<string, unknown>;
92
- export declare function parseHerdrSessionSnapshot(value: unknown): Record<string, unknown>;
93
- export declare function ownsHerdrPane(agent: Record<string, unknown>, terminalId: string, paneId: string): boolean;
94
- export declare const HERDR_MAX_OWNED_PANES = 20;
95
- export declare function herdrPaneAllocationKey(target: string, session: string | null, cwd: string): string;
96
- export declare function withHerdrPaneAllocationLock<T>(key: string, action: () => Promise<T>, options?: {
97
- timeoutMs?: number;
98
- pollMs?: number;
99
- }): Promise<T>;
100
49
  export declare function serializeHerdrPiLaunch(launch: ChildSessionLaunch): {
101
50
  args: string[];
102
51
  resources: {
@@ -106,81 +55,6 @@ export declare function serializeHerdrPiLaunch(launch: ChildSessionLaunch): {
106
55
  reads?: string[] | false;
107
56
  };
108
57
  };
109
- export declare function provisionHerdrPane(client: HerdrRpcClient, cwd: string, runId: string, runtimeDir: string, environment?: Record<string, string>, allocationKey?: string): Promise<{
110
- workspaceId: string;
111
- tabId: string;
112
- paneId: string;
113
- }>;
114
- export declare function discoverBridgeManifest(machine: HerdrMachineReference, runId: string, runtimeDir: string, options?: {
115
- sshBin?: string;
116
- env?: NodeJS.ProcessEnv;
117
- }): Promise<{
118
- socketPath: string;
119
- nativeSessionId: string;
120
- packageVersion: string;
121
- protocol: number;
122
- }>;
123
- export declare function createRemoteRuntimeDir(machine: HerdrMachineReference, runId: string, options?: {
124
- sshBin?: string;
125
- env?: NodeJS.ProcessEnv;
126
- }): Promise<string>;
127
- export declare function removeRemoteRuntimeDir(machine: HerdrMachineReference, runtimeDir: string, options?: {
128
- sshBin?: string;
129
- env?: NodeJS.ProcessEnv;
130
- }): void;
131
- /** The single private owner for allocation shared by every pane-native backend projection. */
132
- export declare class HerdrPlacedRunOwner {
133
- #private;
134
- readonly machine: HerdrMachineReference;
135
- readonly runId: string;
136
- readonly runtimeDir: string;
137
- connection: HerdrForwardedConnection;
138
- owned?: {
139
- workspaceId: string;
140
- tabId: string;
141
- paneId: string;
142
- };
143
- terminalId?: string;
144
- agentName?: string;
145
- startedArgv?: string[];
146
- identity?: HerdrRunIdentity;
147
- private constructor();
148
- static create(machine: HerdrMachineReference, runId: string, beforePane?: (owner: HerdrPlacedRunOwner) => void | Promise<void>): Promise<HerdrPlacedRunOwner>;
149
- observeEvent(event: unknown): void;
150
- markConnectionUnknown(observed: HerdrForwardedConnection): boolean;
151
- statusSubscriptions(paneId?: string | undefined): unknown[];
152
- subscribe(listener: (event: unknown) => void, disconnected: (error: Error) => void): Promise<void>;
153
- provision(environment?: Record<string, string>): Promise<{
154
- workspaceId: string;
155
- tabId: string;
156
- paneId: string;
157
- }>;
158
- start(kind: "pi" | "claude" | "cursor" | "codex", name: string, args: string[], options?: {
159
- timeoutMs?: number;
160
- pollMs?: number;
161
- clock?: {
162
- now(): number;
163
- wait(ms: number): Promise<void>;
164
- };
165
- }): Promise<{
166
- terminalId: string;
167
- paneId: string;
168
- argv: string[];
169
- }>;
170
- journal(identity: HerdrRunIdentity): void;
171
- get snapshot(): HerdrRunSnapshot;
172
- runRemote(command: string, options?: {
173
- timeout?: number;
174
- maxBuffer?: number;
175
- }): import("child_process").SpawnSyncReturns<string>;
176
- replaceConnection(input: {
177
- connection: HerdrForwardedConnection;
178
- unsubscribe: () => void;
179
- paneId: string;
180
- state: HerdrRunSnapshot["state"];
181
- }): Promise<void>;
182
- cleanup(closeStartedPane?: boolean): Promise<void>;
183
- }
184
58
  export declare class BridgeChannel {
185
59
  #private;
186
60
  readonly socket: net.Socket;
@@ -205,10 +79,10 @@ export declare class BridgeChannel {
205
79
  reads?: string[] | false;
206
80
  }): Promise<HerdrPiFrame>;
207
81
  supervisorDelivered(requestId: string): void;
208
- prepare(requestId: string, operation: string, text?: string, supervisorId?: string): Promise<HerdrPiFrame>;
82
+ prepare(requestId: string, operation: string, text?: string, supervisorId?: string, supervisorError?: string): Promise<HerdrPiFrame>;
209
83
  close(): void;
210
84
  }
211
- export declare class HerdrPiSession implements ChildSession {
85
+ export declare class HerdrPiSession<C extends PlacementSockets = PlacementSockets> implements ChildSession {
212
86
  #private;
213
87
  bridgeForward: {
214
88
  close(): Promise<void>;
@@ -218,19 +92,20 @@ export declare class HerdrPiSession implements ChildSession {
218
92
  readonly model: string | undefined;
219
93
  readonly supervisorDir: string | undefined;
220
94
  readonly runtime: ChildRuntimeConfig;
221
- readonly owner: HerdrPlacedRunOwner;
222
- constructor(owner: HerdrPlacedRunOwner, bridgeForward: {
95
+ readonly owner: PlacedRunOwner<C>;
96
+ constructor(owner: PlacedRunOwner<C>, bridgeForward: {
223
97
  close(): Promise<void>;
224
98
  }, bridge: BridgeChannel, requestedTools: string[] | undefined, model: string | undefined, runtime: ChildRuntimeConfig, initialGit?: HerdrRemoteGitStatus);
99
+ get host(): PlacementPaneHost<C>;
225
100
  get identity(): HerdrRunIdentity;
226
101
  set identity(value: HerdrRunIdentity);
227
- get connection(): HerdrForwardedConnection;
228
- set connection(value: HerdrForwardedConnection);
102
+ get connection(): C;
103
+ set connection(value: C);
229
104
  armReconnect(factory: () => Promise<void>): void;
230
105
  get evidenceCursor(): number;
231
106
  reconnect(): Promise<void>;
232
107
  replaceTransport(input: {
233
- connection: HerdrForwardedConnection;
108
+ connection: C;
234
109
  bridgeForward: {
235
110
  close(): Promise<void>;
236
111
  };
@@ -240,7 +115,6 @@ export declare class HerdrPiSession implements ChildSession {
240
115
  cursor: number;
241
116
  state: HerdrRunSnapshot["state"];
242
117
  }): Promise<void>;
243
- observeHerdr(value: unknown): void;
244
118
  subscribe(listener: (event: ChildSessionEvent) => void): () => boolean;
245
119
  prompt(text: string): Promise<void>;
246
120
  steer(text: string): Promise<void>;
@@ -254,14 +128,17 @@ export declare class HerdrPiSession implements ChildSession {
254
128
  get machineEvidence(): {
255
129
  final?: HerdrRemoteGitStatus | undefined;
256
130
  initial?: HerdrRemoteGitStatus | undefined;
131
+ provider: "herdr" | "rex";
257
132
  machineId: string;
258
133
  };
259
134
  get placementSnapshot(): HerdrRunSnapshot;
260
135
  }
261
- export declare function reconnectHerdrPiSession(session: HerdrPiSession, launch: ChildSessionLaunch, dependencies?: {
262
- connect?: typeof connectHerdrMachine;
263
- discoverManifest?: typeof discoverBridgeManifest;
136
+ export declare function reconnectHerdrPiSession<C extends PlacementSockets>(session: HerdrPiSession<C>, launch: ChildSessionLaunch, dependencies?: {
137
+ connect?: (machine: PlacementMachineReference) => Promise<C>;
138
+ discoverManifest?: (runId: string, runtimeDir: string) => Promise<PlacedBridgeManifest>;
264
139
  createBridge?: (socketPath: string, runId: string) => BridgeChannel;
265
140
  }): Promise<void>;
266
141
  export declare function createHerdrPiSession(launch: ChildSessionLaunch): Promise<ChildSession>;
142
+ /** A Rex machine: the same bridge session, with the Rex pane host and local (`machine: "local"`) or SSH machine access. */
143
+ export declare function createRexPiSession(launch: ChildSessionLaunch): Promise<ChildSession>;
267
144
  export declare function createPlacementAwareChildSessionFactory(local: ChildSessionFactory, createRemote?: (launch: ChildSessionLaunch) => Promise<ChildSession>): ChildSessionFactory;