@ai-sdk/harness-codex 1.0.78 → 1.0.80

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,24 @@
1
1
  # @ai-sdk/harness-codex
2
2
 
3
+ ## 1.0.80
4
+
5
+ ### Patch Changes
6
+
7
+ - eace6fb: feat(harness): add experimental support for steering agent conversations mid-turn
8
+ - c0595b4: feat(harness): support passing a filesystem and process restricted sandbox session to `HarnessAgentSession`, using fallbacks in favor of the preferred network sandbox session methods
9
+ - Updated dependencies [eace6fb]
10
+ - Updated dependencies [c0595b4]
11
+ - @ai-sdk/harness@1.0.78
12
+
13
+ ## 1.0.79
14
+
15
+ ### Patch Changes
16
+
17
+ - 0fd1b7e: Close the inferred Codex step when a turn completes with stale pending tool items.
18
+ - Updated dependencies [e6087c9]
19
+ - @ai-sdk/provider-utils@5.0.28
20
+ - @ai-sdk/harness@1.0.77
21
+
3
22
  ## 1.0.78
4
23
 
5
24
  ### Patch Changes
@@ -1,6 +1,7 @@
1
1
  // ../harness/dist/bridge/index.js
2
2
  import { appendFile, mkdir, writeFile } from "fs/promises";
3
3
  import { existsSync, readFileSync } from "fs";
4
+ import { randomUUID } from "crypto";
4
5
  import { env as procEnv, pid, stdout } from "process";
5
6
  import { WebSocketServer } from "ws";
6
7
  var DEBUG_LEVEL_WEIGHT = {
@@ -31,6 +32,106 @@ function formatBridgeError(err) {
31
32
  }
32
33
  return { message: String(err) };
33
34
  }
