@axhub/acp 0.1.0 → 0.1.1

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.
Files changed (203) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +4 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/next-minimal-server.js.nft.json +1 -1
  6. package/.next/next-server.js.nft.json +1 -1
  7. package/.next/routes-manifest.json +28 -0
  8. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  21. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next/server/app/api/acp/capabilities/route.js +5 -3
  27. package/.next/server/app/api/acp/capabilities/route.js.nft.json +1 -1
  28. package/.next/server/app/api/acp/commands/route.js +1 -1
  29. package/.next/server/app/api/acp/commands/route.js.nft.json +1 -1
  30. package/.next/server/app/api/acp/runtime/route.js.nft.json +1 -1
  31. package/.next/server/app/api/chat/cancel/route/app-paths-manifest.json +3 -0
  32. package/.next/server/app/api/chat/cancel/route/build-manifest.json +9 -0
  33. package/.next/server/app/api/chat/cancel/route/server-reference-manifest.json +4 -0
  34. package/.next/server/app/api/chat/cancel/route.js +11 -0
  35. package/.next/server/app/api/chat/cancel/route.js.nft.json +1 -0
  36. package/.next/server/app/api/chat/cancel/route_client-reference-manifest.js +3 -0
  37. package/.next/server/app/api/chat/resume/[streamId]/route/app-paths-manifest.json +3 -0
  38. package/.next/server/app/api/chat/resume/[streamId]/route/build-manifest.json +9 -0
  39. package/.next/server/app/api/chat/resume/[streamId]/route/server-reference-manifest.json +4 -0
  40. package/.next/server/app/api/chat/resume/[streamId]/route.js +11 -0
  41. package/.next/server/app/api/chat/resume/[streamId]/route.js.nft.json +1 -0
  42. package/.next/server/app/api/chat/resume/[streamId]/route_client-reference-manifest.js +3 -0
  43. package/.next/server/app/api/chat/route.js +5 -3
  44. package/.next/server/app/api/chat/route.js.nft.json +1 -1
  45. package/.next/server/app/api/conversations/[threadId]/messages/route.js +4 -2
  46. package/.next/server/app/api/conversations/[threadId]/messages/route.js.nft.json +1 -1
  47. package/.next/server/app/api/conversations/[threadId]/route.js +2 -2
  48. package/.next/server/app/api/conversations/[threadId]/route.js.nft.json +1 -1
  49. package/.next/server/app/api/conversations/[threadId]/runtime/route/app-paths-manifest.json +3 -0
  50. package/.next/server/app/api/conversations/[threadId]/runtime/route/build-manifest.json +9 -0
  51. package/.next/server/app/api/conversations/[threadId]/runtime/route/server-reference-manifest.json +4 -0
  52. package/.next/server/app/api/conversations/[threadId]/runtime/route.js +11 -0
  53. package/.next/server/app/api/conversations/[threadId]/runtime/route.js.nft.json +1 -0
  54. package/.next/server/app/api/conversations/[threadId]/runtime/route_client-reference-manifest.js +3 -0
  55. package/.next/server/app/api/conversations/route.js +2 -2
  56. package/.next/server/app/api/conversations/route.js.nft.json +1 -1
  57. package/.next/server/app/api/local-files/image/route.js.nft.json +1 -1
  58. package/.next/server/app/api/local-files/open/route.js.nft.json +1 -1
  59. package/.next/server/app/api/output-artifacts/thread/route/app-paths-manifest.json +3 -0
  60. package/.next/server/app/api/output-artifacts/thread/route/build-manifest.json +9 -0
  61. package/.next/server/app/api/output-artifacts/thread/route/server-reference-manifest.json +4 -0
  62. package/.next/server/app/api/output-artifacts/thread/route.js +8 -0
  63. package/.next/server/app/api/output-artifacts/thread/route.js.nft.json +1 -0
  64. package/.next/server/app/api/output-artifacts/thread/route_client-reference-manifest.js +3 -0
  65. package/.next/server/app/api/output-artifacts/workspace/route.js +1 -1
  66. package/.next/server/app/api/output-artifacts/workspace/route.js.nft.json +1 -1
  67. package/.next/server/app/api/tools/image-generation/files/[id]/route.js.nft.json +1 -1
  68. package/.next/server/app/api/tools/image-generation/records/route.js.nft.json +1 -1
  69. package/.next/server/app/api/tools/user-choice/route.js.nft.json +1 -1
  70. package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  71. package/.next/server/app/page.js +2 -2
  72. package/.next/server/app/page.js.nft.json +1 -1
  73. package/.next/server/app/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/session/[provider]/[sessionId]/page.js +1 -1
  75. package/.next/server/app/session/[provider]/[sessionId]/page.js.nft.json +1 -1
  76. package/.next/server/app/session/[provider]/[sessionId]/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/thread/[threadId]/page.js +1 -1
  78. package/.next/server/app/thread/[threadId]/page.js.nft.json +1 -1
  79. package/.next/server/app/thread/[threadId]/page_client-reference-manifest.js +1 -1
  80. package/.next/server/app-paths-manifest.json +4 -0
  81. package/.next/server/chunks/0zjb_server_app_api_conversations_[threadId]_runtime_route_actions_08lhdqs.js +3 -0
  82. package/.next/server/chunks/[root-of-the-server]__04pn6ap._.js +3 -0
  83. package/.next/server/chunks/[root-of-the-server]__0aovkxs._.js +3 -0
  84. package/.next/server/chunks/[root-of-the-server]__0c.r6ru._.js +76 -0
  85. package/.next/server/chunks/[root-of-the-server]__0gmxr~m._.js +3 -0
  86. package/.next/server/chunks/[root-of-the-server]__0iokgmz._.js +1 -1
  87. package/.next/server/chunks/[root-of-the-server]__0j-lxr4._.js +1 -1
  88. package/.next/server/chunks/[root-of-the-server]__0lbwo2g._.js +3 -0
  89. package/.next/server/chunks/[root-of-the-server]__0ly6hop._.js +1 -1
  90. package/.next/server/chunks/[root-of-the-server]__0ml.1wa._.js +3 -0
  91. package/.next/server/chunks/[root-of-the-server]__0o2epta._.js +3 -0
  92. package/.next/server/chunks/[root-of-the-server]__0os92l7._.js +3 -0
  93. package/.next/server/chunks/[root-of-the-server]__0tmhg7j._.js +1 -1
  94. package/.next/server/chunks/[root-of-the-server]__0txmfnw._.js +2 -2
  95. package/.next/server/chunks/[root-of-the-server]__0wo0b8z._.js +1 -1
  96. package/.next/server/chunks/[root-of-the-server]__0xh8d4~._.js +3 -0
  97. package/.next/server/chunks/[root-of-the-server]__0zmyki-._.js +3 -0
  98. package/.next/server/chunks/[root-of-the-server]__0zn3~pq._.js +3 -0
  99. package/.next/server/chunks/[root-of-the-server]__13xepwb._.js +3 -0
  100. package/.next/server/chunks/_0_9_730._.js +107 -0
  101. package/.next/server/chunks/_0gx~6n6._.js +107 -0
  102. package/.next/server/chunks/_next-internal_server_app_api_chat_cancel_route_actions_0hdg4o_.js +3 -0
  103. package/.next/server/chunks/_next-internal_server_app_api_chat_resume_[streamId]_route_actions_12ynw6q.js +3 -0
  104. package/.next/server/chunks/_next-internal_server_app_api_output-artifacts_thread_route_actions_04~2mo7.js +3 -0
  105. package/.next/server/chunks/lib_conversations_store_ts_0gzcj38._.js +4 -4
  106. package/.next/server/chunks/node_modules_0nyqhq8._.js +3 -0
  107. package/.next/server/chunks/ssr/{[root-of-the-server]__0piffp7._.js → [root-of-the-server]__09wwymw._.js} +2 -2
  108. package/.next/server/chunks/ssr/{[root-of-the-server]__0488vn3._.js → [root-of-the-server]__0n6oe29._.js} +1 -1
  109. package/.next/server/chunks/ssr/{[root-of-the-server]__0icm-_h._.js → [root-of-the-server]__0niwg81._.js} +2 -2
  110. package/.next/server/chunks/ssr/_03.pm1z._.js +18 -16
  111. package/.next/server/chunks/ssr/_0txwi90._.js +1 -1
  112. package/.next/server/chunks/ssr/lib_conversations_store_ts_0-pd6d3._.js +2 -2
  113. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0ghu-f7.js +1 -1
  114. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_02suzhc.js +2 -2
  115. package/.next/server/functions-config-manifest.json +3 -0
  116. package/.next/server/instrumentation.js.nft.json +1 -1
  117. package/.next/server/middleware-build-manifest.js +3 -3
  118. package/.next/server/pages/404.html +1 -1
  119. package/.next/server/pages/500.html +1 -1
  120. package/.next/static/chunks/0btc2281yau9i.js +104 -0
  121. package/.next/static/chunks/0c~b2_-vk17t5.css +1 -0
  122. package/README.md +2 -2
  123. package/dist/components/assistant-ui/acp-command-menu.mjs +42 -9
  124. package/dist/components/assistant-ui/acp-elicitation-option-list.mjs +1 -1
  125. package/dist/components/assistant-ui/file.mjs +2 -2
  126. package/dist/components/assistant-ui/image-generation-settings-dialog.mjs +25 -6
  127. package/dist/components/assistant-ui/image.mjs +2 -2
  128. package/dist/components/assistant-ui/markdown-text.mjs +3 -3
  129. package/dist/components/assistant-ui/thread/composer.d.ts +1 -1
  130. package/dist/components/assistant-ui/thread/composer.mjs +37 -3
  131. package/dist/components/assistant-ui/thread/context-chips.mjs +3 -2
  132. package/dist/components/assistant-ui/thread/index.mjs +1 -1
  133. package/dist/components/assistant-ui/thread/message-list.mjs +1 -1
  134. package/dist/components/assistant-ui/thread/messages.mjs +2 -2
  135. package/dist/components/assistant-ui/thread-list.d.ts +8 -2
  136. package/dist/components/assistant-ui/thread-list.mjs +80 -7
  137. package/dist/components/assistant-ui/threadlist-sidebar.d.ts +4 -1
  138. package/dist/components/assistant-ui/threadlist-sidebar.mjs +2 -2
  139. package/dist/components/assistant-ui/tool-fallback.d.ts +1 -1
  140. package/dist/components/assistant-ui/tool-fallback.mjs +48 -11
  141. package/dist/components/tool-ui/option-list.d.ts +2 -1
  142. package/dist/components/tool-ui/option-list.mjs +4 -4
  143. package/dist/lib/acp2aisdk/client-context.d.ts +6 -0
  144. package/dist/lib/acp2aisdk/client-context.mjs +43 -7
  145. package/dist/lib/acp2aisdk/commands.mjs +5 -20
  146. package/dist/lib/acp2aisdk/context.d.ts +4 -0
  147. package/dist/lib/acp2aisdk/errors.d.ts +2 -0
  148. package/dist/lib/acp2aisdk/errors.mjs +37 -0
  149. package/dist/lib/acp2aisdk/index.d.ts +7 -0
  150. package/dist/lib/acp2aisdk/index.mjs +110 -34
  151. package/dist/lib/acp2aisdk/provider-compat.mjs +4 -2
  152. package/dist/lib/acp2aisdk/resumable-ui-stream.d.ts +11 -0
  153. package/dist/lib/acp2aisdk/resumable-ui-stream.mjs +323 -0
  154. package/dist/lib/acp2aisdk/runtime-message-filter.d.ts +6 -0
  155. package/dist/lib/acp2aisdk/runtime-message-filter.mjs +22 -0
  156. package/dist/lib/acp2aisdk/runtime-persistence.d.ts +21 -0
  157. package/dist/lib/acp2aisdk/runtime-persistence.mjs +135 -0
  158. package/dist/lib/acp2aisdk/session-store.mjs +7 -1
  159. package/dist/lib/acp2aisdk/skill-command-cache.mjs +1 -1
  160. package/dist/lib/api/client.d.ts +51 -4
  161. package/dist/lib/api/client.mjs +35 -4
  162. package/dist/lib/api/cors.mjs +7 -1
  163. package/dist/lib/api/http-response.d.ts +2 -0
  164. package/dist/lib/api/http-response.mjs +22 -0
  165. package/dist/lib/conversations/client-adapter.d.ts +1 -0
  166. package/dist/lib/conversations/client-adapter.mjs +158 -51
  167. package/dist/lib/conversations/provider-message-loader.d.ts +7 -0
  168. package/dist/lib/conversations/provider-message-loader.mjs +99 -0
  169. package/dist/lib/conversations/runtime-message-recovery.d.ts +9 -0
  170. package/dist/lib/conversations/runtime-message-recovery.mjs +95 -0
  171. package/dist/lib/conversations/store.d.ts +2 -2
  172. package/dist/lib/conversations/store.mjs +49 -149
  173. package/dist/lib/conversations/title-text.d.ts +3 -0
  174. package/dist/lib/conversations/title-text.mjs +81 -0
  175. package/dist/lib/conversations/types.d.ts +12 -1
  176. package/dist/lib/local-image-files.mjs +9 -1
  177. package/dist/lib/local-image-paths.mjs +31 -6
  178. package/dist/lib/output-artifacts/thread.d.ts +22 -0
  179. package/dist/lib/output-artifacts/thread.mjs +47 -0
  180. package/dist/lib/output-artifacts/workspace.mjs +6 -2
  181. package/dist/lib/provider-history/codex.mjs +5 -30
  182. package/dist/public-api/server.d.ts +1 -1
  183. package/dist/public-api/server.mjs +1 -1
  184. package/dist/tools/image-generation/client.mjs +6 -4
  185. package/dist/tools/image-generation/server.mjs +3 -1
  186. package/dist/tools/image-generation/shared.d.ts +1 -0
  187. package/dist/tools/image-generation/shared.mjs +4 -0
  188. package/dist/tools/image-generation/ui-detail.mjs +66 -2
  189. package/dist/tools/user-choice/ui.mjs +66 -30
  190. package/package.json +2 -1
  191. package/.next/server/chunks/[root-of-the-server]__04xq..~._.js +0 -3
  192. package/.next/server/chunks/[root-of-the-server]__07sxz4_._.js +0 -3
  193. package/.next/server/chunks/[root-of-the-server]__0dwg3fr._.js +0 -178
  194. package/.next/server/chunks/[root-of-the-server]__0eanzwb._.js +0 -3
  195. package/.next/server/chunks/[root-of-the-server]__0gqx~5k._.js +0 -3
  196. package/.next/server/chunks/[root-of-the-server]__0~mtsby._.js +0 -3
  197. package/.next/server/chunks/[root-of-the-server]__10-n4io._.js +0 -3
  198. package/.next/server/chunks/[root-of-the-server]__10g507v._.js +0 -3
  199. package/.next/static/chunks/0zftsky7gte_9.js +0 -102
  200. package/.next/static/chunks/1610ha42i.fl~.css +0 -1
  201. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → Kri5x_Y9TwyCw9FEY15ME}/_buildManifest.js +0 -0
  202. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → Kri5x_Y9TwyCw9FEY15ME}/_clientMiddlewareManifest.js +0 -0
  203. /package/.next/static/{mbk_N5Gs4ZJg3lciRL6ya → Kri5x_Y9TwyCw9FEY15ME}/_ssgManifest.js +0 -0
