@ai-sdk/harness-opencode 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,20 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.75
4
+
5
+ ### Patch Changes
6
+
7
+ - @ai-sdk/harness@1.0.74
8
+
9
+ ## 1.0.74
10
+
11
+ ### Patch Changes
12
+
13
+ - 62a9c2a: feat(harness): add support for structured output to `HarnessAgent` via `output` property
14
+ - Updated dependencies [62a9c2a]
15
+ - Updated dependencies [d25cae2]
16
+ - @ai-sdk/harness@1.0.73
17
+
3
18
  ## 1.0.73
4
19
 
5
20
  ### Patch Changes
@@ -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 };
@@ -972,6 +966,7 @@ function createEmitStreamEvent({
972
966
  if (type === "session.next.tool.called") {
973
967
  const callID = String(props.callID ?? event.id);
974
968
  const rawToolName = String(props.tool ?? "unknown");
969
+ if (rawToolName === "StructuredOutput") return;
975
970
  const toolName = toWireToolName2(rawToolName);
976
971
  state.toolNames.set(callID, { rawToolName, toolName });
977
972
  const hostToolName = getHostToolName2(toolName, props.tool);
@@ -1000,6 +995,7 @@ function createEmitStreamEvent({
1000
995
  const callID = String(props.callID ?? event.id);
1001
996
  const cachedTool = state.toolNames.get(callID);
1002
997
  const rawToolName = cachedTool?.rawToolName ?? String(props.tool ?? "");
998
+ if (rawToolName === "StructuredOutput") return;
1003
999
  const toolName = cachedTool?.toolName ?? toWireToolName2(rawToolName || "unknown");
1004
1000
  if (getHostToolName2(toolName, rawToolName)) return;
1005
1001
  emit({
@@ -1118,6 +1114,7 @@ function emitLegacyToolPart({
1118
1114
  }
1119
1115
  const callID = toolPart.callID;
1120
1116
  const rawToolName = toolPart.tool;
1117
+ if (rawToolName === "StructuredOutput") return;
1121
1118
  const toolName = toWireToolName2(rawToolName);
1122
1119
  state.toolNames.set(callID, { rawToolName, toolName });
1123
1120
  const hostToolName = getHostToolName2(toolName, rawToolName);
@@ -1736,6 +1733,12 @@ async function legacySessionPrompt({
1736
1733
  sessionID: sessionId,
1737
1734
  ...start.instructions ? { system: start.instructions } : {},
1738
1735
  ...start.variant ? { variant: start.variant } : {},
1736
+ ...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? {
1737
+ format: {
1738
+ type: "json_schema",
1739
+ schema: start.responseFormat.schema
1740
+ }
1741
+ } : {},
1739
1742
  parts: [{ type: "text", text: start.prompt }]
1740
1743
  });
1741
1744
  }
@@ -1875,11 +1878,36 @@ async function runPrompt({
1875
1878
  state,
1876
1879
  emit
1877
1880
  });
1881
+ const info = asOpenCodeObject(event.properties?.info);
1882
+ if (start.responseFormat?.type === "json" && info?.structured !== void 0) {
1883
+ const id = String(info.id ?? randomUUID());
1884
+ emit({ type: "text-start", id });
1885
+ emit({
1886
+ type: "text-delta",
1887
+ id,
1888
+ delta: JSON.stringify(info.structured)
1889
+ });
1890
+ emit({ type: "text-end", id });
1891
+ emit({
1892
+ type: "finish-step",
1893
+ finishReason: { unified: "stop", raw: "stop" },
1894
+ usage: defaultUsage()
1895
+ });
1896
+ sawFinishStep = true;
1897
+ turnSettled.resolve();
1898
+ return true;
1899
+ }
1878
1900
  }
1879
1901
  if (event.type === "session.updated") {
1880
1902
  latestSessionTokens = extractSessionTokens(event.properties) ?? latestSessionTokens;
1881
1903
  }
1882
1904
  if (isStepSettlementEvent(event)) {
1905
+ if (event.type === "session.error") {
1906
+ terminalError = formatError(event.properties?.error ?? event);
1907
+ }
1908
+ if (start.responseFormat?.type === "json" && event.type === "session.next.step.ended") {
1909
+ return;
1910
+ }
1883
1911
  turnSettled.resolve();
1884
1912
  return true;
1885
1913
  }
@@ -1890,13 +1918,10 @@ async function runPrompt({
1890
1918
  sawBusy = true;
1891
1919
  turn.emitWarning({ message: legacyRetryStatusMessage(event) });
1892
1920
  } else if (sawBusy && status === "idle") {
1893
- turnSettled.resolve();
1894
- return true;
1895
- }
1896
- if (event.type === "session.error") {
1897
- terminalError = formatError(event.properties?.error ?? event);
1898
- turnSettled.resolve();
1899
- return true;
1921
+ if (start.responseFormat?.type !== "json") {
1922
+ turnSettled.resolve();
1923
+ return true;
1924
+ }
1900
1925
  }
1901
1926
  }
1902
1927
  }).finally(() => {