@lesscap/baton-cli 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/baton.mjs +452 -47
  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
@@ -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
 
@@ -6511,6 +6523,161 @@ var augmentPrompt = (text, relPaths, labels) => {
6511
6523
  ${text}` : header;
6512
6524
  };
6513
6525
 
6526
+ // src/session/runner/codex.ts
6527
+ import { Codex } from "@openai/codex-sdk";
6528
+ var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
6529
+ var str = (v) => typeof v === "string" ? v : void 0;
6530
+ var compactEnv = (env) => {
6531
+ if (!env) return void 0;
6532
+ const out = {};
6533
+ for (const [key, value] of Object.entries(env)) {
6534
+ if (value !== void 0) out[key] = value;
6535
+ }
6536
+ return out;
6537
+ };
6538
+ var statusOf = (v) => v === "in_progress" || v === "failed" || v === "completed" ? v : "completed";
6539
+ var itemId = (item) => str(item.id) ?? `${str(item.type) ?? "item"}-${Math.random().toString(36).slice(2)}`;
6540
+ var todoItems = (raw) => Array.isArray(raw) ? raw.filter(isRecord2).map((todo) => ({
6541
+ text: str(todo.text) ?? str(todo.content) ?? "",
6542
+ completed: todo.completed === true || todo.status === "completed"
6543
+ })) : [];
6544
+ var fileKind = (v) => v === "add" || v === "delete" || v === "update" ? v : "update";
6545
+ var toAgentItem = (raw) => {
6546
+ if (!isRecord2(raw)) {
6547
+ return {
6548
+ type: "error",
6549
+ id: "bad-item",
6550
+ status: "failed",
6551
+ message: "codex item was not an object"
6552
+ };
6553
+ }
6554
+ const type = str(raw.type);
6555
+ const id = itemId(raw);
6556
+ const status = statusOf(raw.status);
6557
+ if (type === "agent_message") {
6558
+ return { type, id, status, text: str(raw.text) ?? str(raw.message) ?? "" };
6559
+ }
6560
+ if (type === "reasoning") {
6561
+ return { type, id, status, text: str(raw.text) ?? str(raw.summary) ?? "" };
6562
+ }
6563
+ if (type === "command_execution") {
6564
+ return {
6565
+ type,
6566
+ id,
6567
+ status,
6568
+ command: str(raw.command) ?? "",
6569
+ output: str(raw.aggregated_output) ?? str(raw.output) ?? "",
6570
+ exitCode: typeof raw.exit_code === "number" ? raw.exit_code : void 0
6571
+ };
6572
+ }
6573
+ if (type === "file_change") {
6574
+ const changes = Array.isArray(raw.changes) ? raw.changes.filter(isRecord2).map((change) => ({
6575
+ path: str(change.path) ?? "",
6576
+ kind: fileKind(change.kind)
6577
+ })) : [];
6578
+ return { type, id, status, changes };
6579
+ }
6580
+ if (type === "mcp_tool_call") {
6581
+ return {
6582
+ type,
6583
+ id,
6584
+ status,
6585
+ server: str(raw.server) ?? "",
6586
+ tool: str(raw.tool) ?? str(raw.name) ?? "",
6587
+ arguments: raw.arguments ?? raw.input ?? null,
6588
+ output: raw.result ?? raw.output ?? raw.error,
6589
+ isError: raw.is_error === true || raw.error !== void 0
6590
+ };
6591
+ }
6592
+ if (type === "web_search") {
6593
+ return { type, id, status, query: str(raw.query) ?? "" };
6594
+ }
6595
+ if (type === "todo_list") {
6596
+ return { type, id, status, items: todoItems(raw.items) };
6597
+ }
6598
+ if (type === "error") {
6599
+ return { type, id, status: "failed", message: str(raw.message) ?? "codex error" };
6600
+ }
6601
+ return {
6602
+ type: "tool_call",
6603
+ id,
6604
+ status,
6605
+ name: str(raw.name) ?? type ?? "codex_item",
6606
+ input: raw,
6607
+ output: raw.output
6608
+ };
6609
+ };
6610
+ var usageOf = (raw) => {
6611
+ if (!isRecord2(raw)) return void 0;
6612
+ return {
6613
+ inputTokens: typeof raw.input_tokens === "number" ? raw.input_tokens : void 0,
6614
+ cachedInputTokens: typeof raw.cached_input_tokens === "number" ? raw.cached_input_tokens : void 0,
6615
+ outputTokens: typeof raw.output_tokens === "number" ? raw.output_tokens : void 0,
6616
+ reasoningOutputTokens: typeof raw.reasoning_output_tokens === "number" ? raw.reasoning_output_tokens : void 0
6617
+ };
6618
+ };
6619
+ var toAgentEvent = (raw) => {
6620
+ if (!isRecord2(raw)) return { type: "raw", raw };
6621
+ const type = str(raw.type);
6622
+ if (type === "thread.started") {
6623
+ return {
6624
+ type,
6625
+ sessionId: str(raw.thread_id) ?? str(raw.sessionId) ?? "",
6626
+ model: str(raw.model),
6627
+ raw
6628
+ };
6629
+ }
6630
+ if (type === "turn.started") return { type, raw };
6631
+ if ((type === "item.started" || type === "item.updated" || type === "item.completed") && raw.item !== void 0) {
6632
+ return { type, item: toAgentItem(raw.item), raw };
6633
+ }
6634
+ if (type === "turn.completed") return { type, usage: usageOf(raw.usage), raw };
6635
+ if (type === "turn.failed") {
6636
+ const error = isRecord2(raw.error) ? raw.error : raw;
6637
+ return { type, error: { message: str(error.message) ?? "codex turn failed" }, raw };
6638
+ }
6639
+ if (type === "error") return { type, message: str(raw.message) ?? "codex error", raw };
6640
+ return { type: "raw", raw };
6641
+ };
6642
+ var withPlanPrefix = (input) => {
6643
+ const prefix = "Plan mode: inspect and propose a plan only. Do not edit files or run mutating commands.\n\n";
6644
+ if (typeof input === "string") return `${prefix}${input}`;
6645
+ return input.map(
6646
+ (entry, i) => i === 0 && entry.type === "text" ? { ...entry, text: `${prefix}${entry.text}` } : entry
6647
+ );
6648
+ };
6649
+ async function* startCodexEvents(config, input, worker2, opts) {
6650
+ const env = compactEnv(buildSdkEnv(opts.envOverlay));
6651
+ const codex = new Codex(env ? { env } : void 0);
6652
+ const resuming = !config.agentSessionId.startsWith("pending:");
6653
+ const addDirs = additionalDirs();
6654
+ const threadOptions = {
6655
+ workingDirectory: config.worktreePath,
6656
+ skipGitRepoCheck: true,
6657
+ sandboxMode: opts.planMode ? "read-only" : "workspace-write",
6658
+ approvalPolicy: "never",
6659
+ ...opts.model ? { model: opts.model } : {},
6660
+ ...addDirs ? { additionalDirectories: addDirs } : {}
6661
+ };
6662
+ const thread = resuming ? codex.resumeThread(config.agentSessionId, threadOptions) : codex.startThread(threadOptions);
6663
+ const runInput = opts.planMode ? withPlanPrefix(input) : input;
6664
+ const { events } = await thread.runStreamed(runInput, {
6665
+ ...opts.signal ? { signal: opts.signal } : {}
6666
+ });
6667
+ for await (const raw of events) {
6668
+ const event = toAgentEvent(raw);
6669
+ if (event.type === "thread.started" && !resuming && event.sessionId) {
6670
+ config.agentSessionId = event.sessionId;
6671
+ await worker2.materialize({
6672
+ agentSessionId: event.sessionId,
6673
+ worktreePath: config.worktreePath
6674
+ });
6675
+ opts.log(`[codex] thread ${event.sessionId}`);
6676
+ }
6677
+ yield event;
6678
+ }
6679
+ }
6680
+
6514
6681
  // src/session/runner/query.ts
6515
6682
  var startQuery = (config, text, resuming, queryFn, abortController, log, { envOverlay, planMode = false, model } = {}) => {
6516
6683
  const env = buildSdkEnv(envOverlay);
@@ -6539,21 +6706,223 @@ var startQuery = (config, text, resuming, queryFn, abortController, log, { envOv
6539
6706
  };
6540
6707
 
6541
6708
  // src/session/runner/stream.ts
6542
- var streamSdkEvents = async (messages, worker2) => {
6709
+ var streamAgentEvents = async (events, worker2) => {
6543
6710
  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);
6711
+ for await (const event of events) {
6712
+ await worker2.emitEvent("agent_event", event);
6713
+ if (event.type === "turn.completed") {
6714
+ result = {
6715
+ subtype: event.subtype ?? "success",
6716
+ isError: false,
6717
+ resultText: ""
6718
+ };
6719
+ } else if (event.type === "turn.failed") {
6720
+ result = {
6721
+ subtype: event.error.subtype ?? "error",
6722
+ isError: true,
6723
+ resultText: event.error.message
6724
+ };
6725
+ }
6547
6726
  }
6548
6727
  return result;
6549
6728
  };
6550
- var toTurnResult = (message) => {
6729
+ var streamClaudeSdkEvents = (messages, worker2) => streamAgentEvents(claudeToAgentEvents(messages), worker2);
6730
+ var isRecord3 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
6731
+ var str2 = (v) => typeof v === "string" ? v : void 0;
6732
+ var messageContent = (payload) => {
6733
+ const msg = payload.message;
6734
+ if (!isRecord3(msg) || !Array.isArray(msg.content)) return [];
6735
+ return msg.content.filter(isRecord3);
6736
+ };
6737
+ var textFromToolResult = (raw) => {
6738
+ const c = raw.content;
6739
+ if (typeof c === "string") return c;
6740
+ if (Array.isArray(c)) {
6741
+ return c.map((b) => isRecord3(b) && typeof b.text === "string" ? b.text : JSON.stringify(b)).join("\n");
6742
+ }
6743
+ return JSON.stringify(c ?? "");
6744
+ };
6745
+ var commandOf = (input) => {
6746
+ if (!isRecord3(input)) return "";
6747
+ return str2(input.command) ?? str2(input.cmd) ?? "";
6748
+ };
6749
+ var pathOf = (input) => {
6750
+ if (!isRecord3(input)) return void 0;
6751
+ return str2(input.file_path) ?? str2(input.path);
6752
+ };
6753
+ var fileKindOf = (name) => name === "Delete" ? "delete" : "update";
6754
+ var todoItems2 = (input) => {
6755
+ if (!isRecord3(input) || !Array.isArray(input.todos)) return [];
6756
+ return input.todos.filter(isRecord3).map((todo) => ({
6757
+ text: str2(todo.content) ?? str2(todo.text) ?? "",
6758
+ completed: todo.status === "completed" || todo.completed === true
6759
+ }));
6760
+ };
6761
+ var mcpParts = (name) => {
6762
+ const match = /^mcp__(.+?)__(.+)$/.exec(name);
6763
+ return match ? { server: match[1] ?? "", tool: match[2] ?? "" } : null;
6764
+ };
6765
+ var startedItemFromToolUse = (block) => {
6766
+ const id = str2(block.id) ?? `tool-${Math.random().toString(36).slice(2)}`;
6767
+ const name = str2(block.name) ?? "tool";
6768
+ const input = block.input ?? null;
6769
+ if (name === "Bash") {
6770
+ return {
6771
+ type: "command_execution",
6772
+ id,
6773
+ status: "in_progress",
6774
+ command: commandOf(input),
6775
+ output: ""
6776
+ };
6777
+ }
6778
+ if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "Delete") {
6779
+ const path = pathOf(input);
6780
+ return {
6781
+ type: "file_change",
6782
+ id,
6783
+ status: "in_progress",
6784
+ changes: path ? [{ path, kind: fileKindOf(name) }] : []
6785
+ };
6786
+ }
6787
+ const mcp = mcpParts(name);
6788
+ if (mcp) {
6789
+ return {
6790
+ type: "mcp_tool_call",
6791
+ id,
6792
+ status: "in_progress",
6793
+ server: mcp.server,
6794
+ tool: mcp.tool,
6795
+ arguments: input
6796
+ };
6797
+ }
6798
+ if (name === "WebSearch" || name === "web_search") {
6799
+ return {
6800
+ type: "web_search",
6801
+ id,
6802
+ status: "in_progress",
6803
+ query: isRecord3(input) ? str2(input.query) ?? "" : ""
6804
+ };
6805
+ }
6806
+ if (name === "TodoWrite") {
6807
+ return { type: "todo_list", id, status: "in_progress", items: todoItems2(input) };
6808
+ }
6809
+ return { type: "tool_call", id, status: "in_progress", name, input };
6810
+ };
6811
+ var completeItem = (item, output, isError) => {
6812
+ if (item.type === "command_execution")
6813
+ return { ...item, status: isError ? "failed" : "completed", output };
6814
+ if (item.type === "mcp_tool_call")
6815
+ return { ...item, status: isError ? "failed" : "completed", output, isError };
6816
+ if (item.type === "tool_call")
6817
+ return { ...item, status: isError ? "failed" : "completed", output, isError };
6818
+ return { ...item, status: isError ? "failed" : "completed" };
6819
+ };
6820
+ var resultEvent = (message) => {
6551
6821
  const rec = message;
6552
- const subtype = typeof rec.subtype === "string" ? rec.subtype : "unknown";
6822
+ const subtype = str2(rec.subtype) ?? "unknown";
6553
6823
  const isError = rec.is_error === true || subtype !== "success";
6554
- const resultText = typeof rec.result === "string" ? rec.result : "";
6555
- return { subtype, isError, resultText };
6824
+ const resultText = str2(rec.result) ?? "";
6825
+ const usage = {
6826
+ numTurns: typeof rec.num_turns === "number" ? rec.num_turns : void 0,
6827
+ totalCostUsd: typeof rec.total_cost_usd === "number" ? rec.total_cost_usd : void 0,
6828
+ durationMs: typeof rec.duration_ms === "number" ? rec.duration_ms : void 0
6829
+ };
6830
+ return isError ? {
6831
+ type: "turn.failed",
6832
+ error: { message: resultText || `claude result: ${subtype}`, subtype },
6833
+ usage,
6834
+ raw: message
6835
+ } : { type: "turn.completed", subtype, usage, raw: message };
6556
6836
  };
6837
+ async function* claudeToAgentEvents(messages) {
6838
+ const pendingTools = /* @__PURE__ */ new Map();
6839
+ let systemEmitted = false;
6840
+ let textSeq = 0;
6841
+ let reasoningSeq = 0;
6842
+ for await (const message of messages) {
6843
+ const p = message;
6844
+ if (message.type === "system") {
6845
+ if (!systemEmitted) {
6846
+ systemEmitted = true;
6847
+ yield {
6848
+ type: "thread.started",
6849
+ sessionId: str2(p.session_id) ?? "",
6850
+ model: str2(p.model) ?? (isRecord3(p.model_info) ? str2(p.model_info.id) : void 0),
6851
+ raw: message
6852
+ };
6853
+ yield { type: "turn.started", raw: message };
6854
+ }
6855
+ continue;
6856
+ }
6857
+ if (message.type === "assistant") {
6858
+ for (const block of messageContent(p)) {
6859
+ if (block.type === "text" && typeof block.text === "string") {
6860
+ yield {
6861
+ type: "item.completed",
6862
+ item: {
6863
+ type: "agent_message",
6864
+ id: `assistant-${++textSeq}`,
6865
+ status: "completed",
6866
+ text: block.text
6867
+ },
6868
+ raw: block
6869
+ };
6870
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
6871
+ const text = block.thinking.trim();
6872
+ if (text) {
6873
+ yield {
6874
+ type: "item.completed",
6875
+ item: {
6876
+ type: "reasoning",
6877
+ id: `reasoning-${++reasoningSeq}`,
6878
+ status: "completed",
6879
+ text
6880
+ },
6881
+ raw: block
6882
+ };
6883
+ }
6884
+ } else if (block.type === "tool_use" || block.type === "server_tool_use") {
6885
+ const item = startedItemFromToolUse(block);
6886
+ pendingTools.set(item.id, item);
6887
+ yield { type: "item.started", item, raw: block };
6888
+ } else if (block.type === "tool_result") {
6889
+ const id = str2(block.tool_use_id) ?? "";
6890
+ const item = pendingTools.get(id);
6891
+ if (item) {
6892
+ const completed = completeItem(item, textFromToolResult(block), block.is_error === true);
6893
+ pendingTools.set(id, completed);
6894
+ yield { type: "item.completed", item: completed, raw: block };
6895
+ } else {
6896
+ yield { type: "raw", raw: block };
6897
+ }
6898
+ } else {
6899
+ yield { type: "raw", raw: block };
6900
+ }
6901
+ }
6902
+ continue;
6903
+ }
6904
+ if (message.type === "user") {
6905
+ for (const block of messageContent(p)) {
6906
+ if (block.type !== "tool_result") continue;
6907
+ const id = str2(block.tool_use_id) ?? "";
6908
+ const item = pendingTools.get(id);
6909
+ if (!item) {
6910
+ yield { type: "raw", raw: block };
6911
+ continue;
6912
+ }
6913
+ const completed = completeItem(item, textFromToolResult(block), block.is_error === true);
6914
+ pendingTools.set(id, completed);
6915
+ yield { type: "item.completed", item: completed, raw: block };
6916
+ }
6917
+ continue;
6918
+ }
6919
+ if (message.type === "result") {
6920
+ yield resultEvent(message);
6921
+ continue;
6922
+ }
6923
+ yield { type: "raw", raw: message };
6924
+ }
6925
+ }
6557
6926
 
6558
6927
  // src/session/runner/turn.ts
6559
6928
  var turnTimeoutMs = () => Number(process.env.BATON_TURN_TIMEOUT_MS) || 30 * 6e4;
@@ -6563,14 +6932,14 @@ var ABORTED = /* @__PURE__ */ Symbol("turn-aborted");
6563
6932
  var finalizeTurn = async (result, worker2, log) => {
6564
6933
  if (!result) {
6565
6934
  log("[exit] stream ended with no result");
6566
- await worker2.emitEvent("turn_error", { message: "claude produced no result" });
6935
+ await worker2.emitEvent("turn_error", { message: "agent produced no result" });
6567
6936
  await worker2.emitEvent("turn_complete", {});
6568
6937
  return -1;
6569
6938
  }
6570
6939
  log(`[exit] subtype=${result.subtype} isError=${result.isError}`);
6571
6940
  if (result.isError) {
6572
6941
  await worker2.emitEvent("turn_error", {
6573
- message: result.resultText || `claude result: ${result.subtype}`,
6942
+ message: result.resultText || `agent result: ${result.subtype}`,
6574
6943
  subtype: result.subtype
6575
6944
  });
6576
6945
  }
@@ -6578,7 +6947,7 @@ var finalizeTurn = async (result, worker2, log) => {
6578
6947
  return result.isError ? 1 : 0;
6579
6948
  };
6580
6949
  var buildPrompt = async (config, rawText, attachments, log, fetchImpl) => {
6581
- if (attachments.length === 0) return rawText;
6950
+ if (attachments.length === 0) return { text: rawText, input: rawText };
6582
6951
  const relPaths = await materializeAttachments({
6583
6952
  worktreePath: config.worktreePath,
6584
6953
  serverBase: config.server,
@@ -6586,7 +6955,13 @@ var buildPrompt = async (config, rawText, attachments, log, fetchImpl) => {
6586
6955
  fetchImpl
6587
6956
  });
6588
6957
  log(`[attach] downloaded ${relPaths.length} file(s) \u2192 ${config.worktreePath}/attachments`);
6589
- return augmentPrompt(rawText, relPaths, labelAttachments(attachments));
6958
+ const labels = labelAttachments(attachments);
6959
+ const text = augmentPrompt(rawText, relPaths, labels);
6960
+ const images = relPaths.flatMap((relPath, i) => {
6961
+ const att = attachments[i];
6962
+ return att?.contentType.startsWith("image/") ? [{ type: "local_image", path: `${config.worktreePath}/${relPath}` }] : [];
6963
+ });
6964
+ return images.length > 0 ? { text, input: [{ type: "text", text }, ...images] } : { text, input: text };
6590
6965
  };
6591
6966
  var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => console.log(m), envOverlay, fetchImpl, externalSignal) => {
6592
6967
  await worker2.emitEvent("turn_start", { messageId: msg.id });
@@ -6599,9 +6974,9 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6599
6974
  await worker2.emitEvent("turn_error", { message: "user_message missing text and attachments" });
6600
6975
  return -1;
6601
6976
  }
6602
- let text;
6977
+ let built;
6603
6978
  try {
6604
- text = await buildPrompt(config, rawText, attachments, log, fetchImpl);
6979
+ built = await buildPrompt(config, rawText, attachments, log, fetchImpl);
6605
6980
  } catch (err) {
6606
6981
  const message = err instanceof Error ? err.message : String(err);
6607
6982
  await worker2.emitEvent("turn_error", { message: `attachment download failed: ${message}` });
@@ -6622,12 +6997,23 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6622
6997
  heartbeat = setInterval(() => {
6623
6998
  void worker2.emitEvent("turn_heartbeat", {}).catch((e) => log(`[heartbeat] ${String(e)}`));
6624
6999
  }, heartbeatMs());
6625
- const messages = startQuery(config, text, resuming, queryFn, abort, log, {
6626
- envOverlay,
6627
- planMode,
6628
- model
6629
- });
6630
- const consume = streamSdkEvents(messages, worker2);
7000
+ const consume = config.agentKind === "codex" ? streamAgentEvents(
7001
+ startCodexEvents(config, built.input, worker2, {
7002
+ envOverlay,
7003
+ planMode,
7004
+ model,
7005
+ signal: abort.signal,
7006
+ log
7007
+ }),
7008
+ worker2
7009
+ ) : streamClaudeSdkEvents(
7010
+ startQuery(config, built.text, resuming, queryFn, abort, log, {
7011
+ envOverlay,
7012
+ planMode,
7013
+ model
7014
+ }),
7015
+ worker2
7016
+ );
6631
7017
  void consume.catch(() => {
6632
7018
  });
6633
7019
  const outcome = await Promise.race([consume, timeout, interrupted]);
@@ -6639,10 +7025,10 @@ var runTurn = async (config, worker2, msg, resuming, queryFn, log = (m) => conso
6639
7025
  return -1;
6640
7026
  }
6641
7027
  if (outcome === TIMEOUT) {
6642
- log(`[watchdog] turn exceeded ${ceiling}ms \u2014 aborting claude`);
7028
+ log(`[watchdog] turn exceeded ${ceiling}ms \u2014 aborting agent`);
6643
7029
  abort.abort();
6644
7030
  await worker2.emitEvent("turn_error", {
6645
- message: `turn exceeded ${ceiling}ms \u2014 claude aborted`
7031
+ message: `turn exceeded ${ceiling}ms \u2014 agent aborted`
6646
7032
  });
6647
7033
  await worker2.emitEvent("turn_complete", {});
6648
7034
  return -1;
@@ -6698,7 +7084,7 @@ var runDaemon = async (config, deps, signal) => {
6698
7084
  log(`bin: ${claudeExecutable() ?? "(sdk bundled)"} cwd: ${config.worktreePath}`);
6699
7085
  log(`runtime env keys: ${maskedEnvKeys(deps.env)}`);
6700
7086
  const state = {
6701
- firstSpawnDone: findTranscriptPath(config.agentSessionId) !== null,
7087
+ firstSpawnDone: config.agentKind === "codex" ? !config.agentSessionId.startsWith("pending:") : findTranscriptPath(config.agentSessionId) !== null,
6702
7088
  seen: /* @__PURE__ */ new Set()
6703
7089
  };
6704
7090
  const pendingQueue = [];
@@ -6866,6 +7252,7 @@ var sessionRunCommand = defineCommand({
6866
7252
  server,
6867
7253
  sessionId,
6868
7254
  name: s.name,
7255
+ agentKind: s.agentKind,
6869
7256
  agentSessionId: s.agentSessionId,
6870
7257
  worktreePath: s.worktreePath
6871
7258
  };
@@ -7147,7 +7534,7 @@ var task = defineCommand({
7147
7534
  }
7148
7535
  });
7149
7536
 
7150
- // src/commands/worker.ts
7537
+ // src/commands/worker/index.ts
7151
7538
  import { hostname as osHostname } from "node:os";
7152
7539
 
7153
7540
  // src/worker/watchdog.ts
@@ -7408,12 +7795,13 @@ var createSessionSupervisor = (deps) => {
7408
7795
  const session2 = await client.sessions.get(sessionId);
7409
7796
  let worktreePath = session2.worktreePath;
7410
7797
  if (!session2.agentSessionId || !worktreePath) {
7411
- const agentSessionId = randomUUID();
7412
- worktreePath = join7(worktreeDir, slug(`${name}-${agentSessionId.slice(0, 8)}`));
7798
+ const sessionCode = randomUUID();
7799
+ const agentSessionId = session2.agentKind === "codex" ? `pending:${sessionCode}` : sessionCode;
7800
+ worktreePath = join7(worktreeDir, slug(`${name}-${sessionCode.slice(0, 8)}`));
7413
7801
  createWorktree({
7414
7802
  repo,
7415
7803
  worktreePath,
7416
- sessionCode: agentSessionId.slice(0, 8),
7804
+ sessionCode: sessionCode.slice(0, 8),
7417
7805
  base: repoHeadBranch(repo)
7418
7806
  });
7419
7807
  await client.sessions.materialize(sessionId, { agentSessionId, worktreePath }, cfg.apiToken);
@@ -7448,6 +7836,8 @@ var createSessionSupervisor = (deps) => {
7448
7836
  log(`deleted session #${sessionId} (removed worktree)`);
7449
7837
  };
