@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 +19 -0
- package/dist/bridge/index.mjs +147 -10
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +83 -26
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/codex-step-tracker.ts +5 -2
- package/src/bridge/create-emit-stream-event.ts +1 -1
- package/src/bridge/index.ts +0 -2
- package/src/codex-harness.ts +111 -30
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
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 ??
|
|
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,
|