@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.
- package/dist/agent-bridge.js +1338 -19
- package/dist/index.js +1483 -167
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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.
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
48516
|
-
|
|
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 };
|