7450
7838
  const title = async (sessionId, agentSessionId, worktreePath) => {
7839
+ const session2 = await client.sessions.get(sessionId);
7840
+ if (session2.agentKind !== "claude-code") return log(`title #${sessionId}: unsupported agent`);
7451
7841
  const exchange = readFirstExchange(agentSessionId);
7452
7842
  if (!exchange) return log(`title #${sessionId}: no transcript yet, skipping`);
7453
7843
  const outcome = await generateTitle({
@@ -7694,7 +8084,7 @@ var readOrCreateMachineId = (path = machineIdPath()) => {
7694
8084
  return id;
7695
8085
  };
7696
8086
 
7697
- // src/commands/worker.ts
8087
+ // src/commands/worker/index.ts
7698
8088
  var resolveWorker = async (client, projectId, handle) => {
7699
8089
  const asId = parseWorkerHandle(handle);
7700
8090
  if (asId !== null) {
@@ -7705,9 +8095,15 @@ var resolveWorker = async (client, projectId, handle) => {
7705
8095
  if (byName) return { id: byName.id, name: byName.name };
7706
8096
  throw new Error(`worker "${handle}" not found in project ${projectId}`);
7707
8097
  };
8098
+ var parseAgentKind = (value) => {
8099
+ const raw = typeof value === "string" && value.trim() ? value.trim() : "claude-code";
8100
+ if (raw === "claude-code" || raw === "codex") return raw;
8101
+ throw new Error(`invalid agent kind "${raw}" (expected claude-code or codex)`);
8102
+ };
7708
8103
  var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
7709
8104
  const out = await client.workers.register({
7710
8105
  projectId: input.projectId,
8106
+ agentKind: input.agentKind,
7711
8107
  machineId: input.machineId,
7712
8108
  name: input.name,
7713
8109
  hostname: input.hostname
@@ -7720,6 +8116,7 @@ var registerWorker = async (client, input, cfgPath = projectConfigPath()) => {
7720
8116
  setWorker(cfgPath, {
7721
8117
  id: out.worker.id,
7722
8118
  name: out.worker.name,
8119
+ agentKind: out.worker.agentKind,
7723
8120
  machineId: out.worker.machineId,
7724
8121
  apiToken: out.apiToken
7725
8122
  });
@@ -7740,6 +8137,10 @@ var worker = defineCommand({
7740
8137
  type: "string",
7741
8138
  description: "path to the worker config file (default: ./.baton.json)"
7742
8139
  },
8140
+ agentKind: {
8141
+ type: "string",
8142
+ description: "agent engine for new sessions: claude-code or codex"
8143
+ },
7743
8144
  ...common
7744
8145
  },
7745
8146
  run: async ({ args }) => {
@@ -7756,6 +8157,7 @@ var worker = defineCommand({
7756
8157
  const hostname = osHostname();
7757
8158
  const machineId = readOrCreateMachineId();
7758
8159
  const name = args.name ?? hostname;
8160
+ const agentKind = parseAgentKind(args.agentKind ?? process.env.BATON_AGENT_KIND);
7759
8161
  const { out, configPath } = await registerWorker(
7760
8162
  c,
7761
8163
  {
@@ -7763,7 +8165,8 @@ var worker = defineCommand({
7763
8165
  name,
7764
8166
  server,
7765
8167
  hostname,
7766
- machineId
8168
+ machineId,
8169
+ agentKind
7767
8170
  },
7768
8171
  cfgPath
7769
8172
  );
@@ -7772,6 +8175,7 @@ var worker = defineCommand({
7772
8175
  return;
7773
8176
  }
7774
8177
  console.log(`worker #${out.worker.id} (${out.worker.name}) \u2014 ${out.outcome}`);
8178
+ console.log(` agentKind: ${out.worker.agentKind}`);
7775
8179
  console.log(` hostname: ${out.worker.hostname}`);
7776
8180
  console.log(` machineId: ${out.worker.machineId}`);
7777
8181
  console.log(` machineId file: ${machineIdPath()}`);
@@ -7886,6 +8290,7 @@ var worker = defineCommand({
7886
8290
  }
7887
8291
  console.log(`worker #${worker2.id} (${worker2.name})`);
7888
8292
  console.log(` machineId: ${worker2.machineId}`);
8293
+ console.log(` agentKind: ${worker2.agentKind ?? "claude-code"}`);
7889
8294
  console.log(` server: ${cfg?.server}`);
7890
8295
  }
7891
8296
  })
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@lesscap/baton-cli",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "type": "module",
6
9
  "description": "baton CLI — register/run a worker, manage projects/requirements/tasks/sessions",
7
10
  "bin": {
@@ -23,6 +26,7 @@
23
26
  },
24
27
  "dependencies": {
25
28
  "@anthropic-ai/claude-agent-sdk": "^0.3.161",
29
+ "@openai/codex-sdk": "^0.142.5",
26
30
  "node-pty": "^1.1.0",
27
31
  "ws": "^8.21.0"
28
32
  },