@ai-sdk/harness-opencode 1.0.91 → 1.0.92

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.92" : "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.92",
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) {
@@ -878,6 +878,22 @@ async function consumeEvents({
878
878
  const stream = await subscribeLegacyEvents({ client, signal });
879
879
  onSubscribed?.();
880
880
  if (!stream) return;
881
+ const taskSessionIds = new Set([sessionId]);
882
+ const registerSubagentSession = (sourceSessionId: string) =>
883
+ function register({
884
+ parentSessionId,
885
+ sessionId: subagentSessionId,
886
+ }: {
887
+ parentSessionId: string;
888
+ sessionId: string;
889
+ }) {
890
+ if (
891
+ parentSessionId === sourceSessionId &&
892
+ taskSessionIds.has(sourceSessionId)
893
+ ) {
894
+ taskSessionIds.add(subagentSessionId);
895
+ }
896
+ };
881
897
  const emitStreamEvent = createEmitStreamEvent({
882
898
  state,
883
899
  emit,
@@ -887,20 +903,59 @@ async function consumeEvents({
887
903
  nativeNameField,
888
904
  getHostToolName,
889
905
  authorizeHostToolCall: input => authorizeHostToolCall({ ...input, state }),
906
+ onSubagentSession: registerSubagentSession(sessionId),
890
907
  isMcpToolName: toolName =>
891
908
  [...runtime.mcpToolPrefixes].some(prefix => toolName.startsWith(prefix)),
892
909
  stripWorkDir,
893
910
  formatError,
894
911
  });
912
+ const descendantEventProcessors = new Map<
913
+ string,
914
+ (event: OpenCodeEvent) => void
915
+ >();
916
+ const processDescendantEvent = (
917
+ descendantSessionId: string,
918
+ event: OpenCodeEvent,
919
+ ) => {
920
+ let processEvent = descendantEventProcessors.get(descendantSessionId);
921
+ if (!processEvent) {
922
+ const descendantState = createTranslationState();
923
+ processEvent = createEmitStreamEvent({
924
+ state: descendantState,
925
+ emit: () => undefined,
926
+ emitWarning: () => undefined,
927
+ emitError: () => undefined,
928
+ toWireToolName,
929
+ nativeNameField,
930
+ getHostToolName,
931
+ authorizeHostToolCall: input =>
932
+ authorizeHostToolCall({ ...input, state: descendantState }),
933
+ onSubagentSession: registerSubagentSession(descendantSessionId),
934
+ isMcpToolName: () => false,
935
+ stripWorkDir,
936
+ formatError,
937
+ });
938
+ descendantEventProcessors.set(descendantSessionId, processEvent);
939
+ }
940
+ processEvent(event);
941
+ };
895
942
  for await (const rawEvent of stream) {
896
943
  if (signal.aborted || turn.abortSignal.aborted) break;
897
944
  const event = unwrapOpenCodeEvent(rawEvent);
898
945
  const eventSessionId = event ? getOpenCodeEventSessionId(event) : undefined;
899
- if (!event || (eventSessionId && eventSessionId !== sessionId)) continue;
946
+ if (!event) continue;
947
+ const scopedSessionId =
948
+ !eventSessionId || eventSessionId === sessionId
949
+ ? sessionId
950
+ : taskSessionIds.has(eventSessionId)
951
+ ? eventSessionId
952
+ : undefined;
953
+ if (!scopedSessionId) continue;
954
+ const isDescendant = scopedSessionId !== sessionId;
900
955
  if (event.type === 'permission.v2.asked') {
901
956
  await handlePermissionV2({
902
957
  client,
903
- sessionId,
958
+ sessionId: scopedSessionId,
904
959
  permissionMode,
905
960
  builtinToolFiltering,
906
961
  turn,
@@ -910,16 +965,19 @@ async function consumeEvents({
910
965
  } else if (event.type === 'permission.asked') {
911
966
  await handlePermission({
912
967
  client,
913
- sessionId,
968
+ sessionId: scopedSessionId,
914
969
  permissionMode,
915
970
  builtinToolFiltering,
916
971
  turn,
917
972
  emit,
918
973
  event,
919
974
  });
975
+ } else if (isDescendant) {
976
+ processDescendantEvent(scopedSessionId, event);
920
977
  } else {
921
978
  emitStreamEvent(event);
922
979
  }
980
+ if (isDescendant) continue;
923
981
  if (onEvent?.(event)) break;
924
982
  }
925
983
  }
@@ -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;