@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
@@ -1,25 +1,48 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { RuntimeAdapterProvider, useAui, useAuiState, useRemoteThreadListRuntime, } from "@assistant-ui/react";
4
- import { AssistantChatTransport, useChatRuntime, } from "@assistant-ui/react-ai-sdk";
4
+ import { AssistantChatTransport, createResumableSessionStorage, useChatRuntime, } from "@assistant-ui/react-ai-sdk";
5
5
  import { lastAssistantMessageIsCompleteWithToolCalls, } from "ai";
6
6
  import { createAssistantStream } from "assistant-stream";
7
- import { useEffect, useMemo, useRef, } from "react";
7
+ import { useCallback, useEffect, useMemo, useRef, } from "react";
8
8
  import { ACP_CAPABILITY_REFRESH_EVENT } from "../acp2aisdk/capability-cache.mjs";
9
9
  import { getAcpProviderDefaultModeId } from "../acp2aisdk/provider-registry.mjs";
10
10
  import { acpApiClient } from "../api/client.mjs";
11
+ import { withoutBodyForNullBodyStatus } from "../api/http-response.mjs";
11
12
  import { getEnabledBuiltinToolIds } from "../../tools/client-registry.mjs";
13
+ import { getUserFacingMessageText } from "./title-text.mjs";
12
14
  const DEFAULT_PROVIDER = "codex";
13
15
  const LOCAL_THREAD_ID_PREFIX = "__LOCALID_";
14
16
  const THREAD_LIST_PAGE_SIZE = 50;
17
+ const RESUMABLE_STORAGE_PREFIX = "acp-ui:resumable";
15
18
  function createConversationThreadId(provider) {
16
19
  const random = typeof crypto !== "undefined" && "randomUUID" in crypto
17
20
  ? crypto.randomUUID()
18
21
  : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
19
22
  return `${provider}-${random}`;
20
23
  }
24
+ function sanitizeResumableStoragePart(value) {
25
+ return encodeURIComponent(value || "default");
26
+ }
27
+ function createResumableStorageKey({ workspacePath, provider, threadId, }) {
28
+ return [
29
+ RESUMABLE_STORAGE_PREFIX,
30
+ sanitizeResumableStoragePart(workspacePath),
31
+ sanitizeResumableStoragePart(provider !== null && provider !== void 0 ? provider : DEFAULT_PROVIDER),
32
+ sanitizeResumableStoragePart(threadId),
33
+ ].join(":");
34
+ }
35
+ function hasLocalPendingResumableStream({ workspacePath, provider, threadId, }) {
36
+ if (typeof window === "undefined")
37
+ return false;
38
+ return Boolean(window.sessionStorage.getItem(createResumableStorageKey({
39
+ workspacePath,
40
+ provider: provider !== null && provider !== void 0 ? provider : DEFAULT_PROVIDER,
41
+ threadId,
42
+ })));
43
+ }
21
44
  function toRemoteMetadata(thread) {
22
- var _a, _b;
45
+ var _a, _b, _c;
23
46
  return {
24
47
  remoteId: thread.threadId,
25
48
  status: thread.status === "archived" ? "archived" : "regular",
@@ -30,22 +53,27 @@ function toRemoteMetadata(thread) {
30
53
  modeId: thread.modeId,
31
54
  thoughtLevel: thread.thoughtLevel,
32
55
  workspacePath: thread.workspacePath,
33
- providerSessionId: (_b = thread.providerSessionId) !== null && _b !== void 0 ? _b : null,
56
+ updatedAt: thread.updatedAt,
57
+ lastActiveAt: (_b = thread.lastActiveAt) !== null && _b !== void 0 ? _b : null,
58
+ providerSessionId: (_c = thread.providerSessionId) !== null && _c !== void 0 ? _c : null,
34
59
  },
35
60
  };
36
61
  }
37
62
  function getTextFromThreadMessage(message) {
38
- return message.content
63
+ const text = message.content
39
64
  .filter((part) => part.type === "text")
40
65
  .map((part) => part.text)
41
66
  .join(" ")
42
67
  .trim();
68
+ return getUserFacingMessageText(text);
43
69
  }
