@gh-symphony/cli 0.0.20 → 0.0.21

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.
@@ -1,448 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ getCodexObservabilityEventName,
4
+ launchCodexAppServer,
5
+ loadLauncherEnvironment,
6
+ normalizeCodexRuntimeEvents,
7
+ prepareCodexRuntimePlan,
8
+ resolveLocalRuntimeLaunchConfig
9
+ } from "./chunk-A67CMOYE.js";
10
+ import {
11
+ DEFAULT_AGENT_INPUT_REQUIRED_REASON,
3
12
  classifySessionExit,
13
+ formatClaudePreflightText,
14
+ isClaudeRuntimeCommand,
4
15
  parseWorkflowMarkdown,
5
- readEnvFile
6
- } from "./chunk-M3IFVLQS.js";
7
-
8
- // ../worker/dist/index.js
9
- import { spawn as spawn2 } from "child_process";
10
- import { readFile as readFile2 } from "fs/promises";
11
- import { join as join3 } from "path";
16
+ resolveClaudeCommandBinary,
17
+ resolveWorkflowRuntimeCommand,
18
+ runClaudePreflight
19
+ } from "./chunk-QEONJ5DZ.js";
12
20
 
13
- // ../runtime-codex/dist/runtime.js
21
+ // ../worker/src/index.ts
14
22
  import { spawn } from "child_process";
