@copilotkit/runtime 1.56.2 → 1.56.4-canary.1777529757

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 (203) 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/graphql/resolvers/copilot.resolver.cjs +2 -1
  8. package/dist/graphql/resolvers/copilot.resolver.cjs.map +1 -1
  9. package/dist/graphql/resolvers/copilot.resolver.mjs +2 -1
  10. package/dist/graphql/resolvers/copilot.resolver.mjs.map +1 -1
  11. package/dist/graphql/resolvers/resolve-message-id.cjs +19 -0
  12. package/dist/graphql/resolvers/resolve-message-id.cjs.map +1 -0
  13. package/dist/graphql/resolvers/resolve-message-id.mjs +18 -0
  14. package/dist/graphql/resolvers/resolve-message-id.mjs.map +1 -0
  15. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs +8 -1
  16. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
  17. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
  18. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
  19. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs +8 -1
  20. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
  21. package/dist/lib/runtime/copilot-runtime.cjs +4 -2
  22. package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
  23. package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
  24. package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
  25. package/dist/lib/runtime/copilot-runtime.mjs +4 -2
  26. package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
  27. package/dist/package.cjs +7 -7
  28. package/dist/package.mjs +7 -7
  29. package/dist/v2/index.d.cts +2 -2
  30. package/dist/v2/index.d.mts +2 -2
  31. package/dist/v2/runtime/core/debug-event-bus.cjs +36 -0
  32. package/dist/v2/runtime/core/debug-event-bus.cjs.map +1 -0
  33. package/dist/v2/runtime/core/debug-event-bus.d.cts +19 -0
  34. package/dist/v2/runtime/core/debug-event-bus.d.cts.map +1 -0
  35. package/dist/v2/runtime/core/debug-event-bus.d.mts +19 -0
  36. package/dist/v2/runtime/core/debug-event-bus.d.mts.map +1 -0
  37. package/dist/v2/runtime/core/debug-event-bus.mjs +35 -0
  38. package/dist/v2/runtime/core/debug-event-bus.mjs.map +1 -0
  39. package/dist/v2/runtime/core/fetch-handler.cjs +8 -0
  40. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
  41. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
  42. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
  43. package/dist/v2/runtime/core/fetch-handler.mjs +8 -0
  44. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
  45. package/dist/v2/runtime/core/fetch-router.cjs +1 -0
  46. package/dist/v2/runtime/core/fetch-router.cjs.map +1 -1
  47. package/dist/v2/runtime/core/fetch-router.mjs +1 -0
  48. package/dist/v2/runtime/core/fetch-router.mjs.map +1 -1
  49. package/dist/v2/runtime/core/hooks.cjs.map +1 -1
  50. package/dist/v2/runtime/core/hooks.d.cts +2 -0
  51. package/dist/v2/runtime/core/hooks.d.cts.map +1 -1
  52. package/dist/v2/runtime/core/hooks.d.mts +2 -0
  53. package/dist/v2/runtime/core/hooks.d.mts.map +1 -1
  54. package/dist/v2/runtime/core/hooks.mjs.map +1 -1
  55. package/dist/v2/runtime/core/runtime.cjs +5 -0
  56. package/dist/v2/runtime/core/runtime.cjs.map +1 -1
  57. package/dist/v2/runtime/core/runtime.d.cts +5 -0
  58. package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
  59. package/dist/v2/runtime/core/runtime.d.mts +5 -1
  60. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  61. package/dist/v2/runtime/core/runtime.mjs +5 -0
  62. package/dist/v2/runtime/core/runtime.mjs.map +1 -1
  63. package/dist/v2/runtime/endpoints/express.cjs +5 -5
  64. package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
  65. package/dist/v2/runtime/endpoints/express.mjs +5 -5
  66. package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
  67. package/dist/v2/runtime/handlers/handle-connect.cjs +3 -2
  68. package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
  69. package/dist/v2/runtime/handlers/handle-connect.mjs +3 -2
  70. package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
  71. package/dist/v2/runtime/handlers/handle-debug-events.cjs +33 -0
  72. package/dist/v2/runtime/handlers/handle-debug-events.cjs.map +1 -0
  73. package/dist/v2/runtime/handlers/handle-debug-events.mjs +32 -0
  74. package/dist/v2/runtime/handlers/handle-debug-events.mjs.map +1 -0
  75. package/dist/v2/runtime/handlers/handle-run.cjs +1 -0
  76. package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
  77. package/dist/v2/runtime/handlers/handle-run.mjs +1 -0
  78. package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
  79. package/dist/v2/runtime/handlers/intelligence/connect.cjs +24 -4
  80. package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
  81. package/dist/v2/runtime/handlers/intelligence/connect.mjs +25 -5
  82. package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
  83. package/dist/v2/runtime/handlers/intelligence/run.cjs +111 -26
  84. package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
  85. package/dist/v2/runtime/handlers/intelligence/run.mjs +111 -26
  86. package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
  87. package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs +7 -3
  88. package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs.map +1 -1
  89. package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs +7 -3
  90. package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs.map +1 -1
  91. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs +5 -1
  92. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs.map +1 -1
  93. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs +5 -1
  94. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs.map +1 -1
  95. package/dist/v2/runtime/handlers/shared/sse-response.cjs +21 -1
  96. package/dist/v2/runtime/handlers/shared/sse-response.cjs.map +1 -1
  97. package/dist/v2/runtime/handlers/shared/sse-response.mjs +21 -1
  98. package/dist/v2/runtime/handlers/shared/sse-response.mjs.map +1 -1
  99. package/dist/v2/runtime/handlers/sse/connect.cjs +3 -1
  100. package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
  101. package/dist/v2/runtime/handlers/sse/connect.mjs +3 -1
  102. package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
  103. package/dist/v2/runtime/handlers/sse/run.cjs +3 -1
  104. package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
  105. package/dist/v2/runtime/handlers/sse/run.mjs +3 -1
  106. package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
  107. package/dist/v2/runtime/index.d.cts +1 -1
  108. package/dist/v2/runtime/index.d.mts +1 -2
  109. package/dist/v2/runtime/index.d.mts.map +1 -1
  110. package/dist/v2/runtime/intelligence-platform/client.cjs +6 -8
  111. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  112. package/dist/v2/runtime/intelligence-platform/client.d.cts +16 -21
  113. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  114. package/dist/v2/runtime/intelligence-platform/client.d.mts +16 -21
  115. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  116. package/dist/v2/runtime/intelligence-platform/client.mjs +6 -8
  117. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  118. package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
  119. package/dist/v2/runtime/runner/agent-runner.d.cts +0 -1
  120. package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
  121. package/dist/v2/runtime/runner/agent-runner.d.mts +0 -1
  122. package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
  123. package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
  124. package/dist/v2/runtime/runner/index.d.cts +1 -1
  125. package/dist/v2/runtime/runner/index.d.mts +1 -1
  126. package/dist/v2/runtime/runner/intelligence.cjs +47 -10
  127. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  128. package/dist/v2/runtime/runner/intelligence.d.cts +8 -1
  129. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  130. package/dist/v2/runtime/runner/intelligence.d.mts +8 -1
  131. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  132. package/dist/v2/runtime/runner/intelligence.mjs +47 -10
  133. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  134. package/dist/v2/runtime/telemetry/instance-created.cjs +33 -0
  135. package/dist/v2/runtime/telemetry/instance-created.cjs.map +1 -0
  136. package/dist/v2/runtime/telemetry/instance-created.mjs +33 -0
  137. package/dist/v2/runtime/telemetry/instance-created.mjs.map +1 -0
  138. package/dist/v2/runtime/telemetry/telemetry-client.cjs +1 -38
  139. package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
  140. package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -37
  141. package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
  142. package/package.json +8 -8
  143. package/src/agent/__tests__/agent-test-helpers.ts +31 -1
  144. package/src/agent/__tests__/converter-tanstack.test.ts +280 -0
  145. package/src/agent/converters/tanstack.ts +167 -10
  146. package/src/agents/langgraph/__tests__/event-source.test.ts +256 -0
  147. package/src/graphql/resolvers/__tests__/resolve-message-id.test.ts +25 -0
  148. package/src/graphql/resolvers/copilot.resolver.ts +2 -1
  149. package/src/graphql/resolvers/resolve-message-id.ts +14 -0
  150. package/src/lib/runtime/__tests__/handle-service-adapter.test.ts +108 -0
  151. package/src/lib/runtime/__tests__/retry-utils.test.ts +137 -0
  152. package/src/lib/runtime/agent-integrations/langgraph/__tests__/dispatch-event-filtering.test.ts +190 -0
  153. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +8 -1
  154. package/src/lib/runtime/copilot-runtime.ts +20 -4
  155. package/src/lib/runtime/retry-utils.ts +41 -1
  156. package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +1 -1
  157. package/src/v2/runtime/__tests__/express-single-telemetry.integration.test.ts +65 -0
  158. package/src/v2/runtime/__tests__/express-telemetry.integration.test.ts +101 -0
  159. package/src/v2/runtime/__tests__/fetch-router.test.ts +22 -0
  160. package/src/v2/runtime/__tests__/handle-connect.test.ts +183 -23
  161. package/src/v2/runtime/__tests__/handle-run.test.ts +411 -33
  162. package/src/v2/runtime/__tests__/handle-threads.test.ts +66 -4
  163. package/src/v2/runtime/__tests__/hono-single-telemetry.integration.test.ts +46 -0
  164. package/src/v2/runtime/__tests__/hono-telemetry.integration.test.ts +99 -0
  165. package/src/v2/runtime/__tests__/integration/node-servers.integration.test.ts +19 -0
  166. package/src/v2/runtime/__tests__/integration/suites/debug-events.suite.ts +253 -0
  167. package/src/v2/runtime/__tests__/intelligence-run-telemetry.test.ts +194 -0
  168. package/src/v2/runtime/__tests__/runtime.test.ts +3 -1
  169. package/src/v2/runtime/__tests__/sse-response-telemetry.test.ts +108 -0
  170. package/src/v2/runtime/__tests__/telemetry.test.ts +0 -61
  171. package/src/v2/runtime/core/__tests__/debug-event-bus.test.ts +156 -0
  172. package/src/v2/runtime/core/debug-event-bus.ts +45 -0
  173. package/src/v2/runtime/core/fetch-handler.ts +7 -0
  174. package/src/v2/runtime/core/fetch-router.ts +11 -0
  175. package/src/v2/runtime/core/hooks.ts +2 -1
  176. package/src/v2/runtime/core/runtime.ts +12 -0
  177. package/src/v2/runtime/endpoints/express.ts +9 -3
  178. package/src/v2/runtime/handlers/__tests__/handle-debug-events.test.ts +176 -0
  179. package/src/v2/runtime/handlers/handle-connect.ts +2 -1
  180. package/src/v2/runtime/handlers/handle-debug-events.ts +52 -0
  181. package/src/v2/runtime/handlers/handle-run.ts +1 -0
  182. package/src/v2/runtime/handlers/intelligence/connect.ts +48 -11
  183. package/src/v2/runtime/handlers/intelligence/run.ts +162 -21
  184. package/src/v2/runtime/handlers/shared/intelligence-utils.ts +21 -1
  185. package/src/v2/runtime/handlers/shared/resolve-intelligence-user.ts +4 -1
  186. package/src/v2/runtime/handlers/shared/sse-response.ts +46 -0
  187. package/src/v2/runtime/handlers/sse/__tests__/sse-connect-agent-id.test.ts +71 -0
  188. package/src/v2/runtime/handlers/sse/connect.ts +6 -0
  189. package/src/v2/runtime/handlers/sse/run.ts +4 -0
  190. package/src/v2/runtime/intelligence-platform/__tests__/client.test.ts +33 -37
  191. package/src/v2/runtime/intelligence-platform/client.ts +37 -40
  192. package/src/v2/runtime/runner/__tests__/intelligence-runner.test.ts +66 -8
  193. package/src/v2/runtime/runner/agent-runner.ts +0 -1
  194. package/src/v2/runtime/runner/intelligence.ts +74 -15
  195. package/src/v2/runtime/telemetry/__tests__/instance-created.test.ts +96 -0
  196. package/src/v2/runtime/telemetry/instance-created.ts +44 -0
  197. package/src/v2/runtime/telemetry/telemetry-client.ts +1 -57
  198. package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
  199. package/dist/v2/runtime/telemetry/utils.cjs +0 -15
  200. package/dist/v2/runtime/telemetry/utils.cjs.map +0 -1
  201. package/dist/v2/runtime/telemetry/utils.mjs +0 -14
  202. package/dist/v2/runtime/telemetry/utils.mjs.map +0 -1
  203. package/src/v2/runtime/telemetry/utils.ts +0 -15
