@copilotkit/core 1.68.3-canary.1787220377 → 1.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -572,6 +572,31 @@ var IntelligenceAgent = class IntelligenceAgent extends _ag_ui_client.AbstractAg
572
572
  }
573
573
  };
574
574
 
575
+ //#endregion
576
+ //#region src/utils/runtime-info-error.ts
577
+ /**
578
+ * Build the error for a failed runtime `/info` response, folding in whatever
579
+ * explanation the runtime sent back.
580
+ *
581
+ * The status alone cannot distinguish a wrong `runtimeUrl` from a transport
582
+ * mismatch, so the runtime answers the latter with a body naming the cause
583
+ * (see the handler's `single_route_envelope_against_multi_route_runtime`
584
+ * diagnostic). Discarding that body — as every `/info` caller used to — left
585
+ * the developer with a bare 404 and nothing to act on (OSS-882).
586
+ *
587
+ * A body that isn't JSON, or carries no string `message`, contributes nothing:
588
+ * the status is then the whole error.
589
+ */
590
+ async function runtimeInfoError(response) {
591
+ const base = `Runtime info request failed with status ${response.status}`;
592
+ let detail;
593
+ try {
594
+ const message = (await response.clone().json())?.message;
595
+ if (typeof message === "string" && message.trim().length > 0) detail = message.trim();
596
+ } catch {}
597
+ return new Error(detail ? `${base}: ${detail}` : base);
598
+ }
599
+
575
600
  //#endregion
576
601
  //#region src/agent.ts
577
602
  function hasHeaders(agent) {
@@ -861,7 +886,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
861
886
  headers,
862
887
  ...this.credentials ? { credentials: this.credentials } : {}
863
888
  });
864
- if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
889
+ if (!response.ok) throw await runtimeInfoError(response);
865
890
  return await response.json();
866
891
  }
867
892
  async fetchRuntimeInfoAutoDetect(headers) {
@@ -883,7 +908,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
883
908
  body: JSON.stringify({ method: "info" }),
884
909
  ...this.credentials ? { credentials: this.credentials } : {}
885
910
  });
886
- if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
911
+ if (!response.ok) throw await runtimeInfoError(response);
887
912
  this.transport = "single";
888
913
  this.singleEndpointUrl = this.runtimeUrl;
889
914
  return await response.json();
@@ -1492,7 +1517,7 @@ var AgentRegistry = class {
1492
1517
  headers,
1493
1518
  ...credentials ? { credentials } : {}
1494
1519
  });
1495
- if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
1520
+ if (!response.ok) throw await runtimeInfoError(response);
1496
1521
  return {
1497
1522
  runtimeInfo: await response.json(),
1498
1523
  resolvedTransport: "rest"
@@ -1505,7 +1530,7 @@ var AgentRegistry = class {
1505
1530
  body: JSON.stringify({ method: "info" }),
1506
1531
  ...credentials ? { credentials } : {}
1507
1532
  });
1508
- if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
1533
+ if (!response.ok) throw await runtimeInfoError(response);
1509
1534
  return await response.json();
1510
1535
  }
1511
1536
  /**
@@ -2778,6 +2803,7 @@ const isContinuation = (input) => input.resume !== void 0 || Object.prototype.ha
2778
2803
  var StateManager = class {
2779
2804
  stateByRun = /* @__PURE__ */ new Map();
2780
2805
  messageToRun = /* @__PURE__ */ new Map();
2806
+ rawEventByMessage = /* @__PURE__ */ new Map();
2781
2807
  activeRun = /* @__PURE__ */ new Map();
2782
2808
  agentSubscriptions = /* @__PURE__ */ new Map();
2783
2809
  pendingContinuations = /* @__PURE__ */ new WeakMap();
@@ -2829,12 +2855,13 @@ var StateManager = class {
2829
2855
  runId: subRunId ?? input.runId
2830
2856
  });
