bs-agent 0.0.12 → 0.0.16

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.
@@ -91,22 +91,22 @@ var useSessionUtils = (agentId, allSessions, setAllSessions, currentSessionId, s
91
91
  setAllSessions((prev) => {
92
92
  const updatedAgentSessions = { ...prev[agentId] };
93
93
  delete updatedAgentSessions[sessionId];
94
+ if (sessionId === currentSessionId) {
95
+ const remainingSessions = Object.values(updatedAgentSessions);
96
+ if (remainingSessions.length > 0) {
97
+ const mostRecent = remainingSessions.sort((a, b) => b.updatedAt - a.updatedAt)[0];
98
+ setCurrentSessionId(mostRecent.id);
99
+ } else {
100
+ setCurrentSessionId(TEMPORARY_SESSION_ID);
101
+ }
102
+ }
94
103
  return {
95
104
  ...prev,
96
105
  [agentId]: updatedAgentSessions
97
106
  };
98
107
  });
99
- if (sessionId === currentSessionId) {
100
- const remainingSessions = Object.values(agentSessions).filter((s) => s.id !== sessionId);
101
- if (remainingSessions.length > 0) {
102
- const mostRecent = remainingSessions.sort((a, b) => b.updatedAt - a.updatedAt)[0];
103
- setCurrentSessionId(mostRecent.id);
104
- } else {
105
- setCurrentSessionId(TEMPORARY_SESSION_ID);
106
- }
107
- }
108
108
  },
109
- [agentId, currentSessionId, agentSessions, setAllSessions, setCurrentSessionId]
109
+ [agentId, currentSessionId, setAllSessions, setCurrentSessionId]
110
110
  );
111
111
  const sessionsList = (0, import_react.useMemo)(
112
112
  () => Object.values(agentSessions).sort((a, b) => b.updatedAt - a.updatedAt),
@@ -312,15 +312,13 @@ function useClientToolHelpers(agentId, toolContext) {
312
312
  const tools = toolContext.getToolsForAgent(agentId);
313
313
  const map = /* @__PURE__ */ new Map();
314
314
  for (const tool of tools) {
315
- if (tool.handler) {
316
- map.set(tool.name, {
317
- name: tool.name,
318
- description: tool.description,
319
- parameters: tool.parameters,
320
- await: tool.await,
321
- handler: tool.handler
322
- });
323
- }
315
+ map.set(tool.name, {
316
+ name: tool.name,
317
+ description: tool.description,
318
+ parameters: tool.parameters,
319
+ await: tool.await,
320
+ handler: tool.handler
321
+ });
324
322
  }
325
323
  return map;
326
324
  }, [agentId, toolContext]);
@@ -337,233 +335,6 @@ function useClientToolHelpers(agentId, toolContext) {
337
335
  return { getClientToolsMap, getClientToolDefs };
338
336
  }
339
337
 
