@ai-sdk/harness-codex 1.0.73 → 1.0.75

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,27 @@
1
1
  # @ai-sdk/harness-codex
2
2
 
3
+ ## 1.0.75
4
+
5
+ ### Patch Changes
6
+
7
+ - 62a9c2a: feat(harness): add support for structured output to `HarnessAgent` via `output` property
8
+ - Updated dependencies [62a9c2a]
9
+ - Updated dependencies [d25cae2]
10
+ - @ai-sdk/harness@1.0.73
11
+
12
+ ## 1.0.74
13
+
14
+ ### Patch Changes
15
+
16
+ - 69bb613: feat(harness): support request transformations in network sandbox abstraction and use it to apply credential brokering when available
17
+ - 52bc889: feat(harness): add `getPortEndpoint()` as more comprehensive replacement to `getPortUrl()` (now deprecated) in `HarnessV1NetworkSandboxSession`
18
+ - c89c2ec: feat(harness-codex): allow passing arbitrary `config.toml` values to the harness via `codexConfig`
19
+ - 4cd4989: chore(harness): decouple bridge based harness bootstrap recipe logic from dynamic params and enforce unique harnesses in `prepareSandboxForHarness()` helper
20
+ - Updated dependencies [69bb613]
21
+ - Updated dependencies [52bc889]
22
+ - Updated dependencies [4cd4989]
23
+ - @ai-sdk/harness@1.0.72
24
+
3
25
  ## 1.0.73
4
26
 
5
27
  ### Patch Changes
package/README.md CHANGED
@@ -20,7 +20,11 @@ import { tool } from 'ai';
20
20
  import { z } from 'zod/v4';
21
21
 
22
22
  const agent = new HarnessAgent({
23
- harness: createCodex(),
23
+ harness: createCodex({
24
+ codexConfig: {
25
+ model_verbosity: 'low',
26
+ },
27
+ }),
24
28
  id: 'demo',
25
29
  sandbox: createVercelSandbox({
26
30
  runtime: 'node24',
@@ -39,6 +43,11 @@ const agent = new HarnessAgent({
39
43
  });
40
44
  ```
41
45
 
46
+ `codexConfig` accepts additional native Codex configuration. Values pass
47
+ through as provided, so use the snake_case keys from Codex's `config.toml`
48
+ reference. The adapter's managed values take precedence over conflicting
49
+ entries.
50
+
42
51
  > Codex does not auto-discover a skills directory the way the `claude` CLI
43
52
  > does, so when you supply `skills: [...]` on the factory the adapter
44
53
  > injects every skill inline into the user prompt on each turn. Use fewer,
@@ -53,7 +53,6 @@ async function runBridge(options) {
53
53
  let currentBoundPort = 0;
54
54
  let currentTurnState = "init";
55
55
  let activeSocket;
56
- let activeSocketReadyForLiveEvents = false;
57
56
  let isFirstTurn = true;
58
57
  let turnAbort;
59
58
  let currentUserMessages;
@@ -137,14 +136,6 @@ async function runBridge(options) {
137
136
  } catch {
138
137
  }
139
138
  };
140
- const sendControl = (msg) => {
141
- if (activeSocket?.readyState === WS_OPEN) {
142
- try {
143
- activeSocket.send(JSON.stringify(msg));
144
- } catch {
145
- }
146
- }
147
- };
148
139
  const emit = (event) => {
149
140
  const seq = ++seqCounter;
150
141
  const line = JSON.stringify({ ...event, seq });
@@ -152,7 +143,7 @@ async function runBridge(options) {
152
143
  diskBuffer += `${line}
153
144
  `;
154
145
  scheduleEventFlush();
155
- if (activeSocketReadyForLiveEvents && activeSocket?.readyState === WS_OPEN) {
146
+ if (activeSocket?.readyState === WS_OPEN) {
156
147
  try {
157
148
  activeSocket.send(line);
158
149
  } catch {
@@ -255,8 +246,7 @@ async function runBridge(options) {
255
246
  const handleInbound = async (msg, ws) => {
256
247
  switch (msg.type) {
257
248
  case "start": {
258
- if (activeSocket !== ws) return;
259
- activeSocketReadyForLiveEvents = true;
249
+ activeSocket = ws;
260
250
  const firstTurn = isFirstTurn;
261
251
  isFirstTurn = false;
262
252
  eventLog = [];
@@ -336,9 +326,8 @@ async function runBridge(options) {
336
326
  turnAbort?.abort();
337
327
  return;
338
328
  case "resume":
339
- if (activeSocket !== ws) return;
329
+ activeSocket = ws;
340
330
  replay(ws, msg.lastSeenEventId);
341
- activeSocketReadyForLiveEvents = true;
342
331
  return;
343
332
  case "destroy":
344
333
  currentTurnState = "done";
@@ -350,7 +339,7 @@ async function runBridge(options) {
350
339
  currentTurnState = "done";
351
340
  void writeBridgeMeta("done");
352
341
  const data = await onStop?.() ?? {};
353
- sendControl({ type: "bridge-stop", data });
342
+ sendControl(ws, { type: "bridge-stop", data });
354
343
  drainThenExit(ws, 1e3, "stop");
355
344
  return;
356
345
  }
@@ -403,9 +392,7 @@ async function runBridge(options) {
403
392
  ws.close(1008, "unauthorized");
404
393
  return;
405
394
  }
406
- activeSocket = ws;
407
- activeSocketReadyForLiveEvents = false;
408
- sendControl({
395
+ sendControl(ws, {
409
396
  type: "bridge-hello",
410
397
  state: currentTurnState,
411
398
  lastSeq: seqCounter
@@ -416,7 +403,7 @@ async function runBridge(options) {
416
403
  const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
417
404
  parsed = JSON.parse(text);
418
405
  } catch (err) {
419
- sendControl({
406
+ sendControl(ws, {
420
407
  type: "error",
421
408
  error: `protocol parse error: ${err.message}`
422
409
  });
@@ -427,7 +414,6 @@ async function runBridge(options) {
427
414
  ws.on("close", () => {
428
415
  if (activeSocket === ws) {
429
416
  activeSocket = void 0;
430
- activeSocketReadyForLiveEvents = false;
431
417
  }
432
418
  });
433
419
  ws.on("error", () => {
@@ -454,6 +440,14 @@ async function runBridge(options) {
454
440
  })
455
441
  };
456
442
  }
443
+ function sendControl(socket, message) {
444
+ if (socket?.readyState === WS_OPEN) {
445
+ try {
446
+ socket.send(JSON.stringify(message));
447
+ } catch {
448
+ }
449
+ }
450
+ }
457
451
  function serialiseError(err) {
458
452
  if (err instanceof Error) {
459
453
  return { name: err.name, message: err.message, stack: err.stack };
@@ -1159,6 +1153,7 @@ async function runTurn(start, turn) {
1159
1153
  );
1160
1154
  }
1161
1155
  const codexConfig = {
1156
+ ...start.codexConfig,
1162
1157
  developer_instructions: [
1163
1158
  start.instructions,
1164
1159
  "Only respond with your `final` message once you have fully addressed the user request."
@@ -1234,7 +1229,8 @@ async function runTurn(start, turn) {
1234
1229
  });
1235
1230
  try {
1236
1231
  const { events } = await thread.runStreamed(userMessage, {
1237
- signal: turn.abortSignal
1232
+ signal: turn.abortSignal,
1233
+ ...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? { outputSchema: start.responseFormat.schema } : {}
1238
1234
  });
1239
1235
  for await (const event of events) {
1240
1236
  if (turn.abortSignal.aborted) break;