2831
2857
  const { unsubscribe } = agent.subscribe({
2832
- onRunStartedEvent: ({ input, state }) => {
2858
+ onRunStartedEvent: ({ event, input, state }) => {
2833
2859
  if (revoked) return;
2834
2860
  const internalContinuation = [...this.pendingContinuations.get(agent) ?? []].find((pending) => pending.expectedInput === input);
2835
2861
  internalContinuation?.cancel();
2836
- if (runFinished && input.runId === subRunId && !internalContinuation && !isContinuation(input)) subRunId = (0, _ag_ui_client.randomUUID)();
2837
- else subRunId = internalContinuation?.expectedRunId ?? input.runId;
2862
+ if (internalContinuation) subRunId = internalContinuation.expectedRunId ?? event.runId ?? input.runId;
2863
+ else if (runFinished && input.runId === subRunId && !isContinuation(input) && (event.runId == null || event.runId === subRunId)) subRunId = (0, _ag_ui_client.randomUUID)();
2864
+ else subRunId = event.runId || input.runId;
2838
2865
  runFinished = false;
2839
2866
  this.handleRunStarted(agent, effectiveInput(input), state);
2840
2867
  },
@@ -2856,13 +2883,22 @@ var StateManager = class {
2856
2883
  if (revoked) return;
2857
2884
  this.handleStateDelta(agent, event, effectiveInput(input), state);
2858
2885
  },
2886
+ onTextMessageStartEvent: ({ event, input }) => {
2887
+ if (revoked) return;
2888
+ this.handleTextMessageStart(agent, event, effectiveInput(input));
2889
+ },
2859
2890
  onMessagesSnapshotEvent: ({ event, input, messages }) => {
2860
2891
  if (revoked) return;
2861
2892
  this.handleMessagesSnapshot(agent, event, effectiveInput(input), messages);
2893
+ this.pruneRawEvents(agent.agentId, input.threadId, event.messages, effectiveInput(input));
2862
2894
  },
2863
2895
  onNewMessage: ({ message, input }) => {
2864
2896
  if (revoked) return;
2865
2897
  this.handleNewMessage(agent, message, input ? effectiveInput(input) : void 0);
2898
+ },
2899
+ onMessagesChanged: ({ messages, input }) => {
2900
+ if (revoked) return;
2901
+ if (!input) this.pruneRawEvents(agent.agentId, agent.threadId, messages);
2866
2902
  }
2867
2903
  });
2868
2904
  this.agentSubscriptions.set(agentId, () => {
@@ -2880,6 +2916,7 @@ var StateManager = class {
2880
2916
  unsubscribe();
2881
2917
  this.agentSubscriptions.delete(agentId);
2882
2918
  }
2919
+ this.rawEventByMessage.delete(agentId);
2883
2920
  }
2884
2921
  /**
2885
2922
  * Get state for a specific run
@@ -2897,6 +2934,13 @@ var StateManager = class {
2897
2934
  return this.messageToRun.get(agentId)?.get(threadId)?.get(messageId);
2898
2935
  }
2899
2936
  /**
2937
+ * Get direct text-start metadata associated with a message.
2938
+ */
2939
+ getRawEventForMessage(agentId, threadId, messageId) {
2940
+ const rawEvent = this.rawEventByMessage.get(agentId)?.get(threadId)?.get(messageId);
2941
+ return rawEvent === void 0 ? void 0 : (0, _ag_ui_client.structuredClone_)(rawEvent);
2942
+ }
2943
+ /**
2900
2944
  * Get all states for an agent's thread
2901
2945
  */
2902
2946
  getStatesForThread(agentId, threadId) {
@@ -2948,12 +2992,30 @@ var StateManager = class {
2948
2992
  this.saveState(agent.agentId, threadId, runId, state);
2949
2993
  }
2950
2994
  /**
2995
+ * Capture only defined metadata from a normalized direct text-start event.
2996
+ */
2997
+ handleTextMessageStart(agent, event, input) {
2998
+ if (!agent.agentId) return;
2999
+ const { threadId } = input;
3000
+ if (event.rawEvent === void 0) {
3001
+ const threadEvents = this.rawEventByMessage.get(agent.agentId)?.get(threadId);
3002
+ threadEvents?.delete(event.messageId);
3003
+ if (threadEvents?.size === 0) this.rawEventByMessage.get(agent.agentId)?.delete(threadId);
3004
+ if (this.rawEventByMessage.get(agent.agentId)?.size === 0) this.rawEventByMessage.delete(agent.agentId);
3005
+ return;
3006
+ }
3007
+ if (!this.rawEventByMessage.has(agent.agentId)) this.rawEventByMessage.set(agent.agentId, /* @__PURE__ */ new Map());
3008
+ const agentEvents = this.rawEventByMessage.get(agent.agentId);
3009
+ if (!agentEvents.has(threadId)) agentEvents.set(threadId, /* @__PURE__ */ new Map());
3010
+ agentEvents.get(threadId).set(event.messageId, event.rawEvent);
3011
+ }
3012
+ /**
2951
3013
  * Handle messages snapshot event
2952
3014
  */
2953
3015
  handleMessagesSnapshot(agent, event, input, messages) {
2954
3016
  if (!agent.agentId) return;
2955
3017
  const { threadId, runId } = input;
2956
- for (const message of event.messages) this.associateMessageWithRun(agent.agentId, threadId, message.id, runId);
3018
+ for (const message of event.messages) if (this.getRunIdForMessage(agent.agentId, threadId, message.id) === void 0) this.associateMessageWithRun(agent.agentId, threadId, message.id, runId);
2957
3019
  }
2958
3020
  /**
2959
3021
  * Handle new message event
@@ -2987,12 +3049,23 @@ var StateManager = class {
2987
3049
  if (!agentMessages.has(threadId)) agentMessages.set(threadId, /* @__PURE__ */ new Map());
2988
3050
  agentMessages.get(threadId).set(messageId, runId);
2989
3051
  }
3052
+ pruneRawEvents(agentId, fallbackThreadId, messages, input) {
3053
+ const threadId = input?.threadId ?? fallbackThreadId;
3054
+ if (!threadId) return;
3055
+ const threadEvents = this.rawEventByMessage.get(agentId)?.get(threadId);
3056
+ if (!threadEvents) return;
3057
+ const messageIds = new Set(messages.map((message) => message.id));
3058
+ for (const messageId of threadEvents.keys()) if (!messageIds.has(messageId)) threadEvents.delete(messageId);
3059
+ if (threadEvents.size === 0) this.rawEventByMessage.get(agentId)?.delete(threadId);
3060
+ if (this.rawEventByMessage.get(agentId)?.size === 0) this.rawEventByMessage.delete(agentId);
3061
+ }
2990
3062
  /**
2991
3063
  * Clear all state for an agent
2992
3064
  */
2993
3065
  clearAgentState(agentId) {
2994
3066
  this.stateByRun.delete(agentId);
2995
3067
  this.messageToRun.delete(agentId);
3068
+ this.rawEventByMessage.delete(agentId);
2996
3069
  }
2997
3070
  /**
2998
3071
  * Clear all state for a thread
@@ -3000,124 +3073,7 @@ var StateManager = class {
3000
3073
  clearThreadState(agentId, threadId) {
3001
3074
  this.stateByRun.get(agentId)?.delete(threadId);
3002
3075
  this.messageToRun.get(agentId)?.delete(threadId);
3003
- }
3004
- };
3005
-
3006
- //#endregion
3007
- //#region src/core/subagent-registry.ts
3008
- /**
3009
- * Tracks subagent lifecycle events (SUBAGENT_STARTED / FINISHED / ERROR) per
3010
- * owning agent so consumers can resolve a message's `subagentRunId` to a name,
3011
- * description, and running status.
3012
- *
3013
- * Modeled on {@link SuggestionEngine}: an in-memory `Record` keyed by (owning)
3014
- * agentId, mutated from streamed AG-UI events, made reactive by calling
3015
- * `notifySubscribers(onSubagentsChanged)` after every change. It owns its own
3016
- * per-agent `agent.subscribe` (like {@link StateManager}) so it is fully
3017
- * self-contained and adds no coupling to the state/message tracking path.
3018
- *
3019
- * No-op for runs without subagents (a normal LangGraph run never emits
3020
- * SUBAGENT_* events), so the registry simply stays empty.
3021
- */
3022
- var SubagentRegistry = class {
3023
- _subagents = {};
3024
- agentSubscriptions = /* @__PURE__ */ new Map();
3025
- constructor(core) {
3026
- this.core = core;
3027
- }
3028
- /**
3029
- * Subscribe to an agent's subagent lifecycle events. Idempotent per agentId:
3030
- * an existing subscription for the same agent is revoked and replaced.
3031
- */
3032
- subscribeToAgent(agent) {
3033
- if (!agent.agentId) return;
3034
- const agentId = agent.agentId;
3035
- const existingUnsubscribe = this.agentSubscriptions.get(agentId);
3036
- if (existingUnsubscribe) {
3037
- existingUnsubscribe();
3038
- this.agentSubscriptions.delete(agentId);
3039
- }
3040
- let revoked = false;
3041
- const { unsubscribe } = agent.subscribe({
3042
- onSubagentStartedEvent: ({ event }) => {
3043
- if (revoked) return;
3044
- this.handleStarted(agentId, event);
3045
- },
3046
- onSubagentFinishedEvent: ({ event }) => {
3047
- if (revoked) return;
3048
- this.handleFinished(agentId, event);
3049
- },
3050
- onSubagentErrorEvent: ({ event }) => {
3051
- if (revoked) return;
3052
- this.handleError(agentId, event);
3053
- }
3054
- });
3055
- this.agentSubscriptions.set(agentId, () => {
3056
- revoked = true;
3057
- unsubscribe();
3058
- });
3059
- }
3060
- /**
3061
- * Unsubscribe an agent's subscription (symmetric with StateManager cleanup).
3062
- */
3063
- unsubscribeFromAgent(agentId) {
3064
- const unsubscribe = this.agentSubscriptions.get(agentId);
3065
- if (unsubscribe) {
3066
- unsubscribe();
3067
- this.agentSubscriptions.delete(agentId);
3068
- }
3069
- }
3070
- /**
3071
- * Get the subagents observed for an owning agent, keyed by subagentRunId.
3072
- * Returns a fresh shallow copy each call so consumers (and React state) see
3073
- * a new reference when anything changed.
3074
- */
3075
- getSubagents(agentId) {
3076
- return { ...this._subagents[agentId] };
3077
- }
3078
- handleStarted(agentId, event) {
3079
- const bucket = this._subagents[agentId] ??= {};
3080
- bucket[event.subagentRunId] = {
3081
- subagentRunId: event.subagentRunId,
3082
- name: event.name,
3083
- description: event.description,
3084
- parentSubagentRunId: event.parentSubagentRunId,
3085
- status: "running"
3086
- };
3087
- this.notifySubagentsChanged(agentId);
3088
- }
3089
- handleFinished(agentId, event) {
3090
- const bucket = this._subagents[agentId];
3091
- const existing = bucket?.[event.subagentRunId];
3092
- if (!bucket || !existing) return;
3093
- bucket[event.subagentRunId] = {
3094
- ...existing,
3095
- status: "finished"
3096
- };
3097
- this.notifySubagentsChanged(agentId);
3098
- }
3099
- handleError(agentId, event) {
3100
- const bucket = this._subagents[agentId] ??= {};
3101
- const existing = bucket[event.subagentRunId];
3102
- bucket[event.subagentRunId] = existing ? {
3103
- ...existing,
3104
- status: "error",
3105
- error: event.message
3106
- } : {
3107
- subagentRunId: event.subagentRunId,
3108
- name: event.subagentRunId,
3109
- status: "error",
3110
- error: event.message
3111
- };
3112
- this.notifySubagentsChanged(agentId);
3113
- }
3114
- async notifySubagentsChanged(agentId) {
3115
- const subagents = this.getSubagents(agentId);
3116
- await this.core.notifySubscribers((subscriber) => subscriber.onSubagentsChanged?.({
3117
- copilotkit: this.core,
3118
- agentId,
3119
- subagents
3120
- }), "Subscriber onSubagentsChanged error:");
3076
+ this.rawEventByMessage.get(agentId)?.delete(threadId);
3121
3077
  }
3122
3078
  };
3123
3079
 
@@ -4309,7 +4265,6 @@ var CopilotKitCore = class {
4309
4265
  suggestionEngine;
4310
4266
  runHandler;
4311
4267
  stateManager;
4312
- subagentRegistry;
4313
4268
  threadStoreRegistry;
4314
4269
  /**
4315
4270
  * The single core-owned memory store, created lazily on first
@@ -4335,7 +4290,6 @@ var CopilotKitCore = class {
4335
4290
  this.suggestionEngine = new SuggestionEngine(this);
4336
4291
  this.runHandler = new RunHandler(this);
4337
4292
  this.stateManager = new StateManager(this);
4338
- this.subagentRegistry = new SubagentRegistry(this);
4339
4293
  this.threadStoreRegistry = new ThreadStoreRegistry(this);
4340
4294
  this.agentRegistry.initialize(agents__unsafe_dev_only);
4341
4295
  this.runHandler.initialize(tools);
@@ -4350,10 +4304,7 @@ var CopilotKitCore = class {
4350
4304
  },
4351
4305
  onAgentsChanged: ({ agents }) => {
4352
4306
  Object.values(agents).forEach((agent) => {
4353
- if (agent.agentId) {
4354
- this.stateManager.subscribeToAgent(agent);
4355
- this.subagentRegistry.subscribeToAgent(agent);
4356
- }
4307
+ if (agent.agentId) this.stateManager.subscribeToAgent(agent);
4357
4308
  });
4358
4309
  const currentAgentIds = new Set(Object.keys(agents));
4359
4310
  for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
@@ -4361,17 +4312,10 @@ var CopilotKitCore = class {
4361
4312
  } catch (err) {
4362
4313
  console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
4363
4314
  }
4364
- for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) {
4365
- try {
4366
- this.stateManager.unsubscribeFromAgent(agentId);
4367
- } catch (err) {
4368
- console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
4369
- }
4370
- try {
4371
- this.subagentRegistry.unsubscribeFromAgent(agentId);
4372
- } catch (err) {
4373
- console.error(`CopilotKitCore.onAgentsChanged: subagentRegistry.unsubscribeFromAgent failed for "${agentId}":`, err);
4374
- }
4315
+ for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
4316
+ this.stateManager.unsubscribeFromAgent(agentId);
4317
+ } catch (err) {
4318
+ console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
4375
4319
  }
4376
4320
  this.previousAgentIds = currentAgentIds;
4377
4321
  }
@@ -4741,16 +4685,6 @@ var CopilotKitCore = class {
4741
4685
  return this.suggestionEngine.getSuggestions(agentId);
4742
4686
  }
4743
4687
  /**
4744
- * Subagent registry (delegated to SubagentRegistry).
4745
- *
4746
- * Returns the subagents observed for an owning agent during its run(s),
4747
- * keyed by `subagentRunId`, each carrying `name` / `description` / `status`.
4748
- * Empty for runs that never emit SUBAGENT_* events.
4749
- */
4750
- getSubagents(agentId) {
4751
- return this.subagentRegistry.getSubagents(agentId);
4752
- }
4753
- /**
4754
4688
  * Tool management (delegated to RunHandler)
4755
4689
  */
4756
4690
  addTool(tool) {
@@ -4969,6 +4903,9 @@ var CopilotKitCore = class {
4969
4903
  getRunIdForMessage(agentId, threadId, messageId) {
4970
4904
  return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
4971
4905
  }
4906
+ getRawEventForMessage(agentId, threadId, messageId) {
4907
+ return this.stateManager.getRawEventForMessage(agentId, threadId, messageId);
4908
+ }
4972
4909
  getRunIdsForThread(agentId, threadId) {
4973
4910
  return this.stateManager.getRunIdsForThread(agentId, threadId);
4974
4911
  }
@@ -5253,6 +5190,16 @@ function optionalString(value) {
5253
5190
  return typeof value === "string" && value.length > 0 ? value : void 0;
5254
5191
  }
5255
5192
  /**
5193
+ * REST `/threads` is agent-scoped. The Phoenix `user_meta` channel is not.
5194
+ * Drop live upserts that belong to a different agent, or one Inspector
5195
+ * `all-agents` flatten shows the same thread twice until a hard refresh.
5196
+ */
5197
+ function threadMetadataTargetsAgent(payload, agentId) {
5198
+ if (!agentId || payload.operation === "deleted") return true;
5199
+ const threadAgentId = optionalString(payload.thread.agentId);
5200
+ return !threadAgentId || threadAgentId === agentId;
5201
+ }
5202
+ /**
5256
5203
  * Converts gateway run-activity payloads into the internal TypeScript shape.
5257
5204
  */
5258
5205
  function normalizeThreadRunActivityNotification(payload) {
@@ -5678,7 +5625,7 @@ function createThreadStore(environment) {
5678
5625
  }))), ɵobservePhoenixJoinOutcome$(channel$).pipe((0, rxjs_operators.filter)((outcome) => outcome.type !== "joined"), (0, rxjs_operators.map)((outcome) => outcome.type === "timeout" ? threadSocketEvents.joinTimedOut({ sessionId: action.sessionId }) : threadSocketEvents.joinFailed({ sessionId: action.sessionId })))).pipe((0, rxjs_operators.takeUntil)((0, rxjs.merge)(shutdown$, fatalSocketShutdown$)));
5679
5626
  });
