@lesscap/baton-cli 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/baton.mjs +1014 -83
  2. package/package.json +5 -1
package/dist/baton.mjs CHANGED
@@ -3724,14 +3724,14 @@ function isUppercase(char = "") {
3724
3724
  if (NUMBER_CHAR_RE.test(char)) return;
3725
3725
  return char !== char.toLowerCase();
3726
3726
  }
3727
- function splitByCase(str, separators) {
3727
+ function splitByCase(str3, separators) {
3728
3728
  const splitters = separators ?? STR_SPLITTERS;
3729
3729
  const parts = [];
3730
- if (!str || typeof str !== "string") return parts;
3730
+ if (!str3 || typeof str3 !== "string") return parts;
3731
3731
  let buff = "";
3732
3732
  let previousUpper;
3733
3733
  let previousSplitter;
3734
- for (const char of str) {
3734
+ for (const char of str3) {
3735
3735
  const isSplitter = splitters.includes(char);
3736
3736
  if (isSplitter === true) {
3737
3737
  parts.push(buff);
@@ -3762,23 +3762,23 @@ function splitByCase(str, separators) {
3762
3762
  parts.push(buff);
3763
3763
  return parts;
3764
3764
  }
3765
- function upperFirst(str) {
3766
- return str ? str[0].toUpperCase() + str.slice(1) : "";
3765
+ function upperFirst(str3) {
3766
+ return str3 ? str3[0].toUpperCase() + str3.slice(1) : "";
3767
3767
  }
3768
- function lowerFirst(str) {
3769
- return str ? str[0].toLowerCase() + str.slice(1) : "";
3768
+ function lowerFirst(str3) {
3769
+ return str3 ? str3[0].toLowerCase() + str3.slice(1) : "";
3770
3770
  }
3771
- function pascalCase(str, opts) {
3772
- return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p)).join("") : "";
3771
+ function pascalCase(str3, opts) {
3772
+ return str3 ? (Array.isArray(str3) ? str3 : splitByCase(str3)).map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p)).join("") : "";
3773
3773
  }
3774
- function camelCase(str, opts) {
3775
- return lowerFirst(pascalCase(str || "", opts));
3774
+ function camelCase(str3, opts) {
3775
+ return lowerFirst(pascalCase(str3 || "", opts));
3776
3776
  }
3777
- function kebabCase(str, joiner) {
3778
- return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
3777
+ function kebabCase(str3, joiner) {
3778
+ return str3 ? (Array.isArray(str3) ? str3 : splitByCase(str3)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
3779
3779
  }
3780
- function snakeCase(str) {
3781
- return kebabCase(str || "", "_");
3780
+ function snakeCase(str3) {
3781
+ return kebabCase(str3 || "", "_");
3782
3782
  }
3783
3783
 
3784
3784
  // ../../node_modules/.pnpm/citty@0.2.2/node_modules/citty/dist/index.mjs
@@ -4777,30 +4777,30 @@ import { join } from "node:path";
4777
4777
  var PROJECT_CONFIG_NAME = ".baton.json";
4778
4778
  var projectConfigPath = (cwd = process.cwd()) => join(cwd, PROJECT_CONFIG_NAME);
4779
4779
  var configPathFromArgs = (args, cwd) => args.config ?? projectConfigPath(cwd);
4780
- var loadProjectConfig = (path) => {
4780
+ var loadProjectConfig = (path3) => {
4781
4781
  try {
4782
- return JSON.parse(readFileSync(path, "utf8"));
4782
+ return JSON.parse(readFileSync(path3, "utf8"));
4783
4783
  } catch (err) {
4784
- const cause = err.code === "ENOENT" ? `not found at ${path}` : String(err);
4784
+ const cause = err.code === "ENOENT" ? `not found at ${path3}` : String(err);
4785
4785
  throw new Error(`${PROJECT_CONFIG_NAME} ${cause} \u2014 run \`baton init\` first`);
4786
4786
  }
4787
4787
  };
4788
- var loadProjectConfigOrNull = (path) => {
4789
- if (!existsSync(path)) return null;
4788
+ var loadProjectConfigOrNull = (path3) => {
4789
+ if (!existsSync(path3)) return null;
4790
4790
  try {
4791
- return JSON.parse(readFileSync(path, "utf8"));
4791
+ return JSON.parse(readFileSync(path3, "utf8"));
4792
4792
  } catch {
4793
4793
  return null;
4794
4794
  }
4795
4795
  };
4796
- var saveProjectConfig = (path, config) => {
4797
- writeFileSync(path, `${JSON.stringify(config, null, 2)}
4796
+ var saveProjectConfig = (path3, config) => {
4797
+ writeFileSync(path3, `${JSON.stringify(config, null, 2)}
4798
4798
  `, "utf8");
4799
4799
  };
4800
- var patch = (path, fn) => {
4801
- saveProjectConfig(path, fn(loadProjectConfig(path)));
4800
+ var patch = (path3, fn) => {
4801
+ saveProjectConfig(path3, fn(loadProjectConfig(path3)));
4802
4802
  };
4803
- var setWorker = (path, w) => patch(path, (cfg) => ({ ...cfg, worker: w }));
4803
+ var setWorker = (path3, w) => patch(path3, (cfg) => ({ ...cfg, worker: w }));
4804
4804
  var requireBase = (cfg) => {
4805
4805
  if (!cfg.server || !cfg.project || !cfg.worker)
4806
4806
  throw new Error("project-config missing server/project/worker \u2014 run `baton worker register`");
@@ -4812,6 +4812,7 @@ var viewWorker = (cfg) => {
4812
4812
  server,
4813
4813
  projectId: project2,
4814
4814
  workerId: worker2.id,
4815
+ agentKind: worker2.agentKind ?? "claude-code",
4815
4816
  name: worker2.name,
4816
4817
  machineId: worker2.machineId,
4817
4818
  apiToken: worker2.apiToken
@@ -4820,7 +4821,13 @@ var viewWorker = (cfg) => {
4820
4821
  var worktreeConfig = (cfg) => ({
4821
4822
  server: cfg.server,
4822
4823
  project: cfg.projectId,
4823
- worker: { id: cfg.workerId, name: cfg.name, machineId: cfg.machineId, apiToken: cfg.apiToken }
4824
+ worker: {
4825
+ id: cfg.workerId,
4826
+ name: cfg.name,
4827
+ agentKind: cfg.agentKind,
4828
+ machineId: cfg.machineId,
4829
+ apiToken: cfg.apiToken
4830
+ }
4824
4831
  });
4825
4832
 
4826
4833
  // src/config.ts
@@ -4847,7 +4854,7 @@ var fmtComment = (c) => `${c.workerId !== void 0 ? `worker#${c.workerId}` : "you
4847
4854
  var fmtSession = (s) => `${s.id} ${s.name} ${s.attached ? "[active]" : "[inactive]"} ${s.worktreePath ?? "(pending)"}`;
4848
4855
  var fmtWorker = (w) => {
4849
4856
  const offline = w.connected === false ? " [offline]" : "";
4850
- return `W-${w.id} ${w.name} ${w.hostname}${offline}`;
4857
+ return `W-${w.id} ${w.name} ${w.hostname} ${w.agentKind}${offline}`;
4851
4858
  };
4852
4859
  var fmtInterval = (sec) => {
4853
4860
  if (sec % 86400 === 0) return `${sec / 86400}d`;
@@ -5133,7 +5140,12 @@ var createWorkerClient = (baseUrl, workerToken, sessionId) => {
5133
5140
  body: { active },
5134
5141
  headers: auth
5135
5142
  }),
5136
- listEvents: () => request(u(`/sessions/${sessionId}/events`), { method: "GET", headers: auth })
5143
+ listEvents: () => request(u(`/sessions/${sessionId}/events`), { method: "GET", headers: auth }),
5144
+ materialize: (input) => request(u(`/sessions/${sessionId}`), {
5145
+ method: "PATCH",
5146
+ body: input,
5147
+ headers: auth
5148
+ })
5137
5149
  };
5138
5150
  };
5139
5151
 
@@ -5554,7 +5566,7 @@ var BY_EXT = {
5554
5566
  ".json": "application/json",
5555
5567
  ".csv": "text/csv"
5556
5568
  };
5557
- var contentTypeForPath = (path) => BY_EXT[extname(path).toLowerCase()] ?? "application/octet-stream";
5569
+ var contentTypeForPath = (path3) => BY_EXT[extname(path3).toLowerCase()] ?? "application/octet-stream";
5558
5570
 
5559
5571
  // src/commands/attach.ts
5560
5572
  import { openAsBlob } from "node:fs";
@@ -5562,12 +5574,12 @@ import { basename } from "node:path";
5562
5574
  var attachPaths = (raw) => (raw === void 0 ? [] : Array.isArray(raw) ? raw : [raw]).flatMap((p) => p.split(",")).map((p) => p.trim()).filter(Boolean);
5563
5575
  var uploadAttachments = async (client, sessionId, paths) => {
5564
5576
  const out = [];
5565
- for (const path of paths) {
5566
- const body = await openAsBlob(path, { type: contentTypeForPath(path) });
5577
+ for (const path3 of paths) {
5578
+ const body = await openAsBlob(path3, { type: contentTypeForPath(path3) });
5567
5579
  out.push(
5568
5580
  await client.sessions.uploadAttachment(sessionId, {
5569
- filename: basename(path),
5570
- contentType: contentTypeForPath(path),
5581
+ filename: basename(path3),
5582
+ contentType: contentTypeForPath(path3),
5571
5583
  body
5572
5584
  })
5573
5585
  );
@@ -5600,12 +5612,12 @@ var sendCommand = defineCommand({
5600
5612
  if (text === void 0 && args.file) text = readFileSync2(args.file, "utf8");
5601
5613
  if (text === void 0 && !process.stdin.isTTY) text = await readStdin();
5602
5614
  const attachments = [];
5603
- for (const path of attachPaths(args.attach)) {
5615
+ for (const path3 of attachPaths(args.attach)) {
5604
5616
  attachments.push(
5605
5617
  await client.uploadAttachment(args.channel, {
5606
- filename: basename2(path),
5607
- contentType: contentTypeForPath(path),
5608
- body: await openAsBlob2(path, { type: contentTypeForPath(path) })
5618
+ filename: basename2(path3),
5619
+ contentType: contentTypeForPath(path3),
5620
+ body: await openAsBlob2(path3, { type: contentTypeForPath(path3) })
5609
5621
  })
5610
5622
  );
5611
5623
  }
@@ -6451,10 +6463,10 @@ var parseFirstExchange = (content) => {
6451
6463
  return userText || assistantText ? { userText, assistantText } : null;
6452
6464
  };
6453
6465
  var readFirstExchange = (agentSessionId) => {
6454
- const path = findTranscriptPath(agentSessionId);
6455
- if (!path) return null;
6466
+ const path3 = findTranscriptPath(agentSessionId);
6467
+ if (!path3) return null;
6456
6468
  try {
6457
- return parseFirstExchange(readFileSync4(path, "utf8"));
6469
+ return parseFirstExchange(readFileSync4(path3, "utf8"));
6458
6470
  } catch {
6459
6471
  return null;
6460
6472
  }
@@ -6511,6 +6523,687 @@ var augmentPrompt = (text, relPaths, labels) => {
6511
6523
  ${text}` : header;
6512
6524
  };
6513
6525
 
6526
+ // ../../node_modules/.pnpm/@openai+codex-sdk@0.142.5/node_modules/@openai/codex-sdk/dist/index.js
6527
+ import { promises as fs } from "fs";
6528
+ import os from "os";
6529
+ import path from "path";
6530
+ import { spawn } from "child_process";
6531
+ import { statSync as statSync2 } from "fs";
6532
+ import path2 from "path";
6533
+ import readline2 from "readline";
6534
+ import { createRequire } from "module";
6535
+ async function createOutputSchemaFile(schema) {
6536
+ if (schema === void 0) {
6537
+ return { cleanup: async () => {
6538
+ } };
6539
+ }
6540
+ if (!isJsonObject(schema)) {
6541
+ throw new Error("outputSchema must be a plain JSON object");
6542
+ }
6543
+ const schemaDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-output-schema-"));
6544
+ const schemaPath = path.join(schemaDir, "schema.json");
6545
+ const cleanup = async () => {
6546
+ try {
6547
+ await fs.rm(schemaDir, { recursive: true, force: true });
6548
+ } catch {
6549
+ }
6550
+ };
6551
+ try {
6552
+ await fs.writeFile(schemaPath, JSON.stringify(schema), "utf8");
6553
+ return { schemaPath, cleanup };
6554
+ } catch (error) {
6555
+ await cleanup();
6556
+ throw error;
6557
+ }
6558
+ }
6559
+ function isJsonObject(value) {
6560
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6561
+ }
6562
+ var Thread = class {
6563
+ _exec;
6564
+ _options;
6565
+ _id;
6566
+ _threadOptions;
6567
+ /** Returns the ID of the thread. Populated after the first turn starts. */
6568
+ get id() {
6569
+ return this._id;
6570
+ }
6571
+ /* @internal */
6572
+ constructor(exec, options, threadOptions, id = null) {
6573
+ this._exec = exec;
6574
+ this._options = options;
6575
+ this._id = id;
6576
+ this._threadOptions = threadOptions;
6577
+ }
6578
+ /** Provides the input to the agent and streams events as they are produced during the turn. */
6579
+ async runStreamed(input, turnOptions = {}) {
6580
+ return { events: this.runStreamedInternal(input, turnOptions) };
6581
+ }
6582
+ async *runStreamedInternal(input, turnOptions = {}) {
6583
+ const { schemaPath, cleanup } = await createOutputSchemaFile(turnOptions.outputSchema);
6584
+ const options = this._threadOptions;
6585
+ const { prompt, images } = normalizeInput(input);
6586
+ const generator = this._exec.run({
6587
+ input: prompt,
6588
+ baseUrl: this._options.baseUrl,
6589
+ apiKey: this._options.apiKey,
6590
+ threadId: this._id,
6591
+ images,
6592
+ model: options?.model,
6593
+ sandboxMode: options?.sandboxMode,
6594
+ workingDirectory: options?.workingDirectory,
6595
+ skipGitRepoCheck: options?.skipGitRepoCheck,
6596
+ outputSchemaFile: schemaPath,
6597
+ modelReasoningEffort: options?.modelReasoningEffort,
6598
+ signal: turnOptions.signal,
6599
+ networkAccessEnabled: options?.networkAccessEnabled,
6600
+ webSearchMode: options?.webSearchMode,
6601
+ webSearchEnabled: options?.webSearchEnabled,
6602
+ approvalPolicy: options?.approvalPolicy,
6603
+ additionalDirectories: options?.additionalDirectories
6604
+ });
6605
+ try {
6606
+ for await (const item of generator) {
6607
+ let parsed;
6608
+ try {
6609
+ parsed = JSON.parse(item);
6610
+ } catch (error) {
6611
+ throw new Error(`Failed to parse item: ${item}`, { cause: error });
6612
+ }
6613
+ if (parsed.type === "thread.started") {
6614
+ this._id = parsed.thread_id;
6615
+ }
6616
+ yield parsed;
6617
+ }
6618
+ } finally {
6619
+ await cleanup();
6620
+ }
6621
+ }
6622
+ /** Provides the input to the agent and returns the completed turn. */
6623
+ async run(input, turnOptions = {}) {
6624
+ const generator = this.runStreamedInternal(input, turnOptions);
6625
+ const items = [];
6626
+ let finalResponse = "";
6627
+ let usage = null;
6628
+ let turnFailure = null;
6629
+ for await (const event of generator) {
6630
+ if (event.type === "item.completed") {
6631
+ if (event.item.type === "agent_message") {
6632
+ finalResponse = event.item.text;
6633
+ }
6634
+ items.push(event.item);
6635
+ } else if (event.type === "turn.completed") {
6636
+ usage = event.usage;
6637
+ } else if (event.type === "turn.failed") {
6638
+ turnFailure = event.error;
6639
+ break;
6640
+ }
6641
+ }
6642
+ if (turnFailure) {
6643
+ throw new Error(turnFailure.message);
6644
+ }
6645
+ return { items, finalResponse, usage };
6646
+ }
6647
+ };
6648
+ function normalizeInput(input) {
6649
+ if (typeof input === "string") {
6650
+ return { prompt: input, images: [] };
6651
+ }
6652
+ const promptParts = [];
6653
+ const images = [];
6654
+ for (const item of input) {
6655
+ if (item.type === "text") {
6656
+ promptParts.push(item.text);
6657
+ } else if (item.type === "local_image") {
6658
+ images.push(item.path);
6659
+ }
6660
+ }
6661
+ return { prompt: promptParts.join("\n\n"), images };
6662
+ }
6663
+ var INTERNAL_ORIGINATOR_ENV = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
6664
+ var TYPESCRIPT_SDK_ORIGINATOR = "codex_sdk_ts";
6665
+ var CODEX_NPM_NAME = "@openai/codex";
6666
+ var PLATFORM_PACKAGE_BY_TARGET = {
6667
+ "x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
6668
+ "aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
6669
+ "x86_64-apple-darwin": "@openai/codex-darwin-x64",
6670
+ "aarch64-apple-darwin": "@openai/codex-darwin-arm64",
6671
+ "x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
6672
+ "aarch64-pc-windows-msvc": "@openai/codex-win32-arm64"
6673
+ };
6674
+ var moduleRequire = createRequire(import.meta.url);
6675
+ var CodexExec = class {
6676
+ executablePath;
6677
+ pathDirs;
6678
+ envOverride;
6679
+ configOverrides;
6680
+ constructor(executablePath = null, env, configOverrides) {
6681
+ if (executablePath) {
6682
+ this.executablePath = executablePath;
6683
+ this.pathDirs = [];
6684
+ } else {
6685
+ const resolved = findCodexPath();
6686
+ this.executablePath = resolved.executablePath;
6687
+ this.pathDirs = resolved.pathDirs;
6688
+ }
6689
+ this.envOverride = env;
6690
+ this.configOverrides = configOverrides;
6691
+ }
6692
+ async *run(args) {
6693
+ const commandArgs = ["exec", "--experimental-json"];
6694
+ if (this.configOverrides) {
6695
+ for (const override of serializeConfigOverrides(this.configOverrides)) {
6696
+ commandArgs.push("--config", override);
6697
+ }
6698
+ }
6699
+ if (args.baseUrl) {
6700
+ commandArgs.push(
6701
+ "--config",
6702
+ `openai_base_url=${toTomlValue(args.baseUrl, "openai_base_url")}`
6703
+ );
6704
+ }
6705
+ if (args.model) {
6706
+ commandArgs.push("--model", args.model);
6707
+ }
6708
+ if (args.sandboxMode) {
6709
+ commandArgs.push("--sandbox", args.sandboxMode);
6710
+ }
6711
+ if (args.workingDirectory) {
6712
+ commandArgs.push("--cd", args.workingDirectory);
6713
+ }
6714
+ if (args.additionalDirectories?.length) {
6715
+ for (const dir of args.additionalDirectories) {
6716
+ commandArgs.push("--add-dir", dir);
6717
+ }
6718
+ }
6719
+ if (args.skipGitRepoCheck) {
6720
+ commandArgs.push("--skip-git-repo-check");
6721
+ }
6722
+ if (args.outputSchemaFile) {
6723
+ commandArgs.push("--output-schema", args.outputSchemaFile);
6724
+ }
6725
+ if (args.modelReasoningEffort) {
6726
+ commandArgs.push("--config", `model_reasoning_effort="${args.modelReasoningEffort}"`);
6727
+ }
6728
+ if (args.networkAccessEnabled !== void 0) {
6729
+ commandArgs.push(
6730
+ "--config",
6731
+ `sandbox_workspace_write.network_access=${args.networkAccessEnabled}`
6732
+ );
6733
+ }
6734
+ if (args.webSearchMode) {
6735
+ commandArgs.push("--config", `web_search="${args.webSearchMode}"`);
6736
+ } else if (args.webSearchEnabled === true) {
6737
+ commandArgs.push("--config", `web_search="live"`);
6738
+ } else if (args.webSearchEnabled === false) {
6739
+ commandArgs.push("--config", `web_search="disabled"`);
6740
+ }
6741
+ if (args.approvalPolicy) {
6742
+ commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`);
6743
+ }
6744
+ if (args.threadId) {
6745
+ commandArgs.push("resume", args.threadId);
6746
+ }
6747
+ if (args.images?.length) {
6748
+ for (const image of args.images) {
6749
+ commandArgs.push("--image", image);
6750
+ }
6751
+ }
6752
+ const env = {};
6753
+ if (this.envOverride) {
6754
+ Object.assign(env, this.envOverride);
6755
+ } else {
6756
+ for (const [key, value] of Object.entries(process.env)) {
6757
+ if (value !== void 0) {
6758
+ env[key] = value;
6759
+ }
6760
+ }
6761
+ }
6762
+ if (!env[INTERNAL_ORIGINATOR_ENV]) {
6763
+ env[INTERNAL_ORIGINATOR_ENV] = TYPESCRIPT_SDK_ORIGINATOR;
6764
+ }
6765
+ if (args.apiKey) {
6766
+ env.CODEX_API_KEY = args.apiKey;
6767
+ }
6768
+ if (this.pathDirs.length > 0) {
6769
+ prependPathDirs(env, this.pathDirs);
6770
+ }
6771
+ const child = spawn(this.executablePath, commandArgs, {
6772
+ env,
6773
+ signal: args.signal
6774
+ });
6775
+ let spawnError = null;
6776
+ child.once("error", (err) => spawnError = err);
6777
+ if (!child.stdin) {
6778
+ child.kill();
6779
+ throw new Error("Child process has no stdin");
6780
+ }
6781
+ child.stdin.write(args.input);
6782
+ child.stdin.end();
6783
+ if (!child.stdout) {
6784
+ child.kill();
6785
+ throw new Error("Child process has no stdout");
6786
+ }
6787
+ const stderrChunks = [];
6788
+ if (child.stderr) {
6789
+ child.stderr.on("data", (data) => {
6790
+ stderrChunks.push(data);
6791
+ });
6792
+ }
6793
+ const exitPromise = new Promise(
6794
+ (resolve2) => {
6795
+ child.once("exit", (code, signal) => {
6796
+ resolve2({ code, signal });
6797
+ });
6798
+ }
6799
+ );
6800
+ const rl = readline2.createInterface({
6801
+ input: child.stdout,
6802
+ crlfDelay: Infinity
6803
+ });
6804
+ try {
6805
+ for await (const line of rl) {
6806
+ yield line;
6807
+ }
6808
+ if (spawnError) throw spawnError;
6809
+ const { code, signal } = await exitPromise;
6810
+ if (code !== 0 || signal) {
6811
+ const stderrBuffer = Buffer.concat(stderrChunks);
6812
+ const detail = signal ? `signal ${signal}` : `code ${code ?? 1}`;
6813
+ throw new Error(`Codex Exec exited with ${detail}: ${stderrBuffer.toString("utf8")}`);
6814
+ }
6815
+ } finally {
6816
+ rl.close();
6817
+ child.removeAllListeners();
6818
+ try {
6819
+ if (!child.killed) child.kill();
6820
+ } catch {
6821
+ }
6822
+ }
6823
+ }
6824
+ };
6825
+ function serializeConfigOverrides(configOverrides) {
6826
+ const overrides = [];
6827
+ flattenConfigOverrides(configOverrides, "", overrides);
6828
+ return overrides;
6829
+ }
6830
+ function flattenConfigOverrides(value, prefix, overrides) {
6831
+ if (!isPlainObject(value)) {
6832
+ if (prefix) {
6833
+ overrides.push(`${prefix}=${toTomlValue(value, prefix)}`);
6834
+ return;
6835
+ } else {
6836
+ throw new Error("Codex config overrides must be a plain object");
6837
+ }
6838
+ }
6839
+ const entries = Object.entries(value);
6840
+ if (!prefix && entries.length === 0) {
6841
+ return;
6842
+ }
6843
+ if (prefix && entries.length === 0) {
6844
+ overrides.push(`${prefix}={}`);
6845
+ return;
6846
+ }
6847
+ for (const [key, child] of entries) {
6848
+ if (!key) {
6849
+ throw new Error("Codex config override keys must be non-empty strings");
6850
+ }
6851
+ if (child === void 0) {
6852
+ continue;
6853
+ }
6854
+ const path3 = prefix ? `${prefix}.${key}` : key;
6855
+ if (isPlainObject(child)) {
6856
+ flattenConfigOverrides(child, path3, overrides);
6857
+ } else {
6858
+ overrides.push(`${path3}=${toTomlValue(child, path3)}`);
6859
+ }
6860
+ }
6861
+ }
6862
+ function toTomlValue(value, path3) {
6863
+ if (typeof value === "string") {
6864
+ return JSON.stringify(value);
6865
+ } else if (typeof value === "number") {
6866
+ if (!Number.isFinite(value)) {
6867
+ throw new Error(`Codex config override at ${path3} must be a finite number`);
6868
+ }
6869
+ return `${value}`;
6870
+ } else if (typeof value === "boolean") {
6871
+ return value ? "true" : "false";
6872
+ } else if (Array.isArray(value)) {
6873
+ const rendered = value.map((item, index) => toTomlValue(item, `${path3}[${index}]`));
6874
+ return `[${rendered.join(", ")}]`;
6875
+ } else if (isPlainObject(value)) {
6876
+ const parts = [];
6877
+ for (const [key, child] of Object.entries(value)) {
6878
+ if (!key) {
6879
+ throw new Error("Codex config override keys must be non-empty strings");
6880
+ }
6881
+ if (child === void 0) {
6882
+ continue;
6883
+ }
6884
+ parts.push(`${formatTomlKey(key)} = ${toTomlValue(child, `${path3}.${key}`)}`);
6885
+ }
6886
+ return `{${parts.join(", ")}}`;
6887
+ } else if (value === null) {
6888
+ throw new Error(`Codex config override at ${path3} cannot be null`);
6889
+ } else {
6890
+ const typeName = typeof value;
6891
+ throw new Error(`Unsupported Codex config override value at ${path3}: ${typeName}`);
6892
+ }
6893
+ }
6894
+ var TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;
6895
+ function formatTomlKey(key) {
6896
+ return TOML_BARE_KEY.test(key) ? key : JSON.stringify(key);
6897
+ }
6898
+ function isPlainObject(value) {
6899
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6900
+ }
6901
+ function findCodexPath() {
6902
+ const { platform, arch } = process;
6903
+ let targetTriple = null;
6904
+ switch (platform) {
6905
+ case "linux":
6906
+ case "android":
6907
+ switch (arch) {
6908
+ case "x64":
6909
+ targetTriple = "x86_64-unknown-linux-musl";
6910
+ break;
6911
+ case "arm64":
6912
+ targetTriple = "aarch64-unknown-linux-musl";
6913
+ break;
6914
+ default:
6915
+ break;
6916
+ }
6917
+ break;
6918
+ case "darwin":
6919
+ switch (arch) {
6920
+ case "x64":
6921
+ targetTriple = "x86_64-apple-darwin";
6922
+ break;
6923
+ case "arm64":
6924
+ targetTriple = "aarch64-apple-darwin";
6925
+ break;
6926
+ default:
6927
+ break;
6928
+ }
6929
+ break;
6930
+ case "win32":
6931
+ switch (arch) {
6932
+ case "x64":
6933
+ targetTriple = "x86_64-pc-windows-msvc";
6934
+ break;
6935
+ case "arm64":
6936
+ targetTriple = "aarch64-pc-windows-msvc";
6937
+ break;
6938
+ default:
6939
+ break;
6940
+ }
6941
+ break;
6942
+ default:
6943
+ break;
6944
+ }
6945
+ if (!targetTriple) {
6946
+ throw new Error(`Unsupported platform: ${platform} (${arch})`);
6947
+ }
6948
+ const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
6949
+ if (!platformPackage) {
6950
+ throw new Error(`Unsupported target triple: ${targetTriple}`);
6951
+ }
6952
+ let vendorRoot;
6953
+ try {
6954
+ const codexPackageJsonPath = moduleRequire.resolve(`${CODEX_NPM_NAME}/package.json`);
6955
+ const codexRequire = createRequire(codexPackageJsonPath);
6956
+ const platformPackageJsonPath = codexRequire.resolve(`${platformPackage}/package.json`);
6957
+ vendorRoot = path2.join(path2.dirname(platformPackageJsonPath), "vendor");
6958
+ } catch {
6959
+ throw new Error(
6960
+ `Unable to locate Codex CLI binaries. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
6961
+ );
6962
+ }
6963
+ const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
6964
+ const nativePackage = resolveNativePackage(vendorRoot, targetTriple, codexBinaryName);
6965
+ if (!nativePackage) {
6966
+ throw new Error(
6967
+ `Unable to locate Codex CLI binaries for ${targetTriple}. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies.`
6968
+ );
6969
+ }
6970
+ return nativePackage;
6971
+ }
6972
+ function resolveNativePackage(vendorRoot, targetTriple, codexBinaryName) {
6973
+ const packageRoot = path2.join(vendorRoot, targetTriple);
6974
+ const packageBinaryPath = path2.join(packageRoot, "bin", codexBinaryName);
6975
+ if (isFile(packageBinaryPath) && isFile(path2.join(packageRoot, "codex-package.json"))) {
6976
+ return {
6977
+ executablePath: packageBinaryPath,
6978
+ pathDirs: existingDirs(path2.join(packageRoot, "codex-path"))
6979
+ };
6980
+ }
6981
+ const legacyBinaryPath = path2.join(packageRoot, "codex", codexBinaryName);
6982
+ if (isFile(legacyBinaryPath)) {
6983
+ return {
6984
+ executablePath: legacyBinaryPath,
6985
+ pathDirs: existingDirs(path2.join(packageRoot, "path"))
6986
+ };
6987
+ }
6988
+ return null;
6989
+ }
6990
+ function existingDirs(...dirs) {
6991
+ return dirs.filter(isDirectory);
6992
+ }
6993
+ function prependPathDirs(env, pathDirs, platform = process.platform) {
6994
+ const pathKey = pathEnvKey(env, platform);
6995
+ if (platform === "win32") {
6996
+ for (const key of Object.keys(env)) {
6997
+ if (key.toLowerCase() === "path" && key !== pathKey) {
6998
+ delete env[key];
6999
+ }
7000
+ }
7001
+ }
7002
+ const existingEntries = (env[pathKey] ?? "").split(path2.delimiter).filter((entry) => entry.length > 0 && !pathDirs.includes(entry));
7003
+ env[pathKey] = [...pathDirs, ...existingEntries].join(path2.delimiter);
7004
+ }
7005
+ function pathEnvKey(env, platform) {
7006
+ if (platform !== "win32") {
7007
+ return "PATH";
7008
+ }
7009
+ const matchingKeys = Object.keys(env).filter((key) => key.toLowerCase() === "path");
7010
+ return matchingKeys.includes("Path") ? "Path" : matchingKeys.at(-1) ?? "PATH";
7011
+ }
7012
+ function isFile(filePath) {
7013
+ try {
7014
+ return statSync2(filePath).isFile();
7015
+ } catch {
7016
+ return false;
7017
+ }
7018
+ }
7019
+ function isDirectory(filePath) {
7020
+ try {
7021
+ return statSync2(filePath).isDirectory();
7022
+ } catch {
7023
+ return false;
7024
+ }
7025
+ }
7026
+ var Codex = class {
7027
+ exec;
7028
+ options;
7029
+ constructor(options = {}) {
7030
+ const { codexPathOverride, env, config } = options;
7031
+ this.exec = new CodexExec(codexPathOverride, env, config);
7032
+ this.options = options;
7033
+ }
7034
+ /**
7035
+ * Starts a new conversation with an agent.
7036
+ * @returns A new thread instance.
7037
+ */
7038
+ startThread(options = {}) {
7039
+ return new Thread(this.exec, this.options, options);
7040
+ }
7041
+ /**
7042
+ * Resumes a conversation with an agent based on the thread id.
7043
+ * Threads are persisted in ~/.codex/sessions.
7044
+ *
7045
+ * @param id The id of the thread to resume.
7046
+ * @returns A new thread instance.
7047
+ */
7048
+ resumeThread(id, options = {}) {
7049
+ return new Thread(this.exec, this.options, options, id);
7050
+ }
7051
+ };
7052
+
7053
+ // src/session/runner/codex.ts
7054
+ var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7055
+ var str = (v) => typeof v === "string" ? v : void 0;
7056
+ var compactEnv = (env) => {
7057
+ if (!env) return void 0;
7058
+ const out = {};
7059
+ for (const [key, value] of Object.entries(env)) {
7060
+ if (value !== void 0) out[key] = value;
7061
+ }
7062
+ return out;
7063
+ };
7064
+ var statusOf = (v) => v === "in_progress" || v === "failed" || v === "completed" ? v : "completed";
7065
+ var itemId = (item) => str(item.id) ?? `${str(item.type) ?? "item"}-${Math.random().toString(36).slice(2)}`;
7066
+ var todoItems = (raw) => Array.isArray(raw) ? raw.filter(isRecord2).map((todo) => ({
7067
+ text: str(todo.text) ?? str(todo.content) ?? "",
7068
+ completed: todo.completed === true || todo.status === "completed"
7069
+ })) : [];
7070
+ var fileKind = (v) => v === "add" || v === "delete" || v === "update" ? v : "update";
7071
+ var toAgentItem = (raw) => {
7072
+ if (!isRecord2(raw)) {
7073
+ return {
7074
+ type: "error",
7075
+ id: "bad-item",
7076
+ status: "failed",
7077
+ message: "codex item was not an object"
7078
+ };
7079
+ }
7080
+ const type = str(raw.type);
7081
+ const id = itemId(raw);
7082
+ const status = statusOf(raw.status);
7083
+ if (type === "agent_message") {
7084
+ return { type, id, status, text: str(raw.text) ?? str(raw.message) ?? "" };
7085
+ }
7086
+ if (type === "reasoning") {
7087
+ return { type, id, status, text: str(raw.text) ?? str(raw.summary) ?? "" };
7088
+ }
7089
+ if (type === "command_execution") {
7090
+ return {
7091
+ type,
7092
+ id,
7093
+ status,
7094
+ command: str(raw.command) ?? "",
7095
+ output: str(raw.aggregated_output) ?? str(raw.output) ?? "",
7096
+ exitCode: typeof raw.exit_code === "number" ? raw.exit_code : void 0
7097
+ };
7098
+ }
7099
+ if (type === "file_change") {
7100
+ const changes = Array.isArray(raw.changes) ? raw.changes.filter(isRecord2).map((change) => ({
7101
+ path: str(change.path) ?? "",
7102
+ kind: fileKind(change.kind)
7103
+ })) : [];
7104
+ return { type, id, status, changes };
7105
+ }
7106
+ if (type === "mcp_tool_call") {
7107
+ return {
7108
+ type,
7109
+ id,
7110
+ status,
7111
+ server: str(raw.server) ?? "",
7112
+ tool: str(raw.tool) ?? str(raw.name) ?? "",
7113
+ arguments: raw.arguments ?? raw.input ?? null,
7114
+ output: raw.result ?? raw.output ?? raw.error,
7115
+ isError: raw.is_error === true || raw.error !== void 0
7116
+ };
7117
+ }
7118
+ if (type === "web_search") {
7119
+ return { type, id, status, query: str(raw.query) ?? "" };
7120
+ }
7121
+ if (type === "todo_list") {
7122
+ return { type, id, status, items: todoItems(raw.items) };
7123
+ }
7124
+ if (type === "error") {
7125
+ return { type, id, status: "failed", message: str(raw.message) ?? "codex error" };
7126
+ }
7127
+ return {
7128
+ type: "tool_call",
7129
+ id,
7130
+ status,
7131
+ name: str(raw.name) ?? type ?? "codex_item",
7132
+ input: raw,
7133
+ output: raw.output
7134
+ };
7135
+ };
7136
+ var usageOf = (raw) => {
7137
+ if (!isRecord2(raw)) return void 0;
7138
+ return {
7139
+ inputTokens: typeof raw.input_tokens === "number" ? raw.input_tokens : void 0,
7140
+ cachedInputTokens: typeof raw.cached_input_tokens === "number" ? raw.cached_input_tokens : void 0,
7141
+ outputTokens: typeof raw.output_tokens === "number" ? raw.output_tokens : void 0,
7142
+ reasoningOutputTokens: typeof raw.reasoning_output_tokens === "number" ? raw.reasoning_output_tokens : void 0
7143
+ };
7144
+ };
7145
+ var toAgentEvent = (raw) => {
7146
+ if (!isRecord2(raw)) return { type: "raw", raw };
7147
+ const type = str(raw.type);
7148
+ if (type === "thread.started") {
7149
+ return {
7150
+ type,
7151
+ sessionId: str(raw.thread_id) ?? str(raw.sessionId) ?? "",
7152
+ model: str(raw.model),
7153
+ raw
7154
+ };
7155
+ }
7156
+ if (type === "turn.started") return { type, raw };
7157
+ if ((type === "item.started" || type === "item.updated" || type === "item.completed") && raw.item !== void 0) {
7158
+ return { type, item: toAgentItem(raw.item), raw };
7159
+ }
7160
+ if (type === "turn.completed") return { type, usage: usageOf(raw.usage), raw };
7161
+ if (type === "turn.failed") {
7162
+ const error = isRecord2(raw.error) ? raw.error : raw;
7163
+ return { type, error: { message: str(error.message) ?? "codex turn failed" }, raw };
7164
+ }
7165
+ if (type === "error") return { type, message: str(raw.message) ?? "codex error", raw };
7166
+ return { type: "raw", raw };
7167
+ };
7168
+ var withPlanPrefix = (input) => {
7169
+ const prefix = "Plan mode: inspect and propose a plan only. Do not edit files or run mutating commands.\n\n";
7170
+ if (typeof input === "string") return `${prefix}${input}`;
7171
+ return input.map(
7172
+ (entry, i) => i === 0 && entry.type === "text" ? { ...entry, text: `${prefix}${entry.text}` } : entry
7173
+ );
7174
+ };
7175
+ async function* startCodexEvents(config, input, worker2, opts) {
7176
+ const env = compactEnv(buildSdkEnv(opts.envOverlay));
7177
+ const codex = new Codex(env ? { env } : void 0);
7178
+ const resuming = !config.agentSessionId.startsWith("pending:");
7179
+ const addDirs = additionalDirs();
7180
+ const threadOptions = {
7181
+ workingDirectory: config.worktreePath,
7182
+ skipGitRepoCheck: true,
7183
+ sandboxMode: opts.planMode ? "read-only" : "workspace-write",
7184
+ approvalPolicy: "never",
7185
+ ...opts.model ? { model: opts.model } : {},
7186
+ ...addDirs ? { additionalDirectories: addDirs } : {}
7187
+ };
7188
+ const thread = resuming ? codex.resumeThread(config.agentSessionId, threadOptions) : codex.startThread(threadOptions);
7189
+ const runInput = opts.planMode ? withPlanPrefix(input) : input;
7190
+ const { events } = await thread.runStreamed(runInput, {
7191
+ ...opts.signal ? { signal: opts.signal } : {}
7192
+ });
7193
+ for await (const raw of events) {
7194
+ const event = toAgentEvent(raw);
7195
+ if (event.type === "thread.started" && !resuming && event.sessionId) {
7196
+ config.agentSessionId = event.sessionId;
7197
+ await worker2.materialize({
7198
+ agentSessionId: event.sessionId,
7199
+ worktreePath: config.worktreePath
7200
+ });
7201
+ opts.log(`[codex] thread ${event.sessionId}`);
7202
+ }
7203
+ yield event;
7204
+ }
7205
+ }
7206
+
6514
7207
  // src/session/runner/query.ts
6515
7208
  var startQuery = (config, text, resuming, queryFn, abortController, log, { envOverlay, planMode = false, model } = {}) => {
6516
7209
  const env = buildSdkEnv(envOverlay);
@@ -6539,21 +7232,223 @@ var startQuery = (config, text, resuming, queryFn, abortController, log, { envOv
6539
7232
  };
6540
7233
 
6541
7234
  // src/session/runner/stream.ts
6542
- var streamSdkEvents = async (messages, worker2) => {
7235
+ var streamAgentEvents = async (events, worker2) => {
6543
7236
  let result = null;
6544
- for await (const message of messages) {
6545
- await worker2.emitEvent("sdk_event", message);
6546
- if (message.type === "result") result = toTurnResult(message);
7237
+ for await (const event of events) {
7238
+ await worker2.emitEvent("agent_event", event);
7239
+ if (event.type === "turn.completed") {
7240
+ result = {
7241
+ subtype: event.subtype ?? "success",
7242
+ isError: false,
7243
+ resultText: ""
7244
+ };
7245
+ } else if (event.type === "turn.failed") {
7246
+ result = {
7247
+ subtype: event.error.subtype ?? "error",
7248
+ isError: true,
7249
+ resultText: event.error.message
7250
+ };
7251
+ }
6547
7252
  }
6548
7253
  return result;
6549
7254
  };
6550
- var toTurnResult = (message) => {
7255
+ var streamClaudeSdkEvents = (messages, worker2) => streamAgentEvents(claudeToAgentEvents(messages), worker2);
7256
+ var isRecord3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
7257
+ var str2 = (v) => typeof v === "string" ? v : void 0;
7258
+ var messageContent = (payload) => {
7259
+ const msg = payload.message;
7260
+ if (!isRecord3(msg) || !Array.isArray(msg.content)) return [];
7261
+ return msg.content.filter(isRecord3);
7262
+ };
7263
+ var textFromToolResult = (raw) => {
7264
+ const c = raw.content;
7265
+ if (typeof c === "string") return c;
7266
+ if (Array.isArray(c)) {
7267
+ return c.map((b) => isRecord3(b) && typeof b.text === "string" ? b.text : JSON.stringify(b)).join("\n");
7268
+ }
7269
+ return JSON.stringify(c ?? "");
7270
+ };
7271
+ var commandOf = (input) => {
7272
+ if (!isRecord3(input)) return "";
7273
+ return str2(input.command) ?? str2(input.cmd) ?? "";
7274
+ };
7275
+ var pathOf = (input) => {
7276
+ if (!isRecord3(input)) return void 0;
7277
+ return str2(input.file_path) ?? str2(input.path);
7278
+ };
7279
+ var fileKindOf = (name) => name === "Delete" ? "delete" : "update";
7280
+ var todoItems2 = (input) => {
7281
+ if (!isRecord3(input) || !Array.isArray(input.todos)) return [];
7282
+ return input.todos.filter(isRecord3).map((todo) => ({
7283
+ text: str2(todo.content) ?? str2(todo.text) ?? "",
7284
+ completed: todo.status === "completed" || todo.completed === true
7285
+ }));
7286
+ };
7287
+ var mcpParts = (name) => {
7288
+ const match = /^mcp__(.+?)__(.+)$/.exec(name);
7289
+ return match ? { server: match[1] ?? "", tool: match[2] ?? "" } : null;
7290
+ };
7291
+ var startedItemFromToolUse = (block) => {
7292
+ const id = str2(block.id) ?? `tool-${Math.random().toString(36).slice(2)}`;
7293
+ const name = str2(block.name) ?? "tool";
7294
+ const input = block.input ?? null;
7295
+ if (name === "Bash") {
7296
+ return {
7297
+ type: "command_execution",
7298
+ id,
7299
+ status: "in_progress",
7300
+ command: commandOf(input),
7301
+ output: ""
7302
+ };
7303
+ }
7304
+ if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "Delete") {
7305
+ const path3 = pathOf(input);
7306
+ return {
7307
+ type: "file_change",
7308
+ id,
7309
+ status: "in_progress",
7310
+ changes: path3 ? [{ path: path3, kind: fileKindOf(name) }] : []
7311
+ };
7312
+ }
7313
+ const mcp = mcpParts(name);
7314
+ if (mcp) {
7315
+ return {
7316
+ type: "mcp_tool_call",
7317
+ id,
7318
+ status: "in_progress",
7319
+ server: mcp.server,
7320
+ tool: mcp.tool,
7321
+ arguments: input
7322
+ };
7323
+ }
7324
+ if (name === "WebSearch" || name === "web_search") {
7325
+ return {
7326
+ type: "web_search",
7327
+ id,
7328
+ status: "in_progress",
7329
+ query: isRecord3(input) ? str2(input.query) ?? "" : ""
7330
+ };
7331
+ }
7332
+ if (name === "TodoWrite") {
7333
+ return { type: "todo_list", id, status: "in_progress", items: todoItems2(input) };
7334
+ }
7335
+ return { type: "tool_call", id, status: "in_progress", name, input };
7336
+ };
7337
+ var completeItem = (item, output, isError) => {
7338
+ if (item.type === "command_execution")
7339
+ return { ...item, status: isError ? "failed" : "completed", output };
7340
+ if (item.type === "mcp_tool_call")
7341
+ return { ...item, status: isError ? "failed" : "completed", output, isError };
7342
+ if (item.type === "tool_call")
7343
+ return { ...item, status: isError ? "failed" : "completed", output, isError };
7344
+ return { ...item, status: isError ? "failed" : "completed" };
7345
+ };
7346
+ var resultEvent = (message) => {
6551
7347
  const rec = message;
6552
- const subtype = typeof rec.subtype === "string" ? rec.subtype : "unknown";
7348
+ const subtype = str2(rec.subtype) ?? "unknown";
6553
7349
  const isError = rec.is_error === true || subtype !== "success";
6554
- const resultText = typeof rec.result === "string" ? rec.result : "";
6555
- return { subtype, isError, resultText };
7350
+ const resultText = str2(rec.result) ?? "";
7351
+ const usage = {
7352
+ numTurns: typeof rec.num_turns === "number" ? rec.num_turns : void 0,
7353
+ totalCostUsd: typeof rec.total_cost_usd === "number" ? rec.total_cost_usd : void 0,
7354
+ durationMs: typeof rec.duration_ms === "number" ? rec.duration_ms : void 0
7355
+ };
7356
+ return isError ? {
7357
+ type: "turn.failed",
7358
+ error: { message: resultText || `claude result: ${subtype}`, subtype },
7359
+ usage,
7360
+ raw: message
7361
+ } : { type: "turn.completed", subtype, usage, raw: message };
6556
7362
  };
7363
+ async function* claudeToAgentEvents(messages) {
7364
+ const pendingTools = /* @__PURE__ */ new Map();
7365
+ let systemEmitted = false;
7366
+ let textSeq = 0;
7367
+ let reasoningSeq = 0;
7368
+ for await (const message of messages) {
7369
+ const p = message;
7370
+ if (message.type === "system") {
7371
+ if (!systemEmitted) {
7372
+ systemEmitted = true;
7373
+ yield {
7374
+ type: "thread.started",
7375
+ sessionId: str2(p.session_id) ?? "",
7376
+ model: str2(p.model) ?? (isRecord3(p.model_info) ? str2(p.model_info.id) : void 0),
7377
+ raw: message
7378
+ };
7379
+ yield { type: "turn.started", raw: message };
7380
+ }
7381
+ continue;
7382
+ }
7383
+ if (message.type === "assistant") {
7384
+ for (const block of messageContent(p)) {
7385
+ if (block.type === "text" && typeof block.text === "string") {
7386
+ yield {
7387
+ type: "item.completed",
7388
+ item: {
7389
+ type: "agent_message",
7390
+ id: `assistant-${++textSeq}`,
7391
+ status: "completed",
7392
+ text: block.text
7393
+ },
7394
+ raw: block
7395
+ };
7396
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
7397
+ const text = block.thinking.trim();
7398
+ if (text) {
7399
+ yield {
7400
+ type: "item.completed",
7401
+ item: {
7402
+ type: "reasoning",
7403
+ id: `reasoning-${++reasoningSeq}`,
7404
+ status: "completed",
7405
+ text
7406
+ },
7407
+ raw: block
7408
+ };
7409
+ }
7410
+ } else if (block.type === "tool_use" || block.type === "server_tool_use") {
7411
+ const item = startedItemFromToolUse(block);
7412
+ pendingTools.set(item.id, item);
7413
+ yield { type: "item.started", item, raw: block };
7414
+ } else if (block.type === "tool_result") {
7415
+ const id = str2(block.tool_use_id) ?? "";
7416
+ const item = pendingTools.get(id);
7417
+ if (item) {
7418
+ const completed = completeItem(item, textFromToolResult(block), block.is_error === true);
7419
+ pendingTools.set(id, completed);
7420
+ yield { type: "item.completed", item: completed, raw: block };
7421
+ } else {
7422
+ yield { type: "raw", raw: block };
7423
+ }
7424
+ } else {
7425
+ yield { type: "raw", raw: block };
7426
+ }
7427
+ }
7428
+ continue;
7429
+ }
7430
+ if (message.type === "user") {
7431
+ for (const block of messageContent(p)) {
7432
+ if (block.type !== "tool_result") continue;
7433
+ const id = str2(block.tool_use_id) ?? "";
7434
+ const item = pendingTools.get(id);
7435
+ if (!item) {
7436
+ yield { type: "raw", raw: block };
7437
+ continue;
7438
+ }
7439
+ const completed = completeItem(item, textFromToolResult(block), block.is_error === true);
7440
+ pendingTools.set(id, completed);
7441
+ yield { type: "item.completed", item: completed, raw: block };
7442
+ }
7443
+ continue;
7444
+ }
7445
+ if (message.type === "result") {
7446
+ yield resultEvent(message);
7447
+ continue;
7448
+ }
7449
+ yield { type: "raw", raw: message };
7450
+ }
7451
+ }
6557
7452
 
6558
7453
  // src/session/runner/turn.ts
6559
7454
  var turnTimeoutMs = () => Number(process.env.BATON_TURN_TIMEOUT_MS) || 30 * 6e4;
@@ -6563,14 +7458,14 @@ var ABORTED = /* @__PURE__ */ Symbol("turn-aborted");
6563
7458
  var finalizeTurn = async (result, worker2, log) => {
6564
7459
  if (!result) {
6565
7460
  log("[exit] stream ended with no result");
6566
- await worker2.emitEvent("turn_error", { message: "claude produced no result" });
7461
+ await worker2.emitEvent("turn_error", { message: "agent produced no result" });
6567
7462
  await worker2.emitEvent("turn_complete", {});
6568
7463
  return -1;
6569
7464
  }
6570
7465
  log(`[exit] subtype=${result.subtype} isError=${result.isError}`);
6571
7466
  if (result.isError) {
6572
7467
  await worker2.emitEvent("turn_error", {
6573
- message: result.resultText || `claude result: ${result.subtype}`,
7468
+ message: result.resultText || `agent result: ${result.subtype}`,
6574
7469
  subtype: result.subtype
6575
7470
  });
6576
7471
  }
@@ -6578,7 +7473,7 @@ var finalizeTurn = async (result, worker2, log) => {
6578
7473
  return result.isError ? 1 : 0;
6579
7474
  };
6580
7475
  var buildPrompt = async (config, rawText, attachments, log, fetchImpl) => {
6581
- if (attachments.length === 0) return rawText;
7476
+ if (attachments.length === 0) return { text: rawText, input: rawText };
6582
7477
  const relPaths = await materializeAttachments({
6583
7478
  worktreePath: config.worktreePath,
6584
7479
  serverBase: config.server,
@@ -6586,7 +7481,13 @@ var buildPrompt = async (config, rawText, attachments, log, fetchImpl) => {
6586
7481
  fetchImpl
6587
7482
  });
6588
7483
  log(`[attach] downloaded ${relPaths.length} file(s) \u2192 ${config.worktreePath}/attachments`);
6589
- return augmentPrompt(rawText, relPaths, labelAttachments(attachments));
7484
+ const labels = labelAttachments(attachments);
7485
+ const text = augmentPrompt(rawText, relPaths, labels);
7486
+ const images = relPaths.flatMap((relPath, i) => {
7487
+ const att = attachments[i];
7488
+ return att?.contentType.startsWith("image/") ? [{ type: "local_image", path: `${config.worktreePath}/${relPath}` }] : [];
7489
+ });
7490
+ return images.length > 0 ? { text, input: [{ type: "text", text }, ...images] } : { text, input: text };
6590
7491
  };
6591
7492
  var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => console.log(m), envOverlay, fetchImpl, externalSignal) => {
6592
7493
  await worker2.emitEvent("turn_start", { messageId: msg.id });
@@ -6599,9 +7500,9 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6599
7500
  await worker2.emitEvent("turn_error", { message: "user_message missing text and attachments" });
6600
7501
  return -1;
6601
7502
  }
6602
- let text;
7503
+ let built;
6603
7504
  try {
6604
- text = await buildPrompt(config, rawText, attachments, log, fetchImpl);
7505
+ built = await buildPrompt(config, rawText, attachments, log, fetchImpl);
6605
7506
  } catch (err) {
6606
7507
  const message = err instanceof Error ? err.message : String(err);
6607
7508
  await worker2.emitEvent("turn_error", { message: `attachment download failed: ${message}` });
@@ -6622,12 +7523,23 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6622
7523
  heartbeat = setInterval(() => {
6623
7524
  void worker2.emitEvent("turn_heartbeat", {}).catch((e) => log(`[heartbeat] ${String(e)}`));
6624
7525
  }, heartbeatMs());
6625
- const messages = startQuery(config, text, resuming, queryFn, abort, log, {
6626
- envOverlay,
6627
- planMode,
6628
- model
6629
- });
6630
- const consume = streamSdkEvents(messages, worker2);
7526
+ const consume = config.agentKind === "codex" ? streamAgentEvents(
7527
+ startCodexEvents(config, built.input, worker2, {
7528
+ envOverlay,
7529
+ planMode,
7530
+ model,
7531
+ signal: abort.signal,
7532
+ log
7533
+ }),
7534
+ worker2
7535
+ ) : streamClaudeSdkEvents(
7536
+ startQuery(config, built.text, resuming, queryFn, abort, log, {
7537
+ envOverlay,
7538
+ planMode,
7539
+ model
7540
+ }),
7541
+ worker2
7542
+ );
6631
7543
  void consume.catch(() => {
6632
7544
  });
6633
7545
  const outcome = await Promise.race([consume, timeout, interrupted]);
@@ -6639,10 +7551,10 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6639
7551
  return -1;
6640
7552
  }
6641
7553
  if (outcome === TIMEOUT) {
6642
- log(`[watchdog] turn exceeded ${ceiling}ms \u2014 aborting claude`);
7554
+ log(`[watchdog] turn exceeded ${ceiling}ms \u2014 aborting agent`);
6643
7555
  abort.abort();
6644
7556
  await worker2.emitEvent("turn_error", {
6645
- message: `turn exceeded ${ceiling}ms \u2014 claude aborted`
7557
+ message: `turn exceeded ${ceiling}ms \u2014 agent aborted`
6646
7558
  });
6647
7559
  await worker2.emitEvent("turn_complete", {});
6648
7560
  return -1;
@@ -6698,7 +7610,7 @@ var runDaemon = async (config, deps, signal) => {
6698
7610
  log(`bin: ${claudeExecutable() ?? "(sdk bundled)"} cwd: ${config.worktreePath}`);
6699
7611
  log(`runtime env keys: ${maskedEnvKeys(deps.env)}`);
6700
7612
  const state = {
6701
- firstSpawnDone: findTranscriptPath(config.agentSessionId) !== null,
7613
+ firstSpawnDone: config.agentKind === "codex" ? !config.agentSessionId.startsWith("pending:") : findTranscriptPath(config.agentSessionId) !== null,
6702
7614
  seen: /* @__PURE__ */ new Set()
6703
7615
  };
6704
7616
  const pendingQueue = [];
@@ -6866,6 +7778,7 @@ var sessionRunCommand = defineCommand({
6866
7778
  server,
6867
7779
  sessionId,
6868
7780
  name: s.name,
7781
+ agentKind: s.agentKind,
6869
7782
  agentSessionId: s.agentSessionId,
6870
7783
  worktreePath: s.worktreePath
6871
7784
  };
@@ -7147,7 +8060,7 @@ var task = defineCommand({
7147
8060
  }
7148
8061
  });
7149
8062
 
7150
- // src/commands/worker.ts
8063
+ // src/commands/worker/index.ts
7151
8064
  import { hostname as osHostname } from "node:os";
7152
8065
 
7153
8066
  // src/worker/watchdog.ts
@@ -7202,7 +8115,7 @@ var createLiveness = (deps) => {
7202
8115
  };
7203
8116
 
7204
8117
  // src/worker/session-supervisor.ts
7205
- import { spawn } from "node:child_process";
8118
+ import { spawn as spawn2 } from "node:child_process";
7206
8119
  import { randomUUID } from "node:crypto";
7207
8120
  import { existsSync as existsSync5 } from "node:fs";
7208
8121
  import { dirname as dirname2, join as join7 } from "node:path";
@@ -7335,12 +8248,12 @@ var ensureExcluded = (repo, pattern) => {
7335
8248
  });
7336
8249
  if (r.status !== 0) return;
7337
8250
  const rel = r.stdout.trim();
7338
- const path = isAbsolute2(rel) ? rel : resolve(repo, rel);
7339
- const current = existsSync4(path) ? readFileSync5(path, "utf8") : "";
8251
+ const path3 = isAbsolute2(rel) ? rel : resolve(repo, rel);
8252
+ const current = existsSync4(path3) ? readFileSync5(path3, "utf8") : "";
7340
8253
  if (current.split("\n").includes(pattern)) return;
7341
- mkdirSync3(dirname(path), { recursive: true });
8254
+ mkdirSync3(dirname(path3), { recursive: true });
7342
8255
  const sep = current === "" || current.endsWith("\n") ? "" : "\n";
7343
- appendFileSync(path, `${sep}${pattern}
8256
+ appendFileSync(path3, `${sep}${pattern}
7344
8257
  `, "utf8");
7345
8258
  } catch {
7346
8259
  }
@@ -7387,7 +8300,7 @@ var createSessionSupervisor = (deps) => {
7387
8300
  const children = /* @__PURE__ */ new Map();
7388
8301
  const spawnChild = (sessionId, worktreePath) => {
7389
8302
  if (children.has(sessionId)) return;
7390
- const child = spawn(process.execPath, [binPath(), "session", "run", String(sessionId)], {
8303
+ const child = spawn2(process.execPath, [binPath(), "session", "run", String(sessionId)], {
7391
8304
  detached: true,
7392
8305
  stdio: "inherit",
7393
8306
  env: { ...process.env, BATON_SERVER: cfg.server, BATON_WORKER_TOKEN: cfg.apiToken }
@@ -7408,12 +8321,13 @@ var createSessionSupervisor = (deps) => {
7408
8321
  const session2 = await client.sessions.get(sessionId);
7409
8322
  let worktreePath = session2.worktreePath;
7410
8323
  if (!session2.agentSessionId || !worktreePath) {
7411
- const agentSessionId = randomUUID();
7412
- worktreePath = join7(worktreeDir, slug(`${name}-${agentSessionId.slice(0, 8)}`));
8324
+ const sessionCode = randomUUID();
8325
+ const agentSessionId = session2.agentKind === "codex" ? `pending:${sessionCode}` : sessionCode;
8326
+ worktreePath = join7(worktreeDir, slug(`${name}-${sessionCode.slice(0, 8)}`));
7413
8327
  createWorktree({
7414
8328
  repo,
7415
8329
  worktreePath,
7416
- sessionCode: agentSessionId.slice(0, 8),
8330
+ sessionCode: sessionCode.slice(0, 8),
7417
8331
  base: repoHeadBranch(repo)
7418
8332
  });
7419
8333
  await client.sessions.materialize(sessionId, { agentSessionId, worktreePath }, cfg.apiToken);
@@ -7448,6 +8362,8 @@ var createSessionSupervisor = (deps) => {
7448
8362
  log(`deleted session #${sessionId} (removed worktree)`);
7449
8363
  };
7450
8364
  const title = async (sessionId, agentSessionId, worktreePath) => {
8365
+ const session2 = await client.sessions.get(sessionId);
8366
+ if (session2.agentKind !== "claude-code") return log(`title #${sessionId}: unsupported agent`);
7451
8367
  const exchange = readFirstExchange(agentSessionId);
7452
8368
  if (!exchange) return log(`title #${sessionId}: no transcript yet, skipping`);
7453
8369
  const outcome = await generateTitle({
@@ -7490,7 +8406,7 @@ var createSessionSupervisor = (deps) => {
7490
8406
 
7491
8407
  // src/worker/terminal-manager.ts
7492
8408
  import { existsSync as existsSync7 } from "node:fs";
7493
- import { spawn as spawn2 } from "node-pty";
8409
+ import { spawn as spawn3 } from "node-pty";
7494
8410
 
7495
8411
  // ../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/wrapper.mjs
7496
8412
  var import_stream2 = __toESM(require_stream(), 1);
@@ -7529,7 +8445,7 @@ var createTerminalManager = (deps) => {
7529
8445
  return;
7530
8446
  }
7531
8447
  const claudeBin = process.env.BATON_CLAUDE_BIN ?? "claude";
7532
- const term = spawn2(claudeBin, ptyArgs(agentSessionId, hasSessionJsonl(agentSessionId)), {
8448
+ const term = spawn3(claudeBin, ptyArgs(agentSessionId, hasSessionJsonl(agentSessionId)), {
7533
8449
  name: "xterm-color",
7534
8450
  cols: 80,
7535
8451
  rows: 24,
@@ -7682,19 +8598,19 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as rea
7682
8598
  import { homedir as homedir4 } from "node:os";
7683
8599
  import { dirname as dirname3, join as join9 } from "node:path";
7684
8600
  var machineIdPath = (env = process.env) => join9(env.XDG_DATA_HOME ?? join9(env.HOME ?? homedir4(), ".local/share"), "baton", "machine-id");
7685
- var readOrCreateMachineId = (path = machineIdPath()) => {
7686
- if (existsSync8(path)) {
7687
- const s = readFileSync6(path, "utf8").trim();
8601
+ var readOrCreateMachineId = (path3 = machineIdPath()) => {
8602
+ if (existsSync8(path3)) {
8603
+ const s = readFileSync6(path3, "utf8").trim();
7688
8604
  if (s) return s;
7689
8605
  }
7690
8606
  const id = randomUUID2();
7691
- mkdirSync4(dirname3(path), { recursive: true });
7692
- writeFileSync5(path, `${id}
8607
+ mkdirSync4(dirname3(path3), { recursive: true });
8608
+ writeFileSync5(path3, `${id}
7693
8609
  `, "utf8");
7694
8610
  return id;
7695
8611
  };
7696
8612
 
7697
- // src/commands/worker.ts
8613
+ // src/commands/worker/index.ts
7698
8614
  var resolveWorker = async (client, projectId, handle) => {
7699
8615
  const asId = parseWorkerHandle(handle);
7700
8616
  if (asId !== null) {
@@ -7705,9 +8621,15 @@ var resolveWorker = async (client, projectId, handle) => {
7705
8621
  if (byName) return { id: byName.id, name: byName.name };
7706
8622
  throw new Error(`worker "${handle}" not found in project ${projectId}`);
7707
8623
  };
8624
+ var parseAgentKind = (value) => {
8625
+ const raw = typeof value === "string" && value.trim() ? value.trim() : "claude-code";
8626
+ if (raw === "claude-code" || raw === "codex") return raw;
8627
+ throw new Error(`invalid agent kind "${raw}" (expected claude-code or codex)`);
8628
+ };
7708
8629
  var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
7709
8630
  const out = await client.workers.register({
7710
8631
  projectId: input.projectId,
8632
+ agentKind: input.agentKind,
7711
8633
  machineId: input.machineId,
7712
8634
  name: input.name,
7713
8635
  hostname: input.hostname
@@ -7720,6 +8642,7 @@ var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
7720
8642
  setWorker(cfgPath, {
7721
8643
  id: out.worker.id,
7722
8644
  name: out.worker.name,
8645
+ agentKind: out.worker.agentKind,
7723
8646
  machineId: out.worker.machineId,
7724
8647
  apiToken: out.apiToken
7725
8648
  });
@@ -7740,6 +8663,10 @@ var worker = defineCommand({
7740
8663
  type: "string",
7741
8664
  description: "path to the worker config file (default: ./.baton.json)"
7742
8665
  },
8666
+ agentKind: {
8667
+ type: "string",
8668
+ description: "agent engine for new sessions: claude-code or codex"
8669
+ },
7743
8670
  ...common
7744
8671
  },
7745
8672
  run: async ({ args }) => {
@@ -7756,6 +8683,7 @@ var worker = defineCommand({
7756
8683
  const hostname = osHostname();
7757
8684
  const machineId = readOrCreateMachineId();
7758
8685
  const name = args.name ?? hostname;
8686
+ const agentKind = parseAgentKind(args.agentKind ?? process.env.BATON_AGENT_KIND);
7759
8687
  const { out, configPath } = await registerWorker(
7760
8688
  c,
7761
8689
  {
@@ -7763,7 +8691,8 @@ var worker = defineCommand({
7763
8691
  name,
7764
8692
  server,
7765
8693
  hostname,
7766
- machineId
8694
+ machineId,
8695
+ agentKind
7767
8696
  },
7768
8697
  cfgPath
7769
8698
  );
@@ -7772,6 +8701,7 @@ var worker = defineCommand({
7772
8701
  return;
7773
8702
  }
7774
8703
  console.log(`worker #${out.worker.id} (${out.worker.name}) \u2014 ${out.outcome}`);
8704
+ console.log(` agentKind: ${out.worker.agentKind}`);
7775
8705
  console.log(` hostname: ${out.worker.hostname}`);
7776
8706
  console.log(` machineId: ${out.worker.machineId}`);
7777
8707
  console.log(` machineId file: ${machineIdPath()}`);
@@ -7886,6 +8816,7 @@ var worker = defineCommand({
7886
8816
  }
7887
8817
  console.log(`worker #${worker2.id} (${worker2.name})`);
7888
8818
  console.log(` machineId: ${worker2.machineId}`);
8819
+ console.log(` agentKind: ${worker2.agentKind ?? "claude-code"}`);
7889
8820
  console.log(` server: ${cfg?.server}`);
7890
8821
  }
7891
8822
  })