@blocksdiy/react-common 1.47.0 → 1.49.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.
@@ -2,9 +2,11 @@ import { type Message, ToolMessage, type UserMessage } from "@ag-ui/client";
2
2
  import { useRenderToolCall } from "@copilotkit/react-core/v2";
3
3
  import { ReactNode, SetStateAction } from "react";
4
4
  import { type RenderProp } from "../utils/compose-render.js";
5
- import { type MessageRunIds } from "../utils/message-run-ids.js";
5
+ import { type ComposerMessageMeta } from "../utils/composer-message.js";
6
+ import { type MessageRunIds, type RunStartedAt } from "../utils/message-run-ids.js";
6
7
  export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
7
- export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, type MessageRunIds } from "../utils/message-run-ids.js";
8
+ export { type ComposerMessageMeta, FEEDBACK_MESSAGE_NAME, type FeedbackDraft, type FeedbackRate, feedbackComposerMeta, feedbackDraftOf, feedbackMessage, feedbackThreadId, isFeedbackMessage, isFeedbackTag, parseFeedbackContent, parseFeedbackTag, type ParsedFeedback, } from "../utils/composer-message.js";
9
+ export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, type MessageRunIds, type RunStartedAt, } from "../utils/message-run-ids.js";
8
10
  export { type Message, type AssistantMessage, type UserMessage } from "@ag-ui/client";
9
11
  export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
