@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
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+ import { useQueryClient } from '@tanstack/react-query';
3
+ import { useEffect, useRef } from 'react';
4
+ import { getSessionTurn } from '../core/rest/projects-client/sessions.js';
5
+ import { openSessionStream, sessionStreamConnected } from '../core/session/control-stream.js';
6
+ import { createTurnEndWatch, } from '../core/session/turn-end-watch.js';
7
+ import { unwrapError } from '../core/turns/errors.js';
8
+ import { hasNotifySink, notifySessionError, notifyTaskComplete } from '../platform/ui.js';
9
+ import { registerIdentityReset } from './identity-reset-registry.js';
10
+ import { findCachedProjectSession } from './session-cache-write.js';
11
+ import { realRuntimeTitle } from './use-opencode-events/helpers.js';
12
+ import { runtimeKeys } from './use-opencode-sessions/index.js';
13
+ let watch = null;
14
+ let overrides = null;
15
+ function announce(...[handoff, outcome, watchedSinceMs]) {
16
+ const title = handoff.title?.();
17
+ if (outcome.kind === 'completed') {
18
+ notifyTaskComplete(handoff.sessionId, title, { watchedSinceMs, projectId: handoff.projectId });
19
+ }
20
+ else if (outcome.kind === 'failed') {
21
+ notifySessionError(handoff.sessionId, unwrapError(outcome.error), title, {
22
+ projectId: handoff.projectId,
23
+ });
24
+ }
25
+ }
26
+ function subscribeTurnFrames(projectId, sessionId, onRead) {
27
+ const handle = openSessionStream({
28
+ projectId,
29
+ sessionId,
30
+ onControl: (frame) => {
31
+ if (frame.type !== 'beeuni.control.turn')
32
+ return;
33
+ const payload = frame.payload;
34
+ if (!payload?.known || !Array.isArray(payload.turns))
35
+ return;
36
+ onRead({
37
+ turns: payload.turns,
38
+ ...(payload.last_ended ? { last_ended: payload.last_ended } : {}),
39
+ ...(payload.recent_failures ? { recent_failures: payload.recent_failures } : {}),
40
+ });
41
+ },
42
+ });
43
+ return () => handle.close();
44
+ }
45
+ function turnEndWatch() {
46
+ watch ??= createTurnEndWatch({
47
+ readTurn: (projectId, sessionId) => getSessionTurn(projectId, sessionId, { showErrors: false }),
48
+ subscribe: subscribeTurnFrames,
49
+ pushLive: sessionStreamConnected,
50
+ onEnded: announce,
51
+ ...overrides,
52
+ });
53
+ return watch;
54
+ }
55
+ registerIdentityReset(() => watch?.releaseAll());
56
+ export function resetTurnEndHandoffForTests(deps) {
57
+ overrides = deps ?? null;
58
+ watch = null;
59
+ }
60
+ export function turnEndHandoffWatching() {
61
+ return watch?.watching() ?? [];
62
+ }
63
+ function sessionTitle(queryClient, projectId, sessionId, runtimeSessionId) {
64
+ const row = findCachedProjectSession(queryClient, projectId, (session) => session.session_id === sessionId);
65
+ const named = row?.custom_name || row?.name;
66
+ if (named)
67
+ return named;
68
+ if (!runtimeSessionId)
69
+ return undefined;
70
+ const runtime = queryClient.getQueryData(runtimeKeys.runtimeSession(runtimeSessionId)) ??
71
+ queryClient.getQueryData(runtimeKeys.sessions())?.find((s) => s.id === runtimeSessionId);
72
+ return realRuntimeTitle(runtime?.title) ?? undefined;
73
+ }
74
+ export function useTurnEndHandoff(input) {
75
+ const queryClient = useQueryClient();
76
+ const latest = useRef(input);
77
+ useEffect(() => {
78
+ latest.current = input;
79
+ });
80
+ const { enabled, projectId, sessionId } = input;
81
+ useEffect(() => {
82
+ if (!enabled || !projectId || !sessionId)
83
+ return;
84
+ watch?.release(projectId, sessionId);
85
+ return () => {
86
+ const last = latest.current;
87
+ if (!last.working || last.projectId !== projectId || last.sessionId !== sessionId)
88
+ return;
89
+ if (!hasNotifySink())
90
+ return;
91
+ const runtimeSessionId = last.runtimeSessionId;
92
+ turnEndWatch().watch({
93
+ projectId,
94
+ sessionId,
95
+ openTurnToken: last.openTurnToken,
96
+ lastEndedToken: last.lastEndedToken,
97
+ title: () => sessionTitle(queryClient, projectId, sessionId, runtimeSessionId),
98
+ });
99
+ };
100
+ }, [enabled, projectId, sessionId, queryClient]);
101
+ }
@@ -1,4 +1,3 @@
1
- import { type AdminBootModePolicy } from '../core/rest/projects-client/admin-providers.js';
2
1
  export declare function useAdminProviderDistribution(): import("@tanstack/react-query").UseQueryResult<import("../internal/platform-admin.js").AdminProviderDistribution, Error>;
