@beeuni/sdk 0.2.0 → 0.3.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.
Files changed (231) hide show
  1. package/README.md +19 -10
  2. package/dist/beeuni.esm.min.js +37 -35
  3. package/dist/beeuni.global.js +37 -35
  4. package/dist/browser/cache/idb-sync-cache.js +15 -5
  5. package/dist/browser/session-sync/server-transcript-mirror.d.ts +7 -1
  6. package/dist/browser/session-sync/server-transcript-mirror.js +30 -24
  7. package/dist/browser/session-sync/session-sync-registry.d.ts +3 -1
  8. package/dist/browser/session-sync/session-sync-registry.js +48 -3
  9. package/dist/browser/stores/sync-store/keep-unchanged.d.ts +4 -0
  10. package/dist/browser/stores/sync-store/keep-unchanged.js +34 -0
  11. package/dist/browser/stores/sync-store.d.ts +1 -0
  12. package/dist/browser/stores/sync-store.js +72 -16
  13. package/dist/core/attachments/prompt-attachments.d.ts +4 -0
  14. package/dist/core/attachments/prompt-attachments.js +34 -0
  15. package/dist/core/capture/rules.d.ts +17 -0
  16. package/dist/core/capture/rules.js +27 -0
  17. package/dist/core/client/beeuni.d.ts +92 -5
  18. package/dist/core/client/beeuni.js +2 -0
  19. package/dist/core/client/capture.d.ts +105 -0
  20. package/dist/core/client/capture.js +48 -0
  21. package/dist/core/client/facade-identity.d.ts +1 -0
  22. package/dist/core/client/facade-identity.js +1 -0
  23. package/dist/core/client/project-access-resources.d.ts +3 -1
  24. package/dist/core/client/project-access-resources.js +1 -1
  25. package/dist/core/client/project-operations-admin.d.ts +4 -4
  26. package/dist/core/client/project-operations-review.d.ts +2 -3
  27. package/dist/core/client/project-operations-review.js +1 -0
  28. package/dist/core/client/project-operations-workspace.d.ts +4 -4
  29. package/dist/core/client/project-operations-workspace.js +1 -1
  30. package/dist/core/client/project-operations.d.ts +5 -4
  31. package/dist/core/client/session-context.d.ts +2 -0
  32. package/dist/core/client/session-handle.d.ts +13 -0
  33. package/dist/core/client/session-lifecycle-resources.d.ts +11 -0
  34. package/dist/core/client/session-lifecycle-resources.js +4 -0
  35. package/dist/core/client/session-lifecycle-security.d.ts +2 -0
  36. package/dist/core/client/session-lifecycle-security.js +1 -0
  37. package/dist/core/client/session-readiness.d.ts +2 -0
  38. package/dist/core/client/session-readiness.js +20 -1
  39. package/dist/core/http/api-client.d.ts +1 -0
  40. package/dist/core/http/api-client.js +1 -0
  41. package/dist/core/http/config.d.ts +2 -0
  42. package/dist/core/http/response-error.js +26 -11
  43. package/dist/core/http/transport.js +6 -1
  44. package/dist/core/models/composer-model.d.ts +14 -0
  45. package/dist/core/models/composer-model.js +21 -1
  46. package/dist/core/rest/platform-client/capture.d.ts +363 -0
  47. package/dist/core/rest/platform-client/capture.js +116 -0
  48. package/dist/core/rest/platform-client/index.d.ts +1 -0
  49. package/dist/core/rest/platform-client/index.js +1 -0
  50. package/dist/core/rest/platform-client/lifecycle.js +18 -12
  51. package/dist/core/rest/platform-client/shared.d.ts +5 -0
  52. package/dist/core/rest/platform-client/shared.js +18 -0
  53. package/dist/core/rest/projects-client/account-secret-resources.d.ts +2 -1
  54. package/dist/core/rest/projects-client/accounts.d.ts +34 -0
  55. package/dist/core/rest/projects-client/accounts.js +10 -0
  56. package/dist/core/rest/projects-client/admin-providers.d.ts +0 -61
  57. package/dist/core/rest/projects-client/admin-providers.js +0 -6
  58. package/dist/core/rest/projects-client/agent-config.d.ts +3 -0
  59. package/dist/core/rest/projects-client/apps-fleet.d.ts +5 -1
  60. package/dist/core/rest/projects-client/apps-fleet.js +2 -2
  61. package/dist/core/rest/projects-client/apps.d.ts +3 -0
  62. package/dist/core/rest/projects-client/change-requests.d.ts +2 -0
  63. package/dist/core/rest/projects-client/change-requests.js +2 -0
  64. package/dist/core/rest/projects-client/connectors.d.ts +7 -0
  65. package/dist/core/rest/projects-client/connectors.js +3 -0
  66. package/dist/core/rest/projects-client/files.d.ts +3 -0
  67. package/dist/core/rest/projects-client/files.js +4 -0
  68. package/dist/core/rest/projects-client/gateway.d.ts +5 -0
  69. package/dist/core/rest/projects-client/iam-actions.d.ts +76 -0
  70. package/dist/core/rest/projects-client/iam-actions.js +101 -0
  71. package/dist/core/rest/projects-client/iam.d.ts +1 -0
  72. package/dist/core/rest/projects-client/iam.js +1 -0
  73. package/dist/core/rest/projects-client/notifications.d.ts +1 -1
  74. package/dist/core/rest/projects-client/notifications.js +4 -0
  75. package/dist/core/rest/projects-client/projects.d.ts +3 -2
  76. package/dist/core/rest/projects-client/projects.js +2 -3
  77. package/dist/core/rest/projects-client/review-items.d.ts +2 -0
  78. package/dist/core/rest/projects-client/review-items.js +4 -0
  79. package/dist/core/rest/projects-client/review.d.ts +17 -3
  80. package/dist/core/rest/projects-client/review.js +11 -0
  81. package/dist/core/rest/projects-client/secrets.d.ts +3 -0
  82. package/dist/core/rest/projects-client/session-attachments.js +14 -1
  83. package/dist/core/rest/projects-client/sessions.d.ts +29 -2
  84. package/dist/core/rest/projects-client/sessions.js +34 -4
  85. package/dist/core/runtime/runtime-rest-client.js +1 -1
  86. package/dist/core/runtime/runtime-types.d.ts +1 -0
  87. package/dist/core/session/open-bundle.d.ts +4 -4
  88. package/dist/core/session/open-bundle.js +29 -46
  89. package/dist/core/session/turn-end-watch.d.ts +47 -0
  90. package/dist/core/session/turn-end-watch.js +141 -0
  91. package/dist/core/session-log/index.d.ts +4 -0
  92. package/dist/core/session-log/index.js +3 -0
  93. package/dist/core/session-log/tool-call.d.ts +7 -2
  94. package/dist/core/session-log/tool-call.js +25 -6
  95. package/dist/core/session-log/turns/blocks.d.ts +60 -0
  96. package/dist/core/session-log/turns/blocks.js +124 -0
  97. package/dist/core/session-log/turns/history.d.ts +32 -0
  98. package/dist/core/session-log/turns/history.js +99 -0
  99. package/dist/core/session-log/turns/index.d.ts +4 -0
  100. package/dist/core/session-log/turns/index.js +4 -0
  101. package/dist/core/session-log/turns/metrics.d.ts +21 -0
  102. package/dist/core/session-log/turns/metrics.js +29 -0
  103. package/dist/core/session-log/turns/turn.d.ts +55 -0
  104. package/dist/core/session-log/turns/turn.js +279 -0
  105. package/dist/core/session-log/turns/v1-parts.d.ts +8 -0
  106. package/dist/core/session-log/turns/v1-parts.js +43 -0
  107. package/dist/core/session-log/v1-view.d.ts +5 -2
  108. package/dist/core/session-log/v1-view.js +86 -22
  109. package/dist/core/session-log/v1.js +10 -3
  110. package/dist/core/session-sync/saved-transcript.d.ts +15 -1
  111. package/dist/core/session-sync/saved-transcript.js +55 -0
  112. package/dist/core/session-sync/session-sync-controller.js +3 -0
  113. package/dist/core/stream/event-stream.js +3 -3
  114. package/dist/core/turns/classify.js +4 -2
  115. package/dist/core/turns/edit-blocks.d.ts +1 -0
  116. package/dist/core/turns/edit-blocks.js +15 -0
  117. package/dist/core/turns/segments/burst-summary.d.ts +1 -0
  118. package/dist/core/turns/segments/burst-summary.js +24 -0
  119. package/dist/core/turns/segments/group-steps.d.ts +4 -3
  120. package/dist/core/turns/segments/group-steps.js +9 -6
  121. package/dist/core/turns/segments/narration.d.ts +5 -4
  122. package/dist/core/turns/segments/narration.js +12 -3
  123. package/dist/core/turns/segments/session-activity-groups.d.ts +2 -1
  124. package/dist/core/turns/segments/session-activity-groups.js +3 -1
  125. package/dist/core/turns/segments/step-label.d.ts +2 -1
  126. package/dist/core/turns/segments/step-label.js +3 -1
  127. package/dist/core/turns/segments/working-turn.d.ts +4 -2
  128. package/dist/core/turns/segments/working-turn.js +9 -8
  129. package/dist/core/turns/tool-kind.d.ts +1 -1
  130. package/dist/core/turns/tool-kind.js +3 -2
  131. package/dist/core/turns/tool-registry.js +1 -0
  132. package/dist/core/turns/tools/tool-meta.d.ts +1 -1
  133. package/dist/core/turns/tools/tool-meta.js +3 -2
  134. package/dist/core/turns/view-model.js +1 -13
  135. package/dist/genui/catalog.d.ts +28 -0
  136. package/dist/genui/catalog.js +396 -0
  137. package/dist/genui/expansion.d.ts +3 -0
  138. package/dist/genui/expansion.js +295 -0
  139. package/dist/genui/fence-entry.d.ts +2 -0
  140. package/dist/genui/fence-entry.js +1 -0
  141. package/dist/genui/fence.d.ts +21 -0
  142. package/dist/genui/fence.js +138 -0
  143. package/dist/genui/index.d.ts +6 -0
  144. package/dist/genui/index.js +6 -0
  145. package/dist/genui/markdown.d.ts +12 -0
  146. package/dist/genui/markdown.js +104 -0
  147. package/dist/genui/parse.d.ts +6 -0
  148. package/dist/genui/parse.js +75 -0
  149. package/dist/genui/prompt.d.ts +2 -0
  150. package/dist/genui/prompt.js +206 -0
  151. package/dist/genui/react/default-components.d.ts +2 -0
  152. package/dist/genui/react/default-components.js +72 -0
  153. package/dist/genui/react/default-components.test.d.ts +1 -0
  154. package/dist/genui/react/default-components.test.js +94 -0
  155. package/dist/genui/react/genui-block-disabled.test.d.ts +1 -0
  156. package/dist/genui/react/genui-block-disabled.test.js +50 -0
  157. package/dist/genui/react/genui-block.d.ts +28 -0
  158. package/dist/genui/react/genui-block.js +91 -0
  159. package/dist/genui/react/genui-block.test.d.ts +1 -0
  160. package/dist/genui/react/genui-block.test.js +205 -0
  161. package/dist/genui/react/index.d.ts +2 -0
  162. package/dist/genui/react/index.js +2 -0
  163. package/dist/genui/share.d.ts +5 -0
  164. package/dist/genui/share.js +30 -0
  165. package/dist/genui/test-fixtures.d.ts +2 -0
  166. package/dist/genui/test-fixtures.js +16 -0
  167. package/dist/genui/types.d.ts +29 -0
  168. package/dist/genui/types.js +1 -0
  169. package/dist/genui/urls.d.ts +1 -0
  170. package/dist/genui/urls.js +18 -0
  171. package/dist/genui/validate.d.ts +9 -0
  172. package/dist/genui/validate.js +116 -0
  173. package/dist/index.d.ts +2 -2
  174. package/dist/index.js +2 -2
  175. package/dist/internal/connector-catalog.d.ts +1 -0
  176. package/dist/platform/ui.d.ts +8 -2
  177. package/dist/platform/ui.js +19 -4
  178. package/dist/react/chat/use-chat-turns.d.ts +1 -1
  179. package/dist/react/human-presence.d.ts +9 -0
  180. package/dist/react/human-presence.js +12 -0
  181. package/dist/react/index.d.ts +4 -0
  182. package/dist/react/index.js +4 -0
  183. package/dist/react/opencode.d.ts +1 -1
  184. package/dist/react/opencode.js +1 -1
  185. package/dist/react/query-contracts.d.ts +6 -0
  186. package/dist/react/query-contracts.js +6 -0
  187. package/dist/react/query-keys.d.ts +17 -1
  188. package/dist/react/query-keys.js +24 -1
  189. package/dist/react/reset-identity-state.js +2 -0
  190. package/dist/react/session-cache-write.d.ts +1 -0
  191. package/dist/react/session-cache-write.js +20 -0
  192. package/dist/react/session-transcript-subscription.js +1 -1
  193. package/dist/react/sign-in-with-beeuni.d.ts +5 -5
  194. package/dist/react/turn-end-handoff.d.ts +13 -0
  195. package/dist/react/turn-end-handoff.js +101 -0
  196. package/dist/react/use-admin-providers.d.ts +0 -3
  197. package/dist/react/use-admin-providers.js +1 -11
  198. package/dist/react/use-capture.d.ts +47 -0
  199. package/dist/react/use-capture.js +173 -0
  200. package/dist/react/use-feature-flag.js +1 -0
  201. package/dist/react/use-opencode-events/handle-interaction-event.js +8 -3
  202. package/dist/react/use-opencode-events/handle-session-event.js +13 -8
  203. package/dist/react/use-opencode-events/handler-context.d.ts +1 -0
  204. package/dist/react/use-opencode-events/helpers.d.ts +1 -0
  205. package/dist/react/use-opencode-events/helpers.js +11 -1
  206. package/dist/react/use-opencode-local.d.ts +5 -1
  207. package/dist/react/use-opencode-local.js +15 -2
  208. package/dist/react/use-opencode-sessions/commands.d.ts +11 -1
  209. package/dist/react/use-opencode-sessions/commands.js +21 -1
  210. package/dist/react/use-opencode-sessions/permissions.js +28 -1
  211. package/dist/react/use-project-sessions.d.ts +1 -1
  212. package/dist/react/use-project-sessions.js +6 -5
  213. package/dist/react/use-prompt-attachments.js +1 -0
  214. package/dist/react/use-review-summary.d.ts +13 -0
  215. package/dist/react/use-review-summary.js +32 -0
  216. package/dist/react/use-runtime-reconnect.js +2 -2
  217. package/dist/react/use-session-list-state.d.ts +6 -0
  218. package/dist/react/use-session-list-state.js +33 -0
  219. package/dist/react/use-session-log.d.ts +3 -2
  220. package/dist/react/use-session-log.js +26 -17
  221. package/dist/react/use-session-messages.d.ts +2 -1
  222. package/dist/react/use-session-messages.js +15 -14
  223. package/dist/react/use-session-stream.js +12 -1
  224. package/dist/react/use-session-sync.d.ts +2 -2
  225. package/dist/react/use-session-sync.js +10 -8
  226. package/dist/react/use-session-transcript-history.js +2 -12
  227. package/dist/react/use-session-working.d.ts +2 -0
  228. package/dist/react/use-session-working.js +10 -0
  229. package/dist/react/use-session.d.ts +6 -0
  230. package/dist/react/use-session.js +87 -7
  231. package/package.json +49 -3
