@akagilnc/pi-workflow-roles 0.1.3999 → 0.1.4021

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.
@@ -417,7 +417,7 @@ function normalizeReviewComment(raw) {
417
417
  function createGhApiRunner(options = {}) {
418
418
  const spawnImpl = options.spawnImpl ?? spawn;
419
419
  return async (args, runOptions = {}) => {
420
- return await new Promise((resolve20, reject) => {
420
+ return await new Promise((resolve21, reject) => {
421
421
  const signal = runOptions.signal;
422
422
  if (signal?.aborted) {
423
423
  reject(signal.reason ?? new Error("aborted"));
@@ -490,11 +490,11 @@ function createGhApiRunner(options = {}) {
490
490
  const value = line2.slice(idx + 1).trim();
491
491
  headers[name] = value;
492
492
  }
493
- resolve20({ status, headers, bodyText });
493
+ resolve21({ status, headers, bodyText });
494
494
  return;
495
495
  }
496
496
  if (code === 0) {
497
- resolve20({ status: 200, headers: {}, bodyText: stdout });
497
+ resolve21({ status: 200, headers: {}, bodyText: stdout });
498
498
  return;
499
499
  }
500
500
  const failure2 = new Error(
@@ -1549,7 +1549,7 @@ async function spawnEngineDetourOnce(input) {
1549
1549
  }
1550
1550
  const command = input.argv[0];
1551
1551
  const args = input.argv.slice(1);
1552
- return await new Promise((resolve20, reject) => {
1552
+ return await new Promise((resolve21, reject) => {
1553
1553
  let settled = false;
1554
1554
  const signal = input.signal;
1555
1555
  const child = spawn2(command, args, {
@@ -1579,7 +1579,7 @@ async function spawnEngineDetourOnce(input) {
1579
1579
  if (signal !== void 0) {
1580
1580
  signal.removeEventListener("abort", onAbort);
1581
1581
  }
1582
- resolve20(result);
1582
+ resolve21(result);
1583
1583
  };
1584
1584
  const onAbort = () => {
1585
1585
  fail4(signal !== void 0 ? abortReasonError(signal) : new Error("aborted"));
@@ -3195,6 +3195,7 @@ var init_host_descriptions = __esm({
3195
3195
  });
3196
3196
  HEADLESS_HOST_DESCRIPTIONS = Object.freeze({
3197
3197
  "claude": Object.freeze({
3198
+ protocol: "claude-print",
3198
3199
  binaryFromHome: Object.freeze([".local", "bin", "claude"]),
3199
3200
  sessionBindingFile: "claude-headless-session.json",
3200
3201
  fixedArgs: Object.freeze([
@@ -3220,6 +3221,15 @@ var init_host_descriptions = __esm({
3220
3221
  mcpConfigFlag: "--mcp-config",
3221
3222
  sessionIdFlag: "--session-id",
3222
3223
  resumeFlag: "--resume"
3224
+ }),
3225
+ /**
3226
+ * Codex headless (#646). Binary under operator home; auth stays in CODEX_HOME.
3227
+ * Argv/parse/schema-close are codex-exec helpers — not Claude flag mapping.
3228
+ */
3229
+ "codex": Object.freeze({
3230
+ protocol: "codex-exec",
3231
+ binaryFromHome: Object.freeze([".local", "bin", "codex"]),
3232
+ sessionBindingFile: "codex-headless-session.json"
3223
3233
  })
3224
3234
  });
3225
3235
  }
@@ -10976,12 +10986,12 @@ function createSystemCollectorClock() {
10976
10986
  return {
10977
10987
  wallNow: () => /* @__PURE__ */ new Date(),
10978
10988
  monoNow: () => Number(process.hrtime.bigint() - start) / 1e6,
10979
- sleep: (ms, signal) => new Promise((resolve20, reject) => {
10989
+ sleep: (ms, signal) => new Promise((resolve21, reject) => {
10980
10990
  if (signal?.aborted) {
10981
10991
  reject(signal.reason ?? new Error("aborted"));
10982
10992
  return;
10983
10993
  }
10984
- const timer = setTimeout(resolve20, ms);
10994
+ const timer = setTimeout(resolve21, ms);
10985
10995
  const onAbort = () => {
10986
10996
  clearTimeout(timer);
10987
10997
  reject(signal?.reason ?? new Error("aborted"));
@@ -14515,10 +14525,10 @@ function presentFailureTerminal(terminal, io) {
14515
14525
  }
14516
14526
  function defaultNavigatorGraceSleep() {
14517
14527
  let timer;
14518
- const sleep = ((ms) => new Promise((resolve20) => {
14528
+ const sleep = ((ms) => new Promise((resolve21) => {
14519
14529
  timer = setTimeout(() => {
14520
14530
  timer = void 0;
14521
- resolve20();
14531
+ resolve21();
14522
14532
  }, ms);
14523
14533
  }));
14524
14534
  sleep.cancel = () => {
@@ -14530,7 +14540,7 @@ function defaultNavigatorGraceSleep() {
14530
14540
  return sleep;
14531
14541
  }
14532
14542
  function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep = defaultNavigatorGraceSleep()) {
14533
- return new Promise((resolve20, reject) => {
14543
+ return new Promise((resolve21, reject) => {
14534
14544
  let settled = false;
14535
14545
  const finish = (action) => {
14536
14546
  if (settled) return;
@@ -14539,11 +14549,11 @@ function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep
14539
14549
  action();
14540
14550
  };
14541
14551
  void work.then(
14542
- (value) => finish(() => resolve20({ status: "done", value })),
14552
+ (value) => finish(() => resolve21({ status: "done", value })),
14543
14553
  (error) => finish(() => reject(error))
14544
14554
  );
14545
14555
  void sleep(graceMs).then(() => {
14546
- finish(() => resolve20({ status: "timeout" }));
14556
+ finish(() => resolve21({ status: "timeout" }));
14547
14557
  });
14548
14558
  });
14549
14559
  }
@@ -22946,11 +22956,11 @@ function buildSkillExpansion(methodSkills, prompt) {
22946
22956
  });
22947
22957
  }
22948
22958
  async function listen(server, path) {
22949
- await new Promise((resolve20, reject) => {
22959
+ await new Promise((resolve21, reject) => {
22950
22960
  server.once("error", reject);
22951
22961
  server.listen(path, () => {
22952
22962
  server.off("error", reject);
22953
- resolve20();
22963
+ resolve21();
22954
22964
  });
22955
22965
  });
22956
22966
  }
@@ -23345,8 +23355,8 @@ async function prepareRoleEnvelope(options) {
23345
23355
  try {
23346
23356
  const closeAll = server.closeAllConnections;
23347
23357
  if (typeof closeAll === "function") closeAll.call(server);
23348
- await new Promise((resolve20, reject) => {
23349
- server.close((error) => error ? reject(error) : resolve20());
23358
+ await new Promise((resolve21, reject) => {
23359
+ server.close((error) => error ? reject(error) : resolve21());
23350
23360
  });
23351
23361
  } catch (error) {
23352
23362
  cleanupFailures.push(error);
@@ -23478,6 +23488,12 @@ init_session_identity();
23478
23488
 
23479
23489
  // src/headless-host/description.ts
23480
23490
  import { join as join37 } from "node:path";
23491
+ function isClaudePrintDescription(description) {
23492
+ return description.protocol === "claude-print";
23493
+ }
23494
+ function isCodexExecDescription(description) {
23495
+ return description.protocol === "codex-exec";
23496
+ }
23481
23497
  function resolveHeadlessBinary(description, operatorHome) {
23482
23498
  return join37(operatorHome, ...description.binaryFromHome);
23483
23499
  }
@@ -23508,6 +23524,214 @@ function headlessTurnArgs(options) {
23508
23524
  }
23509
23525
  return args;
23510
23526
  }
23527
+ function codexTomlString(value) {
23528
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
23529
+ }
23530
+ function codexTomlStringArray(values) {
23531
+ return `[${values.map(codexTomlString).join(",")}]`;
23532
+ }
23533
+ function codexTomlStringTable(entries) {
23534
+ const parts = Object.entries(entries).map(
23535
+ ([key, value]) => `${key}=${codexTomlString(value)}`
23536
+ );
23537
+ return `{${parts.join(",")}}`;
23538
+ }
23539
+ var CODEX_JSON_VALUE_DEF = "codexJsonValue";
23540
+ var CODEX_JSON_VALUE_REF = `#/$defs/${CODEX_JSON_VALUE_DEF}`;
23541
+ var CODEX_JSON_VALUE_SCHEMA = Object.freeze({
23542
+ anyOf: Object.freeze([
23543
+ Object.freeze({ type: "string" }),
23544
+ Object.freeze({ type: "number" }),
23545
+ Object.freeze({ type: "boolean" }),
23546
+ Object.freeze({ type: "null" }),
23547
+ Object.freeze({ type: "array", items: Object.freeze({ $ref: CODEX_JSON_VALUE_REF }) }),
23548
+ Object.freeze({
23549
+ type: "object",
23550
+ properties: Object.freeze({}),
23551
+ required: Object.freeze([]),
23552
+ additionalProperties: Object.freeze({ $ref: CODEX_JSON_VALUE_REF })
23553
+ })
23554
+ ])
23555
+ });
23556
+ function closeJsonSchemaForCodex(schema) {
23557
+ const closed = closeSchemaNode(schema);
23558
+ const existingDefs = isPlainObject(closed.$defs) ? closed.$defs : {};
23559
+ return {
23560
+ ...closed,
23561
+ $defs: {
23562
+ ...existingDefs,
23563
+ [CODEX_JSON_VALUE_DEF]: CODEX_JSON_VALUE_SCHEMA
23564
+ }
23565
+ };
23566
+ }
23567
+ function isPlainObject(value) {
23568
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23569
+ }
23570
+ function isPureNullTypeSchema(value) {
23571
+ if (!isPlainObject(value)) return false;
23572
+ if (value.type === "null") return true;
23573
+ if (Array.isArray(value.type) && value.type.length > 0 && value.type.every((t) => t === "null")) {
23574
+ return true;
23575
+ }
23576
+ return false;
23577
+ }
23578
+ function stripNullFromLeafType(leaf) {
23579
+ if (!isPlainObject(leaf) || !Array.isArray(leaf.type)) return leaf;
23580
+ const nonNull = leaf.type.filter((t) => t !== "null");
23581
+ if (nonNull.length === leaf.type.length) return leaf;
23582
+ if (nonNull.length === 0) return { ...leaf, type: "null" };
23583
+ if (nonNull.length === 1) return { ...leaf, type: nonNull[0] };
23584
+ return { ...leaf, type: nonNull };
23585
+ }
23586
+ function flattenUnionLeaves(schema) {
23587
+ if (!isPlainObject(schema)) return [schema];
23588
+ if (Array.isArray(schema.anyOf)) {
23589
+ return schema.anyOf.flatMap(flattenUnionLeaves);
23590
+ }
23591
+ return [schema];
23592
+ }
23593
+ function nonNullLeaves(schema) {
23594
+ return flattenUnionLeaves(schema).map(stripNullFromLeafType).filter((leaf) => !isPureNullTypeSchema(leaf));
23595
+ }
23596
+ function closePropertySchema(schema, optional) {
23597
+ const leaves = nonNullLeaves(schema).map(closeSchemaNode);
23598
+ if (leaves.length === 0) return { type: "null" };
23599
+ if (!optional) {
23600
+ return leaves.length === 1 ? leaves[0] : { anyOf: leaves };
23601
+ }
23602
+ return { anyOf: [...leaves, { type: "null" }] };
23603
+ }
23604
+ function ensureTypedLeaf(schema) {
23605
+ if (schema.type !== void 0) return schema;
23606
+ if (typeof schema.$ref === "string") return schema;
23607
+ if (isPlainObject(schema.properties) || schema.additionalProperties !== void 0) {
23608
+ return { ...schema, type: "object" };
23609
+ }
23610
+ if (schema.items !== void 0) {
23611
+ return { ...schema, type: "array" };
23612
+ }
23613
+ if (schema.const !== void 0) {
23614
+ const value = schema.const;
23615
+ if (value === null) return { ...schema, type: "null" };
23616
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
23617
+ return { ...schema, type: typeof value };
23618
+ }
23619
+ }
23620
+ if (Array.isArray(schema.enum) && schema.enum.length > 0) {
23621
+ const sample = schema.enum.find((item) => item !== null);
23622
+ if (typeof sample === "string" || typeof sample === "number" || typeof sample === "boolean") {
23623
+ return { ...schema, type: typeof sample };
23624
+ }
23625
+ }
23626
+ return { $ref: CODEX_JSON_VALUE_REF };
23627
+ }
23628
+ function originalRequiredNames(node) {
23629
+ if (!Array.isArray(node.required)) return /* @__PURE__ */ new Set();
23630
+ return new Set(node.required.filter((item) => typeof item === "string"));
23631
+ }
23632
+ function closeSchemaNode(node) {
23633
+ if (!isPlainObject(node)) return node;
23634
+ if (typeof node.$ref === "string") return node;
23635
+ if (Array.isArray(node.anyOf)) {
23636
+ const leaves = nonNullLeaves(node).map(closeSchemaNode);
23637
+ if (leaves.length === 0) return { type: "null" };
23638
+ if (leaves.length === 1) return leaves[0];
23639
+ return { anyOf: leaves };
23640
+ }
23641
+ let out = { ...node };
23642
+ if (node.items !== void 0) {
23643
+ out.items = closeSchemaNode(node.items);
23644
+ }
23645
+ if (isPlainObject(node.$defs)) {
23646
+ out.$defs = Object.fromEntries(
23647
+ Object.entries(node.$defs).map(([key, value]) => [key, closeSchemaNode(value)])
23648
+ );
23649
+ }
23650
+ const hasProperties = isPlainObject(node.properties);
23651
+ const isObjectType = node.type === "object" || Array.isArray(node.type) && node.type.includes("object") || hasProperties || node.additionalProperties !== void 0;
23652
+ if (hasProperties) {
23653
+ const props = node.properties;
23654
+ const wasRequired = originalRequiredNames(node);
23655
+ const closedProps = {};
23656
+ const required = [];
23657
+ for (const [name, propSchema] of Object.entries(props)) {
23658
+ required.push(name);
23659
+ closedProps[name] = closePropertySchema(propSchema, !wasRequired.has(name));
23660
+ }
23661
+ out.properties = closedProps;
23662
+ out.required = required;
23663
+ out.additionalProperties = false;
23664
+ if (out.type === void 0) out.type = "object";
23665
+ delete out.anyOf;
23666
+ } else if (isObjectType) {
23667
+ out.additionalProperties = false;
23668
+ if (!Array.isArray(out.required)) out.required = [];
23669
+ if (out.type === void 0) out.type = "object";
23670
+ } else {
23671
+ out = ensureTypedLeaf(out);
23672
+ }
23673
+ return out;
23674
+ }
23675
+ function stringEnvironment(value) {
23676
+ const entries = Array.isArray(value) ? value.flatMap((item) => {
23677
+ if (!isPlainObject(item) || typeof item.name !== "string" || typeof item.value !== "string") return [];
23678
+ return [[item.name, item.value]];
23679
+ }) : isPlainObject(value) ? Object.entries(value).filter((entry) => typeof entry[1] === "string") : [];
23680
+ return entries.length === 0 ? void 0 : Object.fromEntries(entries);
23681
+ }
23682
+ function codexMcpConfigArgs(mcpServers) {
23683
+ const args = [];
23684
+ for (const row of mcpServers) {
23685
+ const name = typeof row.name === "string" ? row.name : void 0;
23686
+ const command = typeof row.command === "string" ? row.command : void 0;
23687
+ if (name === void 0 || name === "" || command === void 0 || command === "") continue;
23688
+ const prefix = `mcp_servers.${name}`;
23689
+ args.push("-c", `${prefix}.command=${codexTomlString(command)}`);
23690
+ args.push("-c", `${prefix}.required=true`);
23691
+ if (Array.isArray(row.args) && row.args.every((item) => typeof item === "string")) {
23692
+ args.push("-c", `${prefix}.args=${codexTomlStringArray(row.args)}`);
23693
+ }
23694
+ const env = stringEnvironment(row.env);
23695
+ if (env !== void 0) {
23696
+ args.push("-c", `${prefix}.env=${codexTomlStringTable(env)}`);
23697
+ }
23698
+ }
23699
+ return args;
23700
+ }
23701
+ function codexTurnArgs(options) {
23702
+ const args = ["exec", "--approve-for-me"];
23703
+ if (options.session.kind === "resume") {
23704
+ args.push("resume", options.session.id);
23705
+ }
23706
+ args.push("--json");
23707
+ args.push("--ignore-user-config", "--ignore-rules");
23708
+ const roots = (options.writableRoots ?? []).filter((root) => root !== "");
23709
+ if (roots.length > 0) {
23710
+ args.push(
23711
+ "-c",
23712
+ `sandbox_workspace_write.writable_roots=${codexTomlStringArray(roots)}`
23713
+ );
23714
+ if (options.session.kind === "new") {
23715
+ for (const root of roots) {
23716
+ args.push("--add-dir", root);
23717
+ }
23718
+ }
23719
+ }
23720
+ args.push("-c", `model_instructions_file=${codexTomlString(options.systemPromptPath)}`);
23721
+ args.push("--output-schema", options.outputSchemaPath);
23722
+ args.push(...codexMcpConfigArgs(options.mcpServers));
23723
+ if (options.model !== void 0 && options.model !== "") {
23724
+ args.push("-m", options.model);
23725
+ }
23726
+ if (options.effort !== void 0 && options.effort !== "") {
23727
+ args.push("-c", `model_reasoning_effort=${codexTomlString(options.effort)}`);
23728
+ }
23729
+ if (options.skipGitRepoCheck === true) {
23730
+ args.push("--skip-git-repo-check");
23731
+ }
23732
+ args.push("--", options.prompt);
23733
+ return args;
23734
+ }
23511
23735
  function headlessMcpConfigDocument(mcpServers) {
23512
23736
  const servers = {};
23513
23737
  for (const row of mcpServers) {
@@ -23516,29 +23740,19 @@ function headlessMcpConfigDocument(mcpServers) {
23516
23740
  if (name === void 0 || name === "" || command === void 0 || command === "") continue;
23517
23741
  const entry = { command };
23518
23742
  if (Array.isArray(row.args)) entry.args = row.args;
23519
- if (Array.isArray(row.env)) {
23520
- const env = {};
23521
- for (const item of row.env) {
23522
- if (typeof item !== "object" || item === null) continue;
23523
- const record4 = item;
23524
- if (typeof record4.name === "string" && typeof record4.value === "string") {
23525
- env[record4.name] = record4.value;
23526
- }
23527
- }
23528
- if (Object.keys(env).length > 0) entry.env = env;
23529
- } else if (typeof row.env === "object" && row.env !== null && !Array.isArray(row.env)) {
23530
- entry.env = row.env;
23531
- }
23743
+ const env = stringEnvironment(row.env);
23744
+ if (env !== void 0) entry.env = env;
23532
23745
  servers[name] = entry;
23533
23746
  }
23534
23747
  return Object.freeze({ mcpServers: Object.freeze(servers) });
23535
23748
  }
23536
23749
 
23537
23750
  // src/headless-host/role-turn-host.ts
23538
- import { spawn as spawn4 } from "node:child_process";
23751
+ import { spawn as spawn4, spawnSync } from "node:child_process";
23539
23752
  import { randomUUID as randomUUID8 } from "node:crypto";
23753
+ import { existsSync as existsSync12 } from "node:fs";
23540
23754
  import { writeFile as writeFile11 } from "node:fs/promises";
23541
- import { join as join38 } from "node:path";
23755
+ import { dirname as dirname21, isAbsolute as isAbsolute11, join as join38, resolve as resolve20 } from "node:path";
23542
23756
 
23543
23757
  // src/external-host-turn-loop.ts
23544
23758
  var EXTERNAL_ROLE_TURN_ROUND_LIMIT = 8;
@@ -23667,7 +23881,7 @@ function failure(cause, name, code, details, diagnostic) {
23667
23881
  };
23668
23882
  }
23669
23883
  function isHeadlessResultCandidate(value) {
23670
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
23884
+ if (!isPlainObject(value)) return false;
23671
23885
  const record4 = value;
23672
23886
  return record4.type === void 0 || record4.type === "result" || record4.structured_output !== void 0;
23673
23887
  }
@@ -23685,7 +23899,7 @@ function parseHeadlessCliStdout(stdout) {
23685
23899
  if (text === "") continue;
23686
23900
  try {
23687
23901
  const value = JSON.parse(text);
23688
- if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
23902
+ if (!isPlainObject(value)) continue;
23689
23903
  const record4 = value;
23690
23904
  if (record4.type === "result" || record4.structured_output !== void 0) {
23691
23905
  last = record4;
@@ -23705,8 +23919,87 @@ function resultCandidateText(line2) {
23705
23919
  return void 0;
23706
23920
  }
23707
23921
  }
23922
+ function createCodexExecTurnObserver() {
23923
+ let threadId;
23924
+ let finalMessage;
23925
+ let failureDiagnostic;
23926
+ let turnCompleted = false;
23927
+ return {
23928
+ observe(value) {
23929
+ if (!isPlainObject(value)) return;
23930
+ const type = typeof value.type === "string" ? value.type : void 0;
23931
+ if (type === "thread.started" && typeof value.thread_id === "string" && value.thread_id !== "") {
23932
+ threadId = value.thread_id;
23933
+ } else if (type === "item.completed" && isPlainObject(value.item)) {
23934
+ if (value.item.type === "agent_message" && typeof value.item.text === "string") {
23935
+ finalMessage = value.item.text;
23936
+ }
23937
+ } else if (type === "turn.completed") {
23938
+ turnCompleted = true;
23939
+ failureDiagnostic = void 0;
23940
+ } else if (type === "turn.failed") {
23941
+ turnCompleted = false;
23942
+ failureDiagnostic = formatCodexFailurePayload(value.error ?? value);
23943
+ }
23944
+ },
23945
+ result() {
23946
+ return {
23947
+ ...threadId === void 0 ? {} : { threadId },
23948
+ ...finalMessage === void 0 ? {} : { finalMessage },
23949
+ ...failureDiagnostic === void 0 ? {} : { failureDiagnostic },
23950
+ turnCompleted
23951
+ };
23952
+ }
23953
+ };
23954
+ }
23955
+ function formatCodexFailurePayload(payload) {
23956
+ if (typeof payload === "string" && payload.trim() !== "") return payload;
23957
+ if (isPlainObject(payload)) {
23958
+ if (typeof payload.message === "string" && payload.message.trim() !== "") return payload.message;
23959
+ try {
23960
+ return JSON.stringify(payload);
23961
+ } catch {
23962
+ return "codex turn failed";
23963
+ }
23964
+ }
23965
+ return String(payload);
23966
+ }
23967
+ function cwdIsGitWorkTree(cwd) {
23968
+ let dir = cwd;
23969
+ for (; ; ) {
23970
+ if (existsSync12(join38(dir, ".git"))) return true;
23971
+ const parent = dirname21(dir);
23972
+ if (parent === dir) return false;
23973
+ dir = parent;
23974
+ }
23975
+ }
23976
+ function resolveGitCommonDir(cwd) {
23977
+ if (!cwdIsGitWorkTree(cwd)) return void 0;
23978
+ let result;
23979
+ try {
23980
+ result = spawnSync("git", ["rev-parse", "--git-common-dir"], {
23981
+ cwd,
23982
+ encoding: "utf8"
23983
+ });
23984
+ } catch (error) {
23985
+ const message = error instanceof Error ? error.message : String(error);
23986
+ throw new Error(`git rev-parse --git-common-dir failed: ${message}`);
23987
+ }
23988
+ if (result.error !== void 0) {
23989
+ throw new Error(`git rev-parse --git-common-dir failed: ${result.error.message}`);
23990
+ }
23991
+ if (result.status !== 0) {
23992
+ const detail = result.stderr.trim() || `exit ${String(result.status)}`;
23993
+ throw new Error(`git rev-parse --git-common-dir failed: ${detail}`);
23994
+ }
23995
+ const raw = result.stdout.trim();
23996
+ if (raw === "") {
23997
+ throw new Error("git rev-parse --git-common-dir returned empty stdout");
23998
+ }
23999
+ return isAbsolute11(raw) ? raw : resolve20(cwd, raw);
24000
+ }
23708
24001
  function spawnHeadlessTurn(options) {
23709
- return new Promise((resolve20, reject) => {
24002
+ return new Promise((resolve21, reject) => {
23710
24003
  if (options.signal?.aborted) {
23711
24004
  reject(hostAbortedError("headless host aborted"));
23712
24005
  return;
@@ -23765,7 +24058,7 @@ function spawnHeadlessTurn(options) {
23765
24058
  settled = true;
23766
24059
  if (timer !== void 0) clearTimeout(timer);
23767
24060
  options.signal?.removeEventListener("abort", onAbort);
23768
- resolve20({ code, stdout: resultStdout, stderr, timedOut });
24061
+ resolve21({ code, stdout: resultStdout, stderr, timedOut });
23769
24062
  };
23770
24063
  const onAbort = () => {
23771
24064
  child.kill("SIGTERM");
@@ -23821,28 +24114,70 @@ function terminalFromSpawned(spawned, knownFailure) {
23821
24114
  }
23822
24115
  };
23823
24116
  }
24117
+ function buildTurnArgs(options) {
24118
+ if (isCodexExecDescription(options.description)) {
24119
+ if (options.sessionKind === "resume" && !options.sessionId) {
24120
+ throw new Error("codex resume requires a bound thread_id");
24121
+ }
24122
+ if (options.outputSchemaPath === void 0) throw new Error("codex requires an output schema path");
24123
+ return codexTurnArgs({
24124
+ prompt: options.prompt,
24125
+ systemPromptPath: options.systemPromptPath,
24126
+ outputSchemaPath: options.outputSchemaPath,
24127
+ mcpServers: options.mcpServers,
24128
+ ...options.model === void 0 ? {} : { model: options.model },
24129
+ ...options.effort === void 0 ? {} : { effort: options.effort },
24130
+ session: options.sessionKind === "resume" ? { kind: "resume", id: options.sessionId } : { kind: "new" },
24131
+ skipGitRepoCheck: !cwdIsGitWorkTree(options.cwd),
24132
+ ...!options.writableRoots?.length ? {} : { writableRoots: options.writableRoots }
24133
+ });
24134
+ }
24135
+ if (!isClaudePrintDescription(options.description)) throw new Error("unsupported headless host protocol");
24136
+ if (!options.sessionId) throw new Error("claude print-mode requires a session id");
24137
+ if (options.mcpConfigPath === void 0) throw new Error("claude print-mode requires an MCP config path");
24138
+ return headlessTurnArgs({
24139
+ description: options.description,
24140
+ prompt: options.prompt,
24141
+ systemPromptPath: options.systemPromptPath,
24142
+ jsonSchema: options.jsonSchema,
24143
+ mcpConfigPath: options.mcpConfigPath,
24144
+ ...options.model === void 0 ? {} : { model: options.model },
24145
+ ...options.effort === void 0 ? {} : { effort: options.effort },
24146
+ session: { kind: options.sessionKind, id: options.sessionId }
24147
+ });
24148
+ }
23824
24149
  function createHeadlessRoleTurnHost(config) {
23825
24150
  return createSerializedRoleTurnHost(async (request) => {
23826
24151
  const prepared = await config.prepare(request);
23827
24152
  const systemPrompt = renderSystemPromptOverride(prepared.systemPrompt);
24153
+ const codex = isCodexExecDescription(config.description);
23828
24154
  let outcome = failure("session", "HeadlessNoOutcome", "no-outcome");
23829
24155
  try {
23830
24156
  let sessionId = await config.sessionIdentity.load(request.principal);
23831
24157
  let sessionKind = request.continuation.kind === "resume" && sessionId !== void 0 && sessionId !== "" ? "resume" : "new";
23832
- if (sessionKind === "new") {
24158
+ if (sessionKind === "new" && !codex) {
23833
24159
  sessionId = randomUUID8();
23834
24160
  await config.sessionIdentity.bind(request.principal, sessionId);
23835
24161
  }
23836
24162
  const env = { ...process.env, ...config.env ?? {} };
23837
24163
  const systemPromptPath = join38(request.runDirectory, "headless-system-prompt.txt");
23838
24164
  await writeFile11(systemPromptPath, systemPrompt, "utf8");
23839
- const mcpConfigPath = join38(request.runDirectory, "headless-mcp-config.json");
23840
- await writeFile11(
23841
- mcpConfigPath,
23842
- `${JSON.stringify(headlessMcpConfigDocument(prepared.mcpServers), null, 2)}
24165
+ let mcpConfigPath;
24166
+ let outputSchemaPath;
24167
+ if (codex) {
24168
+ outputSchemaPath = join38(request.runDirectory, "headless-output-schema.json");
24169
+ const closed = closeJsonSchemaForCodex(prepared.jsonSchema);
24170
+ await writeFile11(outputSchemaPath, `${JSON.stringify(closed, null, 2)}
24171
+ `, "utf8");
24172
+ } else {
24173
+ mcpConfigPath = join38(request.runDirectory, "headless-mcp-config.json");
24174
+ await writeFile11(
24175
+ mcpConfigPath,
24176
+ `${JSON.stringify(headlessMcpConfigDocument(prepared.mcpServers), null, 2)}
23843
24177
  `,
23844
- "utf8"
23845
- );
24178
+ "utf8"
24179
+ );
24180
+ }
23846
24181
  const sessionParent = config.sessionIdentity.resolveSessionFile(request.principal);
23847
24182
  outcome = await driveExternalRoleTurnRounds(prepared, request, {
23848
24183
  roundLimitName: "HeadlessRoundLimit",
@@ -23851,16 +24186,32 @@ function createHeadlessRoleTurnHost(config) {
23851
24186
  sessionKind = "resume";
23852
24187
  },
23853
24188
  async runRound({ prompt, abortSignal }) {
23854
- const args = headlessTurnArgs({
23855
- description: config.description,
23856
- prompt,
23857
- systemPromptPath,
23858
- jsonSchema: prepared.jsonSchema,
23859
- mcpConfigPath,
23860
- ...request.model?.model !== void 0 ? { model: request.model.model } : {},
23861
- ...request.model?.thinking !== void 0 ? { effort: request.model.thinking } : {},
23862
- session: sessionKind === "new" ? { kind: "new", id: sessionId } : { kind: "resume", id: sessionId }
23863
- });
24189
+ let args;
24190
+ try {
24191
+ const gitCommonDir = codex ? resolveGitCommonDir(request.cwd) : void 0;
24192
+ args = buildTurnArgs({
24193
+ description: config.description,
24194
+ prompt,
24195
+ systemPromptPath,
24196
+ jsonSchema: prepared.jsonSchema,
24197
+ mcpServers: prepared.mcpServers,
24198
+ ...mcpConfigPath === void 0 ? {} : { mcpConfigPath },
24199
+ ...outputSchemaPath === void 0 ? {} : { outputSchemaPath },
24200
+ ...request.model?.model !== void 0 ? { model: request.model.model } : {},
24201
+ ...request.model?.thinking !== void 0 ? { effort: request.model.thinking } : {},
24202
+ sessionId,
24203
+ sessionKind,
24204
+ cwd: request.cwd,
24205
+ ...gitCommonDir === void 0 ? {} : { writableRoots: [gitCommonDir] }
24206
+ });
24207
+ } catch (error) {
24208
+ const message = error instanceof Error ? error.message : String(error);
24209
+ return {
24210
+ status: "terminal",
24211
+ result: failure("session", "HeadlessArgvFailure", "argv-failed", { diagnostic: message }, message)
24212
+ };
24213
+ }
24214
+ const codexObserver = codex ? createCodexExecTurnObserver() : void 0;
23864
24215
  let spawned;
23865
24216
  try {
23866
24217
  spawned = await spawnHeadlessTurn({
@@ -23886,6 +24237,7 @@ function createHeadlessRoleTurnHost(config) {
23886
24237
  source: "headless-host",
23887
24238
  event
23888
24239
  });
24240
+ codexObserver?.observe(event);
23889
24241
  }
23890
24242
  });
23891
24243
  } catch (error) {
@@ -23919,6 +24271,64 @@ function createHeadlessRoleTurnHost(config) {
23919
24271
  details: { sessionId }
23920
24272
  });
23921
24273
  }
24274
+ if (codex) {
24275
+ const observation = codexObserver.result();
24276
+ if (observation.threadId === void 0 || observation.threadId === "") {
24277
+ return terminalFromSpawned(spawned, {
24278
+ cause: "session",
24279
+ identity: { name: "HeadlessMissingThreadId", code: "missing-thread-id" },
24280
+ diagnostic: "codex exec emitted no thread.started thread_id",
24281
+ details: { sessionId, exitCode: spawned.code }
24282
+ });
24283
+ }
24284
+ sessionId = observation.threadId;
24285
+ await config.sessionIdentity.bind(request.principal, sessionId);
24286
+ if (observation.failureDiagnostic !== void 0) {
24287
+ return terminalFromSpawned(spawned, {
24288
+ cause: "output",
24289
+ identity: { name: "HeadlessCliError", code: "codex-turn-failed" },
24290
+ diagnostic: observation.failureDiagnostic,
24291
+ details: { sessionId, exitCode: spawned.code }
24292
+ });
24293
+ }
24294
+ if (spawned.code !== 0 && spawned.code !== null) {
24295
+ return terminalFromSpawned(spawned, {
24296
+ cause: "output",
24297
+ identity: { name: "HeadlessCliError", code: "codex-nonzero-exit" },
24298
+ diagnostic: spawned.stderr.trim() || `codex exec exited ${String(spawned.code)}`,
24299
+ details: { sessionId, exitCode: spawned.code }
24300
+ });
24301
+ }
24302
+ if (!observation.turnCompleted) {
24303
+ return terminalFromSpawned(spawned, {
24304
+ cause: "output",
24305
+ identity: { name: "HeadlessCliError", code: "codex-missing-terminal-event" },
24306
+ diagnostic: "codex exec exited without turn.completed",
24307
+ details: { sessionId, exitCode: spawned.code }
24308
+ });
24309
+ }
24310
+ if (observation.finalMessage === void 0) {
24311
+ return terminalFromSpawned(spawned, {
24312
+ cause: "output",
24313
+ identity: { name: "HeadlessEmptyOutput", code: "empty-stdout" },
24314
+ diagnostic: spawned.stderr.trim() || "codex exec produced no agent_message",
24315
+ details: { sessionId, exitCode: spawned.code }
24316
+ });
24317
+ }
24318
+ let receipt;
24319
+ try {
24320
+ receipt = JSON.parse(observation.finalMessage);
24321
+ } catch {
24322
+ return terminalFromSpawned(spawned, {
24323
+ cause: "output",
24324
+ identity: { name: "HeadlessEmptyOutput", code: "unparseable-final-message" },
24325
+ diagnostic: "codex final agent_message was not JSON",
24326
+ details: { sessionId, exitCode: spawned.code }
24327
+ });
24328
+ }
24329
+ await prepared.ingestStructuredOutput(receipt);
24330
+ return { status: "delivered", stderr: spawned.stderr };
24331
+ }
23922
24332
  const envelope = parseHeadlessCliStdout(spawned.stdout);
23923
24333
  if (envelope === void 0) {
23924
24334
  return terminalFromSpawned(spawned, {