340
- // src/core/stream.ts
341
- function tryParseJSON2(value) {
342
- if (typeof value === "string") {
343
- try {
344
- return JSON.parse(value);
345
- } catch {
346
- return value;
347
- }
348
- }
349
- return value;
350
- }
351
- var FatalStreamError = class extends Error {
352
- constructor(message, statusCode) {
353
- super(message);
354
- this.statusCode = statusCode;
355
- this.name = "FatalStreamError";
356
- }
357
- };
358
- async function executeStream(options) {
359
- const {
360
- url,
361
- body,
362
- headers,
363
- callbacks,
364
- clientTools,
365
- signal,
366
- onSessionId,
367
- onPaused,
368
- onAutoResume,
369
- onResponse
370
- } = options;
371
- let fullText = "";
372
- const pendingAutoResumes = [];
373
- const response = await fetch(url, {
374
- method: "POST",
375
- headers: {
376
- "Content-Type": "application/json",
377
- Accept: "text/event-stream",
378
- ...headers
379
- },
380
- body: JSON.stringify(body),
381
- signal
382
- });
383
- const sessionId = response.headers.get("X-BuildShip-Agent-Session-ID");
384
- if (sessionId && onSessionId) {
385
- const sessionName = response.headers.get("X-BuildShip-Agent-Session-Name") || void 0;
386
- onSessionId(sessionId, sessionName);
387
- }
388
- onResponse?.(response);
389
- if (!response.ok) {
390
- let errorBody = "";
391
- try {
392
- errorBody = await response.text();
393
- } catch {
394
- }
395
- const error = new FatalStreamError(
396
- `HTTP ${response.status}: ${errorBody || response.statusText}`,
397
- response.status
398
- );
399
- callbacks.onError?.(error);
400
- throw error;
401
- }
402
- if (!response.body) {
403
- callbacks.onComplete?.(fullText);
404
- return;
405
- }
406
- const reader = response.body.getReader();
407
- const decoder = new TextDecoder();
408
- let buffer = "";
409
- try {
410
- while (true) {
411
- const { done, value } = await reader.read();
412
- if (done) break;
413
- buffer += decoder.decode(value, { stream: true });
414
- const parts = buffer.split("\n\n");
415
- buffer = parts.pop() || "";
416
- for (const part of parts) {
417
- const trimmedPart = part.trim();
418
- if (!trimmedPart) continue;
419
- let jsonStr = "";
420
- for (const line of trimmedPart.split("\n")) {
421
- if (line.startsWith("data: ")) {
422
- jsonStr += line.slice(6);
423
- } else if (line.startsWith("data:")) {
424
- jsonStr += line.slice(5);
425
- }
426
- }
427
- jsonStr = jsonStr.trim();
428
- if (!jsonStr) continue;
429
- let raw;
430
- try {
431
- raw = JSON.parse(jsonStr);
432
- } catch {
433
- continue;
434
- }
435
- const streamEvent = normalizeEvent(raw);
436
- handleEvent(
437
- streamEvent,
438
- fullText,
439
- callbacks,
440
- clientTools,
441
- onPaused,
442
- onAutoResume,
443
- pendingAutoResumes
444
- );
445
- if (streamEvent.type === "text_delta") {
446
- fullText += streamEvent.data;
447
- }
448
- }
449
- }
450
- if (buffer.trim()) {
451
- let jsonStr = "";
452
- for (const line of buffer.trim().split("\n")) {
453
- if (line.startsWith("data: ")) {
454
- jsonStr += line.slice(6);
455
- } else if (line.startsWith("data:")) {
456
- jsonStr += line.slice(5);
457
- }
458
- }
459
- jsonStr = jsonStr.trim();
460
- if (jsonStr) {
461
- try {
462
- const raw = JSON.parse(jsonStr);
463
- const streamEvent = normalizeEvent(raw);
464
- handleEvent(
465
- streamEvent,
466
- fullText,
467
- callbacks,
468
- clientTools,
469
- onPaused,
470
- onAutoResume,
471
- pendingAutoResumes
472
- );
473
- if (streamEvent.type === "text_delta") {
474
- fullText += streamEvent.data;
475
- }
476
- } catch {
477
- }
478
- }
479
- }
480
- } catch (err) {
481
- if (err instanceof Error && err.name === "AbortError") {
482
- throw err;
483
- }
484
- const error = err instanceof Error ? err : new Error(String(err));
485
- callbacks.onError?.(error);
486
- throw error;
487
- }
488
- if (pendingAutoResumes.length > 0) {
489
- await Promise.allSettled(pendingAutoResumes);
490
- }
491
- callbacks.onComplete?.(fullText);
492
- }
493
- function normalizeEvent(raw) {
494
- const typeMap = {
495
- llm_text_delta: "text_delta",
496
- llm_reasoning_delta: "reasoning_delta"
497
- };
498
- if (raw && typeof raw.type === "string" && typeMap[raw.type]) {
499
- raw.type = typeMap[raw.type];
500
- }
501
- return raw;
502
- }
503
- function handleEvent(event, _fullText, callbacks, clientTools, onPaused, onAutoResume, pendingAutoResumes) {
504
- callbacks.onEvent?.(event);
505
- switch (event.type) {
506
- case "text_delta": {
507
- callbacks.onText?.(event.data);
508
- break;
509
- }
510
- case "reasoning_delta": {
511
- callbacks.onReasoning?.(event.data.delta, event.data.index);
512
- break;
513
- }
514
- case "agent_handoff": {
515
- callbacks.onAgentHandoff?.(event.data.agentName);
516
- break;
517
- }
518
- case "tool_call_start": {
519
- const { callId, toolName, toolType, inputs: rawInputs, paused } = event.data;
520
- const inputs = tryParseJSON2(rawInputs);
521
- callbacks.onToolStart?.(toolName, toolType);
522
- if (toolType === "client") {
523
- const tool = clientTools.get(toolName);
524
- if (tool) {
525
- if (paused && tool.handler) {
526
- const handlerPromise = (async () => {
527
- try {
528
- const result = await tool.handler(inputs);
529
- await onAutoResume?.(callId, result);
530
- } catch {
531
- callbacks.onPaused?.(toolName, inputs);
532
- onPaused?.({ callId, toolName, args: inputs });
533
- }
534
- })();
535
- pendingAutoResumes.push(handlerPromise);
536
- } else if (paused) {
537
- callbacks.onPaused?.(toolName, inputs);
538
- onPaused?.({ callId, toolName, args: inputs });
539
- } else if (tool.handler) {
540
- try {
541
- tool.handler(inputs);
542
- } catch {
543
- }
544
- }
545
- } else if (paused) {
546
- callbacks.onPaused?.(toolName, inputs);
547
- onPaused?.({ callId, toolName, args: inputs });
548
- }
549
- }
550
- break;
551
- }
552
- case "tool_call_end": {
553
- const { toolName, result, error } = event.data;
554
- callbacks.onToolEnd?.(toolName, result, error);
555
- break;
556
- }
557
- case "run_error": {
558
- const { message, code } = event.data;
559
- const error = new Error(message);
560
- if (code != null) error.code = code;
561
- callbacks.onError?.(error);
562
- break;
563
- }
564
- }
565
- }
566
-
567
338
  // src/react/utils/message.ts