35
+ function createBridgeUserMessageQueue(options) {
36
+ const messages = [];
37
+ const waiters = [];
38
+ const entries = /* @__PURE__ */ new Map();
39
+ let closed = false;
40
+ let pendingCount = 0;
41
+ const enqueue = (input) => {
42
+ const existing = entries.get(input.messageId);
43
+ if (existing != null) {
44
+ if (existing.response != null) {
45
+ options.respond(existing.response);
46
+ }
47
+ return;
48
+ }
49
+ let settled = false;
50
+ const settle = (response) => {
51
+ if (settled) return;
52
+ settled = true;
53
+ pendingCount--;
54
+ const entry = entries.get(input.messageId);
55
+ if (entry != null) entry.response = response;
56
+ options.respond(response);
57
+ };
58
+ const message = {
59
+ messageId: input.messageId,
60
+ text: input.text,
61
+ accept: () => {
62
+ settle({
63
+ type: "user-message-response",
64
+ messageId: input.messageId,
65
+ accepted: true
66
+ });
67
+ },
68
+ reject: (error) => {
69
+ settle({
70
+ type: "user-message-response",
71
+ messageId: input.messageId,
72
+ accepted: false,
73
+ error: { message: formatBridgeError(error).message }
74
+ });
75
+ }
76
+ };
77
+ entries.set(input.messageId, {
78
+ reject: message.reject
79
+ });
80
+ pendingCount++;
81
+ if (closed) {
82
+ message.reject(
83
+ new Error("The bridge turn is no longer accepting user messages.")
84
+ );
85
+ return;
86
+ }
87
+ const waiter = waiters.shift();
88
+ if (waiter != null) {
89
+ waiter({ done: false, value: message });
90
+ } else {
91
+ messages.push(message);
92
+ }
93
+ };
94
+ const close = (error) => {
95
+ if (closed) return;
96
+ closed = true;
97
+ const reason = error ?? new Error("The bridge turn ended before accepting the user message.");
98
+ for (const entry of entries.values()) {
99
+ if (entry.response == null) entry.reject(reason);
100
+ }
101
+ messages.length = 0;
102
+ while (waiters.length > 0) {
103
+ waiters.shift()({ done: true, value: void 0 });
104
+ }
105
+ };
106
+ return {
107
+ get pendingCount() {
108
+ return pendingCount;
109
+ },
110
+ enqueue,
111
+ close,
112
+ [Symbol.asyncIterator]() {
113
+ return {
114
+ next: () => {
115
+ const message = messages.shift();
116
+ if (message != null) {
117
+ return Promise.resolve({ done: false, value: message });
118
+ }
119
+ if (closed) {
120
+ return Promise.resolve({
121
+ done: true,
122
+ value: void 0
123
+ });
124
+ }
125
+ return new Promise(
126
+ (resolve) => {
127
+ waiters.push(resolve);
128
+ }
129
+ );
130
+ }
131
+ };
132
+ }
133
+ };
134
+ }
34
135
  function parseEnvList(value) {
35
136
  if (!value) return void 0;
36
137
  const items = value.split(",").map((item) => item.trim()).filter(Boolean);
@@ -247,6 +348,9 @@ async function runBridge(options) {
247
348
  switch (msg.type) {
248
349
  case "start": {
249
350
  activeSocket = ws;
351
+ currentUserMessages?.close(
352
+ new Error("A new bridge turn replaced the active turn.")
353
+ );
250
354
  const firstTurn = isFirstTurn;
251
355
  isFirstTurn = false;
252
356
  eventLog = [];
@@ -266,6 +370,7 @@ async function runBridge(options) {
266
370
  if (debugConfig.enabled) {
267
371
  installConsoleCapture();
268
372
  }
373
+ const userMessages = createBridgeUserMessageQueue({ respond: emit });
269
374
  const turn = {
270
375
  emit,
271
376
  requestToolResult: (toolCallId) => new Promise((resolve) => {
@@ -274,7 +379,7 @@ async function runBridge(options) {
274
379
  requestToolApproval: (approvalId) => new Promise((resolve) => {
275
380
  pendingToolApprovals.set(approvalId, resolve);
276
381
  }),
277
- pendingUserMessages: [],
382
+ experimental_userMessages: userMessages,
278
383
  abortSignal: turnAbort.signal,
279
384
  firstTurn,
280
385
  bridgeLog: (input) => {
@@ -292,12 +397,16 @@ async function runBridge(options) {
292
397
  emitWarning,
293
398
  emitError
294
399
  };
295
- currentUserMessages = turn.pendingUserMessages;
400
+ currentUserMessages = userMessages;
296
401
  try {
297
402
  await onStart(msg, turn);
298
403
  } catch (err) {
299
404
  emitError({ error: err, message: "bridge turn failed" });
300
405
  } finally {
406
+ userMessages.close();
407
+ if (currentUserMessages === userMessages) {
408
+ currentUserMessages = void 0;
409
+ }
301
410
  currentTurnState = "waiting";
302
411
  void writeBridgeMeta("waiting");
303
412
  }
@@ -319,9 +428,34 @@ async function runBridge(options) {
319
428
  }
320
429
  return;
321
430
  }
322
- case "user-message":
323
- currentUserMessages?.push(msg.text);
431
+ case "user-message": {
432
+ const messageId = msg.messageId ?? randomUUID();
433
+ if (currentUserMessages == null) {
434
+ sendControl(ws, {
435
+ type: "user-message-response",
436
+ messageId,
437
+ accepted: false,
438
+ error: { message: "The bridge has no active turn to steer." }
439
+ });
440
+ return;
441
+ }
442
+ if (ws !== activeSocket) {
443
+ sendControl(ws, {
444
+ type: "user-message-response",
445
+ messageId,
446
+ accepted: false,
447
+ error: {
448
+ message: "The connection does not own the active bridge turn."
449
+ }
450
+ });
451
+ return;
452
+ }
453
+ currentUserMessages.enqueue({
454
+ messageId,
455
+ text: msg.text
456
+ });
324
457
  return;
458
+ }
325
459
  case "abort":
326
460
  turnAbort?.abort();
327
461
  return;
@@ -395,7 +529,8 @@ async function runBridge(options) {
395
529
  sendControl(ws, {
396
530
  type: "bridge-hello",
397
531
  state: currentTurnState,
398
- lastSeq: seqCounter
532
+ lastSeq: seqCounter,
533
+ capabilities: { experimental_userMessageResponses: true }
399
534
  });
400
535
  ws.on("message", (raw) => {
401
536
  let parsed;
@@ -682,7 +817,10 @@ function createCodexStepTracker(input) {
682
817
  return;
683
818
  }
684
819
  },
685
- finishStep
820
+ finishTurn() {
821
+ pendingToolItemIds.clear();
822
+ finishStep();
823
+ }
686
824
  };
687
825
  }
688
826
  function isStepItem(item) {
@@ -702,7 +840,7 @@ function defaultUsage() {
702
840
  }
703
841
 
704
842
  // src/bridge/create-emit-stream-event.ts
705
- import { randomUUID } from "crypto";
843
+ import { randomUUID as randomUUID2 } from "crypto";
706
844
  var NATIVE_TO_COMMON = {
707
845
  shell: "bash",
708
846
  web_search: "webSearch"
@@ -727,7 +865,7 @@ function createEmitStreamEvent({
727
865
  }
728
866
  if (event.type === "turn.completed") {
729
867
  if (event.usage) setTurnUsage(mapUsage(event.usage));
730
- stepTracker.finishStep();
868
+ stepTracker.finishTurn();
731
869
  return;
732
870
  }
733
871
  if (event.type === "turn.failed") {
@@ -746,7 +884,7 @@ function createEmitStreamEvent({
746
884
  }
747
885
  if (!event.item) return;
748
886
  const item = event.item;
749
- const id = item.id ?? randomUUID();
887
+ const id = item.id ?? randomUUID2();
750
888
  const observeStep = () => {
751
889
  stepTracker.observeEvent({ event, itemId: id });
752
890
  };
@@ -1262,7 +1400,6 @@ async function runTurn(start, turn) {
1262
1400
  finishReason: { unified: "stop", raw: "stop" },
1263
1401
  totalUsage: turnUsage ?? defaultUsage()
1264
1402
  });
1265
- void turn.pendingUserMessages;
1266
1403
  }
1267
1404
  async function startToolRelay({
1268
1405
  tools,