@beeuni/sdk 0.1.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 (291) hide show
  1. package/README.md +53 -92
  2. package/dist/beeuni.esm.min.js +36 -34
  3. package/dist/beeuni.global.js +36 -34
  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 +7 -0
  12. package/dist/browser/stores/sync-store.js +122 -35
  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 +99 -34
  18. package/dist/core/client/beeuni.js +5 -11
  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-catalog.d.ts +0 -4
  22. package/dist/core/client/facade-catalog.js +1 -5
  23. package/dist/core/client/facade-identity.d.ts +1 -0
  24. package/dist/core/client/facade-identity.js +1 -0
  25. package/dist/core/client/project-access-resources.d.ts +3 -1
  26. package/dist/core/client/project-access-resources.js +1 -1
  27. package/dist/core/client/project-connections.d.ts +2 -2
  28. package/dist/core/client/project-connections.js +2 -2
  29. package/dist/core/client/project-operations-admin.d.ts +4 -4
  30. package/dist/core/client/project-operations-review.d.ts +2 -3
  31. package/dist/core/client/project-operations-review.js +1 -0
  32. package/dist/core/client/project-operations-workspace.d.ts +4 -4
  33. package/dist/core/client/project-operations-workspace.js +1 -1
  34. package/dist/core/client/project-operations.d.ts +9 -19
  35. package/dist/core/client/project-operations.js +2 -6
  36. package/dist/core/client/project-platform-security.d.ts +1 -1
  37. package/dist/core/client/project-platform-settings.d.ts +1 -1
  38. package/dist/core/client/session-context.d.ts +2 -0
  39. package/dist/core/client/session-handle.d.ts +13 -0
  40. package/dist/core/client/session-lifecycle-resources.d.ts +11 -0
  41. package/dist/core/client/session-lifecycle-resources.js +4 -0
  42. package/dist/core/client/session-lifecycle-security.d.ts +2 -0
  43. package/dist/core/client/session-lifecycle-security.js +1 -0
  44. package/dist/core/client/session-readiness.d.ts +2 -0
  45. package/dist/core/client/session-readiness.js +20 -1
  46. package/dist/core/http/api-client.d.ts +1 -7
  47. package/dist/core/http/api-client.js +2 -37
  48. package/dist/core/http/auth.d.ts +2 -4
  49. package/dist/core/http/auth.js +2 -8
  50. package/dist/core/http/config.d.ts +2 -0
  51. package/dist/core/http/response-error.js +26 -11
  52. package/dist/core/http/transport.js +6 -1
  53. package/dist/core/models/composer-model.d.ts +14 -0
  54. package/dist/core/models/composer-model.js +21 -1
  55. package/dist/core/rest/platform-client/capture.d.ts +363 -0
  56. package/dist/core/rest/platform-client/capture.js +116 -0
  57. package/dist/core/rest/platform-client/client-policy.d.ts +8 -0
  58. package/dist/core/rest/platform-client/client-policy.js +30 -0
  59. package/dist/core/rest/platform-client/index.d.ts +4 -4
  60. package/dist/core/rest/platform-client/index.js +4 -4
  61. package/dist/core/rest/platform-client/lifecycle.js +18 -12
  62. package/dist/core/rest/platform-client/shared.d.ts +5 -0
  63. package/dist/core/rest/platform-client/shared.js +18 -0
  64. package/dist/core/rest/platform-client/types.d.ts +1 -1
  65. package/dist/core/rest/projects-client/account-secret-resources.d.ts +2 -1
  66. package/dist/core/rest/projects-client/accounts.d.ts +34 -0
  67. package/dist/core/rest/projects-client/accounts.js +10 -0
  68. package/dist/core/rest/projects-client/admin-providers.d.ts +0 -61
  69. package/dist/core/rest/projects-client/admin-providers.js +0 -6
  70. package/dist/core/rest/projects-client/agent-config.d.ts +3 -0
  71. package/dist/core/rest/projects-client/apps-fleet.d.ts +5 -1
  72. package/dist/core/rest/projects-client/apps-fleet.js +2 -2
  73. package/dist/core/rest/projects-client/apps.d.ts +11 -1
  74. package/dist/core/rest/projects-client/change-requests.d.ts +2 -0
  75. package/dist/core/rest/projects-client/change-requests.js +2 -0
  76. package/dist/core/rest/projects-client/connectors.d.ts +13 -55
  77. package/dist/core/rest/projects-client/connectors.js +5 -29
  78. package/dist/core/rest/projects-client/files.d.ts +3 -0
  79. package/dist/core/rest/projects-client/files.js +4 -0
  80. package/dist/core/rest/projects-client/gateway.d.ts +5 -0
  81. package/dist/core/rest/projects-client/git-backend.d.ts +1 -10
  82. package/dist/core/rest/projects-client/git-backend.js +0 -10
  83. package/dist/core/rest/projects-client/iam-actions.d.ts +76 -0
  84. package/dist/core/rest/projects-client/iam-actions.js +101 -0
  85. package/dist/core/rest/projects-client/iam-sso.d.ts +0 -10
  86. package/dist/core/rest/projects-client/iam-sso.js +0 -3
  87. package/dist/core/rest/projects-client/iam.d.ts +1 -0
  88. package/dist/core/rest/projects-client/iam.js +1 -0
  89. package/dist/core/rest/projects-client/index.d.ts +1 -4
  90. package/dist/core/rest/projects-client/index.js +1 -4
  91. package/dist/core/rest/projects-client/notifications.d.ts +1 -1
  92. package/dist/core/rest/projects-client/notifications.js +4 -0
  93. package/dist/core/rest/projects-client/projects.d.ts +4 -2
  94. package/dist/core/rest/projects-client/projects.js +4 -2
  95. package/dist/core/rest/projects-client/review-items.d.ts +2 -0
  96. package/dist/core/rest/projects-client/review-items.js +4 -0
  97. package/dist/core/rest/projects-client/review.d.ts +17 -3
  98. package/dist/core/rest/projects-client/review.js +11 -0
  99. package/dist/core/rest/projects-client/sandbox.d.ts +8 -9
  100. package/dist/core/rest/projects-client/secrets.d.ts +3 -0
  101. package/dist/core/rest/projects-client/session-attachments.js +14 -1
  102. package/dist/core/rest/projects-client/session-sandbox.d.ts +1 -1
  103. package/dist/core/rest/projects-client/sessions.d.ts +31 -4
  104. package/dist/core/rest/projects-client/sessions.js +34 -4
  105. package/dist/core/runtime/runtime-rest-client.js +1 -1
  106. package/dist/core/runtime/runtime-types.d.ts +1 -0
  107. package/dist/core/session/control-stream.d.ts +1 -0
  108. package/dist/core/session/control-stream.js +18 -1
  109. package/dist/core/session/open-bundle.d.ts +4 -4
  110. package/dist/core/session/open-bundle.js +29 -46
  111. package/dist/core/session/turn-end-watch.d.ts +47 -0
  112. package/dist/core/session/turn-end-watch.js +141 -0
  113. package/dist/core/session-log/guard.d.ts +6 -0
  114. package/dist/core/session-log/guard.js +109 -0
  115. package/dist/core/session-log/index.d.ts +7 -0
  116. package/dist/core/session-log/index.js +5 -0
  117. package/dist/core/session-log/saved-log.d.ts +10 -0
  118. package/dist/core/session-log/saved-log.js +25 -0
  119. package/dist/core/session-log/source.d.ts +25 -0
  120. package/dist/core/session-log/source.js +30 -0
  121. package/dist/core/session-log/store.d.ts +21 -0
  122. package/dist/core/session-log/store.js +126 -0
  123. package/dist/core/session-log/tool-call.d.ts +11 -0
  124. package/dist/core/session-log/tool-call.js +65 -0
  125. package/dist/core/session-log/turns/blocks.d.ts +60 -0
  126. package/dist/core/session-log/turns/blocks.js +124 -0
  127. package/dist/core/session-log/turns/history.d.ts +32 -0
  128. package/dist/core/session-log/turns/history.js +99 -0
  129. package/dist/core/session-log/turns/index.d.ts +4 -0
  130. package/dist/core/session-log/turns/index.js +4 -0
  131. package/dist/core/session-log/turns/metrics.d.ts +21 -0
  132. package/dist/core/session-log/turns/metrics.js +29 -0
  133. package/dist/core/session-log/turns/turn.d.ts +55 -0
  134. package/dist/core/session-log/turns/turn.js +279 -0
  135. package/dist/core/session-log/turns/v1-parts.d.ts +8 -0
  136. package/dist/core/session-log/turns/v1-parts.js +43 -0
  137. package/dist/core/session-log/types.d.ts +264 -0
  138. package/dist/core/session-log/types.js +6 -0
  139. package/dist/core/session-log/v1-view.d.ts +13 -0
  140. package/dist/core/session-log/v1-view.js +215 -0
  141. package/dist/core/session-log/v1.d.ts +31 -0
  142. package/dist/core/session-log/v1.js +473 -0
  143. package/dist/core/session-sync/copy-store-types.d.ts +19 -0
  144. package/dist/core/session-sync/copy-store-types.js +1 -0
  145. package/dist/core/session-sync/saved-copy-store.js +25 -4
  146. package/dist/core/session-sync/saved-transcript.d.ts +15 -1
  147. package/dist/core/session-sync/saved-transcript.js +55 -0
  148. package/dist/core/session-sync/session-sync-controller.js +3 -0
  149. package/dist/core/stream/event-stream.js +2 -2
  150. package/dist/core/turns/classify.d.ts +2 -1
  151. package/dist/core/turns/classify.js +7 -3
  152. package/dist/core/turns/edit-blocks.d.ts +1 -0
  153. package/dist/core/turns/edit-blocks.js +15 -0
  154. package/dist/core/turns/parts.d.ts +3 -2
  155. package/dist/core/turns/parts.js +4 -2
  156. package/dist/core/turns/segments/burst-summary.d.ts +1 -0
  157. package/dist/core/turns/segments/burst-summary.js +24 -0
  158. package/dist/core/turns/segments/group-steps.d.ts +4 -3
  159. package/dist/core/turns/segments/group-steps.js +9 -6
  160. package/dist/core/turns/segments/index.d.ts +1 -0
  161. package/dist/core/turns/segments/index.js +1 -0
  162. package/dist/core/turns/segments/narration.d.ts +5 -4
  163. package/dist/core/turns/segments/narration.js +12 -3
  164. package/dist/core/turns/segments/session-activity-groups.d.ts +2 -1
  165. package/dist/core/turns/segments/session-activity-groups.js +3 -1
  166. package/dist/core/turns/segments/step-label.d.ts +2 -1
  167. package/dist/core/turns/segments/step-label.js +3 -1
  168. package/dist/core/turns/segments/work-split.d.ts +10 -0
  169. package/dist/core/turns/segments/work-split.js +40 -0
  170. package/dist/core/turns/segments/working-turn.d.ts +4 -2
  171. package/dist/core/turns/segments/working-turn.js +9 -8
  172. package/dist/core/turns/state.d.ts +2 -7
  173. package/dist/core/turns/state.js +28 -134
  174. package/dist/core/turns/tool-kind.d.ts +1 -1
  175. package/dist/core/turns/tool-kind.js +3 -2
  176. package/dist/core/turns/tool-registry.js +1 -0
  177. package/dist/core/turns/tools/tool-meta.d.ts +3 -2
  178. package/dist/core/turns/tools/tool-meta.js +5 -2
  179. package/dist/core/turns/tools/tool-outcome.d.ts +2 -1
  180. package/dist/core/turns/tools/tool-outcome.js +3 -1
  181. package/dist/core/turns/types.d.ts +1 -1
  182. package/dist/core/turns/view-model.js +1 -13
  183. package/dist/core/warm-session-pool.d.ts +29 -0
  184. package/dist/core/warm-session-pool.js +127 -0
  185. package/dist/genui/catalog.d.ts +28 -0
  186. package/dist/genui/catalog.js +396 -0
  187. package/dist/genui/expansion.d.ts +3 -0
  188. package/dist/genui/expansion.js +295 -0
  189. package/dist/genui/fence-entry.d.ts +2 -0
  190. package/dist/genui/fence-entry.js +1 -0
  191. package/dist/genui/fence.d.ts +21 -0
  192. package/dist/genui/fence.js +138 -0
  193. package/dist/genui/index.d.ts +6 -0
  194. package/dist/genui/index.js +6 -0
  195. package/dist/genui/markdown.d.ts +12 -0
  196. package/dist/genui/markdown.js +104 -0
  197. package/dist/genui/parse.d.ts +6 -0
  198. package/dist/genui/parse.js +75 -0
  199. package/dist/genui/prompt.d.ts +2 -0
  200. package/dist/genui/prompt.js +206 -0
  201. package/dist/genui/react/default-components.d.ts +2 -0
  202. package/dist/genui/react/default-components.js +72 -0
  203. package/dist/genui/react/default-components.test.d.ts +1 -0
  204. package/dist/genui/react/default-components.test.js +94 -0
  205. package/dist/genui/react/genui-block-disabled.test.d.ts +1 -0
  206. package/dist/genui/react/genui-block-disabled.test.js +50 -0
  207. package/dist/genui/react/genui-block.d.ts +28 -0
  208. package/dist/genui/react/genui-block.js +91 -0
  209. package/dist/genui/react/genui-block.test.d.ts +1 -0
  210. package/dist/genui/react/genui-block.test.js +205 -0
  211. package/dist/genui/react/index.d.ts +2 -0
  212. package/dist/genui/react/index.js +2 -0
  213. package/dist/genui/share.d.ts +5 -0
  214. package/dist/genui/share.js +30 -0
  215. package/dist/genui/test-fixtures.d.ts +2 -0
  216. package/dist/genui/test-fixtures.js +16 -0
  217. package/dist/genui/types.d.ts +29 -0
  218. package/dist/genui/types.js +1 -0
  219. package/dist/genui/urls.d.ts +1 -0
  220. package/dist/genui/urls.js +18 -0
  221. package/dist/genui/validate.d.ts +9 -0
  222. package/dist/genui/validate.js +116 -0
  223. package/dist/index.d.ts +4 -4
  224. package/dist/index.js +4 -4
  225. package/dist/internal/connector-catalog.d.ts +48 -0
  226. package/dist/internal/connector-catalog.js +27 -0
  227. package/dist/internal/host-configuration.d.ts +22 -0
  228. package/dist/internal/host-configuration.js +28 -0
  229. package/dist/internal/platform-admin-react.d.ts +12 -0
  230. package/dist/internal/platform-admin-react.js +13 -0
  231. package/dist/internal/platform-admin.d.ts +9 -0
  232. package/dist/internal/platform-admin.js +9 -0
  233. package/dist/platform/ui.d.ts +8 -2
  234. package/dist/platform/ui.js +19 -4
  235. package/dist/react/chat/use-chat-turns.d.ts +1 -1
  236. package/dist/react/human-presence.d.ts +9 -0
  237. package/dist/react/human-presence.js +12 -0
  238. package/dist/react/index.d.ts +4 -13
  239. package/dist/react/index.js +4 -13
  240. package/dist/react/opencode.d.ts +1 -0
  241. package/dist/react/opencode.js +1 -0
  242. package/dist/react/query-contracts.d.ts +6 -0
  243. package/dist/react/query-contracts.js +6 -0
  244. package/dist/react/query-keys.d.ts +17 -2
  245. package/dist/react/query-keys.js +24 -2
  246. package/dist/react/reset-identity-state.js +2 -0
  247. package/dist/react/session-cache-write.d.ts +1 -0
  248. package/dist/react/session-cache-write.js +20 -0
  249. package/dist/react/session-transcript-subscription.js +3 -1
  250. package/dist/react/sign-in-with-beeuni.d.ts +6 -6
  251. package/dist/react/turn-end-handoff.d.ts +13 -0
  252. package/dist/react/turn-end-handoff.js +101 -0
  253. package/dist/react/use-admin-providers.d.ts +3 -6
  254. package/dist/react/use-admin-providers.js +1 -11
  255. package/dist/react/use-capture.d.ts +47 -0
  256. package/dist/react/use-capture.js +173 -0
  257. package/dist/react/use-feature-flag.js +1 -0
  258. package/dist/react/use-opencode-events/handle-event.d.ts +1 -0
  259. package/dist/react/use-opencode-events/handle-event.js +5 -0
  260. package/dist/react/use-opencode-events/handle-interaction-event.js +8 -3
  261. package/dist/react/use-opencode-events/handle-session-event.js +13 -8
  262. package/dist/react/use-opencode-events/handler-context.d.ts +1 -0
  263. package/dist/react/use-opencode-events/helpers.d.ts +1 -0
  264. package/dist/react/use-opencode-events/helpers.js +11 -1
  265. package/dist/react/use-opencode-events/index.js +1 -0
  266. package/dist/react/use-opencode-local.d.ts +5 -1
  267. package/dist/react/use-opencode-local.js +15 -2
  268. package/dist/react/use-opencode-sessions/commands.d.ts +11 -1
  269. package/dist/react/use-opencode-sessions/commands.js +21 -1
  270. package/dist/react/use-opencode-sessions/permissions.js +28 -1
  271. package/dist/react/use-project-sessions.d.ts +1 -1
  272. package/dist/react/use-project-sessions.js +6 -5
  273. package/dist/react/use-prompt-attachments.js +1 -0
  274. package/dist/react/use-review-summary.d.ts +13 -0
  275. package/dist/react/use-review-summary.js +32 -0
  276. package/dist/react/use-runtime-reconnect.js +1 -1
  277. package/dist/react/use-session-list-state.d.ts +6 -0
  278. package/dist/react/use-session-list-state.js +33 -0
  279. package/dist/react/use-session-log.d.ts +16 -0
  280. package/dist/react/use-session-log.js +143 -0
  281. package/dist/react/use-session-messages.d.ts +2 -1
  282. package/dist/react/use-session-messages.js +15 -14
  283. package/dist/react/use-session-stream.js +12 -1
  284. package/dist/react/use-session-sync.d.ts +2 -2
  285. package/dist/react/use-session-sync.js +10 -8
  286. package/dist/react/use-session-transcript-history.js +2 -12
  287. package/dist/react/use-session-working.d.ts +2 -0
  288. package/dist/react/use-session-working.js +10 -0
  289. package/dist/react/use-session.d.ts +6 -0
  290. package/dist/react/use-session.js +88 -6
  291. package/package.json +62 -8