3
2
  export declare function useAdminProviderSandboxes(): import("@tanstack/react-query").UseQueryResult<import("../internal/platform-admin.js").AdminProviderSandboxesResponse, Error>;
4
3
  export declare function useAdminProviderAnalytics(days: number, enabled: boolean): import("@tanstack/react-query").UseQueryResult<import("../internal/platform-admin.js").AdminProviderAnalytics, Error>;
@@ -16,5 +15,3 @@ export interface AdminProviderMigrationVariables {
16
15
  export declare function useSetAdminProviderDistribution(options?: AdminProviderMutationOptions): import("@tanstack/react-query").UseMutationResult<unknown, Error, Record<string, number>, unknown>;
17
16
  export declare function useMigrateAdminSandboxProvider(options?: AdminProviderMutationOptions): import("@tanstack/react-query").UseMutationResult<unknown, Error, AdminProviderMigrationVariables, unknown>;
18
17
  export declare function useSetAdminProviderFallback(options?: AdminProviderMutationOptions): import("@tanstack/react-query").UseMutationResult<unknown, Error, boolean, unknown>;
19
- export declare function useAdminBootModes(): import("@tanstack/react-query").UseQueryResult<import("../internal/platform-admin.js").AdminBootModes, Error>;
20
- export declare function useSetAdminBootModes(options?: AdminProviderMutationOptions): import("@tanstack/react-query").UseMutationResult<import("../internal/platform-admin.js").AdminBootModes, Error, AdminBootModePolicy, unknown>;
@@ -1,5 +1,5 @@
1
1
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
- import { getAdminProviderDistribution, listAdminSandboxes, getAdminProviderAnalytics, getAdminProviderFallback, setAdminProviderDistribution, migrateAdminSandboxProvider, setAdminProviderFallback, getAdminBootModes, setAdminBootModes } from '../core/rest/projects-client/admin-providers.js';
2
+ import { getAdminProviderDistribution, listAdminSandboxes, getAdminProviderAnalytics, getAdminProviderFallback, setAdminProviderDistribution, migrateAdminSandboxProvider, setAdminProviderFallback } from '../core/rest/projects-client/admin-providers.js';
3
3
  export function useAdminProviderDistribution() {
4
4
  return useQuery({ queryKey: ['admin', 'provider-distribution'], queryFn: () => getAdminProviderDistribution() });
5
5
  }
@@ -33,13 +33,3 @@ export function useSetAdminProviderFallback(options = {}) {
33
33
  void qc.invalidateQueries({ queryKey: ['admin', 'provider-fallback'] });
34
34
  } });
35
35
  }