568
339
  function updateAgentMessageParts(parts, newPart) {
569
340
  const last = parts[parts.length - 1];
@@ -583,7 +354,15 @@ function updateAgentMessageParts(parts, newPart) {
583
354
 
584
355
  // src/react/stream-callbacks.ts
585
356
  function buildStreamCallbacks(deps, debugKey) {
586
- const { setMessages, setInProgress, syncSessionRef, messagesRef, toolContext, agentId } = deps;
357
+ const {
358
+ setMessages,
359
+ setInProgress,
360
+ syncSessionRef,
361
+ messagesRef,
362
+ toolContext,
363
+ agentId,
364
+ textDeltaModifier
365
+ } = deps;
587
366
  return {
588
367
  onComplete: () => {
589
368
  console.log("Agent closed");
@@ -601,7 +380,7 @@ function buildStreamCallbacks(deps, debugKey) {
601
380
  },
602
381
  onEvent: (event) => {
603
382
  if (event.type === "text_delta") {
604
- handleTextDelta(event, setMessages, syncSessionRef);
383
+ handleTextDelta(event, setMessages, syncSessionRef, textDeltaModifier, agentId);
605
384
  } else if (event.type === "tool_call_start" && event.data.toolType === "client") {
606
385
  handleClientToolCall(event, setMessages, syncSessionRef, toolContext, agentId);
607
386
  if (debugKey) {
@@ -631,82 +410,21 @@ function buildStreamCallbacks(deps, debugKey) {
631
410
  }
632
411
  };
633
412
  }
634
- var TEMPORARY_SESSION_ID2 = "sess_temp";
635
- var DEFAULT_SESSION_NAME2 = "New Chat";
636
- async function executeAgentStream(deps, body, headers, callbacks, debugKey) {
637
- const {
638
- agentUrl,
639
- signal,
640
- currentSessionId,
641
- lastRequestContextRef,
642
- messagesRef,
643
- createSessionFromResponse,
644
- setMessages,
645
- syncSessionRef,
646
- runAgentRef
647
- } = deps;
648
- await executeStream({
649
- url: agentUrl,
650
- body,
651
- headers,
652
- callbacks,
653
- clientTools: deps.getClientToolsMap(),
654
- signal,
655
- onSessionId: (sessionId, sessionName) => {
656
- lastRequestContextRef.current.sessionId = sessionId;
657
- if (!currentSessionId || currentSessionId === TEMPORARY_SESSION_ID2) {
658
- createSessionFromResponse(
659
- sessionId,
660
- sessionName || DEFAULT_SESSION_NAME2,
661
- messagesRef.current
662
- );
663
- deps.setCurrentSessionId(sessionId);
664
- }
665
- },
666
- // Session creation is handled by onSessionId above.
667
- onAutoResume: async (callId, result) => {
668
- setMessages((prev) => {
669
- const updatedMessages = prev.map((msg) => {
670
- if (msg.parts) {
671
- const updatedParts = msg.parts.map((part) => {
672
- if (part.type === "widget" && part.callId === callId) {
673
- return { ...part, status: "submitted", result };
674
- }
675
- return part;
676
- });
677
- return { ...msg, parts: updatedParts };
678
- }
679
- return msg;
680
- });
681
- if (syncSessionRef.current) {
682
- syncSessionRef.current(updatedMessages);
683
- }
684
- return updatedMessages;
685
- });
686
- if (debugKey) {
687
- deps.debugHandlers.handleStreamEvent({
688
- type: "tool_call_end",
689
- data: { callId, toolName: "", toolType: "client", result },
690
- meta: { executionId: debugKey, sequence: 0 }
691
- });
692
- }
693
- try {
694
- await runAgentRef.current?.(void 0, {
695
- resumeToolCallId: callId,
696
- resumeToolResult: result
697
- });
698
- } catch (error) {
699
- console.error("Auto-resume failed", error);
700
- }
701
- }
702
- });
703
- }
704
- function handleTextDelta(event, setMessages, syncSessionRef) {
413
+ function handleTextDelta(event, setMessages, syncSessionRef, modifier, agentId) {
705
414
  const sequence = event.meta.sequence;
706
- const text = event.data;
415
+ const originalText = event.data;
707
416
  const eventExecutionId = event.meta.executionId;
708
417
  setMessages((prev) => {
709
418
  const lastMessage = prev[prev.length - 1];
419
+ let text = originalText;
420
+ if (modifier) {
421
+ const currentFullText = lastMessage?.role === "agent" ? lastMessage.content : "";
422
+ text = modifier(originalText, currentFullText, {
423
+ executionId: eventExecutionId,
424
+ sequence,
425
+ agentId: event.meta.agentId || agentId
426
+ });
427
+ }
710
428
  const newPart = {
711
429
  type: "text",
712
430
  text,
@@ -774,7 +492,8 @@ function handleClientToolCall(event, setMessages, syncSessionRef, toolContext, a
774
492
  }
775
493
 
776
494
  // src/react/use-agent.ts
777
- function useAgent(agentId, agentUrl, accessKey) {
495
+ function useAgent(agent, options) {
496
+ const agentId = agent._agentId;
778
497
  const { allSessions, setAllSessions, debugData, setDebugData } = useAgentGlobalState();
779
498
  const toolContext = (0, import_react3.useContext)(AgentToolContext);
780
499
  const [inProgress, setInProgress] = (0, import_react3.useState)(false);
@@ -792,7 +511,7 @@ function useAgent(agentId, agentUrl, accessKey) {
792
511
  (0, import_react3.useEffect)(() => {
793
512
  const initialSessionId = sessionUtils.getInitialSessionId();
794
513
  setCurrentSessionId(initialSessionId);
795
- }, []);
514
+ }, [agentId]);
796
515
  const debugHandlers = (0, import_react3.useMemo)(() => createDebugHandlers(setDebugData), [setDebugData]);
797
516
  (0, import_react3.useEffect)(() => {
798
517
  messagesRef.current = messages;
@@ -805,82 +524,90 @@ function useAgent(agentId, agentUrl, accessKey) {
805
524
  } else {
806
525
  setMessages([]);
807
526
  }
808
- }, [currentSessionId, agentId]);
527
+ }, [currentSessionId, agentId, sessionUtils.agentSessions, inProgress]);
809
528
  (0, import_react3.useEffect)(() => {
529
+ const syncRef = sessionUtils.syncSessionRef;
530
+ const msgRef = messagesRef;
810
531
  return () => {
811
- if (messagesRef.current.length > 0 && sessionUtils.syncSessionRef.current) {
812
- sessionUtils.syncSessionRef.current();
532
+ if (msgRef.current.length > 0 && syncRef.current) {
533
+ syncRef.current();
813
534
  }
814
535
  };
815
- }, []);
816
- const controller = (0, import_react3.useRef)();
817
- const lastRequestContextRef = (0, import_react3.useRef)({});
818
- const { getClientToolsMap, getClientToolDefs } = useClientToolHelpers(agentId, toolContext);
819
- const runAgentRef = (0, import_react3.useRef)();
536
+ }, [sessionUtils.syncSessionRef]);
537
+ const optionsRef = (0, import_react3.useRef)(options);
538
+ (0, import_react3.useEffect)(() => {
539
+ optionsRef.current = options;
540
+ }, [options]);
541
+ const lastRunOptionsRef = (0, import_react3.useRef)({});
542
+ const { getClientToolsMap } = useClientToolHelpers(agentId, toolContext);
543
+ const activeSessionRef = (0, import_react3.useRef)(null);
820
544
  const runAgent = (0, import_react3.useCallback)(
821
545
  async (input, runOptions) => {
822
- const realSessionId = currentSessionId && currentSessionId !== TEMPORARY_SESSION_ID ? currentSessionId : lastRequestContextRef.current.sessionId;
823
- lastRequestContextRef.current = {
824
- additionalHeaders: runOptions?.additionalHeaders ?? lastRequestContextRef.current.additionalHeaders,
825
- additionalBody: runOptions?.additionalBody ?? lastRequestContextRef.current.additionalBody,
826
- sessionId: realSessionId
827
- };
546
+ const isNewSession = !currentSessionId || currentSessionId === TEMPORARY_SESSION_ID;
828
547
  setInProgress(true);
829
- if (!runOptions?.resumeToolCallId && controller.current) {
830
- controller.current.abort();
831
- }
832
- controller.current = new AbortController();
833
- const headers = {
834
- ...lastRequestContextRef.current.additionalHeaders || {},
835
- ...accessKey ? { Authorization: `Bearer ${accessKey}` } : {}
836
- };
837
- if (realSessionId) {
838
- headers["x-buildship-agent-session-id"] = realSessionId;
839
- }
840
- const body = {
841
- stream: true,
842
- ...lastRequestContextRef.current.additionalBody || {}
843
- };
844
- if (input !== void 0) {
845
- body.input = input;
846
- }
847
- if (runOptions?.context) {
848
- Object.assign(body, { context: runOptions.context });
849
- }
850
- if (runOptions?.resumeToolCallId) {
851
- body.toolCallResult = {
852
- callId: runOptions.resumeToolCallId,
853
- result: runOptions.resumeToolResult
854
- };
855
- }
856
- const registeredToolDefs = getClientToolDefs() || [];
857
- const legacyToolDefs = runOptions?.clientTools || [];
858
- const allToolDefs = [...registeredToolDefs, ...legacyToolDefs];
859
- if (allToolDefs.length > 0) {
860
- body.clientTools = allToolDefs;
861
- }
862
548
  const debugKey = runOptions?.optimisticExecutionId || messagesRef.current.findLast((m) => m.role === "user")?.executionId;
863
549
  const deps = {
864
- agentUrl,
865
550
  agentId,
866
- accessKey,
867
551
  currentSessionId,
868
552
  messagesRef,
869
553
  setMessages,
870
554
  setInProgress,
871
- setCurrentSessionId,
872
555
  syncSessionRef: sessionUtils.syncSessionRef,
873
- createSessionFromResponse: sessionUtils.createSessionFromResponse,
874
556
  debugHandlers,
875
557
  toolContext,
876
- lastRequestContextRef,
877
- getClientToolsMap,
878
- signal: controller.current.signal,
879
- runAgentRef
558
+ textDeltaModifier: optionsRef.current?.textDeltaModifier
880
559
  };
881
560
  const callbacks = buildStreamCallbacks(deps, debugKey);
561
+ const executeOptions = {
562
+ context: runOptions?.context,
563
+ headers: runOptions?.additionalHeaders,
564
+ body: runOptions?.additionalBody
565
+ };
566
+ const toolsMap = getClientToolsMap();
567
+ for (const tool of toolsMap.values()) {
568
+ agent.registerClientTool(tool);
569
+ }
570
+ if (runOptions?.clientTools && runOptions.clientTools.length > 0) {
571
+ executeOptions.body = {
572
+ ...executeOptions.body || {},
573
+ clientTools: [
574
+ ...executeOptions.body?.clientTools || [],
575
+ ...runOptions.clientTools
576
+ ]
577
+ };
578
+ }
882
579
  try {
883
- await executeAgentStream(deps, body, headers, callbacks, debugKey);
580
+ if (runOptions?.resumeToolCallId) {
581
+ if (isNewSession) throw new Error("Cannot resume a tool call on a temporary session.");
582
+ const session = agent.session(currentSessionId);
583
+ activeSessionRef.current = session;
584
+ await session.resumeWithCallId(
585
+ runOptions.resumeToolCallId,
586
+ runOptions.resumeToolResult,
587
+ callbacks,
588
+ executeOptions
589
+ );
590
+ } else {
591
+ if (isNewSession) {
592
+ const extendedOptions = {
593
+ ...executeOptions,
594
+ onSessionId: (newSessionId, sessionName) => {
595
+ sessionUtils.createSessionFromResponse(
596
+ newSessionId,
597
+ sessionName || DEFAULT_SESSION_NAME,
598
+ messagesRef.current
599
+ );
600
+ setCurrentSessionId(newSessionId);
601
+ }
602
+ };
603
+ const session = await agent.execute(input || "", callbacks, extendedOptions);
604
+ activeSessionRef.current = session;
605
+ } else {
606
+ const session = agent.session(currentSessionId);
607
+ activeSessionRef.current = session;
608
+ await session.execute(input || "", callbacks, executeOptions);
609
+ }
610
+ }
884
611
  } catch (error) {
885
612
  console.log("Agent execution failed", error);
886
613
  setInProgress(false);
@@ -888,32 +615,20 @@ function useAgent(agentId, agentUrl, accessKey) {
888
615
  sessionUtils.syncSessionRef.current(messagesRef.current);
889
616
  }
890
617
  throw error;
618
+ } finally {
619
+ activeSessionRef.current = null;
891
620
  }
892
621
  },
893
- [
894
- agentUrl,
895
- accessKey,
896
- currentSessionId,
897
- sessionUtils.syncSessionRef,
898
- sessionUtils.createSessionFromResponse,
899
- debugHandlers,
900
- getClientToolsMap,
901
- getClientToolDefs,
902
- agentId,
903
- toolContext
904
- ]
622
+ [currentSessionId, sessionUtils, debugHandlers, agentId, toolContext, agent, getClientToolsMap]
905
623
  );
906
- (0, import_react3.useEffect)(() => {
907
- runAgentRef.current = runAgent;
908
- }, [runAgent]);
909
624
  const handleSend = (0, import_react3.useCallback)(
910
- async (input, options) => {
625
+ async (input, options2) => {
911
626
  const userMessage = {
912
627
  role: "user",
913
628
  content: input,
914
629
  executionId: Date.now().toString()
915
630
  };
916
- if (!options?.skipUserMessage) {
631
+ if (!options2?.skipUserMessage) {
917
632
  setMessages((prev) => {
918
633
  const updatedMessages = [...prev, userMessage];
919
634
  if (sessionUtils.syncSessionRef.current) {
@@ -922,14 +637,19 @@ function useAgent(agentId, agentUrl, accessKey) {
922
637
  return updatedMessages;
923
638
  });
924
639
  }
925
- const effectiveExecutionId = options?.skipUserMessage ? messagesRef.current.findLast((m) => m.role === "user")?.executionId ?? userMessage.executionId : userMessage.executionId;
640
+ const effectiveExecutionId = options2?.skipUserMessage ? messagesRef.current.findLast((m) => m.role === "user")?.executionId ?? userMessage.executionId : userMessage.executionId;
641
+ lastRunOptionsRef.current = {
642
+ context: options2?.context,
643
+ additionalHeaders: options2?.additionalHeaders,
644
+ additionalBody: options2?.additionalBody
645
+ };
926
646
  try {
927
647
  await runAgent(input, {
928
- ...options,
648
+ ...options2,
929
649
  optimisticExecutionId: effectiveExecutionId
930
650
  });
931
651
  } catch (error) {
932
- if (!options?.skipUserMessage) {
652
+ if (!options2?.skipUserMessage) {
933
653
  setMessages((prev) => {
934
654
  const updatedMessages = prev.some((m) => m === userMessage) ? prev : [...prev, userMessage];
935
655
  if (sessionUtils.syncSessionRef.current) {
@@ -973,7 +693,8 @@ function useAgent(agentId, agentUrl, accessKey) {
973
693
  }
974
694
  await runAgent(void 0, {
975
695
  resumeToolCallId: callId,
976
- resumeToolResult: result
696
+ resumeToolResult: result,
697
+ ...lastRunOptionsRef.current
977
698
  });
978
699
  },
979
700
  [runAgent, sessionUtils.syncSessionRef, debugHandlers]
@@ -996,8 +717,8 @@ function useAgent(agentId, agentUrl, accessKey) {
996
717
  [sessionUtils.syncSessionRef]
997
718
  );
998
719
  const abort = (0, import_react3.useCallback)(() => {
999
- if (controller.current) {
1000
- controller.current.abort();
720
+ if (activeSessionRef.current) {
721
+ activeSessionRef.current.abort();
1001
722
  }
1002
723
  setInProgress(false);
1003
724
  if (sessionUtils.syncSessionRef.current) {
@@ -1019,6 +740,536 @@ function useAgent(agentId, agentUrl, accessKey) {
1019
740
  };
1020
741
  }
1021
742
 
743
+ // src/core/stream.ts
744
+ function tryParseJSON2(value) {
745
+ if (typeof value === "string") {
746
+ try {
747
+ return JSON.parse(value);
748
+ } catch {
749
+ return value;
750
+ }
751
+ }
752
+ return value;
753
+ }
754
+ var FatalStreamError = class extends Error {
755
+ constructor(message, statusCode) {
756
+ super(message);
757
+ this.statusCode = statusCode;
758
+ this.name = "FatalStreamError";
759
+ }
760
+ };
761
+ async function executeStream(options) {
762
+ const {
763
+ url,
764
+ body,
765
+ headers,
766
+ callbacks,
767
+ clientTools,
768
+ signal,
769
+ onSessionId,
770
+ onPaused,
771
+ onAutoResume,
772
+ onResponse
773
+ } = options;
774
+ let fullText = "";
775
+ const pendingAutoResumes = [];
776
+ const response = await fetch(url, {
777
+ method: "POST",
778
+ headers: {
779
+ "Content-Type": "application/json",
780
+ Accept: "text/event-stream",
781
+ ...headers
782
+ },
783
+ body: JSON.stringify(body),
784
+ signal
785
+ });
786
+ const sessionId = response.headers.get("X-BuildShip-Agent-Session-ID");
787
+ if (sessionId && onSessionId) {
788
+ const sessionName = response.headers.get("X-BuildShip-Agent-Session-Name") || void 0;
789
+ onSessionId(sessionId, sessionName);
790
+ }
791
+ onResponse?.(response);
792
+ if (!response.ok) {
793
+ let errorBody = "";
794
+ try {
795
+ errorBody = await response.text();
796
+ } catch {
797
+ }
798
+ const error = new FatalStreamError(
799
+ `HTTP ${response.status}: ${errorBody || response.statusText}`,
800
+ response.status
801
+ );
802
+ callbacks.onError?.(error);
803
+ throw error;
804
+ }
805
+ if (!response.body) {
806
+ callbacks.onComplete?.(fullText);
807
+ return;
808
+ }
809
+ const reader = response.body.getReader();
810
+ const decoder = new TextDecoder();
811
+ let buffer = "";
812
+ try {
813
+ while (true) {
814
+ const { done, value } = await reader.read();
815
+ if (done) break;
816
+ buffer += decoder.decode(value, { stream: true });
817
+ const parts = buffer.split("\n\n");
818
+ buffer = parts.pop() || "";
819
+ for (const part of parts) {
820
+ const trimmedPart = part.trim();
821
+ if (!trimmedPart) continue;
822
+ let jsonStr = "";
823
+ for (const line of trimmedPart.split("\n")) {
824
+ if (line.startsWith("data: ")) {
825
+ jsonStr += line.slice(6);
826
+ } else if (line.startsWith("data:")) {
827
+ jsonStr += line.slice(5);
828
+ }
829
+ }
830
+ jsonStr = jsonStr.trim();
831
+ if (!jsonStr) continue;
832
+ let raw;
833
+ try {
834
+ raw = JSON.parse(jsonStr);
835
+ } catch {
836
+ continue;
837
+ }
838
+ const streamEvent = normalizeEvent(raw);
839
+ handleEvent(
840
+ streamEvent,
841
+ fullText,
842
+ callbacks,
843
+ clientTools,
844
+ onPaused,
845
+ onAutoResume,
846
+ pendingAutoResumes
847
+ );
848
+ if (streamEvent.type === "text_delta") {
849
+ fullText += streamEvent.data;
850
+ }
851
+ }
852
+ }
853
+ if (buffer.trim()) {
854
+ let jsonStr = "";
855
+ for (const line of buffer.trim().split("\n")) {
856
+ if (line.startsWith("data: ")) {
857
+ jsonStr += line.slice(6);
858
+ } else if (line.startsWith("data:")) {
859
+ jsonStr += line.slice(5);
860
+ }
861
+ }
862
+ jsonStr = jsonStr.trim();
863
+ if (jsonStr) {
864
+ try {
865
+ const raw = JSON.parse(jsonStr);
866
+ const streamEvent = normalizeEvent(raw);
867
+ handleEvent(
868
+ streamEvent,
869
+ fullText,
870
+ callbacks,
871
+ clientTools,
872
+ onPaused,
873
+ onAutoResume,
874
+ pendingAutoResumes
875
+ );
876
+ if (streamEvent.type === "text_delta") {
877
+ fullText += streamEvent.data;
878
+ }
879
+ } catch {
880
+ }
881
+ }
882
+ }
883
+ } catch (err) {
884
+ if (err instanceof Error && err.name === "AbortError") {
885
+ throw err;
886
+ }
887
+ const error = err instanceof Error ? err : new Error(String(err));
888
+ callbacks.onError?.(error);
889
+ throw error;
890
+ }
891
+ if (pendingAutoResumes.length > 0) {
892
+ const handlerResults = await Promise.allSettled(pendingAutoResumes);
893
+ for (const settled of handlerResults) {
894
+ if (settled.status === "fulfilled" && settled.value) {
895
+ const { callId, result } = settled.value;
896
+ await onAutoResume?.(callId, result);
897
+ }
898
+ }
899
+ }
900
+ callbacks.onComplete?.(fullText);
901
+ }
902
+ function normalizeEvent(raw) {
903
+ const typeMap = {
904
+ llm_text_delta: "text_delta",
905
+ llm_reasoning_delta: "reasoning_delta"
906
+ };
907
+ if (raw && typeof raw.type === "string" && typeMap[raw.type]) {
908
+ raw.type = typeMap[raw.type];
909
+ }
910
+ return raw;
911
+ }
912
+ function handleEvent(event, _fullText, callbacks, clientTools, onPaused, onAutoResume, pendingAutoResumes) {
913
+ callbacks.onEvent?.(event);
914
+ switch (event.type) {
915
+ case "text_delta": {
916
+ callbacks.onText?.(event.data);
917
+ break;
918
+ }
919
+ case "reasoning_delta": {
920
+ callbacks.onReasoning?.(event.data.delta, event.data.index);
921
+ break;
922
+ }
923
+ case "agent_handoff": {
924
+ callbacks.onAgentHandoff?.(event.data.agentName);
925
+ break;
926
+ }
927
+ case "tool_call_start": {
928
+ const { callId, toolName, toolType, inputs: rawInputs, paused } = event.data;
929
+ const inputs = tryParseJSON2(rawInputs);
930
+ callbacks.onToolStart?.(toolName, toolType);
931
+ if (toolType === "client") {
932
+ const tool = clientTools.get(toolName);
933
+ if (tool) {
934
+ if (paused && tool.handler) {
935
+ const handlerPromise = (async () => {
936
+ try {
937
+ const result = await tool.handler(inputs);
938
+ callbacks.onEvent?.({
939
+ type: "tool_call_end",
940
+ data: {
941
+ callId,
942
+ toolName,
943
+ toolType: "client",
944
+ result
945
+ },
946
+ meta: event.meta
947
+ });
948
+ return { callId, result };
949
+ } catch (error) {
950
+ callbacks.onPaused?.(toolName, inputs);
951
+ onPaused?.({ callId, toolName, args: inputs });
952
+ return null;
953
+ }
954
+ })();
955
+ pendingAutoResumes.push(handlerPromise);
956
+ } else if (paused) {
957
+ callbacks.onPaused?.(toolName, inputs);
958
+ onPaused?.({ callId, toolName, args: inputs });
959
+ } else if (tool.handler) {
960
+ try {
961
+ tool.handler(inputs);
962
+ } catch {
963
+ }
964
+ }
965
+ } else if (paused) {
966
+ callbacks.onPaused?.(toolName, inputs);
967
+ onPaused?.({ callId, toolName, args: inputs });
968
+ }
969
+ }
970
+ break;
971
+ }
972
+ case "tool_call_end": {
973
+ const { toolName, result, error } = event.data;
974
+ callbacks.onToolEnd?.(toolName, result, error);
975
+ break;
976
+ }
977
+ case "run_error": {
978
+ const { message, code } = event.data;
979
+ const error = new Error(message);
980
+ if (code != null) error.code = code;
981
+ callbacks.onError?.(error);
982
+ break;
983
+ }
984
+ }
985
+ }
986
+
987
+ // src/core/session.ts
988
+ var import_zod2 = require("zod");
989
+ var AgentSession = class {
990
+ /** @internal */
991
+ _agent;
992
+ /** @internal */
993
+ _sessionId;
994
+ /** @internal */
995
+ _paused = false;
996
+ /** @internal */
997
+ _pausedToolInfo = null;
998
+ /** @internal */
999
+ _abortController = null;
1000
+ /** @internal */
1001
+ constructor(agent, sessionId) {
1002
+ this._agent = agent;
1003
+ this._sessionId = sessionId;
1004
+ }
1005
+ // ─── Public API ────────────────────────────────────────────────────
1006
+ /**
1007
+ * Send a message in this session.
1008
+ *
1009
+ * @param message - The message to send
1010
+ * @param callbacks - Event handlers for the stream
1011
+ * @param callbacks - Event handlers for the stream
1012
+ * @param options - Optional settings like context, headers, or body
1013
+ * @returns This session (for chaining)
1014
+ */
1015
+ async execute(message, callbacks, options) {
1016
+ this._paused = false;
1017
+ this._pausedToolInfo = null;
1018
+ const body = {
1019
+ ...options?.body || {},
1020
+ input: message,
1021
+ stream: true
1022
+ };
1023
+ if (options?.context) {
1024
+ Object.assign(body, { context: options.context });
1025
+ }
1026
+ const clientToolDefs = this._getClientToolDefs();
1027
+ if (clientToolDefs.length > 0) {
1028
+ body.clientTools = clientToolDefs;
1029
+ }
1030
+ await this._run(body, callbacks, options);
1031
+ return this;
1032
+ }
1033
+ /**
1034
+ * Resume a paused session with a tool result.
1035
+ *
1036
+ * @param result - The result to send back to the agent
1037
+ * @param callbacks - Event handlers for the resumed stream
1038
+ * @param options - Optional settings like headers or body
1039
+ * @returns This session (for chaining)
1040
+ */
1041
+ async resume(result, callbacks, options) {
1042
+ if (!this._paused || !this._pausedToolInfo) {
1043
+ throw new Error("AgentSession.resume(): session is not paused. Check isPaused() first.");
1044
+ }
1045
+ const body = {
1046
+ ...options?.body || {},
1047
+ stream: true,
1048
+ toolCallResult: {
1049
+ callId: this._pausedToolInfo.callId,
1050
+ result
1051
+ }
1052
+ };
1053
+ const clientToolDefs = this._getClientToolDefs();
1054
+ if (clientToolDefs.length > 0) {
1055
+ body.clientTools = clientToolDefs;
1056
+ }
1057
+ this._paused = false;
1058
+ this._pausedToolInfo = null;
1059
+ await this._run(body, callbacks, options);
1060
+ return this;
1061
+ }
1062
+ /**
1063
+ * Resume a session with an explicit tool call ID and result.
1064
+ *
1065
+ * Unlike `resume()`, this does NOT require the session to be in a paused state,
1066
+ * making it suitable for external resume flows (e.g. React widget submissions)
1067
+ * where the session object may have been re-created.
1068
+ *
1069
+ * @param callId - The tool call ID to resume
1070
+ * @param result - The result to send back to the agent
1071
+ * @param callbacks - Event handlers for the resumed stream
1072
+ * @param options - Optional settings like headers or body
1073
+ * @returns This session (for chaining)
1074
+ */
1075
+ async resumeWithCallId(callId, result, callbacks, options) {
1076
+ const body = {
1077
+ ...options?.body || {},
1078
+ stream: true,
1079
+ toolCallResult: { callId, result }
1080
+ };
1081
+ const clientToolDefs = this._getClientToolDefs();
1082
+ if (clientToolDefs.length > 0) {
1083
+ body.clientTools = clientToolDefs;
1084
+ }
1085
+ this._paused = false;
1086
+ this._pausedToolInfo = null;
1087
+ await this._run(body, callbacks, options);
1088
+ return this;
1089
+ }
1090
+ /**
1091
+ * Check if this session is waiting for a tool result.
1092
+ */
1093
+ isPaused() {
1094
+ return this._paused;
1095
+ }
1096
+ /**
1097
+ * Get information about the paused tool call.
1098
+ * Returns `null` if the session is not paused.
1099
+ */
1100
+ getPausedTool() {
1101
+ return this._pausedToolInfo;
1102
+ }
1103
+ /**
1104
+ * Get the session ID.
1105
+ * May be `undefined` if the session hasn't executed yet.
1106
+ */
1107
+ getSessionId() {
1108
+ if (!this._sessionId) {
1109
+ throw new Error(
1110
+ "AgentSession.getSessionId(): session ID not yet available. Call execute() first."
1111
+ );
1112
+ }
1113
+ return this._sessionId;
1114
+ }
1115
+ /**
1116
+ * Cancel the current streaming operation.
1117
+ */
1118
+ abort() {
1119
+ this._abortController?.abort();
1120
+ this._abortController = null;
1121
+ }
1122
+ // ─── Private helpers ───────────────────────────────────────────────
1123
+ /** @internal */
1124
+ async _run(body, callbacks, options) {
1125
+ this._abortController = new AbortController();
1126
+ const baseHeaders = this._agent._buildHeaders(this._sessionId);
1127
+ const mergedHeaders = { ...baseHeaders, ...options?.headers || {} };
1128
+ await executeStream({
1129
+ url: this._agent._url,
1130
+ body,
1131
+ headers: mergedHeaders,
1132
+ callbacks,
1133
+ clientTools: this._agent._clientTools,
1134
+ signal: this._abortController.signal,
1135
+ onSessionId: (id, name) => {
1136
+ this._sessionId = id;
1137
+ options?.onSessionId?.(id, name);
1138
+ },
1139
+ onPaused: (info) => {
1140
+ this._paused = true;
1141
+ this._pausedToolInfo = info;
1142
+ },
1143
+ onAutoResume: async (callId, result) => {
1144
+ const resumeBody = {
1145
+ ...options?.body || {},
1146
+ stream: true,
1147
+ toolCallResult: { callId, result }
1148
+ };
1149
+ const clientToolDefs = this._getClientToolDefs();
1150
+ if (clientToolDefs.length > 0) {
1151
+ resumeBody.clientTools = clientToolDefs;
1152
+ }
1153
+ await this._run(resumeBody, callbacks, options);
1154
+ }
1155
+ });
1156
+ }
1157
+ /** @internal */
1158
+ _getClientToolDefs() {
1159
+ const defs = [];
1160
+ for (const tool of this._agent._clientTools.values()) {
1161
+ defs.push({
1162
+ name: tool.name,
1163
+ description: tool.description,
1164
+ parameters: resolveParameters2(tool.parameters),
1165
+ await: tool.await
1166
+ });
1167
+ }
1168
+ return defs;
1169
+ }
1170
+ };
1171
+ function resolveParameters2(params) {
1172
+ let schema;
1173
+ if (params && typeof params === "object" && "_def" in params) {
1174
+ schema = (0, import_zod2.toJSONSchema)(params);
1175
+ delete schema.$schema;
1176
+ } else {
1177
+ schema = { ...params };
1178
+ }
1179
+ if (schema.type === "object") {
1180
+ schema.additionalProperties = false;
1181
+ if (schema.properties) {
1182
+ schema.required = Object.keys(schema.properties);
1183
+ }
1184
+ }
1185
+ return schema;
1186
+ }
1187
+
1188
+ // src/core/agent.ts
1189
+ var DEFAULT_BASE_URL = "https://api.buildship.run";
1190
+ var BuildShipAgent = class {
1191
+ /** @internal */
1192
+ _agentId;
1193
+ /** @internal */
1194
+ _accessKey;
1195
+ /** @internal */
1196
+ _baseUrl;
1197
+ /** @internal */
1198
+ _clientTools = /* @__PURE__ */ new Map();
1199
+ constructor(config) {
1200
+ if (!config.agentId) {
1201
+ throw new Error("BuildShipAgent: agentId is required");
1202
+ }
1203
+ this._agentId = config.agentId;
1204
+ this._accessKey = config.accessKey;
1205
+ this._baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
1206
+ }
1207
+ /**
1208
+ * The URL for the agent's execute endpoint.
1209
+ * @internal
1210
+ */
1211
+ get _url() {
1212
+ return `${this._baseUrl}/executeAgent/${this._agentId}`;
1213
+ }
1214
+ /**
1215
+ * Build the authorization / common headers.
1216
+ * @internal
1217
+ */
1218
+ _buildHeaders(sessionId) {
1219
+ const headers = {};
1220
+ if (this._accessKey) {
1221
+ headers["Authorization"] = `Bearer ${this._accessKey}`;
1222
+ }
1223
+ if (sessionId) {
1224
+ headers["X-BuildShip-Agent-Session-ID"] = sessionId;
1225
+ }
1226
+ return headers;
1227
+ }
1228
+ // ─── Public API ────────────────────────────────────────────────────
1229
+ /**
1230
+ * Start a new conversation.
1231
+ *
1232
+ * Creates a fresh session and sends the first message.
1233
+ *
1234
+ * @param message - The message to send
1235
+ * @param callbacks - Event handlers for the stream
1236
+ * @param options - Optional execution settings (context, headers, body, onSessionId)
1237
+ * @returns The new session
1238
+ */
1239
+ async execute(message, callbacks, options) {
1240
+ const session = new AgentSession(this);
1241
+ await session.execute(message, callbacks, options);
1242
+ return session;
1243
+ }
1244
+ /**
1245
+ * Get an existing session by ID to continue a conversation.
1246
+ *
1247
+ * @param sessionId - The session ID from a previous conversation
1248
+ * @returns The session object
1249
+ */
1250
+ session(sessionId) {
1251
+ if (!sessionId) {
1252
+ throw new Error("BuildShipAgent.session(): sessionId is required");
1253
+ }
1254
+ return new AgentSession(this, sessionId);
1255
+ }
1256
+ /**
1257
+ * Register a client-side tool that the agent can call.
1258
+ */
1259
+ registerClientTool(tool) {
1260
+ if (!tool.name) {
1261
+ throw new Error("registerClientTool: tool.name is required");
1262
+ }
1263
+ this._clientTools.set(tool.name, tool);
1264
+ }
1265
+ /**
1266
+ * Remove a registered client tool.
1267
+ */
1268
+ unregisterClientTool(name) {
1269
+ this._clientTools.delete(name);
1270
+ }
1271
+ };
1272
+
1022
1273
  // src/react/utils/use-synced-local-storage.ts
1023
1274
  var import_react4 = require("react");
1024
1275
  function parseJSON(value, defaultValue) {
@@ -1088,16 +1339,21 @@ function AgentContextProvider({ children }) {
1088
1339
  AGENT_DEBUG_DATA_KEY,
1089
1340
  {}
1090
1341
  );
1091
- const initializeAgent = (0, import_react5.useCallback)((agentId, agentUrl, accessKey) => {
1092
- const existing = activeAgentsRef.current.get(agentId);
1093
- if (!existing) {
1094
- activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
1095
- forceUpdate({});
1096
- } else if (existing.agentUrl !== agentUrl || existing.accessKey !== accessKey) {
1097
- activeAgentsRef.current.set(agentId, { agentUrl, accessKey });
1098
- forceUpdate({});
1099
- }
1100
- }, []);
1342
+ const initializeAgent = (0, import_react5.useCallback)(
1343
+ (agentId, agentUrl, accessKey, options) => {
1344
+ const existing = activeAgentsRef.current.get(agentId);
1345
+ if (!existing) {
1346
+ activeAgentsRef.current.set(agentId, { agentUrl, accessKey, options });
1347
+ forceUpdate({});
1348
+ } else if (existing.agentUrl !== agentUrl || existing.accessKey !== accessKey) {
1349
+ activeAgentsRef.current.set(agentId, { agentUrl, accessKey, options });
1350
+ forceUpdate({});
1351
+ } else if (options) {
1352
+ existing.options = options;
1353
+ }
1354
+ },
1355
+ []
1356
+ );
1101
1357
  const registerRunner = (0, import_react5.useCallback)((agentId, runner) => {
1102
1358
  runnersRef.current.set(agentId, runner);
1103
1359
  const listeners = listenersRef.current.get(agentId);
@@ -1166,41 +1422,53 @@ function AgentContextProvider({ children }) {
1166
1422
  );
1167
1423
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentContext.Provider, { value: agentContextValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AgentToolContext.Provider, { value: toolContextValue, children: [
1168
1424
  children,
1169
- Array.from(activeAgentsRef.current.entries()).map(([agentId, { agentUrl, accessKey }]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1170
- AgentRunnerInstance,
1171
- {
1172
- agentId,
1173
- agentUrl,
1174
- accessKey
1175
- },
1176
- agentId
1177
- ))
1425
+ Array.from(activeAgentsRef.current.entries()).map(
1426
+ ([agentId, { agentUrl, accessKey, options }]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1427
+ AgentRunnerInstance,
1428
+ {
1429
+ agentId,
1430
+ agentUrl,
1431
+ accessKey,
1432
+ options
1433
+ },
1434
+ agentId
1435
+ )
1436
+ )
1178
1437
  ] }) });
1179
1438
  }
1180
1439
  function AgentRunnerInstance({
1181
1440
  agentId,
1182
1441
  agentUrl,
1183
- accessKey
1442
+ accessKey,
1443
+ options
1184
1444
  }) {
1185
- const agent = useAgent(agentId, agentUrl, accessKey);
1445
+ const agent = (0, import_react5.useMemo)(
1446
+ () => new BuildShipAgent({ agentId, baseUrl: agentUrl, accessKey }),
1447
+ [agentId, agentUrl, accessKey]
1448
+ );
1449
+ const optionsRef = (0, import_react5.useRef)(options);
1450
+ optionsRef.current = options;
1451
+ const agentRunner = useAgent(agent, optionsRef.current);
1186
1452
  const context = (0, import_react5.useContext)(AgentContext);
1187
1453
  (0, import_react5.useEffect)(() => {
1188
1454
  if (context) {
1189
- context.registerRunner(agentId, agent);
1455
+ context.registerRunner(agentId, agentRunner);
1190
1456
  }
1191
- }, [agentId, agent, context]);
1457
+ }, [agentId, agentRunner, context]);
1192
1458
  if (!context) return null;
1193
1459
  return null;
1194
1460
  }
1195
- function useAgentContext(agentId, agentUrl, accessKey) {
1461
+ function useAgentContext(agentId, agentUrl, accessKey, options) {
1196
1462
  const context = (0, import_react5.useContext)(AgentContext);
1197
1463
  if (!context) {
1198
1464
  throw new Error("useAgentContext must be used within AgentContextProvider");
1199
1465
  }
1200
1466
  const { initializeAgent, getRunner, listenersRef } = context;
1467
+ const optionsRef = (0, import_react5.useRef)(options);
1468
+ optionsRef.current = options;
1201
1469
  (0, import_react5.useEffect)(() => {
1202
- initializeAgent(agentId, agentUrl, accessKey);
1203
- }, [agentId, agentUrl, initializeAgent]);
1470
+ initializeAgent(agentId, agentUrl, accessKey, optionsRef.current);
1471
+ }, [agentId, agentUrl, accessKey, initializeAgent]);
1204
1472
  const [runner, setRunner] = (0, import_react5.useState)(() => getRunner(agentId));
1205
1473
  (0, import_react5.useEffect)(() => {
1206
1474
  const currentRunner = getRunner(agentId);