5680
5627
  }))),
5681
- createEffect((actions$, state$) => actions$.pipe(ofType(threadSocketEvents.metadataReceived), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([action, state]) => action.sessionId === state.sessionId), (0, rxjs_operators.map)(([action, state]) => {
5628
+ createEffect((actions$, state$) => actions$.pipe(ofType(threadSocketEvents.metadataReceived), (0, rxjs_operators.withLatestFrom)(state$), (0, rxjs_operators.filter)(([action, state]) => action.sessionId === state.sessionId && threadMetadataTargetsAgent(action.payload, state.context?.agentId)), (0, rxjs_operators.map)(([action, state]) => {
5682
5629
  if (action.payload.operation === "deleted") return threadDomainEvents.threadDeleted({
5683
5630
  sessionId: action.sessionId,
5684
5631
  threadId: action.payload.deleted.id
@@ -6045,7 +5992,6 @@ exports.MemoryError = MemoryError;
6045
5992
  exports.ProxiedCopilotRuntimeAgent = ProxiedCopilotRuntimeAgent;
6046
5993
  exports.RunHandler = RunHandler;
6047
5994
  exports.StateManager = StateManager;
6048
- exports.SubagentRegistry = SubagentRegistry;
6049
5995
  exports.SuggestionEngine = SuggestionEngine;
6050
5996
  exports.ToolCallStatus = ToolCallStatus;
6051
5997
  exports.completePartialMarkdown = completePartialMarkdown;