@pi-agents/orchid 0.1.0-beta.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.
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/agents/AGENTS-MANIFEST.md +42 -0
- package/agents/brain.md +42 -0
- package/agents/context-builder.md +46 -0
- package/agents/delegate.md +12 -0
- package/agents/dev-1.md +42 -0
- package/agents/oracle.md +73 -0
- package/agents/planner.md +55 -0
- package/agents/researcher.md +52 -0
- package/agents/reviewer.md +79 -0
- package/agents/scout.md +50 -0
- package/agents/tester.md +45 -0
- package/agents/worker.md +55 -0
- package/extensions/ralph.ts +1 -0
- package/extensions/reviewer-extension.ts +125 -0
- package/extensions/task-orchestrator.ts +28 -0
- package/package.json +63 -0
- package/prompts/gather-context-and-clarify.md +13 -0
- package/prompts/parallel-cleanup.md +59 -0
- package/prompts/parallel-context-build.md +53 -0
- package/prompts/parallel-handoff-plan.md +59 -0
- package/prompts/parallel-research.md +50 -0
- package/prompts/parallel-review.md +54 -0
- package/prompts/review-loop.md +41 -0
- package/skills/orchid/SKILL.md +214 -0
- package/skills/orchid/orchid-cleanup/SKILL.md +122 -0
- package/skills/orchid/orchid-converge/SKILL.md +124 -0
- package/skills/orchid/orchid-decompose/SKILL.md +201 -0
- package/skills/orchid/orchid-doctor/SKILL.md +162 -0
- package/skills/orchid/orchid-investigate/SKILL.md +102 -0
- package/skills/orchid/orchid-launch/SKILL.md +147 -0
- package/skills/ralph/SKILL.md +73 -0
- package/skills/subagents/pi-subagents/SKILL.md +813 -0
- package/src/index.ts +7 -0
- package/src/orchestrator/abort.ts +534 -0
- package/src/orchestrator/agent-bridge-extension.ts +1020 -0
- package/src/orchestrator/agent-host.ts +954 -0
- package/src/orchestrator/cleanup.ts +776 -0
- package/src/orchestrator/config-loader.ts +1412 -0
- package/src/orchestrator/config-schema.ts +690 -0
- package/src/orchestrator/config.ts +81 -0
- package/src/orchestrator/context-window.ts +66 -0
- package/src/orchestrator/diagnostic-reports.ts +475 -0
- package/src/orchestrator/diagnostics.ts +394 -0
- package/src/orchestrator/discovery.ts +1833 -0
- package/src/orchestrator/engine-worker.ts +415 -0
- package/src/orchestrator/engine.ts +5940 -0
- package/src/orchestrator/execution.ts +3104 -0
- package/src/orchestrator/extension.ts +5934 -0
- package/src/orchestrator/formatting.ts +785 -0
- package/src/orchestrator/git.ts +88 -0
- package/src/orchestrator/index.ts +28 -0
- package/src/orchestrator/lane-runner.ts +1787 -0
- package/src/orchestrator/mailbox.ts +780 -0
- package/src/orchestrator/merge.ts +3414 -0
- package/src/orchestrator/messages.ts +1062 -0
- package/src/orchestrator/migrations.ts +278 -0
- package/src/orchestrator/naming.ts +117 -0
- package/src/orchestrator/path-resolver.ts +275 -0
- package/src/orchestrator/persistence.ts +2625 -0
- package/src/orchestrator/process-registry.ts +452 -0
- package/src/orchestrator/quality-gate.ts +1085 -0
- package/src/orchestrator/resume.ts +3488 -0
- package/src/orchestrator/sessions.ts +57 -0
- package/src/orchestrator/settings-loader.ts +136 -0
- package/src/orchestrator/settings-tui.ts +2208 -0
- package/src/orchestrator/sidecar-telemetry.ts +267 -0
- package/src/orchestrator/supervisor.ts +4548 -0
- package/src/orchestrator/task-executor-core.ts +675 -0
- package/src/orchestrator/tmux-compat.ts +37 -0
- package/src/orchestrator/tool-allowlist-constants.ts +37 -0
- package/src/orchestrator/types.ts +4465 -0
- package/src/orchestrator/verification.ts +547 -0
- package/src/orchestrator/waves.ts +1564 -0
- package/src/orchestrator/workspace.ts +707 -0
- package/src/orchestrator/worktree.ts +2725 -0
- package/src/ralph/index.ts +825 -0
- package/src/subagents/agents/agent-management.ts +648 -0
- package/src/subagents/agents/agent-scope.ts +6 -0
- package/src/subagents/agents/agent-selection.ts +23 -0
- package/src/subagents/agents/agent-serializer.ts +86 -0
- package/src/subagents/agents/agents.ts +832 -0
- package/src/subagents/agents/chain-serializer.ts +137 -0
- package/src/subagents/agents/frontmatter.ts +29 -0
- package/src/subagents/agents/identity.ts +30 -0
- package/src/subagents/agents/skills.ts +632 -0
- package/src/subagents/extension/config.ts +16 -0
- package/src/subagents/extension/control-notices.ts +92 -0
- package/src/subagents/extension/doctor.ts +199 -0
- package/src/subagents/extension/fanout-child.ts +170 -0
- package/src/subagents/extension/index.ts +573 -0
- package/src/subagents/extension/schemas.ts +168 -0
- package/src/subagents/intercom/intercom-bridge.ts +379 -0
- package/src/subagents/intercom/result-intercom.ts +377 -0
- package/src/subagents/runs/background/async-execution.ts +712 -0
- package/src/subagents/runs/background/async-job-tracker.ts +310 -0
- package/src/subagents/runs/background/async-resume.ts +345 -0
- package/src/subagents/runs/background/async-status.ts +325 -0
- package/src/subagents/runs/background/completion-dedupe.ts +63 -0
- package/src/subagents/runs/background/notify.ts +108 -0
- package/src/subagents/runs/background/parallel-groups.ts +45 -0
- package/src/subagents/runs/background/result-watcher.ts +307 -0
- package/src/subagents/runs/background/run-id-resolver.ts +83 -0
- package/src/subagents/runs/background/run-status.ts +269 -0
- package/src/subagents/runs/background/stale-run-reconciler.ts +336 -0
- package/src/subagents/runs/background/subagent-runner.ts +1808 -0
- package/src/subagents/runs/background/top-level-async.ts +13 -0
- package/src/subagents/runs/foreground/chain-clarify.ts +1333 -0
- package/src/subagents/runs/foreground/chain-execution.ts +938 -0
- package/src/subagents/runs/foreground/execution.ts +918 -0
- package/src/subagents/runs/foreground/subagent-executor.ts +2527 -0
- package/src/subagents/runs/shared/completion-guard.ts +147 -0
- package/src/subagents/runs/shared/long-running-guard.ts +175 -0
- package/src/subagents/runs/shared/mcp-direct-tool-allowlist.ts +365 -0
- package/src/subagents/runs/shared/model-fallback.ts +103 -0
- package/src/subagents/runs/shared/nested-events.ts +819 -0
- package/src/subagents/runs/shared/nested-path.ts +52 -0
- package/src/subagents/runs/shared/nested-render.ts +115 -0
- package/src/subagents/runs/shared/parallel-utils.ts +109 -0
- package/src/subagents/runs/shared/pi-args.ts +220 -0
- package/src/subagents/runs/shared/pi-spawn.ts +115 -0
- package/src/subagents/runs/shared/run-history.ts +60 -0
- package/src/subagents/runs/shared/single-output.ts +164 -0
- package/src/subagents/runs/shared/subagent-control.ts +226 -0
- package/src/subagents/runs/shared/subagent-prompt-runtime.ts +170 -0
- package/src/subagents/runs/shared/worktree.ts +577 -0
- package/src/subagents/shared/artifacts.ts +98 -0
- package/src/subagents/shared/atomic-json.ts +16 -0
- package/src/subagents/shared/file-coalescer.ts +40 -0
- package/src/subagents/shared/fork-context.ts +76 -0
- package/src/subagents/shared/formatters.ts +133 -0
- package/src/subagents/shared/jsonl-writer.ts +81 -0
- package/src/subagents/shared/model-info.ts +78 -0
- package/src/subagents/shared/post-exit-stdio-guard.ts +85 -0
- package/src/subagents/shared/session-identity.ts +10 -0
- package/src/subagents/shared/session-tokens.ts +44 -0
- package/src/subagents/shared/settings.ts +397 -0
- package/src/subagents/shared/status-format.ts +49 -0
- package/src/subagents/shared/types.ts +822 -0
- package/src/subagents/shared/utils.ts +450 -0
- package/src/subagents/slash/prompt-template-bridge.ts +397 -0
- package/src/subagents/slash/slash-bridge.ts +174 -0
- package/src/subagents/slash/slash-commands.ts +528 -0
- package/src/subagents/slash/slash-live-state.ts +292 -0
- package/src/subagents/tui/render-helpers.ts +80 -0
- package/src/subagents/tui/render.ts +1358 -0
- package/templates/agents/local/supervisor.md +33 -0
- package/templates/agents/local/task-merger.md +27 -0
- package/templates/agents/local/task-reviewer.md +30 -0
- package/templates/agents/local/task-worker.md +34 -0
- package/templates/agents/supervisor-routing.md +92 -0
- package/templates/agents/supervisor.md +229 -0
- package/templates/agents/task-merger.md +214 -0
- package/templates/agents/task-reviewer.md +260 -0
- package/templates/agents/task-worker-segment.md +44 -0
- package/templates/agents/task-worker.md +557 -0
- package/templates/tasks/CONTEXT.md +30 -0
- package/templates/tasks/EXAMPLE-001-hello-world/PROMPT.md +98 -0
- package/templates/tasks/EXAMPLE-001-hello-world/STATUS.md +73 -0
- package/templates/tasks/EXAMPLE-002-parallel-smoke/PROMPT.md +97 -0
- package/templates/tasks/EXAMPLE-002-parallel-smoke/STATUS.md +73 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine Child Process Entry Point (TP-071)
|
|
3
|
+
*
|
|
4
|
+
* This module serves two purposes:
|
|
5
|
+
* 1. Exports types and helpers used by extension.ts (main thread)
|
|
6
|
+
* 2. When forked as a child process, runs the engine in a separate Node.js process
|
|
7
|
+
*
|
|
8
|
+
* Uses child_process.fork() instead of worker_threads because Node v25's
|
|
9
|
+
* default --experimental-strip-types rejects .ts files inside node_modules.
|
|
10
|
+
* Fork creates a new process where --experimental-transform-types takes effect.
|
|
11
|
+
*
|
|
12
|
+
* Communication:
|
|
13
|
+
* - Child → Parent: process.send() for notify, monitor-update, engine-event, state-sync, complete, error
|
|
14
|
+
* - Parent → Child: child.send() for init, pause, resume, abort
|
|
15
|
+
*
|
|
16
|
+
* @module orch/engine-worker
|
|
17
|
+
*/
|
|
18
|
+
import type {
|
|
19
|
+
AllocatedLane,
|
|
20
|
+
EngineEvent,
|
|
21
|
+
MonitorState,
|
|
22
|
+
OrchBatchPhase,
|
|
23
|
+
OrchBatchRuntimeState,
|
|
24
|
+
OrchestratorConfig,
|
|
25
|
+
SupervisorAlert,
|
|
26
|
+
TaskRunnerConfig,
|
|
27
|
+
WorkspaceConfig,
|
|
28
|
+
WorkspaceRepoConfig,
|
|
29
|
+
} from "./types.ts";
|
|
30
|
+
|
|
31
|
+
// ── Types for worker <-> main thread messages ────────────────────────
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Messages sent FROM the worker TO the main thread.
|
|
35
|
+
*/
|
|
36
|
+
export type WorkerErrorSource = "enginePromise" | "uncaughtException" | "unhandledRejection";
|
|
37
|
+
|
|
38
|
+
export type WorkerToMainMessage =
|
|
39
|
+
| { type: "notify"; msg: string; level: "info" | "warning" | "error" }
|
|
40
|
+
| { type: "monitor-update"; state: MonitorState }
|
|
41
|
+
| { type: "engine-event"; event: EngineEvent }
|
|
42
|
+
| { type: "supervisor-alert"; alert: SupervisorAlert }
|
|
43
|
+
/**
|
|
44
|
+
* TP-187 (#538): Lane has reached a terminal state. The supervisor process
|
|
45
|
+
* uses this to mark the lane terminated and filter any subsequently-arriving
|
|
46
|
+
* (zombie) alerts whose `context.laneNumber`/`context.agentId` matches.
|
|
47
|
+
*/
|
|
48
|
+
| { type: "lane-terminated"; info: import("./types.ts").LaneTerminatedInfo }
|
|
49
|
+
/**
|
|
50
|
+
* TP-187 (#538): Lane number has been re-allocated to a fresh task. The
|
|
51
|
+
* supervisor lifts the suppression so subsequent alerts pass through.
|
|
52
|
+
*/
|
|
53
|
+
| { type: "lane-respawned"; laneNumber: number; agentId: string; batchId: string }
|
|
54
|
+
| { type: "state-sync"; state: SerializedBatchState }
|
|
55
|
+
| { type: "complete"; state: SerializedBatchState }
|
|
56
|
+
| { type: "error"; message: string; stack?: string; source?: WorkerErrorSource };
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Messages sent FROM the main thread TO the worker.
|
|
60
|
+
*/
|
|
61
|
+
export type WorkerInMessage = { type: "pause" } | { type: "resume" } | { type: "abort" };
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Serializable form of OrchBatchRuntimeState fields synced to main thread.
|
|
65
|
+
* Only includes fields the main thread needs for display/state tracking.
|
|
66
|
+
*/
|
|
67
|
+
export interface SerializedBatchState {
|
|
68
|
+
phase: OrchBatchPhase;
|
|
69
|
+
batchId: string;
|
|
70
|
+
baseBranch: string;
|
|
71
|
+
orchBranch: string;
|
|
72
|
+
mode: string;
|
|
73
|
+
currentWaveIndex: number;
|
|
74
|
+
totalWaves: number;
|
|
75
|
+
totalTasks: number;
|
|
76
|
+
succeededTasks: number;
|
|
77
|
+
failedTasks: number;
|
|
78
|
+
skippedTasks: number;
|
|
79
|
+
blockedTasks: number;
|
|
80
|
+
startedAt: number;
|
|
81
|
+
endedAt: number | null;
|
|
82
|
+
errors: string[];
|
|
83
|
+
/** Active lanes for the current wave (synced so /orch-sessions works). */
|
|
84
|
+
currentLanes: AllocatedLane[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Serializable form of WorkspaceConfig (Map → array of entries).
|
|
89
|
+
*/
|
|
90
|
+
export interface SerializedWorkspaceConfig {
|
|
91
|
+
mode: string;
|
|
92
|
+
repos: Array<[string, WorkspaceRepoConfig]>;
|
|
93
|
+
routing: WorkspaceConfig["routing"];
|
|
94
|
+
configPath: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* workerData shape passed from the main thread.
|
|
99
|
+
*/
|
|
100
|
+
export interface EngineWorkerData {
|
|
101
|
+
/** Sentinel flag — distinguishes engine worker from test-runner worker threads */
|
|
102
|
+
engineWorker: true;
|
|
103
|
+
/** "execute" for new batch, "resume" for resume */
|
|
104
|
+
mode: "execute" | "resume";
|
|
105
|
+
/** User arguments (target string) — only for "execute" mode */
|
|
106
|
+
args?: string;
|
|
107
|
+
/** Orchestrator configuration */
|
|
108
|
+
orchConfig: OrchestratorConfig;
|
|
109
|
+
/** Task runner configuration */
|
|
110
|
+
runnerConfig: TaskRunnerConfig;
|
|
111
|
+
/** Repository root (cwd) */
|
|
112
|
+
cwd: string;
|
|
113
|
+
/** Workspace configuration (serialized) — null for repo mode */
|
|
114
|
+
workspaceConfig?: SerializedWorkspaceConfig | null;
|
|
115
|
+
/** Workspace root directory */
|
|
116
|
+
workspaceRoot?: string;
|
|
117
|
+
/** Agent root directory */
|
|
118
|
+
agentRoot?: string;
|
|
119
|
+
/** Force flag for resume */
|
|
120
|
+
force?: boolean;
|
|
121
|
+
/** Supervisor autonomy mode propagated to worker bridge tools. */
|
|
122
|
+
supervisorAutonomy?: "interactive" | "supervised" | "autonomous";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Serialization helpers (used by both main thread and worker) ──────
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Serialize WorkspaceConfig for cross-thread transfer.
|
|
129
|
+
* Converts the Map to an array of entries.
|
|
130
|
+
*/
|
|
131
|
+
export function serializeWorkspaceConfig(
|
|
132
|
+
config: WorkspaceConfig | null | undefined,
|
|
133
|
+
): SerializedWorkspaceConfig | null {
|
|
134
|
+
if (!config) return null;
|
|
135
|
+
return {
|
|
136
|
+
mode: config.mode,
|
|
137
|
+
repos: [...config.repos.entries()],
|
|
138
|
+
routing: config.routing,
|
|
139
|
+
configPath: config.configPath,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Reconstruct WorkspaceConfig from serialized form.
|
|
145
|
+
*/
|
|
146
|
+
export function deserializeWorkspaceConfig(
|
|
147
|
+
serialized: SerializedWorkspaceConfig | null | undefined,
|
|
148
|
+
): WorkspaceConfig | null {
|
|
149
|
+
if (!serialized) return null;
|
|
150
|
+
return {
|
|
151
|
+
mode: serialized.mode as WorkspaceConfig["mode"],
|
|
152
|
+
repos: new Map(serialized.repos),
|
|
153
|
+
routing: serialized.routing,
|
|
154
|
+
configPath: serialized.configPath,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Extract serializable batch state for sync back to main thread.
|
|
160
|
+
*/
|
|
161
|
+
function serializeBatchState(state: OrchBatchRuntimeState): SerializedBatchState {
|
|
162
|
+
return {
|
|
163
|
+
phase: state.phase,
|
|
164
|
+
batchId: state.batchId,
|
|
165
|
+
baseBranch: state.baseBranch,
|
|
166
|
+
orchBranch: state.orchBranch,
|
|
167
|
+
mode: state.mode,
|
|
168
|
+
currentWaveIndex: state.currentWaveIndex,
|
|
169
|
+
totalWaves: state.totalWaves,
|
|
170
|
+
totalTasks: state.totalTasks,
|
|
171
|
+
succeededTasks: state.succeededTasks,
|
|
172
|
+
failedTasks: state.failedTasks,
|
|
173
|
+
skippedTasks: state.skippedTasks,
|
|
174
|
+
blockedTasks: state.blockedTasks,
|
|
175
|
+
startedAt: state.startedAt,
|
|
176
|
+
endedAt: state.endedAt,
|
|
177
|
+
errors: [...state.errors],
|
|
178
|
+
currentLanes: state.currentLanes,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Apply serialized batch state from worker to main-thread batch state.
|
|
184
|
+
*
|
|
185
|
+
* Updates only the fields that the worker thread tracks — preserves
|
|
186
|
+
* main-thread-only fields like pauseSignal, dependencyGraph, etc.
|
|
187
|
+
*/
|
|
188
|
+
export function applySerializedState(
|
|
189
|
+
batchState: OrchBatchRuntimeState,
|
|
190
|
+
serialized: SerializedBatchState,
|
|
191
|
+
): void {
|
|
192
|
+
batchState.phase = serialized.phase;
|
|
193
|
+
batchState.batchId = serialized.batchId;
|
|
194
|
+
batchState.baseBranch = serialized.baseBranch;
|
|
195
|
+
batchState.orchBranch = serialized.orchBranch;
|
|
196
|
+
batchState.mode = serialized.mode as OrchBatchRuntimeState["mode"];
|
|
197
|
+
batchState.currentWaveIndex = serialized.currentWaveIndex;
|
|
198
|
+
batchState.totalWaves = serialized.totalWaves;
|
|
199
|
+
batchState.totalTasks = serialized.totalTasks;
|
|
200
|
+
batchState.succeededTasks = serialized.succeededTasks;
|
|
201
|
+
batchState.failedTasks = serialized.failedTasks;
|
|
202
|
+
batchState.skippedTasks = serialized.skippedTasks;
|
|
203
|
+
batchState.blockedTasks = serialized.blockedTasks;
|
|
204
|
+
batchState.startedAt = serialized.startedAt;
|
|
205
|
+
batchState.endedAt = serialized.endedAt;
|
|
206
|
+
batchState.errors = [...serialized.errors];
|
|
207
|
+
batchState.currentLanes = serialized.currentLanes ?? [];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ── Engine main (runs when launched as a forked child process) ───────
|
|
211
|
+
|
|
212
|
+
// Guard: only run engine main when launched via fork() with the sentinel env var.
|
|
213
|
+
if (process.env.TASKPLANE_ENGINE_FORK === "1" && typeof process.send === "function") {
|
|
214
|
+
const send = (msg: WorkerToMainMessage) => {
|
|
215
|
+
try {
|
|
216
|
+
process.send?.(msg);
|
|
217
|
+
} catch {
|
|
218
|
+
// best effort only
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const sendWithAck = (msg: WorkerToMainMessage, onFlushed: () => void) => {
|
|
223
|
+
if (typeof process.send !== "function" || !process.connected) {
|
|
224
|
+
onFlushed();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let flushed = false;
|
|
229
|
+
const done = () => {
|
|
230
|
+
if (flushed) return;
|
|
231
|
+
flushed = true;
|
|
232
|
+
onFlushed();
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
(
|
|
237
|
+
process.send as (
|
|
238
|
+
message: WorkerToMainMessage,
|
|
239
|
+
sendHandle?: unknown,
|
|
240
|
+
options?: unknown,
|
|
241
|
+
callback?: (error: Error | null) => void,
|
|
242
|
+
) => boolean
|
|
243
|
+
)(msg, undefined, undefined, () => done());
|
|
244
|
+
setTimeout(done, 75).unref();
|
|
245
|
+
} catch {
|
|
246
|
+
done();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const normalizeError = (err: unknown): { message: string; stack?: string } => {
|
|
251
|
+
if (err instanceof Error) return { message: err.message, stack: err.stack };
|
|
252
|
+
return { message: String(err) };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Wait for the init message carrying workerData, then start the engine.
|
|
256
|
+
process.once("message", async (initMsg: { type: string; data: EngineWorkerData }) => {
|
|
257
|
+
if (initMsg?.type !== "init") return;
|
|
258
|
+
|
|
259
|
+
let batchState: OrchBatchRuntimeState | null = null;
|
|
260
|
+
let fatalHandled = false;
|
|
261
|
+
const reportFatalAndExit = (source: WorkerErrorSource, err: unknown) => {
|
|
262
|
+
if (fatalHandled) return;
|
|
263
|
+
fatalHandled = true;
|
|
264
|
+
|
|
265
|
+
const normalized = normalizeError(err);
|
|
266
|
+
if (batchState && batchState.phase !== "completed" && batchState.phase !== "failed") {
|
|
267
|
+
batchState.phase = "failed";
|
|
268
|
+
batchState.endedAt = Date.now();
|
|
269
|
+
batchState.errors.push(`[${source}] ${normalized.message}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (batchState) send({ type: "state-sync", state: serializeBatchState(batchState) });
|
|
273
|
+
sendWithAck(
|
|
274
|
+
{ type: "error", source, message: normalized.message, stack: normalized.stack },
|
|
275
|
+
() => process.exit(1),
|
|
276
|
+
);
|
|
277
|
+
setTimeout(() => process.exit(1), 200).unref();
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
process.once("uncaughtException", (err: unknown) => reportFatalAndExit("uncaughtException", err));
|
|
281
|
+
process.once("unhandledRejection", (reason: unknown) =>
|
|
282
|
+
reportFatalAndExit("unhandledRejection", reason),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
// Dynamic imports — only loaded in engine context to avoid circular
|
|
286
|
+
// dependencies when this module is imported from extension.ts
|
|
287
|
+
const { executeOrchBatch } = await import("./engine.ts");
|
|
288
|
+
const { resumeOrchBatch } = await import("./resume.ts");
|
|
289
|
+
const { freshOrchBatchState } = await import("./types.ts");
|
|
290
|
+
|
|
291
|
+
const data = initMsg.data;
|
|
292
|
+
|
|
293
|
+
// Create a fresh batch state for this process
|
|
294
|
+
batchState = freshOrchBatchState();
|
|
295
|
+
batchState.phase = "launching";
|
|
296
|
+
batchState.startedAt = Date.now();
|
|
297
|
+
|
|
298
|
+
// Deserialize workspace config
|
|
299
|
+
const wsConfig = deserializeWorkspaceConfig(data.workspaceConfig);
|
|
300
|
+
|
|
301
|
+
// ── Control signal listener ──────────────────────────────────
|
|
302
|
+
// Main process sends pause/resume/abort signals via IPC.
|
|
303
|
+
// We apply them to the in-process batchState.pauseSignal.
|
|
304
|
+
process.on("message", (msg: WorkerInMessage) => {
|
|
305
|
+
if (!batchState) return;
|
|
306
|
+
switch (msg.type) {
|
|
307
|
+
case "pause":
|
|
308
|
+
batchState.pauseSignal.paused = true;
|
|
309
|
+
break;
|
|
310
|
+
case "resume":
|
|
311
|
+
batchState.pauseSignal.paused = false;
|
|
312
|
+
break;
|
|
313
|
+
case "abort":
|
|
314
|
+
batchState.pauseSignal.paused = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// ── Callback factories (replace ctx-dependent callbacks) ─────
|
|
320
|
+
const onNotify = (message: string, level: "info" | "warning" | "error") => {
|
|
321
|
+
send({ type: "notify", msg: message, level });
|
|
322
|
+
if (!batchState) return;
|
|
323
|
+
// Sync batch state on every notify (lightweight — just the summary fields)
|
|
324
|
+
send({ type: "state-sync", state: serializeBatchState(batchState) });
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const onMonitorUpdate = (state: MonitorState) => {
|
|
328
|
+
send({ type: "monitor-update", state });
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const onEngineEvent = (event: EngineEvent) => {
|
|
332
|
+
send({ type: "engine-event", event });
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// TP-076: Supervisor alert callback — sends structured alerts to main thread
|
|
336
|
+
const onSupervisorAlert = (alert: import("./types.ts").SupervisorAlert) => {
|
|
337
|
+
send({ type: "supervisor-alert", alert });
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// TP-187 (#538): Lane termination callback — forwards lane-terminated to
|
|
341
|
+
// the supervisor process so it can suppress in-flight zombie alerts.
|
|
342
|
+
const onLaneTerminated = (info: import("./types.ts").LaneTerminatedInfo) => {
|
|
343
|
+
send({ type: "lane-terminated", info });
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// TP-187 (#538): Lane respawn callback — forwards lane-respawned to
|
|
347
|
+
// the supervisor process so it can lift suppression for re-allocated lanes.
|
|
348
|
+
const onLaneRespawned = (laneNumber: number, agentId: string, batchId: string) => {
|
|
349
|
+
send({ type: "lane-respawned", laneNumber, agentId, batchId });
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// ── Execute engine ───────────────────────────────────────────
|
|
353
|
+
const enginePromise =
|
|
354
|
+
data.mode === "resume"
|
|
355
|
+
? resumeOrchBatch(
|
|
356
|
+
data.orchConfig,
|
|
357
|
+
data.runnerConfig,
|
|
358
|
+
data.cwd,
|
|
359
|
+
batchState,
|
|
360
|
+
onNotify,
|
|
361
|
+
onMonitorUpdate,
|
|
362
|
+
wsConfig,
|
|
363
|
+
data.workspaceRoot,
|
|
364
|
+
data.agentRoot,
|
|
365
|
+
data.force ?? false,
|
|
366
|
+
onSupervisorAlert,
|
|
367
|
+
data.supervisorAutonomy ?? "autonomous",
|
|
368
|
+
onLaneTerminated,
|
|
369
|
+
onLaneRespawned,
|
|
370
|
+
)
|
|
371
|
+
: executeOrchBatch(
|
|
372
|
+
data.args ?? "",
|
|
373
|
+
data.orchConfig,
|
|
374
|
+
data.runnerConfig,
|
|
375
|
+
data.cwd,
|
|
376
|
+
batchState,
|
|
377
|
+
onNotify,
|
|
378
|
+
onMonitorUpdate,
|
|
379
|
+
wsConfig,
|
|
380
|
+
data.workspaceRoot,
|
|
381
|
+
data.agentRoot,
|
|
382
|
+
onEngineEvent,
|
|
383
|
+
onSupervisorAlert,
|
|
384
|
+
data.supervisorAutonomy ?? "autonomous",
|
|
385
|
+
onLaneTerminated,
|
|
386
|
+
onLaneRespawned,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
enginePromise
|
|
390
|
+
.then(() => {
|
|
391
|
+
// Final state sync + completion signal
|
|
392
|
+
const finalState = serializeBatchState(batchState);
|
|
393
|
+
send({ type: "complete", state: finalState });
|
|
394
|
+
// Disconnect IPC so the child process can exit cleanly
|
|
395
|
+
process.disconnect?.();
|
|
396
|
+
})
|
|
397
|
+
.catch((err: unknown) => {
|
|
398
|
+
const normalized = normalizeError(err);
|
|
399
|
+
// Ensure batch state reflects the failure
|
|
400
|
+
if (batchState.phase !== "completed" && batchState.phase !== "failed") {
|
|
401
|
+
batchState.phase = "failed";
|
|
402
|
+
batchState.endedAt = Date.now();
|
|
403
|
+
batchState.errors.push(`Unhandled engine error: ${normalized.message}`);
|
|
404
|
+
}
|
|
405
|
+
send({ type: "state-sync", state: serializeBatchState(batchState) });
|
|
406
|
+
send({
|
|
407
|
+
type: "error",
|
|
408
|
+
source: "enginePromise",
|
|
409
|
+
message: normalized.message,
|
|
410
|
+
stack: normalized.stack,
|
|
411
|
+
});
|
|
412
|
+
process.disconnect?.();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
}
|