10
12
  export interface Attachment {
@@ -43,6 +45,7 @@ export interface Agent {
43
45
  voiceId?: string;
44
46
  harness?: string;
45
47
  }
48
+ export type OnUserMessageSent = (text: string, message: Omit<UserMessage, "id" | "role">) => void;
46
49
  export interface AgentChatContextValue {
47
50
  messages: Message[];
48
51
  attachments: Attachment[];
@@ -60,33 +63,14 @@ export interface AgentChatContextValue {
60
63
  role: "assistant" | "user";
61
64
  }[];
62
65
  messageById: Map<string, Message>;
63
- /**
64
- * The agent run (turn) that produced each replayed message — the `agentRunId` the rows
65
- * it wrote carry. Filled from the connect replay; messages from before run attribution
66
- * (and live-streamed ones) have no entry. Optional so contexts a host builds by hand
67
- * (previews, stubs) need not state it; absent reads as "no run known".
68
- */
69
66
  runIdByMessageId?: MessageRunIds;
67
+ runStartedAt?: RunStartedAt;
70
68
  toolMessages: ToolMessage[];
71
69
  renderToolCall: ReturnType<typeof useRenderToolCall>;
72
70
  sendMessage: (message: Omit<UserMessage, "id" | "role">) => Promise<void>;
73
71
  sendFromInputs: () => Promise<void>;
74
- /**
75
- * Replay this conversation's transcript from the server, in place.
76
- *
77
- * The same round-trip a fresh mount does — the connect endpoint reads the checkpoint and
78
- * answers with a MESSAGES_SNAPSHOT, which replaces the mirrored list. For turns that ran
79
- * SERVER-SIDE, which no client streamed and so no client has: without this they appear only
80
- * after a reload or a conversation switch.
81
- *
82
- * A no-op while a run is in flight: that run's own stream is the authoritative view of the
83
- * conversation, and replacing the list underneath it would fight the events still arriving.
84
- */
85
72
  reloadMessages: () => Promise<void>;
86
- /** Resumes a run parked on a server-side interrupt. Rejects if the resume fails. */
87
- /** Resolves false when a run was already in flight, so nothing was sent. */
88
73
  resolveInterrupt: (resume: unknown) => Promise<boolean>;
89
- /** Answers one parked card; resumes once every card of the turn is answered. */
90
74
  answerInterrupt: (answer: {
91
75
  toolCallId: string;
92
76
  answer: unknown;
@@ -99,6 +83,10 @@ export interface AgentChatContextValue {
99
83
  setAttachments: React.Dispatch<SetStateAction<Attachment[]>>;
100
84
  setIsDraggingFiles: React.Dispatch<SetStateAction<boolean>>;
101
85
  setCurrentThreadId: (currentThreadId: string) => void;
86
+ /** Arms the next send; cleared by send and conversation switch. */
87
+ composerMessageMeta?: ComposerMessageMeta | null;
88
+ setComposerMessageMeta?: (meta: ComposerMessageMeta | null) => void;
89
+ setRunningElsewhere?: (runId: string | null) => void;
102
90
  }
103
91
  export declare const AgentChatContext: import("react").Context<AgentChatContextValue | null>;
104
92
  export declare const useAgentChat: () => AgentChatContextValue;
@@ -140,9 +128,11 @@ export interface AgentChatRootProps {
140
128
  * Fires once a user send has been added to the conversation, with its text (empty
141
129
  * string for attachment-only sends) — the seam for ChatGPT-style optimistic thread
142
130
  * listing: hosts pair it with useAgentThreads' notifyThreadActivity. Covers sends
143
- * routed through `sendMessage`, not the auto-sent `initialPrompt`.
131
+ * routed through `sendMessage`, not the auto-sent `initialPrompt`. The second argument
132
+ * is the message as sent, for hosts that treat a protocol send (one carrying a `name`)
133
+ * differently from an ordinary turn.
144
134
  */
145
- onUserMessageSent?: (text: string) => void;
135
+ onUserMessageSent?: OnUserMessageSent;
146
136
  /**
147
137
  * Skip the agent-chat block's `initialPrompt` auto-send. For surfaces that mint a fresh
148
138
  * thread per conversation (an inbox's "New chat"): the auto-send fires once PER THREAD,
@@ -1 +1 @@
1
- {"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI5E,OAAO,EAAiB,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAEL,SAAS,EACT,cAAc,EAQf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAA4B,KAAK,mBAAmB,EAAqB,MAAM,2BAA2B,CAAC;AAClH,OAAO,EAQL,KAAK,aAAa,EAGlB,KAAK,YAAY,EAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EACL,KAAK,mBAAmB,EACxB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,cAAc,GACf,MAAM,2BAA2B,CAAC;AAOnC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,WAEzD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuND,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;AAiElG,MAAM,WAAW,qBAAqB;IAEpC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,aAAa,EAAE;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;IACtE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,gBAAgB,CAAC,EAAE,aAAa,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,cAAc,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;IAErD,WAAW,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,gBAAgB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,eAAe,EAAE,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IACpD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAE7B,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC7D,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,kBAAkB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtD,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACjD,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC;IACpE,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CACtD;AAED,eAAO,MAAM,gBAAgB,uDAAoD,CAAC;AAElF,eAAO,MAAM,YAAY,6BAMxB,CAAC;AAstBF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,gBAAgB,CAAC,EAAE,UAAU,EAAE,CAAC;IAChC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;OASG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAItC;AAED,eAAO,MAAM,aAAa,GAAI,cAK3B,kBAAkB,uCA2NpB,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,qBAAqB,sCAMjH;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAAA;KAAE,CAAC;CACpE;AAED,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,0BAA0B,+BAU1D;AAED,MAAM,WAAW,sBAAsB;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,0FAA0F;IAC1F,aAAa,EAAE,OAAO,CAAC;CACxB;AAMD,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,8BAA6C,CAAC;AAI/E,wBAAgB,iBAAiB,CAAC,EAChC,mBAAmB,EACnB,UAAU,EACV,gBAA6C,EAC7C,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,+BA0FtD;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAe,EACf,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,sCAUtD;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAe,EACf,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,sCA+BtD;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC;CAC9D;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAe,EACf,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,mBAAmB,+BAkKxD;AACD,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,OAAe,EACf,MAAM,EACN,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,yBAAyB,+BAyC5D;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,OAAe,EACf,MAAM,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,QAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,+BAA+B,+BAmElE;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,UAAU,EACV,OAAe,EACf,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,wBAAwB,+BAWxD;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,UAAU,EACV,OAAe,EACf,MAAM,EACN,OAAO,EACP,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,8BAA8B,+BAgBjE"}
@@ -6,9 +6,11 @@ import { useCopilotKit, useRenderToolCall } from "@copilotkit/react-core/v2";
6
6
  import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
7
7
  import { useStickToBottom } from "use-stick-to-bottom";
8
8
  import { ComposeRender } from "../utils/compose-render.js";
9
- import { EMPTY_MESSAGE_RUN_IDS, findMessageGroupElement, mergeMessageRunIds, MESSAGE_IDS_ATTRIBUTE, messageIdsOfRun, readMessageRunIdsEvent, } from "../utils/message-run-ids.js";
9
+ import { buildArmedMessageContent, userTextOfContent } from "../utils/composer-message.js";
10
+ import { claimRunInput, EMPTY_MESSAGE_RUN_IDS, EMPTY_RUN_STARTED_AT, findMessageGroupElement, mergeEntries, MESSAGE_IDS_ATTRIBUTE, messageIdsOfRun, readMessageRunIdsEvent, readRunStartedAtEvent, } from "../utils/message-run-ids.js";
10
11
  export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
11
- export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun } from "../utils/message-run-ids.js";
12
+ export { FEEDBACK_MESSAGE_NAME, feedbackComposerMeta, feedbackDraftOf, feedbackMessage, feedbackThreadId, isFeedbackMessage, isFeedbackTag, parseFeedbackContent, parseFeedbackTag, } from "../utils/composer-message.js";
13
+ export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, } from "../utils/message-run-ids.js";
12
14
  export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
13
15
  // Client-only `useLayoutEffect`, falls back to `useEffect` during SSR.
14
16
  // Needed so the `agent.threadId` mirror commits before CopilotKit's
@@ -173,9 +175,9 @@ const uploadAttachments = async ({ attachments, appId, token, }) => {
173
175
  };
174
176
  }));
175
177
  };
176
- const hasSendableInput = (prompt, attachments) => {
177
- return prompt.trim().length > 0 || attachments.length > 0;
178
- };
178
+ // An armed send needs text: the composer is the comment path — a rating alone goes out as
179
+ // `feedbackMessage` — and an unrated marker on its own is an ordinary turn on the server.
180
+ const hasSendableInput = (prompt, attachments, meta) => prompt.trim().length > 0 || (!meta && attachments.length > 0);
179
181
  const isRecord = (value) => {
180
182
  return typeof value === "object" && value !== null;
181
183
  };
@@ -311,22 +313,57 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
311
313
  });
312
314
  return groups;
313
315
  }, [copilotMessages]);
314
- // Which run produced each replayed message: the connect replay sends it as a CUSTOM
315
- // event (see utils/message-run-ids). Cleared with the transcript on a conversation swap.
316
316
  const [runIdByMessageId, setRunIdByMessageId] = useState(EMPTY_MESSAGE_RUN_IDS);
317
+ const [runStartedAt, setRunStartedAt] = useState(EMPTY_RUN_STARTED_AT);
318
+ const liveRunRef = useRef(null);
319
+ const ownRunIdsRef = useRef(new Set());
317
320
  useEffect(() => {
318
321
  if (!copilotAgent) {
319
322
  return;
320
323
  }
324
+ const claimLive = (messageId) => {
325
+ const run = liveRunRef.current;
326
+ if (!run) {
327
+ return;
328
+ }
329
+ const { runId, startedAt, input } = run;
330
+ run.input = null;
331
+ if (input) {
332
+ setRunStartedAt((previous) => mergeEntries(previous, { [runId]: startedAt }));
333
+ }
334
+ setRunIdByMessageId((previous) => {
335
+ const claims = input ? claimRunInput(input, previous, runId) : {};
336
+ if (!previous.has(messageId)) {
337
+ claims[messageId] = runId;
338
+ }
339
+ return mergeEntries(previous, claims);
340
+ });
341
+ };
321
342
  const { unsubscribe } = copilotAgent.subscribe({
343
+ onRunStartedEvent: ({ event, messages }) => {
344
+ liveRunRef.current = { runId: event.runId, startedAt: event.timestamp ?? Date.now(), input: messages };
345
+ ownRunIdsRef.current.add(event.runId);
346
+ },
347
+ onTextMessageStartEvent: ({ event }) => claimLive(event.messageId),
348
+ onToolCallStartEvent: ({ event }) => claimLive(event.parentMessageId ?? event.toolCallId),
349
+ onRunFinalized: () => {
350
+ liveRunRef.current = null;
351
+ },
322
352
  onCustomEvent: ({ event }) => {
323
353
  const runIds = readMessageRunIdsEvent(event);
324
354
  if (runIds) {
325
- setRunIdByMessageId((previous) => mergeMessageRunIds(previous, runIds));
355
+ setRunIdByMessageId((previous) => mergeEntries(previous, runIds));
356
+ }
357
+ const startedAt = readRunStartedAtEvent(event);
358
+ if (startedAt) {
359
+ setRunStartedAt((previous) => mergeEntries(previous, startedAt));
326
360
  }
327
361
  },
328
362
  });
329
- return unsubscribe;
363
+ return () => {
364
+ unsubscribe();
365
+ liveRunRef.current = null;
366
+ };
330
367
  }, [copilotAgent]);
331
368
  const interruptAnswersRef = useRef(new Map());
332
369
  // Mirror `currentThreadId` onto the agent instance — the `<CopilotKit>`
@@ -346,11 +383,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
346
383
  }, [copilotAgent, currentThreadId]);
347
384
  const [prompt, setPrompt] = useState("");
348
385
  const [attachments, setAttachments] = useState([]);
349
- // Conversation identity. When a mounted chat is pointed at another conversation, the
350
- // reconnect replay takes a round-trip — clear the previous transcript and composer
351
- // immediately so the old conversation never lingers on screen (same visual contract as
352
- // a remount). Deliberately NOT keyed on `initKey`: that also changes when the block
353
- // configs finish loading, and clearing then would wipe a freshly replayed transcript.
386
+ const [composerMessageMeta, setComposerMessageMeta] = useState(null);
387
+ // setRunningElsewhere: a run elsewhere reads as this chat's own; own runs match RUN_STARTED id; refused at send time via ref (upload awaits).
388
+ const [runningElsewhereRunId, setRunningElsewhere] = useState(null);
389
+ const [ownRunInFlight, setOwnRunInFlight] = useState(false);
390
+ const isRunningElsewhere = runningElsewhereRunId !== null && !ownRunInFlight && !ownRunIdsRef.current.has(runningElsewhereRunId);
391
+ const isRunningElsewhereRef = useRef(isRunningElsewhere);
392
+ isRunningElsewhereRef.current = isRunningElsewhere;
354
393
  const conversationKey = `${chatId ?? ""}|${currentThreadId ?? ""}`;
355
394
  const prevConversationKeyRef = useRef(conversationKey);
356
395
  useIsomorphicLayoutEffect(() => {
@@ -360,16 +399,11 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
360
399
  prevConversationKeyRef.current = conversationKey;
361
400
  copilotAgent?.setMessages?.([]);
362
401
  setRunIdByMessageId(EMPTY_MESSAGE_RUN_IDS);
402
+ setRunStartedAt(EMPTY_RUN_STARTED_AT);
363
403
  setPrompt("");
364
404
  setAttachments([]);
365
- // The clear above runs in the layout phase, but the previous conversation's run is
366
- // detached in passive effects — and even past detach, events already buffered in the
367
- // apply pipeline keep applying, each reassigning `agent.messages` WHOLESALE. One late
368
- // chunk would resurrect the entire previous transcript (and re-arm its auto-executing
369
- // tool-call follow-ups) under the new conversation's header. Detach here too and
370
- // re-clear once the drain settles; the reconnect that follows replays on top of the
371
- // (guaranteed-empty) mirror. On threadId switches the core's fresh-restore re-clears
372
- // as well — harmless overlap — but on chatId-only switches this is the only re-clear.
405
+ setComposerMessageMeta(null);
406
+ setOwnRunInFlight(false);
373
407
  const detachable = copilotAgent;
374
408
  const drained = detachable?.detachActiveRun?.();
375
409
  if (drained) {
@@ -381,15 +415,6 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
381
415
  });
382
416
  }
383
417
  }, [conversationKey]);
384
- // CopilotKit reconnects only when its threadId changes — but a conversation is the
385
- // (chatId, threadId) PAIR, and rows written by surfaces that use the fixed default
386
- // thread id differ only by chatId. On such a chatId-only switch the library's connect
387
- // effect sees unchanged deps and does nothing, which would leave the (just cleared)
388
- // transcript empty forever. Reconnect manually, replicating the core's fresh-restore
389
- // steps: reset state and drop the replay cursor — cursors are keyed by threadId, so
390
- // the shared id makes the previous conversation's cursor actively wrong here — then
391
- // connect (the core detaches any in-flight run itself). The core reads `properties`
392
- // after an await, so the new chatId is already synced by the time the request builds.
393
418
  const prevConnectionRef = useRef({ chatId, threadId: currentThreadId });
394
419
  useEffect(() => {
395
420
  const previous = prevConnectionRef.current;
@@ -401,23 +426,16 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
401
426
  }
402
427
  copilotAgent.setState?.({});
403
428
  copilotAgent.clearReplayCursor?.(currentThreadId);
404
- // Same guard convention as the composer send path: a failed reconnect already
405
- // surfaces through the availability state; the rejection carries no extra signal.
406
429
  void copilotkit.connectAgent({ agent: copilotAgent }).catch(() => undefined);
407
430
  }, [chatId, currentThreadId, copilotAgent, copilotkit]);
408
431
  const [isDraggingFiles, setIsDraggingFiles] = useState(false);
409
432
  const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
410
- const canSendMessage = Boolean(agentChatBlock && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning);
433
+ const canSendMessage = Boolean(agentChatBlock && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning && !isRunningElsewhere);
411
434
  const isFetchingMessages = !isConfigLoaded || (!isAgentReady && copilotMessages.length === 0);
412
- // CopilotKit reports `isLoading` during connect/replay as well as real runs. Only expose
413
- // thinking once the bootstrap connect phase has completed — and that needs the readiness
414
- // latch, not just `isFetchingMessages`, which goes false as soon as ONE replayed message
415
- // exists: without `isAgentReady`, thinking leaks true for the tail of every replay, and
416
- // every load or thread switch flashes the typing dots under the transcript. Real runs are
417
- // unaffected — sending requires readiness (canSendMessage), so the latch is already set.
418
- const isThinking = isAgentRunning && !isFetchingMessages && isAgentReady;
435
+ const isThinking = (isAgentRunning || isRunningElsewhere) && !isFetchingMessages && isAgentReady;
419
436
  const runAgentWithRetry = useCallback(async (agentToRun, messageCountAtRunStart, forwardedProps) => {
420
- if (agentToRun.isRunning) {
437
+ // A run elsewhere is on this conversation's checkpoint too: ours would race it.
438
+ if (agentToRun.isRunning || isRunningElsewhereRef.current) {
421
439
  return false;
422
440
  }
423
441
  const runAgentAndThrowError = async () => {
@@ -440,8 +458,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
440
458
  unsubscribe();
441
459
  }
442
460
  };
461
+ // In flight until the run ends, whichever way (see `runningElsewhereRunId`) — unless
462
+ // the conversation switched under it, which resets this itself.
463
+ const ranOn = prevConversationKeyRef.current;
464
+ setOwnRunInFlight(true);
443
465
  try {
444
466
  await runAgentAndThrowError();
467
+ return true;
445
468
  }
446
469
  catch (error) {
447
470
  const hasAgentStartedResponding = agentToRun.messages.length > messageCountAtRunStart;
@@ -452,24 +475,19 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
452
475
  }
453
476
  throw error;
454
477
  }
455
- return true;
478
+ finally {
479
+ if (prevConversationKeyRef.current === ranOn) {
480
+ setOwnRunInFlight(false);
481
+ }
482
+ }
456
483
  }, [copilotkit]);
457
484
  const sendMessage = useCallback(async (message) => {
458
- if (!canSendMessage || !copilotAgent || copilotAgent.isRunning) {
485
+ if (!canSendMessage || !copilotAgent || copilotAgent.isRunning || isRunningElsewhereRef.current) {
459
486
  return;
460
487
  }
461
488
  copilotAgent.addMessage({ ...message, id: generateId(), role: "user" });
462
- // The send seam, fired once the message is IN the conversation, so hosts can react
463
- // to it (e.g. list the thread optimistically). Covers every send routed through
464
- // `sendMessage` — composer, empty-state heroes, user-generated chat components —
465
- // but deliberately NOT the auto-sent `initialPrompt` below, whose hidden variant is
466
- // a primer rather than something the user wrote.
467
489
  if (onUserMessageSent) {
468
- const { content } = message;
469
- const textPart = Array.isArray(content)
470
- ? content.find((part) => part.type === "text")
471
- : undefined;
472
- onUserMessageSent(typeof content === "string" ? content : (textPart?.text ?? ""));
490
+ onUserMessageSent(userTextOfContent(message.content), message);
473
491
  }
474
492
  await runAgentWithRetry(copilotAgent, copilotAgent.messages.length);
475
493
  }, [canSendMessage, copilotAgent, runAgentWithRetry, onUserMessageSent]);
@@ -477,14 +495,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
477
495
  if (!copilotAgent || copilotAgent.isRunning) {
478
496
  return;
479
497
  }
480
- // Replay cursors are keyed by threadId and record what has already been replayed, so a
481
- // stale one makes connect a no-op — exactly the case here, where the thread is unchanged
482
- // and only the server-side content moved. Messages are NOT cleared first: connect answers
483
- // with a full snapshot that replaces them, and clearing would blank the transcript for the
484
- // round-trip.
485
498
  copilotAgent.clearReplayCursor?.(currentThreadId);
486
- // Same guard convention as the composer send path: a failed reconnect already surfaces
487
- // through the availability state; the rejection carries no extra signal.
488
499
  await copilotkit.connectAgent({ agent: copilotAgent }).catch(() => undefined);
489
500
  }, [copilotAgent, copilotkit, currentThreadId]);
490
501
  // Not sourced from `useInterrupt`: that hook only hands `resolve` to its own
@@ -687,17 +698,44 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
687
698
  return contentArray;
688
699
  };
689
700
  const sendFromInputs = async () => {
690
- if (!canSendMessage || !hasSendableInput(prompt, attachments)) {
701
+ const currentMeta = composerMessageMeta;
702
+ if (!canSendMessage || !hasSendableInput(prompt, attachments, currentMeta)) {
691
703
  return;
692
704
  }
693
705
  const currentPrompt = prompt;
694
- const currentAttachments = attachments;
695
- const uploadedAttachments = await uploadAttachments({
696
- attachments: currentAttachments,
697
- appId,
698
- token,
699
- });
700
- const messageContent = buildMessageContentFromInput(currentPrompt, uploadedAttachments ?? []);
706
+ const composedOn = prevConversationKeyRef.current;
707
+ if (currentMeta) {
708
+ setComposerMessageMeta(null);
709
+ }
710
+ let uploadedAttachments;
711
+ try {
712
+ uploadedAttachments = await uploadAttachments({
713
+ attachments,
714
+ appId,
715
+ token,
716
+ });
717
+ }
718
+ catch (error) {
719
+ // Failed upload: re-arm unless the conversation switched.
720
+ if (currentMeta && prevConversationKeyRef.current === composedOn) {
721
+ setComposerMessageMeta((armed) => armed ?? currentMeta);
722
+ }
723
+ throw error;
724
+ }
725
+ // Post-upload: conversation switch → stop; run elsewhere → keep input and re-arm; failed upload handled above.
726
+ if (prevConversationKeyRef.current !== composedOn) {
727
+ return;
728
+ }
729
+ if (isRunningElsewhereRef.current || copilotAgent?.isRunning) {
730
+ if (currentMeta) {
731
+ setComposerMessageMeta((armed) => armed ?? currentMeta);
732
+ }
733
+ return;
734
+ }
735
+ const composed = buildMessageContentFromInput(currentPrompt, uploadedAttachments ?? []);
736
+ const messageContent = currentMeta
737
+ ? buildArmedMessageContent(currentMeta, composed)
738
+ : composed;
701
739
  setPrompt("");
702
740
  setAttachments([]);
703
741
  // Auto-skip any frontend user-choice tool call that's still waiting.
@@ -743,7 +781,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
743
781
  });
744
782
  }
745
783
  }
746
- await sendMessage({ content: messageContent }).catch(() => undefined);
784
+ await sendMessage(currentMeta?.name ? { content: messageContent, name: currentMeta.name } : { content: messageContent }).catch(() => undefined);
747
785
  };
748
786
  const addAttachments = (attachments) => {
749
787
  if (!attachmentsEnabled) {
@@ -776,6 +814,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
776
814
  renderToolCall,
777
815
  messageById,
778
816
  runIdByMessageId,
817
+ runStartedAt,
779
818
  toolMessages,
780
819
  prompt,
781
820
  attachments,
@@ -792,6 +831,9 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, chatId, token, appId, com
792
831
  setAttachments: setChatAttachments,
793
832
  setIsDraggingFiles: setChatIsDraggingFiles,
794
833
  setCurrentThreadId,
834
+ composerMessageMeta,
835
+ setComposerMessageMeta,
836
+ setRunningElsewhere,
795
837
  isDraggingFiles,
796
838
  isFetchingMessages,
797
839
  isThinking,
@@ -1057,10 +1099,10 @@ export function AgentChatFetching({ asChild = false, render, ...props }) {
1057
1099
  return (_jsx(ComposeRender, { render: render, asChild: asChild, defaultTag: "div", "data-state": isFetchingMessages ? "open" : "closed", "data-slot": "agent-chat-fetching", ...props }));
1058
1100
  }
1059
1101
  export function AgentChatInput({ asChild = false, render, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit, ...props }) {
1060
- const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, agentChat, } = useAgentChat();
1102
+ const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, agentChat, composerMessageMeta, } = useAgentChat();
1061
1103
  const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
1062
1104
  const isDisabled = props.disabled || isThinking || !isAgentReady || isFetchingMessages;
1063
- const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachmentsEnabled ? attachments : []);
1105
+ const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachmentsEnabled ? attachments : [], composerMessageMeta);
1064
1106
  const handleDragOver = useCallback((e) => {
1065
1107
  if (!attachmentsEnabled) {
1066
1108
  return;
@@ -1150,10 +1192,14 @@ export function AgentChatInput({ asChild = false, render, onChange, onKeyDown, o
1150
1192
  : onDrop, "data-slot": "agent-chat-input", ...props }));
1151
1193
  }
1152
1194
  export function AgentChatSendTrigger({ asChild = false, render, onClick, disabled, ...props }) {
1153
- const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs,
1195
+ const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs, composerMessageMeta,
1154
1196
  /* , stopGeneration */
1155
1197
  } = useAgentChat();
1156
- const isDisabled = Boolean(disabled || isThinking || !isAgentReady || isFetchingMessages || !hasSendableInput(prompt, attachments));
1198
+ const isDisabled = Boolean(disabled ||
1199
+ isThinking ||
1200
+ !isAgentReady ||
1201
+ isFetchingMessages ||
1202
+ !hasSendableInput(prompt, attachments, composerMessageMeta));
1157
1203
  return (_jsx(ComposeRender, { render: render, asChild: asChild, defaultTag: "button", onClick: (e) => {
1158
1204
  if (isDisabled) {
1159
1205
  e.preventDefault();
@@ -0,0 +1,32 @@
1
+ import { type ReactNode } from "react";
2
+ import type { AgentThreadItem } from "./agent-thread-live-update.js";
3
+ import { type AgentConversationChat, type AgentConversationRef, type UseAgentConversationResult } from "./use-agent-conversation.js";
4
+ import { type UseAgentThreadsOptions, type UseAgentThreadsResult } from "./use-agent-threads.js";
5
+ export type AgentThreadFilters = Pick<UseAgentThreadsOptions, "status" | "source">;
6
+ export interface AgentConversationContextValue {
7
+ agentId: string;
8
+ conversation: AgentConversationRef;
9
+ /** The new chat, nothing sent on it yet. */
10
+ isDraft: boolean;
11
+ /** The index under `filters`; its `renameThread` also keeps the open conversation's remembered title current. */
12
+ threads: UseAgentThreadsResult;
13
+ /** The open conversation's row: listed, else the last listed, else a stand-in (`isStandIn`). */
14
+ row: AgentThreadItem;
15
+ isStandIn: boolean;
16
+ filters: AgentThreadFilters;
17
+ setFilters: (filters: AgentThreadFilters) => void;
18
+ hasActiveFilters: boolean;
19
+ /** After this client sends on the conversation (the chat root's onUserMessageSent). */
20
+ notifyMessageSent: UseAgentConversationResult["notifyMessageSent"];
21
+ }
22
+ export interface AgentConversationProviderProps extends Pick<UseAgentThreadsOptions, "token" | "headers" | "baseUrl"> {
23
+ agentId: string;
24
+ conversation: AgentConversationRef;
25
+ isDraft: boolean;
26
+ /** The chat runtime's side of the bridge (react-common's useAgentChat). */
27
+ chat: AgentConversationChat;
28
+ children?: ReactNode;
29
+ }
30
+ export declare function AgentConversationProvider({ agentId, conversation, isDraft, chat, token, headers, baseUrl, children, }: AgentConversationProviderProps): import("react").JSX.Element;
31
+ export declare const useAgentConversationContext: () => AgentConversationContextValue;
32
+ //# sourceMappingURL=agent-conversation-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-conversation-context.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-conversation-context.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAmB,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/G,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEnF,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,iHAAiH;IACjH,OAAO,EAAE,qBAAqB,CAAC;IAC/B,gGAAgG;IAChG,GAAG,EAAE,eAAe,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uFAAuF;IACvF,iBAAiB,EAAE,0BAA0B,CAAC,mBAAmB,CAAC,CAAC;CACpE;AAID,MAAM,WAAW,8BAA+B,SAAQ,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACnH,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,oBAAoB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,YAAY,EACZ,OAAO,EACP,IAAI,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,GACT,EAAE,8BAA8B,+BA4BhC;AAED,eAAO,MAAM,2BAA2B,QAAO,6BAM9C,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // The headless Root of an agent conversation surface: one provider that owns the conversation
3
+ // index (useAgentThreads) under the surface's filters and the open conversation's bridge to the
4
+ // chat (useAgentConversation), for parts to read through useAgentConversationContext. The
5
+ // agent-layout registry item renders it; a custom UI over the same behaviour can too.
6
+ import { createContext, useContext, useState } from "react";
7
+ import { useAgentConversation, } from "./use-agent-conversation.js";
8
+ import { useAgentThreads } from "./use-agent-threads.js";
9
+ const AgentConversationContext = createContext(null);
10
+ export function AgentConversationProvider({ agentId, conversation, isDraft, chat, token, headers, baseUrl, children, }) {
11
+ const [filters, setFilters] = useState({});
12
+ const threads = useAgentThreads({ agentId, status: filters.status, source: filters.source, token, headers, baseUrl });
13
+ const { row, isStandIn, renameThread, notifyMessageSent } = useAgentConversation({
14
+ threads,
15
+ agentId,
16
+ conversation,
17
+ isDraft,
18
+ chat,
19
+ });
20
+ return (_jsx(AgentConversationContext.Provider, { value: {
21
+ agentId,
22
+ conversation,
23
+ isDraft,
24
+ threads: { ...threads, renameThread },
25
+ row,
26
+ isStandIn,
27
+ filters,
28
+ setFilters,
29
+ hasActiveFilters: Boolean(filters.status ?? filters.source),
30
+ notifyMessageSent,
31
+ }, children: children }));
32
+ }
33
+ export const useAgentConversationContext = () => {
34
+ const context = useContext(AgentConversationContext);
35
+ if (!context) {
36
+ throw new Error("useAgentConversationContext must be used within <AgentConversationProvider>.");
37
+ }
38
+ return context;
39
+ };