@@ -1,10 +1,37 @@
1
1
  import { getClient } from '../../core/runtime/client.js';
2
2
  import { unwrap } from './shared.js';
3
- export async function replyToPermission(requestId, reply, message) {
3
+ const repliesInFlight = new Map();
4
+ function permissionAlreadyAnswered(result) {
5
+ if (result.response?.status !== 404)
6
+ return false;
7
+ const err = result.error;
8
+ if (typeof err === 'string') {
9
+ return err.toLowerCase().includes('permission request not found');
10
+ }
11
+ if (!err || typeof err !== 'object')
12
+ return false;
13
+ const rec = err;
14
+ const data = rec.data && typeof rec.data === 'object' ? rec.data : undefined;
15
+ const message = data?.message ?? rec.message ?? rec.error ?? '';
16
+ return String(message).toLowerCase().includes('permission request not found');
17
+ }
18
+ async function sendPermissionReply(requestId, reply, message) {
4
19
  const client = getClient();
5
20
  const result = await client.permission.reply({ requestID: requestId, reply, message });
21
+ if (permissionAlreadyAnswered(result))
22
+ return;
6
23
  unwrap(result);
7
24
  }
25
+ export async function replyToPermission(requestId, reply, message) {
26
+ const inFlight = repliesInFlight.get(requestId);
27
+ if (inFlight)
28
+ return inFlight;
29
+ const send = sendPermissionReply(requestId, reply, message).finally(() => {
30
+ repliesInFlight.delete(requestId);
31
+ });
32
+ repliesInFlight.set(requestId, send);
33
+ return send;
34
+ }
8
35
  const KNOWN_PERMISSION_TYPES = [
9
36
  'bash',
10
37
  'edit',
@@ -9,7 +9,7 @@ export declare function shouldScanPastEmptySessionPages(data: {
9
9
  pages: ProjectSessionPage[];
10
10
  pageParams: unknown[];
11
11
  } | undefined, hasNextPage: boolean): boolean;
12
- export interface UseProjectSessionsOptions extends Pick<ListProjectSessionsOptions, 'scope' | 'limit' | 'parent' | 'startedBy' | 'q' | 'labels'> {
12
+ export interface UseProjectSessionsOptions extends Pick<ListProjectSessionsOptions, 'scope' | 'limit' | 'parent' | 'startedBy' | 'q' | 'labels' | 'view'> {
13
13
  enabled?: boolean;
14
14
  refetchInterval?: number | false | ((sessions: ProjectSession[]) => number | false);
15
15
  refetchOnWindowFocus?: boolean;
@@ -31,9 +31,9 @@ export function shouldScanPastEmptySessionPages(data, hasNextPage) {
31
31
  }
32
32
  export function useProjectSessions(projectId, options) {
33
33
  const scope = options?.scope ?? 'visible';
34
- const { parent, startedBy, q, labels } = options ?? {};
34
+ const { parent, startedBy, q, labels, view } = options ?? {};
35
35
  const query = useInfiniteQuery({
36
- queryKey: qk.project.sessionsPaged(projectId, scope, { parent, startedBy, q, labels }),
36
+ queryKey: qk.project.sessionsPaged(projectId, scope, { parent, startedBy, q, labels, view }),
37
37
  initialPageParam: null,
38
38
  queryFn: ({ pageParam }) => listProjectSessionsPage(projectId, {
39
39
  scope,
@@ -43,6 +43,7 @@ export function useProjectSessions(projectId, options) {
43
43
  startedBy,
44
44
  q,
45
45
  labels,
46
+ view,
46
47
  }),
47
48
  getNextPageParam: projectSessionsPageParam,
48
49
  enabled: options?.enabled ?? true,
@@ -55,12 +56,12 @@ export function useProjectSessions(projectId, options) {
55
56
  return interval(flattenProjectSessionPages(query.state.data));
56
57
  },
57
58
  });
58
- const isScanning = shouldScanPastEmptySessionPages(query.data, query.hasNextPage);
59
- const { isFetching, fetchNextPage } = query;
59
+ const isScanning = shouldScanPastEmptySessionPages(query.data, query.hasNextPage) && !query.isError;
60
+ const { isFetching, data, dataUpdatedAt, fetchNextPage } = query;
60
61
  useEffect(() => {
61
62
  if (isScanning && !isFetching)
62
63
  void fetchNextPage();
63
- }, [isScanning, isFetching, fetchNextPage]);
64
+ }, [isScanning, isFetching, data, dataUpdatedAt, fetchNextPage]);
64
65
  return {
65
66
  ...query,
66
67
  isScanning,
@@ -22,6 +22,7 @@ export function usePromptAttachments(projectId, options = {}) {
22
22
  return useMemo(() => ({
23
23
  add: controller.add,
24
24
  addMany: controller.addMany,
25
+ restore: controller.restore,
25
26
  retry: controller.retry,
26
27
  remove: controller.remove,
27
28
  abort: controller.abort,
@@ -0,0 +1,13 @@
1
+ import { type ApiReviewItem, type ReviewItemsPage, type ReviewItemsQuery } from '../core/rest/projects-client/index.js';
2
+ export declare function useReviewSummary(projectId: string, options?: {
3
+ enabled?: boolean;
4
+ refetchInterval?: number | false;
5
+ }): import("@tanstack/react-query").UseQueryResult<import("../index.js").ReviewSummary, Error>;
6
+ export declare function nextReviewItemsCursor(page: ReviewItemsPage): string | undefined;
7
+ export declare function reviewItemsOf(data: {
8
+ pages: ReadonlyArray<Pick<ReviewItemsPage, 'review_items'>>;
9
+ } | undefined): ApiReviewItem[];
10
+ export declare function useReviewItemPages(projectId: string, query?: ReviewItemsQuery, options?: {
11
+ enabled?: boolean;
12
+ refetchInterval?: number | false;
13
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<ReviewItemsPage, unknown>, Error>;
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
3
+ import { getReviewSummary, listReviewItems, } from '../core/rest/projects-client/index.js';
4
+ import { qk } from './query-keys.js';
5
+ export function useReviewSummary(projectId, options) {
6
+ const enabled = Boolean(projectId) && options?.enabled !== false;
7
+ return useQuery({
8
+ queryKey: qk.project.reviewSummary(projectId),
9
+ queryFn: () => getReviewSummary(projectId),
10
+ enabled,
11
+ staleTime: 5_000,
12
+ refetchInterval: enabled ? (options?.refetchInterval ?? 60_000) : false,
13
+ refetchOnWindowFocus: false,
14
+ });
15
+ }
16
+ export function nextReviewItemsCursor(page) {
17
+ return page.next_cursor ?? undefined;
18
+ }
19
+ export function reviewItemsOf(data) {
20
+ return data?.pages.flatMap((page) => page.review_items) ?? [];
21
+ }
22
+ export function useReviewItemPages(projectId, query = {}, options) {
23
+ return useInfiniteQuery({
24
+ queryKey: qk.project.reviewItemPages(projectId, query),
25
+ queryFn: ({ pageParam }) => listReviewItems(projectId, { ...query, cursor: pageParam }),
26
+ initialPageParam: undefined,
27
+ getNextPageParam: nextReviewItemsCursor,
28
+ enabled: Boolean(projectId) && options?.enabled !== false,
29
+ staleTime: 5_000,
30
+ refetchInterval: options?.refetchInterval ?? false,
31
+ });
32
+ }
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { useEffect, useRef } from 'react';
3
- import { getAuthToken } from '../core/http/auth.js';
3
+ import { getAuthToken as getSupabaseAccessToken } from '../core/http/auth.js';
4
4
  import { CONNECTION_FAULT_GRACE_MS, getSessionHealth, isRuntimeReady, } from '../core/session/index.js';
5
5
  import { incrementSandboxFail, markInitialCheckDone, resetForServerSwitch, resetSandboxFail, setRuntimeHealth, setRuntimeCapabilities, setSandboxStatus, useSandboxConnectionStore, } from '../browser/stores/sandbox-connection-store.js';
6
6
  import { useServerStore } from '../browser/stores/server-store.js';
@@ -109,7 +109,7 @@ export function useRuntimeReconnect() {
109
109
  scheduleNext();
110
110
  return;
111
111
  }
112
- token = await getAuthToken();
112
+ token = await getSupabaseAccessToken();
113
113
  if (!token) {
114
114
  scheduleNext();
115
115
  return;
@@ -0,0 +1,6 @@
1
+ import { type SessionListState } from '../core/rest/projects-client/sessions.js';
2
+ export declare function useSessionListState(projectId: string): {
3
+ isPending: boolean;
4
+ setPinned: (sessionId: string, pinned: boolean) => Promise<SessionListState>;
5
+ setArchived: (sessionId: string, archived: boolean) => Promise<SessionListState>;
6
+ };
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
3
+ import { useMemo } from 'react';
4
+ import { setSessionArchived, setSessionPinned, } from '../core/rest/projects-client/sessions.js';
5
+ import { qk } from './query-keys.js';
6
+ import { updateCachedProjectSessions } from './session-cache-write.js';
7
+ function changedState(session, change, now) {
8
+ if ('pinned' in change) {
9
+ return { pinned_at: change.pinned ? (session.pinned_at ?? now) : null, archived_at: change.pinned ? null : (session.archived_at ?? null) };
10
+ }
11
+ return { archived_at: change.archived ? (session.archived_at ?? now) : null, pinned_at: change.archived ? null : (session.pinned_at ?? null) };
12
+ }
13
+ export function useSessionListState(projectId) {
14
+ const client = useQueryClient();
15
+ const write = (sessionId, state) => updateCachedProjectSessions(client, projectId, (sessions) => sessions.map((session) => (session.session_id === sessionId ? { ...session, ...state(session) } : session)));
16
+ const mutation = useMutation({
17
+ mutationFn: ({ sessionId, change }) => 'pinned' in change
18
+ ? setSessionPinned(projectId, sessionId, change.pinned)
19
+ : setSessionArchived(projectId, sessionId, change.archived),
20
+ onMutate: ({ sessionId, change }) => {
21
+ const now = new Date().toISOString();
22
+ write(sessionId, (session) => changedState(session, change, now));
23
+ },
24
+ onSuccess: (state, { sessionId }) => write(sessionId, () => state),
25
+ onSettled: () => client.invalidateQueries({ queryKey: [...qk.project.sessionsScope(projectId), 'list-paged'] }),
26
+ });
27
+ const { mutateAsync, isPending } = mutation;
28
+ const actions = useMemo(() => ({
29
+ setPinned: (sessionId, pinned) => mutateAsync({ sessionId, change: { pinned } }),
30
+ setArchived: (sessionId, archived) => mutateAsync({ sessionId, change: { archived } }),
31
+ }), [mutateAsync]);
32
+ return { ...actions, isPending };
33
+ }
@@ -1,11 +1,12 @@
1
1
  import { type SessionControlStreamOptions } from '../core/session/control-stream.js';
2
2
  import { type LoadSessionLogPage, type SessionLogDialect } from '../core/session-log/source.js';
3
3
  import type { SessionLog, SessionLogThread } from '../core/session-log/types.js';
4
- import { type SessionMessagesSource } from './use-session-messages.js';
4
+ import { type SessionMessagesOptions, type SessionMessagesSource } from './use-session-messages.js';
5
5
  export declare function useSessionLog(session: SessionMessagesSource): (SessionLog & {
6
6
  source: 'v1' | 'v2';
7
7
  }) | null;
8
- export declare function useSessionLogThread(session: SessionMessagesSource, threadId?: string | null): SessionLogThread | null;
8
+ export declare function useSessionLogRecord(session: Pick<SessionMessagesSource, 'sessionId'>): Partial<Omit<SessionLog, 'threads'>> | null;
9
+ export declare function useSessionLogThread(session: SessionMessagesSource, threadId?: string | null, options?: SessionMessagesOptions): SessionLogThread | null;
9
10
  export declare function useSessionLogSync(projectId: string, sessionId: string, options: {
10
11
  enabled: boolean;
11
12
  loader?: LoadSessionLogPage | null;
@@ -4,12 +4,14 @@ import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-
4
4
  import { useSyncStore } from '../browser/stores/sync-store.js';
5
5
  import { openSessionStream } from '../core/session/control-stream.js';
6
6
  import { savedSessionLogCopies } from '../core/session-log/saved-log.js';
7
- import { isV1Thread, sessionLogOfV1Rows } from '../core/session-log/v1-view.js';
7
+ import { sessionLogOfV1Rows, sessionLogThreadWithV1Rows } from '../core/session-log/v1-view.js';
8
8
  import { fetchFirstSessionLogPage, loadSessionLogPage, negotiateSessionLog, } from '../core/session-log/source.js';
9
9
  import { currentSavedCopyStore } from '../core/session-sync/saved-copy-store.js';
10
10
  import { selectSessionLog, selectSessionLogThread } from '../core/session-log/store.js';
11
11
  import { useFeatureFlag } from './use-feature-flag.js';
12
- import { useSessionMessages } from './use-session-messages.js';
12
+ import { selectSessionRows } from './session-transcript-subscription.js';
13
+ import { usePacedStoreValue, useSessionMessages } from './use-session-messages.js';
14
+ const PACED = Symbol('paced');
13
15
  const sourced = new WeakMap();
14
16
  const withSource = (log, source) => {
15
17
  let out = sourced.get(log);
@@ -17,9 +19,9 @@ const withSource = (log, source) => {
17
19
  sourced.set(log, (out = { ...log, source }));
18
20
  return out;
19
21
  };
20
- function useV1SessionLog(session, threadId) {
21
- const native = threadId || session.runtimeSessionId || session.opencodeSessionId || null;
22
- const rows = useSessionMessages({ ...session, runtimeSessionId: native });
22
+ function useV1SessionLog(session, threadId, enabled, options) {
23
+ const native = enabled ? threadId || session.runtimeSessionId || session.opencodeSessionId || null : null;
24
+ const rows = useSessionMessages({ ...session, runtimeSessionId: native, opencodeSessionId: null }, options);
23
25
  return useMemo(() => (native && rows.length ? sessionLogOfV1Rows(session.sessionId, native, rows) : null), [session.sessionId, native, rows]);
24
26
  }
25
27
  export function useSessionLog(session) {
@@ -27,18 +29,27 @@ export function useSessionLog(session) {
27
29
  const log = state.sessionLogs[session.sessionId];
28
30
  return log ? selectSessionLog(log) : null;
29
31
  });
30
- const v1 = useV1SessionLog(session);
32
+ const v1 = useV1SessionLog(session, undefined, !v2);
31
33
  return v2 ? withSource(v2, 'v2') : v1 ? withSource(v1, 'v1') : null;
32
34
  }
33
- export function useSessionLogThread(session, threadId) {
34
- const v2 = useSyncStore((state) => {
35
+ export function useSessionLogRecord(session) {
36
+ return useSyncStore((state) => state.sessionLogs[session.sessionId]?.session ?? null);
37
+ }
38
+ export function useSessionLogThread(session, threadId, options) {
39
+ const throttleMs = options?.throttleMs && options.throttleMs > 0 ? options.throttleMs : 0;
40
+ const select = (state) => {
35
41
  const log = state.sessionLogs[session.sessionId];
36
- return log ? selectSessionLogThread(log, threadId) : undefined;
37
- });
38
- const v1 = useV1SessionLog(session, threadId);
42
+ if (!log)
43
+ return undefined;
44
+ const thread = selectSessionLogThread(log, threadId);
45
+ return thread && sessionLogThreadWithV1Rows(log, thread, selectSessionRows(state, thread.thread_id), (id) => state.optimisticEchoOf(thread.thread_id, id));
46
+ };
47
+ const live = useSyncStore((state) => (throttleMs ? PACED : select(state)));
48
+ const paced = usePacedStoreValue(select, `${session.sessionId}/${threadId ?? ''}`, throttleMs);
49
+ const v2 = live === PACED ? paced : live;
50
+ const v1 = useV1SessionLog(session, threadId, v2 === undefined, options);
39
51
  return v2 !== undefined ? v2 : (v1?.threads[0] ?? null);
40
52
  }
41
- const pageThreadIsV1 = (page) => isV1Thread(page.threads.find((t) => t.thread_id === page.thread_id));
42
53
  export function useSessionLogSync(projectId, sessionId, options) {
43
54
  const loader = options.loader === undefined ? loadSessionLogPage : options.loader;
44
55
  const joinStream = options.joinStream ?? openSessionStream;
@@ -71,7 +82,7 @@ export function useSessionLogSync(projectId, sessionId, options) {
71
82
  const page = await fetchFirstSessionLogPage(loader, projectId, sessionId, controller.signal);
72
83
  if (stopped)
73
84
  return null;
74
- if (page && pageThreadIsV1(page)) {
85
+ if (page) {
75
86
  store().applySessionLogPage(sessionId, page);
76
87
  void copies?.write(projectId, sessionId, page);
77
88
  }
@@ -93,15 +104,13 @@ export function useSessionLogSync(projectId, sessionId, options) {
93
104
  busy = false;
94
105
  if (stopped)
95
106
  return;
96
- if (page && pageThreadIsV1(page)) {
107
+ if (page) {
97
108
  setRefusedKey(null);
98
109
  join({ schema: 'v2', onConnectionChange: onReconnect(() => void load()), onRuntimeResync: () => void load() });
99
110
  return;
100
111
  }
101
112
  store().dropSessionLog(sessionId);
102
113
  setRefusedKey(`${projectId}/${sessionId}`);
103
- if (page)
104
- return;
105
114
  join({
106
115
  onControl: (frame) => {
107
116
  if (frame.type !== 'beeuni.control.turn')
@@ -119,7 +128,7 @@ export function useSessionLogSync(projectId, sessionId, options) {
119
128
  const saved = await Promise.resolve(copies?.read(projectId, sessionId) ?? null).catch(() => null);
120
129
  if (stopped)
121
130
  return;
122
- if (saved && pageThreadIsV1(saved) && !store().sessionLogs[sessionId])
131
+ if (saved && !store().sessionLogs[sessionId])
123
132
  store().applySessionLogPage(sessionId, saved);
124
133
  await attempt();
125
134
  })();
@@ -1,4 +1,4 @@
1
- import { type MessageWithParts } from '../browser/stores/sync-store.js';
1
+ import { useSyncStore, type MessageWithParts } from '../browser/stores/sync-store.js';
2
2
  export interface SessionMessagesSource {
3
3
  projectId: string;
4
4
  sessionId: string;
@@ -9,3 +9,4 @@ export interface SessionMessagesOptions {
9
9
  throttleMs?: number;
10
10
  }
11
11
  export declare function useSessionMessages(session: SessionMessagesSource, options?: SessionMessagesOptions): MessageWithParts[];
12
+ export declare function usePacedStoreValue<T>(read: (state: ReturnType<typeof useSyncStore.getState>) => T, key: string, throttleMs: number): T;
@@ -16,31 +16,32 @@ export function useSessionMessages(session, options = {}) {
16
16
  return useMemo(() => messagesBeforeRewind(rows, rewind), [rows, rewind]);
17
17
  }
18
18
  function usePacedRows(readableSessionId, throttleMs) {
19
- const read = () => readableSessionId ? selectSessionRows(useSyncStore.getState(), readableSessionId) : EMPTY_ROWS;
20
- const [state, setState] = useState(() => ({ id: readableSessionId, rows: read() }));
19
+ return usePacedStoreValue((state) => (readableSessionId ? selectSessionRows(state, readableSessionId) : EMPTY_ROWS), readableSessionId, throttleMs);
20
+ }
21
+ export function usePacedStoreValue(read, key, throttleMs) {
22
+ const current = () => read(useSyncStore.getState());
23
+ const [state, setState] = useState(() => ({ key, value: current() }));
21
24
  useEffect(() => {
22
25
  if (!throttleMs)
23
26
  return;
24
27
  let lastShownAt = -Infinity;
25
28
  let timer = null;
26
- let shown = null;
29
+ let shown;
27
30
  const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
28
31
  const flush = () => {
29
32
  timer = null;
30
- const rows = read();
31
- if (rows === shown)
33
+ const value = current();
34
+ if (value === shown)
32
35
  return;
33
- shown = rows;
36
+ shown = value;
34
37
  lastShownAt = now();
35
- setState({ id: readableSessionId, rows });
38
+ setState({ key, value });
36
39
  };
37
- const initial = read();
40
+ const initial = current();
38
41
  shown = initial;
39
- setState((prev) => prev.id === readableSessionId && prev.rows === initial
40
- ? prev
41
- : { id: readableSessionId, rows: initial });
42
+ setState((prev) => (prev.key === key && prev.value === initial ? prev : { key, value: initial }));
42
43
  const unsubscribe = useSyncStore.subscribe(() => {
43
- if (read() === shown)
44
+ if (current() === shown)
44
45
  return;
45
46
  const wait = throttleMs - (now() - lastShownAt);
46
47
  if (wait <= 0) {
@@ -57,6 +58,6 @@ function usePacedRows(readableSessionId, throttleMs) {
57
58
  if (timer !== null)
58
59
  clearTimeout(timer);
59
60
  };
60
- }, [readableSessionId, throttleMs]);
61
- return state.id === readableSessionId ? state.rows : read();
61
+ }, [key, throttleMs]);
62
+ return state.key === key ? state.value : current();
62
63
  }
@@ -10,19 +10,30 @@ import { qk } from './query-keys.js';
10
10
  export function useSessionStreamConnected(projectId, sessionId) {
11
11
  return useSyncExternalStore(subscribeSessionStreamConnections, () => !!projectId && !!sessionId && sessionStreamConnected(projectId, sessionId), () => false);
12
12
  }
13
+ function openTurnsKey(turns) {
14
+ return (turns ?? []).map((turn) => turn.turn_token).sort().join(',');
15
+ }
13
16
  export function applySessionControlFrame(queryClient, projectId, sessionId, frame, memory, nowMs = Date.now()) {
14
17
  if (frame.type === 'beeuni.control.turn') {
15
18
  const payload = frame.payload;
16
19
  if (!payload?.known || !Array.isArray(payload.turns))
17
20
  return;
21
+ const key = qk.project.sessionTurn(projectId, sessionId);
22
+ const previous = queryClient.getQueryData(key);
23
+ const canStop = typeof payload.can_stop === 'boolean'
24
+ ? payload.can_stop
25
+ : previous && openTurnsKey(previous.turns) === openTurnsKey(payload.turns)
26
+ ? previous.can_stop
27
+ : undefined;
18
28
  const observation = {
19
29
  turns: payload.turns,
20
30
  ...(payload.last_ended ? { last_ended: payload.last_ended } : {}),
21
31
  ...(payload.recent_failures ? { recent_failures: payload.recent_failures } : {}),
32
+ ...(canStop !== undefined ? { can_stop: canStop } : {}),
22
33
  ...(payload.working ? { working: payload.working } : {}),
23
34
  atMs: nowMs,
24
35
  };
25
- queryClient.setQueryData(qk.project.sessionTurn(projectId, sessionId), observation);
36
+ queryClient.setQueryData(key, observation);
26
37
  return;
27
38
  }
28
39
  if (frame.type === 'beeuni.control.session') {
@@ -1,6 +1,6 @@
1
1
  import type { SessionTranscriptSyncEnvelope } from '../core/rest/projects-client/sessions.js';
2
- import { loadSessionRuntimeStatus, loadSessionTranscriptMessages } from '../browser/session-sync/session-sync-registry.js';
3
- export { loadSessionRuntimeStatus, loadSessionTranscriptMessages };
2
+ import { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages } from '../browser/session-sync/session-sync-registry.js';
3
+ export { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages };
4
4
  type FileDiff = Omit<import('../core/runtime/runtime-types.js').SnapshotFileDiff, 'patch'> & {
5
5
  patch?: string;
6
6
  before?: string;
@@ -1,18 +1,18 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
3
3
  import { claimSessionCacheOwnership, getSessionCacheOwnership, resolveSessionCacheOwnerScope, sessionCacheOwnerScopesConflict, } from '../browser/session-sync/session-cache-ownership.js';
4
- import { getSessionSyncController, loadSessionRuntimeStatus, loadSessionTranscriptMessages, resetSessionSyncControllersForSession, retainSessionSyncController, } from '../browser/session-sync/session-sync-registry.js';
4
+ import { getSessionSyncController, loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages, resetSessionSyncControllersForSession, retainSessionSyncController, } from '../browser/session-sync/session-sync-registry.js';
5
5
  import { loadOlderSessionTranscriptMirror, loadSessionTranscriptMirror, mirrorMessagesForHydrate, parseBeeuniSessionScope, refreshSavedCopy, SAVED_COPY_REFRESH_DELAY_MS, shouldHydrateFromMirror, } from '../browser/session-sync/server-transcript-mirror.js';
6
6
  import { currentSavedCopyStore } from '../core/session-sync/saved-copy-store.js';
7
7
  import { chooseOlderSource, mirrorCursorAfter } from '../browser/session-sync/mirror-paging.js';
8
8
  import { transcriptIsFragment } from '../core/session-sync/fragment.js';
9
9
  import { onTabVisible } from '../browser/session-sync/visibility.js';
10
10
  import { useSandboxConnectionStore } from '../browser/stores/sandbox-connection-store.js';
11
- import { hasOnlyCacheSourcedMessages, useSyncStore } from '../browser/stores/sync-store.js';
11
+ import { hasOnlyCacheSourcedMessages, hasRuntimeConfirmedMessage, useSyncStore } from '../browser/stores/sync-store.js';
12
12
  import { useCurrentRuntime } from './use-current-runtime.js';
13
13
  import { selectSessionRows } from './session-transcript-subscription.js';
14
14
  import { canQueryRuntimeSession } from './use-opencode-sessions/index.js';
15
- export { loadSessionRuntimeStatus, loadSessionTranscriptMessages };
15
+ export { loadSessionExportMessages, loadSessionRuntimeStatus, loadSessionTranscriptMessages };
16
16
  const EMPTY_DIFFS = [];
17
17
  const EMPTY_TODOS = [];
18
18
  const IDLE_STATUS = { type: 'idle' };
@@ -197,21 +197,23 @@ export function useSessionSync(sessionId, options = {}) {
197
197
  setIsLoadingOlderMirror(false);
198
198
  mirrorPageInFlight.current = null;
199
199
  }, [beeuniSessionScope, sessionId]);
200
+ const savedCursorCovered = useSyncStore(() => !!mirrorCursor && !sync.hasOlder && hasRuntimeConfirmedMessage(sessionId, mirrorCursor));
201
+ const savedCursor = savedCursorCovered ? null : mirrorCursor;
200
202
  const loadOlder = useCallback(() => {
201
203
  const source = chooseOlderSource({
202
204
  runtimeHasOlder: sync.hasOlder,
203
- mirrorCursor,
205
+ mirrorCursor: savedCursor,
204
206
  });
205
207
  if (source === 'runtime')
206
208
  return controller.loadOlder();
207
- if (source !== 'mirror' || !mirrorCursor)
209
+ if (source !== 'mirror' || !savedCursor)
208
210
  return Promise.resolve();
209
211
  if (mirrorPageInFlight.current)
210
212
  return mirrorPageInFlight.current;
211
213
  setIsLoadingOlderMirror(true);
212
214
  const pending = loadOlderSessionTranscriptMirror({
213
215
  beeuniSessionScope,
214
- before: mirrorCursor,
216
+ before: savedCursor,
215
217
  child: savedChild ? sessionId : undefined,
216
218
  })
217
219
  .then((envelope) => {
@@ -231,7 +233,7 @@ export function useSessionSync(sessionId, options = {}) {
231
233
  });
232
234
  mirrorPageInFlight.current = pending;
233
235
  return pending;
234
- }, [controller, beeuniSessionScope, mirrorCursor, savedChild, sessionId, sync.hasOlder]);
236
+ }, [controller, beeuniSessionScope, savedCursor, savedChild, sessionId, sync.hasOlder]);
235
237
  const retryTranscript = useCallback(() => {
236
238
  void controller.reconcile('manual');
237
239
  }, [controller]);
@@ -243,7 +245,7 @@ export function useSessionSync(sessionId, options = {}) {
243
245
  isBusy,
244
246
  isLoading,
245
247
  mirrorState,
246
- hasOlder: sync.hasOlder || Boolean(mirrorCursor),
248
+ hasOlder: sync.hasOlder || Boolean(savedCursor),
247
249
  isLoadingOlder: sync.isLoadingOlder || isLoadingOlderMirror,
248
250
  loadOlder,
249
251
  diffs: diffs ?? EMPTY_DIFFS,
@@ -1,21 +1,11 @@
1
1
  'use client';
2
2
  import { useQuery } from '@tanstack/react-query';
3
- import { getSessionTranscriptSync } from '../core/rest/projects-client/sessions.js';
4
- import { OPEN_BUNDLE_TRANSCRIPT_LIMIT, openBundleHistory, settledOpenBundle, } from '../core/session/open-bundle.js';
5
- import { savedCopyEmptyRoot } from '../core/session-sync/saved-transcript.js';
3
+ import { loadSavedTranscript, savedCopyEmptyRoot } from '../core/session-sync/saved-transcript.js';
6
4
  import { qk } from './query-keys.js';
7
5
  export function useSessionTranscriptHistory(projectId, sessionId, enabled) {
8
6
  const query = useQuery({
9
7
  queryKey: [...qk.project.session(projectId, sessionId), 'transcript-history'],
10
- queryFn: ({ signal }) => {
11
- const bundle = settledOpenBundle(projectId, sessionId);
12
- return ((bundle && openBundleHistory(bundle)) ??
13
- getSessionTranscriptSync(projectId, sessionId, {
14
- limit: OPEN_BUNDLE_TRANSCRIPT_LIMIT,
15
- history: true,
16
- signal,
17
- }));
18
- },
8
+ queryFn: ({ signal }) => loadSavedTranscript(projectId, sessionId, { signal }),
19
9
  enabled: enabled && !!projectId && !!sessionId,
20
10
  staleTime: 0,
21
11
  gcTime: 0,
@@ -11,6 +11,7 @@ export interface SessionTurnObservation {
11
11
  turns: SessionTurn[];
12
12
  last_ended?: SessionTurnEnded;
13
13
  recent_failures?: SessionTurnFailure[];
14
+ can_stop?: boolean;
14
15
  working?: SessionWorkingVerdict;
15
16
  atMs: number;
16
17
  }
@@ -35,3 +36,4 @@ export declare function readSessionTurnObservation(projectId: string, sessionId:
35
36
  }): Promise<SessionTurnObservation>;
36
37
  export declare function useSessionWorking(projectId: string, sessionId: string, options?: UseSessionWorkingOptions): WorkingProjection;
37
38
  export declare function useSessionTurnOutcome(projectId: string, sessionId: string): SessionTurnOutcome;
39
+ export declare function useSessionCanStop(projectId: string, sessionId: string): boolean;
@@ -59,6 +59,7 @@ export async function readSessionTurnObservation(projectId, sessionId, options =
59
59
  turns: turn.turns,
60
60
  last_ended: turn.last_ended,
61
61
  recent_failures: turn.recent_failures,
62
+ ...(turn.can_stop !== undefined ? { can_stop: turn.can_stop } : {}),
62
63
  atMs: turn.atMs,
63
64
  };
64
65
  }
@@ -70,6 +71,7 @@ export async function readSessionTurnObservation(projectId, sessionId, options =
70
71
  turns: status.turns ?? [],
71
72
  last_ended: status.last_ended,
72
73
  recent_failures: status.recent_failures,
74
+ ...(typeof status.can_stop === 'boolean' ? { can_stop: status.can_stop } : {}),
73
75
  atMs,
74
76
  };
75
77
  });
@@ -215,3 +217,11 @@ export function useSessionTurnOutcome(projectId, sessionId) {
215
217
  }, [settleInMs, queryClient, projectId, sessionId]);
216
218
  return useMemo(() => ({ last_ended: lastEnded, recent_failures: recentFailures, atMs }), [lastEnded, recentFailures, atMs]);
217
219
  }
220
+ export function useSessionCanStop(projectId, sessionId) {
221
+ const query = useQuery({
222
+ queryKey: qk.project.sessionTurn(projectId, sessionId),
223
+ queryFn: () => readSessionTurnObservation(projectId, sessionId, { bundle: false }),
224
+ enabled: false,
225
+ });
226
+ return query.data?.can_stop !== false;
227
+ }
@@ -6,6 +6,7 @@ import { type AbortSettlement, type PromptPart } from './use-opencode-sessions/i
6
6
  import { type SessionPicks } from './use-session-picks.js';
7
7
  export type SessionPhase = 'starting' | 'ready' | 'error';
8
8
  export declare function shouldRetrySessionStart(failureCount: number, error: unknown, sessionId: string): boolean;
9
+ export declare function presenceReturnReopensSession(heldByThisTab: boolean, cachedStage: string | null | undefined): boolean;
9
10
  export declare const SESSION_START_POLL_MS = 1500;
10
11
  export declare function resolveSessionRuntimeUrl(start: Pick<SessionStartResult, 'stage' | 'sandbox' | 'runtime_url'> | null | undefined): string | null;
11
12
  export interface SendOptions {
@@ -153,6 +154,11 @@ export declare function useSession(projectId: string, sessionId: string, options
153
154
  runtimePhase: import("./use-runtime-phase.js").RuntimePhase;
154
155
  working: import("../index.js").WorkingProjection;
155
156
  isBusy: boolean;
157
+ canStop: boolean;
158
+ presenceAwake: {
159
+ until: Date;
160
+ capAt: Date;
161
+ } | null;
156
162
  isCompacting: boolean;
157
163
  isLoading: boolean;
158
164
  savedTranscript: import("../core/session-sync/saved-transcript.js").SavedTranscript;