@@ -2,11 +2,18 @@ import type { AcpChatRequest, AcpRuntimeMetadata, AcpSessionLookupRequest, AcpSt
2
2
  export type { AcpBuiltinToolSettings, AcpCapabilitySnapshot, AcpChatRequest, AcpCleanupResult, AcpPermissionMode, AcpProviderKey, AcpRunState, AcpRuntimeMetadata, AcpSessionLookupRequest, } from "./types";
3
3
  export declare function getAcpSessionMetadata(request: AcpSessionLookupRequest): AcpRuntimeMetadata | null;
4
4
  export declare function listAcpSessionMetadata(): AcpRuntimeMetadata[];
5
+ export declare function reconcileAcpSessionWithRuntimeMessages(request: AcpSessionLookupRequest): Promise<AcpRuntimeMetadata | null>;
5
6
  export declare function cleanupAcpSession(request: AcpSessionLookupRequest): Promise<{
6
7
  cleaned: boolean;
7
8
  metadata: AcpRuntimeMetadata | null;
8
9
  remainingSessions: AcpRuntimeMetadata[];
9
10
  }>;
11
+ export declare function cancelAcpSession(request: AcpSessionLookupRequest): Promise<{
12
+ cancelled: boolean;
13
+ cleaned: boolean;
14
+ metadata: AcpRuntimeMetadata | null;
15
+ remainingSessions: AcpRuntimeMetadata[];
16
+ }>;
10
17
  export declare function streamAcpChat(request: AcpChatRequest, options?: {
11
18
  abortSignal?: AbortSignal;
12
19
  }): Promise<AcpStreamChatResult>;
@@ -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, touchConversationFromRuntime, upsertConversation, } from "../conversations/store.mjs";
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 { normalizePermissionMode } from "./runtime-options.mjs";
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, normalizeThreadId, normalizeWorkspacePath, } from "./session-store.mjs";
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
- async function persistBuiltinToolSettingsPatch(request) {
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 persistBuiltinToolSettingsPatch(request);
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 error instanceof Error ? error.message : String(error);
246
+ return message;
176
247
  },
177
248
  });
178
- return {
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.finally(() => {
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
  }
@@ -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 {};