@ai-sdk/harness-codex 1.0.41 → 1.0.43

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @ai-sdk/harness-codex
2
2
 
3
+ ## 1.0.43
4
+
5
+ ### Patch Changes
6
+
7
+ - 07c977a: chore(harness): refactor bridge code to break out stream event emission from launcher to make it testable
8
+ - 560db3a: fix(harness-codex): fix obsolete default Codex model ID
9
+ - Updated dependencies [a94425b]
10
+ - Updated dependencies [2de0611]
11
+ - @ai-sdk/harness@1.0.41
12
+
13
+ ## 1.0.42
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [59a2306]
18
+ - Updated dependencies [5f65e61]
19
+ - @ai-sdk/harness@1.0.40
20
+
3
21
  ## 1.0.41
4
22
 
5
23
  ### Patch Changes
@@ -53,6 +53,7 @@ async function runBridge(options) {
53
53
  let currentBoundPort = 0;
54
54
  let currentTurnState = "init";
55
55
  let activeSocket;
56
+ let activeSocketReadyForLiveEvents = false;
56
57
  let isFirstTurn = true;
57
58
  let turnAbort;
58
59
  let currentUserMessages;
@@ -152,7 +153,7 @@ async function runBridge(options) {
152
153
  diskBuffer += `${line}
153
154
  `;
154
155
  scheduleEventFlush();
155
- if (activeSocket?.readyState === WS_OPEN) {
156
+ if (activeSocketReadyForLiveEvents && activeSocket?.readyState === WS_OPEN) {
156
157
  try {
157
158
  activeSocket.send(line);
158
159
  } catch {
@@ -255,6 +256,8 @@ async function runBridge(options) {
255
256
  const handleInbound = async (msg, ws) => {
256
257
  switch (msg.type) {
257
258
  case "start": {
259
+ if (activeSocket !== ws) return;
260
+ activeSocketReadyForLiveEvents = true;
258
261
  const firstTurn = isFirstTurn;
259
262
  isFirstTurn = false;
260
263
  eventLog = [];
@@ -357,7 +360,9 @@ async function runBridge(options) {
357
360
  }
358
361
  return;
359
362
  case "resume":
363
+ if (activeSocket !== ws) return;
360
364
  replay(ws, msg.lastSeenEventId);
365
+ activeSocketReadyForLiveEvents = true;
361
366
  return;
362
367
  case "shutdown":
363
368
  currentTurnState = "done";
@@ -422,6 +427,7 @@ async function runBridge(options) {
422
427
  return;
423
428
  }
424
429
  activeSocket = ws;
430
+ activeSocketReadyForLiveEvents = false;
425
431
  sendControl({
426
432
  type: "bridge-hello",
427
433
  state: currentTurnState,
@@ -444,6 +450,7 @@ async function runBridge(options) {
444
450
  ws.on("close", () => {
445
451
  if (activeSocket === ws) {
446
452
  activeSocket = void 0;
453
+ activeSocketReadyForLiveEvents = false;
447
454
  }
448
455
  });
449
456
  ws.on("error", () => {
@@ -478,7 +485,6 @@ function serialiseError(err) {
478
485
  }
479
486
 
480
487
  // src/bridge/index.ts
481
- import { randomUUID } from "crypto";
482
488
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
483
489
 
484
490
  // src/bridge/cli-relay.ts
@@ -724,6 +730,198 @@ function defaultUsage() {
724
730
  };
725
731
  }
726
732
 
733
+ // src/bridge/create-emit-stream-event.ts
734
+ import { randomUUID } from "crypto";
735
+ var NATIVE_TO_COMMON = {
736
+ shell: "bash",
737
+ web_search: "webSearch"
738
+ };
739
+ function toCommonName(nativeName) {
740
+ return NATIVE_TO_COMMON[nativeName] ?? nativeName;
741
+ }
742
+ function createEmitStreamEvent({
743
+ send,
744
+ stepTracker,
745
+ setTurnUsage,
746
+ setThreadId,
747
+ emitWarning,
748
+ emitError
749
+ }) {
750
+ const textByItem = /* @__PURE__ */ new Map();
751
+ const reasoningByItem = /* @__PURE__ */ new Map();
752
+ return (event) => {
753
+ if (event.type === "thread.started" && typeof event.thread_id === "string") {
754
+ setThreadId(event.thread_id);
755
+ send({ type: "bridge-thread", threadId: event.thread_id });
756
+ }
757
+ if (event.type === "turn.completed") {
758
+ if (event.usage) setTurnUsage(mapUsage(event.usage));
759
+ stepTracker.finishStep();
760
+ return;
761
+ }
762
+ if (event.type === "turn.failed") {
763
+ emitError({
764
+ error: event.error?.message ?? "codex turn failed",
765
+ message: "codex turn failed"
766
+ });
767
+ return;
768
+ }
769
+ if (event.type === "error") {
770
+ emitError({
771
+ error: event.message ?? "codex error",
772
+ message: "codex stream error"
773
+ });
774
+ return;
775
+ }
776
+ if (!event.item) return;
777
+ const item = event.item;
778
+ const id = item.id ?? randomUUID();
779
+ const observeStep = () => {
780
+ stepTracker.observeEvent({ event, itemId: id });
781
+ };
782
+ if (item.type === "agent_message" && typeof item.text === "string") {
783
+ if (!textByItem.has(id)) {
784
+ send({ type: "text-start", id });
785
+ textByItem.set(id, "");
786
+ }
787
+ const last = textByItem.get(id) ?? "";
788
+ const next = item.text;
789
+ if (next.length > last.length) {
790
+ send({ type: "text-delta", id, delta: next.slice(last.length) });
791
+ textByItem.set(id, next);
792
+ }
793
+ if (event.type === "item.completed") send({ type: "text-end", id });
794
+ observeStep();
795
+ return;
796
+ }
797
+ if (item.type === "reasoning" && typeof item.text === "string") {
798
+ if (!reasoningByItem.has(id)) {
799
+ send({ type: "reasoning-start", id });
800
+ reasoningByItem.set(id, "");
801
+ }
802
+ const last = reasoningByItem.get(id) ?? "";
803
+ const next = item.text;
804
+ if (next.length > last.length) {
805
+ send({ type: "reasoning-delta", id, delta: next.slice(last.length) });
806
+ reasoningByItem.set(id, next);
807
+ }
808
+ if (event.type === "item.completed") send({ type: "reasoning-end", id });
809
+ observeStep();
810
+ return;
811
+ }
812
+ if (item.type === "command_execution") {
813
+ const nativeName = "shell";
814
+ if (event.type === "item.started") {
815
+ send({
816
+ type: "tool-call",
817
+ toolCallId: id,
818
+ toolName: toCommonName(nativeName),
819
+ nativeName,
820
+ input: JSON.stringify({ command: item.command ?? "" }),
821
+ providerExecuted: true
822
+ });
823
+ } else if (event.type === "item.completed") {
824
+ send({
825
+ type: "tool-result",
826
+ toolCallId: id,
827
+ toolName: toCommonName(nativeName),
828
+ result: {
829
+ exitCode: item.exit_code ?? null,
830
+ output: item.aggregated_output ?? "",
831
+ status: item.status ?? "completed"
832
+ }
833
+ });
834
+ }
835
+ observeStep();
836
+ return;
837
+ }
838
+ if (item.type === "mcp_tool_call") {
839
+ if (event.type === "item.started") {
840
+ send({
841
+ type: "tool-call",
842
+ toolCallId: id,
843
+ toolName: item.tool ?? "unknown",
844
+ nativeName: item.tool ?? "unknown",
845
+ input: JSON.stringify(item.arguments ?? {}),
846
+ providerExecuted: true
847
+ });
848
+ } else if (event.type === "item.completed") {
849
+ send({
850
+ type: "tool-result",
851
+ toolCallId: id,
852
+ toolName: item.tool ?? "unknown",
853
+ result: extractMcpToolCallResult(item)
854
+ });
855
+ }
856
+ observeStep();
857
+ return;
858
+ }
859
+ if (item.type === "web_search") {
860
+ const nativeName = "web_search";
861
+ if (event.type === "item.started") {
862
+ send({
863
+ type: "tool-call",
864
+ toolCallId: id,
865
+ toolName: toCommonName(nativeName),
866
+ nativeName,
867
+ input: JSON.stringify({ query: item.query ?? "" }),
868
+ providerExecuted: true
869
+ });
870
+ } else if (event.type === "item.completed") {
871
+ send({
872
+ type: "tool-result",
873
+ toolCallId: id,
874
+ toolName: toCommonName(nativeName),
875
+ result: item.result ?? null
876
+ });
877
+ }
878
+ observeStep();
879
+ return;
880
+ }
881
+ if (item.type === "file_change" && event.type === "item.completed") {
882
+ for (const change of item.changes ?? []) {
883
+ send({
884
+ type: "file-change",
885
+ event: change.kind === "add" ? "create" : change.kind === "delete" ? "delete" : "modify",
886
+ path: change.path
887
+ });
888
+ }
889
+ observeStep();
890
+ return;
891
+ }
892
+ if (item.type === "error" && event.type === "item.completed") {
893
+ const message = typeof item.message === "string" && item.message.trim() ? item.message : "codex reported a non-fatal error item";
894
+ emitWarning({ message });
895
+ }
896
+ };
897
+ }
898
+ function extractMcpToolCallResult(item) {
899
+ if (item.result === void 0 || item.result === null || typeof item.result !== "object") {
900
+ return item.error?.message ? { error: item.error.message } : null;
901
+ }
902
+ const result = item.result;
903
+ if (result.structured_content !== void 0 && result.structured_content !== null) {
904
+ return result.structured_content;
905
+ }
906
+ return result.content ?? null;
907
+ }
908
+ function mapUsage(usage) {
909
+ const input = usage.input_tokens ?? 0;
910
+ const cacheRead = usage.cached_input_tokens ?? 0;
911
+ return {
912
+ inputTokens: {
913
+ total: input,
914
+ noCache: Math.max(0, input - cacheRead),
915
+ cacheRead,
916
+ cacheWrite: 0
917
+ },
918
+ outputTokens: {
919
+ total: usage.output_tokens ?? 0,
920
+ text: usage.output_tokens ?? 0
921
+ }
922
+ };
923
+ }
924
+
727
925
  // src/bridge/tool-relay.ts
728
926
  import { createServer } from "http";
729
927
 
@@ -941,13 +1139,6 @@ function closeServer(server) {
941
1139
  // src/bridge/index.ts
942
1140
  import { argv, env as procEnv2, stdout as stdout2 } from "process";
943
1141
  import * as codexSdkModule from "@openai/codex-sdk";
944
- var NATIVE_TO_COMMON = {
945
- shell: "bash",
946
- web_search: "webSearch"
947
- };
948
- function toCommonName(nativeName) {
949
- return NATIVE_TO_COMMON[nativeName] ?? nativeName;
950
- }
951
1142
  var args = parseArgs(argv.slice(2));
952
1143
  var workdir = requireArg({ value: args.workdir, name: "--workdir" });
953
1144
  var bridgeStateDir = requireArg({
@@ -1040,19 +1231,21 @@ async function runTurn(start, turn) {
1040
1231
  emit({ type: "stream-start" });
1041
1232
  const userMessage = start.prompt;
1042
1233
  let turnUsage;
1043
- const textByItem = /* @__PURE__ */ new Map();
1044
- const reasoningByItem = /* @__PURE__ */ new Map();
1045
1234
  const stepTracker = createCodexStepTracker({ send: emit });
1235
+ const emitStreamEvent = createEmitStreamEvent({
1236
+ send: emit,
1237
+ stepTracker,
1238
+ setTurnUsage: (usage) => turnUsage = usage,
1239
+ setThreadId: (threadId) => threadState.id = threadId,
1240
+ emitWarning: turn.emitWarning,
1241
+ emitError: turn.emitError
1242
+ });
1046
1243
  try {
1047
1244
  const { events } = await thread.runStreamed(userMessage, {
1048
1245
  signal: turn.abortSignal
1049
1246
  });
1050
1247
  for await (const event of events) {
1051
1248
  if (turn.abortSignal.aborted) break;
1052
- if (event.type === "thread.started" && typeof event.thread_id === "string") {
1053
- threadState.id = event.thread_id;
1054
- emit({ type: "bridge-thread", threadId: event.thread_id });
1055
- }
1056
1249
  if (cliShimPath && event.item?.type === "command_execution") {
1057
1250
  const relayCalls = typeof event.item.command === "string" ? parseToolRelayCommands({
1058
1251
  command: event.item.command,
@@ -1068,15 +1261,7 @@ async function runTurn(start, turn) {
1068
1261
  continue;
1069
1262
  }
1070
1263
  }
1071
- translateAndEmit(event, {
1072
- send: emit,
1073
- textByItem,
1074
- reasoningByItem,
1075
- stepTracker,
1076
- setTurnUsage: (u) => turnUsage = u,
1077
- emitWarning: turn.emitWarning,
1078
- emitError: turn.emitError
1079
- });
1264
+ emitStreamEvent(event);
1080
1265
  }
1081
1266
  } catch (err) {
1082
1267
  turn.emitError({ error: err, message: "codex turn failed" });
@@ -1091,175 +1276,6 @@ async function runTurn(start, turn) {
1091
1276
  });
1092
1277
  void turn.pendingUserMessages;
1093
1278
  }
1094
- function extractMcpToolCallResult(item) {
1095
- if (item.result === void 0 || item.result === null || typeof item.result !== "object") {
1096
- return item.error?.message ? { error: item.error.message } : null;
1097
- }
1098
- const result = item.result;
1099
- if (result.structured_content !== void 0 && result.structured_content !== null) {
1100
- return result.structured_content;
1101
- }
1102
- return result.content ?? null;
1103
- }
1104
- function translateAndEmit(event, ctx) {
1105
- if (event.type === "turn.completed") {
1106
- if (event.usage) ctx.setTurnUsage(mapUsage(event.usage));
1107
- ctx.stepTracker.finishStep();
1108
- return;
1109
- }
1110
- if (event.type === "turn.failed") {
1111
- ctx.emitError({
1112
- error: event.error?.message ?? "codex turn failed",
1113
- message: "codex turn failed"
1114
- });
1115
- return;
1116
- }
1117
- if (event.type === "error") {
1118
- ctx.emitError({
1119
- error: event.message ?? "codex error",
1120
- message: "codex stream error"
1121
- });
1122
- return;
1123
- }
1124
- if (!event.item) return;
1125
- const item = event.item;
1126
- const id = item.id ?? randomUUID();
1127
- const observeStep = () => {
1128
- ctx.stepTracker.observeEvent({ event, itemId: id });
1129
- };
1130
- if (item.type === "agent_message" && typeof item.text === "string") {
1131
- if (!ctx.textByItem.has(id)) {
1132
- ctx.send({ type: "text-start", id });
1133
- ctx.textByItem.set(id, "");
1134
- }
1135
- const last = ctx.textByItem.get(id) ?? "";
1136
- const next = item.text;
1137
- if (next.length > last.length) {
1138
- ctx.send({ type: "text-delta", id, delta: next.slice(last.length) });
1139
- ctx.textByItem.set(id, next);
1140
- }
1141
- if (event.type === "item.completed") ctx.send({ type: "text-end", id });
1142
- observeStep();
1143
- return;
1144
- }
1145
- if (item.type === "reasoning" && typeof item.text === "string") {
1146
- if (!ctx.reasoningByItem.has(id)) {
1147
- ctx.send({ type: "reasoning-start", id });
1148
- ctx.reasoningByItem.set(id, "");
1149
- }
1150
- const last = ctx.reasoningByItem.get(id) ?? "";
1151
- const next = item.text;
1152
- if (next.length > last.length) {
1153
- ctx.send({ type: "reasoning-delta", id, delta: next.slice(last.length) });
1154
- ctx.reasoningByItem.set(id, next);
1155
- }
1156
- if (event.type === "item.completed")
1157
- ctx.send({ type: "reasoning-end", id });
1158
- observeStep();
1159
- return;
1160
- }
1161
- if (item.type === "command_execution") {
1162
- const nativeName = "shell";
1163
- if (event.type === "item.started") {
1164
- ctx.send({
1165
- type: "tool-call",
1166
- toolCallId: id,
1167
- toolName: toCommonName(nativeName),
1168
- nativeName,
1169
- input: JSON.stringify({ command: item.command ?? "" }),
1170
- providerExecuted: true
1171
- });
1172
- } else if (event.type === "item.completed") {
1173
- ctx.send({
1174
- type: "tool-result",
1175
- toolCallId: id,
1176
- toolName: toCommonName(nativeName),
1177
- result: {
1178
- exitCode: item.exit_code ?? null,
1179
- output: item.aggregated_output ?? "",
1180
- status: item.status ?? "completed"
1181
- }
1182
- });
1183
- }
1184
- observeStep();
1185
- return;
1186
- }
1187
- if (item.type === "mcp_tool_call") {
1188
- if (event.type === "item.started") {
1189
- ctx.send({
1190
- type: "tool-call",
1191
- toolCallId: id,
1192
- toolName: item.tool ?? "unknown",
1193
- nativeName: item.tool ?? "unknown",
1194
- input: JSON.stringify(item.arguments ?? {}),
1195
- providerExecuted: true
1196
- });
1197
- } else if (event.type === "item.completed") {
1198
- ctx.send({
1199
- type: "tool-result",
1200
- toolCallId: id,
1201
- toolName: item.tool ?? "unknown",
1202
- result: extractMcpToolCallResult(item)
1203
- });
1204
- }
1205
- observeStep();
1206
- return;
1207
- }
1208
- if (item.type === "web_search") {
1209
- const nativeName = "web_search";
1210
- if (event.type === "item.started") {
1211
- ctx.send({
1212
- type: "tool-call",
1213
- toolCallId: id,
1214
- toolName: toCommonName(nativeName),
1215
- nativeName,
1216
- input: JSON.stringify({ query: item.query ?? "" }),
1217
- providerExecuted: true
1218
- });
1219
- } else if (event.type === "item.completed") {
1220
- ctx.send({
1221
- type: "tool-result",
1222
- toolCallId: id,
1223
- toolName: toCommonName(nativeName),
1224
- result: item.result ?? null
1225
- });
1226
- }
1227
- observeStep();
1228
- return;
1229
- }
1230
- if (item.type === "file_change" && event.type === "item.completed") {
1231
- for (const change of item.changes ?? []) {
1232
- ctx.send({
1233
- type: "file-change",
1234
- event: change.kind === "add" ? "create" : change.kind === "delete" ? "delete" : "modify",
1235
- path: change.path
1236
- });
1237
- }
1238
- observeStep();
1239
- return;
1240
- }
1241
- if (item.type === "error" && event.type === "item.completed") {
1242
- const message = typeof item.message === "string" && item.message.trim() ? item.message : "codex reported a non-fatal error item";
1243
- ctx.emitWarning({ message });
1244
- return;
1245
- }
1246
- }
1247
- function mapUsage(usage) {
1248
- const input = usage.input_tokens ?? 0;
1249
- const cacheRead = usage.cached_input_tokens ?? 0;
1250
- return {
1251
- inputTokens: {
1252
- total: input,
1253
- noCache: Math.max(0, input - cacheRead),
1254
- cacheRead,
1255
- cacheWrite: 0
1256
- },
1257
- outputTokens: {
1258
- total: usage.output_tokens ?? 0,
1259
- text: usage.output_tokens ?? 0
1260
- }
1261
- };
1262
- }
1263
1279
  async function startToolRelay({
1264
1280
  tools,
1265
1281
  emit,