@copilotkit/vue 1.70.3 → 1.71.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"use-human-in-the-loop.d.ts","sourceRoot":"","sources":["../../../src/v2/hooks/use-human-in-the-loop.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAIvC,OAAO,KAAK,EAEV,iBAAiB,EAGlB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,QA4D9B"}
1
+ {"version":3,"file":"use-human-in-the-loop.d.ts","sourceRoot":"","sources":["../../../src/v2/hooks/use-human-in-the-loop.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAKvC,OAAO,KAAK,EAEV,iBAAiB,EAGlB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,QA4F9B"}
package/dist/v2/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@copilotkit/core"),i=require("@ag-ui/client"),e=require("../use-render-activity-message-CyThomMH.cjs"),n=require("@copilotkit/shared");exports.A2UIActivityContentSchema=e.A2UIActivityContentSchema;exports.A2UISurfaceActivityRenderer=e._sfc_main;exports.A2UISurfaceActivityType=e.A2UISurfaceActivityType;exports.CopilotChat=e.CopilotChat;exports.CopilotChatAssistantMessage=e._sfc_main$1;exports.CopilotChatAttachmentQueue=e._sfc_main$2;exports.CopilotChatAttachmentRenderer=e._sfc_main$3;exports.CopilotChatAudioRecorder=e._sfc_main$4;exports.CopilotChatConfigurationProvider=e._sfc_main$5;exports.CopilotChatDefaultLabels=e.CopilotChatDefaultLabels;exports.CopilotChatInput=e._sfc_main$6;exports.CopilotChatMessageView=e._sfc_main$7;exports.CopilotChatReasoningMessage=e._sfc_main$8;exports.CopilotChatSuggestionPill=e._sfc_main$9;exports.CopilotChatSuggestionView=e._sfc_main$10;exports.CopilotChatToggleButton=e.CopilotChatToggleButton;exports.CopilotChatToggleButtonCloseIcon=e.CopilotChatToggleButtonCloseIcon;exports.CopilotChatToggleButtonOpenIcon=e.CopilotChatToggleButtonOpenIcon;exports.CopilotChatToolCallsView=e._sfc_main$11;exports.CopilotChatUserMessage=e._sfc_main$12;exports.CopilotChatView=e._sfc_main$13;exports.CopilotKitCoreVue=e.CopilotKitCoreVue;exports.CopilotKitInspector=e._sfc_main$14;exports.CopilotKitProvider=e._sfc_main$15;exports.CopilotModalHeader=e.CopilotModalHeader;exports.CopilotPopup=e._sfc_main$16;exports.CopilotPopupView=e.CopilotPopupView;exports.CopilotSidebar=e._sfc_main$17;exports.CopilotSidebarView=e.CopilotSidebarView;exports.CopilotThreadsDrawer=e._sfc_main$18;exports.GenerateSandboxedUiArgsSchema=e.GenerateSandboxedUiArgsSchema;exports.LicenseContextKey=e.LicenseContextKey;exports.MCPAppsActivityContentSchema=e.MCPAppsActivityContentSchema;exports.MCPAppsActivityRenderer=e.MCPAppsActivityRenderer;exports.MCPAppsActivityType=e.MCPAppsActivityType;exports.OpenGenerativeUIActivityRenderer=e.OpenGenerativeUIActivityRenderer;exports.OpenGenerativeUIActivityType=e.OpenGenerativeUIActivityType;exports.OpenGenerativeUIContentSchema=e.OpenGenerativeUIContentSchema;exports.OpenGenerativeUIRenderer=e.OpenGenerativeUIRenderer;exports.OpenGenerativeUIToolRenderer=e.OpenGenerativeUIToolRenderer;exports.UseAgentUpdate=e.UseAgentUpdate;exports.createA2UIMessageRenderer=e.createA2UIMessageRenderer;exports.createDefaultLicenseRef=e.createDefaultLicenseRef;exports.defineToolCallRenderer=e.defineToolCallRenderer;exports.extractCompleteStyles=e.extractCompleteStyles;exports.getOperationSurfaceId=e.getOperationSurfaceId;exports.processPartialHtml=e.processPartialHtml;exports.useAgent=e.useAgent;exports.useAgentContext=e.useAgentContext;exports.useAttachments=e.useAttachments;exports.useCapabilities=e.useCapabilities;exports.useComponent=e.useComponent;exports.useConfigureSuggestions=e.useConfigureSuggestions;exports.useCopilotChatConfiguration=e.useCopilotChatConfiguration;exports.useCopilotKit=e.useCopilotKit;exports.useDefaultRenderTool=e.useDefaultRenderTool;exports.useFrontendTool=e.useFrontendTool;exports.useHumanInTheLoop=e.useHumanInTheLoop;exports.useInterrupt=e.useInterrupt;exports.useKatexStyles=e.useKatexStyles;exports.useKeyboardHeight=e.useKeyboardHeight;exports.useLicenseContext=e.useLicenseContext;exports.useRenderActivityMessage=e.useRenderActivityMessage;exports.useRenderCustomMessages=e.useRenderCustomMessages;exports.useRenderTool=e.useRenderTool;exports.useSandboxFunctions=e.useSandboxFunctions;exports.useSuggestions=e.useSuggestions;exports.useThreads=e.useThreads;exports.vueBasicCatalog=e.vueBasicCatalog;Object.defineProperty(exports,"createLicenseContextValue",{enumerable:!0,get:()=>n.createLicenseContextValue});Object.keys(o).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>o[t]})});Object.keys(i).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>i[t]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@copilotkit/core"),i=require("@ag-ui/client"),e=require("../use-render-activity-message-DXJDZGkW.cjs"),n=require("@copilotkit/shared");exports.A2UIActivityContentSchema=e.A2UIActivityContentSchema;exports.A2UISurfaceActivityRenderer=e._sfc_main;exports.A2UISurfaceActivityType=e.A2UISurfaceActivityType;exports.CopilotChat=e.CopilotChat;exports.CopilotChatAssistantMessage=e._sfc_main$1;exports.CopilotChatAttachmentQueue=e._sfc_main$2;exports.CopilotChatAttachmentRenderer=e._sfc_main$3;exports.CopilotChatAudioRecorder=e._sfc_main$4;exports.CopilotChatConfigurationProvider=e._sfc_main$5;exports.CopilotChatDefaultLabels=e.CopilotChatDefaultLabels;exports.CopilotChatInput=e._sfc_main$6;exports.CopilotChatMessageView=e._sfc_main$7;exports.CopilotChatReasoningMessage=e._sfc_main$8;exports.CopilotChatSuggestionPill=e._sfc_main$9;exports.CopilotChatSuggestionView=e._sfc_main$10;exports.CopilotChatToggleButton=e.CopilotChatToggleButton;exports.CopilotChatToggleButtonCloseIcon=e.CopilotChatToggleButtonCloseIcon;exports.CopilotChatToggleButtonOpenIcon=e.CopilotChatToggleButtonOpenIcon;exports.CopilotChatToolCallsView=e._sfc_main$11;exports.CopilotChatUserMessage=e._sfc_main$12;exports.CopilotChatView=e._sfc_main$13;exports.CopilotKitCoreVue=e.CopilotKitCoreVue;exports.CopilotKitInspector=e._sfc_main$14;exports.CopilotKitProvider=e._sfc_main$15;exports.CopilotModalHeader=e.CopilotModalHeader;exports.CopilotPopup=e._sfc_main$16;exports.CopilotPopupView=e.CopilotPopupView;exports.CopilotSidebar=e._sfc_main$17;exports.CopilotSidebarView=e.CopilotSidebarView;exports.CopilotThreadsDrawer=e._sfc_main$18;exports.GenerateSandboxedUiArgsSchema=e.GenerateSandboxedUiArgsSchema;exports.LicenseContextKey=e.LicenseContextKey;exports.MCPAppsActivityContentSchema=e.MCPAppsActivityContentSchema;exports.MCPAppsActivityRenderer=e.MCPAppsActivityRenderer;exports.MCPAppsActivityType=e.MCPAppsActivityType;exports.OpenGenerativeUIActivityRenderer=e.OpenGenerativeUIActivityRenderer;exports.OpenGenerativeUIActivityType=e.OpenGenerativeUIActivityType;exports.OpenGenerativeUIContentSchema=e.OpenGenerativeUIContentSchema;exports.OpenGenerativeUIRenderer=e.OpenGenerativeUIRenderer;exports.OpenGenerativeUIToolRenderer=e.OpenGenerativeUIToolRenderer;exports.UseAgentUpdate=e.UseAgentUpdate;exports.createA2UIMessageRenderer=e.createA2UIMessageRenderer;exports.createDefaultLicenseRef=e.createDefaultLicenseRef;exports.defineToolCallRenderer=e.defineToolCallRenderer;exports.extractCompleteStyles=e.extractCompleteStyles;exports.getOperationSurfaceId=e.getOperationSurfaceId;exports.processPartialHtml=e.processPartialHtml;exports.useAgent=e.useAgent;exports.useAgentContext=e.useAgentContext;exports.useAttachments=e.useAttachments;exports.useCapabilities=e.useCapabilities;exports.useComponent=e.useComponent;exports.useConfigureSuggestions=e.useConfigureSuggestions;exports.useCopilotChatConfiguration=e.useCopilotChatConfiguration;exports.useCopilotKit=e.useCopilotKit;exports.useDefaultRenderTool=e.useDefaultRenderTool;exports.useFrontendTool=e.useFrontendTool;exports.useHumanInTheLoop=e.useHumanInTheLoop;exports.useInterrupt=e.useInterrupt;exports.useKatexStyles=e.useKatexStyles;exports.useKeyboardHeight=e.useKeyboardHeight;exports.useLicenseContext=e.useLicenseContext;exports.useRenderActivityMessage=e.useRenderActivityMessage;exports.useRenderCustomMessages=e.useRenderCustomMessages;exports.useRenderTool=e.useRenderTool;exports.useSandboxFunctions=e.useSandboxFunctions;exports.useSuggestions=e.useSuggestions;exports.useThreads=e.useThreads;exports.vueBasicCatalog=e.vueBasicCatalog;Object.defineProperty(exports,"createLicenseContextValue",{enumerable:!0,get:()=>n.createLicenseContextValue});Object.keys(o).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>o[t]})});Object.keys(i).forEach(t=>{t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:()=>i[t]})});
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/v2/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "@copilotkit/core";
2
2
  export * from "@ag-ui/client";
