@axhub/acp 0.1.0 → 0.1.2-beta.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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +4 -0
- package/.next/build-manifest.json +3 -3
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/routes-manifest.json +28 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/acp/capabilities/route.js +5 -3
- package/.next/server/app/api/acp/capabilities/route.js.nft.json +1 -1
- package/.next/server/app/api/acp/commands/route.js +1 -1
- package/.next/server/app/api/acp/commands/route.js.nft.json +1 -1
- package/.next/server/app/api/acp/runtime/route.js.nft.json +1 -1
- package/.next/server/app/api/chat/cancel/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/chat/cancel/route/build-manifest.json +9 -0
- package/.next/server/app/api/chat/cancel/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/chat/cancel/route.js +11 -0
- package/.next/server/app/api/chat/cancel/route.js.nft.json +1 -0
- package/.next/server/app/api/chat/cancel/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/chat/resume/[streamId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/chat/resume/[streamId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/chat/resume/[streamId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/chat/resume/[streamId]/route.js +11 -0
- package/.next/server/app/api/chat/resume/[streamId]/route.js.nft.json +1 -0
- package/.next/server/app/api/chat/resume/[streamId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/chat/route.js +5 -3
- package/.next/server/app/api/chat/route.js.nft.json +1 -1
- package/.next/server/app/api/conversations/[threadId]/messages/route.js +4 -2
- package/.next/server/app/api/conversations/[threadId]/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/conversations/[threadId]/route.js +2 -2
- package/.next/server/app/api/conversations/[threadId]/route.js.nft.json +1 -1
- package/.next/server/app/api/conversations/[threadId]/runtime/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/conversations/[threadId]/runtime/route/build-manifest.json +9 -0
- package/.next/server/app/api/conversations/[threadId]/runtime/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/conversations/[threadId]/runtime/route.js +11 -0
- package/.next/server/app/api/conversations/[threadId]/runtime/route.js.nft.json +1 -0
- package/.next/server/app/api/conversations/[threadId]/runtime/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/conversations/route.js +2 -2
- package/.next/server/app/api/conversations/route.js.nft.json +1 -1
- package/.next/server/app/api/local-files/image/route.js.nft.json +1 -1
- package/.next/server/app/api/local-files/open/route.js.nft.json +1 -1
- package/.next/server/app/api/output-artifacts/thread/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/output-artifacts/thread/route/build-manifest.json +9 -0
- package/.next/server/app/api/output-artifacts/thread/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/output-artifacts/thread/route.js +8 -0
- package/.next/server/app/api/output-artifacts/thread/route.js.nft.json +1 -0
- package/.next/server/app/api/output-artifacts/thread/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/output-artifacts/workspace/route.js +1 -1
- package/.next/server/app/api/output-artifacts/workspace/route.js.nft.json +1 -1
- package/.next/server/app/api/tools/image-generation/files/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/tools/image-generation/records/route.js.nft.json +1 -1
- package/.next/server/app/api/tools/user-choice/route.js.nft.json +1 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/session/[provider]/[sessionId]/page.js +1 -1
- package/.next/server/app/session/[provider]/[sessionId]/page.js.nft.json +1 -1
- package/.next/server/app/session/[provider]/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/thread/[threadId]/page.js +1 -1
- package/.next/server/app/thread/[threadId]/page.js.nft.json +1 -1
- package/.next/server/app/thread/[threadId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +4 -0
- package/.next/server/chunks/0zjb_server_app_api_conversations_[threadId]_runtime_route_actions_08lhdqs.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__04pn6ap._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0aovkxs._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0c.r6ru._.js +76 -0
- package/.next/server/chunks/[root-of-the-server]__0gmxr~m._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0iokgmz._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0j-lxr4._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0lbwo2g._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0ly6hop._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0ml.1wa._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0o2epta._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0os92l7._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tmhg7j._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0txmfnw._.js +2 -2
- package/.next/server/chunks/[root-of-the-server]__0wo0b8z._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0xh8d4~._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0zmyki-._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0zn3~pq._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__13xepwb._.js +3 -0
- package/.next/server/chunks/_0_9_730._.js +107 -0
- package/.next/server/chunks/_0gx~6n6._.js +107 -0
- package/.next/server/chunks/_next-internal_server_app_api_chat_cancel_route_actions_0hdg4o_.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_chat_resume_[streamId]_route_actions_12ynw6q.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_output-artifacts_thread_route_actions_04~2mo7.js +3 -0
- package/.next/server/chunks/lib_conversations_store_ts_0gzcj38._.js +4 -4
- package/.next/server/chunks/node_modules_0nyqhq8._.js +3 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0piffp7._.js → [root-of-the-server]__09wwymw._.js} +2 -2
- package/.next/server/chunks/ssr/{[root-of-the-server]__0488vn3._.js → [root-of-the-server]__0n6oe29._.js} +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__0icm-_h._.js → [root-of-the-server]__0niwg81._.js} +2 -2
- package/.next/server/chunks/ssr/_03.pm1z._.js +18 -16
- package/.next/server/chunks/ssr/_0txwi90._.js +1 -1
- package/.next/server/chunks/ssr/lib_conversations_store_ts_0-pd6d3._.js +2 -2
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js +1 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_02suzhc.js +2 -2
- package/.next/server/functions-config-manifest.json +3 -0
- package/.next/server/instrumentation.js.nft.json +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/0c~b2_-vk17t5.css +1 -0
- package/.next/static/chunks/0r125n-._y5ny.js +104 -0
- package/README.md +2 -2
- package/bin/acp.mjs +0 -48
- package/dist/components/assistant-ui/acp-command-menu.mjs +42 -9
- package/dist/components/assistant-ui/acp-elicitation-option-list.mjs +1 -1
- package/dist/components/assistant-ui/acp-selectors.mjs +1 -3
- package/dist/components/assistant-ui/file.mjs +2 -2
- package/dist/components/assistant-ui/image-generation-settings-dialog.mjs +25 -6
- package/dist/components/assistant-ui/image.mjs +2 -2
- package/dist/components/assistant-ui/markdown-text.mjs +3 -3
- package/dist/components/assistant-ui/thread/composer.d.ts +1 -1
- package/dist/components/assistant-ui/thread/composer.mjs +37 -3
- package/dist/components/assistant-ui/thread/context-chips.mjs +3 -2
- package/dist/components/assistant-ui/thread/index.mjs +1 -1
- package/dist/components/assistant-ui/thread/message-list.mjs +1 -1
- package/dist/components/assistant-ui/thread/messages.mjs +2 -2
- package/dist/components/assistant-ui/thread-list.d.ts +8 -2
- package/dist/components/assistant-ui/thread-list.mjs +80 -7
- package/dist/components/assistant-ui/threadlist-sidebar.d.ts +4 -1
- package/dist/components/assistant-ui/threadlist-sidebar.mjs +2 -2
- package/dist/components/assistant-ui/tool-fallback.d.ts +1 -1
- package/dist/components/assistant-ui/tool-fallback.mjs +48 -11
- package/dist/components/tool-ui/option-list.d.ts +2 -1
- package/dist/components/tool-ui/option-list.mjs +4 -4
- package/dist/lib/acp2aisdk/client-context.d.ts +6 -0
- package/dist/lib/acp2aisdk/client-context.mjs +43 -7
- package/dist/lib/acp2aisdk/commands.mjs +5 -20
- package/dist/lib/acp2aisdk/context.d.ts +4 -0
- package/dist/lib/acp2aisdk/errors.d.ts +2 -0
- package/dist/lib/acp2aisdk/errors.mjs +37 -0
- package/dist/lib/acp2aisdk/index.d.ts +7 -0
- package/dist/lib/acp2aisdk/index.mjs +110 -34
- package/dist/lib/acp2aisdk/provider-compat.mjs +4 -2
- package/dist/lib/acp2aisdk/provider-registry.mjs +2 -5
- package/dist/lib/acp2aisdk/resumable-ui-stream.d.ts +11 -0
- package/dist/lib/acp2aisdk/resumable-ui-stream.mjs +323 -0
- package/dist/lib/acp2aisdk/runtime-message-filter.d.ts +6 -0
- package/dist/lib/acp2aisdk/runtime-message-filter.mjs +22 -0
- package/dist/lib/acp2aisdk/runtime-persistence.d.ts +21 -0
- package/dist/lib/acp2aisdk/runtime-persistence.mjs +135 -0
- package/dist/lib/acp2aisdk/session-store.mjs +7 -1
- package/dist/lib/acp2aisdk/skill-command-cache.mjs +1 -1
- package/dist/lib/acp2aisdk/types.d.ts +0 -1
- package/dist/lib/api/client.d.ts +51 -4
- package/dist/lib/api/client.mjs +35 -4
- package/dist/lib/api/cors.mjs +7 -1
- package/dist/lib/api/http-response.d.ts +2 -0
- package/dist/lib/api/http-response.mjs +22 -0
- package/dist/lib/conversations/client-adapter.d.ts +1 -0
- package/dist/lib/conversations/client-adapter.mjs +158 -51
- package/dist/lib/conversations/provider-message-loader.d.ts +7 -0
- package/dist/lib/conversations/provider-message-loader.mjs +99 -0
- package/dist/lib/conversations/runtime-message-recovery.d.ts +9 -0
- package/dist/lib/conversations/runtime-message-recovery.mjs +95 -0
- package/dist/lib/conversations/store.d.ts +2 -2
- package/dist/lib/conversations/store.mjs +49 -149
- package/dist/lib/conversations/title-text.d.ts +3 -0
- package/dist/lib/conversations/title-text.mjs +81 -0
- package/dist/lib/conversations/types.d.ts +12 -1
- package/dist/lib/local-image-files.mjs +9 -1
- package/dist/lib/local-image-paths.mjs +31 -6
- package/dist/lib/output-artifacts/thread.d.ts +22 -0
- package/dist/lib/output-artifacts/thread.mjs +47 -0
- package/dist/lib/output-artifacts/workspace.mjs +6 -2
- package/dist/lib/provider-history/codex.mjs +5 -30
- package/dist/public-api/server.d.ts +1 -1
- package/dist/public-api/server.mjs +1 -1
- package/dist/tools/image-generation/client.mjs +6 -4
- package/dist/tools/image-generation/server.mjs +3 -1
- package/dist/tools/image-generation/shared.d.ts +1 -0
- package/dist/tools/image-generation/shared.mjs +4 -0
- package/dist/tools/image-generation/ui-detail.mjs +66 -2
- package/dist/tools/user-choice/ui.mjs +66 -30
- package/package.json +6 -6
- package/.next/server/chunks/[root-of-the-server]__04xq..~._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__07sxz4_._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0dwg3fr._.js +0 -178
- package/.next/server/chunks/[root-of-the-server]__0eanzwb._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0gqx~5k._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0~mtsby._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__10-n4io._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__10g507v._.js +0 -3
- package/.next/static/chunks/0zftsky7gte_9.js +0 -102
- package/.next/static/chunks/1610ha42i.fl~.css +0 -1
- /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_buildManifest.js +0 -0
- /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → 0241KNhzDEg6VFWZ_e_qK}/_ssgManifest.js +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { frontendTools } from "@assistant-ui/react-ai-sdk";
|
|
2
2
|
import { consumeStream, convertToModelMessages, streamText, } from "ai";
|
|
3
|
-
import { closeConversationSession,
|
|
4
|
-
import { sanitizePersistedBuiltinToolSettingsPatch } from "../../tools/registry.mjs";
|
|
3
|
+
import { closeConversationSession, loadConversationMessages, touchConversationFromRuntime, } from "../conversations/store.mjs";
|
|
5
4
|
import { ACP_ELICITATION_OPTION_LIST_TOOL_NAME, ACP_ELICITATION_OPTION_LIST_TOOL_SCHEMA, } from "./elicitation.mjs";
|
|
5
|
+
import { acpErrorToMessage } from "./errors.mjs";
|
|
6
6
|
import { selectMessagesForAcpPrompt, selectMessagesForFreshAcpSessionPrompt, wrapLatestUserMessageWithDynamicContext, } from "./prompt-history.mjs";
|
|
7
7
|
import { closeAcpProviderSession, stageAcpElicitationResultFromMessages, } from "./provider-compat.mjs";
|
|
8
|
-
import { normalizeAcpProvider } from "./provider-registry.mjs";
|
|
9
8
|
import { createDirectMessageResponse, createRuntimeHeaders } from "./response.mjs";
|
|
10
|
-
import {
|
|
9
|
+
import { createResumableUIMessageStreamResponse } from "./resumable-ui-stream.mjs";
|
|
10
|
+
import { persistRequestRuntimeMessages, persistRuntimeMessages, } from "./runtime-persistence.mjs";
|
|
11
11
|
import { applyRequestedModelAndMode, getOrCreateSession, getSessionKeyForLookup, updateMetadataFromProvider, } from "./session-runtime.mjs";
|
|
12
|
-
import { getGlobalAcpSessionStore
|
|
12
|
+
import { getGlobalAcpSessionStore } from "./session-store.mjs";
|
|
13
13
|
import { createAcpMessageMetadata } from "./stream-metadata.mjs";
|
|
14
14
|
import { acpTools } from "./vendor/acp-ai-provider/index.mjs";
|
|
15
15
|
const INTERNAL_FRONTEND_TOOLS = {
|
|
@@ -18,31 +18,7 @@ const INTERNAL_FRONTEND_TOOLS = {
|
|
|
18
18
|
parameters: ACP_ELICITATION_OPTION_LIST_TOOL_SCHEMA,
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
var _a, _b, _c, _d;
|
|
23
|
-
const builtinToolSettings = sanitizePersistedBuiltinToolSettingsPatch(request.builtinToolSettings);
|
|
24
|
-
if (!builtinToolSettings)
|
|
25
|
-
return;
|
|
26
|
-
const patch = {
|
|
27
|
-
threadId: normalizeThreadId((_a = request.threadId) !== null && _a !== void 0 ? _a : request.id),
|
|
28
|
-
workspacePath: normalizeWorkspacePath(request.workspacePath),
|
|
29
|
-
builtinToolSettings,
|
|
30
|
-
};
|
|
31
|
-
if (request.provider !== undefined) {
|
|
32
|
-
patch.provider = normalizeAcpProvider(request.provider);
|
|
33
|
-
}
|
|
34
|
-
if (request.model !== undefined)
|
|
35
|
-
patch.model = (_b = request.model) !== null && _b !== void 0 ? _b : null;
|
|
36
|
-
if (request.modeId !== undefined)
|
|
37
|
-
patch.modeId = (_c = request.modeId) !== null && _c !== void 0 ? _c : null;
|
|
38
|
-
if (request.thoughtLevel !== undefined) {
|
|
39
|
-
patch.thoughtLevel = (_d = request.thoughtLevel) !== null && _d !== void 0 ? _d : null;
|
|
40
|
-
}
|
|
41
|
-
if (request.permissionMode !== undefined) {
|
|
42
|
-
patch.permissionMode = normalizePermissionMode(request.permissionMode);
|
|
43
|
-
}
|
|
44
|
-
await upsertConversation(patch);
|
|
45
|
-
}
|
|
21
|
+
const ORPHAN_RUNNING_SESSION_MS = 5 * 60 * 1000;
|
|
46
22
|
export function getAcpSessionMetadata(request) {
|
|
47
23
|
var _a, _b;
|
|
48
24
|
const store = getGlobalAcpSessionStore();
|
|
@@ -54,6 +30,42 @@ export function listAcpSessionMetadata() {
|
|
|
54
30
|
store.cleanupIdle();
|
|
55
31
|
return store.list();
|
|
56
32
|
}
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
35
|
+
}
|
|
36
|
+
function hasFreshRunningRuntimeMessage(messages, nowMs = Date.now()) {
|
|
37
|
+
return messages.some((message) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const content = isRecord(message.content) ? message.content : null;
|
|
40
|
+
const metadata = isRecord(content === null || content === void 0 ? void 0 : content.metadata) ? content.metadata : null;
|
|
41
|
+
const custom = isRecord(metadata === null || metadata === void 0 ? void 0 : metadata.custom) ? metadata.custom : null;
|
|
42
|
+
const acpRun = isRecord(custom === null || custom === void 0 ? void 0 : custom.acpRun) ? custom.acpRun : null;
|
|
43
|
+
if ((acpRun === null || acpRun === void 0 ? void 0 : acpRun.status) !== "running")
|
|
44
|
+
return false;
|
|
45
|
+
const updatedAtMs = Date.parse(String((_a = message.updatedAt) !== null && _a !== void 0 ? _a : ""));
|
|
46
|
+
return (Number.isFinite(updatedAtMs) &&
|
|
47
|
+
updatedAtMs >= nowMs - ORPHAN_RUNNING_SESSION_MS);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export async function reconcileAcpSessionWithRuntimeMessages(request) {
|
|
51
|
+
var _a;
|
|
52
|
+
const store = getGlobalAcpSessionStore();
|
|
53
|
+
store.cleanupIdle();
|
|
54
|
+
const sessionKey = getSessionKeyForLookup(request);
|
|
55
|
+
const entry = store.get(sessionKey);
|
|
56
|
+
const metadata = (_a = entry === null || entry === void 0 ? void 0 : entry.metadata) !== null && _a !== void 0 ? _a : null;
|
|
57
|
+
if (!entry || (metadata === null || metadata === void 0 ? void 0 : metadata.runState) !== "running")
|
|
58
|
+
return metadata;
|
|
59
|
+
const runtimeRepo = await loadConversationMessages(metadata.threadId, "ai-sdk/v6", metadata.workspacePath, "runtime");
|
|
60
|
+
if (hasFreshRunningRuntimeMessage(runtimeRepo.messages))
|
|
61
|
+
return entry.metadata;
|
|
62
|
+
entry.metadata.runState = "aborted";
|
|
63
|
+
updateMetadataFromProvider(entry);
|
|
64
|
+
void touchConversationFromRuntime(entry.metadata);
|
|
65
|
+
store.cleanup(sessionKey);
|
|
66
|
+
void closeConversationSession(entry.metadata);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
57
69
|
export async function cleanupAcpSession(request) {
|
|
58
70
|
var _a;
|
|
59
71
|
const store = getGlobalAcpSessionStore();
|
|
@@ -72,9 +84,31 @@ export async function cleanupAcpSession(request) {
|
|
|
72
84
|
remainingSessions: store.list(),
|
|
73
85
|
};
|
|
74
86
|
}
|
|
87
|
+
export async function cancelAcpSession(request) {
|
|
88
|
+
var _a;
|
|
89
|
+
const store = getGlobalAcpSessionStore();
|
|
90
|
+
store.cleanupIdle();
|
|
91
|
+
const sessionKey = getSessionKeyForLookup(request);
|
|
92
|
+
const entry = store.get(sessionKey);
|
|
93
|
+
const metadata = (_a = entry === null || entry === void 0 ? void 0 : entry.metadata) !== null && _a !== void 0 ? _a : null;
|
|
94
|
+
if (entry) {
|
|
95
|
+
entry.metadata.runState = "aborted";
|
|
96
|
+
updateMetadataFromProvider(entry);
|
|
97
|
+
void touchConversationFromRuntime(entry.metadata);
|
|
98
|
+
await closeAcpProviderSession(entry.provider, entry.metadata);
|
|
99
|
+
}
|
|
100
|
+
const cleaned = store.cleanup(sessionKey);
|
|
101
|
+
void closeConversationSession(metadata);
|
|
102
|
+
return {
|
|
103
|
+
cancelled: Boolean(entry),
|
|
104
|
+
cleaned,
|
|
105
|
+
metadata,
|
|
106
|
+
remainingSessions: store.list(),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
75
109
|
export async function streamAcpChat(request, options = {}) {
|
|
76
110
|
var _a, _b, _c;
|
|
77
|
-
await
|
|
111
|
+
await persistRequestRuntimeMessages(request);
|
|
78
112
|
const externalFrontendTools = Object.assign({}, ((_a = request.tools) !== null && _a !== void 0 ? _a : {}));
|
|
79
113
|
delete externalFrontendTools[ACP_ELICITATION_OPTION_LIST_TOOL_NAME];
|
|
80
114
|
const tools = Object.assign(Object.assign({}, acpTools(frontendTools(externalFrontendTools))), frontendTools(INTERNAL_FRONTEND_TOOLS));
|
|
@@ -90,6 +124,16 @@ export async function streamAcpChat(request, options = {}) {
|
|
|
90
124
|
entry.metadata.runState = "error";
|
|
91
125
|
entry.metadata.warnings.push(elicitationStage.reason);
|
|
92
126
|
updateMetadataFromProvider(entry);
|
|
127
|
+
await persistRuntimeMessages({
|
|
128
|
+
request,
|
|
129
|
+
messages: request.messages,
|
|
130
|
+
runState: "error",
|
|
131
|
+
acpSessionId: entry.metadata.acpSessionId,
|
|
132
|
+
error: elicitationStage.reason,
|
|
133
|
+
}).catch((error) => {
|
|
134
|
+
entry.metadata.warnings.push(`Failed to persist stale elicitation runtime messages: ${error instanceof Error ? error.message : String(error)}`);
|
|
135
|
+
updateMetadataFromProvider(entry);
|
|
136
|
+
});
|
|
93
137
|
void touchConversationFromRuntime(entry.metadata);
|
|
94
138
|
return {
|
|
95
139
|
response: createDirectMessageResponse(entry.metadata, "ACP 选项请求已经失效,无法继续这次选择。"),
|
|
@@ -146,12 +190,26 @@ export async function streamAcpChat(request, options = {}) {
|
|
|
146
190
|
abortSignal: options.abortSignal,
|
|
147
191
|
});
|
|
148
192
|
const response = result.toUIMessageStreamResponse({
|
|
193
|
+
originalMessages: request.messages,
|
|
194
|
+
generateMessageId: () => `assistant-${Date.now().toString(36)}-${Math.random()
|
|
195
|
+
.toString(36)
|
|
196
|
+
.slice(2)}`,
|
|
149
197
|
headers: createRuntimeHeaders(entry.metadata),
|
|
150
198
|
consumeSseStream: consumeStream,
|
|
151
199
|
messageMetadata: ({ part }) => createAcpMessageMetadata(part, entry),
|
|
152
|
-
onFinish: ({ isAborted }) => {
|
|
200
|
+
onFinish: async ({ isAborted, responseMessage }) => {
|
|
153
201
|
var _a;
|
|
154
202
|
entry.metadata.runState = isAborted ? "aborted" : "completed";
|
|
203
|
+
await persistRuntimeMessages({
|
|
204
|
+
request,
|
|
205
|
+
messages: [...request.messages, responseMessage],
|
|
206
|
+
runState: entry.metadata.runState,
|
|
207
|
+
acpSessionId: entry.metadata.acpSessionId,
|
|
208
|
+
runMetadataMessageId: responseMessage.id,
|
|
209
|
+
}).catch((error) => {
|
|
210
|
+
entry.metadata.warnings.push(`Failed to persist ${entry.metadata.runState} runtime messages: ${error instanceof Error ? error.message : String(error)}`);
|
|
211
|
+
updateMetadataFromProvider(entry);
|
|
212
|
+
});
|
|
155
213
|
updateMetadataFromProvider(entry);
|
|
156
214
|
void touchConversationFromRuntime(entry.metadata);
|
|
157
215
|
if (isAborted) {
|
|
@@ -166,17 +224,35 @@ export async function streamAcpChat(request, options = {}) {
|
|
|
166
224
|
},
|
|
167
225
|
onError: (error) => {
|
|
168
226
|
var _a;
|
|
227
|
+
const message = acpErrorToMessage(error);
|
|
169
228
|
if (entry.metadata.runState !== "aborted") {
|
|
170
229
|
entry.metadata.runState = "error";
|
|
171
230
|
}
|
|
231
|
+
void persistRuntimeMessages({
|
|
232
|
+
request,
|
|
233
|
+
messages: request.messages,
|
|
234
|
+
runState: entry.metadata.runState,
|
|
235
|
+
acpSessionId: entry.metadata.acpSessionId,
|
|
236
|
+
error: message,
|
|
237
|
+
}).catch((persistError) => {
|
|
238
|
+
entry.metadata.warnings.push(`Failed to persist errored runtime messages: ${persistError instanceof Error
|
|
239
|
+
? persistError.message
|
|
240
|
+
: String(persistError)}`);
|
|
241
|
+
updateMetadataFromProvider(entry);
|
|
242
|
+
});
|
|
172
243
|
updateMetadataFromProvider(entry);
|
|
173
244
|
void touchConversationFromRuntime(entry.metadata);
|
|
174
245
|
(_a = options.abortSignal) === null || _a === void 0 ? void 0 : _a.removeEventListener("abort", markAborted);
|
|
175
|
-
return
|
|
246
|
+
return message;
|
|
176
247
|
},
|
|
177
248
|
});
|
|
178
|
-
|
|
249
|
+
const resumableResponse = await createResumableUIMessageStreamResponse({
|
|
250
|
+
request,
|
|
251
|
+
metadata: entry.metadata,
|
|
179
252
|
response,
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
response: resumableResponse,
|
|
180
256
|
metadata: updateMetadataFromProvider(entry),
|
|
181
257
|
};
|
|
182
258
|
}
|
|
@@ -345,11 +345,13 @@ function patchPromptTracking(model) {
|
|
|
345
345
|
model.promptWithLazyAuthRetry = (request) => {
|
|
346
346
|
const promptPromise = originalPromptWithLazyAuthRetry(request);
|
|
347
347
|
model.__acpUiPromptPromise = promptPromise;
|
|
348
|
-
void promptPromise
|
|
348
|
+
void promptPromise
|
|
349
|
+
.finally(() => {
|
|
349
350
|
if (model.__acpUiPromptPromise === promptPromise) {
|
|
350
351
|
model.__acpUiPromptPromise = null;
|
|
351
352
|
}
|
|
352
|
-
})
|
|
353
|
+
})
|
|
354
|
+
.catch(() => { });
|
|
353
355
|
return promptPromise;
|
|
354
356
|
};
|
|
355
357
|
}
|
|
@@ -22,15 +22,14 @@ export const ACP_PROVIDER_REGISTRY = {
|
|
|
22
22
|
defaultModel: "sonnet",
|
|
23
23
|
defaultModeId: ACP_PROVIDER_DEFAULT_MODE_IDS.claude,
|
|
24
24
|
supportsImages: true,
|
|
25
|
-
enabledInP0: false,
|
|
26
25
|
},
|
|
27
26
|
codex: {
|
|
28
27
|
provider: "codex",
|
|
29
28
|
label: "Codex",
|
|
30
|
-
command: "
|
|
29
|
+
command: "npx",
|
|
30
|
+
args: ["-y", "@zed-industries/codex-acp@^0.15.0"],
|
|
31
31
|
defaultModeId: ACP_PROVIDER_DEFAULT_MODE_IDS.codex,
|
|
32
32
|
supportsImages: true,
|
|
33
|
-
enabledInP0: true,
|
|
34
33
|
},
|
|
35
34
|
gemini: {
|
|
36
35
|
provider: "gemini",
|
|
@@ -40,7 +39,6 @@ export const ACP_PROVIDER_REGISTRY = {
|
|
|
40
39
|
defaultModel: "gemini-3-pro-preview",
|
|
41
40
|
defaultModeId: ACP_PROVIDER_DEFAULT_MODE_IDS.gemini,
|
|
42
41
|
supportsImages: false,
|
|
43
|
-
enabledInP0: false,
|
|
44
42
|
},
|
|
45
43
|
opencode: {
|
|
46
44
|
provider: "opencode",
|
|
@@ -50,7 +48,6 @@ export const ACP_PROVIDER_REGISTRY = {
|
|
|
50
48
|
defaultModel: "opencode/big-pickle",
|
|
51
49
|
defaultModeId: ACP_PROVIDER_DEFAULT_MODE_IDS.opencode,
|
|
52
50
|
supportsImages: false,
|
|
53
|
-
enabledInP0: false,
|
|
54
51
|
},
|
|
55
52
|
};
|
|
56
53
|
export function isAcpProviderKey(value) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AcpChatRequest, AcpRuntimeMetadata } from "./types";
|
|
2
|
+
type ResumableUIStreamOptions = {
|
|
3
|
+
request: AcpChatRequest;
|
|
4
|
+
metadata: AcpRuntimeMetadata;
|
|
5
|
+
response: Response;
|
|
6
|
+
};
|
|
7
|
+
export declare function createResumableUIMessageStreamResponse({ request, metadata, response, }: ResumableUIStreamOptions): Promise<Response>;
|
|
8
|
+
export declare function resumeUIMessageStream(streamId: string, metadata?: AcpRuntimeMetadata | null): Promise<Response>;
|
|
9
|
+
export declare function deleteResumableUIMessageStream(streamId: string): Promise<void>;
|
|
10
|
+
export declare function hasResumableUIMessageStream(streamId: string): Promise<boolean>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createInMemoryResumableStreamStore, createResumableStreamContext, RESUMABLE_STREAM_ID_HEADER, } from "assistant-stream/resumable";
|
|
3
|
+
import { createRuntimeHeaders } from "./response.mjs";
|
|
4
|
+
import { persistPartialAssistantMessage } from "./runtime-persistence.mjs";
|
|
5
|
+
const RESUMABLE_STREAM_TTL_MS = 15 * 60 * 1000;
|
|
6
|
+
const PARTIAL_PERSIST_MIN_INTERVAL_MS = 750;
|
|
7
|
+
const PARTIAL_PERSIST_MIN_TEXT_DELTA = 24;
|
|
8
|
+
const MAX_SSE_EVENT_BYTES = 1024 * 1024;
|
|
9
|
+
const globalForResumableUIMessageStream = globalThis;
|
|
10
|
+
function getResumableRuntime() {
|
|
11
|
+
const existing = globalForResumableUIMessageStream.__acpUiResumableUIMessageStreamRuntime;
|
|
12
|
+
if (existing)
|
|
13
|
+
return existing;
|
|
14
|
+
const store = createInMemoryResumableStreamStore({
|
|
15
|
+
defaultTtlMs: RESUMABLE_STREAM_TTL_MS,
|
|
16
|
+
gcIntervalMs: RESUMABLE_STREAM_TTL_MS,
|
|
17
|
+
maxEntriesPerStream: 20000,
|
|
18
|
+
maxStreams: 200,
|
|
19
|
+
});
|
|
20
|
+
const runtime = {
|
|
21
|
+
context: createResumableStreamContext({
|
|
22
|
+
store,
|
|
23
|
+
ttlMs: RESUMABLE_STREAM_TTL_MS,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
globalForResumableUIMessageStream.__acpUiResumableUIMessageStreamRuntime =
|
|
27
|
+
runtime;
|
|
28
|
+
return runtime;
|
|
29
|
+
}
|
|
30
|
+
function createStreamId(metadata) {
|
|
31
|
+
return ["acp", metadata.provider, Date.now().toString(36), randomUUID()].join(":");
|
|
32
|
+
}
|
|
33
|
+
function mergeResponseHeaders(response, metadata, streamId) {
|
|
34
|
+
const headers = new Headers(response.headers);
|
|
35
|
+
for (const [key, value] of new Headers(createRuntimeHeaders(metadata))) {
|
|
36
|
+
headers.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
headers.set(RESUMABLE_STREAM_ID_HEADER, streamId);
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
function createMissingStreamResponse() {
|
|
42
|
+
return new Response(null, {
|
|
43
|
+
status: 204,
|
|
44
|
+
headers: {
|
|
45
|
+
"cache-control": "no-cache",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function createInitialAssistantMessage(request, metadata, streamId) {
|
|
50
|
+
const lastMessage = request.messages.at(-1);
|
|
51
|
+
if ((lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.role) === "assistant") {
|
|
52
|
+
return structuredClone(lastMessage);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
id: `assistant-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`,
|
|
56
|
+
role: "assistant",
|
|
57
|
+
metadata: {
|
|
58
|
+
custom: {
|
|
59
|
+
acpRun: {
|
|
60
|
+
status: "running",
|
|
61
|
+
threadId: metadata.threadId,
|
|
62
|
+
acpSessionId: metadata.acpSessionId,
|
|
63
|
+
error: null,
|
|
64
|
+
streamId,
|
|
65
|
+
updatedAt: Date.now(),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
parts: [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function createPartialState(request, metadata, streamId) {
|
|
73
|
+
return {
|
|
74
|
+
message: createInitialAssistantMessage(request, metadata, streamId),
|
|
75
|
+
streamId,
|
|
76
|
+
activeTextParts: {},
|
|
77
|
+
activeReasoningParts: {},
|
|
78
|
+
lastPersistedAt: 0,
|
|
79
|
+
lastPersistedTextLength: 0,
|
|
80
|
+
pendingPersist: Promise.resolve(),
|
|
81
|
+
dirty: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function isRecord(value) {
|
|
85
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
86
|
+
}
|
|
87
|
+
function mergeObjects(left, right) {
|
|
88
|
+
return Object.assign(Object.assign({}, left), right);
|
|
89
|
+
}
|
|
90
|
+
function mergeMessageMetadata(message, metadata) {
|
|
91
|
+
if (!isRecord(metadata))
|
|
92
|
+
return;
|
|
93
|
+
message.metadata = isRecord(message.metadata)
|
|
94
|
+
? mergeObjects(message.metadata, metadata)
|
|
95
|
+
: metadata;
|
|
96
|
+
}
|
|
97
|
+
function getTextLength(message) {
|
|
98
|
+
return message.parts.reduce((total, part) => {
|
|
99
|
+
if (part.type !== "text" && part.type !== "reasoning")
|
|
100
|
+
return total;
|
|
101
|
+
return total + part.text.length;
|
|
102
|
+
}, 0);
|
|
103
|
+
}
|
|
104
|
+
async function persistIfDue(state, request, metadata, force = false) {
|
|
105
|
+
if (!state.dirty && !force) {
|
|
106
|
+
await state.pendingPersist;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const textLength = getTextLength(state.message);
|
|
111
|
+
const shouldPersist = force ||
|
|
112
|
+
now - state.lastPersistedAt >= PARTIAL_PERSIST_MIN_INTERVAL_MS ||
|
|
113
|
+
textLength - state.lastPersistedTextLength >=
|
|
114
|
+
PARTIAL_PERSIST_MIN_TEXT_DELTA;
|
|
115
|
+
if (!shouldPersist || textLength === 0) {
|
|
116
|
+
await state.pendingPersist;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const snapshot = structuredClone(state.message);
|
|
120
|
+
state.dirty = false;
|
|
121
|
+
state.lastPersistedAt = now;
|
|
122
|
+
state.lastPersistedTextLength = textLength;
|
|
123
|
+
state.pendingPersist = state.pendingPersist
|
|
124
|
+
.then(() => persistPartialAssistantMessage({
|
|
125
|
+
request,
|
|
126
|
+
message: snapshot,
|
|
127
|
+
acpSessionId: metadata.acpSessionId,
|
|
128
|
+
streamId: state.streamId,
|
|
129
|
+
}))
|
|
130
|
+
.catch((error) => {
|
|
131
|
+
metadata.warnings.push(`Failed to persist partial runtime message: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
+
});
|
|
133
|
+
await state.pendingPersist;
|
|
134
|
+
}
|
|
135
|
+
async function flushPersist(state) {
|
|
136
|
+
await state.pendingPersist.catch(() => { });
|
|
137
|
+
}
|
|
138
|
+
function applyUIMessageChunk(state, chunk) {
|
|
139
|
+
switch (chunk.type) {
|
|
140
|
+
case "start":
|
|
141
|
+
if (chunk.messageId)
|
|
142
|
+
state.message.id = chunk.messageId;
|
|
143
|
+
mergeMessageMetadata(state.message, chunk.messageMetadata);
|
|
144
|
+
state.dirty = true;
|
|
145
|
+
break;
|
|
146
|
+
case "text-start": {
|
|
147
|
+
const part = Object.assign({ type: "text", text: "", state: "streaming" }, (chunk.providerMetadata
|
|
148
|
+
? { providerMetadata: chunk.providerMetadata }
|
|
149
|
+
: {}));
|
|
150
|
+
state.activeTextParts[chunk.id] = part;
|
|
151
|
+
state.message.parts.push(part);
|
|
152
|
+
state.dirty = true;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "text-delta": {
|
|
156
|
+
let part = state.activeTextParts[chunk.id];
|
|
157
|
+
if (!part) {
|
|
158
|
+
part = { type: "text", text: "", state: "streaming" };
|
|
159
|
+
state.activeTextParts[chunk.id] = part;
|
|
160
|
+
state.message.parts.push(part);
|
|
161
|
+
}
|
|
162
|
+
part.text += chunk.delta;
|
|
163
|
+
if (chunk.providerMetadata)
|
|
164
|
+
part.providerMetadata = chunk.providerMetadata;
|
|
165
|
+
state.dirty = true;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case "text-end": {
|
|
169
|
+
const part = state.activeTextParts[chunk.id];
|
|
170
|
+
if (part) {
|
|
171
|
+
part.state = "done";
|
|
172
|
+
if (chunk.providerMetadata)
|
|
173
|
+
part.providerMetadata = chunk.providerMetadata;
|
|
174
|
+
delete state.activeTextParts[chunk.id];
|
|
175
|
+
state.dirty = true;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case "reasoning-start": {
|
|
180
|
+
const part = Object.assign({ type: "reasoning", text: "", state: "streaming" }, (chunk.providerMetadata
|
|
181
|
+
? { providerMetadata: chunk.providerMetadata }
|
|
182
|
+
: {}));
|
|
183
|
+
state.activeReasoningParts[chunk.id] = part;
|
|
184
|
+
state.message.parts.push(part);
|
|
185
|
+
state.dirty = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case "reasoning-delta": {
|
|
189
|
+
let part = state.activeReasoningParts[chunk.id];
|
|
190
|
+
if (!part) {
|
|
191
|
+
part = { type: "reasoning", text: "", state: "streaming" };
|
|
192
|
+
state.activeReasoningParts[chunk.id] = part;
|
|
193
|
+
state.message.parts.push(part);
|
|
194
|
+
}
|
|
195
|
+
part.text += chunk.delta;
|
|
196
|
+
if (chunk.providerMetadata)
|
|
197
|
+
part.providerMetadata = chunk.providerMetadata;
|
|
198
|
+
state.dirty = true;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
case "reasoning-end": {
|
|
202
|
+
const part = state.activeReasoningParts[chunk.id];
|
|
203
|
+
if (part) {
|
|
204
|
+
part.state = "done";
|
|
205
|
+
if (chunk.providerMetadata)
|
|
206
|
+
part.providerMetadata = chunk.providerMetadata;
|
|
207
|
+
delete state.activeReasoningParts[chunk.id];
|
|
208
|
+
state.dirty = true;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case "message-metadata":
|
|
213
|
+
mergeMessageMetadata(state.message, chunk.messageMetadata);
|
|
214
|
+
state.dirty = true;
|
|
215
|
+
break;
|
|
216
|
+
case "finish":
|
|
217
|
+
mergeMessageMetadata(state.message, chunk.messageMetadata);
|
|
218
|
+
for (const part of Object.values(state.activeTextParts)) {
|
|
219
|
+
part.state = "done";
|
|
220
|
+
}
|
|
221
|
+
for (const part of Object.values(state.activeReasoningParts)) {
|
|
222
|
+
part.state = "done";
|
|
223
|
+
}
|
|
224
|
+
state.activeTextParts = {};
|
|
225
|
+
state.activeReasoningParts = {};
|
|
226
|
+
state.dirty = true;
|
|
227
|
+
break;
|
|
228
|
+
case "finish-step":
|
|
229
|
+
for (const part of Object.values(state.activeTextParts)) {
|
|
230
|
+
part.state = "done";
|
|
231
|
+
}
|
|
232
|
+
for (const part of Object.values(state.activeReasoningParts)) {
|
|
233
|
+
part.state = "done";
|
|
234
|
+
}
|
|
235
|
+
state.activeTextParts = {};
|
|
236
|
+
state.activeReasoningParts = {};
|
|
237
|
+
state.dirty = true;
|
|
238
|
+
break;
|
|
239
|
+
default:
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function createSsePartialPersistenceTap(request, metadata, streamId) {
|
|
244
|
+
const state = createPartialState(request, metadata, streamId);
|
|
245
|
+
const decoder = new TextDecoder();
|
|
246
|
+
let buffer = "";
|
|
247
|
+
async function processEvent(rawEvent) {
|
|
248
|
+
for (const line of rawEvent.split(/\r?\n/)) {
|
|
249
|
+
if (!line.startsWith("data:"))
|
|
250
|
+
continue;
|
|
251
|
+
const rawData = line.slice("data:".length).trim();
|
|
252
|
+
if (!rawData || rawData === "[DONE]")
|
|
253
|
+
continue;
|
|
254
|
+
try {
|
|
255
|
+
applyUIMessageChunk(state, JSON.parse(rawData));
|
|
256
|
+
await persistIfDue(state, request, metadata);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
metadata.warnings.push(`Failed to observe runtime stream chunk: ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async function drainEvents() {
|
|
264
|
+
let separatorIndex = buffer.indexOf("\n\n");
|
|
265
|
+
while (separatorIndex !== -1) {
|
|
266
|
+
await processEvent(buffer.slice(0, separatorIndex));
|
|
267
|
+
buffer = buffer.slice(separatorIndex + 2);
|
|
268
|
+
if (buffer.length > MAX_SSE_EVENT_BYTES) {
|
|
269
|
+
buffer = buffer.slice(-MAX_SSE_EVENT_BYTES);
|
|
270
|
+
}
|
|
271
|
+
separatorIndex = buffer.indexOf("\n\n");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return new TransformStream({
|
|
275
|
+
async transform(chunk, controller) {
|
|
276
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
277
|
+
await drainEvents();
|
|
278
|
+
controller.enqueue(chunk);
|
|
279
|
+
},
|
|
280
|
+
async flush() {
|
|
281
|
+
buffer += decoder.decode();
|
|
282
|
+
if (buffer.trim())
|
|
283
|
+
await processEvent(buffer);
|
|
284
|
+
await persistIfDue(state, request, metadata, true);
|
|
285
|
+
await flushPersist(state);
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
export async function createResumableUIMessageStreamResponse({ request, metadata, response, }) {
|
|
290
|
+
if (!response.body)
|
|
291
|
+
return response;
|
|
292
|
+
const streamId = createStreamId(metadata);
|
|
293
|
+
const sourceStream = response.body.pipeThrough(createSsePartialPersistenceTap(request, metadata, streamId));
|
|
294
|
+
const stream = await getResumableRuntime().context.run(streamId, () => sourceStream);
|
|
295
|
+
return new Response(stream, {
|
|
296
|
+
status: response.status,
|
|
297
|
+
statusText: response.statusText,
|
|
298
|
+
headers: mergeResponseHeaders(response, metadata, streamId),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
export async function resumeUIMessageStream(streamId, metadata) {
|
|
302
|
+
const stream = await getResumableRuntime().context.resume(streamId);
|
|
303
|
+
if (!stream)
|
|
304
|
+
return createMissingStreamResponse();
|
|
305
|
+
const headers = metadata
|
|
306
|
+
? new Headers(createRuntimeHeaders(metadata))
|
|
307
|
+
: new Headers();
|
|
308
|
+
headers.set("content-type", "text/event-stream");
|
|
309
|
+
headers.set("cache-control", "no-cache");
|
|
310
|
+
headers.set("connection", "keep-alive");
|
|
311
|
+
headers.set("x-vercel-ai-ui-message-stream", "v1");
|
|
312
|
+
headers.set("x-accel-buffering", "no");
|
|
313
|
+
headers.set(RESUMABLE_STREAM_ID_HEADER, streamId);
|
|
314
|
+
return new Response(stream, { headers });
|
|
315
|
+
}
|
|
316
|
+
export async function deleteResumableUIMessageStream(streamId) {
|
|
317
|
+
await getResumableRuntime().context.delete(streamId);
|
|
318
|
+
}
|
|
319
|
+
export async function hasResumableUIMessageStream(streamId) {
|
|
320
|
+
return ((await getResumableRuntime()
|
|
321
|
+
.context.status(streamId)
|
|
322
|
+
.catch(() => "missing")) !== "missing");
|
|
323
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ConversationMessageRepository } from "../conversations/types";
|
|
2
|
+
type RuntimeMessageFilterOptions = {
|
|
3
|
+
hideResumable?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function hideLivePartialAssistantMessages<TContent extends Record<string, unknown>>(repository: ConversationMessageRepository<TContent>, options?: RuntimeMessageFilterOptions): Promise<ConversationMessageRepository<TContent>>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getLivePartialStreamId, recoverStaleRunningRuntimeMessages, } from "../conversations/runtime-message-recovery.mjs";
|
|
2
|
+
import { hasResumableUIMessageStream } from "./resumable-ui-stream.mjs";
|
|
3
|
+
export async function hideLivePartialAssistantMessages(repository, options = {}) {
|
|
4
|
+
var _a, _b;
|
|
5
|
+
const messages = [];
|
|
6
|
+
let changed = false;
|
|
7
|
+
const hideResumable = options.hideResumable !== false;
|
|
8
|
+
const recovered = recoverStaleRunningRuntimeMessages(repository);
|
|
9
|
+
for (const message of recovered.repository.messages) {
|
|
10
|
+
const streamId = getLivePartialStreamId(message.content);
|
|
11
|
+
if (hideResumable &&
|
|
12
|
+
streamId &&
|
|
13
|
+
(await hasResumableUIMessageStream(streamId))) {
|
|
14
|
+
changed = true;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
messages.push(message);
|
|
18
|
+
}
|
|
19
|
+
if (!changed && !recovered.changed)
|
|
20
|
+
return repository;
|
|
21
|
+
return Object.assign(Object.assign({}, recovered.repository), { headId: (_b = (_a = messages.at(-1)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null, messages });
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UIMessage } from "ai";
|
|
2
|
+
import type { AcpChatRequest, AcpRunState } from "./types";
|
|
3
|
+
type PersistRuntimeMessagesOptions = {
|
|
4
|
+
request: AcpChatRequest;
|
|
5
|
+
messages: UIMessage[];
|
|
6
|
+
runState: AcpRunState;
|
|
7
|
+
acpSessionId?: string | null;
|
|
8
|
+
error?: string | null;
|
|
9
|
+
runMetadataMessageId?: string | null;
|
|
10
|
+
streamId?: string | null;
|
|
11
|
+
};
|
|
12
|
+
type PersistPartialAssistantMessageOptions = {
|
|
13
|
+
request: AcpChatRequest;
|
|
14
|
+
message: UIMessage;
|
|
15
|
+
acpSessionId?: string | null;
|
|
16
|
+
streamId?: string | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function persistRuntimeMessages({ request, messages, runState, acpSessionId, error, runMetadataMessageId, streamId, }: PersistRuntimeMessagesOptions): Promise<void>;
|
|
19
|
+
export declare function persistRequestRuntimeMessages(request: AcpChatRequest): Promise<void>;
|
|
20
|
+
export declare function persistPartialAssistantMessage({ request, message, acpSessionId, streamId, }: PersistPartialAssistantMessageOptions): Promise<void>;
|
|
21
|
+
export {};
|