@ai-sdk/harness-codex 1.0.126 → 1.0.129

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.
@@ -643,202 +643,1214 @@ function serialiseError(err) {
643
643
  return err;
644
644
  }
645
645
 
646
- // src/bridge/index.ts
647
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
646
+ // src/bridge/codex-app-server-driver.ts
647
+ import { createHash as createHash2 } from "crypto";
648
+ import { fileURLToPath } from "url";
648
649
 
649
- // src/bridge/cli-relay.ts
650
- var CLI_SHIM_FILENAME = "harness-tool.mjs";
651
- function buildCliShimScript({
652
- relayPort
650
+ // src/bridge/codex-tool-filtering.ts
651
+ function resolveCodexBuiltinToolPolicy({
652
+ builtinToolFiltering,
653
+ webSearch
653
654
  }) {
654
- return `#!/usr/bin/env node
655
- const [toolName, inputJson = '{}'] = process.argv.slice(2);
656
- if (!toolName) {
657
- console.error('Usage: harness-tool <tool_name> <json_input>');
658
- process.exit(64);
655
+ const included = (toolName) => builtinToolFiltering == null || (builtinToolFiltering.mode === "allow" ? builtinToolFiltering.toolNames.includes(toolName) : !builtinToolFiltering.toolNames.includes(toolName));
656
+ const disabled = {
657
+ bash: !included("bash"),
658
+ webSearch: !included("webSearch"),
659
+ apply_patch: !included("apply_patch"),
660
+ view_image: !included("view_image")
661
+ };
662
+ const disableEnvironments = disabled.bash && disabled.apply_patch && disabled.view_image;
663
+ return {
664
+ disabled,
665
+ disableEnvironments,
666
+ denyApplyPatchWithHook: disabled.apply_patch && !disableEnvironments,
667
+ webSearchMode: webSearch && !disabled.webSearch ? "live" : "disabled"
668
+ };
659
669
  }
660
- let input;
661
- try {
662
- input = JSON.parse(inputJson);
663
- } catch (error) {
664
- console.error('Invalid JSON input: ' + (error instanceof Error ? error.message : String(error)));
665
- process.exit(64);
670
+
671
+ // src/bridge/codex-app-server-client.ts
672
+ import { spawn } from "child_process";
673
+ var CodexAppServerRequestError = class extends Error {
674
+ constructor({ method, error }) {
675
+ super(`Codex app-server ${method} failed: ${error.message}`);
676
+ this.name = "CodexAppServerRequestError";
677
+ this.method = method;
678
+ this.code = error.code;
679
+ this.data = error.data;
680
+ }
681
+ };
682
+ var CodexAppServerClient = class {
683
+ constructor({
684
+ executable,
685
+ args: args2,
686
+ cwd,
687
+ env,
688
+ onNotification,
689
+ onRequest,
690
+ onStderr
691
+ }) {
692
+ this.pending = /* @__PURE__ */ new Map();
693
+ this.nextRequestId = 1;
694
+ this.stdoutBuffer = "";
695
+ this.parseQueue = Promise.resolve();
696
+ this.closed = false;
697
+ this.resolveFailure = () => {
698
+ };
699
+ this.exitResult = null;
700
+ this.onNotification = onNotification;
701
+ this.onRequest = onRequest;
702
+ this.onStderr = onStderr;
703
+ this.failed = new Promise((resolve) => {
704
+ this.resolveFailure = resolve;
705
+ });
706
+ this.child = spawn(executable, args2, {
707
+ cwd,
708
+ env,
709
+ stdio: ["pipe", "pipe", "pipe"]
710
+ });
711
+ this.exited = new Promise((resolve) => {
712
+ this.child.once("exit", (code, signal) => {
713
+ this.exitResult = { code, signal };
714
+ this.fail(
715
+ new Error(
716
+ `Codex app-server exited (code ${code ?? "null"}, signal ${signal ?? "null"}).`
717
+ )
718
+ );
719
+ resolve(this.exitResult);
720
+ });
721
+ });
722
+ this.child.once("error", (error) => this.fail(error));
723
+ this.child.stdout.setEncoding("utf8");
724
+ this.child.stdout.on("data", (chunk) => this.consumeStdout(String(chunk)));
725
+ this.child.stderr.setEncoding("utf8");
726
+ this.child.stderr.on("data", (chunk) => this.onStderr(String(chunk)));
727
+ }
728
+ async initialize({
729
+ clientName,
730
+ clientVersion
731
+ }) {
732
+ await this.request({
733
+ method: "initialize",
734
+ params: {
735
+ clientInfo: { name: clientName, version: clientVersion },
736
+ capabilities: { experimentalApi: true }
737
+ }
738
+ });
739
+ this.notify({ method: "initialized" });
740
+ }
741
+ request({
742
+ method,
743
+ params
744
+ }) {
745
+ if (this.closed || this.exitResult != null) {
746
+ return Promise.reject(new Error("Codex app-server is not running."));
747
+ }
748
+ const id = this.nextRequestId++;
749
+ const response = new Promise((resolve, reject) => {
750
+ this.pending.set(id, { method, resolve, reject });
751
+ });
752
+ try {
753
+ this.write({ id, method, ...params === void 0 ? {} : { params } });
754
+ } catch (error) {
755
+ this.pending.delete(id);
756
+ return Promise.reject(error);
757
+ }
758
+ return response;
759
+ }
760
+ notify({ method, params }) {
761
+ this.write({ method, ...params === void 0 ? {} : { params } });
762
+ }
763
+ async close() {
764
+ if (this.closed) return;
765
+ this.closed = true;
766
+ this.child.stdin.end();
767
+ if (await this.waitForExit({ timeoutMs: 500 })) return;
768
+ this.child.kill("SIGTERM");
769
+ if (await this.waitForExit({ timeoutMs: 1e3 })) return;
770
+ this.child.kill("SIGKILL");
771
+ await this.waitForExit({ timeoutMs: 1e3 });
772
+ }
773
+ waitUntilExit() {
774
+ return this.exited;
775
+ }
776
+ waitUntilFailure() {
777
+ return this.failed.then((error) => {
778
+ throw error;
779
+ });
780
+ }
781
+ write(message) {
782
+ if (this.closed || this.exitResult != null || !this.child.stdin.writable) {
783
+ throw new Error("Codex app-server is not writable.");
784
+ }
785
+ this.child.stdin.write(`${JSON.stringify(message)}
786
+ `);
787
+ }
788
+ consumeStdout(chunk) {
789
+ this.stdoutBuffer += chunk;
790
+ let newline = this.stdoutBuffer.indexOf("\n");
791
+ while (newline !== -1) {
792
+ const line = this.stdoutBuffer.slice(0, newline).trim();
793
+ this.stdoutBuffer = this.stdoutBuffer.slice(newline + 1);
794
+ if (line.length > 0) {
795
+ this.parseQueue = this.parseQueue.then(() => this.processLine(line)).catch((error) => this.fail(error));
796
+ }
797
+ newline = this.stdoutBuffer.indexOf("\n");
798
+ }
799
+ }
800
+ async processLine(line) {
801
+ let message;
802
+ try {
803
+ message = JSON.parse(line);
804
+ } catch {
805
+ throw new Error("Codex app-server wrote an invalid JSON-RPC message.");
806
+ }
807
+ if (!isRecord(message)) {
808
+ throw new Error("Codex app-server wrote an invalid JSON-RPC message.");
809
+ }
810
+ if (typeof message.method === "string") {
811
+ if (isJsonRpcId(message.id)) {
812
+ void this.handleServerRequest({
813
+ id: message.id,
814
+ method: message.method,
815
+ ...message.params === void 0 ? {} : { params: message.params }
816
+ });
817
+ } else {
818
+ this.onNotification({
819
+ method: message.method,
820
+ ...message.params === void 0 ? {} : { params: message.params }
821
+ });
822
+ }
823
+ return;
824
+ }
825
+ if (!isJsonRpcId(message.id)) {
826
+ throw new Error("Codex app-server response is missing a valid id.");
827
+ }
828
+ const pending = this.pending.get(message.id);
829
+ if (pending == null) return;
830
+ this.pending.delete(message.id);
831
+ if (isJsonRpcError(message.error)) {
832
+ pending.reject(
833
+ new CodexAppServerRequestError({
834
+ method: pending.method,
835
+ error: message.error
836
+ })
837
+ );
838
+ return;
839
+ }
840
+ if (!Object.prototype.hasOwnProperty.call(message, "result")) {
841
+ pending.reject(
842
+ new Error(`Codex app-server ${pending.method} response has no result.`)
843
+ );
844
+ return;
845
+ }
846
+ pending.resolve(message.result);
847
+ }
848
+ async handleServerRequest(request) {
849
+ try {
850
+ const result = await this.onRequest(request);
851
+ if (!this.closed && this.exitResult == null) {
852
+ this.write({ id: request.id, result });
853
+ }
854
+ } catch (error) {
855
+ if (!this.closed && this.exitResult == null) {
856
+ this.write({
857
+ id: request.id,
858
+ error: {
859
+ code: -32603,
860
+ message: error instanceof Error ? error.message : String(error)
861
+ }
862
+ });
863
+ }
864
+ }
865
+ }
866
+ failPending(error) {
867
+ for (const pending of this.pending.values()) pending.reject(error);
868
+ this.pending.clear();
869
+ }
870
+ fail(error) {
871
+ this.failPending(error);
872
+ if (this.failure !== void 0) return;
873
+ this.failure = error;
874
+ this.resolveFailure(error);
875
+ }
876
+ async waitForExit({
877
+ timeoutMs
878
+ }) {
879
+ if (this.exitResult != null) return true;
880
+ return Promise.race([
881
+ this.exited.then(() => true),
882
+ new Promise((resolve) => {
883
+ const timer = setTimeout(() => resolve(false), timeoutMs);
884
+ timer.unref?.();
885
+ })
886
+ ]);
887
+ }
888
+ };
889
+ function isRecord(value) {
890
+ return value != null && typeof value === "object" && !Array.isArray(value);
666
891
  }
667
- const requestId = 'cli-' + Date.now() + '-' + Math.random().toString(16).slice(2);
668
- const response = await fetch('http://127.0.0.1:${relayPort}', {
669
- method: 'POST',
670
- headers: { 'Content-Type': 'application/json' },
671
- body: JSON.stringify({ requestId, toolName, input }),
672
- });
673
- const text = await response.text();
674
- let payload;
675
- try {
676
- payload = text ? JSON.parse(text) : {};
677
- } catch {
678
- payload = { error: text };
892
+ function isJsonRpcId(value) {
893
+ return typeof value === "string" || typeof value === "number";
679
894
  }
680
- if (!response.ok || payload.error) {
681
- console.error(String(payload.error ?? ('tool relay failed with HTTP ' + response.status)));
682
- process.exit(1);
895
+ function isJsonRpcError(value) {
896
+ return isRecord(value) && typeof value.code === "number" && typeof value.message === "string";
683
897
  }
684
- console.log(JSON.stringify(payload.result ?? payload, null, 2));
685
- `;
898
+
899
+ // src/bridge/create-app-server-event-handler.ts
900
+ function createAppServerEventHandler({
901
+ stepTracker,
902
+ emitStreamEvent,
903
+ emitWarning,
904
+ emitError
905
+ }) {
906
+ let activeThreadId;
907
+ let activeTurnId;
908
+ let settled = false;
909
+ let accumulatedUsage = emptyUsageBreakdown();
910
+ let lastCumulativeUsageKey;
911
+ const textByItem = /* @__PURE__ */ new Map();
912
+ const reasoningByItem = /* @__PURE__ */ new Map();
913
+ const nativeToolCalls = /* @__PURE__ */ new Map();
914
+ let resolveCompletion = () => {
915
+ };
916
+ const completion = new Promise((resolve) => {
917
+ resolveCompletion = resolve;
918
+ });
919
+ const announceThread = (threadId) => {
920
+ if (activeThreadId != null) return;
921
+ activeThreadId = threadId;
922
+ emitStreamEvent({ type: "thread.started", thread_id: threadId });
923
+ };
924
+ const handleItem = ({
925
+ eventType,
926
+ params
927
+ }) => {
928
+ if (!matchesActiveTurn({ params, activeThreadId, activeTurnId })) return;
929
+ const item = asRecord(params.item);
930
+ if (item == null || typeof item.type !== "string") return;
931
+ if (eventType === "item.completed" && typeof item.id === "string") {
932
+ const call = nativeToolCalls.get(item.id);
933
+ if (call?.name === "apply_patch" && item.type === "fileChange") {
934
+ call.successConfirmed = item.status === "completed";
935
+ } else if (call?.name === "view_image" && item.type === "imageView") {
936
+ call.successConfirmed = true;
937
+ }
938
+ }
939
+ const normalized = normalizeItem({ item, textByItem, reasoningByItem });
940
+ if (normalized == null) return;
941
+ if (normalized.type === "dynamic_tool_call") {
942
+ stepTracker.observeEvent({
943
+ event: { type: eventType, item: normalized },
944
+ itemId: normalized.id
945
+ });
946
+ return;
947
+ }
948
+ emitStreamEvent({ type: eventType, item: normalized });
949
+ };
950
+ const emitNativeToolResult = ({
951
+ callId,
952
+ result
953
+ }) => {
954
+ const call = nativeToolCalls.get(callId);
955
+ if (call == null || call.resultEmitted) return;
956
+ call.resultEmitted = true;
957
+ emitStreamEvent({
958
+ type: "item.completed",
959
+ item: {
960
+ type: "native_tool",
961
+ id: callId,
962
+ tool: call.name,
963
+ result,
964
+ ...!call.successConfirmed ? { isError: true } : {}
965
+ }
966
+ });
967
+ };
968
+ const handleRawItem = (params) => {
969
+ if (!matchesActiveTurn({ params, activeThreadId, activeTurnId })) return;
970
+ const item = asRecord(params.item);
971
+ if (item == null) return;
972
+ const toolCall = normalizeNativeToolCall({ item });
973
+ if (toolCall != null) {
974
+ if (nativeToolCalls.has(toolCall.callId)) return;
975
+ nativeToolCalls.set(toolCall.callId, {
976
+ name: toolCall.name,
977
+ resultEmitted: false,
978
+ successConfirmed: false
979
+ });
980
+ emitStreamEvent({
981
+ type: "item.started",
982
+ item: {
983
+ type: "native_tool",
984
+ id: toolCall.callId,
985
+ tool: toolCall.name,
986
+ input: toolCall.input
987
+ }
988
+ });
989
+ return;
990
+ }
991
+ if (typeof item.call_id !== "string") return;
992
+ const call = nativeToolCalls.get(item.call_id);
993
+ if (call == null || (call.name === "apply_patch" ? item.type !== "custom_tool_call_output" : item.type !== "function_call_output") || !Object.prototype.hasOwnProperty.call(item, "output")) {
994
+ return;
995
+ }
996
+ emitNativeToolResult({
997
+ callId: item.call_id,
998
+ result: call.name === "view_image" && call.successConfirmed && Array.isArray(item.output) && item.output.length === 0 ? "Image viewed." : item.output
999
+ });
1000
+ };
1001
+ return {
1002
+ announceThread,
1003
+ setTurnId(turnId) {
1004
+ activeTurnId = turnId;
1005
+ },
1006
+ handle(notification) {
1007
+ const params = asRecord(notification.params);
1008
+ if (notification.method === "thread/started") {
1009
+ const thread = asRecord(params?.thread);
1010
+ if (typeof thread?.id === "string") announceThread(thread.id);
1011
+ return;
1012
+ }
1013
+ if (notification.method === "turn/started") {
1014
+ const turn = asRecord(params?.turn);
1015
+ if (params?.threadId === activeThreadId && typeof turn?.id === "string") {
1016
+ activeTurnId = turn.id;
1017
+ }
1018
+ return;
1019
+ }
1020
+ if (notification.method === "item/started" && params != null) {
1021
+ handleItem({ eventType: "item.started", params });
1022
+ return;
1023
+ }
1024
+ if (notification.method === "item/completed" && params != null) {
1025
+ handleItem({ eventType: "item.completed", params });
1026
+ return;
1027
+ }
1028
+ if (notification.method === "rawResponseItem/completed" && params != null) {
1029
+ handleRawItem(params);
1030
+ return;
1031
+ }
1032
+ if (notification.method === "item/agentMessage/delta" && params != null && matchesActiveTurn({ params, activeThreadId, activeTurnId }) && typeof params.itemId === "string" && typeof params.delta === "string") {
1033
+ const text = (textByItem.get(params.itemId) ?? "") + params.delta;
1034
+ textByItem.set(params.itemId, text);
1035
+ emitStreamEvent({
1036
+ type: "item.updated",
1037
+ item: { type: "agent_message", id: params.itemId, text }
1038
+ });
1039
+ return;
1040
+ }
1041
+ if (notification.method === "item/reasoning/summaryTextDelta" && params != null && matchesActiveTurn({ params, activeThreadId, activeTurnId }) && typeof params.itemId === "string" && typeof params.delta === "string") {
1042
+ const text = (reasoningByItem.get(params.itemId) ?? "") + params.delta;
1043
+ reasoningByItem.set(params.itemId, text);
1044
+ emitStreamEvent({
1045
+ type: "item.updated",
1046
+ item: { type: "reasoning", id: params.itemId, text }
1047
+ });
1048
+ return;
1049
+ }
1050
+ if (notification.method === "thread/tokenUsage/updated" && params != null && matchesActiveTurn({ params, activeThreadId, activeTurnId })) {
1051
+ const tokenUsage = asRecord(params.tokenUsage);
1052
+ const total = readUsageBreakdown(tokenUsage?.total);
1053
+ const last = readUsageBreakdown(tokenUsage?.last);
1054
+ if (total != null && last != null) {
1055
+ const key = usageKey(total);
1056
+ if (key !== lastCumulativeUsageKey) {
1057
+ lastCumulativeUsageKey = key;
1058
+ accumulatedUsage = addUsage({
1059
+ total: accumulatedUsage,
1060
+ increment: last
1061
+ });
1062
+ }
1063
+ }
1064
+ return;
1065
+ }
1066
+ if (notification.method === "error" && params != null) {
1067
+ if (!matchesActiveTurn({ params, activeThreadId, activeTurnId }))
1068
+ return;
1069
+ const error = asRecord(params.error);
1070
+ const message = typeof error?.message === "string" ? error.message : "Codex app-server reported an error.";
1071
+ if (params.willRetry === true) {
1072
+ emitWarning({ message });
1073
+ } else {
1074
+ emitError({ error: message, message: "codex turn failed" });
1075
+ }
1076
+ return;
1077
+ }
1078
+ if ((notification.method === "warning" || notification.method === "configWarning" || notification.method === "deprecationNotice") && typeof params?.message === "string") {
1079
+ emitWarning({ message: params.message });
1080
+ return;
1081
+ }
1082
+ if (notification.method === "turn/completed" && params != null && matchesActiveTurn({ params, activeThreadId, activeTurnId })) {
1083
+ if (settled) return;
1084
+ settled = true;
1085
+ const turn = asRecord(params.turn);
1086
+ const status = typeof turn?.status === "string" ? turn.status : "failed";
1087
+ const turnError = asRecord(turn?.error);
1088
+ if (status === "completed") {
1089
+ for (const [callId, call] of nativeToolCalls) {
1090
+ if (call.resultEmitted) continue;
1091
+ emitNativeToolResult({
1092
+ callId,
1093
+ result: call.successConfirmed ? call.name === "apply_patch" ? "Patch applied." : "Image viewed." : "Codex did not report the tool result."
1094
+ });
1095
+ }
1096
+ }
1097
+ emitStreamEvent({
1098
+ type: "turn.completed",
1099
+ usage: toLegacyUsage(accumulatedUsage)
1100
+ });
1101
+ resolveCompletion({
1102
+ status,
1103
+ ...typeof turnError?.message === "string" ? { error: turnError.message } : {}
1104
+ });
1105
+ }
1106
+ },
1107
+ waitForCompletion: () => completion
1108
+ };
1109
+ }
1110
+ function normalizeNativeToolCall({
1111
+ item
1112
+ }) {
1113
+ if (item.namespace != null || typeof item.call_id !== "string" || item.call_id.length === 0) {
1114
+ return void 0;
1115
+ }
1116
+ if (item.type === "custom_tool_call" && item.name === "apply_patch" && typeof item.input === "string") {
1117
+ return {
1118
+ callId: item.call_id,
1119
+ name: "apply_patch",
1120
+ input: JSON.stringify(item.input)
1121
+ };
1122
+ }
1123
+ if (item.type === "function_call" && item.name === "view_image" && typeof item.arguments === "string") {
1124
+ return {
1125
+ callId: item.call_id,
1126
+ name: "view_image",
1127
+ input: item.arguments
1128
+ };
1129
+ }
1130
+ return void 0;
686
1131
  }
687
- function parseToolRelayCommands({
688
- command,
689
- cliShimPath
1132
+ function normalizeItem({
1133
+ item,
1134
+ textByItem,
1135
+ reasoningByItem
690
1136
  }) {
691
- return parseToolRelayCommandsInternal({ command, cliShimPath, depth: 0 });
1137
+ const id = typeof item.id === "string" ? item.id : void 0;
1138
+ if (item.type === "agentMessage") {
1139
+ const text = typeof item.text === "string" ? item.text : "";
1140
+ if (id != null) textByItem.set(id, text);
1141
+ return { type: "agent_message", id, text };
1142
+ }
1143
+ if (item.type === "reasoning") {
1144
+ const summary = stringArray(item.summary).join("\n\n");
1145
+ const content = stringArray(item.content).join("\n\n");
1146
+ const text = summary || reasoningByItem.get(id ?? "") || content;
1147
+ if (id != null) reasoningByItem.set(id, text);
1148
+ return { type: "reasoning", id, text };
1149
+ }
1150
+ if (item.type === "commandExecution") {
1151
+ return {
1152
+ type: "command_execution",
1153
+ id,
1154
+ command: typeof item.command === "string" ? item.command : "",
1155
+ exit_code: typeof item.exitCode === "number" ? item.exitCode : void 0,
1156
+ aggregated_output: typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : void 0,
1157
+ status: normalizeCommandStatus(item.status)
1158
+ };
1159
+ }
1160
+ if (item.type === "mcpToolCall") {
1161
+ const result = asRecord(item.result);
1162
+ return {
1163
+ type: "mcp_tool_call",
1164
+ id,
1165
+ server: typeof item.server === "string" ? item.server : void 0,
1166
+ tool: typeof item.tool === "string" ? item.tool : void 0,
1167
+ arguments: item.arguments,
1168
+ result: result == null ? item.result : {
1169
+ content: result.content,
1170
+ structured_content: result.structuredContent
1171
+ },
1172
+ error: asRecord(item.error)
1173
+ };
1174
+ }
1175
+ if (item.type === "dynamicToolCall") {
1176
+ return { type: "dynamic_tool_call", id };
1177
+ }
1178
+ if (item.type === "webSearch") {
1179
+ return {
1180
+ type: "web_search",
1181
+ id,
1182
+ query: typeof item.query === "string" ? item.query : void 0,
1183
+ action: asRecord(item.action) ?? void 0,
1184
+ result: item.results
1185
+ };
1186
+ }
1187
+ if (item.type === "fileChange") {
1188
+ return {
1189
+ type: "file_change",
1190
+ id,
1191
+ changes: Array.isArray(item.changes) ? item.changes.flatMap((change) => {
1192
+ const value = asRecord(change);
1193
+ const kind = asRecord(value?.kind)?.type;
1194
+ return typeof value?.path === "string" && (kind === "add" || kind === "delete" || kind === "update") ? [{ path: value.path, kind }] : [];
1195
+ }) : []
1196
+ };
1197
+ }
1198
+ if (item.type === "plan") return { type: "todo_list", id };
1199
+ return void 0;
1200
+ }
1201
+ function normalizeCommandStatus(value) {
1202
+ if (value === "completed") return "completed";
1203
+ if (value === "inProgress") return "in_progress";
1204
+ return "failed";
692
1205
  }
693
- function parseToolRelayCommandsInternal({
694
- command,
695
- cliShimPath,
696
- depth
1206
+ function matchesActiveTurn({
1207
+ params,
1208
+ activeThreadId,
1209
+ activeTurnId
697
1210
  }) {
698
- const commands = splitShellAndCommands(command);
699
- if (!commands) return void 0;
700
- if (commands.length > 1) {
701
- const relayCalls = [];
702
- for (const nestedCommand of commands) {
703
- const nestedCalls = parseToolRelayCommandsInternal({
704
- command: nestedCommand,
705
- cliShimPath,
706
- depth
707
- });
708
- if (!nestedCalls) return void 0;
709
- relayCalls.push(...nestedCalls);
1211
+ const nestedTurn = asRecord(params.turn);
1212
+ const turnId = typeof params.turnId === "string" ? params.turnId : typeof nestedTurn?.id === "string" ? nestedTurn.id : void 0;
1213
+ return activeThreadId != null && activeTurnId != null && params.threadId === activeThreadId && turnId === activeTurnId;
1214
+ }
1215
+ function asRecord(value) {
1216
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1217
+ }
1218
+ function stringArray(value) {
1219
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
1220
+ }
1221
+ function emptyUsageBreakdown() {
1222
+ return {
1223
+ inputTokens: 0,
1224
+ cachedInputTokens: 0,
1225
+ cacheWriteInputTokens: 0,
1226
+ outputTokens: 0
1227
+ };
1228
+ }
1229
+ function readUsageBreakdown(value) {
1230
+ const usage = asRecord(value);
1231
+ if (usage == null) return void 0;
1232
+ return {
1233
+ inputTokens: numberOrZero(usage.inputTokens),
1234
+ cachedInputTokens: numberOrZero(usage.cachedInputTokens),
1235
+ cacheWriteInputTokens: numberOrZero(usage.cacheWriteInputTokens),
1236
+ outputTokens: numberOrZero(usage.outputTokens)
1237
+ };
1238
+ }
1239
+ function addUsage({
1240
+ total,
1241
+ increment
1242
+ }) {
1243
+ return {
1244
+ inputTokens: total.inputTokens + increment.inputTokens,
1245
+ cachedInputTokens: total.cachedInputTokens + increment.cachedInputTokens,
1246
+ cacheWriteInputTokens: total.cacheWriteInputTokens + increment.cacheWriteInputTokens,
1247
+ outputTokens: total.outputTokens + increment.outputTokens
1248
+ };
1249
+ }
1250
+ function usageKey(usage) {
1251
+ return [
1252
+ usage.inputTokens,
1253
+ usage.cachedInputTokens,
1254
+ usage.cacheWriteInputTokens,
1255
+ usage.outputTokens
1256
+ ].join(":");
1257
+ }
1258
+ function toLegacyUsage(usage) {
1259
+ return {
1260
+ input_tokens: usage.inputTokens,
1261
+ cached_input_tokens: usage.cachedInputTokens,
1262
+ cache_write_input_tokens: usage.cacheWriteInputTokens,
1263
+ output_tokens: usage.outputTokens
1264
+ };
1265
+ }
1266
+ function numberOrZero(value) {
1267
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
1268
+ }
1269
+
1270
+ // src/bridge/codex-app-server-driver.ts
1271
+ import { isDeepStrictEqual } from "util";
1272
+
1273
+ // src/bridge/codex-tool-filtering-hook.ts
1274
+ import { createHash } from "crypto";
1275
+ import path from "path";
1276
+ var MATCHER = "^apply_patch$";
1277
+ var TIMEOUT_SECONDS = 10;
1278
+ var COMMAND = `printf '%s\\n' "Tool 'apply_patch' is inactive due to the HarnessAgent tool filtering policy." >&2; exit 2`;
1279
+ function createTrustedApplyPatchHook({
1280
+ codexConfig
1281
+ }) {
1282
+ const threadConfig = { ...codexConfig };
1283
+ const hooks = { ...asRecord2(threadConfig.hooks) };
1284
+ delete threadConfig.hooks;
1285
+ for (const [key2, value] of Object.entries(threadConfig)) {
1286
+ if (!key2.startsWith("hooks.")) continue;
1287
+ const name = key2.slice("hooks.".length);
1288
+ if (name.startsWith("state.")) {
1289
+ hooks.state = {
1290
+ ...asRecord2(hooks.state),
1291
+ [name.slice("state.".length)]: value
1292
+ };
1293
+ } else {
1294
+ hooks[name] = value;
710
1295
  }
711
- return relayCalls;
1296
+ delete threadConfig[key2];
712
1297
  }
713
- const argv2 = parseShellWords(command);
714
- if (!argv2) return void 0;
715
- const relayCall = parseDirectToolRelayArgv({ argv: argv2, cliShimPath });
716
- if (relayCall) return [relayCall];
717
- const innerCommand = extractShellEvalCommand(argv2);
718
- if (!innerCommand || depth >= 2) return void 0;
719
- return parseToolRelayCommandsInternal({
720
- command: innerCommand,
721
- cliShimPath,
722
- depth: depth + 1
723
- });
1298
+ const userGroups = hooks.PreToolUse ?? [];
1299
+ if (!Array.isArray(userGroups)) {
1300
+ throw new Error("Codex config hooks.PreToolUse must be an array.");
1301
+ }
1302
+ const handler = {
1303
+ type: "command",
1304
+ command: COMMAND,
1305
+ timeout: TIMEOUT_SECONDS,
1306
+ async: false
1307
+ };
1308
+ const hash = `sha256:${createHash("sha256").update(
1309
+ JSON.stringify(
1310
+ canonicalize({
1311
+ event_name: "pre_tool_use",
1312
+ matcher: MATCHER,
1313
+ hooks: [handler]
1314
+ })
1315
+ )
1316
+ ).digest("hex")}`;
1317
+ const sourcePath = process.platform === "win32" ? path.win32.resolve("C:\\", "<session-flags>/config.toml") : path.posix.resolve("/", "<session-flags>/config.toml");
1318
+ const key = `${sourcePath}:pre_tool_use:${userGroups.length}:0`;
1319
+ hooks.PreToolUse = [...userGroups, { matcher: MATCHER, hooks: [handler] }];
1320
+ hooks.state = {
1321
+ ...asRecord2(hooks.state),
1322
+ [key]: { enabled: true, trusted_hash: hash }
1323
+ };
1324
+ return {
1325
+ cliOverrides: [`hooks=${toTomlInline(hooks)}`, "features.hooks=true"],
1326
+ threadConfig,
1327
+ key,
1328
+ hash,
1329
+ command: COMMAND
1330
+ };
724
1331
  }
725
- function parseDirectToolRelayArgv({
726
- argv: argv2,
727
- cliShimPath
1332
+ function assertTrustedApplyPatchHook({
1333
+ response,
1334
+ hook
728
1335
  }) {
729
- if (argv2.length < 3 || argv2.length > 4) return void 0;
730
- if (argv2[0] !== "node" || argv2[1] !== cliShimPath) return void 0;
731
- const toolName = argv2[2];
732
- if (!toolName) return void 0;
733
- try {
734
- return { toolName, input: JSON.parse(argv2[3] ?? "{}") };
735
- } catch {
736
- return void 0;
1336
+ const data = asRecord2(response)?.data;
1337
+ const entry = Array.isArray(data) && data.length === 1 ? data[0] : null;
1338
+ const record = asRecord2(entry);
1339
+ const matches = Array.isArray(record?.hooks) ? record.hooks.filter((item) => asRecord2(item)?.key === hook.key) : [];
1340
+ const match = matches.length === 1 ? asRecord2(matches[0]) : void 0;
1341
+ if (!match || match.eventName !== "preToolUse" || match.handlerType !== "command" || match.command !== hook.command || match.matcher !== MATCHER || match.currentHash !== hook.hash || match.enabled !== true || match.trustStatus !== "trusted" || Array.isArray(record?.errors) && record.errors.length > 0) {
1342
+ throw new Error(
1343
+ "Codex app-server did not load the trusted apply_patch filtering hook."
1344
+ );
737
1345
  }
738
1346
  }
739
- function extractShellEvalCommand(argv2) {
740
- if (argv2.length !== 3) return void 0;
741
- const shellName = argv2[0].split("/").at(-1);
742
- if (shellName !== "bash" && shellName !== "sh" && shellName !== "zsh") {
743
- return void 0;
1347
+ function canonicalize(value) {
1348
+ if (Array.isArray(value)) return value.map(canonicalize);
1349
+ if (value != null && typeof value === "object") {
1350
+ return Object.fromEntries(
1351
+ Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonicalize(item)])
1352
+ );
744
1353
  }
745
- if (argv2[1] !== "-c" && argv2[1] !== "-lc") return void 0;
746
- return argv2[2];
1354
+ return value;
747
1355
  }
748
- function splitShellAndCommands(command) {
749
- const commands = [];
750
- let start = 0;
751
- let quote;
752
- for (let i = 0; i < command.length; i++) {
753
- const char = command[i];
754
- if (quote === "'") {
755
- if (char === "'") quote = void 0;
756
- continue;
757
- }
758
- if (quote === '"') {
759
- if (char === '"') {
760
- quote = void 0;
761
- } else if (char === "\\") {
762
- i++;
763
- }
764
- continue;
765
- }
766
- if (char === '"' || char === "'") {
767
- quote = char;
768
- continue;
769
- }
770
- if (char === "\\") {
771
- i++;
772
- continue;
773
- }
774
- if (char === "&" && command[i + 1] === "&") {
775
- const nestedCommand2 = command.slice(start, i).trim();
776
- if (!nestedCommand2) return void 0;
777
- commands.push(nestedCommand2);
778
- start = i + 2;
779
- i++;
780
- }
1356
+ function toTomlInline(value) {
1357
+ if (typeof value === "string") return JSON.stringify(value);
1358
+ if (typeof value === "boolean") return String(value);
1359
+ if (typeof value === "number" && Number.isFinite(value)) {
1360
+ return String(value);
1361
+ }
1362
+ if (Array.isArray(value)) {
1363
+ return `[${value.map(toTomlInline).join(",")}]`;
1364
+ }
1365
+ if (value != null && typeof value === "object") {
1366
+ return `{${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${JSON.stringify(key)}=${toTomlInline(item)}`).join(",")}}`;
781
1367
  }
782
- if (quote) return void 0;
783
- const nestedCommand = command.slice(start).trim();
784
- if (!nestedCommand) return void 0;
785
- commands.push(nestedCommand);
786
- return commands;
1368
+ throw new Error("Codex hook configuration contains an unsupported value.");
787
1369
  }
788
- function parseShellWords(command) {
789
- const words = [];
790
- let current = "";
791
- let quote;
792
- let hasCurrent = false;
793
- const pushCurrent = () => {
794
- if (!hasCurrent) return;
795
- words.push(current);
796
- current = "";
797
- hasCurrent = false;
1370
+ function asRecord2(value) {
1371
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1372
+ }
1373
+
1374
+ // src/bridge/codex-app-server-driver.ts
1375
+ var CODEX_CLI_PATH = fileURLToPath(
1376
+ new URL("./node_modules/@openai/codex/bin/codex.js", import.meta.url)
1377
+ );
1378
+ function createCodexAppServerRuntime() {
1379
+ let client;
1380
+ let loadedThreadId;
1381
+ let loadedConfig;
1382
+ let activeTurn;
1383
+ const close = async () => {
1384
+ const previous = client;
1385
+ client = void 0;
1386
+ loadedThreadId = void 0;
1387
+ loadedConfig = void 0;
1388
+ await previous?.close();
798
1389
  };
799
- for (let i = 0; i < command.length; i++) {
800
- const char = command[i];
801
- if (quote === "'") {
802
- if (char === "'") {
803
- quote = void 0;
804
- } else {
805
- current += char;
806
- }
807
- hasCurrent = true;
808
- continue;
1390
+ const runTurn2 = async (options) => {
1391
+ const { start, turn, emit, workdir: workdir2, threadId, codexModel, codexConfig } = options;
1392
+ if (activeTurn != null) throw new Error("A Codex turn is already active.");
1393
+ const toolPolicy = resolveCodexBuiltinToolPolicy({
1394
+ builtinToolFiltering: start.builtinToolFiltering,
1395
+ webSearch: start.webSearch
1396
+ });
1397
+ const trustedHook = toolPolicy.denyApplyPatchWithHook ? createTrustedApplyPatchHook({ codexConfig }) : void 0;
1398
+ const nextConfig = {
1399
+ ...trustedHook?.threadConfig ?? codexConfig,
1400
+ web_search: toolPolicy.webSearchMode,
1401
+ ...toolPolicy.disabled.bash || toolPolicy.disabled.view_image ? {
1402
+ features: {
1403
+ ...asRecord3(codexConfig.features),
1404
+ ...toolPolicy.disabled.bash ? { shell_tool: false } : {},
1405
+ ...toolPolicy.disabled.view_image ? { view_image: false } : {}
1406
+ }
1407
+ } : {}
1408
+ };
1409
+ const processConfig = {
1410
+ thread: nextConfig,
1411
+ cliOverrides: trustedHook?.cliOverrides ?? []
1412
+ };
1413
+ if (client != null && !isDeepStrictEqual(loadedConfig, processConfig)) {
1414
+ await close();
809
1415
  }
810
- if (quote === '"') {
811
- if (char === '"') {
812
- quote = void 0;
813
- } else if (char === "\\" && i + 1 < command.length) {
814
- current += command[++i];
815
- } else {
816
- current += char;
817
- }
818
- hasCurrent = true;
819
- continue;
1416
+ const handler = createAppServerEventHandler({
1417
+ stepTracker: options.stepTracker,
1418
+ emitStreamEvent: options.emitStreamEvent,
1419
+ emitWarning: turn.emitWarning,
1420
+ emitError: turn.emitError
1421
+ });
1422
+ const dynamicTools = createDynamicTools(start.tools ?? []);
1423
+ let rejectProtocolFailure = () => {
1424
+ };
1425
+ const protocolFailure = new Promise((_, reject) => {
1426
+ rejectProtocolFailure = reject;
1427
+ });
1428
+ const currentTurn = {
1429
+ threadId,
1430
+ turnId: void 0,
1431
+ handler,
1432
+ dynamicTools,
1433
+ options,
1434
+ fail: rejectProtocolFailure
1435
+ };
1436
+ activeTurn = currentTurn;
1437
+ let initialized = false;
1438
+ if (client == null) {
1439
+ const created = new CodexAppServerClient({
1440
+ executable: process.execPath,
1441
+ args: createCodexAppServerArgs({
1442
+ cliOverrides: trustedHook?.cliOverrides
1443
+ }),
1444
+ cwd: workdir2,
1445
+ env: process.env,
1446
+ onNotification: (notification) => {
1447
+ if (client !== created || activeTurn == null) return;
1448
+ const params = asRecord3(notification.params);
1449
+ if (notification.method === "turn/started" && params?.threadId === activeTurn.threadId && activeTurn.turnId == null) {
1450
+ const turnId = asRecord3(params?.turn)?.id;
1451
+ if (typeof turnId === "string") activeTurn.turnId = turnId;
1452
+ }
1453
+ activeTurn.handler.handle(notification);
1454
+ },
1455
+ onRequest: (request) => {
1456
+ const active = activeTurn;
1457
+ const params = asRecord3(request.params);
1458
+ if (client !== created || active == null || params?.threadId !== active.threadId || params?.turnId !== active.turnId || active.turnId == null) {
1459
+ return Promise.reject(
1460
+ new Error(
1461
+ "Codex app-server requested a tool for an inactive turn."
1462
+ )
1463
+ );
1464
+ }
1465
+ return handleAppServerRequest({
1466
+ request,
1467
+ dynamicTools: active.dynamicTools,
1468
+ emit: active.options.emit,
1469
+ requestToolResult: active.options.turn.requestToolResult
1470
+ }).catch((error) => {
1471
+ active.fail(error);
1472
+ throw error;
1473
+ });
1474
+ },
1475
+ onStderr: (text) => {
1476
+ const message = text.trim();
1477
+ if (message.length > 0) {
1478
+ activeTurn?.options.turn.bridgeLog({
1479
+ level: "debug",
1480
+ subsystem: "codex.app-server.stderr",
1481
+ message
1482
+ });
1483
+ }
1484
+ }
1485
+ });
1486
+ client = created;
1487
+ loadedConfig = processConfig;
1488
+ initialized = true;
820
1489
  }
821
- if (/\s/.test(char)) {
822
- pushCurrent();
823
- continue;
1490
+ const runningClient = client;
1491
+ const clientFailure = runningClient.waitUntilFailure();
1492
+ const exitFailure = runningClient.waitUntilExit().then(({ code, signal }) => {
1493
+ throw new Error(
1494
+ `Codex app-server exited before the turn completed (code ${code ?? "null"}, signal ${signal ?? "null"}).`
1495
+ );
1496
+ });
1497
+ let removeAbortListener = () => {
1498
+ };
1499
+ const abortFailure = new Promise((_, reject) => {
1500
+ const onAbort = () => {
1501
+ if (currentTurn.threadId != null && currentTurn.turnId != null) {
1502
+ void runningClient.request({
1503
+ method: "turn/interrupt",
1504
+ params: {
1505
+ threadId: currentTurn.threadId,
1506
+ turnId: currentTurn.turnId
1507
+ }
1508
+ }).catch(() => {
1509
+ });
1510
+ }
1511
+ reject(
1512
+ turn.abortSignal.reason ?? new DOMException("Aborted", "AbortError")
1513
+ );
1514
+ };
1515
+ if (turn.abortSignal.aborted) onAbort();
1516
+ else {
1517
+ turn.abortSignal.addEventListener("abort", onAbort, { once: true });
1518
+ removeAbortListener = () => turn.abortSignal.removeEventListener("abort", onAbort);
1519
+ }
1520
+ });
1521
+ const raceWithProcess = ({ operation }) => Promise.race([
1522
+ operation,
1523
+ abortFailure,
1524
+ protocolFailure,
1525
+ clientFailure,
1526
+ exitFailure
1527
+ ]);
1528
+ let stopSteering = () => {
1529
+ };
1530
+ const steeringStopped = new Promise((resolve) => {
1531
+ stopSteering = resolve;
1532
+ });
1533
+ const userMessageLoop = async () => {
1534
+ for await (const message of turn.experimental_userMessages) {
1535
+ try {
1536
+ const response = await Promise.race([
1537
+ raceWithProcess({
1538
+ operation: runningClient.request({
1539
+ method: "turn/steer",
1540
+ params: {
1541
+ threadId: currentTurn.threadId,
1542
+ expectedTurnId: currentTurn.turnId,
1543
+ clientUserMessageId: message.messageId,
1544
+ input: [
1545
+ { type: "text", text: message.text, text_elements: [] }
1546
+ ]
1547
+ }
1548
+ })
1549
+ }),
1550
+ steeringStopped.then(() => {
1551
+ throw new Error(
1552
+ "The Codex turn ended before accepting the user message."
1553
+ );
1554
+ })
1555
+ ]);
1556
+ if (asRecord3(response)?.turnId !== currentTurn.turnId) {
1557
+ throw new Error(
1558
+ "Codex app-server turn/steer returned an unexpected turn ID."
1559
+ );
1560
+ }
1561
+ message.accept();
1562
+ } catch (error) {
1563
+ message.reject(error);
1564
+ }
1565
+ }
1566
+ };
1567
+ let keepClient = false;
1568
+ try {
1569
+ if (initialized) {
1570
+ await raceWithProcess({
1571
+ operation: runningClient.initialize({
1572
+ clientName: "ai-sdk-harness-codex",
1573
+ clientVersion: "1"
1574
+ })
1575
+ });
1576
+ }
1577
+ if (start.restartThread && loadedThreadId != null) {
1578
+ await raceWithProcess({
1579
+ operation: runningClient.request({
1580
+ method: "thread/unsubscribe",
1581
+ params: { threadId: loadedThreadId }
1582
+ })
1583
+ });
1584
+ loadedThreadId = void 0;
1585
+ }
1586
+ if (trustedHook != null) {
1587
+ const hooks = await raceWithProcess({
1588
+ operation: runningClient.request({
1589
+ method: "hooks/list",
1590
+ params: {}
1591
+ })
1592
+ });
1593
+ assertTrustedApplyPatchHook({ response: hooks, hook: trustedHook });
1594
+ }
1595
+ const threadMethod = threadId == null ? "thread/start" : "thread/resume";
1596
+ if (loadedThreadId !== threadId || threadId == null) {
1597
+ const threadResponse = await raceWithProcess({
1598
+ operation: runningClient.request({
1599
+ method: threadMethod,
1600
+ params: threadId == null ? {
1601
+ ...createThreadParams({
1602
+ start,
1603
+ workdir: workdir2,
1604
+ codexModel,
1605
+ codexConfig: nextConfig
1606
+ }),
1607
+ dynamicTools: dynamicTools.specs,
1608
+ experimentalRawEvents: true
1609
+ } : {
1610
+ threadId,
1611
+ ...createThreadParams({
1612
+ start,
1613
+ workdir: workdir2,
1614
+ codexModel,
1615
+ codexConfig: nextConfig
1616
+ }),
1617
+ excludeTurns: true
1618
+ }
1619
+ })
1620
+ });
1621
+ assertCodexThreadPermissions({
1622
+ response: threadResponse,
1623
+ method: threadMethod
1624
+ });
1625
+ currentTurn.threadId = readNestedString({
1626
+ value: threadResponse,
1627
+ path: ["thread", "id"],
1628
+ method: threadMethod
1629
+ });
1630
+ loadedThreadId = currentTurn.threadId;
1631
+ }
1632
+ handler.announceThread(currentTurn.threadId);
1633
+ emit({ type: "stream-start" });
1634
+ const turnResponse = await raceWithProcess({
1635
+ operation: runningClient.request({
1636
+ method: "turn/start",
1637
+ params: createTurnParams({
1638
+ threadId: currentTurn.threadId,
1639
+ start,
1640
+ codexModel,
1641
+ disableEnvironments: toolPolicy.disableEnvironments
1642
+ })
1643
+ })
1644
+ });
1645
+ currentTurn.turnId = readNestedString({
1646
+ value: turnResponse,
1647
+ path: ["turn", "id"],
1648
+ method: "turn/start"
1649
+ });
1650
+ handler.setTurnId(currentTurn.turnId);
1651
+ void userMessageLoop();
1652
+ const result = await raceWithProcess({
1653
+ operation: handler.waitForCompletion()
1654
+ });
1655
+ keepClient = true;
1656
+ assertSuccessfulTurn(result);
1657
+ } catch (error) {
1658
+ if (turn.abortSignal.aborted && currentTurn.turnId != null) {
1659
+ let timer;
1660
+ try {
1661
+ keepClient = await Promise.race([
1662
+ handler.waitForCompletion().then(() => true),
1663
+ new Promise((resolve) => {
1664
+ timer = setTimeout(() => resolve(false), 5e3);
1665
+ }),
1666
+ clientFailure.then(
1667
+ () => false,
1668
+ () => false
1669
+ )
1670
+ ]);
1671
+ } finally {
1672
+ clearTimeout(timer);
1673
+ }
1674
+ }
1675
+ throw error;
1676
+ } finally {
1677
+ turn.experimental_userMessages.close();
1678
+ stopSteering();
1679
+ removeAbortListener();
1680
+ if (activeTurn === currentTurn) activeTurn = void 0;
1681
+ if (!keepClient) await close();
824
1682
  }
825
- if (char === '"' || char === "'") {
826
- quote = char;
827
- hasCurrent = true;
828
- continue;
1683
+ };
1684
+ return { runTurn: runTurn2, close };
1685
+ }
1686
+ function createCodexAppServerArgs({
1687
+ cliOverrides = []
1688
+ } = {}) {
1689
+ return [
1690
+ CODEX_CLI_PATH,
1691
+ "--config",
1692
+ 'sandbox_mode="danger-full-access"',
1693
+ "--config",
1694
+ 'approval_policy="never"',
1695
+ ...cliOverrides.flatMap((override) => ["--config", override]),
1696
+ "app-server",
1697
+ "--stdio"
1698
+ ];
1699
+ }
1700
+ function createThreadParams({
1701
+ start,
1702
+ workdir: workdir2,
1703
+ codexModel,
1704
+ codexConfig
1705
+ }) {
1706
+ return {
1707
+ ...codexModel == null ? {} : { model: codexModel },
1708
+ cwd: workdir2,
1709
+ approvalPolicy: "never",
1710
+ sandbox: "danger-full-access",
1711
+ config: {
1712
+ ...codexConfig,
1713
+ web_search: codexConfig.web_search ?? (start.webSearch ? "live" : "disabled")
829
1714
  }
830
- if (char === "\\" && i + 1 < command.length) {
831
- current += command[++i];
832
- hasCurrent = true;
833
- continue;
1715
+ };
1716
+ }
1717
+ function createTurnParams({
1718
+ threadId,
1719
+ start,
1720
+ codexModel,
1721
+ disableEnvironments = false
1722
+ }) {
1723
+ return {
1724
+ threadId,
1725
+ ...disableEnvironments ? { environments: [] } : {},
1726
+ input: [{ type: "text", text: start.prompt, text_elements: [] }],
1727
+ approvalPolicy: "never",
1728
+ sandboxPolicy: {
1729
+ type: "externalSandbox",
1730
+ networkAccess: "enabled"
1731
+ },
1732
+ ...codexModel == null ? {} : { model: codexModel },
1733
+ ...start.reasoningEffort == null ? {} : { effort: start.reasoningEffort },
1734
+ ...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? { outputSchema: start.responseFormat.schema } : {}
1735
+ };
1736
+ }
1737
+ function assertCodexThreadPermissions({
1738
+ response,
1739
+ method
1740
+ }) {
1741
+ const value = asRecord3(response);
1742
+ const sandbox = asRecord3(value?.sandbox);
1743
+ if (value?.approvalPolicy !== "never" || sandbox?.type !== "dangerFullAccess") {
1744
+ throw new Error(
1745
+ `Codex app-server ${method} did not disable approvals and its platform sandbox.`
1746
+ );
1747
+ }
1748
+ }
1749
+ function createDynamicTools(tools) {
1750
+ const validOriginalNames = new Set(
1751
+ tools.map((tool) => tool.name).filter(isValidDynamicToolName)
1752
+ );
1753
+ const usedAliases = /* @__PURE__ */ new Set();
1754
+ const originalNameByAlias = /* @__PURE__ */ new Map();
1755
+ const specs = tools.map((tool, index) => {
1756
+ let alias = tool.name;
1757
+ if (!isValidDynamicToolName(alias) || usedAliases.has(alias)) {
1758
+ const digest = createHash2("sha256").update(tool.name).digest("hex").slice(0, 16);
1759
+ const base = `ai_sdk_tool_${digest}`;
1760
+ alias = base;
1761
+ let suffix = index;
1762
+ while (usedAliases.has(alias) || validOriginalNames.has(alias)) {
1763
+ alias = `${base}_${suffix++}`;
1764
+ }
834
1765
  }
835
- if (/[;&|<>()`$]/.test(char)) return void 0;
836
- current += char;
837
- hasCurrent = true;
1766
+ usedAliases.add(alias);
1767
+ originalNameByAlias.set(alias, tool.name);
1768
+ return {
1769
+ type: "function",
1770
+ name: alias,
1771
+ description: tool.description ?? "",
1772
+ inputSchema: tool.inputSchema ?? {}
1773
+ };
1774
+ });
1775
+ return { specs, originalNameByAlias };
1776
+ }
1777
+ function isValidDynamicToolName(name) {
1778
+ return name.length > 0 && name.length <= 128 && /^[a-zA-Z0-9_-]+$/.test(name) && name !== "mcp" && !name.startsWith("mcp__");
1779
+ }
1780
+ async function handleAppServerRequest({
1781
+ request,
1782
+ dynamicTools,
1783
+ emit,
1784
+ requestToolResult
1785
+ }) {
1786
+ if (request.method !== "item/tool/call") {
1787
+ throw new Error(
1788
+ `Codex app-server requested unsupported method '${request.method}'.`
1789
+ );
1790
+ }
1791
+ const params = asRecord3(request.params);
1792
+ if (params == null || typeof params.callId !== "string" || typeof params.tool !== "string") {
1793
+ throw new Error("Codex app-server sent an invalid dynamic tool request.");
1794
+ }
1795
+ const originalToolName = dynamicTools.originalNameByAlias.get(params.tool);
1796
+ if (originalToolName == null) {
1797
+ throw new Error(
1798
+ `Codex app-server requested unknown dynamic tool '${params.tool}'.`
1799
+ );
838
1800
  }
839
- if (quote) return void 0;
840
- pushCurrent();
841
- return words;
1801
+ emit({
1802
+ type: "tool-call",
1803
+ toolCallId: params.callId,
1804
+ toolName: originalToolName,
1805
+ input: JSON.stringify(params.arguments ?? {}),
1806
+ providerExecuted: false
1807
+ });
1808
+ const result = await requestToolResult(params.callId);
1809
+ emit({
1810
+ type: "tool-result",
1811
+ toolCallId: params.callId,
1812
+ toolName: originalToolName,
1813
+ result: result.output ?? null,
1814
+ isError: result.isError === true
1815
+ });
1816
+ return {
1817
+ contentItems: [
1818
+ { type: "inputText", text: serializeToolOutput(result.output) }
1819
+ ],
1820
+ success: result.isError !== true
1821
+ };
1822
+ }
1823
+ function serializeToolOutput(output) {
1824
+ if (typeof output === "string") return output;
1825
+ try {
1826
+ return JSON.stringify(output) ?? String(output);
1827
+ } catch {
1828
+ return String(output);
1829
+ }
1830
+ }
1831
+ function readNestedString({
1832
+ value,
1833
+ path: path2,
1834
+ method
1835
+ }) {
1836
+ let current = value;
1837
+ for (const segment of path2) current = asRecord3(current)?.[segment];
1838
+ if (typeof current !== "string" || current.length === 0) {
1839
+ throw new Error(`Codex app-server ${method} returned an invalid response.`);
1840
+ }
1841
+ return current;
1842
+ }
1843
+ function assertSuccessfulTurn(result) {
1844
+ if (result.status === "completed") return;
1845
+ if (result.status === "interrupted") {
1846
+ throw new DOMException("Codex turn was interrupted.", "AbortError");
1847
+ }
1848
+ throw new Error(
1849
+ result.error ?? `Codex turn ended with status '${result.status}'.`
1850
+ );
1851
+ }
1852
+ function asRecord3(value) {
1853
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
842
1854
  }
843
1855
 
844
1856
  // src/bridge/codex-step-tracker.ts
@@ -882,7 +1894,7 @@ function isModelStepItem(item) {
882
1894
  return item.type === "reasoning" || item.type === "agent_message";
883
1895
  }
884
1896
  function isToolStepItem(item) {
885
- return item.type === "command_execution" || item.type === "mcp_tool_call" || item.type === "web_search" || item.type === "file_change" || item.type === "todo_list";
1897
+ return item.type === "command_execution" || item.type === "native_tool" || item.type === "mcp_tool_call" || item.type === "dynamic_tool_call" || item.type === "web_search" || item.type === "file_change" || item.type === "todo_list";
886
1898
  }
887
1899
  function defaultUsage() {
888
1900
  return {
@@ -941,6 +1953,27 @@ function createEmitStreamEvent({
941
1953
  const observeStep = () => {
942
1954
  stepTracker.observeEvent({ event, itemId: id });
943
1955
  };
1956
+ if (item.type === "native_tool" && item.tool != null) {
1957
+ if (event.type === "item.started" && item.input != null) {
1958
+ send({
1959
+ type: "tool-call",
1960
+ toolCallId: id,
1961
+ toolName: item.tool,
1962
+ input: item.input,
1963
+ providerExecuted: true
1964
+ });
1965
+ } else if (event.type === "item.completed") {
1966
+ send({
1967
+ type: "tool-result",
1968
+ toolCallId: id,
1969
+ toolName: item.tool,
1970
+ result: item.result,
1971
+ ...item.isError ? { isError: true } : {}
1972
+ });
1973
+ }
1974
+ stepTracker.observeEvent({ event, itemId: `native-tool:${id}` });
1975
+ return;
1976
+ }
944
1977
  if (item.type === "agent_message" && typeof item.text === "string") {
945
1978
  if (!textByItem.has(id)) {
946
1979
  send({ type: "text-start", id });
@@ -1108,12 +2141,13 @@ function extractMcpToolCallResult(item) {
1108
2141
  function mapUsage(usage) {
1109
2142
  const input = usage.input_tokens ?? 0;
1110
2143
  const cacheRead = usage.cached_input_tokens ?? 0;
2144
+ const cacheWrite = usage.cache_write_input_tokens ?? 0;
1111
2145
  return {
1112
2146
  inputTokens: {
1113
2147
  total: input,
1114
- noCache: Math.max(0, input - cacheRead),
2148
+ noCache: Math.max(0, input - cacheRead - cacheWrite),
1115
2149
  cacheRead,
1116
- cacheWrite: 0
2150
+ cacheWrite
1117
2151
  },
1118
2152
  outputTokens: {
1119
2153
  total: usage.output_tokens ?? 0,
@@ -1122,241 +2156,27 @@ function mapUsage(usage) {
1122
2156
  };
1123
2157
  }
1124
2158
 
1125
- // src/bridge/tool-relay.ts
1126
- import { createServer } from "http";
1127
-
1128
- // src/bridge/tool-relay-auth.ts
1129
- var ToolRelayAuthorizer = class {
1130
- constructor({
1131
- ttlMs = 1e4,
1132
- now = Date.now
1133
- } = {}) {
1134
- this.authorizations = [];
1135
- this.pendingRequests = [];
1136
- this.ttlMs = ttlMs;
1137
- this.now = now;
1138
- }
1139
- authorizeToolCall(call) {
1140
- this.pruneExpired();
1141
- const key = toolRelayCallKey(call);
1142
- const pendingRequestIndex = this.pendingRequests.findIndex(
1143
- (request) => request.key === key
1144
- );
1145
- if (pendingRequestIndex !== -1) {
1146
- const [pendingRequest] = this.pendingRequests.splice(
1147
- pendingRequestIndex,
1148
- 1
1149
- );
1150
- clearTimeout(pendingRequest.timeout);
1151
- pendingRequest.resolve(true);
1152
- return;
1153
- }
1154
- this.authorizations.push({
1155
- key,
1156
- expiresAt: this.now() + this.ttlMs
1157
- });
1158
- }
1159
- waitForToolCallAuthorization(call) {
1160
- this.pruneExpired();
1161
- const key = toolRelayCallKey(call);
1162
- const authorizationIndex = this.authorizations.findIndex(
1163
- (authorization) => authorization.key === key
1164
- );
1165
- if (authorizationIndex !== -1) {
1166
- this.authorizations.splice(authorizationIndex, 1);
1167
- return Promise.resolve(true);
1168
- }
1169
- const expiresAt = this.now() + this.ttlMs;
1170
- return new Promise((resolve) => {
1171
- const pendingRequest = {
1172
- key,
1173
- expiresAt,
1174
- timeout: setTimeout(() => {
1175
- const index = this.pendingRequests.indexOf(pendingRequest);
1176
- if (index !== -1) this.pendingRequests.splice(index, 1);
1177
- resolve(false);
1178
- }, this.ttlMs),
1179
- resolve
1180
- };
1181
- this.pendingRequests.push(pendingRequest);
1182
- });
1183
- }
1184
- close() {
1185
- this.authorizations.length = 0;
1186
- for (const pendingRequest of this.pendingRequests.splice(0)) {
1187
- clearTimeout(pendingRequest.timeout);
1188
- pendingRequest.resolve(false);
1189
- }
1190
- }
1191
- pruneExpired() {
1192
- const now = this.now();
1193
- for (let i = this.authorizations.length - 1; i >= 0; i--) {
1194
- if (this.authorizations[i].expiresAt <= now) {
1195
- this.authorizations.splice(i, 1);
1196
- }
1197
- }
1198
- for (let i = this.pendingRequests.length - 1; i >= 0; i--) {
1199
- const pendingRequest = this.pendingRequests[i];
1200
- if (pendingRequest.expiresAt <= now) {
1201
- this.pendingRequests.splice(i, 1);
1202
- clearTimeout(pendingRequest.timeout);
1203
- pendingRequest.resolve(false);
1204
- }
1205
- }
1206
- }
1207
- };
1208
- var ToolRelayPendingCalls = class {
1209
- constructor() {
1210
- this.calls = /* @__PURE__ */ new Map();
1211
- }
1212
- begin({
1213
- call,
1214
- run
1215
- }) {
1216
- const key = toolRelayCallKey(call);
1217
- const existing = this.calls.get(key);
1218
- if (existing) return { result: existing, isNew: false };
1219
- const result = run();
1220
- this.calls.set(key, result);
1221
- void result.finally(() => {
1222
- if (this.calls.get(key) === result) {
1223
- this.calls.delete(key);
1224
- }
1225
- }).catch(() => {
1226
- });
1227
- return { result, isNew: true };
1228
- }
1229
- };
1230
- function toolRelayCallKey({ toolName, input }) {
1231
- return `${toolName}\0${canonicalJson(input ?? {})}`;
1232
- }
1233
- function canonicalJson(value) {
1234
- return JSON.stringify(normalizeJsonValue(value));
1235
- }
1236
- function normalizeJsonValue(value) {
1237
- if (Array.isArray(value)) {
1238
- return value.map(normalizeJsonValue);
1239
- }
1240
- if (value && typeof value === "object") {
1241
- return Object.fromEntries(
1242
- Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue)])
1243
- );
1244
- }
1245
- return value;
1246
- }
1247
-
1248
- // src/bridge/tool-relay.ts
1249
- async function startAuthorizedToolRelay({
1250
- tools,
1251
- emit,
1252
- requestToolResult,
1253
- authorizer = new ToolRelayAuthorizer()
1254
- }) {
1255
- const toolNames = new Set(tools.map((tool) => tool.name));
1256
- const pendingCalls = new ToolRelayPendingCalls();
1257
- const server = createServer(async (req, res) => {
1258
- try {
1259
- if (req.method !== "POST" || req.url !== "/") {
1260
- res.writeHead(401, { "Content-Type": "application/json" });
1261
- res.end(JSON.stringify({ error: "unauthorized tool relay request" }));
1262
- return;
1263
- }
1264
- const chunks = [];
1265
- for await (const chunk of req) {
1266
- chunks.push(chunk);
1267
- }
1268
- const body = Buffer.concat(chunks).toString("utf8");
1269
- const { requestId, toolName, input } = JSON.parse(body);
1270
- if (!toolNames.has(toolName)) {
1271
- res.writeHead(403, { "Content-Type": "application/json" });
1272
- res.end(
1273
- JSON.stringify({ error: `Tool "${toolName}" is not available` })
1274
- );
1275
- return;
1276
- }
1277
- const relayCall = { toolName, input };
1278
- if (!await authorizer.waitForToolCallAuthorization(relayCall)) {
1279
- res.writeHead(401, { "Content-Type": "application/json" });
1280
- res.end(JSON.stringify({ error: "unauthorized tool relay request" }));
1281
- return;
1282
- }
1283
- const { result } = pendingCalls.begin({
1284
- call: relayCall,
1285
- run: async () => {
1286
- emit({
1287
- type: "tool-call",
1288
- toolCallId: requestId,
1289
- toolName,
1290
- input: JSON.stringify(input ?? {}),
1291
- providerExecuted: false
1292
- });
1293
- const toolResult = await requestToolResult(requestId);
1294
- emit({
1295
- type: "tool-result",
1296
- toolCallId: requestId,
1297
- toolName,
1298
- result: toolResult.output ?? null,
1299
- isError: !!toolResult.isError
1300
- });
1301
- return toolResult;
1302
- }
1303
- });
1304
- const { output } = await result;
1305
- res.writeHead(200, { "Content-Type": "application/json" });
1306
- res.end(JSON.stringify({ result: output }));
1307
- } catch (error) {
1308
- res.writeHead(500, { "Content-Type": "application/json" });
1309
- res.end(
1310
- JSON.stringify({
1311
- error: error instanceof Error ? error.message : String(error)
1312
- })
1313
- );
1314
- }
1315
- });
1316
- await new Promise(
1317
- (resolve) => server.listen(0, "127.0.0.1", () => resolve())
1318
- );
1319
- const address = server.address();
1320
- if (!address || typeof address === "string") {
1321
- throw new Error("tool relay did not expose a numeric port");
1322
- }
1323
- return {
1324
- port: address.port,
1325
- close: () => {
1326
- authorizer.close();
1327
- closeServer(server);
1328
- },
1329
- authorizeToolCall: (call) => authorizer.authorizeToolCall(call)
1330
- };
1331
- }
1332
- function closeServer(server) {
1333
- try {
1334
- server.close();
1335
- } catch {
1336
- }
1337
- }
1338
-
1339
2159
  // src/bridge/index.ts
1340
2160
  import { argv, env as procEnv2, stdout as stdout2 } from "process";
1341
- import * as codexSdkModule from "@openai/codex-sdk";
1342
2161
  var args = parseArgs(argv.slice(2));
1343
2162
  var workdir = requireArg({ value: args.workdir, name: "--workdir" });
1344
2163
  var bridgeStateDir = requireArg({
1345
2164
  value: args.bridgeStateDir,
1346
2165
  name: "--bridge-state-dir"
1347
2166
  });
1348
- var cliShimDir = requireArg({
1349
- value: args.cliShimDir,
1350
- name: "--cli-shim-dir"
1351
- });
1352
2167
  var HARNESS_CLIENT_APP = procEnv2.AI_SDK_HARNESS_CLIENT_APP;
1353
- var codexSdk = codexSdkModule;
1354
2168
  var threadState = { id: void 0 };
2169
+ var appServer = createCodexAppServerRuntime();
1355
2170
  await runBridge({
1356
2171
  bridgeType: "codex",
1357
2172
  bridgeStateDir,
1358
2173
  onStart: runTurn,
1359
- onStop: () => threadState.id ? { threadId: threadState.id } : {}
2174
+ onStop: async () => {
2175
+ const data = threadState.id ? { threadId: threadState.id } : {};
2176
+ await appServer.close();
2177
+ return data;
2178
+ },
2179
+ onDestroy: () => appServer.close()
1360
2180
  });
1361
2181
  async function runTurn(start, turn) {
1362
2182
  const emit = (msg) => turn.emit(msg);
@@ -1365,22 +2185,42 @@ async function runTurn(start, turn) {
1365
2185
  } else if (typeof start.resumeThreadId === "string" && start.resumeThreadId.length > 0) {
1366
2186
  threadState.id = start.resumeThreadId;
1367
2187
  }
1368
- let relay;
1369
- let cliShimPath;
1370
- if (start.tools && start.tools.length > 0) {
1371
- cliShimPath = `${cliShimDir}/${CLI_SHIM_FILENAME}`;
1372
- relay = await startToolRelay({
1373
- tools: start.tools,
2188
+ const runtime = resolveCodexRuntime({ start });
2189
+ let turnUsage = defaultUsage();
2190
+ const stepTracker = createCodexStepTracker({ send: emit });
2191
+ const emitStreamEvent = createEmitStreamEvent({
2192
+ send: emit,
2193
+ stepTracker,
2194
+ setTurnUsage: (usage) => turnUsage = usage,
2195
+ setThreadId: (threadId) => threadState.id = threadId,
2196
+ emitWarning: turn.emitWarning,
2197
+ emitError: turn.emitError
2198
+ });
2199
+ try {
2200
+ await appServer.runTurn({
2201
+ start,
2202
+ turn,
1374
2203
  emit,
1375
- requestToolResult: turn.requestToolResult
2204
+ workdir,
2205
+ threadId: threadState.id,
2206
+ codexModel: runtime.codexModel,
2207
+ codexConfig: runtime.codexConfig,
2208
+ stepTracker,
2209
+ emitStreamEvent
1376
2210
  });
1377
- await mkdir2(cliShimDir, { recursive: true });
1378
- await writeFile2(
1379
- cliShimPath,
1380
- buildCliShimScript({ relayPort: relay.port }),
1381
- "utf8"
1382
- );
2211
+ } catch (error) {
2212
+ if (!turn.abortSignal.aborted) {
2213
+ turn.emitError({ error, message: "codex turn failed" });
2214
+ }
2215
+ return;
1383
2216
  }
2217
+ emit({
2218
+ type: "finish",
2219
+ finishReason: { unified: "stop", raw: "stop" },
2220
+ totalUsage: turnUsage
2221
+ });
2222
+ }
2223
+ function resolveCodexRuntime({ start }) {
1384
2224
  const codexConfig = {
1385
2225
  ...start.codexConfig,
1386
2226
  developer_instructions: [
@@ -1396,7 +2236,7 @@ async function runTurn(start, turn) {
1396
2236
  "AI Gateway auth was selected but AI_GATEWAY_BASE_URL is missing from the Codex bridge environment."
1397
2237
  );
1398
2238
  }
1399
- const apiBaseUrl = hasGatewayAuth ? gatewayBaseUrl : procEnv2.OPENAI_BASE_URL ?? (start.headers != null ? "https://api.openai.com/v1" : void 0);
2239
+ const apiBaseUrl = hasGatewayAuth ? gatewayBaseUrl : procEnv2.OPENAI_BASE_URL ?? (procEnv2.CODEX_API_KEY != null || start.headers != null ? "https://api.openai.com/v1" : void 0);
1400
2240
  const codexModel = start.model && hasGatewayAuth && !start.model.includes("/") ? `openai/${start.model}` : start.model;
1401
2241
  if (hasGatewayAuth && codexModel?.startsWith("openai/")) {
1402
2242
  codexConfig.model_supports_reasoning_summaries = true;
@@ -1426,81 +2266,7 @@ async function runTurn(start, turn) {
1426
2266
  if (start.mcpServers != null) {
1427
2267
  codexConfig.mcp_servers = start.mcpServers;
1428
2268
  }
1429
- const usesConfiguredModelProvider = typeof codexConfig.model_provider === "string";
1430
- const codex = new codexSdk.Codex({
1431
- ...procEnv2.CODEX_API_KEY ? { apiKey: procEnv2.CODEX_API_KEY } : {},
1432
- ...!usesConfiguredModelProvider && apiBaseUrl ? { baseUrl: apiBaseUrl } : {},
1433
- env: Object.fromEntries(
1434
- Object.entries(procEnv2).filter(
1435
- (entry) => typeof entry[1] === "string"
1436
- )
1437
- ),
1438
- ...Object.keys(codexConfig).length > 0 ? { config: codexConfig } : {}
1439
- });
1440
- const threadOptions = {
1441
- ...codexModel ? { model: codexModel } : {},
1442
- sandboxMode: "danger-full-access",
1443
- approvalPolicy: "never",
1444
- workingDirectory: workdir,
1445
- skipGitRepoCheck: true,
1446
- ...start.reasoningEffort ? { modelReasoningEffort: start.reasoningEffort } : {},
1447
- webSearchMode: start.webSearch ? "live" : "disabled"
1448
- };
1449
- const thread = threadState.id ? codex.resumeThread(threadState.id, threadOptions) : codex.startThread(threadOptions);
1450
- emit({ type: "stream-start" });
1451
- const userMessage = start.prompt;
1452
- let turnUsage;
1453
- const stepTracker = createCodexStepTracker({ send: emit });
1454
- const emitStreamEvent = createEmitStreamEvent({
1455
- send: emit,
1456
- stepTracker,
1457
- setTurnUsage: (usage) => turnUsage = usage,
1458
- setThreadId: (threadId) => threadState.id = threadId,
1459
- emitWarning: turn.emitWarning,
1460
- emitError: turn.emitError
1461
- });
1462
- try {
1463
- const { events } = await thread.runStreamed(userMessage, {
1464
- signal: turn.abortSignal,
1465
- ...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? { outputSchema: start.responseFormat.schema } : {}
1466
- });
1467
- for await (const event of events) {
1468
- if (turn.abortSignal.aborted) break;
1469
- if (cliShimPath && event.item?.type === "command_execution") {
1470
- const relayCalls = typeof event.item.command === "string" ? parseToolRelayCommands({
1471
- command: event.item.command,
1472
- cliShimPath
1473
- }) : void 0;
1474
- if (event.type === "item.started" && relay && relayCalls) {
1475
- for (const relayCall of relayCalls) {
1476
- relay.authorizeToolCall(relayCall);
1477
- }
1478
- }
1479
- if (relayCalls) {
1480
- stepTracker.observeEvent({ event, itemId: event.item.id });
1481
- continue;
1482
- }
1483
- }
1484
- emitStreamEvent(event);
1485
- }
1486
- } catch (err) {
1487
- turn.emitError({ error: err, message: "codex turn failed" });
1488
- return;
1489
- } finally {
1490
- relay?.close();
1491
- }
1492
- emit({
1493
- type: "finish",
1494
- finishReason: { unified: "stop", raw: "stop" },
1495
- totalUsage: turnUsage ?? defaultUsage()
1496
- });
1497
- }
1498
- async function startToolRelay({
1499
- tools,
1500
- emit,
1501
- requestToolResult
1502
- }) {
1503
- return startAuthorizedToolRelay({ tools, emit, requestToolResult });
2269
+ return { codexConfig, codexModel };
1504
2270
  }
1505
2271
  function parseArgs(args2) {
1506
2272
  const out = {};
@@ -1509,8 +2275,6 @@ function parseArgs(args2) {
1509
2275
  out.workdir = args2[++i];
1510
2276
  } else if (args2[i] === "--bridge-state-dir" && i + 1 < args2.length) {
1511
2277
  out.bridgeStateDir = args2[++i];
1512
- } else if (args2[i] === "--cli-shim-dir" && i + 1 < args2.length) {
1513
- out.cliShimDir = args2[++i];
1514
2278
  }
1515
2279
  }
1516
2280
  return out;