3
- import { A as o, _ as i, a as n, C, b as r, c as p, d as l, e as u, f as c, g, h as d, i as h, j as A, k as v, l as S, m, n as y, o as I, p as f, q as R, r as T, s as U, t as x, u as M, v as P, w as b, x as V, y as w, z as K, B as O, G, L, M as B, D, E as H, O as F, F as Q, H as j, I as k, J as q, U as z, K as E, N as J, P as N, Q as W, R as X, S as Y, T as Z, V as _, W as $, X as ee, Y as ae, Z as te, $ as se, a0 as oe, a1 as ie, a2 as ne, a3 as Ce, a4 as re, a5 as pe, a6 as le, a7 as ue, a8 as ce, a9 as ge, aa as de, ab as he, ac as Ae, ad as ve, ae as Se } from "../use-render-activity-message-WeN5iF5H.js";
3
+ import { A as o, _ as i, a as n, C, b as r, c as p, d as l, e as u, f as c, g, h as d, i as h, j as A, k as v, l as S, m, n as y, o as I, p as f, q as R, r as T, s as U, t as x, u as M, v as P, w as b, x as V, y as w, z as K, B as O, G, L, M as B, D, E as H, O as F, F as Q, H as j, I as k, J as q, U as z, K as E, N as J, P as N, Q as W, R as X, S as Y, T as Z, V as _, W as $, X as ee, Y as ae, Z as te, $ as se, a0 as oe, a1 as ie, a2 as ne, a3 as Ce, a4 as re, a5 as pe, a6 as le, a7 as ue, a8 as ce, a9 as ge, aa as de, ab as he, ac as Ae, ad as ve, ae as Se } from "../use-render-activity-message-Ciee03Rx.js";
4
4
  import { createLicenseContextValue as ye } from "@copilotkit/shared";
