@agent-native/core 0.188.0 → 0.189.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/corpus/README.md +1 -1
- package/corpus/templates/content/CHANGELOG.md +6 -0
- package/corpus/templates/content/actions/list-document-versions.ts +102 -2
- package/corpus/templates/content/actions/restore-document-version.ts +50 -2
- package/corpus/templates/content/app/components/layout/Layout.tsx +6 -1
- package/corpus/templates/content/changelog/2026-09-25-chat-messages-can-be-reverted-to-their-autosaved-version-inc.md +6 -0
- package/corpus/templates/content/server/lib/document-history.ts +59 -8
- package/corpus/templates/content/server/lib/document-version-context.ts +5 -0
- package/corpus/templates/content/server/plugins/agent-chat.ts +164 -0
- package/corpus/templates/content/vite.config.ts +2 -38
- package/corpus/templates/design/CHANGELOG.md +4 -0
- package/corpus/templates/design/actions/list-design-versions.ts +3 -1
- package/corpus/templates/design/app/components/layout/Layout.tsx +17 -2
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +10 -3
- package/corpus/templates/design/changelog/2026-09-25-chat-messages-can-be-reverted-to-their-autosaved-version-inc.md +6 -0
- package/corpus/templates/design/server/lib/design-versions.ts +136 -20
- package/corpus/templates/design/server/plugins/agent-chat.ts +18 -2
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +36 -11
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +82 -0
- package/corpus/templates/mail/app/pages/InboxPage.tsx +18 -19
- package/corpus/templates/mail/changelog/2026-09-25-keep-inbox-tab-counts-consistent-when-switching-tabs.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +4 -0
- package/corpus/templates/slides/actions/list-deck-versions.ts +62 -10
- package/corpus/templates/slides/app/components/layout/Layout.tsx +10 -2
- package/corpus/templates/slides/changelog/2026-09-25-chat-messages-can-be-reverted-to-their-autosaved-version-inc.md +6 -0
- package/corpus/templates/slides/server/lib/deck-versions.ts +36 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +26 -2
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/index.js +1 -2
- package/dist/client/AssistantChat.js +112 -35
- package/dist/client/chat/assistant-chat-history-version.d.ts +1 -0
- package/dist/client/chat/message-components.d.ts +8 -1
- package/dist/client/chat/message-components.js +52 -11
- package/dist/client/resources/mcp-integration-catalog.js +1 -1
- package/dist/client/resources/mcp-integration-logos.js +29 -30
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/deploy/build.d.ts +1 -1
- package/dist/deploy/build.js +9 -348
- package/dist/deploy/workspace-deploy.d.ts +2 -2
- package/dist/deploy/workspace-deploy.js +9 -130
- package/dist/localization/core-messages/ar-SA.js +2 -0
- package/dist/localization/core-messages/de-DE.js +2 -0
- package/dist/localization/core-messages/en-US.d.ts +2 -0
- package/dist/localization/core-messages/en-US.js +2 -0
- package/dist/localization/core-messages/es-ES.js +2 -0
- package/dist/localization/core-messages/fr-FR.js +2 -0
- package/dist/localization/core-messages/hi-IN.js +2 -0
- package/dist/localization/core-messages/ja-JP.js +2 -0
- package/dist/localization/core-messages/ko-KR.js +2 -0
- package/dist/localization/core-messages/pt-BR.js +2 -0
- package/dist/localization/core-messages/zh-CN.js +2 -0
- package/dist/localization/core-messages/zh-TW.js +2 -0
- package/dist/localization/core-messages.d.ts +2 -0
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/plugin-options.d.ts +2 -0
- package/dist/server/agent-chat-plugin.d.ts +1 -0
- package/dist/server/agent-chat-plugin.js +26 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/onboarding-html.js +28 -5
- package/dist/vite/client.d.ts +1 -1
- package/docs/content/a2a-protocol.mdx +2 -2
- package/docs/content/cloudflare.mdx +2 -10
- package/docs/content/deploy-an-app.mdx +0 -1
- package/docs/content/deployment.mdx +14 -9
- package/docs/content/environment-variables.mdx +1 -1
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/docs/content/netlify.mdx +1 -1
- package/docs/content/real-time-collaboration.mdx +6 -6
- package/docs/content/workspace-deployment.mdx +4 -15
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
2
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
|
-
import { and, desc, eq } from "drizzle-orm";
|
|
3
|
+
import { and, asc, desc, eq, like } from "drizzle-orm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -50,13 +50,18 @@ export default defineAction({
|
|
|
50
50
|
schema: z.object({
|
|
51
51
|
deckId: z.string().describe("Deck ID"),
|
|
52
52
|
limit: z.coerce.number().int().min(1).max(100).default(50),
|
|
53
|
+
threadId: z.string().min(1).optional(),
|
|
53
54
|
}),
|
|
54
55
|
http: { method: "GET" },
|
|
55
|
-
run: async ({ deckId, limit }) => {
|
|
56
|
+
run: async ({ deckId, limit, threadId }) => {
|
|
56
57
|
const access = await assertAccess("deck", deckId, "viewer");
|
|
57
58
|
const ownerEmail = access.resource.ownerEmail as string;
|
|
58
59
|
const db = getDb();
|
|
59
60
|
|
|
61
|
+
const where = and(
|
|
62
|
+
eq(schema.deckVersions.deckId, deckId),
|
|
63
|
+
eq(schema.deckVersions.ownerEmail, ownerEmail),
|
|
64
|
+
);
|
|
60
65
|
const versions = await db
|
|
61
66
|
.select({
|
|
62
67
|
id: schema.deckVersions.id,
|
|
@@ -68,19 +73,66 @@ export default defineAction({
|
|
|
68
73
|
createdAt: schema.deckVersions.createdAt,
|
|
69
74
|
})
|
|
70
75
|
.from(schema.deckVersions)
|
|
71
|
-
.where(
|
|
72
|
-
and(
|
|
73
|
-
eq(schema.deckVersions.deckId, deckId),
|
|
74
|
-
eq(schema.deckVersions.ownerEmail, ownerEmail),
|
|
75
|
-
),
|
|
76
|
-
)
|
|
76
|
+
.where(where)
|
|
77
77
|
.orderBy(desc(schema.deckVersions.createdAt))
|
|
78
78
|
.limit(limit);
|
|
79
|
+
const beginningVersions = await db
|
|
80
|
+
.select({
|
|
81
|
+
id: schema.deckVersions.id,
|
|
82
|
+
deckId: schema.deckVersions.deckId,
|
|
83
|
+
title: schema.deckVersions.title,
|
|
84
|
+
data: schema.deckVersions.data,
|
|
85
|
+
changeLabel: schema.deckVersions.changeLabel,
|
|
86
|
+
chatContext: schema.deckVersions.chatContext,
|
|
87
|
+
createdAt: schema.deckVersions.createdAt,
|
|
88
|
+
})
|
|
89
|
+
.from(schema.deckVersions)
|
|
90
|
+
.where(
|
|
91
|
+
threadId
|
|
92
|
+
? and(
|
|
93
|
+
where,
|
|
94
|
+
eq(schema.deckVersions.changeGroup, `start:thread:${threadId}`),
|
|
95
|
+
)
|
|
96
|
+
: and(
|
|
97
|
+
where,
|
|
98
|
+
like(schema.deckVersions.chatContext, '%"phase":"start"%'),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
.orderBy(
|
|
102
|
+
threadId
|
|
103
|
+
? asc(schema.deckVersions.createdAt)
|
|
104
|
+
: desc(schema.deckVersions.createdAt),
|
|
105
|
+
)
|
|
106
|
+
.limit(threadId ? 1 : limit);
|
|
107
|
+
const versionsById = new Map(
|
|
108
|
+
[...beginningVersions, ...versions].map((version) => [
|
|
109
|
+
version.id,
|
|
110
|
+
version,
|
|
111
|
+
]),
|
|
112
|
+
);
|
|
113
|
+
const allVersions = [...versionsById.values()];
|
|
114
|
+
allVersions.sort(
|
|
115
|
+
(left, right) =>
|
|
116
|
+
new Date(right.createdAt).getTime() -
|
|
117
|
+
new Date(left.createdAt).getTime(),
|
|
118
|
+
);
|
|
119
|
+
const limitedVersions = allVersions.slice(0, limit);
|
|
120
|
+
const activeStart = threadId
|
|
121
|
+
? allVersions.find((version) => beginningVersions.includes(version))
|
|
122
|
+
: undefined;
|
|
123
|
+
if (activeStart && !limitedVersions.includes(activeStart)) {
|
|
124
|
+
limitedVersions[limitedVersions.length - 1] = activeStart;
|
|
125
|
+
limitedVersions.sort(
|
|
126
|
+
(left, right) =>
|
|
127
|
+
new Date(right.createdAt).getTime() -
|
|
128
|
+
new Date(left.createdAt).getTime(),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
79
131
|
|
|
80
132
|
return {
|
|
81
133
|
deckId,
|
|
82
|
-
count:
|
|
83
|
-
versions:
|
|
134
|
+
count: limitedVersions.length,
|
|
135
|
+
versions: limitedVersions.map((version) => {
|
|
84
136
|
let chatContext;
|
|
85
137
|
try {
|
|
86
138
|
chatContext = parseDeckVersionChatContext(version.chatContext);
|
|
@@ -21,6 +21,7 @@ import { IconMenu2 } from "@tabler/icons-react";
|
|
|
21
21
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
22
22
|
import { useLocation, useNavigate } from "react-router";
|
|
23
23
|
|
|
24
|
+
import { useDecks } from "@/context/DeckContext";
|
|
24
25
|
import { useSidebarCollapsed } from "@/hooks/use-sidebar-collapsed";
|
|
25
26
|
import {
|
|
26
27
|
buildSlidesAgentContext,
|
|
@@ -67,6 +68,7 @@ export function Layout({ children }: LayoutProps) {
|
|
|
67
68
|
const location = useLocation();
|
|
68
69
|
const navigate = useNavigate();
|
|
69
70
|
const t = useT();
|
|
71
|
+
const { flushDeckSave } = useDecks();
|
|
70
72
|
const creativeContextEnabled = useCreativeContextLab();
|
|
71
73
|
const isChatRoute =
|
|
72
74
|
location.pathname === "/chat" || location.pathname.startsWith("/chat/");
|
|
@@ -155,9 +157,14 @@ export function Layout({ children }: LayoutProps) {
|
|
|
155
157
|
if (!deckScope) return undefined;
|
|
156
158
|
const deckId = deckScope.id;
|
|
157
159
|
return {
|
|
160
|
+
beforeStart: () => flushDeckSave(deckId),
|
|
158
161
|
list: {
|
|
159
162
|
action: "list-deck-versions",
|
|
160
|
-
args:
|
|
163
|
+
args: (threadId) => ({
|
|
164
|
+
deckId,
|
|
165
|
+
limit: 100,
|
|
166
|
+
...(threadId ? { threadId } : {}),
|
|
167
|
+
}),
|
|
161
168
|
getVersions: (result: unknown) => {
|
|
162
169
|
const versions =
|
|
163
170
|
result && typeof result === "object"
|
|
@@ -174,9 +181,10 @@ export function Layout({ children }: LayoutProps) {
|
|
|
174
181
|
deckId,
|
|
175
182
|
versionId: version.id,
|
|
176
183
|
}),
|
|
184
|
+
beforeRestore: () => flushDeckSave(deckId),
|
|
177
185
|
},
|
|
178
186
|
};
|
|
179
|
-
}, [deckScope]);
|
|
187
|
+
}, [deckScope, flushDeckSave]);
|
|
180
188
|
|
|
181
189
|
useAgentChatHomeHandoffLinks({
|
|
182
190
|
storageKey: "slides",
|
|
@@ -13,12 +13,14 @@ export interface DeckVersionChatContext {
|
|
|
13
13
|
threadId?: string;
|
|
14
14
|
runId?: string;
|
|
15
15
|
turnId?: string;
|
|
16
|
+
phase?: "start" | "end";
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function contextFromFields(value: {
|
|
19
20
|
threadId?: unknown;
|
|
20
21
|
runId?: unknown;
|
|
21
22
|
turnId?: unknown;
|
|
23
|
+
phase?: unknown;
|
|
22
24
|
}): DeckVersionChatContext | undefined {
|
|
23
25
|
const context: DeckVersionChatContext = {};
|
|
24
26
|
for (const key of ["threadId", "runId", "turnId"] as const) {
|
|
@@ -26,6 +28,9 @@ function contextFromFields(value: {
|
|
|
26
28
|
context[key] = value[key];
|
|
27
29
|
}
|
|
28
30
|
}
|
|
31
|
+
if (value.phase === "start" || value.phase === "end") {
|
|
32
|
+
context.phase = value.phase;
|
|
33
|
+
}
|
|
29
34
|
return Object.keys(context).length > 0 ? context : undefined;
|
|
30
35
|
}
|
|
31
36
|
|
|
@@ -139,6 +144,7 @@ export async function createDeckVersionSnapshot(
|
|
|
139
144
|
.limit(1);
|
|
140
145
|
|
|
141
146
|
if (
|
|
147
|
+
options.chatContext?.phase !== "start" &&
|
|
142
148
|
latestVersion &&
|
|
143
149
|
latestVersion.title === source.title &&
|
|
144
150
|
deckContentSignature(latestVersion.data) ===
|
|
@@ -150,8 +156,12 @@ export async function createDeckVersionSnapshot(
|
|
|
150
156
|
const requestedChatContext = serializeDeckVersionChatContext(
|
|
151
157
|
options.chatContext,
|
|
152
158
|
);
|
|
153
|
-
const
|
|
159
|
+
const turnGroup =
|
|
154
160
|
options.chatContext?.turnId ?? options.chatContext?.runId ?? undefined;
|
|
161
|
+
const changeGroup =
|
|
162
|
+
options.chatContext?.phase === "start" && turnGroup
|
|
163
|
+
? `start:thread:${options.chatContext.threadId ?? turnGroup}`
|
|
164
|
+
: turnGroup;
|
|
155
165
|
if (
|
|
156
166
|
requestedChatContext &&
|
|
157
167
|
requestedChatContext === normalizedChatContext(latestVersion?.chatContext)
|
|
@@ -196,3 +206,28 @@ export async function createDeckVersionSnapshot(
|
|
|
196
206
|
|
|
197
207
|
return { created: true, id };
|
|
198
208
|
}
|
|
209
|
+
|
|
210
|
+
export async function createDeckChatBeginningSnapshot(
|
|
211
|
+
source: DeckSnapshotSource,
|
|
212
|
+
run: { threadId: string; runId: string },
|
|
213
|
+
): Promise<{ created: boolean; id?: string; reason?: string }> {
|
|
214
|
+
const rows = await getDb()
|
|
215
|
+
.select({ id: schema.deckVersions.id })
|
|
216
|
+
.from(schema.deckVersions)
|
|
217
|
+
.where(
|
|
218
|
+
and(
|
|
219
|
+
eq(schema.deckVersions.deckId, source.id),
|
|
220
|
+
eq(schema.deckVersions.ownerEmail, source.ownerEmail),
|
|
221
|
+
eq(schema.deckVersions.changeGroup, `start:thread:${run.threadId}`),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
.limit(1);
|
|
225
|
+
if (rows.length) {
|
|
226
|
+
return { created: false, reason: "beginning-exists" };
|
|
227
|
+
}
|
|
228
|
+
return createDeckVersionSnapshot(source, {
|
|
229
|
+
force: true,
|
|
230
|
+
label: "Before chat",
|
|
231
|
+
chatContext: { ...run, phase: "start" },
|
|
232
|
+
});
|
|
233
|
+
}
|
|
@@ -7,7 +7,10 @@ import { assertAccess } from "@agent-native/core/sharing";
|
|
|
7
7
|
import actionsRegistry from "../../.generated/actions-registry.js";
|
|
8
8
|
import { resolveSlidesRequestAuthContext } from "../handlers/request-auth-context.js";
|
|
9
9
|
import { prepareSlidesChatAttachments } from "../lib/chat-attachments.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
createDeckChatBeginningSnapshot,
|
|
12
|
+
deckVersionChatContextFromRun,
|
|
13
|
+
} from "../lib/deck-versions.js";
|
|
11
14
|
import "../register-secrets.js";
|
|
12
15
|
|
|
13
16
|
const SLIDES_BACKGROUND_RUN_SOFT_TIMEOUT_MS = 13 * 60_000;
|
|
@@ -138,6 +141,7 @@ async function autosaveDeckAfterAgentTurn(
|
|
|
138
141
|
},
|
|
139
142
|
): Promise<void> {
|
|
140
143
|
if (scope.type !== "deck" || !hasDeckEdit(run, scope.id)) return;
|
|
144
|
+
if (!run.threadId || !run.runId) return;
|
|
141
145
|
|
|
142
146
|
const access = await assertAccess("deck", scope.id, "editor");
|
|
143
147
|
const deck = access.resource as {
|
|
@@ -147,15 +151,35 @@ async function autosaveDeckAfterAgentTurn(
|
|
|
147
151
|
ownerEmail: string;
|
|
148
152
|
};
|
|
149
153
|
const { createDeckVersionSnapshot } = await import("../lib/deck-versions.js");
|
|
154
|
+
const chatContext = deckVersionChatContextFromRun(run);
|
|
150
155
|
await createDeckVersionSnapshot(deck, {
|
|
151
156
|
force: true,
|
|
152
157
|
label: "Chat autosave",
|
|
153
|
-
chatContext:
|
|
158
|
+
chatContext: chatContext ? { ...chatContext, phase: "end" } : undefined,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function autosaveDeckBeforeAgentTurn(
|
|
163
|
+
scope: { type: string; id: string },
|
|
164
|
+
run: { threadId?: string; runId?: string },
|
|
165
|
+
): Promise<void> {
|
|
166
|
+
if (scope.type !== "deck" || !run.threadId || !run.runId) return;
|
|
167
|
+
const access = await assertAccess("deck", scope.id, "editor");
|
|
168
|
+
const deck = access.resource as {
|
|
169
|
+
id: string;
|
|
170
|
+
title: string;
|
|
171
|
+
data: string;
|
|
172
|
+
ownerEmail: string;
|
|
173
|
+
};
|
|
174
|
+
await createDeckChatBeginningSnapshot(deck, {
|
|
175
|
+
threadId: run.threadId,
|
|
176
|
+
runId: run.runId,
|
|
154
177
|
});
|
|
155
178
|
}
|
|
156
179
|
|
|
157
180
|
export default createAgentChatPlugin({
|
|
158
181
|
appId: "slides",
|
|
182
|
+
onAgentTurnStart: autosaveDeckBeforeAgentTurn,
|
|
159
183
|
onAgentTurnComplete: autosaveDeckAfterAgentTurn,
|
|
160
184
|
actions: loadActionsFromStaticRegistry(actionsRegistry),
|
|
161
185
|
initialToolNames: INITIAL_TOOL_NAMES,
|
|
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
186
186
|
duplicate?: undefined;
|
|
187
187
|
eventId?: undefined;
|
|
188
188
|
} | {
|
|
189
|
-
error?: undefined;
|
|
190
189
|
accepted: boolean;
|
|
191
190
|
duplicate: boolean;
|
|
192
191
|
eventId: string;
|
|
192
|
+
error?: undefined;
|
|
193
193
|
}>;
|
package/dist/cli/index.js
CHANGED
|
@@ -1233,8 +1233,7 @@ Options:
|
|
|
1233
1233
|
--standalone Scaffold a single standalone app (no workspace)
|
|
1234
1234
|
--emit [dir] With migrate, emit an own-agent dossier
|
|
1235
1235
|
--describe <text> With migrate, describe URL/prose-only sources
|
|
1236
|
-
--preset <name> Workspace deploy preset:
|
|
1237
|
-
cloudflare_pages (default), netlify, or vercel
|
|
1236
|
+
--preset <name> Workspace deploy preset: netlify (default) or vercel
|
|
1238
1237
|
--build-only Build workspace deploy artifacts without publishing
|
|
1239
1238
|
--eager With workspace dev, start every app immediately
|
|
1240
1239
|
--prewarm With workspace dev, warm non-default apps in the background
|
|
@@ -23,7 +23,7 @@ import { AgentActivityTrace } from "./chat/agent-activity-trace.js";
|
|
|
23
23
|
import { DownscalingImageAttachmentAdapter, BinaryDocumentAttachmentAdapter, MAX_PDF_BYTES, MAX_ESTIMATED_BODY_BYTES, AGGRESSIVE_MAX_IMAGE_DIMENSION, AGGRESSIVE_JPEG_QUALITY, transcodeImageToDataURL, createAgentImageAttachments, serializeQueuedAttachments, getSubmittedPromptBodyStrings, measureJsonStringBytes, getAttachmentBodyStrings, } from "./chat/attachment-adapters.js";
|
|
24
24
|
import { readAssistantChatComposerDraft, writeAssistantChatComposerDraft, } from "./chat/composer-draft.js";
|
|
25
25
|
import { AgentTextStreamingProvider, ExternalTextStreamingContext, } from "./chat/markdown-renderer.js";
|
|
26
|
-
import { AssistantChatHistoryContext, assistantMessageHasCompletedSideEffect, findMatchingAssistantChatHistoryVersion, isAssistantChatHistoryVersion, CheckpointContext, MessageActionsContext, assistantMessageRunId, assistantMessageTurnId, UserMessage, AssistantMessage, ExternalUserStoppedRunContext, SelectionAttachedPill, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, UserStoppedRunContext, } from "./chat/message-components.js";
|
|
26
|
+
import { AssistantChatHistoryContext, AssistantChatHistoryBeginningRevertButton, assistantMessageHasCompletedSideEffect, findAssistantChatHistoryBeginningVersion, findMatchingAssistantChatHistoryVersion, isAssistantChatHistoryVersion, CheckpointContext, MessageActionsContext, assistantMessageRunId, assistantMessageTurnId, UserMessage, AssistantMessage, ExternalUserStoppedRunContext, SelectionAttachedPill, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, UserStoppedRunContext, } from "./chat/message-components.js";
|
|
27
27
|
import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, dedupeRepoMessagesById, dropEmptyAssistantMessages, withLastAssistantRunDuration, } from "./chat/repo-helpers.js";
|
|
28
28
|
import { BuilderSetupCard, LoopLimitContinueCard, RunErrorRecoveryCard, PlanModeCallout, getLoopLimitMetadata, getRunErrorMetadata, getRequestModeMetadata, isBuilderReconnectRunError, runErrorKey, } from "./chat/run-recovery.js";
|
|
29
29
|
import { createAgentChatRuntimeAdapter, } from "./chat/runtime.js";
|
|
@@ -1321,6 +1321,7 @@ export function shouldShowAssistantChatSuggestions(visibility, hasAssistantMessa
|
|
|
1321
1321
|
return visibility === "always" || hasAssistantMessage;
|
|
1322
1322
|
}
|
|
1323
1323
|
export async function restoreAssistantChatHistoryVersion(options) {
|
|
1324
|
+
await options.history.restore.beforeRestore?.();
|
|
1324
1325
|
const args = await options.history.restore.args(options.version);
|
|
1325
1326
|
const restored = await options.restore(args);
|
|
1326
1327
|
let applicationFailed = false;
|
|
@@ -1976,7 +1977,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1976
1977
|
isRunningRef.current = isRunning;
|
|
1977
1978
|
const submissionInFlightRef = useRef(0);
|
|
1978
1979
|
const submissionTailRef = useRef(Promise.resolve());
|
|
1979
|
-
const chatHistoryListQuery = useActionQuery((chatHistory?.list.action ?? "list-resource-versions"), chatHistory?.list.args
|
|
1980
|
+
const chatHistoryListQuery = useActionQuery((chatHistory?.list.action ?? "list-resource-versions"), (typeof chatHistory?.list.args === "function"
|
|
1981
|
+
? chatHistory.list.args(threadId)
|
|
1982
|
+
: chatHistory?.list.args), { enabled: chatHistory !== undefined });
|
|
1980
1983
|
const chatHistoryVersions = useMemo(() => {
|
|
1981
1984
|
if (!chatHistory || chatHistoryListQuery.data == null)
|
|
1982
1985
|
return [];
|
|
@@ -1990,24 +1993,59 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1990
1993
|
const refetchChatHistory = chatHistoryListQuery.refetch;
|
|
1991
1994
|
const restoreHistory = chatHistoryRestoreMutation.mutateAsync;
|
|
1992
1995
|
const createHistoryVersion = chatHistoryCreateMutation.mutateAsync;
|
|
1996
|
+
const [isChatHistoryRestoring, setIsChatHistoryRestoring] = useState(false);
|
|
1997
|
+
const chatHistoryRestoreInFlightRef = useRef(false);
|
|
1998
|
+
const chatHistoryRestoreWaitRef = useRef(null);
|
|
1999
|
+
const waitForChatHistoryRestore = useCallback(async () => {
|
|
2000
|
+
let restoreWait = chatHistoryRestoreWaitRef.current;
|
|
2001
|
+
while (restoreWait) {
|
|
2002
|
+
await restoreWait;
|
|
2003
|
+
restoreWait = chatHistoryRestoreWaitRef.current;
|
|
2004
|
+
}
|
|
2005
|
+
}, []);
|
|
1993
2006
|
const restoreChatHistoryVersion = useCallback(async (version) => {
|
|
1994
2007
|
if (!chatHistory)
|
|
1995
2008
|
return;
|
|
1996
|
-
|
|
1997
|
-
history
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
},
|
|
2006
|
-
}),
|
|
2009
|
+
if (chatHistoryRestoreInFlightRef.current) {
|
|
2010
|
+
throw new Error("A chat history restore is already in progress.");
|
|
2011
|
+
}
|
|
2012
|
+
if (submissionInFlightRef.current > 0) {
|
|
2013
|
+
throw new Error("A chat submission is already in progress.");
|
|
2014
|
+
}
|
|
2015
|
+
let finishRestore;
|
|
2016
|
+
const restoreWait = new Promise((resolve) => {
|
|
2017
|
+
finishRestore = resolve;
|
|
2007
2018
|
});
|
|
2019
|
+
chatHistoryRestoreWaitRef.current = restoreWait;
|
|
2020
|
+
chatHistoryRestoreInFlightRef.current = true;
|
|
2021
|
+
setIsChatHistoryRestoring(true);
|
|
2022
|
+
try {
|
|
2023
|
+
await restoreAssistantChatHistoryVersion({
|
|
2024
|
+
history: chatHistory,
|
|
2025
|
+
version,
|
|
2026
|
+
restore: restoreHistory,
|
|
2027
|
+
refetch: refetchChatHistory,
|
|
2028
|
+
onRefetchError: (error) => captureError(error, {
|
|
2029
|
+
tags: {
|
|
2030
|
+
source: "agent-chat-client",
|
|
2031
|
+
phase: "chat-history-refetch-after-restore",
|
|
2032
|
+
},
|
|
2033
|
+
}),
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
finally {
|
|
2037
|
+
chatHistoryRestoreInFlightRef.current = false;
|
|
2038
|
+
setIsChatHistoryRestoring(false);
|
|
2039
|
+
if (chatHistoryRestoreWaitRef.current === restoreWait) {
|
|
2040
|
+
chatHistoryRestoreWaitRef.current = null;
|
|
2041
|
+
}
|
|
2042
|
+
finishRestore();
|
|
2043
|
+
}
|
|
2008
2044
|
}, [chatHistory, refetchChatHistory, restoreHistory]);
|
|
2009
2045
|
const chatHistoryContext = useMemo(() => chatHistory
|
|
2010
2046
|
? {
|
|
2047
|
+
beginningVersion: findAssistantChatHistoryBeginningVersion(chatHistoryVersions, threadId, chatHistory.isEditable),
|
|
2048
|
+
isRestoring: isChatHistoryRestoring,
|
|
2011
2049
|
findVersion: (message) => findMatchingAssistantChatHistoryVersion(chatHistoryVersions, message, {
|
|
2012
2050
|
isEditable: chatHistory.isEditable,
|
|
2013
2051
|
scope: chatHistory.scope ?? contextScope ?? undefined,
|
|
@@ -2015,7 +2053,13 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2015
2053
|
}),
|
|
2016
2054
|
restoreVersion: restoreChatHistoryVersion,
|
|
2017
2055
|
}
|
|
2018
|
-
: null, [
|
|
2056
|
+
: null, [
|
|
2057
|
+
chatHistory,
|
|
2058
|
+
chatHistoryVersions,
|
|
2059
|
+
isChatHistoryRestoring,
|
|
2060
|
+
restoreChatHistoryVersion,
|
|
2061
|
+
threadId,
|
|
2062
|
+
]);
|
|
2019
2063
|
const chatHistoryRunObservedRef = useRef(false);
|
|
2020
2064
|
const chatHistoryCreateKeyRef = useRef(null);
|
|
2021
2065
|
useEffect(() => {
|
|
@@ -2035,6 +2079,12 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2035
2079
|
.find((message) => message.role === "assistant");
|
|
2036
2080
|
if (!latestAssistantMessage ||
|
|
2037
2081
|
!assistantMessageHasCompletedSideEffect(latestAssistantMessage)) {
|
|
2082
|
+
void refetchChatHistory().catch((error) => captureError(error, {
|
|
2083
|
+
tags: {
|
|
2084
|
+
source: "agent-chat-client",
|
|
2085
|
+
phase: "chat-history-refetch-after-run",
|
|
2086
|
+
},
|
|
2087
|
+
}));
|
|
2038
2088
|
return;
|
|
2039
2089
|
}
|
|
2040
2090
|
const historyMessage = {
|
|
@@ -3736,6 +3786,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3736
3786
|
// Keep prompts visible while setup is missing. Dequeuing them would remove
|
|
3737
3787
|
// the only user-visible copy and immediately re-enter the provider failure.
|
|
3738
3788
|
if (isRestoring ||
|
|
3789
|
+
isChatHistoryRestoring ||
|
|
3739
3790
|
engineSetupRequired ||
|
|
3740
3791
|
isRunning ||
|
|
3741
3792
|
queuedMessages.length === 0) {
|
|
@@ -3762,7 +3813,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3762
3813
|
// complete. Starting the queued turn during that window can reconnect
|
|
3763
3814
|
// to the old run and replay the old answer under the new prompt.
|
|
3764
3815
|
const runCleared = await waitForThreadRunToClear(apiUrl, threadId);
|
|
3765
|
-
if (cancelled)
|
|
3816
|
+
if (cancelled || chatHistoryRestoreInFlightRef.current)
|
|
3766
3817
|
return;
|
|
3767
3818
|
if (!runCleared) {
|
|
3768
3819
|
// The server still owns this turn (including a deferred durable
|
|
@@ -3869,6 +3920,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3869
3920
|
apiUrl,
|
|
3870
3921
|
appendThreadMessage,
|
|
3871
3922
|
applyLocalQueuedMessages,
|
|
3923
|
+
isChatHistoryRestoring,
|
|
3872
3924
|
isRestoring,
|
|
3873
3925
|
isRunning,
|
|
3874
3926
|
engineSetupRequired,
|
|
@@ -4201,7 +4253,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4201
4253
|
}, [onStop, stopActiveRun]);
|
|
4202
4254
|
// The composer stop button uses the handler above; queued send-now keeps the
|
|
4203
4255
|
// active run alive and only promotes the selected message for later dequeue.
|
|
4204
|
-
const sendQueuedMessageNow = useCallback((id) => {
|
|
4256
|
+
const sendQueuedMessageNow = useCallback(async (id) => {
|
|
4257
|
+
await waitForChatHistoryRestore();
|
|
4205
4258
|
const message = queuedMessagesRef.current.find((candidate) => candidate.id === id);
|
|
4206
4259
|
if (!message || message.promoted)
|
|
4207
4260
|
return;
|
|
@@ -4235,9 +4288,18 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4235
4288
|
return;
|
|
4236
4289
|
}
|
|
4237
4290
|
applyLocalQueuedMessages((prev) => promoteQueuedMessage(prev, id));
|
|
4238
|
-
}, [
|
|
4291
|
+
}, [
|
|
4292
|
+
appendThreadMessage,
|
|
4293
|
+
applyLocalQueuedMessages,
|
|
4294
|
+
threadId,
|
|
4295
|
+
threadRuntime,
|
|
4296
|
+
waitForChatHistoryRestore,
|
|
4297
|
+
]);
|
|
4239
4298
|
const visibleQueuedMessages = useMemo(() => queuedMessages.filter((message) => !message.hideUserMessage && !message.promoted), [queuedMessages]);
|
|
4240
4299
|
const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false, preserveReconnectAutoRecoveryBudget = false, hideUserMessage = false, submitMessageId, approvedToolCalls, continuationTurnId, usageLabel, actionScope) => {
|
|
4300
|
+
if (isAgentChatSubmitCancelled(submitMessageId))
|
|
4301
|
+
return false;
|
|
4302
|
+
await waitForChatHistoryRestore();
|
|
4241
4303
|
if (isAgentChatSubmitCancelled(submitMessageId))
|
|
4242
4304
|
return false;
|
|
4243
4305
|
const wasSubmissionInFlight = submissionInFlightRef.current > 0;
|
|
@@ -4448,6 +4510,16 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4448
4510
|
]);
|
|
4449
4511
|
}
|
|
4450
4512
|
else {
|
|
4513
|
+
try {
|
|
4514
|
+
await chatHistory?.beforeStart?.();
|
|
4515
|
+
}
|
|
4516
|
+
catch (error) {
|
|
4517
|
+
setComposerError(String(error));
|
|
4518
|
+
reportAgentChatSubmitResult(submitMessageId, false, "editor-save-failed");
|
|
4519
|
+
return false;
|
|
4520
|
+
}
|
|
4521
|
+
if (isAgentChatSubmitCancelled(submitMessageId))
|
|
4522
|
+
return false;
|
|
4451
4523
|
markOptimisticRunning();
|
|
4452
4524
|
try {
|
|
4453
4525
|
appendThreadMessage({
|
|
@@ -4506,6 +4578,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4506
4578
|
t,
|
|
4507
4579
|
threadId,
|
|
4508
4580
|
updateComposerContextItems,
|
|
4581
|
+
waitForChatHistoryRestore,
|
|
4582
|
+
chatHistory,
|
|
4509
4583
|
]);
|
|
4510
4584
|
const mcpResumeTimerRef = useRef(null);
|
|
4511
4585
|
const scheduleMcpConnectionResume = useCallback((request) => {
|
|
@@ -4974,19 +5048,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4974
5048
|
if (engineSetupRequired)
|
|
4975
5049
|
return;
|
|
4976
5050
|
void addToQueue(suggestion);
|
|
4977
|
-
}, className: "agent-empty-suggestion w-full rounded-xl border border-border/70 bg-card/60 px-3 py-2.5 text-left text-[13px] text-muted-foreground shadow-sm transition-[border-color,background-color,color,transform] hover:-translate-y-px hover:border-border hover:bg-card hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null, emptyStateFooter ? (_jsx("div", { className: "agent-empty-state-footer", children: emptyStateFooter })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [resolvedThreadContentSlot ? (_jsx(MessageScrollerItem, { children: _jsx("div", { className: "agent-thread-content-slot", children: resolvedThreadContentSlot }) })) : null, threadRestoreErrorSurface ? (_jsx(MessageScrollerItem, { children: threadRestoreErrorSurface })) : null, _jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasUserStoppedRun, children: _jsx(ExternalUserStoppedRunContext.Provider, { value: externalUserStopped, children: _jsx(ExternalTextStreamingContext.Provider, { value: externalStreaming, children:
|
|
4978
|
-
// Deliberately NOT keyed on part structure. Doing that
|
|
4979
|
-
// remounted the whole transcript every time a tool call
|
|
4980
|
-
// started or a placeholder id was rewritten — a flash and a
|
|
4981
|
-
// lost scroll position in the middle of an answer. The
|
|
4982
|
-
// error boundary above is the mechanism for assistant-ui's
|
|
4983
|
-
// stale tap-resource errors: it catches them, clears, and
|
|
4984
|
-
// retries, and its retry signature includes the reset key so
|
|
4985
|
-
// a genuinely new structure always gets a fresh budget.
|
|
4986
|
-
// `assistant-ui-part-churn.spec.tsx` drives append, mutate,
|
|
4987
|
-
// rename and splice through both the import and streaming
|
|
4988
|
-
// paths and records that no such error occurs.
|
|
4989
|
-
, {
|
|
5051
|
+
}, className: "agent-empty-suggestion w-full rounded-xl border border-border/70 bg-card/60 px-3 py-2.5 text-left text-[13px] text-muted-foreground shadow-sm transition-[border-color,background-color,color,transform] hover:-translate-y-px hover:border-border hover:bg-card hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null, emptyStateFooter ? (_jsx("div", { className: "agent-empty-state-footer", children: emptyStateFooter })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [resolvedThreadContentSlot ? (_jsx(MessageScrollerItem, { children: _jsx("div", { className: "agent-thread-content-slot", children: resolvedThreadContentSlot }) })) : null, threadRestoreErrorSurface ? (_jsx(MessageScrollerItem, { children: threadRestoreErrorSurface })) : null, _jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasUserStoppedRun, children: _jsx(ExternalUserStoppedRunContext.Provider, { value: externalUserStopped, children: _jsx(ExternalTextStreamingContext.Provider, { value: externalStreaming, children: _jsxs(AssistantChatHistoryContext.Provider, { value: chatHistoryContext, children: [chatHistoryContext?.beginningVersion ? (_jsx(MessageScrollerItem, { children: _jsx(AssistantChatHistoryBeginningRevertButton, {}) })) : null, _jsx(ThreadPrimitive.Messages
|
|
4990
5052
|
// Deliberately NOT keyed on part structure. Doing that
|
|
4991
5053
|
// remounted the whole transcript every time a tool call
|
|
4992
5054
|
// started or a placeholder id was rewritten — a flash and a
|
|
@@ -4998,10 +5060,22 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4998
5060
|
// `assistant-ui-part-churn.spec.tsx` drives append, mutate,
|
|
4999
5061
|
// rename and splice through both the import and streaming
|
|
5000
5062
|
// paths and records that no such error occurs.
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5063
|
+
, {
|
|
5064
|
+
// Deliberately NOT keyed on part structure. Doing that
|
|
5065
|
+
// remounted the whole transcript every time a tool call
|
|
5066
|
+
// started or a placeholder id was rewritten — a flash and a
|
|
5067
|
+
// lost scroll position in the middle of an answer. The
|
|
5068
|
+
// error boundary above is the mechanism for assistant-ui's
|
|
5069
|
+
// stale tap-resource errors: it catches them, clears, and
|
|
5070
|
+
// retries, and its retry signature includes the reset key so
|
|
5071
|
+
// a genuinely new structure always gets a fresh budget.
|
|
5072
|
+
// `assistant-ui-part-churn.spec.tsx` drives append, mutate,
|
|
5073
|
+
// rename and splice through both the import and streaming
|
|
5074
|
+
// paths and records that no such error occurs.
|
|
5075
|
+
components: {
|
|
5076
|
+
UserMessage: AssistantChatUserMessageItem,
|
|
5077
|
+
AssistantMessage: AssistantChatAssistantMessageItem,
|
|
5078
|
+
} })] }) }) }) }) }), visibleLoopLimit && !showRunningInUI && (_jsx(MessageScrollerItem, { children: _jsx(LoopLimitContinueCard, { info: visibleLoopLimit, onContinue: () => {
|
|
5005
5079
|
setShowContinue(false);
|
|
5006
5080
|
setLoopLimitInfo(null);
|
|
5007
5081
|
const continuation = latestProtocolContinuationContext(messagesRef.current);
|
|
@@ -5089,7 +5163,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
5089
5163
|
? bounceMissingKeySetup
|
|
5090
5164
|
: undefined, children: _jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, maxDocumentAttachmentBytes: MAX_PDF_BYTES, initialText: initialComposerText ?? undefined, initialTextKey: composerDraftScope, onTextChange: isActiveComposer
|
|
5091
5165
|
? handleComposerTextChange
|
|
5092
|
-
: undefined, disabled: isComposerDisabled ||
|
|
5166
|
+
: undefined, disabled: isComposerDisabled ||
|
|
5167
|
+
showMissingKeySetup ||
|
|
5168
|
+
isChatHistoryRestoring, placeholder: showMissingKeySetup
|
|
5093
5169
|
? t("agentChat.setup.connectPlaceholder")
|
|
5094
5170
|
: engineSetupRequired
|
|
5095
5171
|
? t("agentChat.setup.connectPlaceholder")
|
|
@@ -5119,6 +5195,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
5119
5195
|
}
|
|
5120
5196
|
}, willQueue: engineSetupRequired ||
|
|
5121
5197
|
isRunning ||
|
|
5198
|
+
isChatHistoryRestoring ||
|
|
5122
5199
|
submissionInFlightRef.current > 0, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, availableAgents: availableAgents, selectedAgent: selectedAgent, hostedHarness: hostedHarness, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
|
|
5123
5200
|
? onModelChange
|
|
5124
5201
|
: undefined, onEffortChange: onEffortChange, onAgentChange: onAgentChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: visibleComposerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: composerDraftScope, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleComposerStop, "aria-label": t("agentChat.composer.stopResponse"), "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.composer.stopResponse") })] })) : undefined })] }) }) })] })] }) }) }) }) }) }) }) }) }) }) }));
|
|
@@ -57,14 +57,17 @@ export interface AssistantChatHistoryMessage {
|
|
|
57
57
|
hasCompletedSideEffect: boolean;
|
|
58
58
|
}
|
|
59
59
|
export interface AssistantChatHistoryConfig<TListResult = unknown, TVersion extends AssistantChatHistoryVersion = AssistantChatHistoryVersion, TRestoreResult = unknown> {
|
|
60
|
+
/** Flush host editor writes before an agent turn starts. */
|
|
61
|
+
beforeStart?: () => void | Promise<void>;
|
|
60
62
|
list: {
|
|
61
63
|
action: string;
|
|
62
|
-
args?: Record<string, unknown
|
|
64
|
+
args?: Record<string, unknown> | ((threadId?: string) => Record<string, unknown>);
|
|
63
65
|
getVersions: (result: TListResult) => readonly TVersion[];
|
|
64
66
|
};
|
|
65
67
|
restore: {
|
|
66
68
|
action: string;
|
|
67
69
|
args: (version: TVersion) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
70
|
+
beforeRestore?: () => void | Promise<void>;
|
|
68
71
|
onRestored?: (result: TRestoreResult, version: TVersion) => void | Promise<void>;
|
|
69
72
|
};
|
|
70
73
|
createVersion?: {
|
|
@@ -76,6 +79,8 @@ export interface AssistantChatHistoryConfig<TListResult = unknown, TVersion exte
|
|
|
76
79
|
matchVersion?: (version: TVersion, message: AssistantChatHistoryMessage) => boolean;
|
|
77
80
|
}
|
|
78
81
|
export interface AssistantChatHistoryContextValue {
|
|
82
|
+
beginningVersion: AssistantChatHistoryVersion | null;
|
|
83
|
+
isRestoring: boolean;
|
|
79
84
|
findVersion: (message: AssistantChatHistoryMessage) => AssistantChatHistoryVersion | null;
|
|
80
85
|
restoreVersion: (version: AssistantChatHistoryVersion) => Promise<void>;
|
|
81
86
|
}
|
|
@@ -93,6 +98,7 @@ export declare const MessageActionsContext: React.Context<{
|
|
|
93
98
|
export declare function isLocalDevelopmentHost(hostname: string): boolean;
|
|
94
99
|
export declare function assistantMessageHasCompletedSideEffect(message: unknown): boolean;
|
|
95
100
|
export declare function findMatchingAssistantChatHistoryVersion<TVersion extends AssistantChatHistoryVersion>(versions: readonly TVersion[], message: AssistantChatHistoryMessage, options?: Pick<AssistantChatHistoryConfig<unknown, TVersion>, "isEditable" | "matchVersion" | "scope">): TVersion | null;
|
|
101
|
+
export declare function findAssistantChatHistoryBeginningVersion<TVersion extends AssistantChatHistoryVersion>(versions: readonly TVersion[], threadId?: string, isEditable?: (version: TVersion) => boolean): TVersion | null;
|
|
96
102
|
/**
|
|
97
103
|
* Restore rewrites the working tree, so only offer it when the server actually
|
|
98
104
|
* has a checkpoint for this turn. Auto-checkpointing skips turns that started
|
|
@@ -129,6 +135,7 @@ export declare function MessageActionsMenu({ showRevert, onRevert, threadId, }?:
|
|
|
129
135
|
onRevert?: () => void;
|
|
130
136
|
threadId?: string;
|
|
131
137
|
}): React.JSX.Element;
|
|
138
|
+
export declare function AssistantChatHistoryBeginningRevertButton(): React.JSX.Element | null;
|
|
132
139
|
export declare function UserMessage(): React.JSX.Element | null;
|
|
133
140
|
export declare function messageTextFromContent(content: unknown): string;
|
|
134
141
|
export declare function isMissingFinalResponseWarningText(text: string): boolean;
|