44
70
  function isRecord(value) {
45
71
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
46
72
  }
47
73
  function normalizeTitleText(value, maxLength) {
48
- const title = typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
74
+ const title = typeof value === "string"
75
+ ? getUserFacingMessageText(value).replace(/\s+/g, " ").trim()
76
+ : "";
49
77
  return (maxLength ? title.slice(0, maxLength) : title).trim();
50
78
  }
51
79
  function normalizeStoredTitle(value) {
@@ -78,8 +106,38 @@ function createTitle(messages) {
78
106
  const firstUserMessage = messages.find((message) => message.role === "user" && getTextFromThreadMessage(message));
79
107
  const fallbackText = (firstUserMessage ? getTextFromThreadMessage(firstUserMessage) : "") ||
80
108
  getTextFromThreadMessage((_a = messages.find((message) => message.role !== "system")) !== null && _a !== void 0 ? _a : messages[0]);
81
- const title = normalizeTitleText(fallbackText, 48);
82
- return title;
109
+ return normalizeTitleText(fallbackText, 48);
110
+ }
111
+ function shouldStartRunFromAppendMessage(message) {
112
+ var _a;
113
+ return (_a = message.startRun) !== null && _a !== void 0 ? _a : message.role === "user";
114
+ }
115
+ function createAcpCreateMessage(message) {
116
+ var _a, _b;
117
+ const inputParts = [
118
+ ...message.content.filter((part) => part.type !== "file"),
119
+ ...((_b = (_a = message.attachments) === null || _a === void 0 ? void 0 : _a.flatMap((attachment) => attachment.content.map((part) => (Object.assign(Object.assign({}, part), { filename: attachment.name }))))) !== null && _b !== void 0 ? _b : []),
120
+ ];
121
+ const parts = inputParts.map((part) => {
122
+ switch (part.type) {
123
+ case "text":
124
+ return {
125
+ type: "text",
126
+ text: part.text,
127
+ };
128
+ case "image":
129
+ return Object.assign(Object.assign({ type: "file", url: part.image }, (part.filename ? { filename: part.filename } : {})), { mediaType: "image/png" });
130
+ case "file":
131
+ return Object.assign({ type: "file", url: part.data, mediaType: part.mimeType }, (part.filename ? { filename: part.filename } : {}));
132
+ default:
133
+ throw new Error(`Unsupported part type: ${part.type}`);
134
+ }
135
+ });
136
+ return {
137
+ role: message.role,
138
+ parts,
139
+ metadata: message.metadata,
140
+ };
83
141
  }
84
142
  const AcpSessionTitleSync = () => {
85
143
  const aui = useAui();
@@ -106,10 +164,11 @@ const AcpSessionTitleSync = () => {
106
164
  return null;
107
165
  };
108
166
  class RemoteConversationHistoryAdapter {
109
- constructor(aui, workspacePath, ensureConversation) {
167
+ constructor(aui, workspacePath, fallbackThreadId, provider) {
110
168
  this.aui = aui;
111
169
  this.workspacePath = workspacePath;
112
- this.ensureConversation = ensureConversation;
170
+ this.fallbackThreadId = fallbackThreadId;
171
+ this.provider = provider;
113
172
  }
114
173
  async load() {
115
174
  return { messages: [] };
@@ -118,61 +177,67 @@ class RemoteConversationHistoryAdapter {
118
177
  withFormat(formatAdapter) {
119
178
  const aui = this.aui;
120
179
  const workspacePath = this.workspacePath;
121
- const ensureConversation = this.ensureConversation;
122
- const getRemoteId = () => this.aui.threadListItem().getState().remoteId;
123
- const saveItem = async (remoteId, item, messageId) => {
124
- await acpApiClient.saveConversationMessage(remoteId, workspacePath, {
125
- parentId: item.parentId,
126
- message: {
127
- id: messageId,
128
- content: formatAdapter.encode(item),
129
- },
130
- format: formatAdapter.format,
131
- });
180
+ const fallbackThreadId = this.fallbackThreadId;
181
+ const provider = this.provider;
182
+ const getRemoteId = async () => {
183
+ var _a;
184
+ const currentRemoteId = aui.threadListItem().getState().remoteId;
185
+ if (currentRemoteId)
186
+ return currentRemoteId;
187
+ await aui
188
+ .threadListItem()
189
+ .initialize()
190
+ .catch(() => null);
191
+ return (_a = aui.threadListItem().getState().remoteId) !== null && _a !== void 0 ? _a : fallbackThreadId;
132
192
  };
133
193
  return {
134
194
  async load() {
135
- const remoteId = getRemoteId();
195
+ const remoteId = await getRemoteId();
136
196
  if (!remoteId)
137
197
  return { messages: [] };
138
- const repo = await acpApiClient.loadConversationMessages(remoteId, workspacePath, formatAdapter.format);
139
- if (repo.messages.length === 0) {
140
- return null;
141
- }
142
- return {
198
+ const repo = await acpApiClient.loadConversationMessages(remoteId, workspacePath, formatAdapter.format, undefined, {
199
+ hideResumable: hasLocalPendingResumableStream({
200
+ workspacePath,
201
+ provider,
202
+ threadId: remoteId,
203
+ }),
204
+ });
205
+ const messages = repo.messages.map((message) => formatAdapter.decode({
206
+ id: message.id,
207
+ parent_id: message.parent_id,
208
+ format: message.format,
209
+ content: message.content,
210
+ }));
211
+ const externalRepo = {
143
212
  headId: repo.headId,
144
- messages: repo.messages.map((message) => formatAdapter.decode({
145
- id: message.id,
146
- parent_id: message.parent_id,
147
- format: message.format,
148
- content: message.content,
149
- })),
213
+ messages,
150
214
  };
215
+ if (messages.length === 0) {
216
+ aui.thread().import({ messages: [] });
217
+ }
218
+ return externalRepo;
151
219
  },
152
220
  async append(item) {
153
- const { remoteId } = await aui.threadListItem().initialize();
154
- await (ensureConversation === null || ensureConversation === void 0 ? void 0 : ensureConversation(remoteId));
155
- await saveItem(remoteId, item, formatAdapter.getId(item.message));
221
+ await aui.threadListItem().initialize();
222
+ void item;
156
223
  },
157
224
  async update(item, localMessageId) {
158
- const remoteId = getRemoteId();
159
- if (!remoteId)
160
- return;
161
- await saveItem(remoteId, item, localMessageId);
225
+ void item;
226
+ void localMessageId;
162
227
  },
163
228
  };
164
229
  }
165
230
  }
166
- function createHistoryProvider(workspacePath, ensureConversation) {
231
+ function createHistoryProvider(workspacePath, threadId, provider) {
167
232
  const Provider = ({ children }) => {
168
233
  const aui = useAui();
169
- const history = useMemo(() => new RemoteConversationHistoryAdapter(aui, workspacePath, ensureConversation), [aui]);
234
+ const history = useMemo(() => new RemoteConversationHistoryAdapter(aui, workspacePath, threadId, provider), [aui]);
170
235
  const adapters = useMemo(() => ({ history }), [history]);
171
236
  return (_jsxs(RuntimeAdapterProvider, { adapters: adapters, children: [_jsx(AcpSessionTitleSync, {}), children] }));
172
237
  };
173
238
  return Provider;
174
239
  }
175
- function createConversationThreadListAdapter(context = {}) {
240
+ function createConversationThreadListAdapter(context = {}, threadId) {
176
241
  const conversationBody = (threadId) => {
177
242
  var _a, _b, _c, _d, _e, _f, _g;
178
243
  const metadata = (_b = (_a = context.getConversationMetadata) === null || _a === void 0 ? void 0 : _a.call(context)) !== null && _b !== void 0 ? _b : {};
@@ -190,15 +255,23 @@ function createConversationThreadListAdapter(context = {}) {
190
255
  await acpApiClient.upsertConversation(Object.assign(Object.assign(Object.assign({}, conversationBody(threadId)), patch), { threadId }), context.workspacePath);
191
256
  };
192
257
  return {
193
- unstable_Provider: createHistoryProvider(context.workspacePath, ensureConversation),
258
+ unstable_Provider: createHistoryProvider(context.workspacePath, threadId, context.provider),
194
259
  async list(params) {
195
260
  const { threads, nextCursor } = await acpApiClient.listConversations(context.workspacePath, {
196
261
  after: params === null || params === void 0 ? void 0 : params.after,
197
262
  limit: THREAD_LIST_PAGE_SIZE,
198
263
  refreshProvider: false,
199
264
  });
265
+ const hasRouteThread = threadId && threads.some((thread) => thread.threadId === threadId);
266
+ const routeThread = threadId && !hasRouteThread
267
+ ? await acpApiClient
268
+ .getConversation(threadId, context.workspacePath)
269
+ .then(({ thread }) => thread)
270
+ .catch(() => null)
271
+ : null;
272
+ const allThreads = routeThread ? [routeThread, ...threads] : threads;
200
273
  return {
201
- threads: threads.map(toRemoteMetadata),
274
+ threads: allThreads.map(toRemoteMetadata),
202
275
  nextCursor,
203
276
  };
204
277
  },
@@ -256,6 +329,7 @@ function dispatchCapabilityRefresh({ provider, threadId, workspacePath, }) {
256
329
  }));
257
330
  }
258
331
  export function useConversationChatRuntime({ context, threadId, } = {}) {
332
+ var _a;
259
333
  const provider = context === null || context === void 0 ? void 0 : context.provider;
260
334
  const model = context === null || context === void 0 ? void 0 : context.model;
261
335
  const modeId = context === null || context === void 0 ? void 0 : context.modeId;
@@ -266,8 +340,18 @@ export function useConversationChatRuntime({ context, threadId, } = {}) {
266
340
  const explicitThoughtLevel = context === null || context === void 0 ? void 0 : context.explicitThoughtLevel;
267
341
  const permissionMode = context === null || context === void 0 ? void 0 : context.permissionMode;
268
342
  const workspacePath = context === null || context === void 0 ? void 0 : context.workspacePath;
269
- const builtinToolSettings = context === null || context === void 0 ? void 0 : context.builtinToolSettings;
343
+ const builtinToolSettings = (_a = context === null || context === void 0 ? void 0 : context.runtimeBuiltinToolSettings) !== null && _a !== void 0 ? _a : context === null || context === void 0 ? void 0 : context.builtinToolSettings;
270
344
  const consumeContextBundle = context === null || context === void 0 ? void 0 : context.consumeContextBundle;
345
+ const stagedContextBundlesRef = useRef([]);
346
+ const toCreateMessage = useCallback((message) => {
347
+ if (shouldStartRunFromAppendMessage(message)) {
348
+ const contextBundle = consumeContextBundle === null || consumeContextBundle === void 0 ? void 0 : consumeContextBundle();
349
+ if (contextBundle) {
350
+ stagedContextBundlesRef.current.push(contextBundle);
351
+ }
352
+ }
353
+ return createAcpCreateMessage(message);
354
+ }, [consumeContextBundle]);
271
355
  const conversationMetadataRef = useRef({});
272
356
  conversationMetadataRef.current = {
273
357
  model,
@@ -280,22 +364,45 @@ export function useConversationChatRuntime({ context, threadId, } = {}) {
280
364
  workspacePath,
281
365
  getConversationMetadata: () => conversationMetadataRef.current,
282
366
  }), [provider, permissionMode, workspacePath]);
283
- const adapter = useMemo(() => createConversationThreadListAdapter(adapterContext), [adapterContext]);
367
+ const adapter = useMemo(() => createConversationThreadListAdapter(adapterContext, threadId), [adapterContext, threadId]);
284
368
  return useRemoteThreadListRuntime({
285
369
  adapter,
286
370
  allowNesting: true,
287
371
  threadId,
288
372
  runtimeHook: function RuntimeHook() {
373
+ var _a;
374
+ // biome-ignore lint/correctness/useHookAtTopLevel: assistant-ui calls runtimeHook as a runtime component hook.
375
+ const runtimeThreadId = useAuiState((state) => state.threadListItem.id);
376
+ // biome-ignore lint/correctness/useHookAtTopLevel: assistant-ui calls runtimeHook as a runtime component hook.
377
+ const runtimeRemoteId = useAuiState((state) => state.threadListItem.remoteId);
378
+ const activeThreadId = (_a = runtimeRemoteId !== null && runtimeRemoteId !== void 0 ? runtimeRemoteId : threadId) !== null && _a !== void 0 ? _a : runtimeThreadId;
379
+ const activeProvider = provider !== null && provider !== void 0 ? provider : DEFAULT_PROVIDER;
289
380
  // biome-ignore lint/correctness/useHookAtTopLevel: assistant-ui calls runtimeHook as a runtime component hook.
290
381
  return useChatRuntime({
291
382
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
383
+ toCreateMessage,
292
384
  transport: new AssistantChatTransport({
293
385
  api: acpApiClient.chatUrl(),
386
+ resumable: {
387
+ storage: createResumableSessionStorage({
388
+ key: createResumableStorageKey({
389
+ workspacePath,
390
+ provider: activeProvider,
391
+ threadId: activeThreadId,
392
+ }),
393
+ }),
394
+ resumeApi: (streamId) => acpApiClient.chatResumeUrl(streamId, {
395
+ threadId: activeThreadId,
396
+ provider: activeProvider,
397
+ workspacePath: workspacePath !== null && workspacePath !== void 0 ? workspacePath : null,
398
+ }),
399
+ },
294
400
  fetch: async (input, init) => {
295
401
  var _a, _b;
296
402
  const response = await fetch(input, init);
297
- const responseProvider = (_b = (_a = response.headers.get("x-acp-provider")) !== null && _a !== void 0 ? _a : provider) !== null && _b !== void 0 ? _b : DEFAULT_PROVIDER;
298
- const responseThreadId = response.headers.get("x-acp-thread-id");
403
+ const normalizedResponse = withoutBodyForNullBodyStatus(response);
404
+ const responseProvider = (_b = (_a = normalizedResponse.headers.get("x-acp-provider")) !== null && _a !== void 0 ? _a : provider) !== null && _b !== void 0 ? _b : DEFAULT_PROVIDER;
405
+ const responseThreadId = normalizedResponse.headers.get("x-acp-thread-id");
299
406
  const decodedThreadId = responseThreadId
300
407
  ? decodeURIComponent(responseThreadId)
301
408
  : null;
@@ -306,7 +413,7 @@ export function useConversationChatRuntime({ context, threadId, } = {}) {
306
413
  workspacePath,
307
414
  });
308
415
  }
309
- return response;
416
+ return normalizedResponse;
310
417
  },
311
418
  body: () => {
312
419
  var _a;
@@ -319,7 +426,7 @@ export function useConversationChatRuntime({ context, threadId, } = {}) {
319
426
  workspacePath: workspacePath !== null && workspacePath !== void 0 ? workspacePath : null,
320
427
  builtinTools: getEnabledBuiltinToolIds(builtinToolSettings),
321
428
  builtinToolSettings,
322
- context: (_a = consumeContextBundle === null || consumeContextBundle === void 0 ? void 0 : consumeContextBundle()) !== null && _a !== void 0 ? _a : null,
429
+ context: (_a = stagedContextBundlesRef.current.shift()) !== null && _a !== void 0 ? _a : null,
323
430
  });
324
431
  },
325
432
  }),
@@ -0,0 +1,7 @@
1
+ import type { ConversationMessageRepository, ConversationRecord } from "./types";
2
+ export declare function loadProviderMessageRepository({ threadId, format, workspacePath, conversation, }: {
3
+ threadId: string;
4
+ format: string | null | undefined;
5
+ workspacePath: string | null | undefined;
6
+ conversation: ConversationRecord | null;
7
+ }): Promise<ConversationMessageRepository>;
@@ -0,0 +1,99 @@
1
+ import { convertTranscriptToMessageRepository, findProviderSession, loadProviderTranscript, } from "../provider-history/index.mjs";
2
+ function errorMessage(error) {
3
+ return error instanceof Error ? error.message : String(error);
4
+ }
5
+ function filterMessagesByFormat(repo, format) {
6
+ return format
7
+ ? repo.messages.filter((message) => message.format === format)
8
+ : repo.messages;
9
+ }
10
+ export async function loadProviderMessageRepository({ threadId, format, workspacePath, conversation, }) {
11
+ var _a, _b;
12
+ let providerSession = null;
13
+ let providerSessionError = null;
14
+ const providerSessionId = (_a = conversation === null || conversation === void 0 ? void 0 : conversation.providerSessionId) !== null && _a !== void 0 ? _a : (threadId.includes(":") ? threadId.slice(threadId.indexOf(":") + 1) : null);
15
+ const provider = (_b = conversation === null || conversation === void 0 ? void 0 : conversation.provider) !== null && _b !== void 0 ? _b : threadId.split(":")[0];
16
+ if (!(conversation === null || conversation === void 0 ? void 0 : conversation.sourcePath) && providerSessionId) {
17
+ try {
18
+ providerSession = await findProviderSession(provider, providerSessionId, workspacePath);
19
+ }
20
+ catch (error) {
21
+ providerSessionError = errorMessage(error);
22
+ }
23
+ }
24
+ if (conversation === null || conversation === void 0 ? void 0 : conversation.sourcePath) {
25
+ try {
26
+ const transcript = await loadProviderTranscript({
27
+ provider: conversation.provider,
28
+ sessionId: conversation.providerSessionId,
29
+ sourcePath: conversation.sourcePath,
30
+ });
31
+ const repo = convertTranscriptToMessageRepository(transcript, format !== null && format !== void 0 ? format : "ai-sdk/v6");
32
+ const messages = filterMessagesByFormat(repo, format);
33
+ if (messages.length === 0) {
34
+ return {
35
+ messages: [],
36
+ diagnostic: {
37
+ source: "provider-history",
38
+ error: "Provider transcript had no messages",
39
+ },
40
+ };
41
+ }
42
+ return {
43
+ headId: repo.headId,
44
+ messages,
45
+ diagnostic: { source: "provider-history" },
46
+ };
47
+ }
48
+ catch (error) {
49
+ return {
50
+ messages: [],
51
+ diagnostic: {
52
+ source: "provider-history",
53
+ error: errorMessage(error),
54
+ },
55
+ };
56
+ }
57
+ }
58
+ if (providerSession === null || providerSession === void 0 ? void 0 : providerSession.sourcePath) {
59
+ try {
60
+ const transcript = await loadProviderTranscript({
61
+ provider: providerSession.provider,
62
+ sessionId: providerSession.sessionId,
63
+ sourcePath: providerSession.sourcePath,
64
+ });
65
+ const repo = convertTranscriptToMessageRepository(transcript, format !== null && format !== void 0 ? format : "ai-sdk/v6");
66
+ const messages = filterMessagesByFormat(repo, format);
67
+ if (messages.length === 0) {
68
+ return {
69
+ messages: [],
70
+ diagnostic: {
71
+ source: "provider-history",
72
+ error: "Provider transcript had no messages",
73
+ },
74
+ };
75
+ }
76
+ return {
77
+ headId: repo.headId,
78
+ messages,
79
+ diagnostic: { source: "provider-history" },
80
+ };
81
+ }
82
+ catch (error) {
83
+ return {
84
+ messages: [],
85
+ diagnostic: {
86
+ source: "provider-history",
87
+ error: errorMessage(error),
88
+ },
89
+ };
90
+ }
91
+ }
92
+ return {
93
+ messages: [],
94
+ diagnostic: {
95
+ source: "provider-history",
96
+ error: providerSessionError !== null && providerSessionError !== void 0 ? providerSessionError : "Provider transcript not found",
97
+ },
98
+ };
99
+ }
@@ -0,0 +1,9 @@
1
+ import type { ConversationMessageRepository } from "./types";
2
+ export declare const STALE_RUNNING_MESSAGE_MS = 30000;
3
+ export declare const STALE_RUNNING_MESSAGE_ERROR = "Run was interrupted before a resumable assistant stream was available.";
4
+ export declare function getAcpRun(message: unknown): Record<string, unknown> | null;
5
+ export declare function getLivePartialStreamId(message: unknown): string | null;
6
+ export declare function recoverStaleRunningRuntimeMessages<TContent extends Record<string, unknown>>(repository: ConversationMessageRepository<TContent>, nowMs?: number): {
7
+ repository: ConversationMessageRepository<TContent>;
8
+ changed: boolean;
9
+ };
@@ -0,0 +1,95 @@
1
+ export const STALE_RUNNING_MESSAGE_MS = 30000;
2
+ export const STALE_RUNNING_MESSAGE_ERROR = "Run was interrupted before a resumable assistant stream was available.";
3
+ function isRecord(value) {
4
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
5
+ }
6
+ export function getAcpRun(message) {
7
+ if (!isRecord(message))
8
+ return null;
9
+ const metadata = isRecord(message.metadata) ? message.metadata : null;
10
+ const custom = isRecord(metadata === null || metadata === void 0 ? void 0 : metadata.custom) ? metadata.custom : null;
11
+ const acpRun = isRecord(custom === null || custom === void 0 ? void 0 : custom.acpRun) ? custom.acpRun : null;
12
+ return acpRun;
13
+ }
14
+ export function getLivePartialStreamId(message) {
15
+ if (!isRecord(message))
16
+ return null;
17
+ if (message.role !== "assistant")
18
+ return null;
19
+ const acpRun = getAcpRun(message);
20
+ if ((acpRun === null || acpRun === void 0 ? void 0 : acpRun.status) !== "running")
21
+ return null;
22
+ return typeof acpRun.streamId === "string" && acpRun.streamId.trim()
23
+ ? acpRun.streamId
24
+ : null;
25
+ }
26
+ function isStaleRunningMessage(message, nowMs) {
27
+ var _a;
28
+ const acpRun = getAcpRun(message.content);
29
+ if ((acpRun === null || acpRun === void 0 ? void 0 : acpRun.status) !== "running")
30
+ return false;
31
+ const updatedAtMs = Date.parse(String((_a = message.updatedAt) !== null && _a !== void 0 ? _a : ""));
32
+ if (!Number.isFinite(updatedAtMs))
33
+ return true;
34
+ return updatedAtMs < nowMs - STALE_RUNNING_MESSAGE_MS;
35
+ }
36
+ function isTerminalRunStatus(value) {
37
+ return value === "completed" || value === "aborted" || value === "error";
38
+ }
39
+ function finalizeTerminalStreamingParts(content) {
40
+ const acpRun = getAcpRun(content);
41
+ if (!isTerminalRunStatus(acpRun === null || acpRun === void 0 ? void 0 : acpRun.status) || !Array.isArray(content.parts)) {
42
+ return { content, changed: false };
43
+ }
44
+ let changed = false;
45
+ const parts = content.parts.map((part) => {
46
+ if (!isRecord(part) ||
47
+ (part.type !== "text" && part.type !== "reasoning") ||
48
+ part.state !== "streaming") {
49
+ return part;
50
+ }
51
+ changed = true;
52
+ return Object.assign(Object.assign({}, part), { state: "done" });
53
+ });
54
+ if (!changed)
55
+ return { content, changed: false };
56
+ return {
57
+ changed: true,
58
+ content: Object.assign(Object.assign({}, content), { parts }),
59
+ };
60
+ }
61
+ function markStaleRunningMessage(content, nowMs) {
62
+ const metadata = isRecord(content.metadata) ? content.metadata : {};
63
+ const custom = isRecord(metadata.custom) ? metadata.custom : {};
64
+ const acpRun = isRecord(custom.acpRun) ? custom.acpRun : {};
65
+ return Object.assign(Object.assign({}, content), { metadata: Object.assign(Object.assign({}, metadata), { custom: Object.assign(Object.assign({}, custom), { acpRun: Object.assign(Object.assign({}, acpRun), { status: "aborted", error: typeof acpRun.error === "string" && acpRun.error.trim()
66
+ ? acpRun.error
67
+ : STALE_RUNNING_MESSAGE_ERROR, updatedAt: nowMs }) }) }) });
68
+ }
69
+ function recoverRuntimeMessageContent(message, nowMs) {
70
+ const content = isStaleRunningMessage(message, nowMs)
71
+ ? markStaleRunningMessage(message.content, nowMs)
72
+ : message.content;
73
+ const finalized = finalizeTerminalStreamingParts(content);
74
+ return {
75
+ content: finalized.content,
76
+ changed: content !== message.content || finalized.changed,
77
+ };
78
+ }
79
+ export function recoverStaleRunningRuntimeMessages(repository, nowMs = Date.now()) {
80
+ var _a, _b;
81
+ let changed = false;
82
+ const messages = repository.messages.map((message) => {
83
+ const recovered = recoverRuntimeMessageContent(message, nowMs);
84
+ if (!recovered.changed)
85
+ return message;
86
+ changed = true;
87
+ return Object.assign(Object.assign({}, message), { content: recovered.content });
88
+ });
89
+ if (!changed)
90
+ return { repository, changed: false };
91
+ return {
92
+ changed: true,
93
+ repository: Object.assign(Object.assign({}, repository), { headId: (_b = (_a = messages.at(-1)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null, messages }),
94
+ };
95
+ }
@@ -1,5 +1,5 @@
1
1
  import type { AcpRuntimeMetadata } from "../acp2aisdk";
2
- import type { ConversationListOptions, ConversationListResponse, ConversationMessageRepository, ConversationMessageUpsertRequest, ConversationRecord, ConversationSessionResolution, ConversationUpsertRequest, StoredMessageEntry } from "./types";
2
+ import type { ConversationListOptions, ConversationListResponse, ConversationMessageRepository, ConversationMessageSource, ConversationMessageUpsertRequest, ConversationRecord, ConversationSessionResolution, ConversationUpsertRequest, StoredMessageEntry } from "./types";
3
3
  export declare function listConversations(workspacePath?: string | null, options?: ConversationListOptions): Promise<ConversationListResponse>;
4
4
  export declare function getConversation(threadId: string, workspacePath?: string | null): Promise<ConversationRecord | null>;
5
5
  export declare function resolveConversationByProviderSession({ provider: providerInput, sessionId, workspacePath, }: {
@@ -11,7 +11,7 @@ export declare function upsertConversation(request: ConversationUpsertRequest):
11
11
  export declare function renameConversation(threadId: string, title: string, workspacePath?: string | null): Promise<ConversationRecord>;
12
12
  export declare function archiveConversation(threadId: string, archived: boolean, workspacePath?: string | null): Promise<ConversationRecord>;
13
13
  export declare function deleteConversation(threadId: string, workspacePath?: string | null): Promise<void>;
14
- export declare function loadConversationMessages(threadId: string, format?: string | null, workspacePath?: string | null): Promise<ConversationMessageRepository>;
14
+ export declare function loadConversationMessages(threadId: string, format?: string | null, workspacePath?: string | null, source?: ConversationMessageSource): Promise<ConversationMessageRepository>;
15
15
  export declare function saveConversationMessage(threadId: string, request: ConversationMessageUpsertRequest, workspacePath?: string | null): Promise<StoredMessageEntry>;
16
16
  export declare function touchConversationFromRuntime(metadata: AcpRuntimeMetadata): Promise<void>;
17
17
  export declare function closeConversationSession(metadata: AcpRuntimeMetadata | null): Promise<void>;