@ai-sdk/harness-opencode 1.0.91 → 1.0.93

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,18 @@
1
1
  # @ai-sdk/harness-opencode
2
2
 
3
+ ## 1.0.93
4
+
5
+ ### Patch Changes
6
+
7
+ - 928f018: fix(harness-opencode): prevent headless OpenCode sessions from hanging when a model tries to use the interactive question tool
8
+
9
+ ## 1.0.92
10
+
11
+ ### Patch Changes
12
+
13
+ - eccb89e: fix(harness-opencode): authorize host tools and resolve permissions for task-linked subagents.
14
+ - @ai-sdk/harness@1.0.91
15
+
3
16
  ## 1.0.91
4
17
 
5
18
  ### Patch Changes
@@ -666,6 +666,11 @@ function getOpenCodeEventSessionId(event) {
666
666
  if (event.type?.startsWith("session.") && typeof props.id === "string") {
667
667
  return props.id;
668
668
  }
669
+ const info = asOpenCodeObject(props.info);
670
+ if (typeof info?.sessionID === "string") return info.sessionID;
671
+ if ((event.type === "session.created" || event.type === "session.updated" || event.type === "session.deleted") && typeof info?.id === "string") {
672
+ return info.id;
673
+ }
669
674
  const part = props.part;
670
675
  const partObject = asOpenCodeObject(part);
671
676
  if (typeof partObject?.sessionID === "string") return partObject.sessionID;
@@ -973,6 +978,7 @@ function createEmitStreamEvent({
973
978
  nativeNameField: nativeNameField2,
974
979
  getHostToolName: getHostToolName2,
975
980
  authorizeHostToolCall: authorizeHostToolCall2,
981
+ onSubagentSession,
976
982
  isMcpToolName,
977
983
  stripWorkDir: stripWorkDir2,
978
984
  formatError: formatError2
@@ -1004,6 +1010,7 @@ function createEmitStreamEvent({
1004
1010
  nativeNameField: nativeNameField2,
1005
1011
  getHostToolName: getHostToolName2,
1006
1012
  authorizeHostToolCall: authorizeHostToolCall2,
1013
+ onSubagentSession,
1007
1014
  isMcpToolName
1008
1015
  });
1009
1016
  return;
@@ -1252,6 +1259,7 @@ function emitLegacyToolPart({
1252
1259
  nativeNameField: nativeNameField2,
1253
1260
  getHostToolName: getHostToolName2,
1254
1261
  authorizeHostToolCall: authorizeHostToolCall2,
1262
+ onSubagentSession,
1255
1263
  isMcpToolName
1256
1264
  }) {
1257
1265
  const toolPart = legacyToolPartFromValue(part);
@@ -1264,6 +1272,17 @@ function emitLegacyToolPart({
1264
1272
  const rawToolName = toolPart.tool;
1265
1273
  if (rawToolName === "StructuredOutput") return;
1266
1274
  const toolName = toWireToolName2(rawToolName);
1275
+ if (toolName === "agent") {
1276
+ const metadata = {
1277
+ ...toolPart.metadata ?? {},
1278
+ ...toolPart.state?.metadata ?? {}
1279
+ };
1280
+ const parentSessionId = stringValue2(metadata.parentSessionId);
1281
+ const sessionId = stringValue2(metadata.sessionId);
1282
+ if (parentSessionId && sessionId) {
1283
+ onSubagentSession?.({ parentSessionId, sessionId });
1284
+ }
1285
+ }
1267
1286
  state.toolNames.set(callID, { rawToolName, toolName });
1268
1287
  const hostToolName = getHostToolName2(toolName, rawToolName);
1269
1288
  if (hostToolName) {
@@ -1740,7 +1759,8 @@ function buildOpenCodeConfig({
1740
1759
  external_directory: "ask",
1741
1760
  webfetch: "ask",
1742
1761
  doom_loop: "ask",
1743
- task: "ask"
1762
+ task: "ask",
1763
+ question: "deny"
1744
1764
  }
1745
1765
  };
1746
1766
  if (start.model) config.model = start.model;
@@ -2262,6 +2282,15 @@ async function consumeEvents({
2262
2282
  const stream = await subscribeLegacyEvents({ client, signal });
2263
2283
  onSubscribed?.();
2264
2284
  if (!stream) return;
2285
+ const taskSessionIds = /* @__PURE__ */ new Set([sessionId]);
2286
+ const registerSubagentSession = (sourceSessionId) => function register({
2287
+ parentSessionId,
2288
+ sessionId: subagentSessionId
2289
+ }) {
2290
+ if (parentSessionId === sourceSessionId && taskSessionIds.has(sourceSessionId)) {
2291
+ taskSessionIds.add(subagentSessionId);
2292
+ }
2293
+ };
2265
2294
  const emitStreamEvent = createEmitStreamEvent({
2266
2295
  state,
2267
2296
  emit,
@@ -2271,19 +2300,46 @@ async function consumeEvents({
2271
2300
  nativeNameField,
2272
2301
  getHostToolName,
2273
2302
  authorizeHostToolCall: (input) => authorizeHostToolCall({ ...input, state }),
2303
+ onSubagentSession: registerSubagentSession(sessionId),
2274
2304
  isMcpToolName: (toolName) => [...runtime.mcpToolPrefixes].some((prefix) => toolName.startsWith(prefix)),
2275
2305
  stripWorkDir,
2276
2306
  formatError
2277
2307
  });
2308
+ const descendantEventProcessors = /* @__PURE__ */ new Map();
2309
+ const processDescendantEvent = (descendantSessionId, event) => {
2310
+ let processEvent = descendantEventProcessors.get(descendantSessionId);
2311
+ if (!processEvent) {
2312
+ const descendantState = createTranslationState();
2313
+ processEvent = createEmitStreamEvent({
2314
+ state: descendantState,
2315
+ emit: () => void 0,
2316
+ emitWarning: () => void 0,
2317
+ emitError: () => void 0,
2318
+ toWireToolName,
2319
+ nativeNameField,
2320
+ getHostToolName,
2321
+ authorizeHostToolCall: (input) => authorizeHostToolCall({ ...input, state: descendantState }),
2322
+ onSubagentSession: registerSubagentSession(descendantSessionId),
2323
+ isMcpToolName: () => false,
2324
+ stripWorkDir,
2325
+ formatError
2326
+ });
2327
+ descendantEventProcessors.set(descendantSessionId, processEvent);
2328
+ }
2329
+ processEvent(event);
2330
+ };
2278
2331
  for await (const rawEvent of stream) {
2279
2332
  if (signal.aborted || turn.abortSignal.aborted) break;
2280
2333
  const event = unwrapOpenCodeEvent(rawEvent);
2281
2334
  const eventSessionId = event ? getOpenCodeEventSessionId(event) : void 0;
2282
- if (!event || eventSessionId && eventSessionId !== sessionId) continue;
2335
+ if (!event) continue;
2336
+ const scopedSessionId = !eventSessionId || eventSessionId === sessionId ? sessionId : taskSessionIds.has(eventSessionId) ? eventSessionId : void 0;
2337
+ if (!scopedSessionId) continue;
2338
+ const isDescendant = scopedSessionId !== sessionId;
2283
2339
  if (event.type === "permission.v2.asked") {
2284
2340
  await handlePermissionV2({
2285
2341
  client,
2286
- sessionId,
2342
+ sessionId: scopedSessionId,
2287
2343
  permissionMode,
2288
2344
  builtinToolFiltering,
2289
2345
  turn,
@@ -2293,16 +2349,19 @@ async function consumeEvents({
2293
2349
  } else if (event.type === "permission.asked") {
2294
2350
  await handlePermission({
2295
2351
  client,
2296
- sessionId,
2352
+ sessionId: scopedSessionId,
2297
2353
  permissionMode,
2298
2354
  builtinToolFiltering,
2299
2355
  turn,
2300
2356
  emit,
2301
2357
  event
2302
2358
  });
2359
+ } else if (isDescendant) {
2360
+ processDescendantEvent(scopedSessionId, event);
2303
2361
  } else {
2304
2362
  emitStreamEvent(event);
2305
2363
  }
2364
+ if (isDescendant) continue;
2306
2365
  if (onEvent?.(event)) break;
2307
2366
  }
2308
2367
  }