15
- import { copyFile, mkdir, writeFile } from "fs/promises";
16
- import { join } from "path";
17
- import { homedir } from "os";
18
- import { fileURLToPath } from "url";
19
- var DEFAULT_GITHUB_GRAPHQL_API_URL = "https://api.github.com/graphql";
20
- var DEFAULT_GITHUB_GIT_HOST = "github.com";
21
- var DEFAULT_GITHUB_GIT_USERNAME = "x-access-token";
22
- var AgentRuntimeResolutionError = class extends Error {
23
- };
24
- function createGitHubGraphQLToolDefinition(config) {
25
- return {
26
- name: "github_graphql",
27
- description: "Execute GitHub GraphQL queries for the active workspace so the agent can mutate project and issue state directly.",
28
- command: "node",
29
- args: [fileURLToPath(new URL("./github-graphql-mcp-server.js", import.meta.url))],
30
- env: {
31
- GITHUB_GRAPHQL_API_URL: config.githubGraphqlApiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL,
32
- ...config.githubToken ? {
33
- GITHUB_GRAPHQL_TOKEN: config.githubToken
34
- } : {},
35
- ...config.githubTokenBrokerUrl ? {
36
- GITHUB_TOKEN_BROKER_URL: config.githubTokenBrokerUrl
37
- } : {},
38
- ...config.githubTokenBrokerSecret ? {
39
- GITHUB_TOKEN_BROKER_SECRET: config.githubTokenBrokerSecret
40
- } : {},
41
- ...config.githubTokenCachePath ? {
42
- GITHUB_TOKEN_CACHE_PATH: config.githubTokenCachePath
43
- } : {},
44
- ...config.githubProjectId ? {
45
- GITHUB_PROJECT_ID: config.githubProjectId
46
- } : {}
47
- },
48
- inputSchema: {
49
- type: "object",
50
- properties: {
51
- query: {
52
- type: "string",
53
- description: "GraphQL query or mutation document."
54
- },
55
- variables: {
56
- type: "object",
57
- description: "Variables for the GraphQL document."
58
- },
59
- operationName: {
60
- type: "string",
61
- description: "Optional GraphQL operation name."
62
- }
63
- },
64
- required: ["query"],
65
- additionalProperties: false
66
- }
67
- };
68
- }
69
- function buildCodexRuntimePlan(config) {
70
- const tool = createGitHubGraphQLToolDefinition(config);
71
- const gitCredentialHelper = createGitCredentialHelperEnvironment(config);
72
- const shellCmd = (() => {
73
- const cmd = config.agentCommand ?? "codex app-server";
74
- return cmd.startsWith("bash -lc ") ? cmd.slice("bash -lc ".length) : cmd;
75
- })();
76
- return {
77
- cwd: config.workingDirectory,
78
- command: "bash",
79
- args: ["-lc", shellCmd],
80
- env: {
81
- ...process.env,
82
- ...config.extraEnv,
83
- ...config.agentEnv,
84
- CODEX_PROJECT_ID: config.projectId,
85
- GITHUB_PROJECT_ID: config.githubProjectId ?? "",
86
- GITHUB_GRAPHQL_TOOL_NAME: tool.name,
87
- GITHUB_GRAPHQL_TOOL_COMMAND: [tool.command, ...tool.args].join(" "),
88
- // Point codex to an isolated config dir so personal MCPs (playwright,
89
- // chrome-devtools, context7, etc.) from the operator's ~/.codex/config.toml
90
- // are not loaded and do not confuse the implementation agent.
91
- CODEX_HOME: join(config.workingDirectory, ".codex-agent"),
92
- ...gitCredentialHelper,
93
- ...tool.env
94
- },
95
- tools: [tool]
96
- };
97
- }
98
- function launchCodexAppServer(plan, spawnImpl = spawn) {
99
- return spawnImpl(plan.command, plan.args, {
100
- cwd: plan.cwd,
101
- env: plan.env,
102
- stdio: "pipe"
103
- });
104
- }
105
- async function prepareCodexRuntimePlan(config, dependencies = {}) {
106
- const agentEnv = await resolveAgentRuntimeEnvironment(config, dependencies);
107
- const codexHomeDir = join(config.workingDirectory, ".codex-agent");
108
- const mkdirImpl = dependencies.mkdirImpl ?? mkdir;
109
- await mkdirImpl(codexHomeDir, { recursive: true });
110
- const writeFileImpl = dependencies.writeFileImpl ?? writeFile;
111
- await writeFileImpl(join(codexHomeDir, "config.toml"), "# Isolated agent config \u2014 no personal MCP servers\n", "utf8");
112
- const realCodexHome = process.env.CODEX_HOME ?? join(homedir(), ".codex");
113
- const copyFileImpl = dependencies.copyFileImpl ?? copyFile;
114
- try {
115
- await copyFileImpl(join(realCodexHome, "auth.json"), join(codexHomeDir, "auth.json"));
116
- } catch {
117
- }
118
- return buildCodexRuntimePlan({
119
- ...config,
120
- agentEnv
121
- });
122
- }
123
- function createGitCredentialHelperEnvironment(config) {
124
- return {
125
- GITHUB_GIT_HOST: DEFAULT_GITHUB_GIT_HOST,
126
- GITHUB_GIT_USERNAME: DEFAULT_GITHUB_GIT_USERNAME,
127
- GIT_TERMINAL_PROMPT: "0",
128
- GIT_CONFIG_COUNT: "1",
129
- GIT_CONFIG_KEY_0: "credential.helper",
130
- GIT_CONFIG_VALUE_0: `!node ${fileURLToPath(new URL("./git-credential-helper.js", import.meta.url))}`,
131
- ...config.githubToken ? {
132
- GITHUB_GRAPHQL_TOKEN: config.githubToken
133
- } : {},
134
- ...config.githubTokenBrokerUrl ? {
135
- GITHUB_TOKEN_BROKER_URL: config.githubTokenBrokerUrl
136
- } : {},
137
- ...config.githubTokenBrokerSecret ? {
138
- GITHUB_TOKEN_BROKER_SECRET: config.githubTokenBrokerSecret
139
- } : {},
140
- ...config.githubTokenCachePath ? {
141
- GITHUB_TOKEN_CACHE_PATH: config.githubTokenCachePath
142
- } : {}
143
- };
144
- }
145
- async function resolveAgentRuntimeEnvironment(config, dependencies = {}) {
146
- if (config.agentEnv) {
147
- return config.agentEnv;
148
- }
149
- if (!config.agentCredentialBrokerUrl || !config.agentCredentialBrokerSecret) {
150
- return {};
151
- }
152
- const fetchImpl = dependencies.fetchImpl ?? fetch;
153
- const response = await fetchImpl(config.agentCredentialBrokerUrl, {
154
- method: "POST",
155
- headers: {
156
- accept: "application/json",
157
- authorization: `Bearer ${config.agentCredentialBrokerSecret}`
158
- }
159
- });
160
- const payload = await response.json();
161
- if (!response.ok || !payload.env || Object.keys(payload.env).length === 0) {
162
- throw new AgentRuntimeResolutionError(payload.error ?? `Agent credential broker request failed with status ${response.status}.`);
163
- }
164
- if (config.agentCredentialCachePath) {
165
- const writeFileImpl = dependencies.writeFileImpl ?? writeFile;
166
- await writeFileImpl(config.agentCredentialCachePath, JSON.stringify(payload), "utf8");
167
- }
168
- return payload.env;
169
- }
170
-
171
- // ../runtime-codex/dist/launcher.js
172
- import { dirname, resolve } from "path";
173
- import { fileURLToPath as fileURLToPath2 } from "url";
174
- var LocalRuntimeLauncherError = class extends Error {
175
- };
176
- function resolveLocalRuntimeLaunchConfig(env = process.env) {
177
- const projectId = env.PROJECT_ID ?? env.CODEX_PROJECT_ID;
178
- const workingDirectory = env.WORKING_DIRECTORY;
179
- if (!projectId) {
180
- throw new LocalRuntimeLauncherError("PROJECT_ID or CODEX_PROJECT_ID is required.");
181
- }
182
- if (!workingDirectory) {
183
- throw new LocalRuntimeLauncherError("WORKING_DIRECTORY is required.");
184
- }
185
- return {
186
- projectId,
187
- workingDirectory,
188
- githubToken: env.GITHUB_GRAPHQL_TOKEN,
189
- githubTokenBrokerUrl: env.GITHUB_TOKEN_BROKER_URL,
190
- githubTokenBrokerSecret: env.GITHUB_TOKEN_BROKER_SECRET,
191
- githubTokenCachePath: env.GITHUB_TOKEN_CACHE_PATH,
192
- agentEnv: readDirectAgentEnvironment(env),
193
- agentCredentialBrokerUrl: env.AGENT_CREDENTIAL_BROKER_URL,
194
- agentCredentialBrokerSecret: env.AGENT_CREDENTIAL_BROKER_SECRET,
195
- agentCredentialCachePath: env.AGENT_CREDENTIAL_CACHE_PATH,
196
- githubProjectId: env.GITHUB_PROJECT_ID,
197
- githubGraphqlApiUrl: env.GITHUB_GRAPHQL_API_URL,
198
- agentCommand: env.SYMPHONY_AGENT_COMMAND
199
- };
200
- }
201
- async function runLocalRuntimeLauncher(env = process.env) {
202
- const launcherEnv2 = loadLauncherEnvironment(env);
203
- const config = resolveLocalRuntimeLaunchConfig(launcherEnv2);
204
- const plan = await prepareCodexRuntimePlan(config);
205
- emitLaunchSummary(config);
206
- const child = launchCodexAppServer(plan);
207
- process.stdout.write(`[worker] codex app-server started (pid: ${child.pid ?? "unknown"})
208
- `);
209
- child.stdout?.pipe(process.stdout);
210
- child.stderr?.pipe(process.stderr);
211
- return await waitForChildProcess(child);
212
- }
213
- function loadLauncherEnvironment(env = process.env, cwd = process.cwd()) {
214
- const mergedEnv = {
215
- ...readEnvFile(resolve(dirname(fileURLToPath2(import.meta.url)), "..", ".env")),
216
- ...readEnvFile(resolve(cwd, ".env")),
217
- ...env
218
- };
219
- return mergedEnv;
220
- }
221
- function readDirectAgentEnvironment(env) {
222
- const agentEnv = {};
223
- for (const key of [
224
- "OPENAI_API_KEY",
225
- "OPENAI_BASE_URL",
226
- "OPENAI_ORG_ID",
227
- "OPENAI_PROJECT"
228
- ]) {
229
- const value = env[key];
230
- if (value) {
231
- agentEnv[key] = value;
232
- }
233
- }
234
- return Object.keys(agentEnv).length ? agentEnv : void 0;
235
- }
236
- function waitForChildProcess(child) {
237
- return new Promise((resolve2, reject) => {
238
- child.once("error", reject);
239
- child.once("exit", (code, signal) => {
240
- if (signal) {
241
- reject(new LocalRuntimeLauncherError(`codex app-server exited on ${signal}.`));
242
- return;
243
- }
244
- resolve2(code ?? 0);
245
- });
246
- });
247
- }
248
- async function main() {
249
- const exitCode = await runLocalRuntimeLauncher(process.env);
250
- process.exitCode = exitCode;
251
- }
252
- if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
253
- main().catch((error) => {
254
- const message = error instanceof Error ? error.message : "Unknown error";
255
- process.stderr.write(`${message}
256
- `);
257
- process.exitCode = 1;
258
- });
259
- }
260
- function emitLaunchSummary(config) {
261
- const githubAuthMode = config.githubToken ? "direct token" : config.githubTokenBrokerUrl && config.githubTokenBrokerSecret ? "broker" : "missing";
262
- const agentAuthMode = config.agentEnv?.OPENAI_API_KEY ? "direct env" : config.agentCredentialBrokerUrl && config.agentCredentialBrokerSecret ? "broker" : "local codex auth or inherited environment";
263
- process.stdout.write([
264
- "[worker] starting local codex runtime",
265
- `[worker] project: ${config.projectId}`,
266
- `[worker] cwd: ${config.workingDirectory}`,
267
- `[worker] github project: ${config.githubProjectId ?? "(unset)"}`,
268
- `[worker] github auth: ${githubAuthMode}`,
269
- `[worker] agent auth: ${agentAuthMode}`,
270
- "[worker] note: codex app-server does not proactively read GitHub issues.",
271
- "[worker] note: it waits for a client request or tool invocation."
272
- ].join("\n") + "\n");
273
- }
274
-
275
- // ../runtime-codex/dist/github-graphql-tool.js
276
- import { readFile, writeFile as writeFile2 } from "fs/promises";
277
- var DEFAULT_GITHUB_GRAPHQL_API_URL2 = "https://api.github.com/graphql";
278
- var TOKEN_REUSE_WINDOW_MS = 60 * 1e3;
279
- async function executeGitHubGraphQL(invocation, config, fetchImpl = fetch) {
280
- const token = await resolveGitHubGraphQLToken(config, {
281
- fetchImpl
282
- });
283
- const response = await fetchImpl(config.apiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL2, {
284
- method: "POST",
285
- headers: {
286
- "content-type": "application/json",
287
- authorization: `Bearer ${token}`
288
- },
289
- body: JSON.stringify(invocation)
290
- });
291
- const payload = await response.json();
292
- if (!response.ok) {
293
- throw new Error(`GitHub GraphQL request failed with status ${response.status}: ${JSON.stringify(payload)}`);
294
- }
295
- if (payload.errors?.length) {
296
- throw new Error(payload.errors.map((error) => error.message).join("; "));
297
- }
298
- return payload;
299
- }
300
- async function resolveGitHubGraphQLToken(config, dependencies = {}) {
301
- if (config.token) {
302
- return config.token;
303
- }
304
- if (!config.tokenBrokerUrl || !config.tokenBrokerSecret) {
305
- throw new Error("Either GITHUB_GRAPHQL_TOKEN or the runtime token broker configuration is required.");
306
- }
307
- const now = dependencies.now ?? /* @__PURE__ */ new Date();
308
- const readFileImpl = dependencies.readFileImpl ?? readFile;
309
- const writeFileImpl = dependencies.writeFileImpl ?? writeFile2;
310
- const cachedToken = config.tokenCachePath ? await readCachedToken(config.tokenCachePath, readFileImpl) : null;
311
- if (cachedToken && cachedToken.expiresAt.getTime() - now.getTime() > TOKEN_REUSE_WINDOW_MS) {
312
- return cachedToken.token;
313
- }
314
- const fetchImpl = dependencies.fetchImpl ?? fetch;
315
- const response = await fetchImpl(config.tokenBrokerUrl, {
316
- method: "POST",
317
- headers: {
318
- accept: "application/json",
319
- authorization: `Bearer ${config.tokenBrokerSecret}`
320
- }
321
- });
322
- const payload = await response.json();
323
- if (!response.ok || !payload.token || !payload.expiresAt) {
324
- throw new Error(payload.error ?? `Runtime token broker request failed with status ${response.status}.`);
325
- }
326
- if (config.tokenCachePath) {
327
- await writeFileImpl(config.tokenCachePath, JSON.stringify(payload), "utf8");
328
- }
329
- return payload.token;
330
- }
331
- async function readStdin() {
332
- const chunks = [];
333
- for await (const chunk of process.stdin) {
334
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
335
- }
336
- return Buffer.concat(chunks).toString("utf8");
337
- }
338
- async function main2() {
339
- const rawInput = await readStdin();
340
- const invocation = JSON.parse(rawInput);
341
- const result = await executeGitHubGraphQL(invocation, {
342
- token: process.env.GITHUB_GRAPHQL_TOKEN,
343
- apiUrl: process.env.GITHUB_GRAPHQL_API_URL,
344
- tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
345
- tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
346
- tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH
347
- });
348
- process.stdout.write(`${JSON.stringify(result)}
349
- `);
350
- }
351
- if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
352
- main2().catch((error) => {
353
- const message = error instanceof Error ? error.message : "Unknown error";
354
- process.stderr.write(`${message}
355
- `);
356
- process.exitCode = 1;
357
- });
358
- }
359
- async function readCachedToken(path, readFileImpl) {
360
- try {
361
- const payload = JSON.parse(await readFileImpl(path, "utf8"));
362
- if (!payload.token || !payload.expiresAt) {
363
- return null;
364
- }
365
- return {
366
- token: payload.token,
367
- expiresAt: new Date(payload.expiresAt)
368
- };
369
- } catch {
370
- return null;
371
- }
372
- }
373
-
374
- // ../runtime-codex/dist/git-credential-helper.js
375
- var DEFAULT_GITHUB_GIT_HOST2 = "github.com";
376
- var DEFAULT_GITHUB_GIT_USERNAME2 = "x-access-token";
377
- async function resolveGitCredential(request, config, fetchImpl = fetch) {
378
- const requestHost = request.host?.trim();
379
- const requestProtocol = request.protocol?.trim();
380
- if (!requestHost || requestProtocol && requestProtocol !== "https") {
381
- return "";
382
- }
383
- const expectedHost = normalizeGitHost(config.gitHost ?? DEFAULT_GITHUB_GIT_HOST2);
384
- if (normalizeGitHost(requestHost) !== expectedHost) {
385
- return "";
386
- }
387
- const token = await resolveGitHubGraphQLToken(config, {
388
- fetchImpl
389
- });
390
- return formatGitCredentialResponse({
391
- protocol: requestProtocol || "https",
392
- host: requestHost,
393
- username: config.gitUsername ?? DEFAULT_GITHUB_GIT_USERNAME2,
394
- password: token
395
- });
396
- }
397
- function parseGitCredentialRequest(rawInput) {
398
- return rawInput.split("\n").map((line) => line.trim()).filter(Boolean).reduce((request, line) => {
399
- const separatorIndex = line.indexOf("=");
400
- if (separatorIndex === -1) {
401
- return request;
402
- }
403
- const key = line.slice(0, separatorIndex);
404
- const value = line.slice(separatorIndex + 1);
405
- request[key] = value;
406
- return request;
407
- }, {});
408
- }
409
- function formatGitCredentialResponse(value) {
410
- return `${Object.entries(value).map(([key, entry]) => `${key}=${entry}`).join("\n")}
411
-
412
- `;
413
- }
414
- async function readStdin2() {
415
- const chunks = [];
416
- for await (const chunk of process.stdin) {
417
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
418
- }
419
- return Buffer.concat(chunks).toString("utf8");
420
- }
421
- async function main3() {
422
- const request = parseGitCredentialRequest(await readStdin2());
423
- const response = await resolveGitCredential(request, {
424
- token: process.env.GITHUB_GRAPHQL_TOKEN,
425
- tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
426
- tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
427
- tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH,
428
- gitHost: process.env.GITHUB_GIT_HOST,
429
- gitUsername: process.env.GITHUB_GIT_USERNAME
430
- });
431
- process.stdout.write(response);
432
- }
433
- if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
434
- main3().catch((error) => {
435
- const message = error instanceof Error ? error.message : "Unknown error";
436
- process.stderr.write(`${message}
437
- `);
438
- process.exitCode = 1;
439
- });
440
- }
441
- function normalizeGitHost(host) {
442
- return host.trim().toLowerCase();
443
- }
23
+ import { readFile } from "fs/promises";
24
+ import { join as join2 } from "path";
444
25
 
