@copilotkit/runtime 1.56.3 → 1.56.4-canary.1777531098

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.
Files changed (114) hide show
  1. package/dist/agent/converters/tanstack.cjs +121 -25
  2. package/dist/agent/converters/tanstack.cjs.map +1 -1
  3. package/dist/agent/converters/tanstack.d.cts.map +1 -1
  4. package/dist/agent/converters/tanstack.d.mts.map +1 -1
  5. package/dist/agent/converters/tanstack.mjs +121 -25
  6. package/dist/agent/converters/tanstack.mjs.map +1 -1
  7. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs +8 -1
  8. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
  9. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
  10. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
  11. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs +8 -1
  12. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
  13. package/dist/package.cjs +6 -6
  14. package/dist/package.mjs +6 -6
  15. package/dist/v2/index.d.cts +2 -2
  16. package/dist/v2/index.d.mts +2 -2
  17. package/dist/v2/runtime/core/fetch-handler.cjs +2 -0
  18. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
  19. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
  20. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
  21. package/dist/v2/runtime/core/fetch-handler.mjs +2 -0
  22. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
  23. package/dist/v2/runtime/core/runtime.d.mts +0 -1
  24. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  25. package/dist/v2/runtime/endpoints/express.cjs +5 -5
  26. package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
  27. package/dist/v2/runtime/endpoints/express.mjs +5 -5
  28. package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
  29. package/dist/v2/runtime/handlers/handle-connect.cjs +2 -3
  30. package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
  31. package/dist/v2/runtime/handlers/handle-connect.mjs +2 -3
  32. package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
  33. package/dist/v2/runtime/handlers/intelligence/connect.cjs +21 -31
  34. package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
  35. package/dist/v2/runtime/handlers/intelligence/connect.mjs +22 -31
  36. package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
  37. package/dist/v2/runtime/handlers/intelligence/run.cjs +111 -26
  38. package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
  39. package/dist/v2/runtime/handlers/intelligence/run.mjs +111 -26
  40. package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
  41. package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs +7 -3
  42. package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs.map +1 -1
  43. package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs +7 -3
  44. package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs.map +1 -1
  45. package/dist/v2/runtime/index.d.cts +1 -1
  46. package/dist/v2/runtime/index.d.mts +1 -2
  47. package/dist/v2/runtime/index.d.mts.map +1 -1
  48. package/dist/v2/runtime/intelligence-platform/client.cjs +5 -2
  49. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  50. package/dist/v2/runtime/intelligence-platform/client.d.cts +16 -18
  51. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  52. package/dist/v2/runtime/intelligence-platform/client.d.mts +16 -18
  53. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  54. package/dist/v2/runtime/intelligence-platform/client.mjs +5 -2
  55. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  56. package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
  57. package/dist/v2/runtime/runner/agent-runner.d.cts +0 -1
  58. package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
  59. package/dist/v2/runtime/runner/agent-runner.d.mts +0 -1
  60. package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
  61. package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
  62. package/dist/v2/runtime/runner/index.d.cts +1 -1
  63. package/dist/v2/runtime/runner/index.d.mts +1 -1
  64. package/dist/v2/runtime/runner/intelligence.cjs +30 -5
  65. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  66. package/dist/v2/runtime/runner/intelligence.d.cts +7 -1
  67. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  68. package/dist/v2/runtime/runner/intelligence.d.mts +7 -1
  69. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  70. package/dist/v2/runtime/runner/intelligence.mjs +30 -5
  71. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  72. package/dist/v2/runtime/telemetry/instance-created.cjs +33 -0
  73. package/dist/v2/runtime/telemetry/instance-created.cjs.map +1 -0
  74. package/dist/v2/runtime/telemetry/instance-created.mjs +33 -0
  75. package/dist/v2/runtime/telemetry/instance-created.mjs.map +1 -0
  76. package/dist/v2/runtime/telemetry/telemetry-client.cjs +1 -38
  77. package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
  78. package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -37
  79. package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
  80. package/package.json +7 -7
  81. package/src/agent/__tests__/agent-test-helpers.ts +31 -1
  82. package/src/agent/__tests__/converter-tanstack.test.ts +280 -0
  83. package/src/agent/converters/tanstack.ts +167 -10
  84. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +8 -1
  85. package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +1 -1
  86. package/src/v2/runtime/__tests__/express-single-telemetry.integration.test.ts +65 -0
  87. package/src/v2/runtime/__tests__/express-telemetry.integration.test.ts +101 -0
  88. package/src/v2/runtime/__tests__/handle-connect.test.ts +155 -48
  89. package/src/v2/runtime/__tests__/handle-run.test.ts +380 -29
  90. package/src/v2/runtime/__tests__/hono-single-telemetry.integration.test.ts +46 -0
  91. package/src/v2/runtime/__tests__/hono-telemetry.integration.test.ts +99 -0
  92. package/src/v2/runtime/__tests__/intelligence-run-telemetry.test.ts +194 -0
  93. package/src/v2/runtime/__tests__/sse-response-telemetry.test.ts +108 -0
  94. package/src/v2/runtime/__tests__/telemetry.test.ts +0 -61
  95. package/src/v2/runtime/core/fetch-handler.ts +3 -0
  96. package/src/v2/runtime/endpoints/express.ts +9 -3
  97. package/src/v2/runtime/handlers/handle-connect.ts +1 -2
  98. package/src/v2/runtime/handlers/intelligence/connect.ts +48 -68
  99. package/src/v2/runtime/handlers/intelligence/run.ts +162 -21
  100. package/src/v2/runtime/handlers/shared/intelligence-utils.ts +21 -1
  101. package/src/v2/runtime/intelligence-platform/__tests__/client.test.ts +33 -39
  102. package/src/v2/runtime/intelligence-platform/client.ts +36 -31
  103. package/src/v2/runtime/runner/__tests__/intelligence-runner.test.ts +15 -7
  104. package/src/v2/runtime/runner/agent-runner.ts +0 -1
  105. package/src/v2/runtime/runner/intelligence.ts +47 -6
  106. package/src/v2/runtime/telemetry/__tests__/instance-created.test.ts +96 -0
  107. package/src/v2/runtime/telemetry/instance-created.ts +44 -0
  108. package/src/v2/runtime/telemetry/telemetry-client.ts +1 -57
  109. package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
  110. package/dist/v2/runtime/telemetry/utils.cjs +0 -15
  111. package/dist/v2/runtime/telemetry/utils.cjs.map +0 -1
  112. package/dist/v2/runtime/telemetry/utils.mjs +0 -14
  113. package/dist/v2/runtime/telemetry/utils.mjs.map +0 -1
  114. package/src/v2/runtime/telemetry/utils.ts +0 -15
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Integration test: Hono adapter + telemetry.
3
+ *
4
+ * Asserts both ends of the path-to-production chain that the v2 refactor
5
+ * previously broke (2ac4a40b5, 2026-03-29):
6
+ * 1. `oss.runtime.instance_created` fires once per handler factory.
7
+ * 2. `oss.runtime.copilot_request_created` fires when a real HTTP request
8
+ * flows through the handler.
9
+ *
10
+ * If either regresses, this test fails.
11
+ */
12
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
13
+ import type { AbstractAgent } from "@ag-ui/client";
14
+
15
+ import { telemetry } from "../telemetry";
16
+ import { createCopilotHonoHandler } from "../endpoints/hono";
17
+ import { CopilotRuntime } from "../core/runtime";
18
+
19
+ function makeAgent(): AbstractAgent {
20
+ const a: unknown = {
21
+ execute: async () => ({ events: [] }),
22
+ };
23
+ (a as { clone: () => unknown }).clone = () => makeAgent();
24
+ return a as AbstractAgent;
25
+ }
26
+
27
+ describe("Hono adapter — telemetry firing (integration)", () => {
28
+ let captureSpy: ReturnType<typeof vi.spyOn>;
29
+
30
+ beforeEach(() => {
31
+ captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
32
+ });
33
+
34
+ afterEach(() => {
35
+ captureSpy.mockRestore();
36
+ });
37
+
38
+ it("fires instance_created on handler creation (multi-route)", async () => {
39
+ const runtime = new CopilotRuntime({ agents: { default: makeAgent() } });
40
+ createCopilotHonoHandler({ runtime, basePath: "/" });
41
+
42
+ await vi.waitFor(() => {
43
+ expect(captureSpy).toHaveBeenCalledWith(
44
+ "oss.runtime.instance_created",
45
+ expect.objectContaining({
46
+ agentsAmount: 1,
47
+ actionsAmount: 0,
48
+ endpointsAmount: 0,
49
+ "cloud.api_key_provided": false,
50
+ }),
51
+ );
52
+ });
53
+ });
54
+
55
+ it("fires copilot_request_created when a real HTTP request hits the handler", async () => {
56
+ const runtime = new CopilotRuntime({ agents: { default: makeAgent() } });
57
+ const endpoint = createCopilotHonoHandler({ runtime, basePath: "/" });
58
+
59
+ await endpoint.fetch(
60
+ new Request("https://example.com/agent/default/run", {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({ messages: [], state: {}, threadId: "t1" }),
64
+ }),
65
+ );
66
+
67
+ expect(captureSpy).toHaveBeenCalledWith(
68
+ "oss.runtime.copilot_request_created",
69
+ expect.objectContaining({
70
+ requestType: "run",
71
+ "cloud.api_key_provided": false,
72
+ }),
73
+ );
74
+ });
75
+
76
+ it("includes cloud.public_api_key on request when header is present", async () => {
77
+ const runtime = new CopilotRuntime({ agents: { default: makeAgent() } });
78
+ const endpoint = createCopilotHonoHandler({ runtime, basePath: "/" });
79
+
80
+ await endpoint.fetch(
81
+ new Request("https://example.com/agent/default/run", {
82
+ method: "POST",
83
+ headers: {
84
+ "Content-Type": "application/json",
85
+ "x-copilotcloud-public-api-key": "ck_pub_test_123",
86
+ },
87
+ body: JSON.stringify({ messages: [], state: {}, threadId: "t1" }),
88
+ }),
89
+ );
90
+
91
+ expect(captureSpy).toHaveBeenCalledWith(
92
+ "oss.runtime.copilot_request_created",
93
+ expect.objectContaining({
94
+ "cloud.api_key_provided": true,
95
+ "cloud.public_api_key": "ck_pub_test_123",
96
+ }),
97
+ );
98
+ });
99
+ });
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Telemetry lifecycle tests for `packages/runtime/src/v2/runtime/handlers/intelligence/run.ts`.
3
+ *
4
+ * intelligence/run.ts fires three events across the agent run lifecycle:
5
+ * - oss.runtime.agent_execution_stream_started (line 126, after thread lock)
6
+ * - oss.runtime.agent_execution_stream_errored (inside runner subscribe's error handler)
7
+ * - oss.runtime.agent_execution_stream_ended (inside runner subscribe's complete handler)
8
+ *
9
+ * This test verifies each fires under the expected condition. It's paired
10
+ * with sse-response-telemetry.test.ts which covers the SSE path of the
11
+ * same event names — kept separate so a regression in one file fails only
12
+ * its own test.
13
+ */
14
+ import type { AbstractAgent, BaseEvent } from "@ag-ui/client";
15
+ import { Observable } from "rxjs";
16
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
17
+
18
+ import { handleRunAgent } from "../handlers/handle-run";
19
+ import { IntelligenceAgentRunner } from "../runner/intelligence";
20
+ import { telemetry } from "../telemetry";
21
+ import type { CopilotRuntime } from "../core/runtime";
22
+
23
+ // --- Minimal helpers (mirroring handle-run.test.ts's intelligence block) ---
24
+
25
+ interface MockIntelligencePlatform {
26
+ [key: string]: ((...args: any[]) => any) | undefined;
27
+ }
28
+
29
+ function makeAgent(): AbstractAgent {
30
+ const makeClone = () =>
31
+ ({
32
+ clone: vi.fn(() => makeClone()),
33
+ setMessages: vi.fn(),
34
+ setState: vi.fn(),
35
+ threadId: undefined,
36
+ headers: {},
37
+ runAgent: vi.fn().mockResolvedValue(undefined),
38
+ }) as unknown as AbstractAgent;
39
+ const agent: any = {
40
+ clone: vi.fn(() => makeClone()),
41
+ setMessages: vi.fn(),
42
+ setState: vi.fn(),
43
+ threadId: undefined,
44
+ headers: {},
45
+ runAgent: vi.fn().mockResolvedValue(undefined),
46
+ };
47
+ return agent as AbstractAgent;
48
+ }
49
+
50
+ function makeIntelligenceRuntime(
51
+ runObservable: Observable<BaseEvent>,
52
+ extraPlatform: MockIntelligencePlatform = {},
53
+ ): CopilotRuntime {
54
+ const runner = Object.create(IntelligenceAgentRunner.prototype);
55
+ runner.run = vi.fn(() => runObservable);
56
+
57
+ const platform: MockIntelligencePlatform = {
58
+ getOrCreateThread: vi.fn().mockResolvedValue({
59
+ thread: { id: "thread-1", name: null },
60
+ created: false,
61
+ }),
62
+ getThreadMessages: vi.fn().mockResolvedValue({ messages: [] }),
63
+ ɵacquireThreadLock: vi.fn().mockResolvedValue({
64
+ threadId: "thread-1",
65
+ runId: "run-1",
66
+ joinToken: "jt-1",
67
+ }),
68
+ ɵcleanupThreadLock: vi.fn().mockResolvedValue(undefined),
69
+ ɵgetClientWsUrl: vi.fn(() => "wss://runtime.example/client"),
70
+ ɵrenewThreadLock: vi.fn().mockResolvedValue(undefined),
71
+ ...extraPlatform,
72
+ };
73
+
74
+ return {
75
+ agents: Promise.resolve({ "my-agent": makeAgent() }),
76
+ transcriptionService: undefined,
77
+ beforeRequestMiddleware: undefined,
78
+ afterRequestMiddleware: undefined,
79
+ runner,
80
+ mode: "intelligence",
81
+ generateThreadNames: false,
82
+ intelligence: platform,
83
+ identifyUser: vi.fn().mockResolvedValue({ id: "user-1", name: "User One" }),
84
+ lockTtlSeconds: 20,
85
+ lockHeartbeatIntervalSeconds: 15,
86
+ } as unknown as CopilotRuntime;
87
+ }
88
+
89
+ function makeRunRequest(): Request {
90
+ return new Request("https://example.com/agent/my-agent/run", {
91
+ method: "POST",
92
+ headers: { "Content-Type": "application/json" },
93
+ body: JSON.stringify({
94
+ threadId: "thread-1",
95
+ runId: "run-1",
96
+ state: {},
97
+ messages: [],
98
+ tools: [],
99
+ context: [],
100
+ forwardedProps: {},
101
+ }),
102
+ });
103
+ }
104
+
105
+ // --- Tests ---
106
+
107
+ describe("intelligence/run.ts — telemetry lifecycle", () => {
108
+ let captureSpy: ReturnType<typeof vi.spyOn>;
109
+ let errorSpy: ReturnType<typeof vi.spyOn>;
110
+
111
+ beforeEach(() => {
112
+ vi.useFakeTimers();
113
+ captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
114
+ // Swallow the logger.error that fires on simulated agent errors.
115
+ errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
116
+ });
117
+
118
+ afterEach(() => {
119
+ vi.useRealTimers();
120
+ captureSpy.mockRestore();
121
+ errorSpy.mockRestore();
122
+ });
123
+
124
+ it("fires agent_execution_stream_started once thread lock is acquired", async () => {
125
+ // Observable that never completes — so only stream_started should fire.
126
+ const never = new Observable<BaseEvent>(() => {});
127
+ const runtime = makeIntelligenceRuntime(never);
128
+
129
+ await handleRunAgent({
130
+ runtime,
131
+ request: makeRunRequest(),
132
+ agentId: "my-agent",
133
+ });
134
+
135
+ expect(captureSpy).toHaveBeenCalledWith(
136
+ "oss.runtime.agent_execution_stream_started",
137
+ {},
138
+ );
139
+ // And NOT the other two
140
+ expect(captureSpy).not.toHaveBeenCalledWith(
141
+ "oss.runtime.agent_execution_stream_errored",
142
+ expect.anything(),
143
+ );
144
+ expect(captureSpy).not.toHaveBeenCalledWith(
145
+ "oss.runtime.agent_execution_stream_ended",
146
+ expect.anything(),
147
+ );
148
+ });
149
+
150
+ it("fires agent_execution_stream_ended when runner.run observable completes", async () => {
151
+ // Observable that completes immediately.
152
+ const completing = new Observable<BaseEvent>((subscriber) => {
153
+ subscriber.complete();
154
+ });
155
+ const runtime = makeIntelligenceRuntime(completing);
156
+
157
+ await handleRunAgent({
158
+ runtime,
159
+ request: makeRunRequest(),
160
+ agentId: "my-agent",
161
+ });
162
+
163
+ expect(captureSpy).toHaveBeenCalledWith(
164
+ "oss.runtime.agent_execution_stream_started",
165
+ {},
166
+ );
167
+ expect(captureSpy).toHaveBeenCalledWith(
168
+ "oss.runtime.agent_execution_stream_ended",
169
+ {},
170
+ );
171
+ });
172
+
173
+ it("fires agent_execution_stream_errored when runner.run observable errors", async () => {
174
+ const failing = new Observable<BaseEvent>((subscriber) => {
175
+ subscriber.error(new Error("agent exploded"));
176
+ });
177
+ const runtime = makeIntelligenceRuntime(failing);
178
+
179
+ await handleRunAgent({
180
+ runtime,
181
+ request: makeRunRequest(),
182
+ agentId: "my-agent",
183
+ });
184
+
185
+ expect(captureSpy).toHaveBeenCalledWith(
186
+ "oss.runtime.agent_execution_stream_started",
187
+ {},
188
+ );
189
+ expect(captureSpy).toHaveBeenCalledWith(
190
+ "oss.runtime.agent_execution_stream_errored",
191
+ expect.objectContaining({ error: "agent exploded" }),
192
+ );
193
+ });
194
+ });
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Telemetry lifecycle tests for `packages/runtime/src/v2/runtime/handlers/shared/sse-response.ts`.
3
+ *
4
+ * sse-response.ts fires three events across the SSE stream lifecycle:
5
+ * - oss.runtime.agent_execution_stream_started (line 73, right after observableFactory resolves)
6
+ * - oss.runtime.agent_execution_stream_errored (inside subscribe's error handler)
7
+ * - oss.runtime.agent_execution_stream_ended (inside subscribe's complete handler)
8
+ *
9
+ * Paired with intelligence-run-telemetry.test.ts which covers the
10
+ * intelligence/run path of the same event names — kept separate so a
11
+ * regression in one source file fails only its own test.
12
+ */
13
+ import { BaseEvent } from "@ag-ui/client";
14
+ import { Observable } from "rxjs";
15
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
16
+
17
+ import { createSseEventResponse } from "../handlers/shared/sse-response";
18
+ import { telemetry } from "../telemetry";
19
+
20
+ function makeRequest(): Request {
21
+ return new Request("https://example.com/agent/test/run", { method: "POST" });
22
+ }
23
+
24
+ describe("sse-response.ts — telemetry lifecycle", () => {
25
+ let captureSpy: ReturnType<typeof vi.spyOn>;
26
+ let errorSpy: ReturnType<typeof vi.spyOn>;
27
+
28
+ beforeEach(() => {
29
+ captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
30
+ // Swallow the console.error from SSE logError on simulated failures.
31
+ errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
32
+ });
33
+
34
+ afterEach(() => {
35
+ captureSpy.mockRestore();
36
+ errorSpy.mockRestore();
37
+ });
38
+
39
+ it("fires agent_execution_stream_started once the observable factory resolves", async () => {
40
+ // Observable that never completes — only started should fire.
41
+ const never = new Observable<BaseEvent>(() => {});
42
+ createSseEventResponse({
43
+ request: makeRequest(),
44
+ observableFactory: () => never,
45
+ });
46
+
47
+ await vi.waitFor(() => {
48
+ expect(captureSpy).toHaveBeenCalledWith(
49
+ "oss.runtime.agent_execution_stream_started",
50
+ {},
51
+ );
52
+ });
53
+
54
+ expect(captureSpy).not.toHaveBeenCalledWith(
55
+ "oss.runtime.agent_execution_stream_errored",
56
+ expect.anything(),
57
+ );
58
+ expect(captureSpy).not.toHaveBeenCalledWith(
59
+ "oss.runtime.agent_execution_stream_ended",
60
+ expect.anything(),
61
+ );
62
+ });
63
+
64
+ it("fires agent_execution_stream_ended when the observable completes", async () => {
65
+ const completing = new Observable<BaseEvent>((subscriber) => {
66
+ subscriber.complete();
67
+ });
68
+ createSseEventResponse({
69
+ request: makeRequest(),
70
+ observableFactory: () => completing,
71
+ });
72
+
73
+ await vi.waitFor(() => {
74
+ expect(captureSpy).toHaveBeenCalledWith(
75
+ "oss.runtime.agent_execution_stream_ended",
76
+ {},
77
+ );
78
+ });
79
+
80
+ // started should also have fired before ended
81
+ expect(captureSpy).toHaveBeenCalledWith(
82
+ "oss.runtime.agent_execution_stream_started",
83
+ {},
84
+ );
85
+ });
86
+
87
+ it("fires agent_execution_stream_errored with the error message when the observable errors", async () => {
88
+ const failing = new Observable<BaseEvent>((subscriber) => {
89
+ subscriber.error(new Error("stream blew up"));
90
+ });
91
+ createSseEventResponse({
92
+ request: makeRequest(),
93
+ observableFactory: () => failing,
94
+ });
95
+
96
+ await vi.waitFor(() => {
97
+ expect(captureSpy).toHaveBeenCalledWith(
98
+ "oss.runtime.agent_execution_stream_errored",
99
+ expect.objectContaining({ error: "stream blew up" }),
100
+ );
101
+ });
102
+
103
+ expect(captureSpy).toHaveBeenCalledWith(
104
+ "oss.runtime.agent_execution_stream_started",
105
+ {},
106
+ );
107
+ });
108
+ });
@@ -2,27 +2,15 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import { TelemetryClient } from "../telemetry/telemetry-client";
3
3
  import scarfClient from "../telemetry/scarf-client";
4
4
 
5
- const { segmentTrackMock } = vi.hoisted(() => ({
6
- segmentTrackMock: vi.fn(),
7
- }));
8
-
9
- vi.mock("@segment/analytics-node", () => ({
10
- Analytics: vi.fn().mockImplementation(() => ({
11
- track: segmentTrackMock,
12
- })),
13
- }));
14
-
15
5
  describe("TelemetryClient", () => {
16
6
  let scarfSpy: ReturnType<typeof vi.spyOn>;
17
7
 
18
8
  beforeEach(() => {
19
9
  scarfSpy = vi.spyOn(scarfClient, "logEvent").mockResolvedValue(undefined);
20
- segmentTrackMock.mockClear();
21
10
  });
22
11
 
23
12
  afterEach(() => {
24
13
  scarfSpy.mockRestore();
25
- segmentTrackMock.mockClear();
26
14
  });
27
15
 
28
16
  it("sends event to scarf when sampled in", async () => {
@@ -44,33 +32,6 @@ describe("TelemetryClient", () => {
44
32
  });
45
33
  });
46
34
 
47
- it("sends event with properties to segment when sampled in", async () => {
48
- vi.spyOn(Math, "random").mockReturnValue(0);
49
- const client = new TelemetryClient({
50
- telemetryDisabled: false,
51
- sampleRate: 1,
52
- });
53
-
54
- await client.capture("oss.runtime.instance_created", {
55
- actionsAmount: 5,
56
- endpointTypes: ["rest"],
57
- endpointsAmount: 2,
58
- "cloud.api_key_provided": true,
59
- "cloud.public_api_key": "pk_test_123",
60
- });
61
-
62
- expect(segmentTrackMock).toHaveBeenCalledTimes(1);
63
- const call = segmentTrackMock.mock.calls[0][0];
64
- expect(call.event).toBe("oss.runtime.instance_created");
65
- expect(call.anonymousId).toMatch(/^anon_/);
66
- expect(call.properties).toMatchObject({
67
- actionsAmount: 5,
68
- endpointsAmount: 2,
69
- "cloud.api_key_provided": true,
70
- "cloud.public_api_key": "pk_test_123",
71
- });
72
- });
73
-
74
35
  it("only sends event name to scarf, not properties", async () => {
75
36
  const client = new TelemetryClient({
76
37
  telemetryDisabled: false,
@@ -106,7 +67,6 @@ describe("TelemetryClient", () => {
106
67
  });
107
68
 
108
69
  expect(scarfSpy).not.toHaveBeenCalled();
109
- expect(segmentTrackMock).not.toHaveBeenCalled();
110
70
  });
111
71
 
112
72
  it("does not send events when sampled out", async () => {
@@ -124,7 +84,6 @@ describe("TelemetryClient", () => {
124
84
  });
125
85
 
126
86
  expect(scarfSpy).not.toHaveBeenCalled();
127
- expect(segmentTrackMock).not.toHaveBeenCalled();
128
87
  });
129
88
 
130
89
  it("respects sample rate boundary", async () => {
@@ -139,26 +98,6 @@ describe("TelemetryClient", () => {
139
98
  expect(scarfSpy).toHaveBeenCalled();
140
99
  });
141
100
 
142
- it("includes global properties in segment track call", async () => {
143
- vi.spyOn(Math, "random").mockReturnValue(0);
144
- const client = new TelemetryClient({
145
- telemetryDisabled: false,
146
- sampleRate: 1,
147
- });
148
-
149
- client.setGlobalProperties({ "copilotkit.package.name": "test-pkg" });
150
-
151
- await client.capture("oss.runtime.instance_created", {
152
- actionsAmount: 1,
153
- endpointTypes: [],
154
- endpointsAmount: 0,
155
- "cloud.api_key_provided": false,
156
- });
157
-
158
- const call = segmentTrackMock.mock.calls[0][0];
159
- expect(call.properties["copilotkit.package.name"]).toBe("test-pkg");
160
- });
161
-
162
101
  it("throws when sample rate is out of range", () => {
163
102
  expect(() => new TelemetryClient({ sampleRate: 1.5 })).toThrow(
164
103
  "Sample rate must be between 0 and 1",
@@ -62,6 +62,7 @@ import {
62
62
  type MethodCall,
63
63
  } from "../endpoints/single-route-helpers";
64
64
  import { logger } from "@copilotkit/shared";
65
+ import { fireInstanceCreatedTelemetry } from "../telemetry/instance-created";
65
66
 
66
67
  /* ------------------------------------------------------------------------------------------------
67
68
  * Public types
@@ -114,6 +115,8 @@ export function createCopilotRuntimeHandler(
114
115
  ): CopilotRuntimeFetchHandler {
115
116
  const { runtime, basePath, mode = "multi-route", cors, hooks } = options;
116
117
 
118
+ fireInstanceCreatedTelemetry({ runtime });
119
+
117
120
  const corsConfig = resolveCorsConfig(cors);
118
121
 
119
122
  return async (request: Request): Promise<Response> => {
@@ -147,15 +147,21 @@ export function createCopilotExpressHandler({
147
147
  router.post(normalizedBase, expressHandler);
148
148
  router.options(normalizedBase, expressHandler);
149
149
  } else if (normalizedBase === "/") {
150
- router.all("*", expressHandler);
150
+ router.all(/.*/, expressHandler);
151
151
  } else {
152
- router.all(`${normalizedBase}/*`, expressHandler);
153
- router.all(normalizedBase, expressHandler);
152
+ router.all(
153
+ new RegExp(`^${escapeRegExp(normalizedBase)}(\\/.*)?$`),
154
+ expressHandler,
155
+ );
154
156
  }
155
157
 
156
158
  return router;
157
159
  }
158
160
 
161
+ function escapeRegExp(s: string): string {
162
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
163
+ }
164
+
159
165
  function normalizeBasePath(path: string): string {
160
166
  if (!path) {
161
167
  throw new Error("basePath must be provided for Express endpoint");
@@ -43,9 +43,8 @@ export async function handleConnectAgent({
43
43
  return handleIntelligenceConnect({
44
44
  runtime,
45
45
  request,
46
+ agentId,
46
47
  threadId: connectRequest.input.threadId,
47
- runId: connectRequest.input.runId,
48
- lastSeenEventId: connectRequest.lastSeenEventId,
49
48
  });
50
49
  }
51
50