@ai-sdk/harness-deepagents 1.0.38 → 1.0.40

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,24 @@
1
1
  # @ai-sdk/harness-deepagents
2
2
 
3
+ ## 1.0.40
4
+
5
+ ### Patch Changes
6
+
7
+ - 07c977a: chore(harness): refactor bridge code to break out stream event emission from launcher to make it testable
8
+ - 8c96d41: fix(harness-deepagents): fix Deep Agents missing model ID in telemetry
9
+ - 5c1a6af: fix(harness-deepagents): fix overly limiting override of Deep Agents recursion limit default
10
+ - Updated dependencies [a94425b]
11
+ - Updated dependencies [2de0611]
12
+ - @ai-sdk/harness@1.0.41
13
+
14
+ ## 1.0.39
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [59a2306]
19
+ - Updated dependencies [5f65e61]
20
+ - @ai-sdk/harness@1.0.40
21
+
3
22
  ## 1.0.38
4
23
 
5
24
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  // src/bridge/index.ts
2
- import { randomUUID } from "crypto";
2
+ import { randomUUID as randomUUID2 } from "crypto";
3
3
  import { argv, env as procEnv2 } from "process";
4
4
 
5
5
  // ../harness/dist/bridge/index.js
@@ -57,6 +57,7 @@ async function runBridge(options) {
57
57
  let currentBoundPort = 0;
58
58
  let currentTurnState = "init";
59
59
  let activeSocket;
60
+ let activeSocketReadyForLiveEvents = false;
60
61
  let isFirstTurn = true;
61
62
  let turnAbort;
62
63
  let currentUserMessages;
@@ -156,7 +157,7 @@ async function runBridge(options) {
156
157
  diskBuffer += `${line}
157
158
  `;
158
159
  scheduleEventFlush();
159
- if (activeSocket?.readyState === WS_OPEN) {
160
+ if (activeSocketReadyForLiveEvents && activeSocket?.readyState === WS_OPEN) {
160
161
  try {
161
162
  activeSocket.send(line);
162
163
  } catch {
@@ -259,6 +260,8 @@ async function runBridge(options) {
259
260
  const handleInbound = async (msg, ws) => {
260
261
  switch (msg.type) {
261
262
  case "start": {
263
+ if (activeSocket !== ws) return;
264
+ activeSocketReadyForLiveEvents = true;
262
265
  const firstTurn = isFirstTurn;
263
266
  isFirstTurn = false;
264
267
  eventLog = [];
@@ -361,7 +364,9 @@ async function runBridge(options) {
361
364
  }
362
365
  return;
363
366
  case "resume":
367
+ if (activeSocket !== ws) return;
364
368
  replay(ws, msg.lastSeenEventId);
369
+ activeSocketReadyForLiveEvents = true;
365
370
  return;
366
371
  case "shutdown":
367
372
  currentTurnState = "done";
@@ -426,6 +431,7 @@ async function runBridge(options) {
426
431
  return;
427
432
  }
428
433
  activeSocket = ws;
434
+ activeSocketReadyForLiveEvents = false;
429
435
  sendControl({
430
436
  type: "bridge-hello",
431
437
  state: currentTurnState,
@@ -448,6 +454,7 @@ async function runBridge(options) {
448
454
  ws.on("close", () => {
449
455
  if (activeSocket === ws) {
450
456
  activeSocket = void 0;
457
+ activeSocketReadyForLiveEvents = false;
451
458
  }
452
459
  });
453
460
  ws.on("error", () => {
@@ -541,6 +548,222 @@ function collectActionRequests(interrupts) {
541
548
  return out;
542
549
  }
543
550
 
551
+ // src/bridge/create-emit-stream-event.ts
552
+ import { randomUUID } from "crypto";
553
+ function createDeepAgentsStreamEventState() {
554
+ return {
555
+ streamStarted: false,
556
+ textBlockId: void 0,
557
+ reasoningBlockId: void 0,
558
+ inputTokens: 0,
559
+ outputTokens: 0,
560
+ streamedStepInput: 0,
561
+ streamedStepOutput: 0,
562
+ pendingStep: void 0,
563
+ approvedToolQueue: /* @__PURE__ */ new Map(),
564
+ approvedRunIds: /* @__PURE__ */ new Map()
565
+ };
566
+ }
567
+ var NATIVE_TO_COMMON2 = {
568
+ read_file: "read",
569
+ write_file: "write",
570
+ edit_file: "edit",
571
+ execute: "bash"
572
+ };
573
+ function toCommonName2(nativeName) {
574
+ return NATIVE_TO_COMMON2[nativeName] ?? nativeName;
575
+ }
576
+ function createEmitStreamEvent({
577
+ state,
578
+ configuredModel,
579
+ hostToolNames,
580
+ emit
581
+ }) {
582
+ return (event) => {
583
+ const kind = event.event;
584
+ const data = event.data ?? {};
585
+ const ns = event.metadata?.langgraph_checkpoint_ns ?? "";
586
+ const nested = ns.includes("|");
587
+ if (kind === "on_chat_model_start") {
588
+ if (!nested) {
589
+ if (!state.streamStarted) {
590
+ state.streamStarted = true;
591
+ const modelId = resolveDeepAgentsModelId({
592
+ configuredModel,
593
+ metadata: event.metadata
594
+ });
595
+ emit({
596
+ type: "stream-start",
597
+ ...modelId ? { modelId } : {}
598
+ });
599
+ }
600
+ flushStep({ state, emit });
601
+ }
602
+ } else if (kind === "on_chat_model_stream") {
603
+ if (nested) return;
604
+ const chunk = data.chunk;
605
+ if (!chunk) return;
606
+ const content = chunk.content;
607
+ if (typeof content === "string" && content) {
608
+ emitText({ state, emit, delta: content });
609
+ } else if (Array.isArray(content)) {
610
+ for (const block of content) {
611
+ if (block && typeof block === "object") {
612
+ const value = block;
613
+ if (value.type === "text" && value.text) {
614
+ emitText({ state, emit, delta: value.text });
615
+ } else if (value.type === "thinking" && value.thinking) {
616
+ emitReasoning({ state, emit, delta: value.thinking });
617
+ }
618
+ }
619
+ }
620
+ }
621
+ const usage = chunk.usage_metadata;
622
+ if (usage) {
623
+ state.streamedStepInput = Math.max(
624
+ state.streamedStepInput,
625
+ usage.input_tokens ?? 0
626
+ );
627
+ state.streamedStepOutput = Math.max(
628
+ state.streamedStepOutput,
629
+ usage.output_tokens ?? 0
630
+ );
631
+ }
632
+ } else if (kind === "on_chat_model_end") {
633
+ const output = data.output;
634
+ const usage = output?.usage_metadata;
635
+ const stepInput = usage?.input_tokens ?? state.streamedStepInput;
636
+ const stepOutput = usage?.output_tokens ?? state.streamedStepOutput;
637
+ state.inputTokens += stepInput;
638
+ state.outputTokens += stepOutput;
639
+ state.streamedStepInput = 0;
640
+ state.streamedStepOutput = 0;
641
+ if (!nested) {
642
+ endTextBlock({ state, emit });
643
+ endReasoningBlock({ state, emit });
644
+ state.pendingStep = { input: stepInput, output: stepOutput };
645
+ }
646
+ } else if (kind === "on_tool_start") {
647
+ const toolName = event.name ?? "unknown";
648
+ const runId = event.run_id ?? "";
649
+ if (!nested && !hostToolNames.has(toolName)) {
650
+ const queued = state.approvedToolQueue.get(toolName);
651
+ if (queued && queued.length > 0) {
652
+ const approvalId = queued.shift();
653
+ if (runId) state.approvedRunIds.set(runId, approvalId);
654
+ } else {
655
+ endTextBlock({ state, emit });
656
+ endReasoningBlock({ state, emit });
657
+ emit({
658
+ type: "tool-call",
659
+ toolCallId: runId,
660
+ toolName: toCommonName2(toolName),
661
+ input: toToolCallInput(data.input),
662
+ providerExecuted: true,
663
+ nativeName: toolName
664
+ });
665
+ }
666
+ }
667
+ } else if (kind === "on_tool_end") {
668
+ const toolName = event.name ?? "unknown";
669
+ const runId = event.run_id ?? "";
670
+ if (!nested && !hostToolNames.has(toolName)) {
671
+ let output = data.output ?? "";
672
+ if (output && typeof output === "object" && "content" in output) {
673
+ output = output.content;
674
+ }
675
+ emit({
676
+ type: "tool-result",
677
+ toolCallId: state.approvedRunIds.get(runId) ?? runId,
678
+ toolName: toCommonName2(toolName),
679
+ result: output ?? null
680
+ });
681
+ state.approvedRunIds.delete(runId);
682
+ }
683
+ }
684
+ };
685
+ }
686
+ function endTextBlock({
687
+ state,
688
+ emit
689
+ }) {
690
+ if (state.textBlockId) {
691
+ emit({ type: "text-end", id: state.textBlockId });
692
+ state.textBlockId = void 0;
693
+ }
694
+ }
695
+ function endReasoningBlock({
696
+ state,
697
+ emit
698
+ }) {
699
+ if (state.reasoningBlockId) {
700
+ emit({ type: "reasoning-end", id: state.reasoningBlockId });
701
+ state.reasoningBlockId = void 0;
702
+ }
703
+ }
704
+ function flushStep({
705
+ state,
706
+ emit
707
+ }) {
708
+ if (!state.pendingStep) return;
709
+ emit({
710
+ type: "finish-step",
711
+ finishReason: { unified: "stop" },
712
+ usage: {
713
+ inputTokens: { total: state.pendingStep.input },
714
+ outputTokens: { total: state.pendingStep.output }
715
+ }
716
+ });
717
+ state.pendingStep = void 0;
718
+ }
719
+ function ensureTextBlock({
720
+ state,
721
+ emit
722
+ }) {
723
+ if (!state.textBlockId) {
724
+ state.textBlockId = `text-${randomUUID()}`;
725
+ emit({ type: "text-start", id: state.textBlockId });
726
+ }
727
+ return state.textBlockId;
728
+ }
729
+ function emitText({
730
+ state,
731
+ emit,
732
+ delta
733
+ }) {
734
+ endReasoningBlock({ state, emit });
735
+ emit({ type: "text-delta", id: ensureTextBlock({ state, emit }), delta });
736
+ }
737
+ function emitReasoning({
738
+ state,
739
+ emit,
740
+ delta
741
+ }) {
742
+ endTextBlock({ state, emit });
743
+ if (!state.reasoningBlockId) {
744
+ state.reasoningBlockId = `reasoning-${randomUUID()}`;
745
+ emit({ type: "reasoning-start", id: state.reasoningBlockId });
746
+ }
747
+ emit({ type: "reasoning-delta", id: state.reasoningBlockId, delta });
748
+ }
749
+ function resolveDeepAgentsModelId({
750
+ configuredModel,
751
+ metadata
752
+ }) {
753
+ if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
754
+ const modelId = metadata.ls_model_name;
755
+ if (typeof modelId === "string" && modelId.length > 0) return modelId;
756
+ }
757
+ return configuredModel;
758
+ }
759
+ function toToolCallInput(raw) {
760
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && Object.keys(raw).length === 1 && typeof raw.input === "string") {
761
+ const inner = raw.input;
762
+ if (/^\s*[[{]/.test(inner)) return inner;
763
+ }
764
+ return JSON.stringify(raw ?? {});
765
+ }
766
+
544
767
  // src/bridge/json-schema-to-zod.ts
545
768
  import { z } from "zod/v4";
546
769
  function jsonSchemaToZodObject(input) {
@@ -685,16 +908,7 @@ function createBuiltinToolFilteringMiddleware(input) {
685
908
  }
686
909
 
687
910
  // src/bridge/index.ts
688
- var NATIVE_TO_COMMON2 = {
689
- read_file: "read",
690
- write_file: "write",
691
- edit_file: "edit",
692
- execute: "bash"
693
- };
694
911
  var HARNESS_CLIENT_APP = procEnv2.AI_SDK_HARNESS_CLIENT_APP;
695
- function toCommonName2(nativeName) {
696
- return NATIVE_TO_COMMON2[nativeName] ?? nativeName;
697
- }
698
912
  function parseArgs(rawArgs) {
699
913
  const out = {};
700
914
  for (let i = 0; i < rawArgs.length; i++) {
@@ -725,13 +939,6 @@ function buildModel(rawModel) {
725
939
  } : {}
726
940
  });
727
941
  }
728
- function toToolCallInput(raw) {
729
- if (raw && typeof raw === "object" && !Array.isArray(raw) && Object.keys(raw).length === 1 && typeof raw.input === "string") {
730
- const inner = raw.input;
731
- if (/^\s*[[{]/.test(inner)) return inner;
732
- }
733
- return JSON.stringify(raw ?? {});
734
- }
735
942
  var args = parseArgs(argv.slice(2));
736
943
  var workdir = args.workdir;
737
944
  var bridgeStateDir = args.bridgeStateDir;
@@ -747,7 +954,7 @@ function buildHostTools(toolSchemas) {
747
954
  async (input) => {
748
955
  const turn = currentTurn;
749
956
  if (!turn) throw new Error("no active turn");
750
- const toolCallId = `${schema.name}-${randomUUID()}`;
957
+ const toolCallId = `${schema.name}-${randomUUID2()}`;
751
958
  turn.emit({
752
959
  type: "tool-call",
753
960
  toolCallId,
@@ -800,67 +1007,18 @@ async function runTurn(start, turn) {
800
1007
  checkpointer: new MemorySaver()
801
1008
  });
802
1009
  }
803
- emit({
804
- type: "stream-start",
805
- ...start.model ? { modelId: start.model } : {}
806
- });
807
1010
  const hostToolNames = new Set((start.tools ?? []).map((t) => t.name));
808
- let textBlockId;
809
- let reasoningBlockId;
810
- let inputTokens = 0;
811
- let outputTokens = 0;
812
- let streamedStepInput = 0;
813
- let streamedStepOutput = 0;
814
- let pendingStep;
815
- const approvedToolQueue = /* @__PURE__ */ new Map();
816
- const approvedRunIds = /* @__PURE__ */ new Map();
817
- const ensureTextBlock = () => {
818
- if (!textBlockId) {
819
- textBlockId = `text-${randomUUID()}`;
820
- emit({ type: "text-start", id: textBlockId });
821
- }
822
- return textBlockId;
823
- };
824
- const endTextBlock = () => {
825
- if (textBlockId) {
826
- emit({ type: "text-end", id: textBlockId });
827
- textBlockId = void 0;
828
- }
829
- };
830
- const endReasoningBlock = () => {
831
- if (reasoningBlockId) {
832
- emit({ type: "reasoning-end", id: reasoningBlockId });
833
- reasoningBlockId = void 0;
834
- }
835
- };
836
- const emitText = (delta) => {
837
- endReasoningBlock();
838
- emit({ type: "text-delta", id: ensureTextBlock(), delta });
839
- };
840
- const emitReasoning = (delta) => {
841
- endTextBlock();
842
- if (!reasoningBlockId) {
843
- reasoningBlockId = `reasoning-${randomUUID()}`;
844
- emit({ type: "reasoning-start", id: reasoningBlockId });
845
- }
846
- emit({ type: "reasoning-delta", id: reasoningBlockId, delta });
847
- };
848
- const flushStep = () => {
849
- if (!pendingStep) return;
850
- emit({
851
- type: "finish-step",
852
- finishReason: { unified: "stop" },
853
- usage: {
854
- inputTokens: { total: pendingStep.input },
855
- outputTokens: { total: pendingStep.output }
856
- }
857
- });
858
- pendingStep = void 0;
859
- };
1011
+ const streamEventState = createDeepAgentsStreamEventState();
1012
+ const emitStreamEvent = createEmitStreamEvent({
1013
+ state: streamEventState,
1014
+ configuredModel: start.model,
1015
+ hostToolNames,
1016
+ emit
1017
+ });
860
1018
  const config = {
861
1019
  version: "v2",
862
1020
  configurable: { thread_id: "bridge-session" },
863
- recursionLimit: start.recursionLimit ?? 100,
1021
+ ...start.recursionLimit != null ? { recursionLimit: start.recursionLimit } : {},
864
1022
  signal: turn.abortSignal
865
1023
  };
866
1024
  const readPendingApprovals = async () => {
@@ -881,100 +1039,15 @@ async function runTurn(start, turn) {
881
1039
  while (true) {
882
1040
  const stream = await agent.streamEvents(resumeInput, config);
883
1041
  for await (const event of stream) {
884
- const kind = event.event;
885
- const data = event.data ?? {};
886
- const ns = event.metadata?.langgraph_checkpoint_ns ?? "";
887
- const nested = ns.includes("|");
888
- if (kind === "on_chat_model_start") {
889
- if (!nested) flushStep();
890
- } else if (kind === "on_chat_model_stream") {
891
- if (nested) continue;
892
- const chunk = data.chunk;
893
- if (!chunk) continue;
894
- const content = chunk.content;
895
- if (typeof content === "string" && content) {
896
- emitText(content);
897
- } else if (Array.isArray(content)) {
898
- for (const block of content) {
899
- if (block && typeof block === "object") {
900
- const b = block;
901
- if (b.type === "text" && b.text) emitText(b.text);
902
- else if (b.type === "thinking" && b.thinking)
903
- emitReasoning(b.thinking);
904
- }
905
- }
906
- }
907
- const usage = chunk.usage_metadata;
908
- if (usage) {
909
- streamedStepInput = Math.max(
910
- streamedStepInput,
911
- usage.input_tokens ?? 0
912
- );
913
- streamedStepOutput = Math.max(
914
- streamedStepOutput,
915
- usage.output_tokens ?? 0
916
- );
917
- }
918
- } else if (kind === "on_chat_model_end") {
919
- const output = data.output;
920
- const usage = output?.usage_metadata;
921
- const stepInput = usage?.input_tokens ?? streamedStepInput;
922
- const stepOutput = usage?.output_tokens ?? streamedStepOutput;
923
- inputTokens += stepInput;
924
- outputTokens += stepOutput;
925
- streamedStepInput = 0;
926
- streamedStepOutput = 0;
927
- if (!nested) {
928
- endTextBlock();
929
- endReasoningBlock();
930
- pendingStep = { input: stepInput, output: stepOutput };
931
- }
932
- } else if (kind === "on_tool_start") {
933
- const toolName = event.name ?? "unknown";
934
- const runId = event.run_id ?? "";
935
- if (!nested && !hostToolNames.has(toolName)) {
936
- const queued = approvedToolQueue.get(toolName);
937
- if (queued && queued.length > 0) {
938
- const approvalId = queued.shift();
939
- if (runId) approvedRunIds.set(runId, approvalId);
940
- } else {
941
- endTextBlock();
942
- endReasoningBlock();
943
- emit({
944
- type: "tool-call",
945
- toolCallId: runId,
946
- toolName: toCommonName2(toolName),
947
- input: toToolCallInput(data.input),
948
- providerExecuted: true,
949
- nativeName: toolName
950
- });
951
- }
952
- }
953
- } else if (kind === "on_tool_end") {
954
- const toolName = event.name ?? "unknown";
955
- const runId = event.run_id ?? "";
956
- if (!nested && !hostToolNames.has(toolName)) {
957
- let output = data.output ?? "";
958
- if (output && typeof output === "object" && "content" in output) {
959
- output = output.content;
960
- }
961
- emit({
962
- type: "tool-result",
963
- toolCallId: approvedRunIds.get(runId) ?? runId,
964
- toolName: toCommonName2(toolName),
965
- result: output ?? null
966
- });
967
- approvedRunIds.delete(runId);
968
- }
969
- }
1042
+ emitStreamEvent(event);
970
1043
  }
971
1044
  const actionRequests = await readPendingApprovals();
972
1045
  if (actionRequests.length === 0) break;
973
1046
  const decisions = [];
974
1047
  for (const action of actionRequests) {
975
- const approvalId = `approval-${randomUUID()}`;
976
- endTextBlock();
977
- endReasoningBlock();
1048
+ const approvalId = `approval-${randomUUID2()}`;
1049
+ endTextBlock({ state: streamEventState, emit });
1050
+ endReasoningBlock({ state: streamEventState, emit });
978
1051
  emit({
979
1052
  type: "tool-call",
980
1053
  toolCallId: approvalId,
@@ -988,12 +1061,12 @@ async function runTurn(start, turn) {
988
1061
  approvalId,
989
1062
  toolCallId: approvalId
990
1063
  });
991
- flushStep();
1064
+ flushStep({ state: streamEventState, emit });
992
1065
  const decision = await turn.requestToolApproval(approvalId);
993
1066
  if (decision.approved) {
994
- const queue = approvedToolQueue.get(action.name) ?? [];
1067
+ const queue = streamEventState.approvedToolQueue.get(action.name) ?? [];
995
1068
  queue.push(approvalId);
996
- approvedToolQueue.set(action.name, queue);
1069
+ streamEventState.approvedToolQueue.set(action.name, queue);
997
1070
  decisions.push({ type: "approve" });
998
1071
  } else {
999
1072
  emit({
@@ -1010,15 +1083,15 @@ async function runTurn(start, turn) {
1010
1083
  }
1011
1084
  resumeInput = new Command({ resume: { decisions } });
1012
1085
  }
1013
- endTextBlock();
1014
- endReasoningBlock();
1015
- flushStep();
1086
+ endTextBlock({ state: streamEventState, emit });
1087
+ endReasoningBlock({ state: streamEventState, emit });
1088
+ flushStep({ state: streamEventState, emit });
1016
1089
  emit({
1017
1090
  type: "finish",
1018
1091
  finishReason: { unified: "stop" },
1019
1092
  totalUsage: {
1020
- inputTokens: { total: inputTokens },
1021
- outputTokens: { total: outputTokens }
1093
+ inputTokens: { total: streamEventState.inputTokens },
1094
+ outputTokens: { total: streamEventState.outputTokens }
1022
1095
  }
1023
1096
  });
1024
1097
  }