@copilotkit/runtime 1.64.2-canary.rc-1 → 1.64.2

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 (79) hide show
  1. package/dist/package.cjs +1 -1
  2. package/dist/runtime/package.mjs +1 -1
  3. package/dist/v2/runtime/core/channel-activation-config.cjs +5 -2
  4. package/dist/v2/runtime/core/channel-activation-config.cjs.map +1 -1
  5. package/dist/v2/runtime/core/channel-activation-config.d.cts +13 -0
  6. package/dist/v2/runtime/core/channel-activation-config.d.cts.map +1 -1
  7. package/dist/v2/runtime/core/channel-activation-config.d.mts +13 -0
  8. package/dist/v2/runtime/core/channel-activation-config.d.mts.map +1 -1
  9. package/dist/v2/runtime/core/channel-activation-config.mjs +5 -2
  10. package/dist/v2/runtime/core/channel-activation-config.mjs.map +1 -1
  11. package/dist/v2/runtime/core/channel-manager.cjs +257 -20
  12. package/dist/v2/runtime/core/channel-manager.cjs.map +1 -1
  13. package/dist/v2/runtime/core/channel-manager.d.cts +9 -5
  14. package/dist/v2/runtime/core/channel-manager.d.cts.map +1 -1
  15. package/dist/v2/runtime/core/channel-manager.d.mts +9 -5
  16. package/dist/v2/runtime/core/channel-manager.d.mts.map +1 -1
  17. package/dist/v2/runtime/core/channel-manager.mjs +257 -20
  18. package/dist/v2/runtime/core/channel-manager.mjs.map +1 -1
  19. package/dist/v2/runtime/core/fetch-handler.cjs +4 -0
  20. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
  21. package/dist/v2/runtime/core/fetch-handler.d.cts +5 -0
  22. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
  23. package/dist/v2/runtime/core/fetch-handler.d.mts +5 -0
  24. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
  25. package/dist/v2/runtime/core/fetch-handler.mjs +4 -0
  26. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
  27. package/dist/v2/runtime/endpoints/auto-start-channels.cjs +47 -0
  28. package/dist/v2/runtime/endpoints/auto-start-channels.cjs.map +1 -0
  29. package/dist/v2/runtime/endpoints/auto-start-channels.mjs +46 -0
  30. package/dist/v2/runtime/endpoints/auto-start-channels.mjs.map +1 -0
  31. package/dist/v2/runtime/endpoints/express.cjs +2 -0
  32. package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
  33. package/dist/v2/runtime/endpoints/express.d.cts +9 -8
  34. package/dist/v2/runtime/endpoints/express.d.cts.map +1 -1
  35. package/dist/v2/runtime/endpoints/express.d.mts +9 -8
  36. package/dist/v2/runtime/endpoints/express.d.mts.map +1 -1
  37. package/dist/v2/runtime/endpoints/express.mjs +2 -0
  38. package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
  39. package/dist/v2/runtime/endpoints/hono.cjs.map +1 -1
  40. package/dist/v2/runtime/endpoints/hono.d.cts +14 -5
  41. package/dist/v2/runtime/endpoints/hono.d.cts.map +1 -1
  42. package/dist/v2/runtime/endpoints/hono.d.mts +14 -5
  43. package/dist/v2/runtime/endpoints/hono.d.mts.map +1 -1
  44. package/dist/v2/runtime/endpoints/hono.mjs.map +1 -1
  45. package/dist/v2/runtime/endpoints/node.cjs +2 -0
  46. package/dist/v2/runtime/endpoints/node.cjs.map +1 -1
  47. package/dist/v2/runtime/endpoints/node.d.cts +23 -13
  48. package/dist/v2/runtime/endpoints/node.d.cts.map +1 -1
  49. package/dist/v2/runtime/endpoints/node.d.mts +23 -13
  50. package/dist/v2/runtime/endpoints/node.d.mts.map +1 -1
  51. package/dist/v2/runtime/endpoints/node.mjs +2 -0
  52. package/dist/v2/runtime/endpoints/node.mjs.map +1 -1
  53. package/dist/v2/runtime/intelligence-platform/client.cjs +33 -2
  54. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  55. package/dist/v2/runtime/intelligence-platform/client.d.cts +14 -3
  56. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  57. package/dist/v2/runtime/intelligence-platform/client.d.mts +14 -3
  58. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  59. package/dist/v2/runtime/intelligence-platform/client.mjs +33 -2
  60. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  61. package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
  62. package/dist/v2/runtime/runner/agent-runner.d.cts +4 -0
  63. package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
  64. package/dist/v2/runtime/runner/agent-runner.d.mts +4 -0
  65. package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
  66. package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
  67. package/dist/v2/runtime/runner/in-memory.cjs +3 -5
  68. package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
  69. package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
  70. package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
  71. package/dist/v2/runtime/runner/in-memory.mjs +3 -5
  72. package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
  73. package/dist/v2/runtime/runner/intelligence.cjs +38 -31
  74. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  75. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  76. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  77. package/dist/v2/runtime/runner/intelligence.mjs +38 -31
  78. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  79. package/package.json +5 -5
