@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/dist/index.js CHANGED
@@ -364,7 +364,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
364
364
  ]);
365
365
 
366
366
  // src/version.ts
367
- var VERSION = true ? "1.0.91" : "0.0.0-test";
367
+ var VERSION = true ? "1.0.93" : "0.0.0-test";
368
368
 
369
369
  // src/opencode-harness.ts
370
370
  var OPENCODE_CLIENT_APP = `ai-sdk/harness-opencode/${VERSION}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-opencode",
3
- "version": "1.0.91",
3
+ "version": "1.0.93",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -26,7 +26,7 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@ai-sdk/harness": "1.0.90",
29
+ "@ai-sdk/harness": "1.0.91",
30
30
  "@ai-sdk/provider-utils": "5.0.32",
31
31
  "ws": "8.21.0"
32
32
  },
@@ -19,6 +19,11 @@ import {
19
19
 
20
20
  type Emit = (message: Record<string, unknown>) => void;
21
21
 
22
+ type SubagentSession = {
23
+ parentSessionId: string;
24
+ sessionId: string;
25
+ };
26
+
22
27
  export function createEmitStreamEvent({
23
28
  state,
24
29
  emit,
@@ -28,6 +33,7 @@ export function createEmitStreamEvent({
28
33
  nativeNameField,
29
34
  getHostToolName,
30
35
  authorizeHostToolCall,
36
+ onSubagentSession,
31
37
  isMcpToolName,
32
38
  stripWorkDir,
33
39
  formatError,
@@ -49,6 +55,7 @@ export function createEmitStreamEvent({
49
55
  toolName: string;
50
56
  input: unknown;
51
57
  }) => void;
58
+ onSubagentSession?: (session: SubagentSession) => void;
52
59
  isMcpToolName: (toolName: string) => boolean;
53
60
  stripWorkDir: (file: string) => string;
54
61
  formatError: (error: unknown) => string;
@@ -83,6 +90,7 @@ export function createEmitStreamEvent({
83
90
  nativeNameField,
84
91
  getHostToolName,
85
92
  authorizeHostToolCall,
93
+ onSubagentSession,
86
94
  isMcpToolName,
87
95
  });
88
96
  return;
@@ -359,6 +367,7 @@ function emitLegacyToolPart({
359
367
  nativeNameField,
360
368
  getHostToolName,
361
369
  authorizeHostToolCall,
370
+ onSubagentSession,
362
371
  isMcpToolName,
363
372
  }: {
364
373
  part: unknown;
@@ -377,6 +386,7 @@ function emitLegacyToolPart({
377
386
  toolName: string;
378
387
  input: unknown;
379
388
  }) => void;
389
+ onSubagentSession?: (session: SubagentSession) => void;
380
390
  isMcpToolName: (toolName: string) => boolean;
381
391
  }): void {
382
392
  const toolPart = legacyToolPartFromValue(part);
@@ -389,6 +399,17 @@ function emitLegacyToolPart({
389
399
  const rawToolName = toolPart.tool;
390
400
  if (rawToolName === 'StructuredOutput') return;
391
401
  const toolName = toWireToolName(rawToolName);
402
+ if (toolName === 'agent') {
403
+ const metadata = {
404
+ ...(toolPart.metadata ?? {}),
405
+ ...(toolPart.state?.metadata ?? {}),
406
+ };
407
+ const parentSessionId = stringValue(metadata.parentSessionId);
408
+ const sessionId = stringValue(metadata.sessionId);
409
+ if (parentSessionId && sessionId) {
410
+ onSubagentSession?.({ parentSessionId, sessionId });
411
+ }
412
+ }
392
413
  state.toolNames.set(callID, { rawToolName, toolName });
393
414
  const hostToolName = getHostToolName(toolName, rawToolName);
394
415
  if (hostToolName) {
@@ -232,6 +232,7 @@ function buildOpenCodeConfig({
232
232
  webfetch: 'ask',
233
233
  doom_loop: 'ask',
234
234
  task: 'ask',
235
+ question: 'deny',
235
236
  },
236
237
  };
237
238
  if (start.model) config.model = start.model;
@@ -878,6 +879,22 @@ async function consumeEvents({
878
879
  const stream = await subscribeLegacyEvents({ client, signal });
879
880
  onSubscribed?.();
880
881
  if (!stream) return;
882
+ const taskSessionIds = new Set([sessionId]);
883
+ const registerSubagentSession = (sourceSessionId: string) =>
884
+ function register({
885
+ parentSessionId,
886
+ sessionId: subagentSessionId,
887
+ }: {
888
+ parentSessionId: string;
889
+ sessionId: string;
890
+ }) {
891
+ if (
892
+ parentSessionId === sourceSessionId &&
893
+ taskSessionIds.has(sourceSessionId)
894
+ ) {
895
+ taskSessionIds.add(subagentSessionId);
896
+ }
897
+ };
881
898
  const emitStreamEvent = createEmitStreamEvent({
882
899
  state,
883
900
  emit,
@@ -887,20 +904,59 @@ async function consumeEvents({
887
904
  nativeNameField,
888
905
  getHostToolName,
889
906
  authorizeHostToolCall: input => authorizeHostToolCall({ ...input, state }),
907
+ onSubagentSession: registerSubagentSession(sessionId),
890
908
  isMcpToolName: toolName =>
891
909
  [...runtime.mcpToolPrefixes].some(prefix => toolName.startsWith(prefix)),
892
910
  stripWorkDir,
893
911
  formatError,
894
912
  });
913
+ const descendantEventProcessors = new Map<
914
+ string,
915
+ (event: OpenCodeEvent) => void
916
+ >();
917
+ const processDescendantEvent = (
918
+ descendantSessionId: string,
919
+ event: OpenCodeEvent,
920
+ ) => {
921
+ let processEvent = descendantEventProcessors.get(descendantSessionId);
922
+ if (!processEvent) {
923
+ const descendantState = createTranslationState();
924
+ processEvent = createEmitStreamEvent({
925
+ state: descendantState,
926
+ emit: () => undefined,
927
+ emitWarning: () => undefined,
928
+ emitError: () => undefined,
929
+ toWireToolName,
930
+ nativeNameField,
931
+ getHostToolName,
932
+ authorizeHostToolCall: input =>
933
+ authorizeHostToolCall({ ...input, state: descendantState }),
934
+ onSubagentSession: registerSubagentSession(descendantSessionId),
935
+ isMcpToolName: () => false,
936
+ stripWorkDir,
937
+ formatError,
938
+ });
939
+ descendantEventProcessors.set(descendantSessionId, processEvent);
940
+ }
941
+ processEvent(event);
942
+ };
895
943
  for await (const rawEvent of stream) {
896
944
  if (signal.aborted || turn.abortSignal.aborted) break;
897
945
  const event = unwrapOpenCodeEvent(rawEvent);
898
946
  const eventSessionId = event ? getOpenCodeEventSessionId(event) : undefined;
899
- if (!event || (eventSessionId && eventSessionId !== sessionId)) continue;
947
+ if (!event) continue;
948
+ const scopedSessionId =
949
+ !eventSessionId || eventSessionId === sessionId
950
+ ? sessionId
951
+ : taskSessionIds.has(eventSessionId)
952
+ ? eventSessionId
953
+ : undefined;
954
+ if (!scopedSessionId) continue;
955
+ const isDescendant = scopedSessionId !== sessionId;
900
956
  if (event.type === 'permission.v2.asked') {
901
957
  await handlePermissionV2({
902
958
  client,
903
- sessionId,
959
+ sessionId: scopedSessionId,
904
960
  permissionMode,
905
961
  builtinToolFiltering,
906
962
  turn,
@@ -910,16 +966,19 @@ async function consumeEvents({
910
966
  } else if (event.type === 'permission.asked') {
911
967
  await handlePermission({
912
968
  client,
913
- sessionId,
969
+ sessionId: scopedSessionId,
914
970
  permissionMode,
915
971
  builtinToolFiltering,
916
972
  turn,
917
973
  emit,
918
974
  event,
919
975
  });
976
+ } else if (isDescendant) {
977
+ processDescendantEvent(scopedSessionId, event);
920
978
  } else {
921
979
  emitStreamEvent(event);
922
980
  }
981
+ if (isDescendant) continue;
923
982
  if (onEvent?.(event)) break;
924
983
  }
925
984
  }
@@ -101,6 +101,16 @@ export function getOpenCodeEventSessionId(
101
101
  if (event.type?.startsWith('session.') && typeof props.id === 'string') {
102
102
  return props.id;
103
103
  }
104
+ const info = asOpenCodeObject(props.info);
105
+ if (typeof info?.sessionID === 'string') return info.sessionID;
106
+ if (
107
+ (event.type === 'session.created' ||
108
+ event.type === 'session.updated' ||
109
+ event.type === 'session.deleted') &&
110
+ typeof info?.id === 'string'
111
+ ) {
112
+ return info.id;
113
+ }
104
114
  const part = props.part;
105
115
  const partObject = asOpenCodeObject(part);
106
116
  if (typeof partObject?.sessionID === 'string') return partObject.sessionID;
@@ -2,6 +2,7 @@ export type OpenCodeObject = Record<string, unknown>;
2
2
 
3
3
  export type OpenCodeMessageInfo = {
4
4
  id?: unknown;
5
+ sessionID?: unknown;
5
6
  role?: unknown;
6
7
  type?: unknown;
7
8
  providerID?: unknown;