5
5
  export {
6
6
  o as A2UIActivityContentSchema,
@@ -3,6 +3,8 @@ import type { Component, VNodeChild } from "vue";
3
3
  export type VueHumanInTheLoopRenderProps<T> = {
4
4
  name: string;
5
5
  description: string;
6
+ toolCallId: string;
7
+ agentId?: string;
6
8
  args: Partial<T>;
7
9
  status: ToolCallStatus.InProgress;
8
10
  result: undefined;
@@ -10,6 +12,8 @@ export type VueHumanInTheLoopRenderProps<T> = {
10
12
  } | {
11
13
  name: string;
12
14
  description: string;
15
+ toolCallId: string;
16
+ agentId?: string;
13
17
  args: T;
14
18
  status: ToolCallStatus.Executing;
15
19
  result: undefined;
@@ -17,6 +21,8 @@ export type VueHumanInTheLoopRenderProps<T> = {
17
21
  } | {
18
22
  name: string;
19
23
  description: string;
24
+ toolCallId: string;
25
+ agentId?: string;
20
26
  args: T;
21
27
  status: ToolCallStatus.Complete;
22
28
  result: string;
@@ -1 +1 @@
1
- {"version":3,"file":"human-in-the-loop.d.ts","sourceRoot":"","sources":["../../../src/v2/types/human-in-the-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEjD,MAAM,MAAM,4BAA4B,CAAC,CAAC,IACtC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC;IAClC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,SAAS,CAAC;CACpB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC;AAEN,MAAM,MAAM,yBAAyB,CAAC,CAAC,IACnC,CAAC,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,GACxD,SAAS,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/C,MAAM,MAAM,iBAAiB,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG;IACrC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC"}
1
+ {"version":3,"file":"human-in-the-loop.d.ts","sourceRoot":"","sources":["../../../src/v2/types/human-in-the-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEjD,MAAM,MAAM,4BAA4B,CAAC,CAAC,IACtC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC;IAClC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,SAAS,CAAC;CACpB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC;AAEN,MAAM,MAAM,yBAAyB,CAAC,CAAC,IACnC,CAAC,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,GACxD,SAAS,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/C,MAAM,MAAM,iBAAiB,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzD,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG;IACrC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC,CAAC;CACtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/vue",
3
- "version": "1.70.3",
3
+ "version": "1.71.0",
4
4
  "private": false,
5
5
  "description": "Vue 3 components and composables for CopilotKit",
6
6
  "keywords": [
@@ -70,10 +70,10 @@
70
70
  "streamdown-vue": "^1.0.29",
71
71
  "zod": "^3.25.75",
72
72
  "zod-to-json-schema": "^3.24.5",
73
- "@copilotkit/core": "1.70.3",
74
- "@copilotkit/shared": "1.70.3",
75
- "@copilotkit/web-components": "1.70.3",
76
- "@copilotkit/web-inspector": "1.70.3"
73
+ "@copilotkit/core": "1.71.0",
74
+ "@copilotkit/web-components": "1.71.0",
75
+ "@copilotkit/shared": "1.71.0",
76
+ "@copilotkit/web-inspector": "1.71.0"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@eslint/js": "^9.39.2",
@@ -8,6 +8,7 @@ import { ToolCallStatus } from "@copilotkit/core";
8
8
  import CopilotChat from "../../components/chat/CopilotChat.vue";
9
9
  import CopilotChatToolCallsView from "../../components/chat/CopilotChatToolCallsView.vue";
10
10
  import { useHumanInTheLoop } from "../use-human-in-the-loop";
11
+ import { useCopilotKit } from "../../providers/useCopilotKit";
11
12
  import {
12
13
  MockStepwiseAgent,
13
14
  MockReconnectableAgent,
@@ -843,6 +844,257 @@ describe("useHumanInTheLoop E2E - HITL Tool Rendering", () => {
843
844
  });
844
845
  });
845
846
  });
847
+
848
+ describe("HITL attribution (toolCallId + agentId)", () => {
849
+ it("exposes toolCallId and the registered agentId to the render props", async () => {
850
+ const agent = new MockStepwiseAgent();
851
+ let renderedToolCallId: string | undefined;
852
+ let renderedAgentId: string | undefined;
853
+
854
+ const AttributionRenderer = defineComponent({
855
+ props: {
856
+ toolCallId: { type: String, required: true },
857
+ agentId: { type: String, required: false },
858
+ status: { type: String as PropType<ToolCallStatus>, required: true },
859
+ args: {
860
+ type: Object as PropType<{ action?: string }>,
861
+ required: true,
862
+ },
863
+ },
864
+ setup(props) {
865
+ watch(
866
+ () => [props.toolCallId, props.agentId],
867
+ () => {
868
+ renderedToolCallId = props.toolCallId;
869
+ renderedAgentId = props.agentId;
870
+ },
871
+ { immediate: true },
872
+ );
873
+ return {};
874
+ },
875
+ template: `
876
+ <div data-testid="attr-hitl">
877
+ <div data-testid="attr-tool-call-id">{{ toolCallId }}</div>
878
+ <div data-testid="attr-agent-id">{{ agentId ?? "none" }}</div>
879
+ <div data-testid="attr-status">{{ status }}</div>
880
+ <div data-testid="attr-action">{{ args.action ?? "" }}</div>
881
+ </div>
882
+ `,
883
+ });
884
+
885
+ const AttributionHITLComponent = defineComponent({
886
+ setup() {
887
+ useHumanInTheLoop({
888
+ name: "scopedApprovalTool",
889
+ description: "Scoped approval",
890
+ agentId: "research-agent",
891
+ parameters: z.object({ action: z.string() }),
892
+ render: AttributionRenderer,
893
+ });
894
+ return {};
895
+ },
896
+ template: `<div />`,
897
+ });
898
+
899
+ renderWithCopilotKit({
900
+ agent,
901
+ agentId: "research-agent",
902
+ children: createChatHost(AttributionHITLComponent),
903
+ });
904
+
905
+ await submitMessage("Need approval");
906
+ const messageId = testId("msg");
907
+ const toolCallId = testId("tc");
908
+ await agent.emit(runStartedEvent());
909
+ await agent.emit(
910
+ toolCallChunkEvent({
911
+ toolCallId,
912
+ toolCallName: "scopedApprovalTool",
913
+ parentMessageId: messageId,
914
+ delta: JSON.stringify({ action: "delete" }),
915
+ }),
916
+ );
917
+
918
+ await waitFor(() => {
919
+ expect(screen.getByTestId("attr-tool-call-id").textContent).toBe(
920
+ toolCallId,
921
+ );
922
+ expect(screen.getByTestId("attr-agent-id").textContent).toBe(
923
+ "research-agent",
924
+ );
925
+ });
926
+ expect(renderedToolCallId).toBe(toolCallId);
927
+ expect(renderedAgentId).toBe("research-agent");
928
+ });
929
+
930
+ it("leaves agentId undefined for an unscoped HITL tool", async () => {
931
+ const agent = new MockStepwiseAgent();
932
+ const UnscopedRenderer = defineComponent({
933
+ props: {
934
+ toolCallId: { type: String, required: true },
935
+ agentId: { type: String, required: false },
936
+ status: { type: String as PropType<ToolCallStatus>, required: true },
937
+ args: {
938
+ type: Object as PropType<{ action?: string }>,
939
+ required: true,
940
+ },
941
+ },
942
+ template: `
943
+ <div data-testid="unscoped-hitl">
944
+ <div data-testid="unscoped-tool-call-id">{{ toolCallId }}</div>
945
+ <div data-testid="unscoped-agent-id">{{ agentId ?? "none" }}</div>
946
+ <div data-testid="unscoped-status">{{ status }}</div>
947
+ <div data-testid="unscoped-action">{{ args.action ?? "" }}</div>
948
+ </div>
949
+ `,
950
+ });
951
+ const UnscopedHITLComponent = defineComponent({
952
+ setup() {
953
+ useHumanInTheLoop({
954
+ name: "unscopedApprovalTool",
955
+ description: "Unscoped approval",
956
+ parameters: z.object({ action: z.string() }),
957
+ render: UnscopedRenderer,
958
+ });
959
+ return {};
960
+ },
961
+ template: `<div />`,
962
+ });
963
+
964
+ renderWithCopilotKit({
965
+ agent,
966
+ children: createChatHost(UnscopedHITLComponent),
967
+ });
968
+ await submitMessage("Need approval");
969
+ const messageId = testId("msg");
970
+ const toolCallId = testId("tc");
971
+ await agent.emit(runStartedEvent());
972
+ await agent.emit(
973
+ toolCallChunkEvent({
974
+ toolCallId,
975
+ toolCallName: "unscopedApprovalTool",
976
+ parentMessageId: messageId,
977
+ delta: JSON.stringify({ action: "archive" }),
978
+ }),
979
+ );
980
+
981
+ await waitFor(() => {
982
+ expect(screen.getByTestId("unscoped-status").textContent).toBe(
983
+ ToolCallStatus.InProgress,
984
+ );
985
+ expect(screen.getByTestId("unscoped-tool-call-id").textContent).toBe(
986
+ toolCallId,
987
+ );
988
+ expect(screen.getByTestId("unscoped-agent-id").textContent).toBe(
989
+ "none",
990
+ );
991
+ });
992
+ await agent.emit(runFinishedEvent());
993
+ await agent.complete();
994
+ await waitFor(() => {
995
+ expect(screen.getByTestId("unscoped-status").textContent).toBe(
996
+ ToolCallStatus.Executing,
997
+ );
998
+ expect(screen.getByTestId("unscoped-tool-call-id").textContent).toBe(
999
+ toolCallId,
1000
+ );
1001
+ expect(screen.getByTestId("unscoped-agent-id").textContent).toBe(
1002
+ "none",
1003
+ );
1004
+ });
1005
+ });
1006
+ });
1007
+ });
1008
+
1009
+ describe("HITL Run Abort", () => {
1010
+ it("rejects the pending HITL promise with an error tool result when the run is aborted", async () => {
1011
+ const agent = new MockStepwiseAgent();
1012
+ const toolExecutionEnds: Array<{ result: string; error?: string }> = [];
1013
+ let stopRun: (() => void) | undefined;
1014
+
1015
+ const AbortHITLComponent = defineComponent({
1016
+ setup() {
1017
+ const { copilotkit } = useCopilotKit();
1018
+ stopRun = () =>
1019
+ copilotkit.value.stopAgent({
1020
+ agent: copilotkit.value.getAgent("default")!,
1021
+ });
1022
+ copilotkit.value.subscribe({
1023
+ onToolExecutionEnd: ({ result, error }) => {
1024
+ toolExecutionEnds.push({ result, error });
1025
+ },
1026
+ });
1027
+ useHumanInTheLoop({
1028
+ name: "abortApprovalTool",
1029
+ description: "Requires human approval",
1030
+ parameters: z.object({ action: z.string() }),
1031
+ render: defineComponent({
1032
+ props: {
1033
+ status: {
1034
+ type: String as PropType<ToolCallStatus>,
1035
+ required: true,
1036
+ },
1037
+ args: {
1038
+ type: Object as PropType<{ action?: string }>,
1039
+ required: true,
1040
+ },
1041
+ respond: {
1042
+ type: Function as PropType<
1043
+ ((result: unknown) => Promise<void>) | undefined
1044
+ >,
1045
+ required: false,
1046
+ },
1047
+ },
1048
+ template: `
1049
+ <div data-testid="abort-hitl">
1050
+ <div data-testid="abort-status">{{ status }}</div>
1051
+ <div data-testid="abort-action">{{ args.action ?? "" }}</div>
1052
+ </div>
1053
+ `,
1054
+ }),
1055
+ });
1056
+ return {};
1057
+ },
1058
+ template: `<div />`,
1059
+ });
1060
+
1061
+ renderWithCopilotKit({
1062
+ agent,
1063
+ children: createChatHost(AbortHITLComponent),
1064
+ });
1065
+ await submitMessage("Request approval");
1066
+ const messageId = testId("msg");
1067
+ const toolCallId = testId("tc");
1068
+ await agent.emit(runStartedEvent());
1069
+ await agent.emit(
1070
+ toolCallChunkEvent({
1071
+ toolCallId,
1072
+ toolCallName: "abortApprovalTool",
1073
+ parentMessageId: messageId,
1074
+ delta: JSON.stringify({ action: "delete" }),
1075
+ }),
1076
+ );
1077
+ await waitFor(() => {
1078
+ expect(screen.getByTestId("abort-status").textContent).toBe(
1079
+ ToolCallStatus.InProgress,
1080
+ );
1081
+ });
1082
+ await agent.emit(runFinishedEvent());
1083
+ await agent.complete();
1084
+ await waitFor(() => {
1085
+ expect(screen.getByTestId("abort-status").textContent).toBe(
1086
+ ToolCallStatus.Executing,
1087
+ );
1088
+ });
1089
+
1090
+ stopRun?.();
1091
+ await waitFor(() => {
1092
+ expect(toolExecutionEnds.length).toBeGreaterThan(0);
1093
+ const last = toolExecutionEnds[toolExecutionEnds.length - 1];
1094
+ expect(last.error).toBeDefined();
1095
+ expect(last.error).not.toBe("");
1096
+ });
1097
+ });
846
1098
  });
847
1099
 
848
1100
  describe("HITL Thread Reconnection Bug", () => {
@@ -58,6 +58,7 @@ describe("useHumanInTheLoop", () => {
58
58
 
59
59
  const executingVNode = renderer!.render({
60
60
  name: "approveTool",
61
+ toolCallId: "tc-approve",
61
62
  args: { question: "Proceed?" },
62
63
  status: "executing",
63
64
  result: undefined,
@@ -112,6 +113,7 @@ describe("useHumanInTheLoop", () => {
112
113
  const respondA = (
113
114
  renderA!.render({
114
115
  name: "toolA",
116
+ toolCallId: "tc-a",
115
117
  args: {},
116
118
  status: "executing",
117
119
  result: undefined,
@@ -122,6 +124,7 @@ describe("useHumanInTheLoop", () => {
122
124
  const respondB = (
123
125
  renderB!.render({
124
126
  name: "toolB",
127
+ toolCallId: "tc-b",
125
128
  args: {},
126
129
  status: "executing",
127
130
  result: undefined,
@@ -163,6 +166,7 @@ describe("useHumanInTheLoop", () => {
163
166
 
164
167
  const inProgress = renderer!.render({
165
168
  name: "approveTool",
169
+ toolCallId: "tc-status",
166
170
  args: {},
167
171
  status: "inProgress",
168
172
  result: undefined,
@@ -171,6 +175,7 @@ describe("useHumanInTheLoop", () => {
171
175
  };
172
176
  const executing = renderer!.render({
173
177
  name: "approveTool",
178
+ toolCallId: "tc-status",
174
179
  args: {},
175
180
  status: "executing",
176
181
  result: undefined,
@@ -179,6 +184,7 @@ describe("useHumanInTheLoop", () => {
179
184
  };
180
185
  const complete = renderer!.render({
181
186
  name: "approveTool",
187
+ toolCallId: "tc-status",
182
188
  args: {},
183
189
  status: "complete",
184
190
  result: "ok",
@@ -279,4 +285,245 @@ describe("useHumanInTheLoop", () => {
279
285
  expect(getCore().getTool({ toolName: "depHitl" })).toBeDefined();
280
286
  addToolSpy.mockRestore();
281
287
  });
288
+
289
+ describe("Vue-specific HITL lifecycle semantics", () => {
290
+ function mountHitlTool(agentId?: string) {
291
+ const RenderComp = defineComponent({
292
+ setup() {
293
+ return () => h("div");
294
+ },
295
+ });
296
+ const Child = defineComponent({
297
+ setup() {
298
+ useHumanInTheLoop({
299
+ name: "lifecycleTool",
300
+ description: "Lifecycle description",
301
+ agentId,
302
+ parameters: z.object({ value: z.string() }),
303
+ render: RenderComp,
304
+ });
305
+ return () => null;
306
+ },
307
+ });
308
+ const mounted = mountWithProvider(() => h(Child));
309
+ const core = mounted.getCore();
310
+ return {
311
+ ...mounted,
312
+ handler: core.getTool({ toolName: "lifecycleTool", agentId })!.handler!,
313
+ renderer: core.renderToolCalls.find(
314
+ (renderTool) =>
315
+ renderTool.name === "lifecycleTool" &&
316
+ renderTool.agentId === agentId,
317
+ )!,
318
+ };
319
+ }
320
+
321
+ it("rejects an already-aborted signal with the exact HITL error", async () => {
322
+ const { handler } = mountHitlTool();
323
+ const controller = new AbortController();
324
+ controller.abort();
325
+
326
+ await expect(handler!({}, { signal: controller.signal })).rejects.toEqual(
327
+ new Error("Human-in-the-loop interaction aborted"),
328
+ );
329
+ });
330
+
331
+ it("rejects once on abort, clears pending refs, and installs a one-shot listener", async () => {
332
+ const { handler, renderer } = mountHitlTool();
333
+ const listeners = new Set<() => void>();
334
+ const signal = {
335
+ aborted: false,
336
+ addEventListener: vi.fn(
337
+ (_type: string, listener: () => void, options) => {
338
+ expect(options).toEqual({ once: true });
339
+ listeners.add(listener);
340
+ },
341
+ ),
342
+ removeEventListener: vi.fn((_type: string, listener: () => void) => {
343
+ listeners.delete(listener);
344
+ }),
345
+ } as unknown as AbortSignal;
346
+
347
+ const pending = handler!({}, { signal });
348
+ expect(listeners.size).toBe(1);
349
+ const onAbort = [...listeners][0];
350
+ onAbort();
351
+ onAbort();
352
+
353
+ await expect(pending).rejects.toEqual(
354
+ new Error("Human-in-the-loop interaction aborted"),
355
+ );
356
+ const respond = (
357
+ renderer.render({
358
+ name: "lifecycleTool",
359
+ toolCallId: "tc-aborted",
360
+ args: {},
361
+ status: "executing",
362
+ result: undefined,
363
+ }) as { props?: { respond?: (result: unknown) => Promise<void> } }
364
+ ).props?.respond;
365
+ await respond?.("late response");
366
+ });
367
+
368
+ it("removes the abort listener before respond resolves and ignores a late abort", async () => {
369
+ const { handler, renderer } = mountHitlTool();
370
+ const events: string[] = [];
371
+ let onAbort: (() => void) | undefined;
372
+ const signal = {
373
+ aborted: false,
374
+ addEventListener: vi.fn((_type: string, listener: () => void) => {
375
+ onAbort = listener;
376
+ }),
377
+ removeEventListener: vi.fn(() => {
378
+ events.push("removed");
379
+ }),
380
+ } as unknown as AbortSignal;
381
+
382
+ const pending = handler!({}, { signal }).then((result) => {
383
+ events.push(`resolved:${String(result)}`);
384
+ return result;
385
+ });
386
+ const respond = (
387
+ renderer.render({
388
+ name: "lifecycleTool",
389
+ toolCallId: "tc-respond",
390
+ args: {},
391
+ status: "executing",
392
+ result: undefined,
393
+ }) as { props?: { respond?: (result: unknown) => Promise<void> } }
394
+ ).props?.respond;
395
+
396
+ await respond!("approved");
397
+ onAbort?.();
398
+ await expect(pending).resolves.toBe("approved");
399
+ expect(events).toEqual(["removed", "resolved:approved"]);
400
+ });
401
+
402
+ it("passes toolCallId and registration agentId through every HITL status", () => {
403
+ const { renderer } = mountHitlTool("research-agent");
404
+ const shared = { name: "lifecycleTool", toolCallId: "tc-props" };
405
+ const inProgress = renderer.render({
406
+ ...shared,
407
+ args: { value: "partial" },
408
+ status: "inProgress",
409
+ result: undefined,
410
+ }) as { props?: Record<string, unknown> };
411
+ const executing = renderer.render({
412
+ ...shared,
413
+ args: { value: "complete-args" },
414
+ status: "executing",
415
+ result: undefined,
416
+ }) as { props?: Record<string, unknown> };
417
+ const complete = renderer.render({
418
+ ...shared,
419
+ args: { value: "complete-args" },
420
+ status: "complete",
421
+ result: "done",
422
+ }) as { props?: Record<string, unknown> };
423
+
424
+ for (const vnode of [inProgress, executing, complete]) {
425
+ expect(vnode.props?.name).toBe("lifecycleTool");
426
+ expect(vnode.props?.description).toBe("Lifecycle description");
427
+ expect(vnode.props?.toolCallId).toBe("tc-props");
428
+ expect(vnode.props?.agentId).toBe("research-agent");
429
+ }
430
+ expect(inProgress.props?.args).toEqual({ value: "partial" });
431
+ expect(inProgress.props?.result).toBeUndefined();
432
+ expect(executing.props?.args).toEqual({ value: "complete-args" });
433
+ expect(executing.props?.result).toBeUndefined();
434
+ expect(complete.props?.args).toEqual({ value: "complete-args" });
435
+ expect(complete.props?.result).toBe("done");
436
+ expect(inProgress.props?.respond).toBeUndefined();
437
+ expect(typeof executing.props?.respond).toBe("function");
438
+ expect(complete.props?.respond).toBeUndefined();
439
+ });
440
+
441
+ it("removes only the agent-scoped renderer on scope disposal", async () => {
442
+ const RenderComp = defineComponent({
443
+ setup() {
444
+ return () => h("div");
445
+ },
446
+ });
447
+ const createScopedChild = (agentId: string) =>
448
+ defineComponent({
449
+ setup() {
450
+ useHumanInTheLoop({
451
+ name: "sharedLifecycleTool",
452
+ agentId,
453
+ parameters: z.object({}),
454
+ render: RenderComp,
455
+ });
456
+ return () => null;
457
+ },
458
+ });
459
+ const ScopedA = createScopedChild("agent-a");
460
+ const ScopedB = createScopedChild("agent-b");
461
+ const Parent = defineComponent({
462
+ setup() {
463
+ const showA = ref(true);
464
+ return () =>
465
+ h("div", [
466
+ h("button", {
467
+ "data-testid": "dispose-agent-a",
468
+ onClick: () => (showA.value = false),
469
+ }),
470
+ showA.value ? h(ScopedA) : null,
471
+ h(ScopedB),
472
+ ]);
473
+ },
474
+ });
475
+
476
+ const { wrapper, getCore } = mountWithProvider(() => h(Parent));
477
+ await nextTick();
478
+ const renderers = () =>
479
+ getCore().renderToolCalls.filter(
480
+ (renderer) => renderer.name === "sharedLifecycleTool",
481
+ );
482
+ expect(renderers().map((renderer) => renderer.agentId)).toEqual([
483
+ "agent-a",
484
+ "agent-b",
485
+ ]);
486
+
487
+ await wrapper.find("[data-testid=dispose-agent-a]").trigger("click");
488
+ await nextTick();
489
+
490
+ expect(renderers().map((renderer) => renderer.agentId)).toEqual([
491
+ "agent-b",
492
+ ]);
493
+ expect(
494
+ getCore().getTool({
495
+ toolName: "sharedLifecycleTool",
496
+ agentId: "agent-b",
497
+ }),
498
+ ).toBeDefined();
499
+ });
500
+
501
+ it("leaves agentId undefined for an unscoped HITL tool", () => {
502
+ const { renderer } = mountHitlTool();
503
+ const vnode = renderer.render({
504
+ name: "lifecycleTool",
505
+ toolCallId: "tc-unscoped",
506
+ args: {},
507
+ status: "executing",
508
+ result: undefined,
509
+ }) as { props?: Record<string, unknown> };
510
+
511
+ expect(vnode.props?.agentId).toBeUndefined();
512
+ });
513
+
514
+ it("does not settle a pending HITL promise when its renderer unmounts", async () => {
515
+ const mounted = mountHitlTool();
516
+ const pending = mounted.handler({}, {});
517
+ mounted.wrapper.unmount();
518
+
519
+ const outcome = await Promise.race([
520
+ pending.then(
521
+ () => "settled",
522
+ () => "settled",
523
+ ),
524
+ new Promise((resolve) => setTimeout(() => resolve("pending"), 0)),
525
+ ]);
526
+ expect(outcome).toBe("pending");
527
+ });
528
+ });
282
529
  });