@@ -35,9 +35,9 @@ var IntelligenceAgentRunner = class extends AgentRunner {
35
35
  * closeWasClean = true and reset the reconnect timer — permanently
36
36
  * killing retries.
37
37
  */
38
- createSocket() {
38
+ createSocket(authToken = this.options.authToken) {
39
39
  const socket = new Socket(this.options.url, {
40
- ...this.options.authToken ? { authToken: this.options.authToken } : {},
40
+ ...authToken ? { authToken } : {},
41
41
  reconnectAfterMs: phoenixExponentialBackoff(100, this.options.maxReconnectMs ?? 1e4),
42
42
  rejoinAfterMs: phoenixExponentialBackoff(1e3, this.options.maxRejoinMs ?? 3e4)
43
43
  });
@@ -53,11 +53,10 @@ var IntelligenceAgentRunner = class extends AgentRunner {
53
53
  return payload;
54
54
  }
55
55
  stampCanonicalRunOwnership(event, request) {
56
- return {
57
- ...event,
58
- threadId: request.threadId,
59
- runId: request.input.runId
60
- };
56
+ const eventRecord = event;
57
+ eventRecord.threadId = request.threadId;
58
+ eventRecord.runId = request.input.runId;
59
+ return eventRecord;
61
60
  }
62
61
  stampRunnerMetadata(event, state) {
63
62
  const eventRecord = event;
@@ -70,14 +69,12 @@ var IntelligenceAgentRunner = class extends AgentRunner {
70
69
  return eventRecord;
71
70
  }
72
71
  const eventSeq = state.nextEventSeq++;
73
- return {
74
- ...eventRecord,
75
- metadata: {
76
- ...existingMetadata,
77
- cpki_event_id: typeof existingMetadata.cpki_event_id === "string" ? existingMetadata.cpki_event_id : randomUUID$1(),
78
- cpki_event_seq: eventSeq
79
- }
72
+ eventRecord.metadata = {
73
+ ...existingMetadata,
74
+ cpki_event_id: typeof existingMetadata.cpki_event_id === "string" ? existingMetadata.cpki_event_id : randomUUID$1(),
75
+ cpki_event_seq: eventSeq
80
76
  };
77
+ return eventRecord;
81
78
  }
82
79
  run(request) {
83
80
  return this.createRunObservable(request);
@@ -101,12 +98,13 @@ var IntelligenceAgentRunner = class extends AgentRunner {
101
98
  const { threadId, agent, input } = request;
102
99
  if (this.threads.get(threadId)?.isRunning) throw new Error("Thread already running");
103
100
  return new Observable((observer) => {
104
- const socket = this.createSocket();
101
+ const socket = this.createSocket(request.authToken);
105
102
  const channel = socket.channel(`ingestion:${input.runId}`, {
106
103
  thread_id: threadId,
107
104
  run_id: input.runId
108
105
  });
109
106
  const state = {
107
+ runId: input.runId,
110
108
  socket,
111
109
  channel,
112
110
  isRunning: true,
@@ -133,7 +131,9 @@ var IntelligenceAgentRunner = class extends AgentRunner {
133
131
  });
134
132
  channel.join().receive("ok", () => {
135
133
  startupBoundary?.resolveStartup();
136
- this.executeAgentRun(request, state, threadId).subscribe({ complete: () => observer.complete() });
134
+ this.executeAgentRun(request, state, threadId, (event) => {
135
+ observer.next(event);
136
+ }).subscribe({ complete: () => observer.complete() });
137
137
  }).receive("error", (resp) => {
138
138
  const error = /* @__PURE__ */ new Error(`Failed to join channel: ${JSON.stringify(resp)}`);
139
139
  const errorEvent = {
@@ -196,13 +196,14 @@ var IntelligenceAgentRunner = class extends AgentRunner {
196
196
  stop(request) {
197
197
  const state = this.threads.get(request.threadId);
198
198
  if (!state || !state.isRunning || state.stopRequested) return Promise.resolve(false);
199
+ if (request.runId !== void 0 && state.runId !== request.runId) return Promise.resolve(false);
199
200
  state.stopRequested = true;
200
201
  if (state.agent) try {
201
202
  state.agent.abortRun();
202
203
  } catch {}
203
204
  return Promise.resolve(true);
204
205
  }
205
- executeAgentRun(request, state, threadId) {
206
+ executeAgentRun(request, state, threadId, onRunError) {
206
207
  const { currentEvents, channel } = state;
207
208
  const pushCanonicalEvent = (event) => {
208
209
  const canonicalEvent = this.stampRunnerMetadata(this.stampCanonicalRunOwnership(event, request), state);
@@ -214,21 +215,20 @@ var IntelligenceAgentRunner = class extends AgentRunner {
214
215
  const buildRunStartedEvent = (source) => {
215
216
  const baseInput = source?.input ?? request.input;
216
217
  const persistedInputMessages = getPersistedInputMessages();
217
- return {
218
- ...source ?? {
219
- type: EventType.RUN_STARTED,
220
- threadId: request.threadId,
221
- runId: request.input.runId
222
- },
218
+ const event = source ?? {
219
+ type: EventType.RUN_STARTED,
220
+ threadId: request.threadId,
221
+ runId: request.input.runId
222
+ };
223
+ event.threadId = request.threadId;
224
+ event.runId = request.input.runId;
225
+ event.input = {
226
+ ...baseInput,
223
227
  threadId: request.threadId,
224
228
  runId: request.input.runId,
225
- input: {
226
- ...baseInput,
227
- threadId: request.threadId,
228
- runId: request.input.runId,
229
- ...persistedInputMessages !== void 0 ? { messages: persistedInputMessages } : {}
230
- }
229
+ ...persistedInputMessages !== void 0 ? { messages: persistedInputMessages } : {}
231
230
  };
231
+ return event;
232
232
  };
233
233
  const ensureRunStarted = () => {
234
234
  if (!state.hasRunStarted) {
@@ -245,10 +245,17 @@ var IntelligenceAgentRunner = class extends AgentRunner {
245
245
  pushCanonicalEvent(event);
246
246
  } })).pipe(catchError$1((error) => {
247
247
  ensureRunStarted();
248
- pushCanonicalEvent({
248
+ const existingError = currentEvents.find((event) => event.type === EventType.RUN_ERROR);
249
+ if (existingError) {
250
+ onRunError(existingError);
251
+ return EMPTY;
252
+ }
253
+ const errorEvent = {
249
254
  type: EventType.RUN_ERROR,
250
255
  message: error instanceof Error ? error.message : String(error)
251
- });
256
+ };
257
+ pushCanonicalEvent(errorEvent);
258
+ onRunError(errorEvent);
252
259
  return EMPTY;
253
260
  }), finalize$1(() => {
254
261
  ensureRunStarted();
@@ -1 +1 @@
1
- {"version":3,"file":"intelligence.mjs","names":["randomUUID","catchError","finalize"],"sources":["../../../../src/v2/runtime/runner/intelligence.ts"],"sourcesContent":["import type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n} from \"./agent-runner\";\nimport { AgentRunner } from \"./agent-runner\";\nimport type { AgentRunnerStopRequest } from \"./agent-runner\";\nimport { EMPTY, Observable, from } from \"rxjs\";\nimport { catchError, finalize } from \"rxjs/operators\";\nimport type { AbstractAgent, BaseEvent, RunStartedEvent } from \"@ag-ui/client\";\nimport { EventType } from \"@ag-ui/client\";\nimport {\n finalizeRunEvents,\n AG_UI_CHANNEL_EVENT,\n phoenixExponentialBackoff,\n} from \"@copilotkit/shared\";\nimport type { Channel } from \"phoenix\";\nimport { Socket } from \"phoenix\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface IntelligenceAgentRunnerOptions {\n /** Phoenix runner websocket URL, e.g. \"ws://localhost:4000/runner\" */\n url: string;\n /** Optional Phoenix socket auth token used during websocket connect. */\n authToken?: string;\n /** Max delay (ms) for WebSocket reconnect backoff. @default 10_000 */\n maxReconnectMs?: number;\n /** Max delay (ms) for channel rejoin backoff. @default 30_000 */\n maxRejoinMs?: number;\n}\n\nexport interface RunnerStartupBoundary {\n events: Observable<BaseEvent>;\n startup: Promise<void>;\n}\n\ninterface ThreadState {\n socket: Socket;\n channel: Channel;\n isRunning: boolean;\n stopRequested: boolean;\n agent: AbstractAgent | null;\n currentEvents: BaseEvent[];\n nextEventSeq: number;\n hasRunStarted: boolean;\n}\n\nexport class IntelligenceAgentRunner extends AgentRunner {\n private options: IntelligenceAgentRunnerOptions;\n private threads = new Map<string, ThreadState>();\n\n constructor(options: IntelligenceAgentRunnerOptions) {\n super();\n // Store config — sockets are created per-run, not eagerly.\n this.options = options;\n }\n\n /**\n * Create a new Phoenix socket with explicit exponential backoff.\n *\n * Each run/connect gets its own socket so that:\n * - A socket failure only affects a single thread, not all threads.\n * - Cleanup is simple: channel.leave() + socket.disconnect() tears\n * down everything for that run with no shared-state concerns.\n * - Each run gets its own independent retry budget.\n *\n * reconnectAfterMs — delay before Phoenix reconnects the WebSocket\n * after an unclean close. 100ms base, doubling up to maxReconnectMs (default 10s).\n *\n * rejoinAfterMs — delay before Phoenix re-joins a channel that\n * entered the \"errored\" state. 1s base, doubling up to maxRejoinMs (default 30s).\n *\n * These are set explicitly because Phoenix's default schedule is a\n * fixed stepped array (not exponential), and any code that calls\n * socket.disconnect() in an onError handler will set\n * closeWasClean = true and reset the reconnect timer — permanently\n * killing retries.\n */\n private createSocket(): Socket {\n const socket = new Socket(this.options.url, {\n ...(this.options.authToken ? { authToken: this.options.authToken } : {}),\n reconnectAfterMs: phoenixExponentialBackoff(\n 100,\n this.options.maxReconnectMs ?? 10_000,\n ),\n rejoinAfterMs: phoenixExponentialBackoff(\n 1_000,\n this.options.maxRejoinMs ?? 30_000,\n ),\n });\n socket.connect();\n return socket;\n }\n\n private createRunnerEventPayload(\n event: BaseEvent,\n request: AgentRunnerRunRequest,\n state: ThreadState,\n ): Record<string, unknown> {\n const canonicalEvent = this.stampRunnerMetadata(\n this.stampCanonicalRunOwnership(event, request),\n state,\n );\n const payload = {\n ...(canonicalEvent as Record<string, unknown>),\n };\n\n payload.threadId = request.threadId;\n payload.runId = request.input.runId;\n payload.thread_id = request.threadId;\n payload.run_id = request.input.runId;\n\n return payload;\n }\n\n private stampCanonicalRunOwnership(\n event: BaseEvent,\n request: AgentRunnerRunRequest,\n ): BaseEvent {\n return {\n ...(event as BaseEvent & Record<string, unknown>),\n threadId: request.threadId,\n runId: request.input.runId,\n } as BaseEvent;\n }\n\n private stampRunnerMetadata(event: BaseEvent, state: ThreadState): BaseEvent {\n const eventRecord = event as BaseEvent & {\n metadata?: Record<string, unknown>;\n };\n\n const existingMetadata = eventRecord.metadata ?? {};\n const hasEventId = typeof existingMetadata.cpki_event_id === \"string\";\n const hasEventSeq = typeof existingMetadata.cpki_event_seq === \"number\";\n\n if (hasEventId && hasEventSeq) {\n const eventSeq = existingMetadata.cpki_event_seq as number;\n state.nextEventSeq = Math.max(state.nextEventSeq, eventSeq + 1);\n return eventRecord;\n }\n\n const eventSeq = state.nextEventSeq++;\n\n return {\n ...eventRecord,\n metadata: {\n ...existingMetadata,\n cpki_event_id:\n typeof existingMetadata.cpki_event_id === \"string\"\n ? existingMetadata.cpki_event_id\n : randomUUID(),\n cpki_event_seq: eventSeq,\n },\n };\n }\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n return this.createRunObservable(request);\n }\n\n runWithStartupBoundary(\n request: AgentRunnerRunRequest,\n ): RunnerStartupBoundary {\n let resolveStartup: (() => void) | undefined;\n let rejectStartup: ((reason: Error) => void) | undefined;\n const startup = new Promise<void>((resolve, reject) => {\n resolveStartup = resolve;\n rejectStartup = reject;\n });\n\n return {\n events: this.createRunObservable(request, {\n resolveStartup: () => resolveStartup?.(),\n rejectStartup: (error) => rejectStartup?.(error),\n }),\n startup,\n };\n }\n\n private createRunObservable(\n request: AgentRunnerRunRequest,\n startupBoundary?: {\n resolveStartup: () => void;\n rejectStartup: (error: Error) => void;\n },\n ): Observable<BaseEvent> {\n const { threadId, agent, input } = request;\n\n const existing = this.threads.get(threadId);\n if (existing?.isRunning) {\n throw new Error(\"Thread already running\");\n }\n\n return new Observable((observer) => {\n const socket = this.createSocket();\n\n const channel = socket.channel(`ingestion:${input.runId}`, {\n thread_id: threadId,\n run_id: input.runId,\n });\n\n const state: ThreadState = {\n socket,\n channel,\n isRunning: true,\n stopRequested: false,\n agent,\n currentEvents: [],\n nextEventSeq: 1,\n hasRunStarted: false,\n };\n this.threads.set(threadId, state);\n\n // Track consecutive socket errors for this run. Phoenix retries\n // automatically via reconnectAfterMs, but if the connection fails\n // repeatedly we abort the agent — otherwise runAgent() completes\n // normally, finalization events buffer silently on the dead\n // channel, and the client never receives them.\n //\n // Aborting the agent is the single trigger that cascades through\n // the existing error pipeline: runAgent() rejects → catchError\n // pushes RUN_ERROR → finalize calls finalizeRunEvents +\n // removeThread → channel.leave() + socket.disconnect().\n const MAX_CONSECUTIVE_ERRORS = 5;\n let consecutiveErrors = 0;\n\n socket.onError(() => {\n consecutiveErrors++;\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS && state.agent) {\n try {\n state.agent.abortRun();\n } catch {\n // Ignore abort errors.\n }\n }\n // Otherwise: Phoenix retries automatically using the exponential\n // backoff schedule configured in createSocket().\n });\n\n socket.onOpen(() => {\n // A successful (re)connection resets the counter so transient\n // network blips don't accumulate across recoveries.\n consecutiveErrors = 0;\n });\n\n // Listen for custom \"stop\" events pushed by the client over the\n // channel. This must be registered before channel.join() so the\n // handler is in place by the time the server starts relaying messages.\n // The client sends the stop event before leaving the channel, so the\n // runner is guaranteed to receive it while still joined.\n channel.on(AG_UI_CHANNEL_EVENT, (payload: BaseEvent) => {\n if (\n payload.type === EventType.CUSTOM &&\n (payload as BaseEvent & { name?: string }).name === \"stop\"\n ) {\n this.stop({ threadId });\n }\n });\n\n channel\n .join()\n .receive(\"ok\", () => {\n startupBoundary?.resolveStartup();\n this.executeAgentRun(request, state, threadId).subscribe({\n complete: () => observer.complete(),\n });\n })\n .receive(\"error\", (resp) => {\n const error = new Error(\n `Failed to join channel: ${JSON.stringify(resp)}`,\n );\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error.message,\n code: \"CHANNEL_JOIN_ERROR\",\n } as BaseEvent;\n observer.next(errorEvent);\n state.currentEvents.push(errorEvent);\n this.removeThread(threadId);\n startupBoundary?.rejectStartup(error);\n observer.complete();\n })\n .receive(\"timeout\", () => {\n const error = new Error(\"Timed out joining channel\");\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error.message,\n code: \"CHANNEL_JOIN_TIMEOUT\",\n } as BaseEvent;\n observer.next(errorEvent);\n state.currentEvents.push(errorEvent);\n this.removeThread(threadId);\n startupBoundary?.rejectStartup(error);\n observer.complete();\n });\n\n return () => {\n this.removeThread(threadId);\n };\n });\n }\n\n connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {\n const { threadId } = request;\n\n return new Observable((observer) => {\n const socket = this.createSocket();\n\n const channel = socket.channel(`thread:${threadId}`);\n\n channel.on(\"ag_ui_event\", (payload: BaseEvent) => {\n observer.next(payload);\n\n if (\n payload.type === EventType.RUN_FINISHED ||\n payload.type === EventType.RUN_ERROR\n ) {\n observer.complete();\n }\n });\n\n const cleanup = () => {\n channel.leave();\n socket.disconnect();\n };\n\n channel\n .join()\n .receive(\"ok\", () => undefined)\n .receive(\"error\", (resp) => {\n observer.error(\n new Error(`Failed to join channel: ${JSON.stringify(resp)}`),\n );\n cleanup();\n })\n .receive(\"timeout\", () => {\n observer.error(new Error(\"Timed out joining channel\"));\n cleanup();\n });\n\n return () => {\n cleanup();\n };\n });\n }\n\n isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {\n const state = this.threads.get(request.threadId);\n return Promise.resolve(state?.isRunning ?? false);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const state = this.threads.get(request.threadId);\n if (!state || !state.isRunning || state.stopRequested) {\n return Promise.resolve(false);\n }\n\n state.stopRequested = true;\n\n // Direct local abort — the runtime is the authority.\n if (state.agent) {\n try {\n state.agent.abortRun();\n } catch {\n // Ignore abort errors.\n }\n }\n\n return Promise.resolve(true);\n }\n\n // The emitted values are ignored by the caller (only `complete` matters);\n // `runAgent()` resolves with a RunAgentResult that flows through `from()`.\n private executeAgentRun(\n request: AgentRunnerRunRequest,\n state: ThreadState,\n threadId: string,\n ): Observable<unknown> {\n const { currentEvents, channel } = state;\n const pushCanonicalEvent = (event: BaseEvent): void => {\n const canonicalEvent = this.stampRunnerMetadata(\n this.stampCanonicalRunOwnership(event, request),\n state,\n );\n currentEvents.push(canonicalEvent);\n\n if (canonicalEvent.type === EventType.RUN_STARTED) {\n state.hasRunStarted = true;\n }\n\n channel.push(\n \"event\",\n this.createRunnerEventPayload(canonicalEvent, request, state),\n );\n };\n\n const getPersistedInputMessages = () =>\n request.persistedInputMessages ?? request.input.messages;\n\n const buildRunStartedEvent = (\n source?: RunStartedEvent,\n ): RunStartedEvent => {\n const baseInput = source?.input ?? request.input;\n const persistedInputMessages = getPersistedInputMessages();\n\n return {\n ...(source ?? {\n type: EventType.RUN_STARTED,\n threadId: request.threadId,\n runId: request.input.runId,\n }),\n threadId: request.threadId,\n runId: request.input.runId,\n input: {\n ...baseInput,\n threadId: request.threadId,\n runId: request.input.runId,\n ...(persistedInputMessages !== undefined\n ? { messages: persistedInputMessages }\n : {}),\n },\n } as RunStartedEvent;\n };\n\n const ensureRunStarted = (): void => {\n if (!state.hasRunStarted) {\n state.hasRunStarted = true;\n pushCanonicalEvent(buildRunStartedEvent());\n }\n };\n\n return from(\n request.agent.runAgent(request.input, {\n onEvent: ({ event }: { event: BaseEvent }) => {\n if (event.type === EventType.RUN_STARTED) {\n pushCanonicalEvent(buildRunStartedEvent(event as RunStartedEvent));\n return;\n }\n\n ensureRunStarted();\n pushCanonicalEvent(event);\n },\n }),\n ).pipe(\n catchError((error) => {\n ensureRunStarted();\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error instanceof Error ? error.message : String(error),\n } as BaseEvent;\n pushCanonicalEvent(errorEvent);\n return EMPTY;\n }),\n finalize(() => {\n ensureRunStarted();\n const appended = finalizeRunEvents(currentEvents, {\n stopRequested: state.stopRequested,\n });\n for (const event of appended) {\n channel.push(\n \"event\",\n this.createRunnerEventPayload(event, request, state),\n );\n }\n this.removeThread(threadId);\n }),\n );\n }\n\n /**\n * Tear down all resources for a thread: leave the channel,\n * disconnect the per-run socket, and remove the thread state.\n *\n * Idempotent — safe to call multiple times for the same threadId\n * (e.g. from join error handlers, finalize, and Observable teardown).\n */\n private removeThread(threadId: string): void {\n const state = this.threads.get(threadId);\n if (!state) {\n return;\n }\n\n // Delete first so concurrent calls see the entry as already removed.\n this.threads.delete(threadId);\n\n try {\n state.channel.leave();\n } catch {\n // Channel may already be closed/left.\n }\n try {\n state.socket.disconnect();\n } catch {\n // Socket may already be disconnected.\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"intelligence.mjs","names":["randomUUID","catchError","finalize"],"sources":["../../../../src/v2/runtime/runner/intelligence.ts"],"sourcesContent":["import type {\n AgentRunnerConnectRequest,\n AgentRunnerIsRunningRequest,\n AgentRunnerRunRequest,\n} from \"./agent-runner\";\nimport { AgentRunner } from \"./agent-runner\";\nimport type { AgentRunnerStopRequest } from \"./agent-runner\";\nimport { EMPTY, Observable, from } from \"rxjs\";\nimport { catchError, finalize } from \"rxjs/operators\";\nimport type { AbstractAgent, BaseEvent, RunStartedEvent } from \"@ag-ui/client\";\nimport { EventType } from \"@ag-ui/client\";\nimport {\n finalizeRunEvents,\n AG_UI_CHANNEL_EVENT,\n phoenixExponentialBackoff,\n} from \"@copilotkit/shared\";\nimport type { Channel } from \"phoenix\";\nimport { Socket } from \"phoenix\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface IntelligenceAgentRunnerOptions {\n /** Phoenix runner websocket URL, e.g. \"ws://localhost:4000/runner\" */\n url: string;\n /** Optional Phoenix socket auth token used during websocket connect. */\n authToken?: string;\n /** Max delay (ms) for WebSocket reconnect backoff. @default 10_000 */\n maxReconnectMs?: number;\n /** Max delay (ms) for channel rejoin backoff. @default 30_000 */\n maxRejoinMs?: number;\n}\n\nexport interface RunnerStartupBoundary {\n events: Observable<BaseEvent>;\n startup: Promise<void>;\n}\n\ninterface ThreadState {\n runId: string;\n socket: Socket;\n channel: Channel;\n isRunning: boolean;\n stopRequested: boolean;\n agent: AbstractAgent | null;\n currentEvents: BaseEvent[];\n nextEventSeq: number;\n hasRunStarted: boolean;\n}\n\nexport class IntelligenceAgentRunner extends AgentRunner {\n private options: IntelligenceAgentRunnerOptions;\n private threads = new Map<string, ThreadState>();\n\n constructor(options: IntelligenceAgentRunnerOptions) {\n super();\n // Store config — sockets are created per-run, not eagerly.\n this.options = options;\n }\n\n /**\n * Create a new Phoenix socket with explicit exponential backoff.\n *\n * Each run/connect gets its own socket so that:\n * - A socket failure only affects a single thread, not all threads.\n * - Cleanup is simple: channel.leave() + socket.disconnect() tears\n * down everything for that run with no shared-state concerns.\n * - Each run gets its own independent retry budget.\n *\n * reconnectAfterMs — delay before Phoenix reconnects the WebSocket\n * after an unclean close. 100ms base, doubling up to maxReconnectMs (default 10s).\n *\n * rejoinAfterMs — delay before Phoenix re-joins a channel that\n * entered the \"errored\" state. 1s base, doubling up to maxRejoinMs (default 30s).\n *\n * These are set explicitly because Phoenix's default schedule is a\n * fixed stepped array (not exponential), and any code that calls\n * socket.disconnect() in an onError handler will set\n * closeWasClean = true and reset the reconnect timer — permanently\n * killing retries.\n */\n private createSocket(authToken = this.options.authToken): Socket {\n const socket = new Socket(this.options.url, {\n ...(authToken ? { authToken } : {}),\n reconnectAfterMs: phoenixExponentialBackoff(\n 100,\n this.options.maxReconnectMs ?? 10_000,\n ),\n rejoinAfterMs: phoenixExponentialBackoff(\n 1_000,\n this.options.maxRejoinMs ?? 30_000,\n ),\n });\n socket.connect();\n return socket;\n }\n\n private createRunnerEventPayload(\n event: BaseEvent,\n request: AgentRunnerRunRequest,\n state: ThreadState,\n ): Record<string, unknown> {\n const canonicalEvent = this.stampRunnerMetadata(\n this.stampCanonicalRunOwnership(event, request),\n state,\n );\n const payload = {\n ...(canonicalEvent as Record<string, unknown>),\n };\n\n payload.threadId = request.threadId;\n payload.runId = request.input.runId;\n payload.thread_id = request.threadId;\n payload.run_id = request.input.runId;\n\n return payload;\n }\n\n private stampCanonicalRunOwnership(\n event: BaseEvent,\n request: AgentRunnerRunRequest,\n ): BaseEvent {\n const eventRecord = event as BaseEvent & Record<string, unknown>;\n eventRecord.threadId = request.threadId;\n eventRecord.runId = request.input.runId;\n return eventRecord;\n }\n\n private stampRunnerMetadata(event: BaseEvent, state: ThreadState): BaseEvent {\n const eventRecord = event as BaseEvent & {\n metadata?: Record<string, unknown>;\n };\n\n const existingMetadata = eventRecord.metadata ?? {};\n const hasEventId = typeof existingMetadata.cpki_event_id === \"string\";\n const hasEventSeq = typeof existingMetadata.cpki_event_seq === \"number\";\n\n if (hasEventId && hasEventSeq) {\n const eventSeq = existingMetadata.cpki_event_seq as number;\n state.nextEventSeq = Math.max(state.nextEventSeq, eventSeq + 1);\n return eventRecord;\n }\n\n const eventSeq = state.nextEventSeq++;\n\n eventRecord.metadata = {\n ...existingMetadata,\n cpki_event_id:\n typeof existingMetadata.cpki_event_id === \"string\"\n ? existingMetadata.cpki_event_id\n : randomUUID(),\n cpki_event_seq: eventSeq,\n };\n return eventRecord;\n }\n\n run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n return this.createRunObservable(request);\n }\n\n runWithStartupBoundary(\n request: AgentRunnerRunRequest,\n ): RunnerStartupBoundary {\n let resolveStartup: (() => void) | undefined;\n let rejectStartup: ((reason: Error) => void) | undefined;\n const startup = new Promise<void>((resolve, reject) => {\n resolveStartup = resolve;\n rejectStartup = reject;\n });\n\n return {\n events: this.createRunObservable(request, {\n resolveStartup: () => resolveStartup?.(),\n rejectStartup: (error) => rejectStartup?.(error),\n }),\n startup,\n };\n }\n\n private createRunObservable(\n request: AgentRunnerRunRequest,\n startupBoundary?: {\n resolveStartup: () => void;\n rejectStartup: (error: Error) => void;\n },\n ): Observable<BaseEvent> {\n const { threadId, agent, input } = request;\n\n const existing = this.threads.get(threadId);\n if (existing?.isRunning) {\n throw new Error(\"Thread already running\");\n }\n\n return new Observable((observer) => {\n const socket = this.createSocket(request.authToken);\n\n const channel = socket.channel(`ingestion:${input.runId}`, {\n thread_id: threadId,\n run_id: input.runId,\n });\n\n const state: ThreadState = {\n runId: input.runId,\n socket,\n channel,\n isRunning: true,\n stopRequested: false,\n agent,\n currentEvents: [],\n nextEventSeq: 1,\n hasRunStarted: false,\n };\n this.threads.set(threadId, state);\n\n // Track consecutive socket errors for this run. Phoenix retries\n // automatically via reconnectAfterMs, but if the connection fails\n // repeatedly we abort the agent — otherwise runAgent() completes\n // normally, finalization events buffer silently on the dead\n // channel, and the client never receives them.\n //\n // Aborting the agent is the single trigger that cascades through\n // the existing error pipeline: runAgent() rejects → catchError\n // pushes RUN_ERROR → finalize calls finalizeRunEvents +\n // removeThread → channel.leave() + socket.disconnect().\n const MAX_CONSECUTIVE_ERRORS = 5;\n let consecutiveErrors = 0;\n\n socket.onError(() => {\n consecutiveErrors++;\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS && state.agent) {\n try {\n state.agent.abortRun();\n } catch {\n // Ignore abort errors.\n }\n }\n // Otherwise: Phoenix retries automatically using the exponential\n // backoff schedule configured in createSocket().\n });\n\n socket.onOpen(() => {\n // A successful (re)connection resets the counter so transient\n // network blips don't accumulate across recoveries.\n consecutiveErrors = 0;\n });\n\n // Listen for custom \"stop\" events pushed by the client over the\n // channel. This must be registered before channel.join() so the\n // handler is in place by the time the server starts relaying messages.\n // The client sends the stop event before leaving the channel, so the\n // runner is guaranteed to receive it while still joined.\n channel.on(AG_UI_CHANNEL_EVENT, (payload: BaseEvent) => {\n if (\n payload.type === EventType.CUSTOM &&\n (payload as BaseEvent & { name?: string }).name === \"stop\"\n ) {\n this.stop({ threadId });\n }\n });\n\n channel\n .join()\n .receive(\"ok\", () => {\n startupBoundary?.resolveStartup();\n this.executeAgentRun(request, state, threadId, (event) => {\n observer.next(event);\n }).subscribe({\n complete: () => observer.complete(),\n });\n })\n .receive(\"error\", (resp) => {\n const error = new Error(\n `Failed to join channel: ${JSON.stringify(resp)}`,\n );\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error.message,\n code: \"CHANNEL_JOIN_ERROR\",\n } as BaseEvent;\n observer.next(errorEvent);\n state.currentEvents.push(errorEvent);\n this.removeThread(threadId);\n startupBoundary?.rejectStartup(error);\n observer.complete();\n })\n .receive(\"timeout\", () => {\n const error = new Error(\"Timed out joining channel\");\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error.message,\n code: \"CHANNEL_JOIN_TIMEOUT\",\n } as BaseEvent;\n observer.next(errorEvent);\n state.currentEvents.push(errorEvent);\n this.removeThread(threadId);\n startupBoundary?.rejectStartup(error);\n observer.complete();\n });\n\n return () => {\n this.removeThread(threadId);\n };\n });\n }\n\n connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {\n const { threadId } = request;\n\n return new Observable((observer) => {\n const socket = this.createSocket();\n\n const channel = socket.channel(`thread:${threadId}`);\n\n channel.on(\"ag_ui_event\", (payload: BaseEvent) => {\n observer.next(payload);\n\n if (\n payload.type === EventType.RUN_FINISHED ||\n payload.type === EventType.RUN_ERROR\n ) {\n observer.complete();\n }\n });\n\n const cleanup = () => {\n channel.leave();\n socket.disconnect();\n };\n\n channel\n .join()\n .receive(\"ok\", () => undefined)\n .receive(\"error\", (resp) => {\n observer.error(\n new Error(`Failed to join channel: ${JSON.stringify(resp)}`),\n );\n cleanup();\n })\n .receive(\"timeout\", () => {\n observer.error(new Error(\"Timed out joining channel\"));\n cleanup();\n });\n\n return () => {\n cleanup();\n };\n });\n }\n\n isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {\n const state = this.threads.get(request.threadId);\n return Promise.resolve(state?.isRunning ?? false);\n }\n\n stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n const state = this.threads.get(request.threadId);\n if (!state || !state.isRunning || state.stopRequested) {\n return Promise.resolve(false);\n }\n if (request.runId !== undefined && state.runId !== request.runId) {\n return Promise.resolve(false);\n }\n\n state.stopRequested = true;\n\n // Direct local abort — the runtime is the authority.\n if (state.agent) {\n try {\n state.agent.abortRun();\n } catch {\n // Ignore abort errors.\n }\n }\n\n return Promise.resolve(true);\n }\n\n // The emitted values are ignored by the caller (only `complete` matters);\n // `runAgent()` resolves with a RunAgentResult that flows through `from()`.\n private executeAgentRun(\n request: AgentRunnerRunRequest,\n state: ThreadState,\n threadId: string,\n onRunError: (event: BaseEvent) => void,\n ): Observable<unknown> {\n const { currentEvents, channel } = state;\n const pushCanonicalEvent = (event: BaseEvent): void => {\n const canonicalEvent = this.stampRunnerMetadata(\n this.stampCanonicalRunOwnership(event, request),\n state,\n );\n currentEvents.push(canonicalEvent);\n\n if (canonicalEvent.type === EventType.RUN_STARTED) {\n state.hasRunStarted = true;\n }\n\n channel.push(\n \"event\",\n this.createRunnerEventPayload(canonicalEvent, request, state),\n );\n };\n\n const getPersistedInputMessages = () =>\n request.persistedInputMessages ?? request.input.messages;\n\n const buildRunStartedEvent = (\n source?: RunStartedEvent,\n ): RunStartedEvent => {\n const baseInput = source?.input ?? request.input;\n const persistedInputMessages = getPersistedInputMessages();\n const event =\n source ??\n ({\n type: EventType.RUN_STARTED,\n threadId: request.threadId,\n runId: request.input.runId,\n } as RunStartedEvent);\n event.threadId = request.threadId;\n event.runId = request.input.runId;\n event.input = {\n ...baseInput,\n threadId: request.threadId,\n runId: request.input.runId,\n ...(persistedInputMessages !== undefined\n ? { messages: persistedInputMessages }\n : {}),\n };\n return event;\n };\n\n const ensureRunStarted = (): void => {\n if (!state.hasRunStarted) {\n state.hasRunStarted = true;\n pushCanonicalEvent(buildRunStartedEvent());\n }\n };\n\n return from(\n request.agent.runAgent(request.input, {\n onEvent: ({ event }: { event: BaseEvent }) => {\n if (event.type === EventType.RUN_STARTED) {\n pushCanonicalEvent(buildRunStartedEvent(event as RunStartedEvent));\n return;\n }\n\n ensureRunStarted();\n pushCanonicalEvent(event);\n },\n }),\n ).pipe(\n catchError((error) => {\n ensureRunStarted();\n const existingError = currentEvents.find(\n (event) => event.type === EventType.RUN_ERROR,\n );\n if (existingError) {\n onRunError(existingError);\n return EMPTY;\n }\n const errorEvent = {\n type: EventType.RUN_ERROR,\n message: error instanceof Error ? error.message : String(error),\n } as BaseEvent;\n pushCanonicalEvent(errorEvent);\n onRunError(errorEvent);\n return EMPTY;\n }),\n finalize(() => {\n ensureRunStarted();\n const appended = finalizeRunEvents(currentEvents, {\n stopRequested: state.stopRequested,\n });\n for (const event of appended) {\n channel.push(\n \"event\",\n this.createRunnerEventPayload(event, request, state),\n );\n }\n this.removeThread(threadId);\n }),\n );\n }\n\n /**\n * Tear down all resources for a thread: leave the channel,\n * disconnect the per-run socket, and remove the thread state.\n *\n * Idempotent — safe to call multiple times for the same threadId\n * (e.g. from join error handlers, finalize, and Observable teardown).\n */\n private removeThread(threadId: string): void {\n const state = this.threads.get(threadId);\n if (!state) {\n return;\n }\n\n // Delete first so concurrent calls see the entry as already removed.\n this.threads.delete(threadId);\n\n try {\n state.channel.leave();\n } catch {\n // Channel may already be closed/left.\n }\n try {\n state.socket.disconnect();\n } catch {\n // Socket may already be disconnected.\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/runtime",
3
- "version": "1.64.2-canary.rc-1",
3
+ "version": "1.64.2",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "ai",
@@ -105,9 +105,9 @@
105
105
  "uuid": "^10.0.0",
106
106
  "ws": "^8.18.0",
107
107
  "zod": "^3.23.3",
108
- "@copilotkit/channels-core": "^0.4.1-canary.rc-1",
109
- "@copilotkit/channels-intelligence": "0.4.1-canary.rc-1",
110
- "@copilotkit/shared": "1.64.2-canary.rc-1"
108
+ "@copilotkit/channels-core": "^0.5.0",
109
+ "@copilotkit/channels-intelligence": "0.5.0",
110
+ "@copilotkit/shared": "1.64.2"
111
111
  },
112
112
  "devDependencies": {
113
113
  "@copilotkit/aimock": "latest",
@@ -130,7 +130,7 @@
130
130
  "typescript": "^5.2.3",
131
131
  "valibot": "^1.2.0",
132
132
  "vitest": "^3.2.4",
133
- "@copilotkit/channels": "0.4.1-canary.rc-1",
133
+ "@copilotkit/channels": "0.5.0",
134
134
  "tsconfig": "1.4.12"
135
135
  },
136
136
  "peerDependencies": {