@autohq/cli 0.1.224 → 0.1.226

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.
@@ -43,7 +43,7 @@ var require_XMLHttpRequest = __commonJS({
43
43
  "use strict";
44
44
  var fs = __require("fs");
45
45
  var Url = __require("url");
46
- var spawn = __require("child_process").spawn;
46
+ var spawn2 = __require("child_process").spawn;
47
47
  module.exports = XMLHttpRequest3;
48
48
  XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
49
49
  function XMLHttpRequest3(opts) {
@@ -339,7 +339,7 @@ var require_XMLHttpRequest = __commonJS({
339
339
  var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
340
340
  fs.writeFileSync(syncFile, "", "utf8");
341
341
  var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
342
- var syncProc = spawn(process.argv[0], ["-e", execString]);
342
+ var syncProc = spawn2(process.argv[0], ["-e", execString]);
343
343
  var statusText;
344
344
  while (fs.existsSync(syncFile)) {
345
345
  }
@@ -19545,6 +19545,9 @@ var AgentBridgeClaudeConfigSchema = AgentBridgeHarnessBaseConfigSchema;
19545
19545
  var AgentBridgeHarnessConfigSchema = external_exports.discriminatedUnion("kind", [
19546
19546
  AgentBridgeHarnessBaseConfigSchema.extend({
19547
19547
  kind: external_exports.literal("claude-code")
19548
+ }),
19549
+ AgentBridgeHarnessBaseConfigSchema.extend({
19550
+ kind: external_exports.literal("codex")
19548
19551
  })
19549
19552
  ]);
19550
19553
  var RuntimeBridgeBootstrapPlaintextSchema = external_exports.object({
@@ -23319,7 +23322,7 @@ Object.assign(lookup, {
23319
23322
  // package.json
23320
23323
  var package_default = {
23321
23324
  name: "@autohq/cli",
23322
- version: "0.1.224",
23325
+ version: "0.1.226",
23323
23326
  license: "SEE LICENSE IN README.md",
23324
23327
  publishConfig: {
23325
23328
  access: "public"
@@ -23847,6 +23850,7 @@ var AuthScopeSchema = external_exports.enum([
23847
23850
  "github:mcp",
23848
23851
  "github:credentials",
23849
23852
  "mcp:connection",
23853
+ "runtime-logs:write",
23850
23854
  "projects:admin",
23851
23855
  "org:admin"
23852
23856
  ]);
@@ -24596,12 +24600,12 @@ function parseClaudeCodeStreamRecord(parsed) {
24596
24600
  }
24597
24601
  const isError = record2.is_error === true || record2.subtype === "error";
24598
24602
  const sdkErrorMessage = record2.errors?.map((error51) => error51.trim()).filter(Boolean).join("\n");
24599
- const errorMessage2 = isError ? record2.error ?? record2.result ?? (sdkErrorMessage || void 0) ?? "claude-code reported an error" : void 0;
24603
+ const errorMessage4 = isError ? record2.error ?? record2.result ?? (sdkErrorMessage || void 0) ?? "claude-code reported an error" : void 0;
24600
24604
  return {
24601
24605
  projections: [],
24602
24606
  result: {
24603
24607
  isError,
24604
- errorMessage: errorMessage2
24608
+ errorMessage: errorMessage4
24605
24609
  }
24606
24610
  };
24607
24611
  }
@@ -24815,6 +24819,250 @@ var CodexFrameSchema = external_exports.object({
24815
24819
  result: external_exports.unknown().optional(),
24816
24820
  error: external_exports.object({ message: external_exports.string().optional() }).passthrough().optional()
24817
24821
  }).passthrough();
24822
+ function parseCodexServerFrame(raw) {
24823
+ const frame = CodexFrameSchema.parse(raw);
24824
+ if (frame.method === void 0 && frame.id !== void 0) {
24825
+ return {
24826
+ kind: "response",
24827
+ id: frame.id,
24828
+ ...frame.result !== void 0 ? { result: toJsonValue(frame.result) } : {},
24829
+ ...frame.error?.message ? { error: frame.error.message } : {}
24830
+ };
24831
+ }
24832
+ if (frame.method === void 0) {
24833
+ return { kind: "ignored" };
24834
+ }
24835
+ if (frame.id !== void 0) {
24836
+ return classifyServerRequest(frame.method, frame.id, frame.params);
24837
+ }
24838
+ const notification = parseNotification(frame.method, frame.params);
24839
+ return notification ? { kind: "notification", notification } : { kind: "ignored" };
24840
+ }
24841
+ function projectCodexItem(input) {
24842
+ const { item, phase } = input;
24843
+ switch (item.type) {
24844
+ case "agentMessage":
24845
+ return phase === "completed" && item.text.length > 0 ? [
24846
+ {
24847
+ role: "assistant",
24848
+ kind: "message",
24849
+ messageId: item.id,
24850
+ content: textContent2(item.text)
24851
+ }
24852
+ ] : [];
24853
+ case "commandExecution":
24854
+ return toolProjection({
24855
+ phase,
24856
+ itemId: item.id,
24857
+ name: "shell",
24858
+ input: {
24859
+ command: item.command,
24860
+ ...item.cwd ? { cwd: item.cwd } : {}
24861
+ },
24862
+ output: item.aggregatedOutput ?? "",
24863
+ isError: isFailedStatus(item.status) || (item.exitCode ?? 0) !== 0
24864
+ });
24865
+ case "fileChange":
24866
+ return toolProjection({
24867
+ phase,
24868
+ itemId: item.id,
24869
+ name: "apply_patch",
24870
+ input: { changes: toJsonValue(item.changes) },
24871
+ output: { status: item.status ?? "unknown" },
24872
+ isError: isFailedStatus(item.status)
24873
+ });
24874
+ case "mcpToolCall":
24875
+ return toolProjection({
24876
+ phase,
24877
+ itemId: item.id,
24878
+ name: `${item.server}.${item.tool}`,
24879
+ input: toJsonValue(item.arguments),
24880
+ output: item.error ? { error: item.error.message } : toJsonValue(item.result),
24881
+ isError: isFailedStatus(item.status) || item.error != null
24882
+ });
24883
+ default:
24884
+ return [];
24885
+ }
24886
+ }
24887
+ function projectCodexApproval(request) {
24888
+ const subject = request.type === "commandExecution" ? `run the command: ${request.command ?? "(unknown command)"}` : "apply file changes";
24889
+ const question = {
24890
+ question: request.reason ? `Codex requests approval to ${subject}. ${request.reason}` : `Codex requests approval to ${subject}.`,
24891
+ header: "Approval",
24892
+ options: [
24893
+ { label: APPROVE_OPTION_LABEL, description: "Allow this action." },
24894
+ { label: DECLINE_OPTION_LABEL, description: "Reject this action." }
24895
+ ],
24896
+ multiSelect: false
24897
+ };
24898
+ return {
24899
+ role: "assistant",
24900
+ kind: "question",
24901
+ content: {
24902
+ parts: [
24903
+ { type: "question", toolCallId: request.itemId, questions: [question] }
24904
+ ]
24905
+ }
24906
+ };
24907
+ }
24908
+ var APPROVE_OPTION_LABEL = "Approve";
24909
+ var DECLINE_OPTION_LABEL = "Decline";
24910
+ function codexApprovalDecision(input) {
24911
+ const values = [...Object.values(input.answers), input.response ?? ""];
24912
+ const approved = values.some(
24913
+ (value2) => value2.trim().toLowerCase() === APPROVE_OPTION_LABEL.toLowerCase()
24914
+ );
24915
+ return approved ? "accept" : "decline";
24916
+ }
24917
+ function parseNotification(method, params) {
24918
+ switch (method) {
24919
+ case "thread/started": {
24920
+ const parsed = external_exports.object({ thread: external_exports.object({ id: external_exports.string() }).passthrough() }).passthrough().safeParse(params);
24921
+ return parsed.success ? { type: "threadStarted", threadId: parsed.data.thread.id } : null;
24922
+ }
24923
+ case "turn/started": {
24924
+ const parsed = CodexTurnEnvelopeSchema.safeParse(params);
24925
+ return parsed.success ? {
24926
+ type: "turnStarted",
24927
+ threadId: parsed.data.threadId,
24928
+ turnId: parsed.data.turn.id
24929
+ } : null;
24930
+ }
24931
+ case "turn/completed": {
24932
+ const parsed = CodexTurnEnvelopeSchema.safeParse(params);
24933
+ return parsed.success ? {
24934
+ type: "turnCompleted",
24935
+ threadId: parsed.data.threadId,
24936
+ turnId: parsed.data.turn.id,
24937
+ status: parsed.data.turn.status,
24938
+ ...parsed.data.turn.error?.message ? { errorMessage: parsed.data.turn.error.message } : {}
24939
+ } : null;
24940
+ }
24941
+ case "item/started":
24942
+ case "item/completed": {
24943
+ const parsed = CodexItemEnvelopeSchema.safeParse(params);
24944
+ if (!parsed.success || parsed.data.item === null) {
24945
+ return null;
24946
+ }
24947
+ return {
24948
+ type: method === "item/started" ? "itemStarted" : "itemCompleted",
24949
+ threadId: parsed.data.threadId,
24950
+ turnId: parsed.data.turnId,
24951
+ item: parsed.data.item
24952
+ };
24953
+ }
24954
+ case "item/agentMessage/delta": {
24955
+ const parsed = external_exports.object({ itemId: external_exports.string(), delta: external_exports.string() }).passthrough().safeParse(params);
24956
+ return parsed.success ? {
24957
+ type: "agentMessageDelta",
24958
+ itemId: parsed.data.itemId,
24959
+ delta: parsed.data.delta
24960
+ } : null;
24961
+ }
24962
+ case "error": {
24963
+ const parsed = external_exports.object({
24964
+ error: external_exports.object({ message: external_exports.string() }).passthrough(),
24965
+ willRetry: external_exports.boolean().default(false),
24966
+ threadId: external_exports.string().default("")
24967
+ }).passthrough().safeParse(params);
24968
+ return parsed.success ? {
24969
+ type: "error",
24970
+ threadId: parsed.data.threadId,
24971
+ willRetry: parsed.data.willRetry,
24972
+ message: parsed.data.error.message
24973
+ } : null;
24974
+ }
24975
+ default:
24976
+ return null;
24977
+ }
24978
+ }
24979
+ function classifyServerRequest(method, requestId, params) {
24980
+ if (method === "mcpServer/elicitation/request") {
24981
+ const parsed = external_exports.object({ serverName: external_exports.string().default("") }).passthrough().safeParse(params);
24982
+ return {
24983
+ kind: "elicitation",
24984
+ requestId,
24985
+ serverName: parsed.success ? parsed.data.serverName : ""
24986
+ };
24987
+ }
24988
+ const approval = parseApprovalRequest(method, requestId, params);
24989
+ if (approval) {
24990
+ return { kind: "serverRequest", request: approval };
24991
+ }
24992
+ return { kind: "unsupportedRequest", requestId, method };
24993
+ }
24994
+ function parseApprovalRequest(method, requestId, params) {
24995
+ const base = external_exports.object({
24996
+ itemId: external_exports.string(),
24997
+ reason: external_exports.string().nullish(),
24998
+ command: external_exports.string().nullish()
24999
+ }).passthrough();
25000
+ switch (method) {
25001
+ case "item/commandExecution/requestApproval": {
25002
+ const parsed = base.safeParse(params);
25003
+ return parsed.success ? {
25004
+ type: "commandExecution",
25005
+ requestId,
25006
+ itemId: parsed.data.itemId,
25007
+ ...parsed.data.reason ? { reason: parsed.data.reason } : {},
25008
+ ...parsed.data.command ? { command: parsed.data.command } : {}
25009
+ } : null;
25010
+ }
25011
+ case "item/fileChange/requestApproval": {
25012
+ const parsed = base.safeParse(params);
25013
+ return parsed.success ? {
25014
+ type: "fileChange",
25015
+ requestId,
25016
+ itemId: parsed.data.itemId,
25017
+ ...parsed.data.reason ? { reason: parsed.data.reason } : {}
25018
+ } : null;
25019
+ }
25020
+ default:
25021
+ return null;
25022
+ }
25023
+ }
25024
+ function toolProjection(input) {
25025
+ if (input.phase === "started") {
25026
+ return [
25027
+ {
25028
+ role: "assistant",
25029
+ kind: "tool_call",
25030
+ content: {
25031
+ parts: [
25032
+ {
25033
+ type: "tool_call",
25034
+ toolCallId: input.itemId,
25035
+ name: input.name,
25036
+ input: input.input
25037
+ }
25038
+ ]
25039
+ }
25040
+ }
25041
+ ];
25042
+ }
25043
+ return [
25044
+ {
25045
+ role: "tool",
25046
+ kind: "tool_result",
25047
+ content: {
25048
+ parts: [
25049
+ {
25050
+ type: "tool_result",
25051
+ toolUseId: input.itemId,
25052
+ output: input.output,
25053
+ isError: input.isError
25054
+ }
25055
+ ]
25056
+ }
25057
+ }
25058
+ ];
25059
+ }
25060
+ function textContent2(text) {
25061
+ return { parts: [{ type: "text", text }] };
25062
+ }
25063
+ function isFailedStatus(status) {
25064
+ return status === "failed" || status === "declined";
25065
+ }
24818
25066
 
24819
25067
  // ../../packages/schemas/src/resources.ts
24820
25068
  var ResourceNameSchema = external_exports.string().trim().min(1).max(128).regex(/^[A-Za-z0-9_.-]+$/);
@@ -27281,20 +27529,24 @@ var SetupOnboardingPullRequestStatusResponseSchema = external_exports.object({
27281
27529
  ready: external_exports.boolean()
27282
27530
  });
27283
27531
 
27284
- // ../../packages/schemas/src/e2b-webhook.ts
27285
- var E2bLifecycleWebhookEventSchema = external_exports.object({
27286
- type: external_exports.string().min(1),
27287
- sandbox_id: external_exports.string().min(1),
27288
- sandbox_execution_id: external_exports.string().optional(),
27289
- timestamp: external_exports.string().optional()
27290
- }).passthrough();
27291
-
27292
27532
  // ../../packages/schemas/src/runtime-log.ts
27293
27533
  var RUNTIME_LOG_LEVELS = ["debug", "info", "warn", "error"];
27294
27534
  var RuntimeLogLevelSchema = external_exports.enum(RUNTIME_LOG_LEVELS);
27535
+ var RUNTIME_LOG_INGEST_URL_ENV = "AUTO_RUNTIME_LOG_INGEST_URL";
27536
+ var RUNTIME_LOG_INGEST_TOKEN_ENV = "AUTO_RUNTIME_LOG_INGEST_TOKEN";
27295
27537
  var DEFAULT_RUNTIME_LOG_LEVEL = "info";
27296
27538
  var SANDBOX_RUNTIME_LOG_DIR = "/home/user/.auto-runtime";
27297
27539
  var SANDBOX_RUNTIME_LOG_PATH = `${SANDBOX_RUNTIME_LOG_DIR}/agent-bridge.log`;
27540
+ var RuntimeLogIngestLineSchema = external_exports.object({
27541
+ logSeq: external_exports.number().int().positive(),
27542
+ timestamp: external_exports.string().datetime(),
27543
+ level: RuntimeLogLevelSchema,
27544
+ component: external_exports.string().trim().min(1),
27545
+ message: external_exports.string()
27546
+ }).passthrough();
27547
+ var RuntimeLogIngestRequestSchema = external_exports.object({
27548
+ lines: external_exports.array(RuntimeLogIngestLineSchema).min(1).max(100)
27549
+ }).strict();
27298
27550
  var LEVEL_SEVERITY = {
27299
27551
  debug: 10,
27300
27552
  info: 20,
@@ -48461,6 +48713,981 @@ function commandLogContext(delivery, fields = {}) {
48461
48713
  };
48462
48714
  }
48463
48715
 
48716
+ // src/commands/agent-bridge/harness/codex/projector.ts
48717
+ var CodexProjector = class {
48718
+ project(notification) {
48719
+ switch (notification.type) {
48720
+ case "agentMessageDelta":
48721
+ return [
48722
+ {
48723
+ type: "delta",
48724
+ delta: {
48725
+ messageId: notification.itemId,
48726
+ partId: "0",
48727
+ role: "assistant",
48728
+ kind: "message",
48729
+ delta: { type: "text", text: notification.delta }
48730
+ }
48731
+ }
48732
+ ];
48733
+ case "itemStarted":
48734
+ return entryProjections(
48735
+ projectCodexItem({ item: notification.item, phase: "started" })
48736
+ );
48737
+ case "itemCompleted":
48738
+ return entryProjections(
48739
+ projectCodexItem({ item: notification.item, phase: "completed" })
48740
+ );
48741
+ case "turnCompleted":
48742
+ return [turnCompletionEntry(notification)];
48743
+ case "error":
48744
+ return [errorEntry(notification)];
48745
+ default:
48746
+ return [];
48747
+ }
48748
+ }
48749
+ // An approval parks the turn on operator input, so the question entry also
48750
+ // marks the delivered turn as waiting for input.
48751
+ projectApproval(request) {
48752
+ return {
48753
+ type: "entry",
48754
+ entry: {
48755
+ ...projectCodexApproval(request),
48756
+ turnStatus: "waiting_for_input"
48757
+ }
48758
+ };
48759
+ }
48760
+ // Surfaces a session-level failure (e.g. the app-server process dying) as a
48761
+ // durable failed status entry rather than only a diagnostic log line.
48762
+ projectSessionFailure(message) {
48763
+ return {
48764
+ type: "entry",
48765
+ entry: {
48766
+ role: "system",
48767
+ kind: "status",
48768
+ status: "failed",
48769
+ turnStatus: "failed",
48770
+ content: {
48771
+ parts: [{ type: "text", text: `codex failed: ${message}` }]
48772
+ }
48773
+ }
48774
+ };
48775
+ }
48776
+ };
48777
+ function entryProjections(projections) {
48778
+ return projections.map((projection) => ({
48779
+ type: "entry",
48780
+ entry: projection
48781
+ }));
48782
+ }
48783
+ function turnCompletionEntry(notification) {
48784
+ if (notification.status === "failed") {
48785
+ const detail = notification.errorMessage ?? "unknown error";
48786
+ return statusEntry({
48787
+ text: `codex turn failed: ${detail}`,
48788
+ status: "failed",
48789
+ turnStatus: "failed"
48790
+ });
48791
+ }
48792
+ const text = notification.status === "interrupted" ? "codex turn interrupted" : "codex turn completed";
48793
+ return statusEntry({
48794
+ text,
48795
+ status: "completed",
48796
+ turnStatus: "completed"
48797
+ });
48798
+ }
48799
+ function errorEntry(notification) {
48800
+ if (notification.willRetry) {
48801
+ return statusEntry({
48802
+ text: `codex retrying after error: ${notification.message}`,
48803
+ status: "completed"
48804
+ });
48805
+ }
48806
+ return statusEntry({
48807
+ text: `codex error: ${notification.message}`,
48808
+ status: "failed",
48809
+ turnStatus: "failed"
48810
+ });
48811
+ }
48812
+ function statusEntry(input) {
48813
+ return {
48814
+ type: "entry",
48815
+ entry: {
48816
+ role: "system",
48817
+ kind: "status",
48818
+ status: input.status,
48819
+ ...input.turnStatus ? { turnStatus: input.turnStatus } : {},
48820
+ content: { parts: [{ type: "text", text: input.text }] }
48821
+ }
48822
+ };
48823
+ }
48824
+
48825
+ // src/commands/agent-bridge/harness/codex/resume-store.ts
48826
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
48827
+ import { dirname as dirname2 } from "path";
48828
+ var AGENT_BRIDGE_RUNTIME_DIR2 = "/tmp/auto-bridge-runtime";
48829
+ var CODEX_THREAD_RESUME_PATH = `${AGENT_BRIDGE_RUNTIME_DIR2}/codex-thread-id`;
48830
+ function fileCodexThreadResumeStore(path2 = CODEX_THREAD_RESUME_PATH) {
48831
+ return {
48832
+ read(sessionId) {
48833
+ if (!existsSync3(path2)) {
48834
+ return null;
48835
+ }
48836
+ const record2 = parseResumeRecord2(readFileSync3(path2, "utf8"));
48837
+ if (!record2 || record2.sessionId !== sessionId) {
48838
+ return null;
48839
+ }
48840
+ return record2.threadId;
48841
+ },
48842
+ write(record2) {
48843
+ mkdirSync3(dirname2(path2), { recursive: true });
48844
+ writeFileSync2(path2, `${JSON.stringify(record2)}
48845
+ `, "utf8");
48846
+ }
48847
+ };
48848
+ }
48849
+ function parseResumeRecord2(raw) {
48850
+ try {
48851
+ const value2 = JSON.parse(raw);
48852
+ if (value2 !== null && typeof value2 === "object" && "sessionId" in value2 && "threadId" in value2 && typeof value2.sessionId === "string" && typeof value2.threadId === "string" && value2.threadId.length > 0) {
48853
+ return { sessionId: value2.sessionId, threadId: value2.threadId };
48854
+ }
48855
+ return null;
48856
+ } catch {
48857
+ return null;
48858
+ }
48859
+ }
48860
+
48861
+ // src/commands/agent-bridge/harness/codex/session.ts
48862
+ import { spawn } from "child_process";
48863
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
48864
+ import { join as join2 } from "path";
48865
+
48866
+ // src/commands/agent-bridge/harness/codex/options.ts
48867
+ import { join } from "path";
48868
+ var CODEX_EXECUTABLE_PATH = "codex";
48869
+ var CODEX_DEFAULT_MODEL = "gpt-5.3-codex";
48870
+ var CODEX_HTTP_PROVIDER_ID = "openai-responses-http";
48871
+ var CODEX_OPENAI_BASE_URL = "https://api.openai.com/v1";
48872
+ var CODEX_API_KEY_ENV = "OPENAI_API_KEY";
48873
+ var CODEX_RUNTIME_PROCESS_ENV_KEYS = ["PATH", "HOME"];
48874
+ function codexLaunchOptions(config2) {
48875
+ return {
48876
+ command: codexExecutablePath(),
48877
+ // `--listen stdio://` is the default, but pin it so a config/feature change
48878
+ // cannot silently move the transport off the stdin/stdout the client drives.
48879
+ args: ["app-server", "--listen", "stdio://"],
48880
+ env: codexProcessEnv(config2.env),
48881
+ // Codex reads config/auth/rollout state from its standard home; the caller
48882
+ // writes the rendered config.toml there.
48883
+ codexHome: codexHomeDir(),
48884
+ configToml: renderCodexConfigToml(config2)
48885
+ };
48886
+ }
48887
+ function codexThreadParams(config2) {
48888
+ return {
48889
+ ...config2.cwd ? { cwd: config2.cwd } : {},
48890
+ ...config2.systemPromptAppend ? { developerInstructions: config2.systemPromptAppend } : {}
48891
+ };
48892
+ }
48893
+ function renderCodexConfigToml(config2) {
48894
+ const lines = [];
48895
+ lines.push(`model = ${tomlString(CODEX_DEFAULT_MODEL)}`);
48896
+ lines.push(`model_provider = ${tomlString(CODEX_HTTP_PROVIDER_ID)}`);
48897
+ lines.push("");
48898
+ lines.push(`[model_providers.${CODEX_HTTP_PROVIDER_ID}]`);
48899
+ lines.push('name = "OpenAI"');
48900
+ lines.push(`base_url = ${tomlString(CODEX_OPENAI_BASE_URL)}`);
48901
+ lines.push('wire_api = "responses"');
48902
+ lines.push(`env_key = ${tomlString(CODEX_API_KEY_ENV)}`);
48903
+ lines.push("supports_websockets = false");
48904
+ for (const [name, server] of Object.entries(config2.mcpServers ?? {})) {
48905
+ lines.push("");
48906
+ lines.push(`[mcp_servers.${tomlKey(name)}]`);
48907
+ lines.push(`url = ${tomlString(server.url)}`);
48908
+ if (server.headers && Object.keys(server.headers).length > 0) {
48909
+ lines.push(`http_headers = ${tomlInlineTable(server.headers)}`);
48910
+ }
48911
+ }
48912
+ return `${lines.join("\n")}
48913
+ `;
48914
+ }
48915
+ function codexProcessEnv(env) {
48916
+ const selected = {};
48917
+ for (const key of CODEX_RUNTIME_PROCESS_ENV_KEYS) {
48918
+ const value2 = process.env[key];
48919
+ if (value2 !== void 0) {
48920
+ selected[key] = value2;
48921
+ }
48922
+ }
48923
+ return {
48924
+ ...selected,
48925
+ ...env
48926
+ };
48927
+ }
48928
+ function codexHomeDir() {
48929
+ const home = process.env.HOME;
48930
+ if (!home) {
48931
+ throw new Error("codex launch requires HOME to locate the ~/.codex home");
48932
+ }
48933
+ return join(home, ".codex");
48934
+ }
48935
+ function codexExecutablePath() {
48936
+ if (process.env.AUTO_AGENT_BRIDGE_TEST_CODEX_COMMAND === "1" && process.env.AUTO_CODEX_COMMAND?.trim()) {
48937
+ return process.env.AUTO_CODEX_COMMAND.trim();
48938
+ }
48939
+ return CODEX_EXECUTABLE_PATH;
48940
+ }
48941
+ function tomlInlineTable(values) {
48942
+ const entries = Object.entries(values).map(
48943
+ ([key, value2]) => `${tomlKey(key)} = ${tomlString(value2)}`
48944
+ );
48945
+ return `{ ${entries.join(", ")} }`;
48946
+ }
48947
+ function tomlKey(key) {
48948
+ return /^[A-Za-z0-9_-]+$/.test(key) ? key : tomlString(key);
48949
+ }
48950
+ function tomlString(value2) {
48951
+ return `"${value2.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
48952
+ }
48953
+
48954
+ // src/commands/agent-bridge/harness/codex/session.ts
48955
+ var CODEX_REQUEST_TIMEOUT_MS = 3e4;
48956
+ var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
48957
+ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
48958
+ "commandExecution",
48959
+ "fileChange",
48960
+ "mcpToolCall"
48961
+ ]);
48962
+ function startCodexAgentBridgeSession(input) {
48963
+ return new CodexAgentBridgeSessionImpl(input);
48964
+ }
48965
+ var codexAgentBridgeRuntime = {
48966
+ start: startCodexAgentBridgeSession
48967
+ };
48968
+ var CodexAgentBridgeSessionImpl = class {
48969
+ input;
48970
+ proc = null;
48971
+ startup = null;
48972
+ closed = false;
48973
+ nextRequestId = 1;
48974
+ pending = /* @__PURE__ */ new Map();
48975
+ stdoutBuffer = "";
48976
+ threadId = null;
48977
+ activeTurnId = null;
48978
+ // Tool-like items started but not yet completed in the active turn. A non-empty
48979
+ // set means an interrupt/steer would race an unsettled item (FRA-3049 analog).
48980
+ pendingToolItemIds = /* @__PURE__ */ new Set();
48981
+ settlementWaiters = /* @__PURE__ */ new Set();
48982
+ // Messages held in "deferred" mode while a turn is in flight; flushed as a
48983
+ // fresh turn once the active turn completes.
48984
+ deferredMessages = [];
48985
+ constructor(input) {
48986
+ this.input = input;
48987
+ }
48988
+ async prepare() {
48989
+ await this.ensureStarted();
48990
+ }
48991
+ async sendMessage(message, options) {
48992
+ await this.ensureStarted();
48993
+ const threadId = this.requireThreadId();
48994
+ const mode = options?.mode ?? "interrupt";
48995
+ if (this.activeTurnId === null) {
48996
+ await this.startTurn(threadId, message);
48997
+ return;
48998
+ }
48999
+ if (mode === "deferred") {
49000
+ this.deferredMessages.push(message);
49001
+ this.input.writeOutput?.("agent_bridge_codex_message_deferred");
49002
+ return;
49003
+ }
49004
+ await this.injectIntoActiveTurn(threadId, message);
49005
+ }
49006
+ resolveApproval(resolution) {
49007
+ this.writeFrame({
49008
+ jsonrpc: "2.0",
49009
+ id: resolution.requestId,
49010
+ result: { decision: resolution.decision }
49011
+ });
49012
+ this.input.writeOutput?.(
49013
+ `agent_bridge_codex_approval_resolved decision=${resolution.decision}`
49014
+ );
49015
+ }
49016
+ close() {
49017
+ if (this.closed) {
49018
+ return;
49019
+ }
49020
+ this.closed = true;
49021
+ if (this.threadId && this.activeTurnId) {
49022
+ this.writeFrame({
49023
+ jsonrpc: "2.0",
49024
+ id: this.allocRequestId(),
49025
+ method: "turn/interrupt",
49026
+ params: { threadId: this.threadId, turnId: this.activeTurnId }
49027
+ });
49028
+ }
49029
+ this.activeTurnId = null;
49030
+ this.pendingToolItemIds.clear();
49031
+ this.resolveSettlement();
49032
+ this.rejectAllPending(new Error("Codex session is closed"));
49033
+ if (this.deferredMessages.length > 0) {
49034
+ this.input.writeOutput?.(
49035
+ `agent_bridge_codex_deferred_dropped count=${this.deferredMessages.length} reason=session_closed`
49036
+ );
49037
+ this.deferredMessages.length = 0;
49038
+ }
49039
+ this.proc?.kill("SIGTERM");
49040
+ this.proc = null;
49041
+ }
49042
+ // ---------------------------------------------------------------------------
49043
+ // Startup
49044
+ // ---------------------------------------------------------------------------
49045
+ ensureStarted() {
49046
+ if (this.startup) {
49047
+ return this.startup;
49048
+ }
49049
+ this.startup = this.start().catch((error51) => {
49050
+ this.startup = null;
49051
+ this.close();
49052
+ throw error51;
49053
+ });
49054
+ return this.startup;
49055
+ }
49056
+ async start() {
49057
+ const options = codexLaunchOptions(this.input.codex);
49058
+ mkdirSync4(options.codexHome, { recursive: true });
49059
+ writeFileSync3(join2(options.codexHome, "config.toml"), options.configToml);
49060
+ const startedAt = Date.now();
49061
+ this.input.writeOutput?.(
49062
+ `agent_bridge_codex_startup_started codex_home=${options.codexHome}`
49063
+ );
49064
+ const proc = spawn(options.command, options.args, {
49065
+ env: options.env,
49066
+ stdio: ["pipe", "pipe", "pipe"]
49067
+ });
49068
+ this.proc = proc;
49069
+ proc.stdout.setEncoding("utf8");
49070
+ proc.stdout.on("data", (chunk) => this.onStdout(chunk));
49071
+ proc.stderr.setEncoding("utf8");
49072
+ proc.stderr.on(
49073
+ "data",
49074
+ (chunk) => this.input.writeOutput?.(`agent_bridge_codex_stderr ${chunk.trimEnd()}`)
49075
+ );
49076
+ proc.on("exit", (code) => this.onProcessExit(code));
49077
+ proc.on("error", (error51) => void this.input.onError(error51));
49078
+ await this.request("initialize", {
49079
+ clientInfo: {
49080
+ name: "auto-agent-bridge",
49081
+ title: "Auto",
49082
+ version: "1"
49083
+ },
49084
+ capabilities: null
49085
+ });
49086
+ this.notify("initialized");
49087
+ await this.openThread();
49088
+ this.input.writeOutput?.(
49089
+ `agent_bridge_codex_startup_ready duration_ms=${Date.now() - startedAt} thread_id=${this.threadId ?? ""}`
49090
+ );
49091
+ }
49092
+ // Resume the stored thread when present, falling back to a fresh thread if the
49093
+ // resume is rejected (e.g. the rollout was pruned). A fresh thread carries the
49094
+ // neutral thread params (cwd + the agent identity as developerInstructions).
49095
+ async openThread() {
49096
+ if (this.input.resumeThreadId) {
49097
+ try {
49098
+ const resumed = await this.request("thread/resume", {
49099
+ threadId: this.input.resumeThreadId
49100
+ });
49101
+ this.adoptThreadId(resumed);
49102
+ return;
49103
+ } catch (error51) {
49104
+ this.input.writeOutput?.(
49105
+ `agent_bridge_codex_resume_fallback thread_id=${this.input.resumeThreadId} error=${errorMessage2(error51)}`
49106
+ );
49107
+ }
49108
+ }
49109
+ const started = await this.request(
49110
+ "thread/start",
49111
+ codexThreadParams(this.input.codex)
49112
+ );
49113
+ this.adoptThreadId(started);
49114
+ }
49115
+ adoptThreadId(result) {
49116
+ const threadId = threadIdFromResult(result);
49117
+ if (!threadId) {
49118
+ throw new Error("Codex thread response did not include a thread id");
49119
+ }
49120
+ this.threadId = threadId;
49121
+ this.input.onThreadId(threadId);
49122
+ }
49123
+ // ---------------------------------------------------------------------------
49124
+ // Turn delivery
49125
+ // ---------------------------------------------------------------------------
49126
+ async startTurn(threadId, message) {
49127
+ await this.request("turn/start", {
49128
+ threadId,
49129
+ input: [userTextInput(message)]
49130
+ });
49131
+ }
49132
+ // Inject a message into the active turn. Prefers `turn/steer` (codex folds the
49133
+ // input into the running turn and owns transcript consistency); falls back to a
49134
+ // hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
49135
+ async injectIntoActiveTurn(threadId, message) {
49136
+ await this.awaitItemSettlement();
49137
+ const expectedTurnId = this.activeTurnId;
49138
+ if (expectedTurnId === null) {
49139
+ await this.startTurn(threadId, message);
49140
+ return;
49141
+ }
49142
+ try {
49143
+ await this.request("turn/steer", {
49144
+ threadId,
49145
+ input: [userTextInput(message)],
49146
+ expectedTurnId
49147
+ });
49148
+ this.input.writeOutput?.("agent_bridge_codex_steered");
49149
+ return;
49150
+ } catch (error51) {
49151
+ this.input.writeOutput?.(
49152
+ `agent_bridge_codex_steer_failed error=${errorMessage2(error51)}`
49153
+ );
49154
+ }
49155
+ if (this.activeTurnId) {
49156
+ await this.interruptActiveTurn(threadId, this.activeTurnId);
49157
+ }
49158
+ await this.startTurn(threadId, message);
49159
+ }
49160
+ async interruptActiveTurn(threadId, turnId) {
49161
+ try {
49162
+ await this.request("turn/interrupt", { threadId, turnId });
49163
+ this.input.writeOutput?.("agent_bridge_codex_interrupted");
49164
+ } catch (error51) {
49165
+ this.input.writeOutput?.(
49166
+ `agent_bridge_codex_interrupt_failed error=${errorMessage2(error51)}`
49167
+ );
49168
+ }
49169
+ }
49170
+ flushDeferredMessages() {
49171
+ if (this.deferredMessages.length === 0 || this.threadId === null) {
49172
+ return;
49173
+ }
49174
+ const pending = this.deferredMessages.splice(0);
49175
+ this.input.writeOutput?.(
49176
+ `agent_bridge_codex_deferred_flush count=${pending.length}`
49177
+ );
49178
+ void (async () => {
49179
+ for (const message of pending) {
49180
+ try {
49181
+ await this.sendMessage(message, { mode: "interrupt" });
49182
+ } catch (error51) {
49183
+ void this.input.onError(error51);
49184
+ }
49185
+ }
49186
+ })();
49187
+ }
49188
+ // ---------------------------------------------------------------------------
49189
+ // Item settlement
49190
+ // ---------------------------------------------------------------------------
49191
+ awaitItemSettlement() {
49192
+ if (this.pendingToolItemIds.size === 0) {
49193
+ return Promise.resolve();
49194
+ }
49195
+ return new Promise((resolve) => {
49196
+ let done = false;
49197
+ const finish = () => {
49198
+ if (done) {
49199
+ return;
49200
+ }
49201
+ done = true;
49202
+ clearTimeout(timer);
49203
+ this.settlementWaiters.delete(waiter);
49204
+ resolve();
49205
+ };
49206
+ const waiter = () => finish();
49207
+ const timer = setTimeout(finish, CODEX_ITEM_SETTLE_TIMEOUT_MS);
49208
+ timer.unref?.();
49209
+ this.settlementWaiters.add(waiter);
49210
+ });
49211
+ }
49212
+ resolveSettlement() {
49213
+ if (this.settlementWaiters.size === 0) {
49214
+ return;
49215
+ }
49216
+ const waiters = [...this.settlementWaiters];
49217
+ this.settlementWaiters.clear();
49218
+ for (const waiter of waiters) {
49219
+ waiter();
49220
+ }
49221
+ }
49222
+ // ---------------------------------------------------------------------------
49223
+ // Incoming frame handling
49224
+ // ---------------------------------------------------------------------------
49225
+ onStdout(chunk) {
49226
+ this.stdoutBuffer += chunk;
49227
+ let newline = this.stdoutBuffer.indexOf("\n");
49228
+ while (newline >= 0) {
49229
+ const line = this.stdoutBuffer.slice(0, newline).trim();
49230
+ this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
49231
+ if (line) {
49232
+ this.handleFrame(line);
49233
+ }
49234
+ newline = this.stdoutBuffer.indexOf("\n");
49235
+ }
49236
+ }
49237
+ handleFrame(line) {
49238
+ let message;
49239
+ try {
49240
+ message = parseCodexServerFrame(JSON.parse(line));
49241
+ } catch (error51) {
49242
+ this.input.writeOutput?.(
49243
+ `agent_bridge_codex_parse_failed error=${errorMessage2(error51)}`
49244
+ );
49245
+ return;
49246
+ }
49247
+ switch (message.kind) {
49248
+ case "response":
49249
+ this.settleResponse(message.id, message.result, message.error);
49250
+ return;
49251
+ case "notification":
49252
+ this.trackNotification(message.notification);
49253
+ void this.input.onNotification(message.notification);
49254
+ return;
49255
+ case "serverRequest":
49256
+ void this.input.onServerRequest(message.request);
49257
+ return;
49258
+ case "elicitation":
49259
+ this.writeFrame({
49260
+ jsonrpc: "2.0",
49261
+ id: message.requestId,
49262
+ result: { action: "accept", content: {}, _meta: null }
49263
+ });
49264
+ this.input.writeOutput?.(
49265
+ `agent_bridge_codex_elicitation_accepted server=${message.serverName}`
49266
+ );
49267
+ return;
49268
+ case "unsupportedRequest":
49269
+ this.writeFrame({
49270
+ jsonrpc: "2.0",
49271
+ id: message.requestId,
49272
+ error: { code: -32601, message: "Unsupported by Auto bridge" }
49273
+ });
49274
+ this.input.writeOutput?.(
49275
+ `agent_bridge_codex_unsupported_request method=${message.method}`
49276
+ );
49277
+ return;
49278
+ case "ignored":
49279
+ return;
49280
+ }
49281
+ }
49282
+ // Track turn/item lifecycle so steer/interrupt target the live turn and gate on
49283
+ // tool-item settlement.
49284
+ trackNotification(notification) {
49285
+ switch (notification.type) {
49286
+ case "turnStarted":
49287
+ this.activeTurnId = notification.turnId;
49288
+ return;
49289
+ case "turnCompleted":
49290
+ if (notification.turnId === this.activeTurnId) {
49291
+ this.activeTurnId = null;
49292
+ }
49293
+ this.pendingToolItemIds.clear();
49294
+ this.resolveSettlement();
49295
+ this.flushDeferredMessages();
49296
+ return;
49297
+ case "itemStarted":
49298
+ if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
49299
+ this.pendingToolItemIds.add(notification.item.id);
49300
+ }
49301
+ return;
49302
+ case "itemCompleted":
49303
+ this.pendingToolItemIds.delete(notification.item.id);
49304
+ if (this.pendingToolItemIds.size === 0) {
49305
+ this.resolveSettlement();
49306
+ }
49307
+ return;
49308
+ default:
49309
+ return;
49310
+ }
49311
+ }
49312
+ onProcessExit(code) {
49313
+ this.rejectAllPending(
49314
+ new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
49315
+ );
49316
+ this.activeTurnId = null;
49317
+ this.pendingToolItemIds.clear();
49318
+ this.resolveSettlement();
49319
+ this.input.writeOutput?.(`agent_bridge_codex_exited code=${code ?? ""}`);
49320
+ if (!this.closed) {
49321
+ this.closed = true;
49322
+ this.input.onExit();
49323
+ }
49324
+ }
49325
+ // ---------------------------------------------------------------------------
49326
+ // JSON-RPC transport
49327
+ // ---------------------------------------------------------------------------
49328
+ request(method, params) {
49329
+ const id = this.allocRequestId();
49330
+ const frame = { jsonrpc: "2.0", id, method, params };
49331
+ return new Promise((resolve, reject) => {
49332
+ this.pending.set(id, { resolve, reject });
49333
+ const timer = setTimeout(() => {
49334
+ if (this.pending.delete(id)) {
49335
+ reject(new Error(`Codex request timed out: ${method}`));
49336
+ }
49337
+ }, CODEX_REQUEST_TIMEOUT_MS);
49338
+ timer.unref?.();
49339
+ this.writeFrame(frame);
49340
+ });
49341
+ }
49342
+ settleResponse(id, result, error51) {
49343
+ if (typeof id !== "number") {
49344
+ return;
49345
+ }
49346
+ const pending = this.pending.get(id);
49347
+ if (!pending) {
49348
+ return;
49349
+ }
49350
+ this.pending.delete(id);
49351
+ if (error51 !== void 0) {
49352
+ pending.reject(new Error(error51));
49353
+ return;
49354
+ }
49355
+ pending.resolve(result);
49356
+ }
49357
+ rejectAllPending(error51) {
49358
+ const pending = [...this.pending.values()];
49359
+ this.pending.clear();
49360
+ for (const request of pending) {
49361
+ request.reject(error51);
49362
+ }
49363
+ }
49364
+ notify(method, params) {
49365
+ this.writeFrame({
49366
+ jsonrpc: "2.0",
49367
+ method,
49368
+ ...params !== void 0 ? { params } : {}
49369
+ });
49370
+ }
49371
+ writeFrame(frame) {
49372
+ const proc = this.proc;
49373
+ if (!proc || proc.stdin.destroyed) {
49374
+ return;
49375
+ }
49376
+ proc.stdin.write(`${JSON.stringify(frame)}
49377
+ `);
49378
+ }
49379
+ allocRequestId() {
49380
+ const id = this.nextRequestId;
49381
+ this.nextRequestId += 1;
49382
+ return id;
49383
+ }
49384
+ requireThreadId() {
49385
+ if (this.threadId === null) {
49386
+ throw new Error("Codex session has no active thread");
49387
+ }
49388
+ return this.threadId;
49389
+ }
49390
+ };
49391
+ function userTextInput(text) {
49392
+ return { type: "text", text, text_elements: [] };
49393
+ }
49394
+ function threadIdFromResult(result) {
49395
+ if (result === null || typeof result !== "object") {
49396
+ return null;
49397
+ }
49398
+ const thread = result.thread;
49399
+ if (thread === null || typeof thread !== "object") {
49400
+ return null;
49401
+ }
49402
+ const id = thread.id;
49403
+ return typeof id === "string" && id.length > 0 ? id : null;
49404
+ }
49405
+ function errorMessage2(error51) {
49406
+ return error51 instanceof Error ? error51.message : String(error51);
49407
+ }
49408
+
49409
+ // src/commands/agent-bridge/harness/codex/index.ts
49410
+ function createCodexCommandHandler(input) {
49411
+ return new CodexCommandHandler({
49412
+ ...input,
49413
+ sessionResume: input.sessionResume ?? fileCodexThreadResumeStore()
49414
+ });
49415
+ }
49416
+ var CodexCommandHandler = class {
49417
+ constructor(input) {
49418
+ this.input = input;
49419
+ this.outputBuffer = new AgentBridgeOutputBuffer(input);
49420
+ }
49421
+ input;
49422
+ context = null;
49423
+ session = null;
49424
+ injectedCommands = /* @__PURE__ */ new Set();
49425
+ // itemId -> JSON-RPC request id of the parked approval request, so an `answer`
49426
+ // command keyed by toolCallId (= itemId) can resolve the right server request.
49427
+ pendingApprovals = /* @__PURE__ */ new Map();
49428
+ outputBuffer;
49429
+ projector = new CodexProjector();
49430
+ // ---------------------------------------------------------------------------
49431
+ // Lifecycle (public API)
49432
+ // ---------------------------------------------------------------------------
49433
+ setContext(nextContext) {
49434
+ this.context = RuntimeBridgeConnectedPayloadSchema.parse(nextContext);
49435
+ }
49436
+ async replayPendingOutputs() {
49437
+ await this.outputBuffer.replayPendingOutputs();
49438
+ }
49439
+ async prepare() {
49440
+ await this.ensureSession().prepare();
49441
+ }
49442
+ shutdown() {
49443
+ this.session?.close();
49444
+ this.session = null;
49445
+ this.pendingApprovals.clear();
49446
+ }
49447
+ async handleCommand(rawDelivery) {
49448
+ const delivery = RuntimeBridgeCommandDeliverySchema.parse(rawDelivery);
49449
+ const socketId = this.input.socketId?.();
49450
+ const activeContext = this.context;
49451
+ if (!activeContext || delivery.sessionId !== activeContext.sessionId || delivery.runtimeId !== activeContext.runtimeId || delivery.bridgeLeaseId !== activeContext.bridgeLeaseId) {
49452
+ return commandAck({ delivery, socketId, status: "stale_lease" });
49453
+ }
49454
+ if (this.injectedCommands.has(delivery.commandId)) {
49455
+ return commandAck({ delivery, socketId, status: "duplicate" });
49456
+ }
49457
+ if (delivery.kind === "answer") {
49458
+ return this.handleAnswerCommand(delivery, activeContext, socketId);
49459
+ }
49460
+ if (delivery.kind === "stop") {
49461
+ this.injectedCommands.add(delivery.commandId);
49462
+ this.shutdown();
49463
+ return commandAck({ delivery, socketId, status: "injected" });
49464
+ }
49465
+ if (delivery.kind !== "message") {
49466
+ return commandAck({
49467
+ delivery,
49468
+ socketId,
49469
+ status: "failed",
49470
+ error: `Unsupported command kind: ${delivery.kind}`
49471
+ });
49472
+ }
49473
+ const message = deliveryMessage2(delivery);
49474
+ if (message === null) {
49475
+ return commandAck({
49476
+ delivery,
49477
+ socketId,
49478
+ status: "failed",
49479
+ error: "Message command payload must include a string message"
49480
+ });
49481
+ }
49482
+ this.injectedCommands.add(delivery.commandId);
49483
+ try {
49484
+ await this.emitUserMessageEntry(
49485
+ activeContext,
49486
+ delivery.commandId,
49487
+ message
49488
+ );
49489
+ await this.ensureSession().sendMessage(message, {
49490
+ mode: deliveryMode2(delivery)
49491
+ });
49492
+ } catch (error51) {
49493
+ this.injectedCommands.delete(delivery.commandId);
49494
+ return commandAck({
49495
+ delivery,
49496
+ socketId,
49497
+ status: "failed",
49498
+ error: errorMessage3(error51)
49499
+ });
49500
+ }
49501
+ return commandAck({ delivery, socketId, status: "injected" });
49502
+ }
49503
+ // ---------------------------------------------------------------------------
49504
+ // Answering parked approvals
49505
+ // ---------------------------------------------------------------------------
49506
+ async handleAnswerCommand(delivery, activeContext, socketId) {
49507
+ const payload = RuntimeBridgeAnswerCommandPayloadSchema.safeParse(
49508
+ delivery.payload
49509
+ );
49510
+ if (!payload.success) {
49511
+ return commandAck({
49512
+ delivery,
49513
+ socketId,
49514
+ status: "failed",
49515
+ error: "Answer command payload must include toolCallId and answers"
49516
+ });
49517
+ }
49518
+ this.injectedCommands.add(delivery.commandId);
49519
+ const requestId = this.pendingApprovals.get(payload.data.toolCallId);
49520
+ try {
49521
+ if (requestId !== void 0) {
49522
+ this.pendingApprovals.delete(payload.data.toolCallId);
49523
+ const decision = codexApprovalDecision({
49524
+ answers: payload.data.answers,
49525
+ ...payload.data.response ? { response: payload.data.response } : {}
49526
+ });
49527
+ this.ensureSession().resolveApproval({ requestId, decision });
49528
+ this.input.writeOutput?.(
49529
+ `agent_bridge_codex_question_answered tool_use_id=${payload.data.toolCallId} decision=${decision}`
49530
+ );
49531
+ } else {
49532
+ const message = answerFallbackMessage2(payload.data);
49533
+ await this.emitUserMessageEntry(
49534
+ activeContext,
49535
+ delivery.commandId,
49536
+ message
49537
+ );
49538
+ await this.ensureSession().sendMessage(message);
49539
+ }
49540
+ } catch (error51) {
49541
+ this.injectedCommands.delete(delivery.commandId);
49542
+ return commandAck({
49543
+ delivery,
49544
+ socketId,
49545
+ status: "failed",
49546
+ error: errorMessage3(error51)
49547
+ });
49548
+ }
49549
+ return commandAck({ delivery, socketId, status: "injected" });
49550
+ }
49551
+ // ---------------------------------------------------------------------------
49552
+ // Session callbacks
49553
+ // ---------------------------------------------------------------------------
49554
+ async emitUserMessageEntry(activeContext, commandId, message) {
49555
+ await this.emit(activeContext, {
49556
+ type: "entry",
49557
+ entry: {
49558
+ messageId: commandId,
49559
+ role: "user",
49560
+ kind: "message",
49561
+ status: "completed",
49562
+ content: { parts: [{ type: "text", text: message }] }
49563
+ }
49564
+ });
49565
+ }
49566
+ async handleNotification(notification) {
49567
+ const activeContext = this.context;
49568
+ if (!activeContext) {
49569
+ return;
49570
+ }
49571
+ for (const projection of this.projector.project(notification)) {
49572
+ await this.emit(activeContext, projection);
49573
+ }
49574
+ }
49575
+ async handleServerRequest(request) {
49576
+ const activeContext = this.context;
49577
+ if (!activeContext) {
49578
+ return;
49579
+ }
49580
+ this.pendingApprovals.set(request.itemId, request.requestId);
49581
+ this.input.writeOutput?.(
49582
+ `agent_bridge_codex_question_pending tool_use_id=${request.itemId}`
49583
+ );
49584
+ await this.emit(activeContext, this.projector.projectApproval(request));
49585
+ }
49586
+ async handleSessionError(error51) {
49587
+ this.input.writeOutput?.(
49588
+ `agent_bridge_codex_session_failed error=${errorMessage3(error51)}`
49589
+ );
49590
+ const activeContext = this.context;
49591
+ if (!activeContext) {
49592
+ return;
49593
+ }
49594
+ await this.emit(
49595
+ activeContext,
49596
+ this.projector.projectSessionFailure(errorMessage3(error51))
49597
+ );
49598
+ }
49599
+ async emit(activeContext, projection) {
49600
+ try {
49601
+ await this.outputBuffer.emitProjection(activeContext, projection);
49602
+ } catch (error51) {
49603
+ this.input.writeOutput?.(
49604
+ `agent_bridge_output_emit_failed error=${errorMessage3(error51)}`
49605
+ );
49606
+ }
49607
+ }
49608
+ ensureSession() {
49609
+ if (this.session) {
49610
+ return this.session;
49611
+ }
49612
+ const resumeThreadId = this.storedResumeThreadId();
49613
+ const session = codexAgentBridgeRuntime.start({
49614
+ codex: this.input.codex,
49615
+ ...resumeThreadId ? { resumeThreadId } : {},
49616
+ onNotification: (notification) => this.handleNotification(notification),
49617
+ onServerRequest: (request) => this.handleServerRequest(request),
49618
+ onThreadId: (threadId) => this.persistThreadId(threadId),
49619
+ onError: (error51) => this.handleSessionError(error51),
49620
+ onExit: () => {
49621
+ if (this.session === session) {
49622
+ this.session = null;
49623
+ }
49624
+ this.input.writeOutput?.("agent_bridge_codex_session_exited");
49625
+ },
49626
+ ...this.input.writeOutput ? { writeOutput: this.input.writeOutput } : {}
49627
+ });
49628
+ this.session = session;
49629
+ return session;
49630
+ }
49631
+ storedResumeThreadId() {
49632
+ const store = this.input.sessionResume;
49633
+ const activeContext = this.context;
49634
+ if (!store || !activeContext) {
49635
+ return void 0;
49636
+ }
49637
+ try {
49638
+ return store.read(activeContext.sessionId) ?? void 0;
49639
+ } catch (error51) {
49640
+ this.input.writeOutput?.(
49641
+ `agent_bridge_codex_thread_read_failed error=${errorMessage3(error51)}`
49642
+ );
49643
+ return void 0;
49644
+ }
49645
+ }
49646
+ persistThreadId(threadId) {
49647
+ const store = this.input.sessionResume;
49648
+ const activeContext = this.context;
49649
+ if (!store || !activeContext) {
49650
+ return;
49651
+ }
49652
+ try {
49653
+ store.write({ sessionId: activeContext.sessionId, threadId });
49654
+ } catch (error51) {
49655
+ this.input.writeOutput?.(
49656
+ `agent_bridge_codex_thread_persist_failed error=${errorMessage3(error51)}`
49657
+ );
49658
+ }
49659
+ }
49660
+ };
49661
+ function answerFallbackMessage2(answer) {
49662
+ const lines = Object.entries(answer.answers).map(
49663
+ ([question, value2]) => `- ${question}: ${value2}`
49664
+ );
49665
+ if (answer.response) {
49666
+ lines.push(`The user also responded: ${answer.response}`);
49667
+ }
49668
+ return ["The user answered your questions:", ...lines].join("\n");
49669
+ }
49670
+ function deliveryMessage2(delivery) {
49671
+ const payload = delivery.payload;
49672
+ if (payload && typeof payload === "object" && "message" in payload && typeof payload.message === "string") {
49673
+ return payload.message;
49674
+ }
49675
+ return null;
49676
+ }
49677
+ function deliveryMode2(delivery) {
49678
+ const payload = delivery.payload;
49679
+ if (payload && typeof payload === "object" && "deliveryMode" in payload) {
49680
+ const parsed = MessageDeliveryModeSchema.safeParse(payload.deliveryMode);
49681
+ if (parsed.success) {
49682
+ return parsed.data;
49683
+ }
49684
+ }
49685
+ return "interrupt";
49686
+ }
49687
+ function errorMessage3(error51) {
49688
+ return error51 instanceof Error ? error51.message : String(error51);
49689
+ }
49690
+
48464
49691
  // src/commands/agent-bridge/harness/index.ts
48465
49692
  async function runAgentBridgeHarness(options) {
48466
49693
  await runAgentBridgeSocket({
@@ -48486,6 +49713,14 @@ function createHarnessCommandHandler(input) {
48486
49713
  sessionResume: fileClaudeSessionResumeStore()
48487
49714
  });
48488
49715
  }
49716
+ case "codex": {
49717
+ const { kind, ...codex } = config2;
49718
+ return createCodexCommandHandler({
49719
+ ...base,
49720
+ codex,
49721
+ sessionResume: fileCodexThreadResumeStore()
49722
+ });
49723
+ }
48489
49724
  }
48490
49725
  }
48491
49726
  function resolveHarnessConfig(bootstrap) {
@@ -48494,14 +49729,27 @@ function resolveHarnessConfig(bootstrap) {
48494
49729
 
48495
49730
  // src/commands/agent-bridge/runtime-log.ts
48496
49731
  var RUNTIME_LOG_COMPONENT = "runtime-cli";
49732
+ var INGEST_BATCH_SIZE = 50;
49733
+ var INGEST_FLUSH_DELAY_MS = 1e3;
49734
+ var INGEST_MAX_QUEUED_LINES = 1e3;
48497
49735
  function createRuntimeLogger(env = process.env) {
48498
49736
  const level = parseRuntimeLogLevel(env.AUTO_RUNTIME_LOG_LEVEL);
49737
+ const ingest = createRuntimeLogIngest(env);
49738
+ let nextLogSeq = 1;
48499
49739
  const emit = (lineLevel) => {
48500
49740
  return (message, context) => {
48501
49741
  if (!runtimeLogLevelEnabled(level, lineLevel)) {
48502
49742
  return;
48503
49743
  }
48504
- writeRuntimeLogLine(lineLevel, message, context);
49744
+ const line = runtimeLogLine({
49745
+ context,
49746
+ level: lineLevel,
49747
+ logSeq: nextLogSeq,
49748
+ message
49749
+ });
49750
+ nextLogSeq += 1;
49751
+ writeRuntimeLogLine(line);
49752
+ ingest?.enqueue(line);
48505
49753
  };
48506
49754
  };
48507
49755
  return {
@@ -48512,17 +49760,88 @@ function createRuntimeLogger(env = process.env) {
48512
49760
  error: emit("error")
48513
49761
  };
48514
49762
  }
48515
- function writeRuntimeLogLine(level, message, context) {
48516
- const payload = {
49763
+ function runtimeLogLine(input) {
49764
+ return {
49765
+ ...input.context ?? {},
49766
+ logSeq: input.logSeq,
48517
49767
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48518
- level,
49768
+ level: input.level,
48519
49769
  component: RUNTIME_LOG_COMPONENT,
48520
- message,
48521
- ...context ?? {}
49770
+ message: input.message
48522
49771
  };
49772
+ }
49773
+ function writeRuntimeLogLine(payload) {
48523
49774
  process.stderr.write(`${JSON.stringify(payload, replaceErrors)}
48524
49775
  `);
48525
49776
  }
49777
+ function createRuntimeLogIngest(env) {
49778
+ const url3 = env[RUNTIME_LOG_INGEST_URL_ENV]?.trim();
49779
+ const token = env[RUNTIME_LOG_INGEST_TOKEN_ENV]?.trim();
49780
+ if (!url3 || !token) {
49781
+ return void 0;
49782
+ }
49783
+ const queue = [];
49784
+ let flushTimer;
49785
+ let inFlight = false;
49786
+ const scheduleFlush = () => {
49787
+ if (flushTimer || inFlight) {
49788
+ return;
49789
+ }
49790
+ flushTimer = setTimeout(() => {
49791
+ flushTimer = void 0;
49792
+ void flush();
49793
+ }, INGEST_FLUSH_DELAY_MS);
49794
+ flushTimer.unref?.();
49795
+ };
49796
+ const flush = async () => {
49797
+ if (inFlight || queue.length === 0) {
49798
+ return;
49799
+ }
49800
+ inFlight = true;
49801
+ const batch = queue.splice(0, INGEST_BATCH_SIZE);
49802
+ try {
49803
+ const response = await fetch(url3, {
49804
+ method: "POST",
49805
+ headers: {
49806
+ authorization: `Bearer ${token}`,
49807
+ "content-type": "application/json"
49808
+ },
49809
+ body: JSON.stringify({ lines: batch }, replaceErrors)
49810
+ });
49811
+ if (!response.ok) {
49812
+ if (shouldRetryIngestResponse(response)) {
49813
+ requeue(batch);
49814
+ }
49815
+ }
49816
+ } catch {
49817
+ requeue(batch);
49818
+ } finally {
49819
+ inFlight = false;
49820
+ if (queue.length > 0) {
49821
+ scheduleFlush();
49822
+ }
49823
+ }
49824
+ };
49825
+ const requeue = (batch) => {
49826
+ queue.unshift(...batch);
49827
+ trimQueue();
49828
+ };
49829
+ const trimQueue = () => {
49830
+ if (queue.length > INGEST_MAX_QUEUED_LINES) {
49831
+ queue.splice(0, queue.length - INGEST_MAX_QUEUED_LINES);
49832
+ }
49833
+ };
49834
+ return {
49835
+ enqueue(line) {
49836
+ queue.push(line);
49837
+ trimQueue();
49838
+ scheduleFlush();
49839
+ }
49840
+ };
49841
+ }
49842
+ function shouldRetryIngestResponse(response) {
49843
+ return response.status === 429 || response.status >= 500;
49844
+ }
48526
49845
  function replaceErrors(_key, value2) {
48527
49846
  if (value2 instanceof Error) {
48528
49847
  return { name: value2.name, message: value2.message, stack: value2.stack };