@@ -286,6 +286,49 @@ describe("IntelligenceAgentRunner", () => {
286
286
  expect(ch.pushLog[2].payload.metadata.cpki_event_seq).toBe(3);
287
287
  });
288
288
 
289
+ it("overrides conflicting event thread and run ownership before pushing to the channel", async () => {
290
+ const threadId = "t-canonical";
291
+ const input = createRunInput({ threadId, runId: "r-canonical" });
292
+
293
+ const agent = new MockAgent([
294
+ {
295
+ type: EventType.RUN_STARTED,
296
+ threadId: "backend-thread",
297
+ runId: "backend-run",
298
+ } as RunStartedEvent,
299
+ {
300
+ type: EventType.RUN_FINISHED,
301
+ threadId: "backend-thread",
302
+ runId: "backend-run",
303
+ } as RunFinishedEvent,
304
+ ]);
305
+
306
+ const eventsPromise = collectEvents(
307
+ runner.run({ threadId, agent, input }),
308
+ );
309
+ const ch = mockChannels[0];
310
+ ch.triggerJoin("ok");
311
+
312
+ await eventsPromise;
313
+
314
+ expect(ch.pushLog.map((entry) => entry.payload)).toEqual([
315
+ expect.objectContaining({
316
+ type: EventType.RUN_STARTED,
317
+ threadId,
318
+ runId: input.runId,
319
+ thread_id: threadId,
320
+ run_id: input.runId,
321
+ }),
322
+ expect.objectContaining({
323
+ type: EventType.RUN_FINISHED,
324
+ threadId,
325
+ runId: input.runId,
326
+ thread_id: threadId,
327
+ run_id: input.runId,
328
+ }),
329
+ ]);
330
+ });
331
+
289
332
  it("rewrites RUN_STARTED input.messages to the unseen persisted subset", async () => {
290
333
  const threadId = "t-persisted-input";
291
334
  const input = createRunInput({
@@ -442,7 +485,14 @@ describe("IntelligenceAgentRunner", () => {
442
485
  (p) => p.payload?.type === EventType.RUN_ERROR,
443
486
  );
444
487
  expect(errorPush).toBeDefined();
445
- expect(errorPush!.payload.message).toBe("Something went wrong");
488
+ expect(errorPush!.payload).toMatchObject({
489
+ type: EventType.RUN_ERROR,
490
+ message: "Something went wrong",
491
+ threadId,
492
+ runId: input.runId,
493
+ thread_id: threadId,
494
+ run_id: input.runId,
495
+ });
446
496
  });
447
497
 
448
498
  it("finalizes open message streams before completing", async () => {
@@ -569,10 +619,9 @@ describe("IntelligenceAgentRunner", () => {
569
619
  });
570
620
  });
571
621
 
572
- describe("run with joinCode", () => {
573
- it("uses joinCode for the channel topic when provided", async () => {
622
+ describe("run channel ownership", () => {
623
+ it("uses runId for the ingestion channel topic", async () => {
574
624
  const threadId = "t-jc";
575
- const joinCode = "join-abc-123";
576
625
  const input = createRunInput({ threadId, runId: "r-jc" });
577
626
  const agent = new MockAgent([
578
627
  {
@@ -583,15 +632,16 @@ describe("IntelligenceAgentRunner", () => {
583
632
  ]);
584
633
 
585
634
  const eventsPromise = collectEvents(
586
- runner.run({ threadId, agent, input, joinCode }),
635
+ runner.run({ threadId, agent, input }),
587
636
  );
588
637
  const ch = mockChannels[0];
589
- expect(ch.topic).toBe(`ingestion:${joinCode}`);
638
+ expect(ch.topic).toBe("ingestion:r-jc");
639
+ expect(ch.params).toEqual({ thread_id: threadId, run_id: "r-jc" });
590
640
  ch.triggerJoin("ok");
591
641
  await eventsPromise;
592
642
  });
593
643
 
594
- it("falls back to threadId when joinCode is not provided", async () => {
644
+ it("keeps pushed event payload ownership on canonical threadId and runId", async () => {
595
645
  const threadId = "t-no-jc";
596
646
  const input = createRunInput({ threadId, runId: "r-no-jc" });
597
647
  const agent = new MockAgent([
@@ -606,9 +656,17 @@ describe("IntelligenceAgentRunner", () => {
606
656
  runner.run({ threadId, agent, input }),
607
657
  );
608
658
  const ch = mockChannels[0];
609
- expect(ch.topic).toBe(`ingestion:${threadId}`);
610
659
  ch.triggerJoin("ok");
611
660
  await eventsPromise;
661
+
662
+ expect(ch.pushLog[0].payload).toEqual(
663
+ expect.objectContaining({
664
+ threadId,
665
+ runId: "r-no-jc",
666
+ thread_id: threadId,
667
+ run_id: "r-no-jc",
668
+ }),
669
+ );
612
670
  });
613
671
  });
614
672
 
@@ -10,7 +10,6 @@ export interface AgentRunnerRunRequest {
10
10
  threadId: string;
11
11
  agent: AbstractAgent;
12
12
  input: RunAgentInput;
13
- joinCode?: string;
14
13
  persistedInputMessages?: Message[];
15
14
  }
16
15
 
@@ -32,6 +32,11 @@ export interface IntelligenceAgentRunnerOptions {
32
32
  maxRejoinMs?: number;
33
33
  }
34
34
 
35
+ export interface RunnerStartupBoundary {
36
+ events: Observable<BaseEvent>;
37
+ startup: Promise<void>;
38
+ }
39
+
35
40
  interface ThreadState {
36
41
  socket: Socket;
37
42
  channel: Channel;
@@ -95,22 +100,33 @@ export class IntelligenceAgentRunner extends AgentRunner {
95
100
  request: AgentRunnerRunRequest,
96
101
  state: ThreadState,
97
102
  ): Record<string, unknown> {
98
- const canonicalEvent = this.stampRunnerMetadata(event, state);
103
+ const canonicalEvent = this.stampRunnerMetadata(
104
+ this.stampCanonicalRunOwnership(event, request),
105
+ state,
106
+ );
99
107
  const payload = {
100
108
  ...(canonicalEvent as Record<string, unknown>),
101
109
  };
102
110
 
103
- payload.thread_id ??= request.threadId;
104
-
105
- const runId = payload.runId ?? payload.run_id ?? request.input.runId;
106
-
107
- if (runId) {
108
- payload.run_id = runId;
109
- }
111
+ payload.threadId = request.threadId;
112
+ payload.runId = request.input.runId;
113
+ payload.thread_id = request.threadId;
114
+ payload.run_id = request.input.runId;
110
115
 
111
116
  return payload;
112
117
  }
113
118
 
119
+ private stampCanonicalRunOwnership(
120
+ event: BaseEvent,
121
+ request: AgentRunnerRunRequest,
122
+ ): BaseEvent {
123
+ return {
124
+ ...(event as BaseEvent & Record<string, unknown>),
125
+ threadId: request.threadId,
126
+ runId: request.input.runId,
127
+ } as BaseEvent;
128
+ }
129
+
114
130
  private stampRunnerMetadata(event: BaseEvent, state: ThreadState): BaseEvent {
115
131
  const eventRecord = event as BaseEvent & {
116
132
  metadata?: Record<string, unknown>;
@@ -142,7 +158,36 @@ export class IntelligenceAgentRunner extends AgentRunner {
142
158
  }
143
159
 
144
160
  run(request: AgentRunnerRunRequest): Observable<BaseEvent> {
145
- const { threadId, agent, input, joinCode } = request;
161
+ return this.createRunObservable(request);
162
+ }
163
+
164
+ runWithStartupBoundary(
165
+ request: AgentRunnerRunRequest,
166
+ ): RunnerStartupBoundary {
167
+ let resolveStartup: (() => void) | undefined;
168
+ let rejectStartup: ((reason: Error) => void) | undefined;
169
+ const startup = new Promise<void>((resolve, reject) => {
170
+ resolveStartup = resolve;
171
+ rejectStartup = reject;
172
+ });
173
+
174
+ return {
175
+ events: this.createRunObservable(request, {
176
+ resolveStartup: () => resolveStartup?.(),
177
+ rejectStartup: (error) => rejectStartup?.(error),
178
+ }),
179
+ startup,
180
+ };
181
+ }
182
+
183
+ private createRunObservable(
184
+ request: AgentRunnerRunRequest,
185
+ startupBoundary?: {
186
+ resolveStartup: () => void;
187
+ rejectStartup: (error: Error) => void;
188
+ },
189
+ ): Observable<BaseEvent> {
190
+ const { threadId, agent, input } = request;
146
191
 
147
192
  const existing = this.threads.get(threadId);
148
193
  if (existing?.isRunning) {
@@ -152,9 +197,9 @@ export class IntelligenceAgentRunner extends AgentRunner {
152
197
  return new Observable((observer) => {
153
198
  const socket = this.createSocket();
154
199
 
155
- const channelTopic = joinCode ?? threadId;
156
- const channel = socket.channel(`ingestion:${channelTopic}`, {
157
- runId: input.runId,
200
+ const channel = socket.channel(`ingestion:${input.runId}`, {
201
+ thread_id: threadId,
202
+ run_id: input.runId,
158
203
  });
159
204
 
160
205
  const state: ThreadState = {
@@ -218,30 +263,37 @@ export class IntelligenceAgentRunner extends AgentRunner {
218
263
  channel
219
264
  .join()
220
265
  .receive("ok", () => {
266
+ startupBoundary?.resolveStartup();
221
267
  this.executeAgentRun(request, state, threadId).subscribe({
222
268
  complete: () => observer.complete(),
223
269
  });
224
270
  })
225
271
  .receive("error", (resp) => {
272
+ const error = new Error(
273
+ `Failed to join channel: ${JSON.stringify(resp)}`,
274
+ );
226
275
  const errorEvent = {
227
276
  type: EventType.RUN_ERROR,
228
- message: `Failed to join channel: ${JSON.stringify(resp)}`,
277
+ message: error.message,
229
278
  code: "CHANNEL_JOIN_ERROR",
230
279
  } as BaseEvent;
231
280
  observer.next(errorEvent);
232
281
  state.currentEvents.push(errorEvent);
233
282
  this.removeThread(threadId);
283
+ startupBoundary?.rejectStartup(error);
234
284
  observer.complete();
235
285
  })
236
286
  .receive("timeout", () => {
287
+ const error = new Error("Timed out joining channel");
237
288
  const errorEvent = {
238
289
  type: EventType.RUN_ERROR,
239
- message: "Timed out joining channel",
290
+ message: error.message,
240
291
  code: "CHANNEL_JOIN_TIMEOUT",
241
292
  } as BaseEvent;
242
293
  observer.next(errorEvent);
243
294
  state.currentEvents.push(errorEvent);
244
295
  this.removeThread(threadId);
296
+ startupBoundary?.rejectStartup(error);
245
297
  observer.complete();
246
298
  });
247
299
 
@@ -327,7 +379,10 @@ export class IntelligenceAgentRunner extends AgentRunner {
327
379
  ): Observable<void> {
328
380
  const { currentEvents, channel } = state;
329
381
  const pushCanonicalEvent = (event: BaseEvent): void => {
330
- const canonicalEvent = this.stampRunnerMetadata(event, state);
382
+ const canonicalEvent = this.stampRunnerMetadata(
383
+ this.stampCanonicalRunOwnership(event, request),
384
+ state,
385
+ );
331
386
  currentEvents.push(canonicalEvent);
332
387
 
333
388
  if (canonicalEvent.type === EventType.RUN_STARTED) {
@@ -355,8 +410,12 @@ export class IntelligenceAgentRunner extends AgentRunner {
355
410
  threadId: request.threadId,
356
411
  runId: request.input.runId,
357
412
  }),
413
+ threadId: request.threadId,
414
+ runId: request.input.runId,
358
415
  input: {
359
416
  ...baseInput,
417
+ threadId: request.threadId,
418
+ runId: request.input.runId,
360
419
  ...(persistedInputMessages !== undefined
361
420
  ? { messages: persistedInputMessages }
362
421
  : {}),
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { telemetry } from "..";
3
+ import { fireInstanceCreatedTelemetry } from "../instance-created";
4
+ import type { CopilotRuntimeLike } from "../../core/runtime";
5
+
6
+ // Minimal runtime stub: we only use `agents` from CopilotRuntimeLike inside
7
+ // the helper, so we cast the stub rather than construct a full runtime.
8
+ function makeRuntime(
9
+ agents:
10
+ | Record<string, unknown>
11
+ | Promise<Record<string, unknown>>
12
+ | ((ctx: { request: Request }) => Record<string, unknown>),
13
+ ): CopilotRuntimeLike {
14
+ return { agents } as unknown as CopilotRuntimeLike;
15
+ }
16
+
17
+ describe("fireInstanceCreatedTelemetry", () => {
18
+ let captureSpy: ReturnType<typeof vi.spyOn>;
19
+
20
+ beforeEach(() => {
21
+ captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
22
+ });
23
+
24
+ afterEach(() => {
25
+ captureSpy.mockRestore();
26
+ });
27
+
28
+ it("captures instance_created with agent count from static agents record", async () => {
29
+ fireInstanceCreatedTelemetry({
30
+ runtime: makeRuntime({ a1: {}, a2: {}, a3: {} }),
31
+ });
32
+
33
+ await vi.waitFor(() => expect(captureSpy).toHaveBeenCalled());
34
+
35
+ expect(captureSpy).toHaveBeenCalledWith("oss.runtime.instance_created", {
36
+ actionsAmount: 0,
37
+ endpointTypes: [],
38
+ endpointsAmount: 0,
39
+ agentsAmount: 3,
40
+ "cloud.api_key_provided": false,
41
+ });
42
+ });
43
+
44
+ it("awaits Promise-based agents before capturing", async () => {
45
+ fireInstanceCreatedTelemetry({
46
+ runtime: makeRuntime(Promise.resolve({ only: {} })),
47
+ });
48
+
49
+ await vi.waitFor(() => expect(captureSpy).toHaveBeenCalled());
50
+
51
+ const call = captureSpy.mock.calls[0][1] as { agentsAmount: number | null };
52
+ expect(call.agentsAmount).toBe(1);
53
+ });
54
+
55
+ it("reports agentsAmount: null when agents is a factory (cannot resolve without request)", async () => {
56
+ fireInstanceCreatedTelemetry({
57
+ runtime: makeRuntime(() => ({ x: {} })),
58
+ });
59
+
60
+ await vi.waitFor(() => expect(captureSpy).toHaveBeenCalled());
61
+
62
+ const call = captureSpy.mock.calls[0][1] as { agentsAmount: number | null };
63
+ expect(call.agentsAmount).toBeNull();
64
+ });
65
+
66
+ it("does not hardcode cloud.api_key_provided — it is false at handler-creation time by design (key arrives per-request via header)", async () => {
67
+ fireInstanceCreatedTelemetry({
68
+ runtime: makeRuntime({ a1: {} }),
69
+ });
70
+
71
+ await vi.waitFor(() => expect(captureSpy).toHaveBeenCalled());
72
+
73
+ const call = captureSpy.mock.calls[0][1] as {
74
+ "cloud.api_key_provided": boolean;
75
+ };
76
+ expect(call["cloud.api_key_provided"]).toBe(false);
77
+ });
78
+
79
+ it("does not throw or reject when agents Promise rejects", async () => {
80
+ // Swallow the unhandled rejection from the input Promise itself — the
81
+ // Promise we pass in rejects synchronously regardless of whether we
82
+ // attach a .catch downstream.
83
+ const rejectingAgents = Promise.reject(new Error("boom"));
84
+ rejectingAgents.catch(() => {});
85
+
86
+ expect(() =>
87
+ fireInstanceCreatedTelemetry({
88
+ runtime: makeRuntime(rejectingAgents as any),
89
+ }),
90
+ ).not.toThrow();
91
+
92
+ // Wait a microtask to let the internal catch fire; no capture should happen.
93
+ await new Promise((resolve) => setTimeout(resolve, 10));
94
+ expect(captureSpy).not.toHaveBeenCalled();
95
+ });
96
+ });
@@ -0,0 +1,44 @@
1
+ import { telemetry } from ".";
2
+ import type { CopilotRuntimeLike } from "../core/runtime";
3
+
4
+ /**
5
+ * Fire the `oss.runtime.instance_created` telemetry event for a v2 runtime
6
+ * handler. Called once per handler factory invocation (not per request).
7
+ *
8
+ * v2 does not have a concept of remote endpoints or standalone actions, so
9
+ * those counts are 0 / []. `cloud.api_key_provided` is false at this level
10
+ * because in v2 the cloud public key arrives per-request via the
11
+ * `x-copilotcloud-public-api-key` header — not at handler creation time.
12
+ * See `handlers/handle-run.ts` for the per-request event that DOES carry
13
+ * the key when present.
14
+ *
15
+ * Errors resolving agents are swallowed — telemetry must never break
16
+ * runtime setup.
17
+ */
18
+ export function fireInstanceCreatedTelemetry({
19
+ runtime,
20
+ }: {
21
+ runtime: CopilotRuntimeLike;
22
+ }): void {
23
+ // agents can be a static Record, a Promise, or a per-request factory.
24
+ // Factory configs cannot be resolved at handler-creation time (no Request
25
+ // context), so report agentsAmount as null in that case.
26
+ const agentsPromise =
27
+ typeof runtime.agents === "function"
28
+ ? Promise.resolve<Record<string, unknown> | null>(null)
29
+ : Promise.resolve(runtime.agents);
30
+
31
+ agentsPromise
32
+ .then((agents) => {
33
+ telemetry.capture("oss.runtime.instance_created", {
34
+ actionsAmount: 0,
35
+ endpointTypes: [],
36
+ endpointsAmount: 0,
37
+ agentsAmount: agents ? Object.keys(agents).length : null,
38
+ "cloud.api_key_provided": false,
39
+ });
40
+ })
41
+ .catch(() => {
42
+ // Swallow — telemetry must not break runtime creation.
43
+ });
44
+ }
@@ -1,7 +1,4 @@
1
- import { Analytics } from "@segment/analytics-node";
2
1
  import { AnalyticsEvents } from "./events";
3
- import { flattenObject } from "./utils";
4
- import { v4 as uuidv4 } from "uuid";
5
2
  import scarfClient from "./scarf-client";
6
3
 
7
4
  export function isTelemetryDisabled(): boolean {
@@ -17,11 +14,8 @@ export function isTelemetryDisabled(): boolean {
17
14
  }
18
15
 
19
16
  export class TelemetryClient {
20
- segment: Analytics | undefined;
21
- globalProperties: Record<string, any> = {};
22
17
  private telemetryDisabled: boolean = false;
23
18
  private sampleRate: number = 0.05;
24
- private anonymousId = `anon_${uuidv4()}`;
25
19
 
26
20
  constructor({
27
21
  telemetryDisabled,
@@ -31,21 +25,7 @@ export class TelemetryClient {
31
25
  sampleRate?: number;
32
26
  } = {}) {
33
27
  this.telemetryDisabled = telemetryDisabled ?? isTelemetryDisabled();
34
-
35
- if (this.telemetryDisabled) {
36
- this.setSampleRate(sampleRate);
37
- return;
38
- }
39
-
40
28
  this.setSampleRate(sampleRate);
41
-
42
- const writeKey =
43
- process.env.COPILOTKIT_SEGMENT_WRITE_KEY ||
44
- "n7XAZtQCGS2v1vvBy3LgBCv2h3Y8whja";
45
-
46
- this.segment = new Analytics({
47
- writeKey,
48
- });
49
29
  }
50
30
 
51
31
  private shouldSendEvent() {
@@ -58,48 +38,17 @@ export class TelemetryClient {
58
38
 
59
39
  async capture<K extends keyof AnalyticsEvents>(
60
40
  event: K,
61
- properties: AnalyticsEvents[K],
41
+ _properties: AnalyticsEvents[K],
62
42
  ) {
63
43
  if (!this.shouldSendEvent()) {
64
44
  return;
65
45
  }
66
46
 
67
- const flattenedProperties = flattenObject(properties);
68
- const propertiesWithGlobal = {
69
- ...this.globalProperties,
70
- ...flattenedProperties,
71
- };
72
- const orderedPropertiesWithGlobal = Object.keys(propertiesWithGlobal)
73
- .sort()
74
- .reduce(
75
- (obj, key) => {
76
- obj[key] = propertiesWithGlobal[key];
77
- return obj;
78
- },
79
- {} as Record<string, any>,
80
- );
81
-
82
- if (this.segment) {
83
- this.segment.track({
84
- anonymousId: this.anonymousId,
85
- event,
86
- properties: { ...orderedPropertiesWithGlobal },
87
- });
88
- }
89
-
90
47
  await scarfClient.logEvent({
91
48
  event,
92
49
  });
93
50
  }
94
51
 
95
- setGlobalProperties(properties: Record<string, any>) {
96
- const flattenedProperties = flattenObject(properties);
97
- this.globalProperties = {
98
- ...this.globalProperties,
99
- ...flattenedProperties,
100
- };
101
- }
102
-
103
52
  private setSampleRate(sampleRate: number | undefined) {
104
53
  let _sampleRate: number;
105
54
 
@@ -114,11 +63,6 @@ export class TelemetryClient {
114
63
  }
115
64
 
116
65
  this.sampleRate = _sampleRate;
117
- this.setGlobalProperties({
118
- sampleRate: this.sampleRate,
119
- sampleRateAdjustmentFactor: 1 - this.sampleRate,
120
- sampleWeight: 1 / this.sampleRate,
121
- });
122
66
  }
123
67
  }
124
68
 
@@ -1,2 +0,0 @@
1
- import "reflect-metadata";
2
- import { CopilotKitIntelligence, CopilotKitIntelligenceConfig, CreateThreadRequest, ListThreadsResponse, SubscribeToThreadsRequest, SubscribeToThreadsResponse, ThreadSummary, UpdateThreadRequest } from "./client.mjs";
@@ -1,15 +0,0 @@
1
- require("reflect-metadata");
2
-
3
- //#region src/v2/runtime/telemetry/utils.ts
4
- function flattenObject(obj, parentKey = "", res = {}) {
5
- for (const key in obj) {
6
- const propName = parentKey ? `${parentKey}.${key}` : key;
7
- if (typeof obj[key] === "object" && obj[key] !== null) flattenObject(obj[key], propName, res);
8
- else res[propName] = obj[key];
9
- }
10
- return res;
11
- }
12
-
13
- //#endregion
14
- exports.flattenObject = flattenObject;
15
- //# sourceMappingURL=utils.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.cjs","names":[],"sources":["../../../../src/v2/runtime/telemetry/utils.ts"],"sourcesContent":["export function flattenObject(\n obj: Record<string, any>,\n parentKey = \"\",\n res: Record<string, any> = {},\n): Record<string, any> {\n for (const key in obj) {\n const propName = parentKey ? `${parentKey}.${key}` : key;\n if (typeof obj[key] === \"object\" && obj[key] !== null) {\n flattenObject(obj[key], propName, res);\n } else {\n res[propName] = obj[key];\n }\n }\n return res;\n}\n"],"mappings":";;;AAAA,SAAgB,cACd,KACA,YAAY,IACZ,MAA2B,EAAE,EACR;AACrB,MAAK,MAAM,OAAO,KAAK;EACrB,MAAM,WAAW,YAAY,GAAG,UAAU,GAAG,QAAQ;AACrD,MAAI,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,KAC/C,eAAc,IAAI,MAAM,UAAU,IAAI;MAEtC,KAAI,YAAY,IAAI;;AAGxB,QAAO"}
@@ -1,14 +0,0 @@
1
- import "reflect-metadata";
2
- //#region src/v2/runtime/telemetry/utils.ts
3
- function flattenObject(obj, parentKey = "", res = {}) {
4
- for (const key in obj) {
5
- const propName = parentKey ? `${parentKey}.${key}` : key;
6
- if (typeof obj[key] === "object" && obj[key] !== null) flattenObject(obj[key], propName, res);
7
- else res[propName] = obj[key];
8
- }
9
- return res;
10
- }
11
-
12
- //#endregion
13
- export { flattenObject };
14
- //# sourceMappingURL=utils.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../../../../src/v2/runtime/telemetry/utils.ts"],"sourcesContent":["export function flattenObject(\n obj: Record<string, any>,\n parentKey = \"\",\n res: Record<string, any> = {},\n): Record<string, any> {\n for (const key in obj) {\n const propName = parentKey ? `${parentKey}.${key}` : key;\n if (typeof obj[key] === \"object\" && obj[key] !== null) {\n flattenObject(obj[key], propName, res);\n } else {\n res[propName] = obj[key];\n }\n }\n return res;\n}\n"],"mappings":";;AAAA,SAAgB,cACd,KACA,YAAY,IACZ,MAA2B,EAAE,EACR;AACrB,MAAK,MAAM,OAAO,KAAK;EACrB,MAAM,WAAW,YAAY,GAAG,UAAU,GAAG,QAAQ;AACrD,MAAI,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,KAC/C,eAAc,IAAI,MAAM,UAAU,IAAI;MAEtC,KAAI,YAAY,IAAI;;AAGxB,QAAO"}
@@ -1,15 +0,0 @@
1
- export function flattenObject(
2
- obj: Record<string, any>,
3
- parentKey = "",
4
- res: Record<string, any> = {},
5
- ): Record<string, any> {
6
- for (const key in obj) {
7
- const propName = parentKey ? `${parentKey}.${key}` : key;
8
- if (typeof obj[key] === "object" && obj[key] !== null) {
9
- flattenObject(obj[key], propName, res);
10
- } else {
11
- res[propName] = obj[key];
12
- }
13
- }
14
- return res;
15
- }