36
- export function useAdminBootModes() {
37
- return useQuery({ queryKey: ['admin', 'boot-modes'], queryFn: () => getAdminBootModes(), refetchInterval: 30_000 });
38
- }
39
- export function useSetAdminBootModes(options = {}) {
40
- const qc = useQueryClient();
41
- return useMutation({ mutationFn: (policy) => setAdminBootModes(policy), onError: options.onError, onSuccess: (data) => {
42
- options.onSuccess?.();
43
- qc.setQueryData(['admin', 'boot-modes'], data);
44
- } });
45
- }
@@ -0,0 +1,47 @@
1
+ import { type CaptureEnrollmentStatus, type CaptureEnrollmentTokenInput, type CaptureDaysQuery, type CapturePolicy, type CaptureSearchQuery, type CaptureWindowQuery } from '../core/rest/platform-client/capture.js';
2
+ export declare function useCaptureDeviceGrant(userCode: string | null | undefined): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureDeviceGrantDetail, Error>;
3
+ export declare function useApproveCaptureDevice(): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureDeviceGrant, Error, {
4
+ userCode: string;
5
+ accountId?: string;
6
+ }, unknown>;
7
+ export declare function useDenyCaptureDevice(): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureDeviceGrant, Error, string, unknown>;
8
+ type AccountId = string | null | undefined;
9
+ export declare function useCaptureDevices(accountId: AccountId, opts?: {
10
+ scope?: 'mine' | 'account';
11
+ userId?: string;
12
+ }): import("@tanstack/react-query").UseQueryResult<{
13
+ devices: import("../index.js").CaptureDevice[];
14
+ health: import("../index.js").CaptureHealthSummary;
15
+ }, Error>;
16
+ export declare function useRevokeCaptureDevice(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureDevice, Error, string, unknown>;
17
+ export declare function useCaptureDays(accountId: AccountId, query?: CaptureDaysQuery): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureDays, Error>;
18
+ export declare function useCaptureTimeline(accountId: AccountId, query: CaptureWindowQuery | null): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureTimeline, Error>;
19
+ export declare function useCaptureTimelineItems(accountId: AccountId, query: CaptureWindowQuery | null): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureTimelineItems, Error>;
20
+ export declare function useCaptureSearch(accountId: AccountId, query: CaptureSearchQuery | null): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureSearchResult, Error>;
21
+ export declare function useCaptureChunkMedia(accountId: AccountId, chunkId: string | null | undefined, opts?: {
22
+ userId?: string;
23
+ }): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureChunkMedia, Error>;
24
+ export declare function useCaptureEnrollmentTokens(accountId: AccountId): import("@tanstack/react-query").UseQueryResult<{
25
+ tokens: import("../index.js").CaptureEnrollmentToken[];
26
+ }, Error>;
27
+ export declare function useCreateCaptureEnrollmentToken(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureEnrollmentTokenSecret, Error, CaptureEnrollmentTokenInput, unknown>;
28
+ export declare function useRevokeCaptureEnrollmentToken(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureEnrollmentToken, Error, string, unknown>;
29
+ export declare function useCaptureEnrollments(accountId: AccountId, opts?: {
30
+ status?: CaptureEnrollmentStatus;
31
+ }): import("@tanstack/react-query").UseQueryResult<{
32
+ enrollments: import("../index.js").CaptureEnrollment[];
33
+ }, Error>;
34
+ export declare function useApproveCaptureEnrollment(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureEnrollment, Error, {
35
+ enrollmentId: string;
36
+ userId?: string | null;
37
+ }, unknown>;
38
+ export declare function useDenyCaptureEnrollment(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureEnrollment, Error, string, unknown>;
39
+ export declare function useLinkCaptureDevice(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureDevice, Error, {
40
+ deviceId: string;
41
+ userId: string | null;
42
+ }, unknown>;
43
+ export declare function useCapturePolicy(accountId: AccountId): import("@tanstack/react-query").UseQueryResult<import("../index.js").CapturePolicyRecord, Error>;
44
+ export declare function useSetCapturePolicy(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CapturePolicyRecord, Error, CapturePolicy, unknown>;
45
+ export declare function useCaptureWorkspace(accountId: AccountId): import("@tanstack/react-query").UseQueryResult<import("../index.js").CaptureWorkspace, Error>;
46
+ export declare function useSetCaptureEnabled(accountId: AccountId): import("@tanstack/react-query").UseMutationResult<import("../index.js").CaptureWorkspace, Error, boolean, unknown>;
47
+ export {};
@@ -0,0 +1,173 @@
1
+ 'use client';
2
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
3
+ import { approveCaptureDeviceGrant, approveCaptureEnrollment, createCaptureEnrollmentToken, denyCaptureEnrollment, listCaptureEnrollments, listCaptureEnrollmentTokens, linkCaptureDevice, revokeCaptureEnrollmentToken, denyCaptureDeviceGrant, getCaptureChunkMedia, getCaptureDays, getCaptureDeviceGrant, getCapturePolicy, getCaptureTimeline, getCaptureTimelineItems, getCaptureWorkspace, listCaptureDevices, revokeCaptureDevice, searchCapture, setCaptureEnabled, setCapturePolicy, } from '../core/rest/platform-client/capture.js';
4
+ import { contract, FRESHNESS } from './query-contracts.js';
5
+ import { qk } from './query-keys.js';
6
+ export function useCaptureDeviceGrant(userCode) {
7
+ return useQuery({
8
+ queryKey: qk.capture.deviceGrant(userCode ?? ''),
9
+ queryFn: () => getCaptureDeviceGrant(userCode),
10
+ enabled: !!userCode,
11
+ ...contract('volatile'),
12
+ });
13
+ }
14
+ export function useApproveCaptureDevice() {
15
+ const queryClient = useQueryClient();
16
+ return useMutation({
17
+ mutationFn: (args) => approveCaptureDeviceGrant(args.userCode, args.accountId),
18
+ onSuccess: (grant, args) => queryClient.setQueryData(qk.capture.deviceGrant(args.userCode), grant),
19
+ });
20
+ }
21
+ export function useDenyCaptureDevice() {
22
+ const queryClient = useQueryClient();
23
+ return useMutation({
24
+ mutationFn: (userCode) => denyCaptureDeviceGrant(userCode),
25
+ onSuccess: (grant, userCode) => queryClient.setQueryData(qk.capture.deviceGrant(userCode), grant),
26
+ });
27
+ }
28
+ export function useCaptureDevices(accountId, opts = {}) {
29
+ return useQuery({
30
+ queryKey: qk.capture.devices(accountId ?? '', opts.scope ?? 'mine', opts.userId ?? null),
31
+ queryFn: () => listCaptureDevices(accountId, opts),
32
+ enabled: !!accountId,
33
+ ...contract(FRESHNESS.captureDevices),
34
+ });
35
+ }
36
+ export function useRevokeCaptureDevice(accountId) {
37
+ const queryClient = useQueryClient();
38
+ return useMutation({
39
+ mutationFn: (deviceId) => revokeCaptureDevice(accountId, deviceId),
40
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: qk.capture.account(accountId ?? '') }),
41
+ });
42
+ }
43
+ export function useCaptureDays(accountId, query = {}) {
44
+ return useQuery({
45
+ queryKey: qk.capture.timelineRead(accountId ?? '', 'days', query),
46
+ queryFn: () => getCaptureDays(accountId, query),
47
+ enabled: !!accountId,
48
+ ...contract(FRESHNESS.captureTimeline),
49
+ });
50
+ }
51
+ export function useCaptureTimeline(accountId, query) {
52
+ return useQuery({
53
+ queryKey: qk.capture.timelineRead(accountId ?? '', 'runs', query),
54
+ queryFn: () => getCaptureTimeline(accountId, query ?? undefined),
55
+ enabled: !!accountId && !!query,
56
+ ...contract(FRESHNESS.captureTimeline),
57
+ });
58
+ }
59
+ export function useCaptureTimelineItems(accountId, query) {
60
+ return useQuery({
61
+ queryKey: qk.capture.timelineRead(accountId ?? '', 'items', query),
62
+ queryFn: () => getCaptureTimelineItems(accountId, query ?? undefined),
63
+ enabled: !!accountId && !!query,
64
+ ...contract(FRESHNESS.captureTimeline),
65
+ });
66
+ }
67
+ export function useCaptureSearch(accountId, query) {
68
+ return useQuery({
69
+ queryKey: qk.capture.timelineRead(accountId ?? '', 'search', query),
70
+ queryFn: () => searchCapture(accountId, query),
71
+ enabled: !!accountId && !!query?.q.trim(),
72
+ ...contract(FRESHNESS.captureTimeline),
73
+ });
74
+ }
75
+ export function useCaptureChunkMedia(accountId, chunkId, opts = {}) {
76
+ return useQuery({
77
+ queryKey: qk.capture.timelineRead(accountId ?? '', 'media', { chunkId, userId: opts.userId ?? null }),
78
+ queryFn: () => getCaptureChunkMedia(accountId, chunkId, opts),
79
+ enabled: !!accountId && !!chunkId,
80
+ ...contract(FRESHNESS.captureTimeline),
81
+ staleTime: 240_000,
82
+ refetchInterval: 240_000,
83
+ });
84
+ }
85
+ export function useCaptureEnrollmentTokens(accountId) {
86
+ return useQuery({
87
+ queryKey: qk.capture.enrollmentTokens(accountId ?? ''),
88
+ queryFn: () => listCaptureEnrollmentTokens(accountId),
89
+ enabled: !!accountId,
90
+ ...contract(FRESHNESS.captureEnrollmentTokens),
91
+ });
92
+ }
93
+ export function useCreateCaptureEnrollmentToken(accountId) {
94
+ const queryClient = useQueryClient();
95
+ return useMutation({
96
+ mutationFn: (input) => createCaptureEnrollmentToken(accountId, input),
97
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: qk.capture.enrollmentTokens(accountId ?? '') }),
98
+ });
99
+ }
100
+ export function useRevokeCaptureEnrollmentToken(accountId) {
101
+ const queryClient = useQueryClient();
102
+ return useMutation({
103
+ mutationFn: (tokenId) => revokeCaptureEnrollmentToken(accountId, tokenId),
104
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: qk.capture.enrollmentTokens(accountId ?? '') }),
105
+ });
106
+ }
107
+ export function useCaptureEnrollments(accountId, opts = {}) {
108
+ return useQuery({
109
+ queryKey: [...qk.capture.enrollments(accountId ?? ''), opts.status ?? 'all'],
110
+ queryFn: () => listCaptureEnrollments(accountId, opts),
111
+ enabled: !!accountId,
112
+ ...contract(FRESHNESS.captureEnrollments),
113
+ });
114
+ }
115
+ function invalidateRollout(queryClient, accountId) {
116
+ for (const queryKey of [qk.capture.enrollments(accountId), qk.capture.enrollmentTokens(accountId), [...qk.capture.account(accountId), 'devices']]) {
117
+ void queryClient.invalidateQueries({ queryKey });
118
+ }
119
+ }
120
+ export function useApproveCaptureEnrollment(accountId) {
121
+ const queryClient = useQueryClient();
122
+ return useMutation({
123
+ mutationFn: (args) => approveCaptureEnrollment(accountId, args.enrollmentId, args.userId),
124
+ onSuccess: () => invalidateRollout(queryClient, accountId ?? ''),
125
+ });
126
+ }
127
+ export function useDenyCaptureEnrollment(accountId) {
128
+ const queryClient = useQueryClient();
129
+ return useMutation({
130
+ mutationFn: (enrollmentId) => denyCaptureEnrollment(accountId, enrollmentId),
131
+ onSuccess: () => invalidateRollout(queryClient, accountId ?? ''),
132
+ });
133
+ }
134
+ export function useLinkCaptureDevice(accountId) {
135
+ const queryClient = useQueryClient();
136
+ return useMutation({
137
+ mutationFn: (args) => linkCaptureDevice(accountId, args.deviceId, args.userId),
138
+ onSuccess: () => invalidateRollout(queryClient, accountId ?? ''),
139
+ });
140
+ }
141
+ export function useCapturePolicy(accountId) {
142
+ return useQuery({
143
+ queryKey: qk.capture.policy(accountId ?? ''),
144
+ queryFn: () => getCapturePolicy(accountId),
145
+ enabled: !!accountId,
146
+ ...contract(FRESHNESS.capturePolicy),
147
+ });
148
+ }
149
+ export function useSetCapturePolicy(accountId) {
150
+ const queryClient = useQueryClient();
151
+ return useMutation({
152
+ mutationFn: (policy) => setCapturePolicy(accountId, policy),
153
+ onSuccess: (record) => queryClient.setQueryData(qk.capture.policy(accountId ?? ''), record),
154
+ });
155
+ }
156
+ export function useCaptureWorkspace(accountId) {
157
+ return useQuery({
158
+ queryKey: qk.capture.workspace(accountId ?? ''),
159
+ queryFn: () => getCaptureWorkspace(accountId),
160
+ enabled: !!accountId,
161
+ ...contract(FRESHNESS.captureWorkspace),
162
+ });
163
+ }
164
+ export function useSetCaptureEnabled(accountId) {
165
+ const queryClient = useQueryClient();
166
+ return useMutation({
167
+ mutationFn: (enabled) => setCaptureEnabled(accountId, enabled),
168
+ onSuccess: (workspace) => {
169
+ queryClient.setQueryData(qk.capture.workspace(accountId ?? ''), workspace);
170
+ void queryClient.invalidateQueries({ queryKey: qk.capture.account(accountId ?? '') });
171
+ },
172
+ });
173
+ }
@@ -9,6 +9,7 @@ const GRADUATED_FEATURE_FLAG_KEYS = new Set([
9
9
  'session_transcript_history',
10
10
  'teams',
11
11
  'config_releases',
12
+ 'apps',
12
13
  ]);
13
14
  export function useFeatureFlag(projectId, key) {
14
15
  const query = useQuery({
@@ -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
+ }
@@ -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() {