@gh-symphony/cli 0.0.19 → 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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // ../core/dist/workflow/lifecycle.js
3
+ // ../core/src/workflow/lifecycle.ts
4
4
  var DEFAULT_WORKFLOW_LIFECYCLE = {
5
5
  stateFieldName: "Status",
6
6
  activeStates: ["Todo", "In Progress"],
@@ -21,8 +21,9 @@ function normalizeWorkflowState(state) {
21
21
  return state.trim().toLowerCase();
22
22
  }
23
23
 
24
- // ../core/dist/workflow/config.js
24
+ // ../core/src/workflow/config.ts
25
25
  var DEFAULT_CODEX_COMMAND = "codex app-server";
26
+ var DEFAULT_CLAUDE_COMMAND = "claude";
26
27
  var DEFAULT_AGENT_COMMAND = DEFAULT_CODEX_COMMAND;
27
28
  var DEFAULT_HOOK_TIMEOUT_MS = 6e4;
28
29
  var DEFAULT_POLL_INTERVAL_MS = 3e4;
@@ -84,6 +85,7 @@ var DEFAULT_WORKFLOW_DEFINITION = {
84
85
  workspace: DEFAULT_WORKFLOW_WORKSPACE,
85
86
  hooks: DEFAULT_WORKFLOW_HOOKS,
86
87
  agent: DEFAULT_WORKFLOW_AGENT,
88
+ runtime: null,
87
89
  codex: DEFAULT_WORKFLOW_CODEX,
88
90
  lifecycle: DEFAULT_WORKFLOW_LIFECYCLE,
89
91
  format: "default",
@@ -92,8 +94,20 @@ var DEFAULT_WORKFLOW_DEFINITION = {
92
94
  hookPath: null,
93
95
  maxConcurrentByState: {}
94
96
  };
97
+ function resolveWorkflowRuntimeCommand(workflow) {
98
+ if (!workflow.runtime) {
99
+ return workflow.codex.command;
100
+ }
101
+ if (workflow.runtime.args.length === 0) {
102
+ return workflow.runtime.command;
103
+ }
104
+ return [workflow.runtime.command, ...workflow.runtime.args].join(" ");
105
+ }
106
+ function resolveWorkflowRuntimeTimeouts(workflow) {
107
+ return workflow.runtime?.timeouts ?? workflow.codex;
108
+ }
95
109
 
96
- // ../core/dist/workflow/parser.js
110
+ // ../core/src/workflow/parser.ts
97
111
  function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
98
112
  const compatibilityMode = options.compatibilityMode ?? "strict";
99
113
  const frontMatterMatch = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
@@ -111,16 +125,39 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
111
125
  const workspace = readObject(frontMatter, "workspace");
112
126
  const hooks = readObject(frontMatter, "hooks");
113
127
  const agent = readObject(frontMatter, "agent");
114
- const codex = readRequiredObject(frontMatter, "codex");
128
+ const runtimeNode = readOptionalRuntimeObject(frontMatter);
129
+ const hasRuntime = runtimeNode !== null;
130
+ const codex = hasRuntime ? readObject(frontMatter, "codex") : readRequiredObject(frontMatter, "codex");
115
131
  const trackerKind = readRequiredString(tracker, "kind", env);
116
132
  const activeStates = readStringList(tracker, "active_states") ?? DEFAULT_WORKFLOW_TRACKER.activeStates;
117
133
  const terminalStates = readStringList(tracker, "terminal_states") ?? DEFAULT_WORKFLOW_TRACKER.terminalStates;
118
134
  const blockerCheckStates = readStringList(tracker, "blocker_check_states") ?? DEFAULT_WORKFLOW_TRACKER.blockerCheckStates;
119
- const maxConcurrentAgentsByState = readNumberMap(agent, "max_concurrent_agents_by_state");
120
- const command = readOptionalString(codex, "command", env) ?? DEFAULT_AGENT_COMMAND;
135
+ const maxConcurrentAgentsByState = readNumberMap(
136
+ agent,
137
+ "max_concurrent_agents_by_state"
138
+ );
139
+ const runtime = hasRuntime ? parseRuntimeConfig(runtimeNode, env) : null;
140
+ const codexConfig = {
141
+ command: readOptionalString(codex, "command", env) ?? DEFAULT_AGENT_COMMAND,
142
+ approvalPolicy: readOptionalString(codex, "approval_policy", env),
143
+ threadSandbox: readOptionalString(codex, "thread_sandbox", env),
144
+ turnSandboxPolicy: readOptionalString(codex, "turn_sandbox_policy", env),
145
+ turnTimeoutMs: readOptionalIntegerLike(codex, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
146
+ readTimeoutMs: readOptionalIntegerLike(codex, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
147
+ stallTimeoutMs: readOptionalIntegerLike(codex, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
148
+ };
149
+ const agentCommand = resolveWorkflowRuntimeCommand({
150
+ runtime,
151
+ codex: codexConfig
152
+ });
121
153
  const parsed = {
122
154
  promptTemplate,
123
- continuationGuidance: readOptionalWorkflowString(frontMatter, "continuationGuidance", "continuation_guidance", env),
155
+ continuationGuidance: readOptionalWorkflowString(
156
+ frontMatter,
157
+ "continuationGuidance",
158
+ "continuation_guidance",
159
+ env
160
+ ),
124
161
  tracker: {
125
162
  kind: trackerKind,
126
163
  endpoint: readOptionalString(tracker, "endpoint", env),
@@ -154,15 +191,8 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
154
191
  maxTurns: readOptionalIntegerLike(agent, "max_turns") ?? DEFAULT_MAX_TURNS,
155
192
  retryBaseDelayMs: readOptionalIntegerLike(agent, "retry_base_delay_ms") ?? DEFAULT_BASE_DELAY_MS
156
193
  },
157
- codex: {
158
- command,
159
- approvalPolicy: readOptionalString(codex, "approval_policy", env),
160
- threadSandbox: readOptionalString(codex, "thread_sandbox", env),
161
- turnSandboxPolicy: readOptionalString(codex, "turn_sandbox_policy", env),
162
- turnTimeoutMs: readOptionalIntegerLike(codex, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
163
- readTimeoutMs: readOptionalIntegerLike(codex, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
164
- stallTimeoutMs: readOptionalIntegerLike(codex, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
165
- },
194
+ runtime,
195
+ codex: codexConfig,
166
196
  lifecycle: {
167
197
  stateFieldName: readOptionalString(tracker, "state_field", env) ?? DEFAULT_WORKFLOW_TRACKER.stateFieldName,
168
198
  activeStates,
@@ -171,7 +201,7 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
171
201
  },
172
202
  format: "front-matter",
173
203
  githubProjectId: readOptionalString(tracker, "project_id", env),
174
- agentCommand: command,
204
+ agentCommand,
175
205
  hookPath: readOptionalString(hooks, "after_create", env),
176
206
  maxConcurrentByState: maxConcurrentAgentsByState
177
207
  };
@@ -209,17 +239,25 @@ function parseBlock(lines, startIndex, indent) {
209
239
  break;
210
240
  }
211
241
  if (lineIndent > indent) {
212
- throw new Error(`Invalid workflow front matter indentation near "${line.trim()}".`);
242
+ throw new Error(
243
+ `Invalid workflow front matter indentation near "${line.trim()}".`
244
+ );
213
245
  }
214
246
  const trimmed = line.trim();
215
247
  if (trimmed.startsWith("- ")) {
216
248
  if (collectionType === "object") {
217
- throw new Error("Cannot mix array and object values in workflow front matter.");
249
+ throw new Error(
250
+ "Cannot mix array and object values in workflow front matter."
251
+ );
218
252
  }
219
253
  collectionType = "array";
220
254
  const itemText = trimmed.slice(2).trim();
221
255
  if (itemText === "|" || itemText === "|-") {
222
- const [multiline, nextIndex3] = parseMultilineScalar(lines, index + 1, indent + 2);
256
+ const [multiline, nextIndex3] = parseMultilineScalar(
257
+ lines,
258
+ index + 1,
259
+ indent + 2
260
+ );
223
261
  arrayValues.push(multiline);
224
262
  index = nextIndex3;
225
263
  continue;
@@ -235,7 +273,9 @@ function parseBlock(lines, startIndex, indent) {
235
273
  continue;
236
274
  }
237
275
  if (collectionType === "array") {
238
- throw new Error("Cannot mix object and array values in workflow front matter.");
276
+ throw new Error(
277
+ "Cannot mix object and array values in workflow front matter."
278
+ );
239
279
  }
240
280
  collectionType = "object";
241
281
  const separatorIndex = trimmed.indexOf(":");
@@ -245,7 +285,11 @@ function parseBlock(lines, startIndex, indent) {
245
285
  const key = trimmed.slice(0, separatorIndex).trim();
246
286
  const remainder = trimmed.slice(separatorIndex + 1).trim();
247
287
  if (remainder === "|" || remainder === "|-") {
248
- const [multiline, nextIndex2] = parseMultilineScalar(lines, index + 1, indent + 2);
288
+ const [multiline, nextIndex2] = parseMultilineScalar(
289
+ lines,
290
+ index + 1,
291
+ indent + 2
292
+ );
249
293
  objectValues[key] = multiline;
250
294
  index = nextIndex2;
251
295
  continue;
@@ -284,29 +328,131 @@ function countIndent(line) {
284
328
  return line.match(/^ */)?.[0].length ?? 0;
285
329
  }
286
330
  function parseScalar(value) {
287
- if (value === "null")
288
- return null;
289
- if (value === "true")
290
- return true;
291
- if (value === "false")
292
- return false;
293
- if (/^-?\d+$/.test(value))
294
- return Number.parseInt(value, 10);
331
+ if (value === "null") return null;
332
+ if (value === "true") return true;
333
+ if (value === "false") return false;
334
+ if (value.startsWith("[") && value.endsWith("]")) {
335
+ return parseInlineArray(value);
336
+ }
337
+ if (/^-?\d+$/.test(value)) return Number.parseInt(value, 10);
295
338
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
296
339
  return value.slice(1, -1);
297
340
  }
298
341
  return value;
299
342
  }
300
- function readObject(input, key) {
343
+ function parseInlineArray(value) {
344
+ const inner = value.slice(1, -1).trim();
345
+ if (!inner) {
346
+ return [];
347
+ }
348
+ return splitInlineArrayEntries(inner).map((entry) => parseScalar(entry));
349
+ }
350
+ function splitInlineArrayEntries(inner) {
351
+ const entries = [];
352
+ let current = "";
353
+ let quote = null;
354
+ for (const char of inner) {
355
+ if (quote) {
356
+ current += char;
357
+ if (char === quote) {
358
+ quote = null;
359
+ }
360
+ continue;
361
+ }
362
+ if (char === '"' || char === "'") {
363
+ quote = char;
364
+ current += char;
365
+ continue;
366
+ }
367
+ if (char === ",") {
368
+ pushInlineArrayEntry(entries, current, "middle");
369
+ current = "";
370
+ continue;
371
+ }
372
+ current += char;
373
+ }
374
+ if (quote) {
375
+ throw new Error("Workflow front matter inline array has an unterminated string.");
376
+ }
377
+ pushInlineArrayEntry(entries, current, "end");
378
+ return entries;
379
+ }
380
+ function pushInlineArrayEntry(entries, entry, position) {
381
+ const trimmed = entry.trim();
382
+ if (!trimmed) {
383
+ const reason = position === "end" ? "has a trailing comma" : "contains an empty item";
384
+ throw new Error(`Workflow front matter inline array ${reason}.`);
385
+ }
386
+ entries.push(trimmed);
387
+ }
388
+ function parseRuntimeConfig(runtime, env) {
389
+ const kind = readRuntimeKind(runtime, env);
390
+ const isolation = readObject(runtime, "isolation", "runtime.isolation");
391
+ const auth = readObject(runtime, "auth", "runtime.auth");
392
+ const timeouts = readObject(runtime, "timeouts", "runtime.timeouts");
393
+ const configuredCommand = readOptionalString(runtime, "command", env);
394
+ const command = configuredCommand ?? defaultRuntimeCommand(kind);
395
+ if (!command) {
396
+ throw new Error(
397
+ 'Workflow front matter field "runtime.command" is required for runtime.kind "custom".'
398
+ );
399
+ }
400
+ return {
401
+ kind,
402
+ command,
403
+ args: readRuntimeArgs(runtime),
404
+ isolation: {
405
+ bare: readOptionalBoolean(isolation, "bare", "runtime.isolation.bare") ?? false,
406
+ strictMcpConfig: readOptionalBoolean(
407
+ isolation,
408
+ "strict_mcp_config",
409
+ "runtime.isolation.strict_mcp_config"
410
+ ) ?? false
411
+ },
412
+ auth: {
413
+ env: readOptionalString(auth, "env", env)
414
+ },
415
+ timeouts: {
416
+ turnTimeoutMs: readOptionalIntegerLike(timeouts, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
417
+ readTimeoutMs: readOptionalIntegerLike(timeouts, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
418
+ stallTimeoutMs: readOptionalIntegerLike(timeouts, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
419
+ }
420
+ };
421
+ }
422
+ function readRuntimeKind(runtime, env) {
423
+ const kind = readRequiredString(runtime, "kind", env);
424
+ if (kind === "codex-app-server" || kind === "claude-print" || kind === "custom") {
425
+ return kind;
426
+ }
427
+ throw new Error(
428
+ `Unsupported workflow runtime kind "${kind}". Supported values: codex-app-server, claude-print, custom.`
429
+ );
430
+ }
431
+ function defaultRuntimeCommand(kind) {
432
+ if (kind === "claude-print") {
433
+ return DEFAULT_CLAUDE_COMMAND;
434
+ }
435
+ if (kind === "codex-app-server") {
436
+ return DEFAULT_AGENT_COMMAND;
437
+ }
438
+ return null;
439
+ }
440
+ function readObject(input, key, path = key) {
301
441
  const value = input[key];
302
442
  if (value === void 0 || value === null) {
303
443
  return {};
304
444
  }
305
445
  if (typeof value !== "object" || Array.isArray(value)) {
306
- throw new Error(`Workflow front matter field "${key}" must be an object.`);
446
+ throw new Error(`Workflow front matter field "${path}" must be an object.`);
307
447
  }
308
448
  return value;
309
449
  }
450
+ function readOptionalRuntimeObject(input) {
451
+ if (input.runtime === void 0 || input.runtime === null) {
452
+ return null;
453
+ }
454
+ return readObject(input, "runtime");
455
+ }
310
456
  function readRequiredObject(input, key) {
311
457
  if (!(key in input)) {
312
458
  throw new Error(`Workflow front matter field "${key}" is required.`);
@@ -342,7 +488,31 @@ function readStringList(input, key) {
342
488
  return value.split(",").map((entry) => entry.trim()).filter(Boolean);
343
489
  }
344
490
  if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
345
- throw new Error(`Workflow front matter field "${key}" must be an array of strings or comma-separated string.`);
491
+ throw new Error(
492
+ `Workflow front matter field "${key}" must be an array of strings or comma-separated string.`
493
+ );
494
+ }
495
+ return value;
496
+ }
497
+ function readRuntimeArgs(input) {
498
+ const value = input.args;
499
+ if (value === void 0 || value === null) {
500
+ return [];
501
+ }
502
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
503
+ throw new Error(
504
+ 'Workflow front matter field "runtime.args" must be an array of strings.'
505
+ );
506
+ }
507
+ return value;
508
+ }
509
+ function readOptionalBoolean(input, key, path = key) {
510
+ const value = input[key];
511
+ if (value === void 0 || value === null) {
512
+ return null;
513
+ }
514
+ if (typeof value !== "boolean") {
515
+ throw new Error(`Workflow front matter field "${path}" must be a boolean.`);
346
516
  }
347
517
  return value;
348
518
  }
@@ -377,7 +547,9 @@ function readNumberMap(input, key) {
377
547
  result[entryKey] = Number.parseInt(entryValue, 10);
378
548
  continue;
379
549
  }
380
- throw new Error(`Workflow front matter field "${key}.${entryKey}" must be an integer.`);
550
+ throw new Error(
551
+ `Workflow front matter field "${key}.${entryKey}" must be an integer.`
552
+ );
381
553
  }
382
554
  return result;
383
555
  }
@@ -386,27 +558,39 @@ function resolveEnvironmentValue(value, env) {
386
558
  if (value.startsWith("env:") && envTokenMatch) {
387
559
  const resolved = env[envTokenMatch[1]];
388
560
  if (!resolved) {
389
- throw new Error(`Workflow front matter requires environment variable ${envTokenMatch[1]}.`);
561
+ throw new Error(
562
+ `Workflow front matter requires environment variable ${envTokenMatch[1]}.`
563
+ );
390
564
  }
391
565
  return resolved;
392
566
  }
393
567
  return value.replace(/\$\{([A-Z0-9_]+)\}/g, (_, name) => {
394
568
  const resolved = env[name];
395
569
  if (!resolved) {
396
- throw new Error(`Workflow front matter requires environment variable ${name}.`);
570
+ throw new Error(
571
+ `Workflow front matter requires environment variable ${name}.`
572
+ );
397
573
  }
398
574
  return resolved;
399
575
  });
400
576
  }
401
577
  function matchOptionalSection(markdown, heading) {
402
578
  const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
403
- const pattern = new RegExp(`## ${escapedHeading}\\n\\n([\\s\\S]*?)(?=\\n## |$)`);
579
+ const pattern = new RegExp(
580
+ `## ${escapedHeading}\\n\\n([\\s\\S]*?)(?=\\n## |$)`
581
+ );
404
582
  const match = markdown.match(pattern);
405
583
  return match?.[1]?.trim() ?? null;
406
584
  }
407
585
 
408
- // ../core/dist/workflow/render.js
409
- import { Liquid, ParseError, RenderError, TokenizationError, UndefinedVariableError } from "liquidjs";
586
+ // ../core/src/workflow/render.ts
587
+ import {
588
+ Liquid,
589
+ ParseError,
590
+ RenderError,
591
+ TokenizationError,
592
+ UndefinedVariableError
593
+ } from "liquidjs";
410
594
  function buildPromptVariables(issue, options) {
411
595
  return {
412
596
  issue: {
@@ -459,7 +643,10 @@ function flattenVariables(obj, prefix = "") {
459
643
  for (const [key, value] of Object.entries(obj)) {
460
644
  const fullKey = prefix ? `${prefix}.${key}` : key;
461
645
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
462
- for (const [nestedKey, nestedValue] of flattenVariables(value, fullKey)) {
646
+ for (const [nestedKey, nestedValue] of flattenVariables(
647
+ value,
648
+ fullKey
649
+ )) {
463
650
  result.set(nestedKey, nestedValue);
464
651
  }
465
652
  } else {
@@ -470,19 +657,22 @@ function flattenVariables(obj, prefix = "") {
470
657
  }
471
658
  function renderLegacyPrompt(template, variables) {
472
659
  const flatVars = flattenVariables(variables);
473
- return template.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_.]*)\}\}/g, (match, key) => {
474
- const value = flatVars.get(key);
475
- if (value === void 0) {
476
- return match;
477
- }
478
- if (value === null) {
479
- return "";
660
+ return template.replace(
661
+ /\{\{([a-zA-Z_][a-zA-Z0-9_.]*)\}\}/g,
662
+ (match, key) => {
663
+ const value = flatVars.get(key);
664
+ if (value === void 0) {
665
+ return match;
666
+ }
667
+ if (value === null) {
668
+ return "";
669
+ }
670
+ return String(value);
480
671
  }
481
- return String(value);
482
- });
672
+ );
483
673
  }
484
674
 
485
- // ../core/dist/contracts/status-surface.js
675
+ // ../core/src/contracts/status-surface.ts
486
676
  var WORKFLOW_EXECUTION_PHASES = [
487
677
  "planning",
488
678
  "human-review",
@@ -506,7 +696,7 @@ function isSessionExitClassification(value) {
506
696
  return typeof value === "string" && SESSION_EXIT_CLASSIFICATIONS.includes(value);
507
697
  }
508
698
 
509
- // ../core/dist/contracts/run-attempt-phase.js
699
+ // ../core/src/contracts/run-attempt-phase.ts
510
700
  var RUN_ATTEMPT_PHASES = [
511
701
  "preparing_workspace",
512
702
  "building_prompt",
@@ -524,7 +714,7 @@ function isRunAttemptPhase(value) {
524
714
  return typeof value === "string" && RUN_ATTEMPT_PHASES.includes(value);
525
715
  }
526
716
 
527
- // ../core/dist/contracts/orchestrator-channel.js
717
+ // ../core/src/contracts/orchestrator-channel.ts
528
718
  function isRecord(value) {
529
719
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
530
720
  }
@@ -616,7 +806,7 @@ function isOrchestratorChannelEvent(value) {
616
806
  return false;
617
807
  }
618
808
 
619
- // ../core/dist/workflow/loader.js
809
+ // ../core/src/workflow/loader.ts
620
810
  import { createHash } from "crypto";
621
811
  import { access, readFile, stat } from "fs/promises";
622
812
  import { constants } from "fs";
@@ -683,7 +873,7 @@ function toWorkflowResolution(workflowPath, workflow, metadata) {
683
873
  };
684
874
  }
685
875
 
686
- // ../core/dist/workflow/exit-classification.js
876
+ // ../core/src/workflow/exit-classification.ts
687
877
  function classifySessionExit(params) {
688
878
  if (params.userInputRequired) {
689
879
  return "user-input-required";
@@ -706,7 +896,7 @@ function classifySessionExit(params) {
706
896
  return "error";
707
897
  }
708
898
 
709
- // ../core/dist/orchestration/retry-policy.js
899
+ // ../core/src/orchestration/retry-policy.ts
710
900
  function calculateRetryDelay(attempt, options = {}) {
711
901
  const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
712
902
  const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
@@ -718,7 +908,100 @@ function scheduleRetryAt(now, attempt, options = {}) {
718
908
  return new Date(now.getTime() + calculateRetryDelay(attempt, options));
719
909
  }
720
910
 
721
- // ../core/dist/workspace/env-file.js
911
+ // ../core/src/runtime/credentials.ts
912
+ import { readFile as readFile2, writeFile } from "fs/promises";
913
+ var TOKEN_REUSE_WINDOW_MS = 60 * 1e3;
914
+ var CODEX_ENV_KEYS = [
915
+ "OPENAI_API_KEY",
916
+ "OPENAI_BASE_URL",
917
+ "OPENAI_ORG_ID",
918
+ "OPENAI_PROJECT"
919
+ ];
920
+ function extractEnvForCodex(env) {
921
+ return pickRuntimeEnv(env, CODEX_ENV_KEYS);
922
+ }
923
+ function toAgentCredentialCacheEntry(brokerResponse, now = /* @__PURE__ */ new Date()) {
924
+ return {
925
+ env: brokerResponse.env,
926
+ expires_at: brokerResponse.expires_at,
927
+ cachedAt: now.toISOString()
928
+ };
929
+ }
930
+ function shouldReuseAgentCredentialCache(entry, now = /* @__PURE__ */ new Date()) {
931
+ if (Object.keys(entry.env).length === 0) {
932
+ return false;
933
+ }
934
+ if (!entry.expires_at) {
935
+ return true;
936
+ }
937
+ const expiresAt = Date.parse(entry.expires_at);
938
+ if (Number.isNaN(expiresAt)) {
939
+ return false;
940
+ }
941
+ return expiresAt - now.getTime() > TOKEN_REUSE_WINDOW_MS;
942
+ }
943
+ async function readAgentCredentialCache(path, readFileImpl = readFile2) {
944
+ try {
945
+ return normalizeAgentCredentialCacheEntry(
946
+ JSON.parse(await readFileImpl(path, "utf8"))
947
+ );
948
+ } catch {
949
+ return null;
950
+ }
951
+ }
952
+ async function writeAgentCredentialCache(path, brokerResponse, writeFileImpl = writeFile, now = /* @__PURE__ */ new Date()) {
953
+ const entry = toAgentCredentialCacheEntry(brokerResponse, now);
954
+ await writeFileImpl(path, JSON.stringify(entry), "utf8");
955
+ return entry;
956
+ }
957
+ function pickRuntimeEnv(env, keys) {
958
+ const resolved = {};
959
+ for (const key of keys) {
960
+ const value = env[key];
961
+ if (value) {
962
+ resolved[key] = value;
963
+ }
964
+ }
965
+ return resolved;
966
+ }
967
+ function normalizeAgentCredentialCacheEntry(payload) {
968
+ if (!isRecord2(payload)) {
969
+ return null;
970
+ }
971
+ if (!isRecord2(payload.env)) {
972
+ return null;
973
+ }
974
+ const env = Object.fromEntries(
975
+ Object.entries(payload.env).filter(
976
+ (entry) => typeof entry[1] === "string"
977
+ )
978
+ );
979
+ if (Object.keys(env).length === 0) {
980
+ return null;
981
+ }
982
+ return {
983
+ env,
984
+ expires_at: typeof payload.expires_at === "string" ? payload.expires_at : void 0,
985
+ cachedAt: typeof payload.cachedAt === "string" ? payload.cachedAt : (/* @__PURE__ */ new Date(0)).toISOString()
986
+ };
987
+ }
988
+ function isRecord2(value) {
989
+ return value !== null && typeof value === "object";
990
+ }
991
+
992
+ // ../core/src/runtime/events.ts
993
+ var DEFAULT_AGENT_INPUT_REQUIRED_REASON = "turn_input_required: agent requires user input";
994
+ function buildAgentInputRequiredReason(prompt) {
995
+ if (typeof prompt === "string") {
996
+ const trimmedPrompt = prompt.trim();
997
+ if (trimmedPrompt) {
998
+ return `turn_input_required: ${trimmedPrompt}`;
999
+ }
1000
+ }
1001
+ return DEFAULT_AGENT_INPUT_REQUIRED_REASON;
1002
+ }
1003
+
1004
+ // ../core/src/workspace/env-file.ts
722
1005
  import { existsSync, readFileSync } from "fs";
723
1006
  function readEnvFile(path) {
724
1007
  if (!existsSync(path)) {
@@ -735,10 +1018,10 @@ function readEnvFile(path) {
735
1018
  }, {});
736
1019
  }
737
1020
 
738
- // ../core/dist/workspace/safety.js
1021
+ // ../core/src/workspace/safety.ts
739
1022
  import { resolve } from "path";
740
1023
 
741
- // ../core/dist/workspace/identity.js
1024
+ // ../core/src/workspace/identity.ts
742
1025
  import { resolve as resolve2, join } from "path";
743
1026
  import { createHash as createHash2 } from "crypto";
744
1027
  function deriveIssueWorkspaceKey(identity, issueIdentifier) {
@@ -766,12 +1049,14 @@ function resolveIssueWorkspaceDirectory(projectDirectory, workspaceKey) {
766
1049
  const normalizedProjectDirectory = resolve2(projectDirectory);
767
1050
  const candidate = resolve2(normalizedProjectDirectory, "issues", workspaceKey);
768
1051
  if (!candidate.startsWith(`${normalizedProjectDirectory}/`)) {
769
- throw new Error("Issue workspace path escapes the configured project directory.");
1052
+ throw new Error(
1053
+ "Issue workspace path escapes the configured project directory."
1054
+ );
770
1055
  }
771
1056
  return candidate;
772
1057
  }
773
1058
 
774
- // ../core/dist/workspace/hooks.js
1059
+ // ../core/src/workspace/hooks.ts
775
1060
  import { spawn } from "child_process";
776
1061
  var DEFAULT_HOOK_TIMEOUT_MS2 = 6e4;
777
1062
  async function executeHook(options) {
@@ -906,10 +1191,20 @@ function normalizeHookCommand(command) {
906
1191
  return command;
907
1192
  }
908
1193
 
909
- // ../core/dist/observability/snapshot-builder.js
1194
+ // ../core/src/observability/snapshot-builder.ts
910
1195
  function buildProjectSnapshot(input) {
911
- const { project, activeRuns, allRuns, summary, lastTickAt, lastError, rateLimits } = input;
912
- const cumulativeTokenUsageByIssue = aggregateTokenUsageByIssue(allRuns ?? activeRuns);
1196
+ const {
1197
+ project,
1198
+ activeRuns,
1199
+ allRuns,
1200
+ summary,
1201
+ lastTickAt,
1202
+ lastError,
1203
+ rateLimits
1204
+ } = input;
1205
+ const cumulativeTokenUsageByIssue = aggregateTokenUsageByIssue(
1206
+ allRuns ?? activeRuns
1207
+ );
913
1208
  return {
914
1209
  projectId: project.projectId,
915
1210
  slug: project.slug,
@@ -941,7 +1236,10 @@ function buildProjectSnapshot(input) {
941
1236
  lastEventAt: run.lastEventAt ?? null,
942
1237
  executionPhase: run.executionPhase ?? null,
943
1238
  runPhase: run.runPhase ?? null,
944
- tokenUsage: attachCumulativeTokenUsage(run.tokenUsage, cumulativeTokenUsageByIssue.get(run.issueId))
1239
+ tokenUsage: attachCumulativeTokenUsage(
1240
+ run.tokenUsage,
1241
+ cumulativeTokenUsageByIssue.get(run.issueId)
1242
+ )
945
1243
  })),
946
1244
  retryQueue: activeRuns.filter((run) => run.status === "retrying" && run.retryKind).map((run) => ({
947
1245
  runId: run.runId,
@@ -1010,11 +1308,11 @@ function aggregateTokenUsage(runs, lastTickAt) {
1010
1308
  return { inputTokens, outputTokens, totalTokens, secondsRunning };
1011
1309
  }
1012
1310
 
1013
- // ../core/dist/observability/fs-reader.js
1014
- import { readFile as readFile2, readdir } from "fs/promises";
1311
+ // ../core/src/observability/fs-reader.ts
1312
+ import { readFile as readFile3, readdir } from "fs/promises";
1015
1313
  async function readJsonFile(path) {
1016
1314
  try {
1017
- const raw = await readFile2(path, "utf8");
1315
+ const raw = await readFile3(path, "utf8");
1018
1316
  return JSON.parse(raw);
1019
1317
  } catch (error) {
1020
1318
  if (isFileMissing(error)) {
@@ -1034,10 +1332,12 @@ async function safeReadDir(path) {
1034
1332
  }
1035
1333
  }
1036
1334
  function isFileMissing(error) {
1037
- return Boolean(error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"));
1335
+ return Boolean(
1336
+ error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")
1337
+ );
1038
1338
  }
1039
1339
 
1040
- // ../core/dist/observability/event-formatter.js
1340
+ // ../core/src/observability/event-formatter.ts
1041
1341
  function formatEventMessage(event) {
1042
1342
  switch (event.event) {
1043
1343
  case "run-dispatched":
@@ -1064,6 +1364,8 @@ function formatEventMessage(event) {
1064
1364
  return `Turn ${event.turnCount} completed in ${event.durationMs}ms`;
1065
1365
  case "turn_failed":
1066
1366
  return event.error ?? `Turn ${event.turnCount} failed`;
1367
+ case "session_invalidated":
1368
+ return event.reason;
1067
1369
  default:
1068
1370
  return null;
1069
1371
  }
@@ -1102,9 +1404,11 @@ function parseRunEventLine(line) {
1102
1404
  }
1103
1405
  }
1104
1406
 
1105
- // ../core/dist/observability/status-assembler.js
1407
+ // ../core/src/observability/status-assembler.ts
1106
1408
  function isMatchingIssueRun(run, projectId, issueId, issueIdentifier) {
1107
- return Boolean(run && run.projectId === projectId && (run.issueId === issueId || run.issueIdentifier === issueIdentifier));
1409
+ return Boolean(
1410
+ run && run.projectId === projectId && (run.issueId === issueId || run.issueIdentifier === issueIdentifier)
1411
+ );
1108
1412
  }
1109
1413
  function mapIssueOrchestrationStateToStatus(state) {
1110
1414
  switch (state) {
@@ -1123,6 +1427,593 @@ function mapIssueOrchestrationStateToStatus(state) {
1123
1427
  }
1124
1428
  }
1125
1429
 
1430
+ // ../runtime-claude/src/preflight.ts
1431
+ import { execFileSync } from "child_process";
1432
+ import { constants as constants2 } from "fs";
1433
+ import { access as access2, readFile as readFile4 } from "fs/promises";
1434
+ import { isAbsolute, join as join2, resolve as resolve3 } from "path";
1435
+ var DEFAULT_DEPENDENCIES = {
1436
+ execFileSync,
1437
+ readFile: readFile4,
1438
+ access: access2,
1439
+ fetchImpl: fetch,
1440
+ platform: process.platform
1441
+ };
1442
+ var CREDENTIAL_BROKER_TIMEOUT_MS = 5e3;
1443
+ async function runClaudePreflight(options, dependencies = {}) {
1444
+ const deps = { ...DEFAULT_DEPENDENCIES, ...dependencies };
1445
+ const env = options.env ?? process.env;
1446
+ const command = resolveRuntimeCommandBinary(options.command) ?? "claude";
1447
+ const checks = [];
1448
+ checks.push(checkClaudeBinary(command, options.cwd, deps));
1449
+ checks.push(await checkAnthropicApiKey(env, options, deps));
1450
+ checks.push(await checkWorkspaceMcpConfig(options.cwd, deps));
1451
+ if (options.includeGhAuth) {
1452
+ checks.push(checkGhAuthentication(options.cwd, deps));
1453
+ }
1454
+ return {
1455
+ ok: checks.every((check) => check.status !== "fail"),
1456
+ checks
1457
+ };
1458
+ }
1459
+ function formatClaudePreflightText(report) {
1460
+ const lines = ["Claude runtime preflight"];
1461
+ for (const check of report.checks) {
1462
+ const label = check.status === "pass" ? "PASS" : check.status === "warn" ? "WARN" : "FAIL";
1463
+ lines.push(`${label} ${check.title}`);
1464
+ lines.push(` ${check.summary}`);
1465
+ if (check.remediation) {
1466
+ lines.push(` Fix: ${check.remediation}`);
1467
+ }
1468
+ }
1469
+ return lines.join("\n");
1470
+ }
1471
+ function pass(id, title, summary, details) {
1472
+ return { id, title, status: "pass", summary, details };
1473
+ }
1474
+ function warn(id, title, summary, remediation, details) {
1475
+ return { id, title, status: "warn", summary, remediation, details };
1476
+ }
1477
+ function fail(id, title, summary, remediation, details) {
1478
+ return { id, title, status: "fail", summary, remediation, details };
1479
+ }
1480
+ function checkClaudeBinary(command, cwd, deps) {
1481
+ const executable = resolveExecutableCommand(command, cwd);
1482
+ try {
1483
+ const locatedPath = locateClaudeBinary(executable, cwd, deps);
1484
+ const version = deps.execFileSync(executable, ["--version"], {
1485
+ encoding: "utf8",
1486
+ cwd,
1487
+ stdio: ["pipe", "pipe", "pipe"]
1488
+ }).toString().trim();
1489
+ return pass(
1490
+ "claude_binary",
1491
+ "Claude CLI binary",
1492
+ version ? `${executable} is available: ${version}.` : `${executable} is available, but --version returned an empty response.`,
1493
+ { command: executable, path: locatedPath, version }
1494
+ );
1495
+ } catch (error) {
1496
+ return fail(
1497
+ "claude_binary",
1498
+ "Claude CLI binary",
1499
+ `${executable} could not be found or executed from PATH.`,
1500
+ `Install Claude Code and ensure '${executable}' is on PATH, then re-run the command.`,
1501
+ {
1502
+ command: executable,
1503
+ error: error instanceof Error ? error.message : String(error)
1504
+ }
1505
+ );
1506
+ }
1507
+ }
1508
+ function resolveExecutableCommand(command, cwd) {
1509
+ if ((command.includes("/") || command.includes("\\")) && !isAbsolute(command)) {
1510
+ return resolve3(cwd, command);
1511
+ }
1512
+ return command;
1513
+ }
1514
+ function locateClaudeBinary(command, cwd, deps) {
1515
+ if (command.includes("/") || command.includes("\\")) {
1516
+ return command;
1517
+ }
1518
+ try {
1519
+ const locator = deps.platform === "win32" ? "where" : "which";
1520
+ return deps.execFileSync(locator, [command], {
1521
+ encoding: "utf8",
1522
+ cwd,
1523
+ stdio: ["pipe", "pipe", "pipe"]
1524
+ }).toString().split(/\r?\n/).find((line) => line.trim())?.trim() ?? null;
1525
+ } catch {
1526
+ return null;
1527
+ }
1528
+ }
1529
+ function checkGhAuthentication(cwd, deps) {
1530
+ try {
1531
+ deps.execFileSync("gh", ["auth", "status"], {
1532
+ encoding: "utf8",
1533
+ cwd,
1534
+ stdio: ["pipe", "pipe", "pipe"]
1535
+ });
1536
+ return pass(
1537
+ "gh_authentication",
1538
+ "GitHub CLI authentication",
1539
+ "gh auth status succeeded."
1540
+ );
1541
+ } catch (error) {
1542
+ return fail(
1543
+ "gh_authentication",
1544
+ "GitHub CLI authentication",
1545
+ "gh auth status failed or no GitHub login is configured.",
1546
+ "Run 'gh auth login --scopes repo,read:org,project' and re-run the command.",
1547
+ { error: error instanceof Error ? error.message : String(error) }
1548
+ );
1549
+ }
1550
+ }
1551
+ async function checkAnthropicApiKey(env, options, deps) {
1552
+ if (env.ANTHROPIC_API_KEY?.trim()) {
1553
+ return pass(
1554
+ "anthropic_api_key",
1555
+ "Anthropic API key",
1556
+ "ANTHROPIC_API_KEY is configured in the environment.",
1557
+ { source: "env" }
1558
+ );
1559
+ }
1560
+ const brokerUrl = env.AGENT_CREDENTIAL_BROKER_URL?.trim();
1561
+ const brokerSecret = env.AGENT_CREDENTIAL_BROKER_SECRET?.trim();
1562
+ if (!brokerUrl || !brokerSecret) {
1563
+ return fail(
1564
+ "anthropic_api_key",
1565
+ "Anthropic API key",
1566
+ "Neither ANTHROPIC_API_KEY nor an agent credential broker is configured.",
1567
+ "Set ANTHROPIC_API_KEY or configure AGENT_CREDENTIAL_BROKER_URL and AGENT_CREDENTIAL_BROKER_SECRET.",
1568
+ { source: "missing" }
1569
+ );
1570
+ }
1571
+ if (options.probeCredentialBroker === false) {
1572
+ return pass(
1573
+ "anthropic_api_key",
1574
+ "Anthropic API key",
1575
+ "Agent credential broker configuration is present.",
1576
+ { source: "broker", brokerUrl }
1577
+ );
1578
+ }
1579
+ try {
1580
+ const response = await deps.fetchImpl(brokerUrl, {
1581
+ method: "POST",
1582
+ headers: {
1583
+ accept: "application/json",
1584
+ authorization: `Bearer ${brokerSecret}`
1585
+ },
1586
+ signal: AbortSignal.timeout(CREDENTIAL_BROKER_TIMEOUT_MS)
1587
+ });
1588
+ const payload = await response.json();
1589
+ if (response.ok && payload.env?.ANTHROPIC_API_KEY?.trim()) {
1590
+ return pass(
1591
+ "anthropic_api_key",
1592
+ "Anthropic API key",
1593
+ "Agent credential broker is reachable and returned ANTHROPIC_API_KEY.",
1594
+ { source: "broker", brokerUrl }
1595
+ );
1596
+ }
1597
+ return fail(
1598
+ "anthropic_api_key",
1599
+ "Anthropic API key",
1600
+ payload.error ? `Agent credential broker did not return ANTHROPIC_API_KEY: ${payload.error}.` : "Agent credential broker did not return ANTHROPIC_API_KEY.",
1601
+ "Set ANTHROPIC_API_KEY or configure the credential broker to return ANTHROPIC_API_KEY.",
1602
+ { source: "broker", brokerUrl, status: response.status }
1603
+ );
1604
+ } catch (error) {
1605
+ return fail(
1606
+ "anthropic_api_key",
1607
+ "Anthropic API key",
1608
+ "Agent credential broker could not be reached.",
1609
+ "Set ANTHROPIC_API_KEY or fix AGENT_CREDENTIAL_BROKER_URL and AGENT_CREDENTIAL_BROKER_SECRET.",
1610
+ {
1611
+ source: "broker",
1612
+ brokerUrl,
1613
+ error: error instanceof Error ? error.message : String(error)
1614
+ }
1615
+ );
1616
+ }
1617
+ }
1618
+ async function checkWorkspaceMcpConfig(cwd, deps) {
1619
+ const path = join2(cwd, ".mcp.json");
1620
+ try {
1621
+ await deps.access(path, constants2.R_OK);
1622
+ } catch (error) {
1623
+ const err = error;
1624
+ if (err.code === "ENOENT") {
1625
+ return warn(
1626
+ "claude_mcp_config",
1627
+ "Workspace .mcp.json",
1628
+ ".mcp.json was not found in the workspace root. Claude can still run without a workspace MCP config.",
1629
+ void 0,
1630
+ { path, reason: "missing" }
1631
+ );
1632
+ }
1633
+ return warn(
1634
+ "claude_mcp_config",
1635
+ "Workspace .mcp.json",
1636
+ `.mcp.json exists but is not readable: ${err.message}.`,
1637
+ "Fix .mcp.json file permissions if this workspace needs Claude MCP servers.",
1638
+ { path, reason: "unreadable", error: err.message }
1639
+ );
1640
+ }
1641
+ try {
1642
+ const content = await deps.readFile(path, "utf8");
1643
+ JSON.parse(content);
1644
+ return pass(
1645
+ "claude_mcp_config",
1646
+ "Workspace .mcp.json",
1647
+ ".mcp.json is readable and contains valid JSON.",
1648
+ { path }
1649
+ );
1650
+ } catch (error) {
1651
+ return fail(
1652
+ "claude_mcp_config",
1653
+ "Workspace .mcp.json",
1654
+ `.mcp.json could not be parsed as JSON: ${error instanceof Error ? error.message : String(error)}.`,
1655
+ "Fix or remove the workspace root .mcp.json file, then re-run the command.",
1656
+ { path, reason: "invalid_json" }
1657
+ );
1658
+ }
1659
+ }
1660
+ function isClaudeRuntimeCommand(command) {
1661
+ return resolveClaudeCommandBinary(command) != null;
1662
+ }
1663
+ function resolveClaudeCommandBinary(command) {
1664
+ const binary = resolveRuntimeCommandBinary(command);
1665
+ return binary != null && isClaudeBinaryName(binary) ? binary : null;
1666
+ }
1667
+ function resolveRuntimeCommandBinary(command) {
1668
+ const normalized = (command ?? "").trim();
1669
+ if (!normalized) {
1670
+ return null;
1671
+ }
1672
+ const tokens = tokenizeRuntimeCommand(normalized);
1673
+ if (tokens.length === 0) {
1674
+ return null;
1675
+ }
1676
+ const first = stripClaudeCommandQuotes(tokens[0]);
1677
+ if ((first === "bash" || first === "sh" || first === "zsh" || first === "fish") && tokens.length >= 3) {
1678
+ const flagIndex = tokens.findIndex((token) => {
1679
+ const value = stripClaudeCommandQuotes(token);
1680
+ return value === "-c" || value === "-lc";
1681
+ });
1682
+ if (flagIndex >= 0 && flagIndex + 1 < tokens.length) {
1683
+ const shellCommand = stripClaudeCommandQuotes(tokens[flagIndex + 1]);
1684
+ return resolveShellCommandClaudeBinary(shellCommand) ?? resolveRuntimeCommandBinary(shellCommand);
1685
+ }
1686
+ }
1687
+ return first;
1688
+ }
1689
+ function stripClaudeCommandQuotes(value) {
1690
+ return value.replace(/^['"]|['"]$/g, "");
1691
+ }
1692
+ function tokenizeRuntimeCommand(command) {
1693
+ return command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
1694
+ }
1695
+ function resolveShellCommandClaudeBinary(command) {
1696
+ for (const segment of command.split(/&&|\|\||[;\n]/g)) {
1697
+ const tokens = tokenizeRuntimeCommand(segment);
1698
+ for (const token of tokens) {
1699
+ const value = stripClaudeCommandQuotes(token);
1700
+ if (value.includes("=") && !value.startsWith("/") && !value.startsWith("./")) {
1701
+ continue;
1702
+ }
1703
+ if (isClaudeBinaryName(value)) {
1704
+ return value;
1705
+ }
1706
+ }
1707
+ }
1708
+ return null;
1709
+ }
1710
+ function isClaudeBinaryName(command) {
1711
+ const normalized = (command.split(/[\\/]/).pop() ?? command).toLowerCase().replace(/\.(exe|cmd|bat)$/i, "");
1712
+ return normalized === "claude" || normalized === "claude-code";
1713
+ }
1714
+
1715
+ // ../runtime-claude/src/adapter.ts
1716
+ import { randomUUID } from "crypto";
1717
+ import { rm } from "fs/promises";
1718
+ import { join as join5 } from "path";
1719
+
1720
+ // ../runtime-claude/src/mcp-compose.ts
1721
+ import { mkdir, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
1722
+ import { basename, dirname, join as join3, resolve as resolve4 } from "path";
1723
+
1724
+ // ../tool-github-graphql/src/tool.ts
1725
+ import { readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
1726
+ var DEFAULT_GITHUB_GRAPHQL_API_URL = "https://api.github.com/graphql";
1727
+ var TOKEN_REUSE_WINDOW_MS2 = 60 * 1e3;
1728
+ async function executeGitHubGraphQL(invocation, config, fetchImpl = fetch) {
1729
+ const token = await resolveGitHubGraphQLToken(config, {
1730
+ fetchImpl
1731
+ });
1732
+ const response = await fetchImpl(
1733
+ config.apiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL,
1734
+ {
1735
+ method: "POST",
1736
+ headers: {
1737
+ "content-type": "application/json",
1738
+ authorization: `Bearer ${token}`
1739
+ },
1740
+ body: JSON.stringify(invocation)
1741
+ }
1742
+ );
1743
+ const payload = await response.json();
1744
+ if (!response.ok) {
1745
+ throw new Error(
1746
+ `GitHub GraphQL request failed with status ${response.status}: ${JSON.stringify(payload)}`
1747
+ );
1748
+ }
1749
+ if (payload.errors?.length) {
1750
+ throw new Error(payload.errors.map((error) => error.message).join("; "));
1751
+ }
1752
+ return payload;
1753
+ }
1754
+ async function resolveGitHubGraphQLToken(config, dependencies = {}) {
1755
+ if (config.token) {
1756
+ return config.token;
1757
+ }
1758
+ if (!config.tokenBrokerUrl || !config.tokenBrokerSecret) {
1759
+ throw new Error(
1760
+ "Either GITHUB_GRAPHQL_TOKEN or the runtime token broker configuration is required."
1761
+ );
1762
+ }
1763
+ const now = dependencies.now ?? /* @__PURE__ */ new Date();
1764
+ const readFileImpl = dependencies.readFileImpl ?? readFile5;
1765
+ const writeFileImpl = dependencies.writeFileImpl ?? writeFile2;
1766
+ const cachedToken = config.tokenCachePath ? await readCachedToken(config.tokenCachePath, readFileImpl) : null;
1767
+ if (cachedToken && cachedToken.expiresAt.getTime() - now.getTime() > TOKEN_REUSE_WINDOW_MS2) {
1768
+ return cachedToken.token;
1769
+ }
1770
+ const fetchImpl = dependencies.fetchImpl ?? fetch;
1771
+ const response = await fetchImpl(config.tokenBrokerUrl, {
1772
+ method: "POST",
1773
+ headers: {
1774
+ accept: "application/json",
1775
+ authorization: `Bearer ${config.tokenBrokerSecret}`
1776
+ }
1777
+ });
1778
+ const payload = await response.json();
1779
+ if (!response.ok || !payload.token || !payload.expiresAt) {
1780
+ throw new Error(
1781
+ payload.error ?? `Runtime token broker request failed with status ${response.status}.`
1782
+ );
1783
+ }
1784
+ if (config.tokenCachePath) {
1785
+ await writeFileImpl(config.tokenCachePath, JSON.stringify(payload), "utf8");
1786
+ }
1787
+ return payload.token;
1788
+ }
1789
+ async function readStdin() {
1790
+ const chunks = [];
1791
+ for await (const chunk of process.stdin) {
1792
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
1793
+ }
1794
+ return Buffer.concat(chunks).toString("utf8");
1795
+ }
1796
+ async function main() {
1797
+ const rawInput = await readStdin();
1798
+ const invocation = JSON.parse(rawInput);
1799
+ const result = await executeGitHubGraphQL(invocation, {
1800
+ token: process.env.GITHUB_GRAPHQL_TOKEN,
1801
+ apiUrl: process.env.GITHUB_GRAPHQL_API_URL,
1802
+ tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
1803
+ tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
1804
+ tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH
1805
+ });
1806
+ process.stdout.write(`${JSON.stringify(result)}
1807
+ `);
1808
+ }
1809
+ if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
1810
+ main().catch((error) => {
1811
+ const message = error instanceof Error ? error.message : "Unknown error";
1812
+ process.stderr.write(`${message}
1813
+ `);
1814
+ process.exitCode = 1;
1815
+ });
1816
+ }
1817
+ async function readCachedToken(path, readFileImpl) {
1818
+ try {
1819
+ const payload = JSON.parse(await readFileImpl(path, "utf8"));
1820
+ if (!payload.token || !payload.expiresAt) {
1821
+ return null;
1822
+ }
1823
+ return {
1824
+ token: payload.token,
1825
+ expiresAt: new Date(payload.expiresAt)
1826
+ };
1827
+ } catch {
1828
+ return null;
1829
+ }
1830
+ }
1831
+
1832
+ // ../tool-github-graphql/src/mcp-server.ts
1833
+ import { fileURLToPath } from "url";
1834
+ var TOOL_SCHEMA = {
1835
+ name: "github_graphql",
1836
+ description: "Execute GitHub GraphQL queries for the active workspace so the agent can mutate project and issue state directly.",
1837
+ inputSchema: {
1838
+ type: "object",
1839
+ properties: {
1840
+ query: {
1841
+ type: "string",
1842
+ description: "GraphQL query or mutation document."
1843
+ },
1844
+ variables: {
1845
+ type: "object",
1846
+ description: "Variables for the GraphQL document."
1847
+ },
1848
+ operationName: {
1849
+ type: "string",
1850
+ description: "Optional GraphQL operation name."
1851
+ }
1852
+ },
1853
+ required: ["query"],
1854
+ additionalProperties: false
1855
+ }
1856
+ };
1857
+ var lineBuffer = "";
1858
+ function resolveGitHubGraphQLMcpServerEntryPoint() {
1859
+ return fileURLToPath(new URL("./mcp-server.js", import.meta.url));
1860
+ }
1861
+ function sendResponse(id, result) {
1862
+ const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
1863
+ process.stdout.write(msg + "\n");
1864
+ }
1865
+ function sendError(id, code, message) {
1866
+ const msg = JSON.stringify({
1867
+ jsonrpc: "2.0",
1868
+ id,
1869
+ error: { code, message }
1870
+ });
1871
+ process.stdout.write(msg + "\n");
1872
+ }
1873
+ async function handleRequest(msg) {
1874
+ const id = msg.id ?? null;
1875
+ switch (msg.method) {
1876
+ case "initialize": {
1877
+ sendResponse(id, {
1878
+ protocolVersion: "2024-11-05",
1879
+ capabilities: { tools: {} },
1880
+ serverInfo: {
1881
+ name: "github-symphony-graphql",
1882
+ version: "0.1.0"
1883
+ }
1884
+ });
1885
+ process.stdout.write(
1886
+ JSON.stringify({
1887
+ jsonrpc: "2.0",
1888
+ method: "notifications/initialized"
1889
+ }) + "\n"
1890
+ );
1891
+ break;
1892
+ }
1893
+ case "tools/list": {
1894
+ sendResponse(id, { tools: [TOOL_SCHEMA] });
1895
+ break;
1896
+ }
1897
+ case "tools/call": {
1898
+ const params = msg.params;
1899
+ if (params.name !== "github_graphql") {
1900
+ sendError(id, -32602, `Unknown tool: ${params.name}`);
1901
+ return;
1902
+ }
1903
+ const args = params.arguments ?? {};
1904
+ const invocation = {
1905
+ query: args.query,
1906
+ variables: args.variables,
1907
+ operationName: args.operationName
1908
+ };
1909
+ try {
1910
+ const result = await executeGitHubGraphQL(invocation, {
1911
+ token: process.env.GITHUB_GRAPHQL_TOKEN,
1912
+ apiUrl: process.env.GITHUB_GRAPHQL_API_URL,
1913
+ tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
1914
+ tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
1915
+ tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH
1916
+ });
1917
+ sendResponse(id, {
1918
+ content: [
1919
+ {
1920
+ type: "text",
1921
+ text: JSON.stringify(result, null, 2)
1922
+ }
1923
+ ]
1924
+ });
1925
+ } catch (err) {
1926
+ const message = err instanceof Error ? err.message : String(err);
1927
+ sendResponse(id, {
1928
+ content: [{ type: "text", text: message }],
1929
+ isError: true
1930
+ });
1931
+ }
1932
+ break;
1933
+ }
1934
+ case "notifications/initialized":
1935
+ case "ping": {
1936
+ if (id !== null && id !== void 0) {
1937
+ sendResponse(id, {});
1938
+ }
1939
+ break;
1940
+ }
1941
+ default: {
1942
+ if (id !== null && id !== void 0) {
1943
+ sendError(id, -32601, `Method not found: ${msg.method}`);
1944
+ }
1945
+ }
1946
+ }
1947
+ }
1948
+ async function main2() {
1949
+ process.stdin.setEncoding("utf8");
1950
+ process.stdin.on("data", (chunk) => {
1951
+ lineBuffer += chunk;
1952
+ const lines = lineBuffer.split("\n");
1953
+ lineBuffer = lines.pop() ?? "";
1954
+ for (const line of lines) {
1955
+ const trimmed = line.trim();
1956
+ if (!trimmed) continue;
1957
+ try {
1958
+ const msg = JSON.parse(trimmed);
1959
+ void handleRequest(msg);
1960
+ } catch (err) {
1961
+ process.stderr.write(
1962
+ `[github-graphql-mcp] parse error: ${err instanceof Error ? err.message : String(err)}
1963
+ `
1964
+ );
1965
+ }
1966
+ }
1967
+ });
1968
+ process.stdin.on("end", () => {
1969
+ process.exit(0);
1970
+ });
1971
+ }
1972
+ if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
1973
+ main2().catch((err) => {
1974
+ process.stderr.write(
1975
+ `[github-graphql-mcp] fatal: ${err instanceof Error ? err.message : String(err)}
1976
+ `
1977
+ );
1978
+ process.exitCode = 1;
1979
+ });
1980
+ }
1981
+
1982
+ // ../tool-github-graphql/src/mcp-entry.ts
1983
+ var DEFAULT_GITHUB_GRAPHQL_API_URL2 = "https://api.github.com/graphql";
1984
+ function createGitHubGraphQLMcpServerEntry(options = {}) {
1985
+ return {
1986
+ command: "node",
1987
+ args: [resolveGitHubGraphQLMcpServerEntryPoint()],
1988
+ env: {
1989
+ GITHUB_GRAPHQL_API_URL: options.githubGraphqlApiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL2,
1990
+ ...options.githubToken ? {
1991
+ GITHUB_GRAPHQL_TOKEN: options.githubToken
1992
+ } : {},
1993
+ ...options.githubTokenBrokerUrl ? {
1994
+ GITHUB_TOKEN_BROKER_URL: options.githubTokenBrokerUrl
1995
+ } : {},
1996
+ ...options.githubTokenBrokerSecret ? {
1997
+ GITHUB_TOKEN_BROKER_SECRET: options.githubTokenBrokerSecret
1998
+ } : {},
1999
+ ...options.githubTokenCachePath ? {
2000
+ GITHUB_TOKEN_CACHE_PATH: options.githubTokenCachePath
2001
+ } : {},
2002
+ ...options.githubProjectId ? {
2003
+ GITHUB_PROJECT_ID: options.githubProjectId
2004
+ } : {}
2005
+ }
2006
+ };
2007
+ }
2008
+
2009
+ // ../runtime-claude/src/spawn.ts
2010
+ import { spawn as spawn2 } from "child_process";
2011
+ import { finished } from "stream/promises";
2012
+
2013
+ // ../runtime-claude/src/session-store.ts
2014
+ import { mkdir as mkdir2, readFile as readFile7, rename, writeFile as writeFile4 } from "fs/promises";
2015
+ import { dirname as dirname2, join as join4 } from "path";
2016
+
1126
2017
  export {
1127
2018
  isOrchestratorChannelEvent,
1128
2019
  DEFAULT_WORKFLOW_LIFECYCLE,
@@ -1130,6 +2021,8 @@ export {
1130
2021
  isStateTerminal,
1131
2022
  matchesWorkflowState,
1132
2023
  DEFAULT_MAX_FAILURE_RETRIES,
2024
+ resolveWorkflowRuntimeCommand,
2025
+ resolveWorkflowRuntimeTimeouts,
1133
2026
  parseWorkflowMarkdown,
1134
2027
  WorkflowConfigStore,
1135
2028
  createDefaultWorkflowResolution,
@@ -1138,6 +2031,12 @@ export {
1138
2031
  renderPrompt,
1139
2032
  classifySessionExit,
1140
2033
  scheduleRetryAt,
2034
+ extractEnvForCodex,
2035
+ shouldReuseAgentCredentialCache,
2036
+ readAgentCredentialCache,
2037
+ writeAgentCredentialCache,
2038
+ DEFAULT_AGENT_INPUT_REQUIRED_REASON,
2039
+ buildAgentInputRequiredReason,
1141
2040
  readEnvFile,
1142
2041
  deriveIssueWorkspaceKey,
1143
2042
  deriveIssueWorkspaceKeyFromIdentifier,
@@ -1151,5 +2050,12 @@ export {
1151
2050
  isFileMissing,
1152
2051
  parseRecentEvents,
1153
2052
  isMatchingIssueRun,
1154
- mapIssueOrchestrationStateToStatus
2053
+ mapIssueOrchestrationStateToStatus,
2054
+ resolveGitHubGraphQLToken,
2055
+ createGitHubGraphQLMcpServerEntry,
2056
+ runClaudePreflight,
2057
+ formatClaudePreflightText,
2058
+ isClaudeRuntimeCommand,
2059
+ resolveClaudeCommandBinary,
2060
+ resolveRuntimeCommandBinary
1155
2061
  };