@@ -20,4 +20,5 @@ export declare function createEventHandler(deps: {
20
20
  reconcileSessionTail?: (sessionID: string, reason: SessionSyncReason) => Promise<void>;
21
21
  userPartsGraceMs?: number;
22
22
  projectId?: string | null;
23
+ sessionId?: string | null;
23
24
  }): (event: RuntimeEvent) => void;
@@ -44,6 +44,11 @@ export function createEventHandler(deps) {
44
44
  const statusBeforeEvent = sessionID
45
45
  ? useSyncStore.getState().sessionStatus[sessionID]
46
46
  : undefined;
47
+ if (event.type.startsWith('log.')) {
48
+ if (deps.sessionId)
49
+ useSyncStore.getState().applySessionLogEvent(deps.sessionId, event);
50
+ return;
51
+ }
47
52
  deps.applySyncEvent(event);
48
53
  if (event.type.startsWith('message.'))
49
54
  handleMessageEvent(event, ctx);
@@ -1,7 +1,12 @@
1
1
  import { notifyPermissionRequest, notifyQuestion } from '../../platform/ui.js';
2
- import { asStringOrUndefined } from './helpers.js';
2
+ import { qk } from '../query-keys.js';
3
+ import { asStringOrUndefined, notificationSessionId } from './helpers.js';
3
4
  export function handleInteractionEvent(event, ctx) {
4
5
  const { addPermission, removePermission, addQuestion, removeQuestion, getSessionTitle } = ctx;
6
+ const notifiedId = (runtimeId) => notificationSessionId(ctx.queryClient, ctx.projectId, ctx.sessionId, runtimeId, true) ?? runtimeId;
7
+ if (event.type.startsWith('question.') && ctx.projectId) {
8
+ ctx.queryClient.invalidateQueries({ queryKey: qk.project.needsInput(ctx.projectId) });
9
+ }
5
10
  switch (event.type) {
6
11
  case 'permission.asked': {
7
12
  const props = event.properties;
@@ -9,7 +14,7 @@ export function handleInteractionEvent(event, ctx) {
9
14
  addPermission(props);
10
15
  const rawProps = props;
11
16
  const toolName = asStringOrUndefined(rawProps.tool) ?? asStringOrUndefined(rawProps.type) ?? 'a tool';
12
- notifyPermissionRequest(props.sessionID, toolName, getSessionTitle(props.sessionID));
17
+ notifyPermissionRequest(notifiedId(props.sessionID), toolName, getSessionTitle(props.sessionID));
13
18
  }
14
19
  break;
15
20
  }
@@ -24,7 +29,7 @@ export function handleInteractionEvent(event, ctx) {
24
29
  if (props.id && props.sessionID) {
25
30
  addQuestion(props);
26
31
  const questionText = props.questions[0]?.question || props.questions[0]?.header || 'Beeuni needs your input';
27
- notifyQuestion(props.sessionID, questionText, getSessionTitle(props.sessionID));
32
+ notifyQuestion(notifiedId(props.sessionID), questionText, getSessionTitle(props.sessionID));
28
33
  }
29
34
  break;
30
35
  }
@@ -1,9 +1,10 @@
1
1
  import { useSyncStore } from '../../browser/stores/sync-store.js';
2
2
  import { isAbortError } from '../../core/http/abort-error.js';
3
3
  import { getClient } from '../../core/runtime/client.js';
4
+ import { extractGatewayErrorDetails, unwrapError } from '../../core/turns/errors.js';
4
5
  import { notifySessionError, notifyTaskComplete } from '../../platform/ui.js';
5
6
  import { runtimeKeys } from '../use-opencode-sessions/index.js';
6
- import { patchBeeuniSessionTitleMirrors, readSessionInfo, realRuntimeTitle, refetchBeeuniSessionMirrors, } from './helpers.js';
7
+ import { notificationSessionId, patchBeeuniSessionTitleMirrors, readSessionInfo, realRuntimeTitle, refetchBeeuniSessionMirrors, } from './helpers.js';
7
8
  export function handleSessionEvent(event, ctx, statusBeforeEvent) {
8
9
  if (event.type === 'session.created' || event.type === 'session.updated')
9
10
  handleSessionLifecycle(event, ctx);
@@ -146,6 +147,11 @@ function handleSessionCompaction(event, ctx) {
146
147
  break;
147
148
  }
148
149
  }
150
+ function notifyTurnEnd(ctx, runtimeId, notify) {
151
+ const id = notificationSessionId(ctx.queryClient, ctx.projectId, ctx.sessionId, runtimeId);
152
+ if (id)
153
+ notify(id);
154
+ }
149
155
  function handleSessionCompletion(event, ctx, statusBeforeEvent) {
150
156
  const { reconcileTail, getSessionTitle, invalidateWorkspaceFilesAfterTurn } = ctx;
151
157
  switch (event.type) {
@@ -164,7 +170,7 @@ function handleSessionCompletion(event, ctx, statusBeforeEvent) {
164
170
  void reconcileTail(sessionID, 'turn-end');
165
171
  const prevStatus = statusBeforeEvent;
166
172
  if (status.type === 'idle' && prevStatus && prevStatus.type !== 'idle') {
167
- notifyTaskComplete(sessionID, getSessionTitle(sessionID));
173
+ notifyTurnEnd(ctx, sessionID, (id) => notifyTaskComplete(id, getSessionTitle(sessionID)));
168
174
  invalidateWorkspaceFilesAfterTurn();
169
175
  }
170
176
  }
@@ -176,7 +182,7 @@ function handleSessionCompletion(event, ctx, statusBeforeEvent) {
176
182
  void reconcileTail(sessionID, 'turn-end');
177
183
  const prevStatus = statusBeforeEvent;
178
184
  if (prevStatus && prevStatus.type !== 'idle') {
179
- notifyTaskComplete(sessionID, getSessionTitle(sessionID));
185
+ notifyTurnEnd(ctx, sessionID, (id) => notifyTaskComplete(id, getSessionTitle(sessionID)));
180
186
  invalidateWorkspaceFilesAfterTurn();
181
187
  }
182
188
  }
@@ -195,11 +201,10 @@ function handleSessionFailure(event, ctx) {
195
201
  const sessionID = props.sessionID;
196
202
  const error = props.error;
197
203
  stopCompaction(sessionID);
198
- const rawMessage = error.data.message;
199
- const errorTitle = error.name ||
200
- (typeof rawMessage === 'string' ? rawMessage : undefined) ||
201
- 'An error occurred';
202
- notifySessionError(sessionID, errorTitle, getSessionTitle(sessionID));
204
+ const errorTitle = extractGatewayErrorDetails(error)?.message || unwrapError(error);
205
+ if (!isAbortError(error)) {
206
+ notifyTurnEnd(ctx, sessionID, (id) => notifySessionError(id, errorTitle, getSessionTitle(sessionID)));
207
+ }
203
208
  const key = runtimeKeys.runtimeMessages(sessionID);
204
209
  queryClient.cancelQueries({ queryKey: key });
205
210
  queryClient.setQueryData(key, (old) => {
@@ -19,6 +19,7 @@ export type HandlerContext = {
19
19
  reconcileSessionTail?: (sessionID: string, reason: SessionSyncReason) => Promise<void>;
20
20
  userPartsGraceMs?: number;
21
21
  projectId?: string | null;
22
+ sessionId?: string | null;
22
23
  } & {
23
24
  reconcileTail: (sessionID: string, reason: import('../../core/session-sync/session-sync-controller.js').SessionSyncReason) => Promise<void>;
24
25
  getSessionTitle: (sessionID: string) => string | undefined;
@@ -25,3 +25,4 @@ export declare function scheduleProjectMetadataRefetch(queryClient: QueryClient)
25
25
  export declare function refetchBeeuniSessionMirrors(queryClient: QueryClient, projectId: string | null): void;
26
26
  export declare function realRuntimeTitle(value: unknown): string | null;
27
27
  export declare function patchBeeuniSessionTitleMirrors(queryClient: QueryClient, projectId: string | null, nativeSessionId: string, title: string | null): void;
28
+ export declare function notificationSessionId(queryClient: QueryClient, projectId: string | null | undefined, streamSessionId: string | null | undefined, runtimeId: string, subAgentAsStream?: boolean): string | null;
@@ -1,7 +1,7 @@
1
1
  import { STREAM_OBSERVATION_MAX_MS } from '../../core/session/working.js';
2
2
  import { runtimeKeys } from '../use-opencode-sessions/index.js';
3
3
  import { qk } from '../query-keys.js';
4
- import { updateCachedProjectSessions } from '../session-cache-write.js';
4
+ import { findCachedProjectSession, updateCachedProjectSessions } from '../session-cache-write.js';
5
5
  export const WIRE_STATUS_FILL_FRESHNESS_MS = STREAM_OBSERVATION_MAX_MS;
6
6
  export function shouldSkipStatusFill(input) {
7
7
  if (!input.hasSlot)
@@ -124,3 +124,13 @@ export function patchBeeuniSessionTitleMirrors(queryClient, projectId, nativeSes
124
124
  return changed ? next : rows;
125
125
  });
126
126
  }
127
+ export function notificationSessionId(queryClient, projectId, streamSessionId, runtimeId, subAgentAsStream = false) {
128
+ if (!streamSessionId)
129
+ return runtimeId;
130
+ const row = projectId
131
+ ? findCachedProjectSession(queryClient, projectId, (session) => (session.runtime_session_id ?? session.opencode_session_id) === runtimeId)
132
+ : undefined;
133
+ if (row)
134
+ return row.session_id;
135
+ return subAgentAsStream ? streamSessionId : null;
136
+ }
@@ -101,6 +101,7 @@ export function useRuntimeEventStream(options = {}) {
101
101
  fetchLspDiagnosticsDebounced,
102
102
  reconcileSessionTail: reconcileSessionTailFromRegistry,
103
103
  projectId,
104
+ sessionId: options.projectId && options.sessionId ? options.sessionId : null,
104
105
  });
105
106
  const hydrate = (options) => hydrateCore({
106
107
  client,
@@ -11,6 +11,10 @@ export interface UseRuntimeLocalOptions {
11
11
  defaultAgentName?: string | null;
12
12
  freeTier?: boolean;
13
13
  resolveServerDefault?: (agentName: string | undefined) => ModelKey | undefined;
14
+ projectSession?: {
15
+ metadata?: Record<string, unknown> | null;
16
+ } | null;
17
+ lastPromptModel?: ModelKey;
14
18
  }
15
19
  export interface RuntimeLocalAgent {
16
20
  current: Agent | undefined;
@@ -64,7 +68,7 @@ export declare function resolveCurrentAgentName(input: {
64
68
  explicitAgentName?: string;
65
69
  lastAgentName?: string;
66
70
  }): string | undefined;
67
- export declare function useRuntimeLocal({ agents: rawAgents, providers, config, sessionId, boundAgentName, defaultAgentName, resolveServerDefault, }: UseRuntimeLocalOptions): RuntimeLocal;
71
+ export declare function useRuntimeLocal({ agents: rawAgents, providers, config, sessionId, boundAgentName, defaultAgentName, resolveServerDefault, projectSession, lastPromptModel, }: UseRuntimeLocalOptions): RuntimeLocal;
68
72
  export type UseOpenCodeLocalOptions = UseRuntimeLocalOptions;
69
73
  export type OpenCodeLocalAgent = RuntimeLocalAgent;
70
74
  export type OpenCodeLocalModel = RuntimeLocalModel;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { flattenModels, isOfferedModel } from './model-flatten.js';
3
3
  import { composerSelectableAgents } from '../core/agents/composer-agents.js';
4
- import { resolveComposerModel } from '../core/models/composer-model.js';
4
+ import { resolveComposerModel, sessionModelCandidates } from '../core/models/composer-model.js';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { createAgentSelectionScope } from './agent-selection-scope.js';
7
7
  import { useBeeuniRouteProjectId } from './route-project.js';
@@ -65,7 +65,7 @@ export function resolveCurrentAgentName(input) {
65
65
  defaultAgentName ??
66
66
  (input.sessionId ? undefined : input.lastAgentName));
67
67
  }
68
- export function useRuntimeLocal({ agents: rawAgents, providers, config, sessionId, boundAgentName, defaultAgentName, resolveServerDefault, }) {
68
+ export function useRuntimeLocal({ agents: rawAgents, providers, config, sessionId, boundAgentName, defaultAgentName, resolveServerDefault, projectSession, lastPromptModel, }) {
69
69
  const providerMode = useMemo(() => modelProviderMode(providers), [providers]);
70
70
  const flatModels = useMemo(() => flattenModels(providers, { providerMode }), [providers, providerMode]);
71
71
  const projectId = useBeeuniRouteProjectId();
@@ -119,11 +119,23 @@ export function useRuntimeLocal({ agents: rawAgents, providers, config, sessionI
119
119
  return visibleAgents[0];
120
120
  }, [visibleAgents, currentAgentName]);
121
121
  const agentModelSlotKey = useMemo(() => agentScopedModelSelectionKey(providerMode, currentAgent?.name), [providerMode, currentAgent?.name]);
122
+ const sessionModels = useMemo(() => sessionModelCandidates({
123
+ session: projectSession,
124
+ lastPromptModel,
125
+ gatewayEnabled: providerMode === 'gateway',
126
+ }), [
127
+ projectSession?.metadata?.opencode_model,
128
+ projectSession?.metadata?.opencode_model_source,
129
+ lastPromptModel?.providerID,
130
+ lastPromptModel?.modelID,
131
+ providerMode,
132
+ ]);
122
133
  const modelResolution = useMemo(() => resolveComposerModel({
123
134
  models: flatModels,
124
135
  picks: [
125
136
  scopedSessionModelKey ? modelStore.getSessionModel(scopedSessionModelKey) : undefined,
126
137
  sessionId ? modelStore.getSessionModel(sessionId) : undefined,
138
+ ...sessionModels,
127
139
  modelStore.getSelectedModel(agentModelSlotKey),
128
140
  currentAgent ? modelStore.getSelectedModel(currentAgent.name) : undefined,
129
141
  ],
@@ -140,6 +152,7 @@ export function useRuntimeLocal({ agents: rawAgents, providers, config, sessionI
140
152
  agentModelSlotKey,
141
153
  currentAgent,
142
154
  resolveServerDefault,
155
+ sessionModels,
143
156
  config?.model,
144
157
  providers,
145
158
  modelStore,
@@ -1,5 +1,15 @@
1
1
  import type { Command } from '../../core/runtime/runtime-types.js';
2
- export declare function useRuntimeCommands(): import("@tanstack/react-query").UseQueryResult<Command[], Error>;
2
+ export declare function useRuntimeCommands(options?: UseRuntimeCommandsOptions): import("@tanstack/react-query").UseQueryResult<Command[], Error>;
3
+ export interface UseRuntimeCommandsOptions {
4
+ agent?: string;
5
+ }
6
+ export declare class CommandNotEnabledError extends Error {
7
+ readonly command: string;
8
+ readonly agent: string;
9
+ readonly status = 403;
10
+ readonly code = "command_not_enabled";
11
+ constructor(command: string, agent: string);
12
+ }
3
13
  export interface ExecuteRuntimeCommandInput {
4
14
  sessionId: string;
5
15
  command: string;
@@ -4,7 +4,8 @@ import { getClient } from '../../core/runtime/client.js';
4
4
  import { useRuntimeSupports } from '../use-runtime-supports.js';
5
5
  import { runtimeKeys, useRuntimeReady } from './keys.js';
6
6
  import { unwrap, asRuntimeList } from './shared.js';
7
- export function useRuntimeCommands() {
7
+ export function useRuntimeCommands(options = {}) {
8
+ const { agent } = options;
8
9
  const runtimeReady = useRuntimeReady();
9
10
  const supported = useRuntimeSupports('session.commands');
10
11
  return useQuery({
@@ -14,11 +15,26 @@ export function useRuntimeCommands() {
14
15
  const result = await client.command.list();
15
16
  return asRuntimeList(unwrap(result));
16
17
  },
18
+ select: agent
19
+ ? (commands) => commands.filter((command) => !command.agents || command.agents.includes(agent))
20
+ : undefined,
17
21
  enabled: runtimeReady && supported,
18
22
  staleTime: Infinity,
19
23
  gcTime: 10 * 60 * 1000,
20
24
  });
21
25
  }
26
+ export class CommandNotEnabledError extends Error {
27
+ command;
28
+ agent;
29
+ status = 403;
30
+ code = 'command_not_enabled';
31
+ constructor(command, agent) {
32
+ super(`The agent "${agent}" cannot run /${command}.`);
33
+ this.command = command;
34
+ this.agent = agent;
35
+ this.name = 'CommandNotEnabledError';
36
+ }
37
+ }
22
38
  export async function executeRuntimeCommand({ sessionId, command, args, agent, model, variant, }) {
23
39
  const client = getClient();
24
40
  const result = await client.session.command({
@@ -29,6 +45,10 @@ export async function executeRuntimeCommand({ sessionId, command, args, agent, m
29
45
  ...(model ? { model } : {}),
30
46
  ...(variant ? { variant } : {}),
31
47
  });
48
+ const body = result.error;
49
+ if (result.response?.status === 403 && body?.error === 'command_not_enabled') {
50
+ throw new CommandNotEnabledError(String(body.command ?? command), String(body.agent ?? agent ?? ''));
51
+ }
32
52
  unwrap(result);
33
53
  }
34
54
  export function useExecuteRuntimeCommand() {
@@ -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 { getSupabaseAccessToken } 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';
@@ -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
+ }
@@ -0,0 +1,16 @@
1
+ import { type SessionControlStreamOptions } from '../core/session/control-stream.js';
2
+ import { type LoadSessionLogPage, type SessionLogDialect } from '../core/session-log/source.js';
3
+ import type { SessionLog, SessionLogThread } from '../core/session-log/types.js';
4
+ import { type SessionMessagesOptions, type SessionMessagesSource } from './use-session-messages.js';
5
+ export declare function useSessionLog(session: SessionMessagesSource): (SessionLog & {
6
+ source: 'v1' | 'v2';
7
+ }) | 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;
10
+ export declare function useSessionLogSync(projectId: string, sessionId: string, options: {
11
+ enabled: boolean;
12
+ loader?: LoadSessionLogPage | null;
13
+ joinStream?: (options: SessionControlStreamOptions) => {
14
+ close: () => void;
15
+ };
16
+ }): SessionLogDialect;