@blocksdiy/react-common 1.64.0 → 1.66.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/components/new-agent-chat.d.ts +13 -0
- package/dist/components/new-agent-chat.d.ts.map +1 -1
- package/dist/components/new-agent-chat.js +197 -43
- package/dist/hooks/agent-thread-live-update.d.ts +1 -1
- package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
- package/dist/hooks/agent-thread-live-update.js +10 -1
- package/dist/utils/live-turn.d.ts +83 -0
- package/dist/utils/live-turn.d.ts.map +1 -0
- package/dist/utils/live-turn.js +71 -0
- package/dist/utils/scheduled-trigger.d.ts +18 -0
- package/dist/utils/scheduled-trigger.d.ts.map +1 -0
- package/dist/utils/scheduled-trigger.js +39 -0
- package/package.json +1 -1
|
@@ -5,10 +5,12 @@ import { type ApprovalCardView, type ApprovalDecision } from "../utils/agui-inte
|
|
|
5
5
|
import { type RenderProp } from "../utils/compose-render.js";
|
|
6
6
|
import { type ComposerMessageMeta } from "../utils/composer-message.js";
|
|
7
7
|
import { type MessageRunIds, type RunStartedAt } from "../utils/message-run-ids.js";
|
|
8
|
+
import { type ScheduledTrigger } from "../utils/scheduled-trigger.js";
|
|
8
9
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
9
10
|
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";
|
|
10
11
|
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, type MessageRunIds, type RunStartedAt, } from "../utils/message-run-ids.js";
|
|
11
12
|
export { type ApprovalCardView, type ApprovalDecision, toolStepVisibleBelowCards } from "../utils/agui-interrupts.js";
|
|
13
|
+
export { type ScheduledTrigger } from "../utils/scheduled-trigger.js";
|
|
12
14
|
export { type Message, type AssistantMessage, type UserMessage } from "@ag-ui/client";
|
|
13
15
|
export { useRenderTool, useDefaultRenderTool, useHumanInTheLoop, useFrontendTool, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
14
16
|
export interface Attachment {
|
|
@@ -73,6 +75,12 @@ export interface AgentChatContextValue {
|
|
|
73
75
|
role: "assistant" | "user";
|
|
74
76
|
}[];
|
|
75
77
|
messageById: Map<string, Message>;
|
|
78
|
+
/**
|
|
79
|
+
* The reminder or schedule that started a turn, keyed by the turn's first message id. A
|
|
80
|
+
* message group starts at each one, so a group whose first message is here draws the line.
|
|
81
|
+
* Optional: a context a host builds by hand may omit it.
|
|
82
|
+
*/
|
|
83
|
+
scheduledTriggerByMessageId?: ReadonlyMap<string, ScheduledTrigger>;
|
|
76
84
|
runIdByMessageId?: MessageRunIds;
|
|
77
85
|
runStartedAt?: RunStartedAt;
|
|
78
86
|
toolMessages: ToolMessage[];
|
|
@@ -99,6 +107,11 @@ export interface AgentChatContextValue {
|
|
|
99
107
|
/** A tool step's title: the tool's display name when the server stamped one, else its name, in words. */
|
|
100
108
|
toolTitleFor?: (toolCallId: string, toolName: string) => string;
|
|
101
109
|
stopGeneration: () => void;
|
|
110
|
+
/**
|
|
111
|
+
* A running reply Stop applies to: this tab's own, or a followed one a chat started (never a
|
|
112
|
+
* workflow's or a reminder's), and only with the live turn stream on.
|
|
113
|
+
*/
|
|
114
|
+
canStop?: boolean;
|
|
102
115
|
addAttachments: (attachments: Attachment[]) => void;
|
|
103
116
|
removeAttachment: (url: string) => void;
|
|
104
117
|
clearAttachments: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAoB,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI9G,OAAO,EAAiB,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAEL,SAAS,EACT,cAAc,EAQf,MAAM,OAAO,CAAC;AAMf,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAqBtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAA4B,KAAK,mBAAmB,EAAqB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAoB,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI9G,OAAO,EAAiB,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAEL,SAAS,EACT,cAAc,EAQf,MAAM,OAAO,CAAC;AAMf,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAqBtB,MAAM,0BAA0B,CAAC;AAClC,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;AAClC,OAAO,EAAE,KAAK,gBAAgB,EAAgC,MAAM,4BAA4B,CAAC;AAEjG,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;AAClC,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACnH,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,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;IACjB,kHAAkH;IAClH,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC9B;AA0LD,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;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,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;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACpE,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,oFAAoF;IACpF,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,oGAAoG;IACpG,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,gBAAgB,CAAC;QAC3B,6DAA6D;QAC7D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,gBAAgB,EAAE,CAAC;IAC9D,yGAAyG;IACzG,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAChE,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,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;AAoiCF,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;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,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;AAMD,eAAO,MAAM,aAAa,GAAI,cAK3B,kBAAkB,uCAgSpB,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,+BA8C5D;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"}
|
|
@@ -12,7 +12,9 @@ import { answerRouteOf, approvalCardsForToolCall, buildResumeEntries, cancelledR
|
|
|
12
12
|
import { ComposeRender } from "../utils/compose-render.js";
|
|
13
13
|
import { buildArmedMessageContent, userTextOfContent } from "../utils/composer-message.js";
|
|
14
14
|
import { forwardedLegacyChatId, pinnedLegacyChatThreadId } from "../utils/legacy-chat-thread-id.js";
|
|
15
|
+
import { buildStopEnvelope, canStop, isThreadBusyError, liveAfterTerminal, readLiveTurnEvent, shouldConnectForRun, shouldReconnectAfterStream, stopRunIdOf, streamRunIdOf, } from "../utils/live-turn.js";
|
|
15
16
|
import { claimRunInput, EMPTY_MESSAGE_RUN_IDS, EMPTY_RUN_STARTED_AT, findMessageGroupElement, mergeEntries, MESSAGE_IDS_ATTRIBUTE, messageIdsOfRun, readMessageRunIdsEvent, readRunStartedAtEvent, } from "../utils/message-run-ids.js";
|
|
17
|
+
import { scheduledTriggersByMessageId } from "../utils/scheduled-trigger.js";
|
|
16
18
|
export { useTextGrowthAnimating } from "../hooks/text-growth-animation.js";
|
|
17
19
|
export { FEEDBACK_MESSAGE_NAME, feedbackComposerMeta, feedbackDraftOf, feedbackMessage, feedbackThreadId, isFeedbackMessage, isFeedbackTag, parseFeedbackContent, parseFeedbackTag, } from "../utils/composer-message.js";
|
|
18
20
|
export { MESSAGE_RUN_IDS_EVENT_NAME, messageIdsOfRun, } from "../utils/message-run-ids.js";
|
|
@@ -247,24 +249,6 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
247
249
|
token ?? "",
|
|
248
250
|
appId ?? "",
|
|
249
251
|
].join("|");
|
|
250
|
-
// Readiness latch. CopilotKit's `isAvailable` deadlocks false when a replayed
|
|
251
|
-
// transcript ends with an unanswered frontend HITL tool call — see
|
|
252
|
-
// `isConnectParkedOnPendingToolCall`. So also treat "parked but loaded" as
|
|
253
|
-
// ready, and latch it: once ready we stay ready for the session, otherwise
|
|
254
|
-
// typing past the question (which empties the pending set) would re-lock the
|
|
255
|
-
// composer while `isAvailable` is still parked-false. Keyed to `initKey` so a
|
|
256
|
-
// swap resets readiness. We skip promotion on the render the key changes on,
|
|
257
|
-
// because `isCopilotAgentAvailable`/`copilotMessages` still hold the previous
|
|
258
|
-
// session's values until CopilotKit's connect effect runs post-commit.
|
|
259
|
-
const agentReadyLatchRef = useRef({ key: initKey, ready: false });
|
|
260
|
-
const initKeyChanged = agentReadyLatchRef.current.key !== initKey;
|
|
261
|
-
if (initKeyChanged) {
|
|
262
|
-
agentReadyLatchRef.current = { key: initKey, ready: false };
|
|
263
|
-
}
|
|
264
|
-
if (!initKeyChanged && (isCopilotAgentAvailable || isConnectParkedOnPendingToolCall(copilotMessages))) {
|
|
265
|
-
agentReadyLatchRef.current.ready = true;
|
|
266
|
-
}
|
|
267
|
-
const isAgentReady = agentReadyLatchRef.current.ready;
|
|
268
252
|
const { copilotkit } = useCopilotKit();
|
|
269
253
|
const renderToolCall = useRenderToolCall();
|
|
270
254
|
const messageById = useMemo(() => {
|
|
@@ -277,6 +261,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
277
261
|
const toolMessages = useMemo(() => {
|
|
278
262
|
return copilotMessages.filter((message) => message.role === "tool");
|
|
279
263
|
}, [copilotMessages]);
|
|
264
|
+
const scheduledTriggerByMessageId = useMemo(() => scheduledTriggersByMessageId(copilotMessages), [copilotMessages]);
|
|
280
265
|
const messageGroups = useMemo(() => {
|
|
281
266
|
const groups = [];
|
|
282
267
|
copilotMessages.forEach((message) => {
|
|
@@ -284,7 +269,9 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
284
269
|
return;
|
|
285
270
|
}
|
|
286
271
|
const lastGroup = groups[groups.length - 1];
|
|
287
|
-
|
|
272
|
+
// A reminder's reply starts its own group even right after the agent's last message, so
|
|
273
|
+
// its trigger line sits above it and not inside the earlier reply.
|
|
274
|
+
if (!lastGroup || lastGroup.role !== message.role || scheduledTriggerByMessageId.has(message.id)) {
|
|
288
275
|
groups.push({ role: message.role, messageIds: [message.id] });
|
|
289
276
|
}
|
|
290
277
|
else {
|
|
@@ -292,11 +279,63 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
292
279
|
}
|
|
293
280
|
});
|
|
294
281
|
return groups;
|
|
295
|
-
}, [copilotMessages]);
|
|
282
|
+
}, [copilotMessages, scheduledTriggerByMessageId]);
|
|
296
283
|
const [runIdByMessageId, setRunIdByMessageId] = useState(EMPTY_MESSAGE_RUN_IDS);
|
|
297
284
|
const [runStartedAt, setRunStartedAt] = useState(EMPTY_RUN_STARTED_AT);
|
|
298
285
|
const liveRunRef = useRef(null);
|
|
299
286
|
const ownRunIdsRef = useRef(new Set());
|
|
287
|
+
// The live turn stream (flag on): what the connect said about the thread's active turn. A
|
|
288
|
+
// flag-off server sends no such event, so `liveTurn` stays null and every branch stays off.
|
|
289
|
+
const [liveTurn, setLiveTurn] = useState(null);
|
|
290
|
+
const liveTurnRef = useRef(null);
|
|
291
|
+
liveTurnRef.current = liveTurn;
|
|
292
|
+
// Readiness latch. CopilotKit's `isAvailable` deadlocks false when a replayed
|
|
293
|
+
// transcript ends with an unanswered frontend HITL tool call — see
|
|
294
|
+
// `isConnectParkedOnPendingToolCall`. So also treat "parked but loaded" as
|
|
295
|
+
// ready, and latch it: once ready we stay ready for the session, otherwise
|
|
296
|
+
// typing past the question (which empties the pending set) would re-lock the
|
|
297
|
+
// composer while `isAvailable` is still parked-false. Keyed to `initKey` so a
|
|
298
|
+
// swap resets readiness. We skip promotion on the render the key changes on,
|
|
299
|
+
// because `isCopilotAgentAvailable`/`copilotMessages` still hold the previous
|
|
300
|
+
// session's values until CopilotKit's connect effect runs post-commit.
|
|
301
|
+
// A tab that joined a running turn is ready too: its connect streams until the turn ends, so
|
|
302
|
+
// `isAvailable` stays false while the tab already shows the reply and may stop it.
|
|
303
|
+
const agentReadyLatchRef = useRef({ key: initKey, ready: false });
|
|
304
|
+
const initKeyChanged = agentReadyLatchRef.current.key !== initKey;
|
|
305
|
+
if (initKeyChanged) {
|
|
306
|
+
agentReadyLatchRef.current = { key: initKey, ready: false };
|
|
307
|
+
}
|
|
308
|
+
if (!initKeyChanged &&
|
|
309
|
+
(isCopilotAgentAvailable || isConnectParkedOnPendingToolCall(copilotMessages) || Boolean(liveTurn?.runId))) {
|
|
310
|
+
agentReadyLatchRef.current.ready = true;
|
|
311
|
+
}
|
|
312
|
+
const isAgentReady = agentReadyLatchRef.current.ready;
|
|
313
|
+
const ownRunInFlightRef = useRef(false);
|
|
314
|
+
// The run each stream last started (a connect's own history run first, then the followed
|
|
315
|
+
// turn's), and the runs a RUN_FINISHED / RUN_ERROR closed.
|
|
316
|
+
const streamRunIdRef = useRef(null);
|
|
317
|
+
const terminalRunIdsRef = useRef(new Set());
|
|
318
|
+
// Whether the stream now ending delivered its RUN_STARTED: a request that failed outright did not.
|
|
319
|
+
const streamStartedRef = useRef(false);
|
|
320
|
+
const connectInFlightRef = useRef(false);
|
|
321
|
+
const lastConnectedForRunIdRef = useRef(null);
|
|
322
|
+
const threadBusyRefusedRef = useRef(false);
|
|
323
|
+
// One connect to watch the thread's running turn; `runId` is the run it answers for.
|
|
324
|
+
const followRunningTurn = useCallback((runId) => {
|
|
325
|
+
if (!copilotAgent || connectInFlightRef.current) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
connectInFlightRef.current = true;
|
|
329
|
+
if (runId) {
|
|
330
|
+
lastConnectedForRunIdRef.current = runId;
|
|
331
|
+
}
|
|
332
|
+
void copilotkit
|
|
333
|
+
.connectAgent({ agent: copilotAgent })
|
|
334
|
+
.catch(() => undefined)
|
|
335
|
+
.finally(() => {
|
|
336
|
+
connectInFlightRef.current = false;
|
|
337
|
+
});
|
|
338
|
+
}, [copilotAgent, copilotkit]);
|
|
300
339
|
// Paired once, when the pause arrives: while the next run streams, a sub-agent's own
|
|
301
340
|
// calls arrive as the parent's and would re-pair the request with them -- a second
|
|
302
341
|
// card on the parent's call.
|
|
@@ -324,6 +363,20 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
324
363
|
if (!copilotAgent) {
|
|
325
364
|
return;
|
|
326
365
|
}
|
|
366
|
+
const threadOfStream = () => copilotAgent.threadId;
|
|
367
|
+
const endRun = (runId) => {
|
|
368
|
+
if (!runId) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
terminalRunIdsRef.current.add(runId);
|
|
372
|
+
const live = liveTurnRef.current;
|
|
373
|
+
if (live?.runId === runId) {
|
|
374
|
+
// Answered for: the row still names it until the settle arrives, so no doorbell.
|
|
375
|
+
lastConnectedForRunIdRef.current = runId;
|
|
376
|
+
liveTurnRef.current = liveAfterTerminal(live, runId);
|
|
377
|
+
setLiveTurn(liveTurnRef.current);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
327
380
|
const claimLive = (messageId) => {
|
|
328
381
|
const run = liveRunRef.current;
|
|
329
382
|
if (!run) {
|
|
@@ -346,7 +399,11 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
346
399
|
onRunStartedEvent: ({ event, messages }) => {
|
|
347
400
|
liveRunRef.current = { runId: event.runId, startedAt: event.timestamp ?? Date.now(), input: messages };
|
|
348
401
|
ownRunIdsRef.current.add(event.runId);
|
|
402
|
+
streamRunIdRef.current = event.runId;
|
|
403
|
+
streamStartedRef.current = true;
|
|
349
404
|
},
|
|
405
|
+
onRunFinishedEvent: ({ event }) => endRun(event.runId),
|
|
406
|
+
onRunErrorEvent: () => endRun(streamRunIdRef.current),
|
|
350
407
|
onTextMessageStartEvent: ({ event }) => claimLive(event.messageId),
|
|
351
408
|
onToolCallStartEvent: ({ event }) => claimLive(event.parentMessageId ?? event.toolCallId),
|
|
352
409
|
onRunFinalized: () => {
|
|
@@ -354,8 +411,36 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
354
411
|
// The agent's own field, which @ag-ui/client has set on RUN_FINISHED by now (pinned
|
|
355
412
|
// in agui-interrupts.spec) -- live runs and connects alike; a RUN_ERROR leaves it.
|
|
356
413
|
setInterruptRequests(requestsOf(openInterruptsOf(copilotAgent), copilotAgent.messages));
|
|
414
|
+
// A stream that closed with no terminal event for the turn it showed (the 400 s
|
|
415
|
+
// self-close, a restart, a drop): watch the turn again.
|
|
416
|
+
const streamStarted = streamStartedRef.current;
|
|
417
|
+
streamStartedRef.current = false;
|
|
418
|
+
const streamRunId = streamRunIdOf({
|
|
419
|
+
ownRunInFlight: ownRunInFlightRef.current,
|
|
420
|
+
lastStartedRunId: streamRunIdRef.current,
|
|
421
|
+
live: liveTurnRef.current,
|
|
422
|
+
});
|
|
423
|
+
streamRunIdRef.current = null;
|
|
424
|
+
if (shouldReconnectAfterStream({
|
|
425
|
+
liveEnabled: liveTurnRef.current?.enabled === true,
|
|
426
|
+
streamRunId,
|
|
427
|
+
sawTerminalFor: terminalRunIdsRef.current,
|
|
428
|
+
streamStarted,
|
|
429
|
+
})) {
|
|
430
|
+
const threadId = threadOfStream();
|
|
431
|
+
setTimeout(() => {
|
|
432
|
+
if (threadOfStream() === threadId && !copilotAgent.isRunning) {
|
|
433
|
+
followRunningTurn(streamRunId);
|
|
434
|
+
}
|
|
435
|
+
}, 0);
|
|
436
|
+
}
|
|
357
437
|
},
|
|
358
438
|
onCustomEvent: ({ event }) => {
|
|
439
|
+
const live = readLiveTurnEvent(event);
|
|
440
|
+
if (live) {
|
|
441
|
+
liveTurnRef.current = live;
|
|
442
|
+
setLiveTurn(live);
|
|
443
|
+
}
|
|
359
444
|
const runIds = readMessageRunIdsEvent(event);
|
|
360
445
|
if (runIds) {
|
|
361
446
|
setRunIdByMessageId((previous) => mergeEntries(previous, runIds));
|
|
@@ -370,14 +455,15 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
370
455
|
unsubscribe();
|
|
371
456
|
liveRunRef.current = null;
|
|
372
457
|
};
|
|
373
|
-
}, [copilotAgent]);
|
|
458
|
+
}, [copilotAgent, followRunningTurn]);
|
|
374
459
|
// Mirror `currentThreadId` onto the agent instance — the `<CopilotKit>`
|
|
375
460
|
// prop only feeds React context, but `agent.threadId` is what hits the
|
|
376
461
|
// wire and the LangGraph checkpoint. `<CopilotChat>` does this for you;
|
|
377
462
|
// we use the headless hook so we do it ourselves. Layout-effect timing
|
|
378
463
|
// is required to beat the library's connect-on-mount/on-swap effect.
|
|
379
|
-
// A swap
|
|
380
|
-
//
|
|
464
|
+
// A swap never stops a run: the connect effect's cleanup only detaches this client's
|
|
465
|
+
// reader. With the live turn stream on, the turn keeps going on the server and a later
|
|
466
|
+
// connect watches it again; with it off, the run still completes server-side as before.
|
|
381
467
|
useIsomorphicLayoutEffect(() => {
|
|
382
468
|
if (copilotAgent && currentThreadId) {
|
|
383
469
|
copilotAgent.threadId = currentThreadId;
|
|
@@ -395,6 +481,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
395
481
|
const isRunningElsewhere = runningElsewhereRunId !== null && !ownRunInFlight && !ownRunIdsRef.current.has(runningElsewhereRunId);
|
|
396
482
|
const isRunningElsewhereRef = useRef(isRunningElsewhere);
|
|
397
483
|
isRunningElsewhereRef.current = isRunningElsewhere;
|
|
484
|
+
ownRunInFlightRef.current = ownRunInFlight;
|
|
398
485
|
const conversationKey = currentThreadId ?? "";
|
|
399
486
|
const prevConversationKeyRef = useRef(conversationKey);
|
|
400
487
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -410,6 +497,11 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
410
497
|
setComposerMessageMeta(null);
|
|
411
498
|
resetInterrupts();
|
|
412
499
|
setOwnRunInFlight(false);
|
|
500
|
+
setLiveTurn(null);
|
|
501
|
+
streamRunIdRef.current = null;
|
|
502
|
+
terminalRunIdsRef.current.clear();
|
|
503
|
+
lastConnectedForRunIdRef.current = null;
|
|
504
|
+
connectInFlightRef.current = false;
|
|
413
505
|
const detachable = copilotAgent;
|
|
414
506
|
const drained = detachable?.detachActiveRun?.();
|
|
415
507
|
if (drained) {
|
|
@@ -426,6 +518,20 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
426
518
|
const canSendMessage = Boolean(agentChatBlock && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning && !isRunningElsewhere);
|
|
427
519
|
const isFetchingMessages = !isConfigLoaded || (!isAgentReady && copilotMessages.length === 0);
|
|
428
520
|
const isThinking = (isAgentRunning || isRunningElsewhere) && !isFetchingMessages && isAgentReady;
|
|
521
|
+
// Doorbell: the row names a run this tab neither started nor follows, so connect and
|
|
522
|
+
// stream it live (replaces thinking until the end, then a reload).
|
|
523
|
+
useEffect(() => {
|
|
524
|
+
if (shouldConnectForRun({
|
|
525
|
+
liveEnabled: liveTurn?.enabled === true,
|
|
526
|
+
runningRunId: runningElsewhereRunId,
|
|
527
|
+
followingRunId: liveTurn?.runId ?? null,
|
|
528
|
+
lastConnectedForRunId: lastConnectedForRunIdRef.current,
|
|
529
|
+
ownRunIds: ownRunIdsRef.current,
|
|
530
|
+
connectInFlight: isAgentRunning || ownRunInFlight || connectInFlightRef.current,
|
|
531
|
+
})) {
|
|
532
|
+
followRunningTurn(runningElsewhereRunId);
|
|
533
|
+
}
|
|
534
|
+
}, [liveTurn, runningElsewhereRunId, isAgentRunning, ownRunInFlight, followRunningTurn]);
|
|
429
535
|
const runAgentWithRetry = useCallback(async (agentToRun, messageCountAtRunStart, forwardedProps, resume, withoutSend = false) => {
|
|
430
536
|
// A run elsewhere is on this conversation's checkpoint too: ours would race it.
|
|
431
537
|
if (agentToRun.isRunning || isRunningElsewhereRef.current) {
|
|
@@ -436,14 +542,21 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
436
542
|
setOwnRunsWithoutSend((count) => count + 1);
|
|
437
543
|
}
|
|
438
544
|
let finished = false;
|
|
545
|
+
threadBusyRefusedRef.current = false;
|
|
439
546
|
const runAgentAndThrowError = async () => {
|
|
440
547
|
let runEventError;
|
|
441
548
|
const { unsubscribe } = agentToRun.subscribe({
|
|
442
549
|
onRunFailed: (event) => {
|
|
443
550
|
runEventError = event;
|
|
444
551
|
},
|
|
445
|
-
onRunErrorEvent: (
|
|
446
|
-
|
|
552
|
+
onRunErrorEvent: (params) => {
|
|
553
|
+
// A refused send on a busy thread is not a failure: the sender puts its text back.
|
|
554
|
+
const { event } = params;
|
|
555
|
+
if (isThreadBusyError(event)) {
|
|
556
|
+
threadBusyRefusedRef.current = true;
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
runEventError = params;
|
|
447
560
|
},
|
|
448
561
|
onRunFinishedEvent: () => {
|
|
449
562
|
finished = true;
|
|
@@ -499,14 +612,15 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
499
612
|
reconnected = true;
|
|
500
613
|
}
|
|
501
614
|
}
|
|
502
|
-
// A reconnected run did not take effect as sent: callers must not record it as done.
|
|
503
|
-
return !reconnected;
|
|
615
|
+
// A reconnected or refused run did not take effect as sent: callers must not record it as done.
|
|
616
|
+
return !reconnected && !threadBusyRefusedRef.current;
|
|
504
617
|
}, [copilotkit, currentThreadId, resetInterrupts, readChatContext]);
|
|
505
618
|
const sendMessage = useCallback(async (message) => {
|
|
506
619
|
if (!canSendMessage || !copilotAgent || copilotAgent.isRunning || isRunningElsewhereRef.current) {
|
|
507
620
|
return;
|
|
508
621
|
}
|
|
509
|
-
|
|
622
|
+
const sentMessageId = generateId();
|
|
623
|
+
copilotAgent.addMessage({ ...message, id: sentMessageId, role: "user" });
|
|
510
624
|
if (onUserMessageSent) {
|
|
511
625
|
onUserMessageSent(userTextOfContent(message.content), message);
|
|
512
626
|
}
|
|
@@ -520,6 +634,13 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
520
634
|
const interrupts = openInterruptsOf(copilotAgent);
|
|
521
635
|
if (interrupts.length === 0) {
|
|
522
636
|
await runAgentWithRetry(copilotAgent, copilotAgent.messages.length);
|
|
637
|
+
if (threadBusyRefusedRef.current) {
|
|
638
|
+
// Refused: a turn is already running on the thread. Give the text back and watch it.
|
|
639
|
+
threadBusyRefusedRef.current = false;
|
|
640
|
+
copilotAgent.setMessages?.(copilotAgent.messages.filter((existing) => existing.id !== sentMessageId));
|
|
641
|
+
setPrompt(userTextOfContent(message.content));
|
|
642
|
+
followRunningTurn(null);
|
|
643
|
+
}
|
|
523
644
|
return;
|
|
524
645
|
}
|
|
525
646
|
const requests = requestsOf(interrupts, copilotAgent.messages);
|
|
@@ -542,7 +663,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
542
663
|
}
|
|
543
664
|
setInterruptAnswers(EMPTY_ANSWERS);
|
|
544
665
|
}
|
|
545
|
-
}, [canSendMessage, copilotAgent, runAgentWithRetry, onUserMessageSent]);
|
|
666
|
+
}, [canSendMessage, copilotAgent, runAgentWithRetry, onUserMessageSent, followRunningTurn]);
|
|
546
667
|
const reloadMessages = useCallback(async () => {
|
|
547
668
|
if (!copilotAgent || copilotAgent.isRunning) {
|
|
548
669
|
return;
|
|
@@ -665,10 +786,39 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
665
786
|
throw new Error("answerInterrupt: a run was already in flight; answer not sent");
|
|
666
787
|
}
|
|
667
788
|
}, [copilotAgent, runAgentWithRetry, submitInterruptAnswers]);
|
|
789
|
+
const stoppable = canStop({ ownRunInFlight, following: liveTurn });
|
|
790
|
+
// The run body's thread fields, from the same values a run sends, to the runtime URL with
|
|
791
|
+
// the headers CopilotKit uses. Fire and forget: a failed stop request is only logged.
|
|
792
|
+
const postStopEnvelope = useCallback(() => {
|
|
793
|
+
const runId = stopRunIdOf({ ownRunInFlight, lastStartedRunId: streamRunIdRef.current, live: liveTurn });
|
|
794
|
+
if (!copilotAgent?.threadId || !runId || !copilotkit.runtimeUrl) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const envelope = buildStopEnvelope({
|
|
798
|
+
threadId: copilotAgent.threadId,
|
|
799
|
+
runId,
|
|
800
|
+
forwardedProps: { ...copilotkit.properties, chatContext: readChatContext() },
|
|
801
|
+
});
|
|
802
|
+
fetch(copilotkit.runtimeUrl, {
|
|
803
|
+
method: "POST",
|
|
804
|
+
headers: { ...copilotkit.headers, "Content-Type": "application/json" },
|
|
805
|
+
body: JSON.stringify(envelope),
|
|
806
|
+
...(copilotkit.credentials ? { credentials: copilotkit.credentials } : {}),
|
|
807
|
+
}).catch((error) => {
|
|
808
|
+
console.warn("agent stop request failed", error);
|
|
809
|
+
});
|
|
810
|
+
}, [copilotAgent, copilotkit, liveTurn, ownRunInFlight, readChatContext]);
|
|
668
811
|
const stopGeneration = useCallback(() => {
|
|
812
|
+
if (liveTurn?.enabled) {
|
|
813
|
+
if (!stoppable) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
// Closing the stream no longer stops a turn: ask the server, before the reader goes.
|
|
817
|
+
postStopEnvelope();
|
|
818
|
+
}
|
|
669
819
|
copilotStopGeneration();
|
|
670
820
|
copilotAgent?.abortRun?.();
|
|
671
|
-
}, [copilotAgent, copilotStopGeneration]);
|
|
821
|
+
}, [copilotAgent, copilotStopGeneration, liveTurn, stoppable, postStopEnvelope]);
|
|
672
822
|
const initKeyRef = useRef(null);
|
|
673
823
|
// Guard against re-firing the initial prompt across re-renders of the
|
|
674
824
|
// post-connect effect. Reset whenever the init key changes (thread
|
|
@@ -925,6 +1075,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
925
1075
|
messageGroups,
|
|
926
1076
|
renderToolCall,
|
|
927
1077
|
messageById,
|
|
1078
|
+
scheduledTriggerByMessageId,
|
|
928
1079
|
runIdByMessageId,
|
|
929
1080
|
runStartedAt,
|
|
930
1081
|
toolMessages,
|
|
@@ -939,6 +1090,7 @@ const ChatInitializer = ({ agentBlock, agentChatBlock, token, appId, components,
|
|
|
939
1090
|
approvalCardsFor,
|
|
940
1091
|
toolTitleFor,
|
|
941
1092
|
stopGeneration,
|
|
1093
|
+
canStop: stoppable,
|
|
942
1094
|
addAttachments,
|
|
943
1095
|
removeAttachment,
|
|
944
1096
|
clearAttachments,
|
|
@@ -1377,26 +1529,28 @@ export function AgentChatInput({ asChild = false, render, onChange, onKeyDown, o
|
|
|
1377
1529
|
: onDrop, "data-slot": "agent-chat-input", ...props }));
|
|
1378
1530
|
}
|
|
1379
1531
|
export function AgentChatSendTrigger({ asChild = false, render, onClick, disabled, ...props }) {
|
|
1380
|
-
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs, composerMessageMeta,
|
|
1381
|
-
|
|
1382
|
-
|
|
1532
|
+
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs, composerMessageMeta, stopGeneration, canStop, } = useAgentChat();
|
|
1533
|
+
// While a reply Stop applies to runs, the send button stops it instead.
|
|
1534
|
+
const stops = isThinking && canStop === true;
|
|
1383
1535
|
const isDisabled = Boolean(disabled ||
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1536
|
+
(!stops &&
|
|
1537
|
+
(isThinking ||
|
|
1538
|
+
!isAgentReady ||
|
|
1539
|
+
isFetchingMessages ||
|
|
1540
|
+
!hasSendableInput(prompt, attachments, composerMessageMeta))));
|
|
1388
1541
|
return (_jsx(ComposeRender, { render: render, asChild: asChild, defaultTag: "button", onClick: (e) => {
|
|
1389
1542
|
if (isDisabled) {
|
|
1390
1543
|
e.preventDefault();
|
|
1391
1544
|
return;
|
|
1392
1545
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1546
|
+
if (stops) {
|
|
1547
|
+
stopGeneration();
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
sendFromInputs();
|
|
1551
|
+
}
|
|
1398
1552
|
onClick?.(e);
|
|
1399
|
-
}, disabled: isDisabled, "data-slot": "agent-chat-send-trigger", ...props }));
|
|
1553
|
+
}, disabled: isDisabled, "data-slot": "agent-chat-send-trigger", "data-state": stops ? "stop" : "send", ...props }));
|
|
1400
1554
|
}
|
|
1401
1555
|
export function AgentChatAttachmentTrigger({ asChild = false, render, onClick, disabled, accept, multiple = true, ...props }) {
|
|
1402
1556
|
const { isThinking, isAgentReady, isFetchingMessages, addAttachments, agentChat } = useAgentChat();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const THREAD_STATUSES: readonly ["idle", "interrupted", "waiting_on_agent", "busy", "error"];
|
|
2
2
|
export type AgentThreadStatus = (typeof THREAD_STATUSES)[number];
|
|
3
|
-
declare const THREAD_SOURCES: readonly ["direct_chat", "send_message", "feedback", "self_config_resume", "unknown"];
|
|
3
|
+
declare const THREAD_SOURCES: readonly ["direct_chat", "send_message", "feedback", "self_config_resume", "scheduled_reminder", "unknown"];
|
|
4
4
|
export type AgentThreadSource = (typeof THREAD_SOURCES)[number] | "agent_call";
|
|
5
5
|
export interface AgentThreadItem {
|
|
6
6
|
agentId: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,eAAe,uEAAwE,CAAC;AAC9F,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,QAAA,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"agent-thread-live-update.d.ts","sourceRoot":"","sources":["../../src/hooks/agent-thread-live-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// Wire types and pure merge logic for the agent-threads index; dependency-free for tests.
|
|
2
2
|
const THREAD_STATUSES = ["idle", "interrupted", "waiting_on_agent", "busy", "error"];
|
|
3
|
-
const THREAD_SOURCES = [
|
|
3
|
+
const THREAD_SOURCES = [
|
|
4
|
+
"direct_chat",
|
|
5
|
+
"send_message",
|
|
6
|
+
"feedback",
|
|
7
|
+
"self_config_resume",
|
|
8
|
+
// A thread a fired reminder or schedule started (wf-actions-py scheduled_reminder_runner.py).
|
|
9
|
+
"scheduled_reminder",
|
|
10
|
+
"unknown",
|
|
11
|
+
];
|
|
4
12
|
/** A thread's identity within its agent: the thread id alone. */
|
|
5
13
|
export const agentThreadKey = (thread) => thread.chatThreadId;
|
|
6
14
|
export const isAgentThreadItem = (value) => {
|
|
@@ -134,6 +142,7 @@ export const AGENT_THREAD_GROUPS = ["chats", "workflows", "feedback"];
|
|
|
134
142
|
const GROUP_OF_SOURCE = {
|
|
135
143
|
direct_chat: "chats",
|
|
136
144
|
self_config_resume: "chats",
|
|
145
|
+
scheduled_reminder: "chats",
|
|
137
146
|
unknown: "chats",
|
|
138
147
|
send_message: "workflows",
|
|
139
148
|
feedback: "feedback",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chat's side of the live turn stream.
|
|
3
|
+
*
|
|
4
|
+
* With the server flag on, every `agent/connect` opens with one CUSTOM event,
|
|
5
|
+
* `blocks.live_turn`, and then streams the thread's active turn live in the same response.
|
|
6
|
+
* With the flag off the server sends none of this, so `liveEnabled` stays false and every
|
|
7
|
+
* decision below answers "do nothing".
|
|
8
|
+
*/
|
|
9
|
+
export declare const LIVE_TURN_EVENT_NAME = "blocks.live_turn";
|
|
10
|
+
/** The RUN_ERROR code of a send refused because a turn is already running on the thread. */
|
|
11
|
+
export declare const THREAD_BUSY_ERROR_CODE = "thread_busy";
|
|
12
|
+
export type LiveTurnInfo = {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
runId?: string;
|
|
15
|
+
startedBy?: "chat" | "server";
|
|
16
|
+
};
|
|
17
|
+
/** The payload of the connect's live-turn CUSTOM event, or null for any other event. */
|
|
18
|
+
export declare const readLiveTurnEvent: (event: {
|
|
19
|
+
type: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
value?: unknown;
|
|
22
|
+
}) => LiveTurnInfo | null;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the thread row's running run (the doorbell) calls for a connect to watch it:
|
|
25
|
+
* one this tab neither follows nor started, that no connect has answered for yet.
|
|
26
|
+
*/
|
|
27
|
+
export declare const shouldConnectForRun: (args: {
|
|
28
|
+
liveEnabled: boolean;
|
|
29
|
+
runningRunId: string | null;
|
|
30
|
+
followingRunId: string | null;
|
|
31
|
+
lastConnectedForRunId: string | null;
|
|
32
|
+
ownRunIds: ReadonlySet<string>;
|
|
33
|
+
connectInFlight: boolean;
|
|
34
|
+
}) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Whether a run or connect stream that just ended should be followed by one more connect: it
|
|
37
|
+
* showed a running turn (this tab's send, or one the server named) and ended with no terminal
|
|
38
|
+
* event for it -- the 400 s self-close, a pod restart, a dropped network. A connect for a turn
|
|
39
|
+
* that has finished meanwhile names none, so it is not followed again. A stream that never
|
|
40
|
+
* started (the server unreachable) is not retried, or an outage would loop.
|
|
41
|
+
*/
|
|
42
|
+
export declare const shouldReconnectAfterStream: (args: {
|
|
43
|
+
liveEnabled: boolean;
|
|
44
|
+
streamRunId: string | null;
|
|
45
|
+
sawTerminalFor: ReadonlySet<string>;
|
|
46
|
+
streamStarted: boolean;
|
|
47
|
+
}) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Stop is for this tab's own run, or a followed turn a chat started (never workflow, reminder,
|
|
50
|
+
* ...), and only with the live turn stream on: only then does the server stop the turn itself.
|
|
51
|
+
*/
|
|
52
|
+
export declare const canStop: (args: {
|
|
53
|
+
ownRunInFlight: boolean;
|
|
54
|
+
following: LiveTurnInfo | null;
|
|
55
|
+
}) => boolean;
|
|
56
|
+
export declare const isThreadBusyError: (event: {
|
|
57
|
+
type: string;
|
|
58
|
+
code?: string;
|
|
59
|
+
}) => boolean;
|
|
60
|
+
/** The `agent/stop` request: the run body's thread fields only, none of its content. */
|
|
61
|
+
export declare const buildStopEnvelope: (runBody: Record<string, unknown>) => {
|
|
62
|
+
method: "agent/stop";
|
|
63
|
+
body: Record<string, unknown>;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The run a stream that just ended is judged by. A tab's own send streams under its own run
|
|
67
|
+
* id; a connect body carries its own id for the history replay (RUN_STARTED/RUN_FINISHED),
|
|
68
|
+
* followed by the live turn's events, so a followed stream is judged by the live turn's id.
|
|
69
|
+
*/
|
|
70
|
+
export declare const streamRunIdOf: (args: {
|
|
71
|
+
ownRunInFlight: boolean;
|
|
72
|
+
lastStartedRunId: string | null;
|
|
73
|
+
live: LiveTurnInfo | null;
|
|
74
|
+
}) => string | null;
|
|
75
|
+
/** The run a stop is for: the tab's own run, else the followed turn (never a connect's id). */
|
|
76
|
+
export declare const stopRunIdOf: (args: {
|
|
77
|
+
ownRunInFlight: boolean;
|
|
78
|
+
lastStartedRunId: string | null;
|
|
79
|
+
live: LiveTurnInfo | null;
|
|
80
|
+
}) => string | null;
|
|
81
|
+
/** After a terminal event for `runId`: a followed turn that just ended is no longer followed. */
|
|
82
|
+
export declare const liveAfterTerminal: (live: LiveTurnInfo | null, runId: string | null) => LiveTurnInfo | null;
|
|
83
|
+
//# sourceMappingURL=live-turn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-turn.d.ts","sourceRoot":"","sources":["../../src/utils/live-turn.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AACvD,4FAA4F;AAC5F,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,CAAC;AAK/F,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,GAAI,OAAO;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAAG,YAAY,GAAG,IAU1G,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,eAAe,EAAE,OAAO,CAAC;CAC1B,KAAG,OAMoC,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM;IAC/C,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,aAAa,EAAE,OAAO,CAAC;CACxB,KAAG,OAC+G,CAAC;AAEpH;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE,KAAG,OACM,CAAC;AAEnG,eAAO,MAAM,iBAAiB,GAAI,OAAO;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,OACN,CAAC;AAEtE,wFAAwF;AACxF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC/B;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAWtD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CAC3B,KAAG,MAAM,GAAG,IAAkF,CAAC;AAEhG,+FAA+F;AAC/F,eAAO,MAAM,WAAW,GAAI,MAAM;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CAC3B,KAAG,MAAM,GAAG,IAC2F,CAAC;AAEzG,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,GAAI,MAAM,YAAY,GAAG,IAAI,EAAE,OAAO,MAAM,GAAG,IAAI,KAAG,YAAY,GAAG,IAChB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chat's side of the live turn stream.
|
|
3
|
+
*
|
|
4
|
+
* With the server flag on, every `agent/connect` opens with one CUSTOM event,
|
|
5
|
+
* `blocks.live_turn`, and then streams the thread's active turn live in the same response.
|
|
6
|
+
* With the flag off the server sends none of this, so `liveEnabled` stays false and every
|
|
7
|
+
* decision below answers "do nothing".
|
|
8
|
+
*/
|
|
9
|
+
export const LIVE_TURN_EVENT_NAME = "blocks.live_turn";
|
|
10
|
+
/** The RUN_ERROR code of a send refused because a turn is already running on the thread. */
|
|
11
|
+
export const THREAD_BUSY_ERROR_CODE = "thread_busy";
|
|
12
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
/** The payload of the connect's live-turn CUSTOM event, or null for any other event. */
|
|
14
|
+
export const readLiveTurnEvent = (event) => {
|
|
15
|
+
if (event.type !== "CUSTOM" || event.name !== LIVE_TURN_EVENT_NAME || !isRecord(event.value)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const { enabled, runId, startedBy } = event.value;
|
|
19
|
+
return {
|
|
20
|
+
enabled: enabled === true,
|
|
21
|
+
...(typeof runId === "string" && runId !== "" ? { runId } : {}),
|
|
22
|
+
...(startedBy === "chat" || startedBy === "server" ? { startedBy } : {}),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Whether the thread row's running run (the doorbell) calls for a connect to watch it:
|
|
27
|
+
* one this tab neither follows nor started, that no connect has answered for yet.
|
|
28
|
+
*/
|
|
29
|
+
export const shouldConnectForRun = (args) => args.liveEnabled &&
|
|
30
|
+
args.runningRunId !== null &&
|
|
31
|
+
!args.connectInFlight &&
|
|
32
|
+
args.runningRunId !== args.followingRunId &&
|
|
33
|
+
args.runningRunId !== args.lastConnectedForRunId &&
|
|
34
|
+
!args.ownRunIds.has(args.runningRunId);
|
|
35
|
+
/**
|
|
36
|
+
* Whether a run or connect stream that just ended should be followed by one more connect: it
|
|
37
|
+
* showed a running turn (this tab's send, or one the server named) and ended with no terminal
|
|
38
|
+
* event for it -- the 400 s self-close, a pod restart, a dropped network. A connect for a turn
|
|
39
|
+
* that has finished meanwhile names none, so it is not followed again. A stream that never
|
|
40
|
+
* started (the server unreachable) is not retried, or an outage would loop.
|
|
41
|
+
*/
|
|
42
|
+
export const shouldReconnectAfterStream = (args) => args.liveEnabled && args.streamStarted && args.streamRunId !== null && !args.sawTerminalFor.has(args.streamRunId);
|
|
43
|
+
/**
|
|
44
|
+
* Stop is for this tab's own run, or a followed turn a chat started (never workflow, reminder,
|
|
45
|
+
* ...), and only with the live turn stream on: only then does the server stop the turn itself.
|
|
46
|
+
*/
|
|
47
|
+
export const canStop = (args) => args.following?.enabled === true && (args.ownRunInFlight || args.following.startedBy === "chat");
|
|
48
|
+
export const isThreadBusyError = (event) => event.type === "RUN_ERROR" && event.code === THREAD_BUSY_ERROR_CODE;
|
|
49
|
+
/** The `agent/stop` request: the run body's thread fields only, none of its content. */
|
|
50
|
+
export const buildStopEnvelope = (runBody) => ({
|
|
51
|
+
method: "agent/stop",
|
|
52
|
+
body: {
|
|
53
|
+
threadId: runBody.threadId,
|
|
54
|
+
runId: runBody.runId,
|
|
55
|
+
forwardedProps: isRecord(runBody.forwardedProps) ? runBody.forwardedProps : {},
|
|
56
|
+
messages: [],
|
|
57
|
+
tools: [],
|
|
58
|
+
context: [],
|
|
59
|
+
state: {},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* The run a stream that just ended is judged by. A tab's own send streams under its own run
|
|
64
|
+
* id; a connect body carries its own id for the history replay (RUN_STARTED/RUN_FINISHED),
|
|
65
|
+
* followed by the live turn's events, so a followed stream is judged by the live turn's id.
|
|
66
|
+
*/
|
|
67
|
+
export const streamRunIdOf = (args) => (args.ownRunInFlight ? args.lastStartedRunId : (args.live?.runId ?? null));
|
|
68
|
+
/** The run a stop is for: the tab's own run, else the followed turn (never a connect's id). */
|
|
69
|
+
export const stopRunIdOf = (args) => args.ownRunInFlight ? (args.lastStartedRunId ?? args.live?.runId ?? null) : (args.live?.runId ?? null);
|
|
70
|
+
/** After a terminal event for `runId`: a followed turn that just ended is no longer followed. */
|
|
71
|
+
export const liveAfterTerminal = (live, runId) => live && runId !== null && live.runId === runId ? { enabled: live.enabled } : live;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ScheduledTrigger {
|
|
2
|
+
/** "reminder": one the agent set in chat. "schedule": one the app's builder created. */
|
|
3
|
+
kind: "reminder" | "schedule";
|
|
4
|
+
/** Absent on a fire from before the line existed. */
|
|
5
|
+
title?: string;
|
|
6
|
+
recurring?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface MessageLike {
|
|
9
|
+
id?: string;
|
|
10
|
+
role?: string;
|
|
11
|
+
metadata?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** The trigger stamped on a message, or null when it carries none. */
|
|
14
|
+
export declare function scheduledTriggerOf(message: MessageLike): ScheduledTrigger | null;
|
|
15
|
+
/** Each stamped message's trigger, by message id: a message group starting there draws the line. */
|
|
16
|
+
export declare function scheduledTriggersByMessageId(messages: readonly MessageLike[]): ReadonlyMap<string, ScheduledTrigger>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=scheduled-trigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-trigger.d.ts","sourceRoot":"","sources":["../../src/utils/scheduled-trigger.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9B,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,WAAW;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,GAAG,IAAI,CAgBhF;AAED,oGAAoG;AACpG,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CASpH"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The line a chat draws above a turn a fired reminder (or app schedule) started.
|
|
3
|
+
*
|
|
4
|
+
* The run's prompt is hidden, so the agent's reply would read as if the agent spoke on its own.
|
|
5
|
+
* Chat load stamps the fire's details on that reply, `metadata["blocks.scheduledTrigger"]` on the
|
|
6
|
+
* agent's first message of the turn (wf-actions-py `scheduled_trigger.py`); this reads it. Never
|
|
7
|
+
* parse the prompt's text: it is for the model, not data.
|
|
8
|
+
*/
|
|
9
|
+
const SCHEDULED_TRIGGER_METADATA_KEY = "blocks.scheduledTrigger";
|
|
10
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
/** The trigger stamped on a message, or null when it carries none. */
|
|
12
|
+
export function scheduledTriggerOf(message) {
|
|
13
|
+
if (message.role !== "assistant" || !isRecord(message.metadata)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const stamp = message.metadata[SCHEDULED_TRIGGER_METADATA_KEY];
|
|
17
|
+
if (!isRecord(stamp)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const trigger = { kind: stamp.kind === "schedule" ? "schedule" : "reminder" };
|
|
21
|
+
if (typeof stamp.title === "string" && stamp.title) {
|
|
22
|
+
trigger.title = stamp.title;
|
|
23
|
+
}
|
|
24
|
+
if (typeof stamp.recurring === "boolean") {
|
|
25
|
+
trigger.recurring = stamp.recurring;
|
|
26
|
+
}
|
|
27
|
+
return trigger;
|
|
28
|
+
}
|
|
29
|
+
/** Each stamped message's trigger, by message id: a message group starting there draws the line. */
|
|
30
|
+
export function scheduledTriggersByMessageId(messages) {
|
|
31
|
+
const byMessageId = new Map();
|
|
32
|
+
for (const message of messages) {
|
|
33
|
+
const trigger = message.id ? scheduledTriggerOf(message) : null;
|
|
34
|
+
if (trigger && message.id) {
|
|
35
|
+
byMessageId.set(message.id, trigger);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return byMessageId;
|
|
39
|
+
}
|