445
- // ../worker/dist/execution-phase.js
26
+ // ../worker/src/execution-phase.ts
446
27
  function resolveInitialExecutionPhase(input) {
447
28
  const { issueState, blockerCheckStates, activeStates } = input;
448
29
  if (!issueState) {
@@ -472,7 +53,7 @@ function resolveFinalExecutionPhase(input) {
472
53
  return resolvePausedExecutionPhase(input.currentPhase) ?? input.currentPhase;
473
54
  }
474
55
 
475
- // ../worker/dist/codex-policy.js
56
+ // ../worker/src/codex-policy.ts
476
57
  function resolveCodexPolicySettings(env) {
477
58
  return {
478
59
  approvalPolicy: env.SYMPHONY_APPROVAL_POLICY || "never",
@@ -481,7 +62,7 @@ function resolveCodexPolicySettings(env) {
481
62
  };
482
63
  }
483
64
 
484
- // ../worker/dist/convergence-detection.js
65
+ // ../worker/src/convergence-detection.ts
485
66
  import { spawnSync } from "child_process";
486
67
  var DEFAULT_MAX_NONPRODUCTIVE_TURNS = 3;
487
68
  function resolveMaxNonProductiveTurns(env) {
@@ -490,10 +71,14 @@ function resolveMaxNonProductiveTurns(env) {
490
71
  return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_NONPRODUCTIVE_TURNS;
491
72
  }
492
73
  function captureTurnWorkspaceSnapshot(cwd) {
493
- const result = spawnSync("git", ["status", "--porcelain=v1", "--untracked-files=all"], {
494
- cwd,
495
- encoding: "utf8"
496
- });
74
+ const result = spawnSync(
75
+ "git",
76
+ ["status", "--porcelain=v1", "--untracked-files=all"],
77
+ {
78
+ cwd,
79
+ encoding: "utf8"
80
+ }
81
+ );
497
82
  if (result.status !== 0) {
498
83
  return {
499
84
  fingerprint: null,
@@ -539,7 +124,7 @@ function normalizeError(value) {
539
124
  return normalized.length > 0 ? normalized : null;
540
125
  }
541
126
 
542
- // ../worker/dist/run-phase.js
127
+ // ../worker/src/run-phase.ts
543
128
  var TERMINAL_RUN_PHASES = /* @__PURE__ */ new Set([
544
129
  "succeeded",
545
130
  "failed",
@@ -554,7 +139,7 @@ function resolveExitRunPhase(currentRunPhase, exit) {
554
139
  return exit.code === 0 && !exit.signal ? "succeeded" : "failed";
555
140
  }
556
141
 
557
- // ../worker/dist/thread-resume.js
142
+ // ../worker/src/thread-resume.ts
558
143
  var DEFAULT_CONTINUATION_GUIDANCE = "Continue working on the issue. Review your progress and complete any remaining tasks.";
559
144
  function parseNonNegativeInteger(value) {
560
145
  const parsed = typeof value === "number" ? value : Number.parseInt(value ?? "", 10);
@@ -563,11 +148,17 @@ function parseNonNegativeInteger(value) {
563
148
  }
564
149
  return Math.floor(parsed);
565
150
  }
566
- function buildContinuationTurnInput({ continuationGuidance, lastTurnSummary, cumulativeTurnCount = 0 }) {
151
+ function buildContinuationTurnInput({
152
+ continuationGuidance,
153
+ lastTurnSummary,
154
+ cumulativeTurnCount = 0
155
+ }) {
567
156
  const template = continuationGuidance?.trim() || DEFAULT_CONTINUATION_GUIDANCE;
568
157
  return renderContinuationGuidance(template, {
569
158
  lastTurnSummary: normalizeContinuationVariable(lastTurnSummary) ?? "No previous turn summary was captured.",
570
- cumulativeTurnCount: String(Math.max(0, parseNonNegativeInteger(cumulativeTurnCount)))
159
+ cumulativeTurnCount: String(
160
+ Math.max(0, parseNonNegativeInteger(cumulativeTurnCount))
161
+ )
571
162
  });
572
163
  }
573
164
  function normalizeContinuationVariable(value) {
@@ -576,7 +167,9 @@ function normalizeContinuationVariable(value) {
576
167
  }
577
168
  function renderContinuationGuidance(template, variables) {
578
169
  if (template.includes("{%") || template.includes("%}")) {
579
- throw new Error("template_parse_error: continuation guidance does not support Liquid tags.");
170
+ throw new Error(
171
+ "template_parse_error: continuation guidance does not support Liquid tags."
172
+ );
580
173
  }
581
174
  let rendered = "";
582
175
  let lastIndex = 0;
@@ -587,7 +180,9 @@ function renderContinuationGuidance(template, variables) {
587
180
  const index = match.index ?? 0;
588
181
  rendered += template.slice(lastIndex, index);
589
182
  if (!(expression in variables)) {
590
- throw new Error(`template_render_error: unsupported continuation guidance variable '${expression}'.`);
183
+ throw new Error(
184
+ `template_render_error: unsupported continuation guidance variable '${expression}'.`
185
+ );
591
186
  }
592
187
  rendered += variables[expression] ?? "";
593
188
  lastIndex = index + matchedText.length;
@@ -595,12 +190,14 @@ function renderContinuationGuidance(template, variables) {
595
190
  rendered += template.slice(lastIndex);
596
191
  const strayLiquidExpression = rendered.match(/\{\{[^}]*\}\}/);
597
192
  if (strayLiquidExpression) {
598
- throw new Error(`template_parse_error: invalid continuation guidance expression '${strayLiquidExpression[0]}'.`);
193
+ throw new Error(
194
+ `template_parse_error: invalid continuation guidance expression '${strayLiquidExpression[0]}'.`
195
+ );
599
196
  }
600
197
  return rendered;
601
198
  }
602
199
 
603
- // ../worker/dist/turn-limits.js
200
+ // ../worker/src/turn-limits.ts
604
201
  var DEFAULT_SESSION_MAX_TURNS = 20;
605
202
  function resolveMaxTurns(value) {
606
203
  const parsed = typeof value === "number" ? value : Number(value);
@@ -623,21 +220,27 @@ function resolveMaxTurns(value) {
623
220
  };
624
221
  }
625
222
 
626
- // ../worker/dist/token-usage.js
627
- import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
628
- import { dirname as dirname2, join as join2 } from "path";
223
+ // ../worker/src/token-usage.ts
224
+ import { mkdir, writeFile } from "fs/promises";
225
+ import { dirname, join } from "path";
629
226
  async function persistTokenUsageArtifact(env, tokenUsage) {
630
227
  const artifactPath = resolveTokenUsageArtifactPath(env);
631
228
  if (!artifactPath) {
632
229
  return;
633
230
  }
634
231
  try {
635
- await mkdir2(dirname2(artifactPath), { recursive: true });
636
- await writeFile3(artifactPath, JSON.stringify(tokenUsage, null, 2) + "\n", "utf8");
232
+ await mkdir(dirname(artifactPath), { recursive: true });
233
+ await writeFile(
234
+ artifactPath,
235
+ JSON.stringify(tokenUsage, null, 2) + "\n",
236
+ "utf8"
237
+ );
637
238
  } catch (error) {
638
239
  const message = error instanceof Error ? error.message : String(error);
639
- process.stderr.write(`[worker] failed to persist token usage artifact: ${message}
640
- `);
240
+ process.stderr.write(
241
+ `[worker] failed to persist token usage artifact: ${message}
242
+ `
243
+ );
641
244
  }
642
245
  }
643
246
  function resolveTokenUsageArtifactPath(env) {
@@ -645,10 +248,10 @@ function resolveTokenUsageArtifactPath(env) {
645
248
  if (!workspaceRuntimeDir) {
646
249
  return null;
647
250
  }
648
- return join2(workspaceRuntimeDir, "token-usage.json");
251
+ return join(workspaceRuntimeDir, "token-usage.json");
649
252
  }
650
253
 
651
- // ../worker/dist/index.js
254
+ // ../worker/src/index.ts
652
255
  var launcherEnv = loadLauncherEnvironment(process.env);
653
256
  var runtimeState = {
654
257
  status: launcherEnv.SYMPHONY_RUN_ID ? "starting" : "idle",
@@ -684,10 +287,16 @@ var runtimeState = {
684
287
  exitClassification: null
685
288
  }
686
289
  };
687
- console.log(JSON.stringify({
688
- package: "@gh-symphony/worker",
689
- runtime: "self-hosted-sample"
690
- }, null, 2));
290
+ console.log(
291
+ JSON.stringify(
292
+ {
293
+ package: "@gh-symphony/worker",
294
+ runtime: "self-hosted-sample"
295
+ },
296
+ null,
297
+ 2
298
+ )
299
+ );
691
300
  var childProcess = null;
692
301
  var shutdownPromise = null;
693
302
  var orchestratorChannelDrainPending = false;
@@ -722,7 +331,9 @@ function shutdown(signal) {
722
331
  stopOrchestratorHeartbeatTimer();
723
332
  emitOrchestratorHeartbeat();
724
333
  await persistSessionTokenUsageArtifact(launcherEnv);
725
- await waitForPendingOrchestratorChannelFlush(resolveTerminalOrchestratorChannelFlushTimeoutMs());
334
+ await waitForPendingOrchestratorChannelFlush(
335
+ resolveTerminalOrchestratorChannelFlushTimeoutMs()
336
+ );
726
337
  console.log(`Worker stopped on ${signal}`);
727
338
  process.exit(0);
728
339
  })();
@@ -755,13 +366,13 @@ function waitForPendingOrchestratorChannelFlush(timeoutMs = ORCHESTRATOR_CHANNEL
755
366
  if (!orchestratorChannelDrainPending && pendingOrchestratorChannelPayloads.length === 0) {
756
367
  return Promise.resolve();
757
368
  }
758
- return new Promise((resolve2) => {
369
+ return new Promise((resolve) => {
759
370
  let settled = false;
760
371
  let timeout = setTimeout(() => {
761
372
  settled = true;
762
373
  process.stderr.removeListener("drain", handleDrain);
763
374
  timeout = null;
764
- resolve2();
375
+ resolve();
765
376
  }, timeoutMs);
766
377
  const handleDrain = () => {
767
378
  if (orchestratorChannelDrainPending || pendingOrchestratorChannelPayloads.length > 0) {
@@ -776,14 +387,17 @@ function waitForPendingOrchestratorChannelFlush(timeoutMs = ORCHESTRATOR_CHANNEL
776
387
  timeout = null;
777
388
  }
778
389
  process.stderr.removeListener("drain", handleDrain);
779
- resolve2();
390
+ resolve();
780
391
  };
781
392
  process.stderr.on("drain", handleDrain);
782
393
  });
783
394
  }
784
395
  function resolveTerminalOrchestratorChannelFlushTimeoutMs() {
785
396
  const pendingPayloadCount = pendingOrchestratorChannelPayloads.length + (orchestratorChannelDrainPending ? 1 : 0);
786
- return Math.max(ORCHESTRATOR_CHANNEL_FLUSH_TIMEOUT_MS, pendingPayloadCount * ORCHESTRATOR_CHANNEL_FLUSH_TIMEOUT_MS);
397
+ return Math.max(
398
+ ORCHESTRATOR_CHANNEL_FLUSH_TIMEOUT_MS,
399
+ pendingPayloadCount * ORCHESTRATOR_CHANNEL_FLUSH_TIMEOUT_MS
400
+ );
787
401
  }
788
402
  function writeOrQueueOrchestratorChannelPayload(serializedPayload) {
789
403
  if (orchestratorChannelDrainPending) {
@@ -861,9 +475,18 @@ function cloneTokenUsageSnapshot() {
861
475
  }
862
476
  function resolveTurnTokenUsageDelta(baseline) {
863
477
  return {
864
- inputTokens: Math.max(0, runtimeState.tokenUsage.inputTokens - baseline.inputTokens),
865
- outputTokens: Math.max(0, runtimeState.tokenUsage.outputTokens - baseline.outputTokens),
866
- totalTokens: Math.max(0, runtimeState.tokenUsage.totalTokens - baseline.totalTokens)
478
+ inputTokens: Math.max(
479
+ 0,
480
+ runtimeState.tokenUsage.inputTokens - baseline.inputTokens
481
+ ),
482
+ outputTokens: Math.max(
483
+ 0,
484
+ runtimeState.tokenUsage.outputTokens - baseline.outputTokens
485
+ ),
486
+ totalTokens: Math.max(
487
+ 0,
488
+ runtimeState.tokenUsage.totalTokens - baseline.totalTokens
489
+ )
867
490
  };
868
491
  }
869
492
  function resolveSessionTokenUsageDelta() {
@@ -900,7 +523,10 @@ function emitTurnCompletedEvent(turn) {
900
523
  issueId,
901
524
  startedAt: turn.startedAt,
902
525
  completedAt,
903
- durationMs: Math.max(0, new Date(completedAt).getTime() - new Date(turn.startedAt).getTime()),
526
+ durationMs: Math.max(
527
+ 0,
528
+ new Date(completedAt).getTime() - new Date(turn.startedAt).getTime()
529
+ ),
904
530
  threadId: turn.threadId,
905
531
  turnId: turn.turnId,
906
532
  turnCount: turn.turnCount,
@@ -921,7 +547,10 @@ function emitTurnFailedEvent(turn, error) {
921
547
  issueId,
922
548
  startedAt: turn.startedAt,
923
549
  failedAt,
924
- durationMs: Math.max(0, new Date(failedAt).getTime() - new Date(turn.startedAt).getTime()),
550
+ durationMs: Math.max(
551
+ 0,
552
+ new Date(failedAt).getTime() - new Date(turn.startedAt).getTime()
553
+ ),
925
554
  threadId: turn.threadId,
926
555
  turnId: turn.turnId,
927
556
  turnCount: turn.turnCount,
@@ -934,16 +563,42 @@ function emitTurnFailedEvent(turn, error) {
934
563
  }
935
564
  async function startAssignedRun() {
936
565
  try {
937
- const workflowPath = launcherEnv.SYMPHONY_WORKFLOW_PATH || join3(launcherEnv.WORKING_DIRECTORY, "WORKFLOW.md");
566
+ const workflowPath = launcherEnv.SYMPHONY_WORKFLOW_PATH || join2(launcherEnv.WORKING_DIRECTORY, "WORKFLOW.md");
938
567
  runtimeState.runPhase = "building_prompt";
939
- const workflow = parseWorkflowMarkdown(await readFile2(workflowPath, "utf8"), launcherEnv);
568
+ const workflow = parseWorkflowMarkdown(
569
+ await readFile(workflowPath, "utf8"),
570
+ launcherEnv
571
+ );
572
+ if (isClaudeRuntimeCommand(workflow.codex.command)) {
573
+ const hasGitHubGraphqlToken = typeof launcherEnv.GITHUB_GRAPHQL_TOKEN === "string" && launcherEnv.GITHUB_GRAPHQL_TOKEN.trim().length > 0;
574
+ const preflight = await runClaudePreflight({
575
+ cwd: launcherEnv.WORKING_DIRECTORY,
576
+ env: launcherEnv,
577
+ command: resolveClaudeCommandBinary(workflow.codex.command) ?? void 0,
578
+ includeGhAuth: !hasGitHubGraphqlToken
579
+ });
580
+ process.stderr.write(
581
+ `[worker] ${formatClaudePreflightText(preflight)}
582
+ `
583
+ );
584
+ if (!preflight.ok) {
585
+ await exitWorkerStartupFailure(
586
+ "Claude runtime preflight failed before agent launch."
587
+ );
588
+ return;
589
+ }
590
+ await exitWorkerStartupFailure(
591
+ "Claude runtime worker launch is not yet implemented in this branch."
592
+ );
593
+ return;
594
+ }
940
595
  runtimeState.executionPhase = resolveInitialExecutionPhase({
941
596
  issueState: runtimeState.run?.state,
942
597
  blockerCheckStates: workflow.lifecycle.blockerCheckStates,
943
598
  activeStates: workflow.lifecycle.activeStates
944
599
  });
945
600
  const config = resolveLocalRuntimeLaunchConfig(launcherEnv);
946
- config.agentCommand = workflow.codex.command;
601
+ config.agentCommand = resolveWorkflowRuntimeCommand(workflow);
947
602
  runtimeState.runPhase = "launching_agent";
948
603
  const plan = await prepareCodexRuntimePlan(config);
949
604
  childProcess = launchCodexAppServer(plan);
@@ -955,24 +610,27 @@ async function startAssignedRun() {
955
610
  void runCodexClientProtocol(childProcess, plan, launcherEnv, {
956
611
  continuationGuidance: workflow.continuationGuidance
957
612
  });
958
- childProcess.once("exit", (code, signal) => {
959
- const currentRunPhase = runtimeState.runPhase;
960
- const nextRunPhase = resolveExitRunPhase(currentRunPhase, {
961
- code,
962
- signal
963
- });
964
- const preservesTerminalPhase = currentRunPhase != null && nextRunPhase === currentRunPhase;
965
- if (!preservesTerminalPhase) {
966
- runtimeState.status = code === 0 && !signal ? "completed" : "failed";
967
- }
968
- runtimeState.runPhase = nextRunPhase;
969
- if (runtimeState.run) {
613
+ childProcess.once(
614
+ "exit",
615
+ (code, signal) => {
616
+ const currentRunPhase = runtimeState.runPhase;
617
+ const nextRunPhase = resolveExitRunPhase(currentRunPhase, {
618
+ code,
619
+ signal
620
+ });
621
+ const preservesTerminalPhase = currentRunPhase != null && nextRunPhase === currentRunPhase;
970
622
  if (!preservesTerminalPhase) {
971
- runtimeState.run.lastError = code === 0 && !signal ? null : `codex app-server exited with ${signal ?? code ?? "unknown"}`;
623
+ runtimeState.status = code === 0 && !signal ? "completed" : "failed";
972
624
  }
625
+ runtimeState.runPhase = nextRunPhase;
626
+ if (runtimeState.run) {
627
+ if (!preservesTerminalPhase) {
628
+ runtimeState.run.lastError = code === 0 && !signal ? null : `codex app-server exited with ${signal ?? code ?? "unknown"}`;
629
+ }
630
+ }
631
+ void persistSessionTokenUsageArtifact(launcherEnv);
973
632
  }
974
- void persistSessionTokenUsageArtifact(launcherEnv);
975
- });
633
+ );
976
634
  childProcess.once("error", (error) => {
977
635
  runtimeState.status = "failed";
978
636
  runtimeState.runPhase = "failed";
@@ -990,17 +648,39 @@ async function startAssignedRun() {
990
648
  await persistSessionTokenUsageArtifact(launcherEnv);
991
649
  }
992
650
  }
651
+ async function exitWorkerStartupFailure(message) {
652
+ runtimeState.status = "failed";
653
+ runtimeState.runPhase = "failed";
654
+ if (runtimeState.run) {
655
+ runtimeState.run.lastError = message;
656
+ }
657
+ process.stderr.write(`[worker] ${message}
658
+ `);
659
+ stopOrchestratorHeartbeatTimer();
660
+ emitOrchestratorHeartbeat();
661
+ await persistSessionTokenUsageArtifact(launcherEnv);
662
+ await waitForPendingOrchestratorChannelFlush(
663
+ resolveTerminalOrchestratorChannelFlushTimeoutMs()
664
+ );
665
+ process.exit(1);
666
+ }
993
667
  async function runCodexClientProtocol(child, plan, env, options) {
994
668
  const renderedPrompt = env.SYMPHONY_RENDERED_PROMPT;
995
669
  if (!renderedPrompt) {
996
- process.stderr.write("[worker] SYMPHONY_RENDERED_PROMPT not set; skipping codex client protocol\n");
670
+ process.stderr.write(
671
+ "[worker] SYMPHONY_RENDERED_PROMPT not set; skipping codex client protocol\n"
672
+ );
997
673
  return;
998
674
  }
999
675
  if (!child.stdin || !child.stdout) {
1000
- process.stderr.write("[worker] codex process has no stdio pipes; cannot run client protocol\n");
676
+ process.stderr.write(
677
+ "[worker] codex process has no stdio pipes; cannot run client protocol\n"
678
+ );
1001
679
  return;
1002
680
  }
1003
- const { maxTurns, exhaustedBeforeStart } = resolveMaxTurns(env.SYMPHONY_MAX_TURNS);
681
+ const { maxTurns, exhaustedBeforeStart } = resolveMaxTurns(
682
+ env.SYMPHONY_MAX_TURNS
683
+ );
1004
684
  const readTimeoutMs = Number(env.SYMPHONY_READ_TIMEOUT_MS) || 5e3;
1005
685
  const turnTimeoutMs = Number(env.SYMPHONY_TURN_TIMEOUT_MS) || 36e5;
1006
686
  const maxNonProductiveTurns = resolveMaxNonProductiveTurns(env);
@@ -1015,10 +695,11 @@ async function runCodexClientProtocol(child, plan, env, options) {
1015
695
  let lineBuffer = "";
1016
696
  let deltaBuffer = null;
1017
697
  function flushDeltaBuffer() {
1018
- if (!deltaBuffer)
1019
- return;
1020
- process.stderr.write(`[worker] codex \u2192 agent_message [accumulated] ${JSON.stringify({ text: deltaBuffer.text }).slice(0, 500)}
1021
- `);
698
+ if (!deltaBuffer) return;
699
+ process.stderr.write(
700
+ `[worker] codex \u2192 agent_message [accumulated] ${JSON.stringify({ text: deltaBuffer.text }).slice(0, 500)}
701
+ `
702
+ );
1022
703
  deltaBuffer = null;
1023
704
  }
1024
705
  const pendingRequests = /* @__PURE__ */ new Map();
@@ -1035,7 +716,8 @@ async function runCodexClientProtocol(child, plan, env, options) {
1035
716
  }
1036
717
  }
1037
718
  function describeTurnTerminalEvent(event, params) {
1038
- const fallback = event === "turn/failed" ? "turn_failed: codex reported turn failure" : "turn_cancelled: codex reported turn cancellation";
719
+ const errorPrefix = event === "agent.turnFailed" ? "turn_failed" : "turn_cancelled";
720
+ const fallback = event === "agent.turnFailed" ? "turn_failed: codex reported turn failure" : "turn_cancelled: codex reported turn cancellation";
1039
721
  if (!params || typeof params !== "object") {
1040
722
  return fallback;
1041
723
  }
@@ -1044,18 +726,18 @@ async function runCodexClientProtocol(child, plan, env, options) {
1044
726
  for (const key of directReasonKeys) {
1045
727
  const value = record[key];
1046
728
  if (typeof value === "string" && value.trim()) {
1047
- return `${event.replace("/", "_")}: ${value.trim()}`;
729
+ return `${errorPrefix}: ${value.trim()}`;
1048
730
  }
1049
731
  if (value && typeof value === "object" && typeof value.message === "string") {
1050
732
  const nested = value;
1051
733
  const nestedMessage = String(nested.message).trim();
1052
734
  if (nestedMessage) {
1053
- return `${event.replace("/", "_")}: ${nestedMessage}`;
735
+ return `${errorPrefix}: ${nestedMessage}`;
1054
736
  }
1055
737
  }
1056
738
  }
1057
739
  const serialized = JSON.stringify(params).slice(0, 300);
1058
- return serialized && serialized !== "{}" ? `${event.replace("/", "_")}: ${serialized}` : fallback;
740
+ return serialized && serialized !== "{}" ? `${errorPrefix}: ${serialized}` : fallback;
1059
741
  }
1060
742
  function markTurnTerminalFailure(runPhase, lastError) {
1061
743
  runtimeState.status = "failed";
@@ -1075,36 +757,45 @@ async function runCodexClientProtocol(child, plan, env, options) {
1075
757
  child.stdin?.write(line);
1076
758
  }
1077
759
  function sendRequest(id, method, params) {
1078
- return new Promise((resolve2, reject) => {
1079
- pendingRequests.set(id, { resolve: resolve2, reject });
760
+ return new Promise((resolve, reject) => {
761
+ pendingRequests.set(id, { resolve, reject });
1080
762
  sendMessage({ jsonrpc: "2.0", id, method, params });
1081
763
  });
1082
764
  }
1083
765
  function sendRequestWithTimeout(id, method, params) {
1084
- return new Promise((resolve2, reject) => {
766
+ return new Promise((resolve, reject) => {
1085
767
  const timer = setTimeout(() => {
1086
768
  pendingRequests.delete(id);
1087
- reject(new Error(`response_timeout: ${method} timed out after ${readTimeoutMs}ms`));
769
+ reject(
770
+ new Error(
771
+ `response_timeout: ${method} timed out after ${readTimeoutMs}ms`
772
+ )
773
+ );
1088
774
  }, readTimeoutMs);
1089
- sendRequest(id, method, params).then((result) => {
1090
- clearTimeout(timer);
1091
- resolve2(result);
1092
- }, (error) => {
1093
- clearTimeout(timer);
1094
- reject(error);
1095
- });
775
+ sendRequest(id, method, params).then(
776
+ (result) => {
777
+ clearTimeout(timer);
778
+ resolve(result);
779
+ },
780
+ (error) => {
781
+ clearTimeout(timer);
782
+ reject(error);
783
+ }
784
+ );
1096
785
  });
1097
786
  }
1098
787
  function waitForTurnCompletion() {
1099
- return new Promise((resolve2) => {
1100
- turnCompletedResolve = resolve2;
788
+ return new Promise((resolve) => {
789
+ turnCompletedResolve = resolve;
1101
790
  });
1102
791
  }
1103
792
  function waitForTurnWithTimeout() {
1104
- return new Promise((resolve2, reject) => {
793
+ return new Promise((resolve, reject) => {
1105
794
  const timer = setTimeout(() => {
1106
- process.stderr.write(`[worker] turn_timeout: turn exceeded ${turnTimeoutMs}ms \u2014 killing codex process
1107
- `);
795
+ process.stderr.write(
796
+ `[worker] turn_timeout: turn exceeded ${turnTimeoutMs}ms \u2014 killing codex process
797
+ `
798
+ );
1108
799
  if (child.pid) {
1109
800
  try {
1110
801
  process.kill(child.pid, "SIGTERM");
@@ -1113,20 +804,27 @@ async function runCodexClientProtocol(child, plan, env, options) {
1113
804
  }
1114
805
  reject(new Error("turn_timeout: turn exceeded time limit"));
1115
806
  }, turnTimeoutMs);
1116
- waitForTurnCompletion().then(() => {
1117
- clearTimeout(timer);
1118
- resolve2();
1119
- }, (error) => {
1120
- clearTimeout(timer);
1121
- reject(error);
1122
- });
807
+ waitForTurnCompletion().then(
808
+ () => {
809
+ clearTimeout(timer);
810
+ resolve();
811
+ },
812
+ (error) => {
813
+ clearTimeout(timer);
814
+ reject(error);
815
+ }
816
+ );
1123
817
  });
1124
818
  }
1125
819
  async function dispatchDynamicToolCall(callId, toolName, threadId, turnId, args) {
1126
- const toolDef = plan.tools.find((t) => t.name === toolName);
820
+ const toolDef = plan.tools.find(
821
+ (t) => t.name === toolName
822
+ );
1127
823
  if (!toolDef) {
1128
- process.stderr.write(`[worker] unknown dynamic tool: ${toolName}; sending error response
1129
- `);
824
+ process.stderr.write(
825
+ `[worker] unknown dynamic tool: ${toolName}; sending error response
826
+ `
827
+ );
1130
828
  sendMessage({
1131
829
  jsonrpc: "2.0",
1132
830
  method: "dynamic_tool_call_response",
@@ -1146,8 +844,10 @@ async function runCodexClientProtocol(child, plan, env, options) {
1146
844
  return;
1147
845
  }
1148
846
  const inputJson = JSON.stringify(args ?? {});
1149
- process.stderr.write(`[worker] executing dynamic tool "${toolName}" (callId=${callId})
1150
- `);
847
+ process.stderr.write(
848
+ `[worker] executing dynamic tool "${toolName}" (callId=${callId})
849
+ `
850
+ );
1151
851
  try {
1152
852
  const output = await runToolProcess(toolDef, inputJson);
1153
853
  sendMessage({
@@ -1178,138 +878,185 @@ async function runCodexClientProtocol(child, plan, env, options) {
1178
878
  });
1179
879
  }
1180
880
  }
1181
- function handleServerMessage(msg) {
1182
- if ("id" in msg && msg.id != null && ("result" in msg || "error" in msg)) {
1183
- const id = String(msg.id);
1184
- const pending = pendingRequests.get(id);
1185
- if (pending) {
1186
- pendingRequests.delete(id);
1187
- if ("error" in msg) {
1188
- pending.reject(new Error(JSON.stringify(msg.error)));
1189
- } else {
1190
- pending.resolve(msg.result);
1191
- }
1192
- }
881
+ function emitObservedAgentEvent(event) {
882
+ if (event.payload.suppressUpdate) {
1193
883
  return;
1194
884
  }
1195
- runtimeState.lastEventAt = (/* @__PURE__ */ new Date()).toISOString();
1196
- const orchestratorEventName = typeof msg.method === "string" ? msg.method : void 0;
1197
- if (msg.method === "dynamic_tool_call_request" && msg.params != null) {
1198
- const params = msg.params;
1199
- void dispatchDynamicToolCall(params.callId, params.tool, params.threadId, params.turnId, params.arguments);
1200
- emitOrchestratorChannelEvent(orchestratorEventName);
1201
- return;
885
+ emitOrchestratorChannelEvent(getCodexObservabilityEventName(event));
886
+ }
887
+ function handleInputRequired(reason, event) {
888
+ process.stderr.write(
889
+ "[worker] user_input_required detected \u2014 terminating agent process\n"
890
+ );
891
+ userInputRequired = true;
892
+ runtimeState.status = "failed";
893
+ if (runtimeState.run) {
894
+ runtimeState.run.lastError = reason;
1202
895
  }
1203
- if (msg.method === "item/tool/requestUserInput") {
1204
- process.stderr.write("[worker] user_input_required detected \u2014 terminating codex process\n");
1205
- userInputRequired = true;
1206
- runtimeState.status = "failed";
1207
- if (runtimeState.run) {
1208
- runtimeState.run.lastError = "turn_input_required: agent requires user input";
1209
- }
1210
- if (child.pid) {
1211
- try {
1212
- process.kill(child.pid, "SIGTERM");
1213
- } catch {
1214
- }
1215
- }
1216
- if (activeTurnTelemetry) {
1217
- emitTurnFailedEvent(activeTurnTelemetry, runtimeState.run?.lastError ?? null);
1218
- activeTurnTelemetry = null;
896
+ if (child.pid) {
897
+ try {
898
+ process.kill(child.pid, "SIGTERM");
899
+ } catch {
1219
900
  }
1220
- resolvePendingTurnCompletion();
1221
- emitOrchestratorChannelEvent(orchestratorEventName);
1222
- return;
1223
901
  }
1224
- if (msg.method === "turn/completed") {
1225
- flushDeltaBuffer();
1226
- const turnParams = msg.params ?? {};
1227
- const turnUsage = extractAbsoluteTokenUsage(turnParams.usage);
1228
- if (turnUsage) {
1229
- applyTokenUsageUpdate("turn/completed", turnUsage);
902
+ if (activeTurnTelemetry) {
903
+ emitTurnFailedEvent(
904
+ activeTurnTelemetry,
905
+ runtimeState.run?.lastError ?? null
906
+ );
907
+ activeTurnTelemetry = null;
908
+ }
909
+ resolvePendingTurnCompletion();
910
+ emitObservedAgentEvent(event);
911
+ }
912
+ function handleAgentEvent(event) {
913
+ switch (event.name) {
914
+ case "agent.turnStarted":
915
+ emitObservedAgentEvent(event);
916
+ return true;
917
+ case "agent.toolCallRequested":
918
+ if (!event.payload.threadId || !event.payload.turnId) {
919
+ process.stderr.write(
920
+ `[worker] dynamic tool call ${event.payload.callId} is missing threadId/turnId; cannot send response
921
+ `
922
+ );
923
+ emitObservedAgentEvent(event);
924
+ return true;
925
+ }
926
+ void dispatchDynamicToolCall(
927
+ event.payload.callId,
928
+ event.payload.toolName,
929
+ event.payload.threadId,
930
+ event.payload.turnId,
931
+ event.payload.arguments
932
+ );
933
+ emitObservedAgentEvent(event);
934
+ return true;
935
+ case "agent.inputRequired":
936
+ handleInputRequired(event.payload.reason, event);
937
+ return true;
938
+ case "agent.tokenUsageUpdated": {
939
+ const tokenUsage = extractAbsoluteTokenUsage(event.payload.params);
940
+ if (tokenUsage) {
941
+ applyTokenUsageUpdate(
942
+ getCodexObservabilityEventName(event) ?? event.name,
943
+ tokenUsage
944
+ );
945
+ }
946
+ emitObservedAgentEvent(event);
947
+ return true;
1230
948
  }
1231
- const rateLimits2 = extractRateLimitPayload(turnParams);
1232
- if (rateLimits2) {
1233
- applyRateLimitUpdate("turn/completed", rateLimits2);
949
+ case "agent.rateLimit": {
950
+ const rateLimits = extractRateLimitPayload(event.payload.params);
951
+ if (rateLimits) {
952
+ applyRateLimitUpdate(
953
+ getCodexObservabilityEventName(event) ?? event.name,
954
+ rateLimits
955
+ );
956
+ }
957
+ emitObservedAgentEvent(event);
958
+ return true;
1234
959
  }
1235
- if (turnParams.inputRequired === true) {
1236
- process.stderr.write("[worker] user_input_required detected \u2014 terminating codex process\n");
1237
- userInputRequired = true;
1238
- runtimeState.status = "failed";
1239
- if (runtimeState.run) {
1240
- runtimeState.run.lastError = "turn_input_required: agent requires user input";
960
+ case "agent.messageDelta": {
961
+ const { delta, itemId } = event.payload;
962
+ if (deltaBuffer?.itemId !== itemId) {
963
+ flushDeltaBuffer();
964
+ deltaBuffer = { itemId, text: delta };
965
+ } else {
966
+ deltaBuffer.text += delta;
1241
967
  }
1242
- if (child.pid) {
1243
- try {
1244
- process.kill(child.pid, "SIGTERM");
1245
- } catch {
1246
- }
968
+ emitObservedAgentEvent(event);
969
+ return true;
970
+ }
971
+ case "agent.turnCompleted":
972
+ flushDeltaBuffer();
973
+ if (event.payload.inputRequired) {
974
+ handleInputRequired(DEFAULT_AGENT_INPUT_REQUIRED_REASON, event);
975
+ return true;
1247
976
  }
977
+ emitObservedAgentEvent(event);
1248
978
  if (activeTurnTelemetry) {
1249
- emitTurnFailedEvent(activeTurnTelemetry, runtimeState.run?.lastError ?? null);
979
+ emitTurnCompletedEvent(activeTurnTelemetry);
1250
980
  activeTurnTelemetry = null;
1251
981
  }
982
+ process.stderr.write("[worker] agent turn completed\n");
1252
983
  resolvePendingTurnCompletion();
1253
- emitOrchestratorChannelEvent(orchestratorEventName);
1254
- return;
984
+ return true;
985
+ case "agent.turnFailed": {
986
+ flushDeltaBuffer();
987
+ const lastError = describeTurnTerminalEvent(
988
+ "agent.turnFailed",
989
+ event.payload.params
990
+ );
991
+ process.stderr.write(
992
+ `[worker] agent turn failed ${JSON.stringify(event.payload.params).slice(0, 300)}
993
+ `
994
+ );
995
+ markTurnTerminalFailure("failed", lastError);
996
+ emitObservedAgentEvent(event);
997
+ return true;
1255
998
  }
1256
- emitOrchestratorChannelEvent(orchestratorEventName);
1257
- if (activeTurnTelemetry) {
1258
- emitTurnCompletedEvent(activeTurnTelemetry);
1259
- activeTurnTelemetry = null;
999
+ case "agent.turnCancelled": {
1000
+ flushDeltaBuffer();
1001
+ const lastError = describeTurnTerminalEvent(
1002
+ "agent.turnCancelled",
1003
+ event.payload.params
1004
+ );
1005
+ process.stderr.write(
1006
+ `[worker] agent turn cancelled ${JSON.stringify(event.payload.params).slice(0, 300)}
1007
+ `
1008
+ );
1009
+ markTurnTerminalFailure("canceled_by_reconciliation", lastError);
1010
+ emitObservedAgentEvent(event);
1011
+ return true;
1260
1012
  }
1261
- process.stderr.write("[worker] codex turn/completed\n");
1262
- resolvePendingTurnCompletion();
1263
- return;
1013
+ case "agent.error":
1014
+ flushDeltaBuffer();
1015
+ process.stderr.write(
1016
+ `[worker] runtime error ${JSON.stringify(event.payload.params).slice(0, 300)}
1017
+ `
1018
+ );
1019
+ markTurnTerminalFailure("failed", event.payload.error);
1020
+ emitObservedAgentEvent(event);
1021
+ return true;
1022
+ default:
1023
+ return false;
1264
1024
  }
1265
- if (msg.method === "turn/failed") {
1266
- flushDeltaBuffer();
1267
- const lastError = describeTurnTerminalEvent("turn/failed", msg.params ?? null);
1268
- process.stderr.write(`[worker] codex turn/failed ${JSON.stringify(msg.params ?? {}).slice(0, 300)}
1269
- `);
1270
- markTurnTerminalFailure("failed", lastError);
1271
- emitOrchestratorChannelEvent(orchestratorEventName);
1025
+ }
1026
+ function handleServerMessage(msg) {
1027
+ if ("id" in msg && msg.id != null && ("result" in msg || "error" in msg)) {
1028
+ const id = String(msg.id);
1029
+ const pending = pendingRequests.get(id);
1030
+ if (pending) {
1031
+ pendingRequests.delete(id);
1032
+ if ("error" in msg) {
1033
+ pending.reject(new Error(JSON.stringify(msg.error)));
1034
+ } else {
1035
+ pending.resolve(msg.result);
1036
+ }
1037
+ }
1272
1038
  return;
1273
1039
  }
1274
- if (msg.method === "turn/cancelled") {
1275
- flushDeltaBuffer();
1276
- const lastError = describeTurnTerminalEvent("turn/cancelled", msg.params ?? null);
1277
- process.stderr.write(`[worker] codex turn/cancelled ${JSON.stringify(msg.params ?? {}).slice(0, 300)}
1278
- `);
1279
- markTurnTerminalFailure("canceled_by_reconciliation", lastError);
1280
- emitOrchestratorChannelEvent(orchestratorEventName);
1281
- return;
1040
+ runtimeState.lastEventAt = (/* @__PURE__ */ new Date()).toISOString();
1041
+ const agentEvents = normalizeCodexRuntimeEvents(msg);
1042
+ let handledAgentEvent = false;
1043
+ for (const event of agentEvents) {
1044
+ handledAgentEvent = handleAgentEvent(event) || handledAgentEvent;
1282
1045
  }
1283
- if (msg.method === "thread/tokenUsage/updated" || msg.method === "total_token_usage" || msg.method === "codex/event/token_count") {
1284
- const tokenUsage = extractAbsoluteTokenUsage(msg.params);
1285
- if (tokenUsage) {
1286
- applyTokenUsageUpdate(msg.method, tokenUsage);
1287
- }
1288
- emitOrchestratorChannelEvent(orchestratorEventName);
1046
+ if (handledAgentEvent) {
1289
1047
  return;
1290
1048
  }
1291
1049
  const rateLimits = extractRateLimitPayload(msg.params);
1292
1050
  if (rateLimits && typeof msg.method === "string") {
1293
1051
  applyRateLimitUpdate(msg.method, rateLimits);
1294
1052
  }
1295
- if (typeof msg.method === "string" && (msg.method === "codex/event/agent_message_content_delta" || msg.method === "codex/event/agent_message_delta" || msg.method === "item/agentMessage/delta")) {
1296
- const params = msg.params ?? {};
1297
- const delta = typeof params.delta === "string" ? params.delta : "";
1298
- const itemId = typeof params.item_id === "string" ? params.item_id : "";
1299
- if (deltaBuffer?.itemId !== itemId) {
1300
- flushDeltaBuffer();
1301
- deltaBuffer = { itemId, text: delta };
1302
- } else {
1303
- deltaBuffer.text += delta;
1304
- }
1305
- emitOrchestratorChannelEvent(orchestratorEventName);
1306
- return;
1307
- }
1308
1053
  if (typeof msg.method === "string") {
1309
1054
  flushDeltaBuffer();
1310
- emitOrchestratorChannelEvent(orchestratorEventName);
1311
- process.stderr.write(`[worker] codex \u2192 ${msg.method} ${JSON.stringify(msg.params ?? {}).slice(0, 300)}
1312
- `);
1055
+ emitOrchestratorChannelEvent(msg.method);
1056
+ process.stderr.write(
1057
+ `[worker] codex \u2192 ${msg.method} ${JSON.stringify(msg.params ?? {}).slice(0, 300)}
1058
+ `
1059
+ );
1313
1060
  }
1314
1061
  }
1315
1062
  child.stdout.on("data", (chunk) => {
@@ -1318,8 +1065,7 @@ async function runCodexClientProtocol(child, plan, env, options) {
1318
1065
  lineBuffer = lines.pop() ?? "";
1319
1066
  for (const line of lines) {
1320
1067
  const trimmed = line.trim();
1321
- if (!trimmed)
1322
- continue;
1068
+ if (!trimmed) continue;
1323
1069
  try {
1324
1070
  const msg = JSON.parse(trimmed);
1325
1071
  handleServerMessage(msg);
@@ -1354,30 +1100,42 @@ async function runCodexClientProtocol(child, plan, env, options) {
1354
1100
  mcp_servers: mcpServers
1355
1101
  }
1356
1102
  };
1357
- process.stderr.write(`[worker] starting codex thread (mcp_servers: ${Object.keys(mcpServers).join(", ")})
1358
- `);
1359
- const threadResult = await sendRequestWithTimeout("thread-1", "thread/start", {
1360
- ...baseThreadParams,
1361
- ephemeral: false
1362
- });
1103
+ process.stderr.write(
1104
+ `[worker] starting codex thread (mcp_servers: ${Object.keys(mcpServers).join(", ")})
1105
+ `
1106
+ );
1107
+ const threadResult = await sendRequestWithTimeout(
1108
+ "thread-1",
1109
+ "thread/start",
1110
+ {
1111
+ ...baseThreadParams,
1112
+ ephemeral: false
1113
+ }
1114
+ );
1363
1115
  const threadId = threadResult.thread_id ?? threadResult.thread?.id;
1364
1116
  runtimeState.sessionInfo.threadId = threadId ?? null;
1365
1117
  runtimeState.sessionInfo.turnId = null;
1366
1118
  runtimeState.sessionInfo.sessionId = null;
1367
1119
  runtimeState.sessionInfo.exitClassification = null;
1368
1120
  runtimeState.sessionId = null;
1369
- process.stderr.write(`[worker] codex thread started (id=${String(threadId ?? "unknown")})
1370
- `);
1121
+ process.stderr.write(
1122
+ `[worker] codex thread started (id=${String(threadId ?? "unknown")})
1123
+ `
1124
+ );
1371
1125
  if (!threadId) {
1372
- process.stderr.write("[worker] warning: no threadId returned; cannot start turn\n");
1126
+ process.stderr.write(
1127
+ "[worker] warning: no threadId returned; cannot start turn\n"
1128
+ );
1373
1129
  return;
1374
1130
  }
1375
1131
  let turnCount = 0;
1376
1132
  let requestIdCounter = 0;
1377
1133
  let maxTurnsReached = exhaustedBeforeStart;
1378
1134
  if (exhaustedBeforeStart) {
1379
- process.stderr.write(`[worker] max_turns (${String(env.SYMPHONY_MAX_TURNS ?? maxTurns)}) does not allow any turns for this worker session \u2014 exiting
1380
- `);
1135
+ process.stderr.write(
1136
+ `[worker] max_turns (${String(env.SYMPHONY_MAX_TURNS ?? maxTurns)}) does not allow any turns for this worker session \u2014 exiting
1137
+ `
1138
+ );
1381
1139
  }
1382
1140
  for (let turn = 0; turn < maxTurns; turn++) {
1383
1141
  turnCount = turn + 1;
@@ -1388,18 +1146,24 @@ async function runCodexClientProtocol(child, plan, env, options) {
1388
1146
  continuationGuidance,
1389
1147
  cumulativeTurnCount: turn
1390
1148
  });
1391
- process.stderr.write(`[worker] starting codex turn ${turnCount}/${maxTurns}${isFirstTurn ? " (initial)" : " (continuation)"}
1392
- `);
1149
+ process.stderr.write(
1150
+ `[worker] starting codex turn ${turnCount}/${maxTurns}${isFirstTurn ? " (initial)" : " (continuation)"}
1151
+ `
1152
+ );
1393
1153
  requestIdCounter += 1;
1394
1154
  const turnRequestId = `turn-${requestIdCounter}`;
1395
- const turnResult = await sendRequestWithTimeout(turnRequestId, "turn/start", {
1396
- threadId,
1397
- input: [{ type: "text", text: turnInput }],
1398
- cwd: plan.cwd,
1399
- title: composeTurnTitle(issueIdentifier, env.SYMPHONY_ISSUE_TITLE),
1400
- approvalPolicy,
1401
- sandboxPolicy: turnSandboxPolicy
1402
- });
1155
+ const turnResult = await sendRequestWithTimeout(
1156
+ turnRequestId,
1157
+ "turn/start",
1158
+ {
1159
+ threadId,
1160
+ input: [{ type: "text", text: turnInput }],
1161
+ cwd: plan.cwd,
1162
+ title: composeTurnTitle(issueIdentifier, env.SYMPHONY_ISSUE_TITLE),
1163
+ approvalPolicy,
1164
+ sandboxPolicy: turnSandboxPolicy
1165
+ }
1166
+ );
1403
1167
  const turnId = turnResult.turn_id ?? turnResult.turn?.id;
1404
1168
  const sessionId = threadId && turnId ? `${threadId}-${turnId}` : null;
1405
1169
  runtimeState.sessionInfo.turnId = turnId ?? null;
@@ -1413,10 +1177,14 @@ async function runCodexClientProtocol(child, plan, env, options) {
1413
1177
  sessionId,
1414
1178
  tokenUsageBaseline: cloneTokenUsageSnapshot()
1415
1179
  };
1416
- process.stderr.write(`[worker] codex turn started (id=${String(turnId ?? "unknown")})
1417
- `);
1418
- process.stderr.write(`[worker] session_id=${String(sessionId ?? "unknown")}
1419
- `);
1180
+ process.stderr.write(
1181
+ `[worker] codex turn started (id=${String(turnId ?? "unknown")})
1182
+ `
1183
+ );
1184
+ process.stderr.write(
1185
+ `[worker] session_id=${String(sessionId ?? "unknown")}
1186
+ `
1187
+ );
1420
1188
  emitTurnStartedEvent(activeTurnTelemetry);
1421
1189
  await waitForTurnWithTimeout();
1422
1190
  if (userInputRequired) {
@@ -1424,14 +1192,18 @@ async function runCodexClientProtocol(child, plan, env, options) {
1424
1192
  break;
1425
1193
  }
1426
1194
  if (turnTerminalFailurePhase) {
1427
- process.stderr.write(`[worker] exiting due to ${turnTerminalFailurePhase}
1428
- `);
1195
+ process.stderr.write(
1196
+ `[worker] exiting due to ${turnTerminalFailurePhase}
1197
+ `
1198
+ );
1429
1199
  break;
1430
1200
  }
1431
1201
  if (turn + 1 >= maxTurns) {
1432
1202
  maxTurnsReached = true;
1433
- process.stderr.write(`[worker] max_turns (${maxTurns}) reached for this worker session \u2014 exiting
1434
- `);
1203
+ process.stderr.write(
1204
+ `[worker] max_turns (${maxTurns}) reached for this worker session \u2014 exiting
1205
+ `
1206
+ );
1435
1207
  break;
1436
1208
  }
1437
1209
  const trackerState = await refreshTrackerState(env);
@@ -1444,19 +1216,26 @@ async function runCodexClientProtocol(child, plan, env, options) {
1444
1216
  trackerState,
1445
1217
  userInputRequired: false
1446
1218
  });
1447
- process.stderr.write("[worker] issue no longer actionable \u2014 exiting multi-turn loop\n");
1219
+ process.stderr.write(
1220
+ "[worker] issue no longer actionable \u2014 exiting multi-turn loop\n"
1221
+ );
1448
1222
  break;
1449
1223
  }
1450
1224
  const currentTurnProgressSnapshot = {
1451
1225
  ...captureTurnWorkspaceSnapshot(plan.cwd),
1452
1226
  lastError: runtimeState.run?.lastError ?? null
1453
1227
  };
1454
- const turnProgress = evaluateTurnProgress(previousTurnProgressSnapshot, currentTurnProgressSnapshot);
1228
+ const turnProgress = evaluateTurnProgress(
1229
+ previousTurnProgressSnapshot,
1230
+ currentTurnProgressSnapshot
1231
+ );
1455
1232
  previousTurnProgressSnapshot = currentTurnProgressSnapshot;
1456
1233
  if (turnProgress.nonProductive) {
1457
1234
  consecutiveNonProductiveTurns += 1;
1458
- process.stderr.write(`[worker] non-productive turn detected (${consecutiveNonProductiveTurns}/${maxNonProductiveTurns})${turnProgress.reason ? `: ${turnProgress.reason}` : ""}
1459
- `);
1235
+ process.stderr.write(
1236
+ `[worker] non-productive turn detected (${consecutiveNonProductiveTurns}/${maxNonProductiveTurns})${turnProgress.reason ? `: ${turnProgress.reason}` : ""}
1237
+ `
1238
+ );
1460
1239
  } else {
1461
1240
  consecutiveNonProductiveTurns = 0;
1462
1241
  }
@@ -1465,13 +1244,17 @@ async function runCodexClientProtocol(child, plan, env, options) {
1465
1244
  if (runtimeState.run) {
1466
1245
  runtimeState.run.lastError = turnProgress.reason ? `convergence_detected: ${turnProgress.reason}` : "convergence_detected: repeated non-productive turn results";
1467
1246
  }
1468
- process.stderr.write(`[worker] convergence detected after ${consecutiveNonProductiveTurns} non-productive turns \u2014 exiting
1469
- `);
1247
+ process.stderr.write(
1248
+ `[worker] convergence detected after ${consecutiveNonProductiveTurns} non-productive turns \u2014 exiting
1249
+ `
1250
+ );
1470
1251
  break;
1471
1252
  }
1472
1253
  }
1473
- process.stderr.write(`[worker] multi-turn loop complete after ${turnCount} turn(s) \u2014 exiting worker
1474
- `);
1254
+ process.stderr.write(
1255
+ `[worker] multi-turn loop complete after ${turnCount} turn(s) \u2014 exiting worker
1256
+ `
1257
+ );
1475
1258
  runtimeState.runPhase = "finishing";
1476
1259
  runtimeState.status = userInputRequired || turnTerminalFailurePhase ? "failed" : "completed";
1477
1260
  runtimeState.runPhase = convergenceDetected ? "failed" : userInputRequired ? "failed" : turnTerminalFailurePhase ?? "succeeded";
@@ -1485,7 +1268,9 @@ async function runCodexClientProtocol(child, plan, env, options) {
1485
1268
  stopOrchestratorHeartbeatTimer();
1486
1269
  emitOrchestratorHeartbeat();
1487
1270
  await persistSessionTokenUsageArtifact(env);
1488
- await waitForPendingOrchestratorChannelFlush(resolveTerminalOrchestratorChannelFlushTimeoutMs());
1271
+ await waitForPendingOrchestratorChannelFlush(
1272
+ resolveTerminalOrchestratorChannelFlushTimeoutMs()
1273
+ );
1489
1274
  setTimeout(() => {
1490
1275
  process.exit(userInputRequired || turnTerminalFailurePhase ? 1 : 0);
1491
1276
  }, 1500);
@@ -1517,13 +1302,18 @@ async function runCodexClientProtocol(child, plan, env, options) {
1517
1302
  maxTurnsReached: false
1518
1303
  });
1519
1304
  if (activeTurnTelemetry) {
1520
- emitTurnFailedEvent(activeTurnTelemetry, runtimeState.run?.lastError ?? errMsg);
1305
+ emitTurnFailedEvent(
1306
+ activeTurnTelemetry,
1307
+ runtimeState.run?.lastError ?? errMsg
1308
+ );
1521
1309
  activeTurnTelemetry = null;
1522
1310
  }
1523
1311
  stopOrchestratorHeartbeatTimer();
1524
1312
  emitOrchestratorHeartbeat();
1525
1313
  await persistSessionTokenUsageArtifact(env);
1526
- await waitForPendingOrchestratorChannelFlush(resolveTerminalOrchestratorChannelFlushTimeoutMs());
1314
+ await waitForPendingOrchestratorChannelFlush(
1315
+ resolveTerminalOrchestratorChannelFlushTimeoutMs()
1316
+ );
1527
1317
  setTimeout(() => {
1528
1318
  process.exit(1);
1529
1319
  }, 1500);
@@ -1533,16 +1323,20 @@ function applyTokenUsageUpdate(source, tokenUsage) {
1533
1323
  runtimeState.tokenUsage.inputTokens = tokenUsage.inputTokens;
1534
1324
  runtimeState.tokenUsage.outputTokens = tokenUsage.outputTokens;
1535
1325
  runtimeState.tokenUsage.totalTokens = tokenUsage.totalTokens;
1536
- process.stderr.write(`[worker] token_usage source=${source} input=${tokenUsage.inputTokens} output=${tokenUsage.outputTokens} total=${tokenUsage.totalTokens}
1537
- `);
1326
+ process.stderr.write(
1327
+ `[worker] token_usage source=${source} input=${tokenUsage.inputTokens} output=${tokenUsage.outputTokens} total=${tokenUsage.totalTokens}
1328
+ `
1329
+ );
1538
1330
  }
1539
1331
  function applyRateLimitUpdate(source, rateLimits) {
1540
1332
  runtimeState.rateLimits = {
1541
1333
  ...rateLimits,
1542
1334
  source: "codex"
1543
1335
  };
1544
- process.stderr.write(`[worker] rate_limits source=${source} payload=${JSON.stringify(runtimeState.rateLimits).slice(0, 300)}
1545
- `);
1336
+ process.stderr.write(
1337
+ `[worker] rate_limits source=${source} payload=${JSON.stringify(runtimeState.rateLimits).slice(0, 300)}
1338
+ `
1339
+ );
1546
1340
  }
1547
1341
  function extractRateLimitPayload(value) {
1548
1342
  if (!value || typeof value !== "object") {
@@ -1673,22 +1467,23 @@ async function refreshTrackerState(env) {
1673
1467
  }
1674
1468
  try {
1675
1469
  const response = await fetch(`${orchestratorUrl}/api/v1/state`);
1676
- if (!response.ok)
1677
- return "unknown";
1470
+ if (!response.ok) return "unknown";
1678
1471
  const status = await response.json();
1679
- const isActive = status.activeRuns?.some((run) => run.issueIdentifier === issueIdentifier);
1472
+ const isActive = status.activeRuns?.some(
1473
+ (run) => run.issueIdentifier === issueIdentifier
1474
+ );
1680
1475
  return isActive ? "active" : "non-actionable";
1681
1476
  } catch {
1682
1477
  return "unknown";
1683
1478
  }
1684
1479
  }
1685
1480
  function runToolProcess(toolDef, inputJson) {
1686
- return new Promise((resolve2, reject) => {
1481
+ return new Promise((resolve, reject) => {
1687
1482
  const toolEnv = {
1688
1483
  ...process.env,
1689
1484
  ...toolDef.env
1690
1485
  };
1691
- const toolProc = spawn2(toolDef.command, toolDef.args, {
1486
+ const toolProc = spawn(toolDef.command, toolDef.args, {
1692
1487
  env: toolEnv,
1693
1488
  stdio: "pipe"
1694
1489
  });
@@ -1700,10 +1495,14 @@ function runToolProcess(toolDef, inputJson) {
1700
1495
  toolProc.once("exit", (code) => {
1701
1496
  const output = Buffer.concat(stdout).toString("utf8").trim();
1702
1497
  if (code === 0) {
1703
- resolve2(output || "{}");
1498
+ resolve(output || "{}");
1704
1499
  } else {
1705
1500
  const errOutput = Buffer.concat(stderr).toString("utf8").trim();
1706
- reject(new Error(`Tool exited with code ${code ?? "unknown"}: ${errOutput || output}`));
1501
+ reject(
1502
+ new Error(
1503
+ `Tool exited with code ${code ?? "unknown"}: ${errOutput || output}`
1504
+ )
1505
+ );
1707
1506
  }
1708
1507
  });
1709
1508
  toolProc.stdin?.write(inputJson);