@kortix/sdk 0.12.8 → 0.13.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 (261) hide show
  1. package/README.md +3 -6
  2. package/dist/browser/session-sync/session-sync-registry.d.ts +7 -2
  3. package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
  4. package/dist/browser/session-sync/session-sync-registry.js +33 -44
  5. package/dist/browser/session-sync/session-sync-registry.js.map +1 -1
  6. package/dist/browser/stores/opencode-compaction-store.d.ts +9 -3
  7. package/dist/browser/stores/opencode-compaction-store.d.ts.map +1 -1
  8. package/dist/browser/stores/opencode-compaction-store.js +5 -12
  9. package/dist/browser/stores/opencode-compaction-store.js.map +1 -1
  10. package/dist/browser/stores/sandbox-connection-store.d.ts +28 -17
  11. package/dist/browser/stores/sandbox-connection-store.d.ts.map +1 -1
  12. package/dist/browser/stores/sandbox-connection-store.js +32 -84
  13. package/dist/browser/stores/sandbox-connection-store.js.map +1 -1
  14. package/dist/browser/stores/session-working-store.d.ts +73 -0
  15. package/dist/browser/stores/session-working-store.d.ts.map +1 -0
  16. package/dist/browser/stores/session-working-store.js +86 -0
  17. package/dist/browser/stores/session-working-store.js.map +1 -0
  18. package/dist/browser/stores/sync-store/types.d.ts +14 -4
  19. package/dist/browser/stores/sync-store/types.d.ts.map +1 -1
  20. package/dist/browser/stores/sync-store.d.ts +140 -3
  21. package/dist/browser/stores/sync-store.d.ts.map +1 -1
  22. package/dist/browser/stores/sync-store.js +425 -36
  23. package/dist/browser/stores/sync-store.js.map +1 -1
  24. package/dist/core/client/kortix.d.ts +102 -1
  25. package/dist/core/client/kortix.d.ts.map +1 -1
  26. package/dist/core/client/kortix.js +78 -0
  27. package/dist/core/client/kortix.js.map +1 -1
  28. package/dist/core/files/client.d.ts +50 -2
  29. package/dist/core/files/client.d.ts.map +1 -1
  30. package/dist/core/files/client.js +253 -44
  31. package/dist/core/files/client.js.map +1 -1
  32. package/dist/core/files/types.d.ts +11 -0
  33. package/dist/core/files/types.d.ts.map +1 -1
  34. package/dist/core/http/abort-error.d.ts +89 -0
  35. package/dist/core/http/abort-error.d.ts.map +1 -0
  36. package/dist/core/http/abort-error.js +190 -0
  37. package/dist/core/http/abort-error.js.map +1 -0
  38. package/dist/core/http/api-client.d.ts.map +1 -1
  39. package/dist/core/http/api-client.js +18 -4
  40. package/dist/core/http/api-client.js.map +1 -1
  41. package/dist/core/rest/platform-client/github-app.d.ts +13 -0
  42. package/dist/core/rest/platform-client/github-app.d.ts.map +1 -1
  43. package/dist/core/rest/platform-client/github-app.js +2 -0
  44. package/dist/core/rest/platform-client/github-app.js.map +1 -1
  45. package/dist/core/rest/platform-client/members.d.ts +10 -64
  46. package/dist/core/rest/platform-client/members.d.ts.map +1 -1
  47. package/dist/core/rest/platform-client/members.js +10 -28
  48. package/dist/core/rest/platform-client/members.js.map +1 -1
  49. package/dist/core/rest/projects-client/accounts.d.ts +30 -1
  50. package/dist/core/rest/projects-client/accounts.d.ts.map +1 -1
  51. package/dist/core/rest/projects-client/accounts.js +1 -1
  52. package/dist/core/rest/projects-client/accounts.js.map +1 -1
  53. package/dist/core/rest/projects-client/apps.d.ts +13 -0
  54. package/dist/core/rest/projects-client/apps.d.ts.map +1 -1
  55. package/dist/core/rest/projects-client/apps.js.map +1 -1
  56. package/dist/core/rest/projects-client/assignments.d.ts +114 -0
  57. package/dist/core/rest/projects-client/assignments.d.ts.map +1 -0
  58. package/dist/core/rest/projects-client/assignments.js +91 -0
  59. package/dist/core/rest/projects-client/assignments.js.map +1 -0
  60. package/dist/core/rest/projects-client/billing.d.ts +10 -1
  61. package/dist/core/rest/projects-client/billing.d.ts.map +1 -1
  62. package/dist/core/rest/projects-client/billing.js +11 -2
  63. package/dist/core/rest/projects-client/billing.js.map +1 -1
  64. package/dist/core/rest/projects-client/connectors.d.ts +2 -0
  65. package/dist/core/rest/projects-client/connectors.d.ts.map +1 -1
  66. package/dist/core/rest/projects-client/connectors.js.map +1 -1
  67. package/dist/core/rest/projects-client/files.js +2 -2
  68. package/dist/core/rest/projects-client/files.js.map +1 -1
  69. package/dist/core/rest/projects-client/gateway.d.ts +51 -0
  70. package/dist/core/rest/projects-client/gateway.d.ts.map +1 -1
  71. package/dist/core/rest/projects-client/gateway.js.map +1 -1
  72. package/dist/core/rest/projects-client/iam.d.ts +51 -3
  73. package/dist/core/rest/projects-client/iam.d.ts.map +1 -1
  74. package/dist/core/rest/projects-client/iam.js +18 -1
  75. package/dist/core/rest/projects-client/iam.js.map +1 -1
  76. package/dist/core/rest/projects-client/index.d.ts +1 -0
  77. package/dist/core/rest/projects-client/index.d.ts.map +1 -1
  78. package/dist/core/rest/projects-client/index.js +1 -0
  79. package/dist/core/rest/projects-client/index.js.map +1 -1
  80. package/dist/core/rest/projects-client/projects.d.ts +1 -1
  81. package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
  82. package/dist/core/rest/projects-client/projects.js +3 -0
  83. package/dist/core/rest/projects-client/projects.js.map +1 -1
  84. package/dist/core/rest/projects-client/secrets.d.ts +11 -0
  85. package/dist/core/rest/projects-client/secrets.d.ts.map +1 -1
  86. package/dist/core/rest/projects-client/secrets.js +1 -1
  87. package/dist/core/rest/projects-client/secrets.js.map +1 -1
  88. package/dist/core/rest/projects-client/session-costs.d.ts +33 -0
  89. package/dist/core/rest/projects-client/session-costs.d.ts.map +1 -1
  90. package/dist/core/rest/projects-client/session-costs.js.map +1 -1
  91. package/dist/core/rest/projects-client/sessions.d.ts +226 -2
  92. package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
  93. package/dist/core/rest/projects-client/sessions.js +101 -4
  94. package/dist/core/rest/projects-client/sessions.js.map +1 -1
  95. package/dist/core/rest/projects-client/shared.d.ts +10 -1
  96. package/dist/core/rest/projects-client/shared.d.ts.map +1 -1
  97. package/dist/core/rest/projects-client/shared.js.map +1 -1
  98. package/dist/core/rest/projects-client/tokens.d.ts +13 -1
  99. package/dist/core/rest/projects-client/tokens.d.ts.map +1 -1
  100. package/dist/core/rest/projects-client/tokens.js +7 -2
  101. package/dist/core/rest/projects-client/tokens.js.map +1 -1
  102. package/dist/core/rest/projects-client/triggers.d.ts +69 -2
  103. package/dist/core/rest/projects-client/triggers.d.ts.map +1 -1
  104. package/dist/core/rest/projects-client/triggers.js +1 -1
  105. package/dist/core/rest/projects-client/triggers.js.map +1 -1
  106. package/dist/core/session/compaction.d.ts +98 -0
  107. package/dist/core/session/compaction.d.ts.map +1 -0
  108. package/dist/core/session/compaction.js +108 -0
  109. package/dist/core/session/compaction.js.map +1 -0
  110. package/dist/core/session/health.d.ts +22 -0
  111. package/dist/core/session/health.d.ts.map +1 -1
  112. package/dist/core/session/health.js +31 -5
  113. package/dist/core/session/health.js.map +1 -1
  114. package/dist/core/session/index.d.ts +2 -0
  115. package/dist/core/session/index.d.ts.map +1 -1
  116. package/dist/core/session/index.js +2 -0
  117. package/dist/core/session/index.js.map +1 -1
  118. package/dist/core/session/message-queue.d.ts +79 -15
  119. package/dist/core/session/message-queue.d.ts.map +1 -1
  120. package/dist/core/session/message-queue.js +73 -27
  121. package/dist/core/session/message-queue.js.map +1 -1
  122. package/dist/core/session/rewind.d.ts +76 -10
  123. package/dist/core/session/rewind.d.ts.map +1 -1
  124. package/dist/core/session/rewind.js +68 -11
  125. package/dist/core/session/rewind.js.map +1 -1
  126. package/dist/core/session/working.d.ts +241 -0
  127. package/dist/core/session/working.d.ts.map +1 -0
  128. package/dist/core/session/working.js +261 -0
  129. package/dist/core/session/working.js.map +1 -0
  130. package/dist/core/session-sync/session-sync-controller.d.ts +35 -20
  131. package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
  132. package/dist/core/session-sync/session-sync-controller.js +48 -115
  133. package/dist/core/session-sync/session-sync-controller.js.map +1 -1
  134. package/dist/core/stream/event-stream.d.ts +22 -0
  135. package/dist/core/stream/event-stream.d.ts.map +1 -1
  136. package/dist/core/stream/event-stream.js +132 -36
  137. package/dist/core/stream/event-stream.js.map +1 -1
  138. package/dist/core/turns/grouping.d.ts +32 -2
  139. package/dist/core/turns/grouping.d.ts.map +1 -1
  140. package/dist/core/turns/grouping.js +31 -2
  141. package/dist/core/turns/grouping.js.map +1 -1
  142. package/dist/core/turns/index.d.ts +1 -0
  143. package/dist/core/turns/index.d.ts.map +1 -1
  144. package/dist/core/turns/index.js +1 -0
  145. package/dist/core/turns/index.js.map +1 -1
  146. package/dist/core/turns/open-turn.d.ts +81 -0
  147. package/dist/core/turns/open-turn.d.ts.map +1 -0
  148. package/dist/core/turns/open-turn.js +77 -0
  149. package/dist/core/turns/open-turn.js.map +1 -0
  150. package/dist/core/turns/state.js +26 -26
  151. package/dist/core/turns/state.js.map +1 -1
  152. package/dist/core/turns/types.d.ts +5 -0
  153. package/dist/core/turns/types.d.ts.map +1 -1
  154. package/dist/index.d.ts +2 -1
  155. package/dist/index.d.ts.map +1 -1
  156. package/dist/index.js +2 -1
  157. package/dist/index.js.map +1 -1
  158. package/dist/kortix.esm.min.js +25 -25
  159. package/dist/kortix.global.js +25 -25
  160. package/dist/node/server.d.ts.map +1 -1
  161. package/dist/node/server.js +14 -2
  162. package/dist/node/server.js.map +1 -1
  163. package/dist/react/index.d.ts +2 -1
  164. package/dist/react/index.d.ts.map +1 -1
  165. package/dist/react/index.js +7 -1
  166. package/dist/react/index.js.map +1 -1
  167. package/dist/react/initial-session-pin.d.ts +17 -0
  168. package/dist/react/initial-session-pin.d.ts.map +1 -1
  169. package/dist/react/initial-session-pin.js +58 -0
  170. package/dist/react/initial-session-pin.js.map +1 -1
  171. package/dist/react/opencode.d.ts +6 -2
  172. package/dist/react/opencode.d.ts.map +1 -1
  173. package/dist/react/opencode.js +5 -1
  174. package/dist/react/opencode.js.map +1 -1
  175. package/dist/react/prefetch-session-start.d.ts +10 -4
  176. package/dist/react/prefetch-session-start.d.ts.map +1 -1
  177. package/dist/react/prefetch-session-start.js +10 -4
  178. package/dist/react/prefetch-session-start.js.map +1 -1
  179. package/dist/react/query-keys.d.ts +8 -2
  180. package/dist/react/query-keys.d.ts.map +1 -1
  181. package/dist/react/query-keys.js +8 -2
  182. package/dist/react/query-keys.js.map +1 -1
  183. package/dist/react/session-start-stash.d.ts +25 -0
  184. package/dist/react/session-start-stash.d.ts.map +1 -1
  185. package/dist/react/session-start-stash.js +34 -3
  186. package/dist/react/session-start-stash.js.map +1 -1
  187. package/dist/react/session-title-sync.d.ts +5 -1
  188. package/dist/react/session-title-sync.d.ts.map +1 -1
  189. package/dist/react/session-title-sync.js +16 -4
  190. package/dist/react/session-title-sync.js.map +1 -1
  191. package/dist/react/use-can.d.ts +105 -0
  192. package/dist/react/use-can.d.ts.map +1 -0
  193. package/dist/react/use-can.js +190 -0
  194. package/dist/react/use-can.js.map +1 -0
  195. package/dist/react/use-canonical-opencode-session.d.ts +6 -1
  196. package/dist/react/use-canonical-opencode-session.d.ts.map +1 -1
  197. package/dist/react/use-canonical-opencode-session.js +46 -3
  198. package/dist/react/use-canonical-opencode-session.js.map +1 -1
  199. package/dist/react/use-opencode-events/handle-event.d.ts +10 -0
  200. package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
  201. package/dist/react/use-opencode-events/handle-event.js +76 -7
  202. package/dist/react/use-opencode-events/handle-event.js.map +1 -1
  203. package/dist/react/use-opencode-events/helpers.d.ts +21 -8
  204. package/dist/react/use-opencode-events/helpers.d.ts.map +1 -1
  205. package/dist/react/use-opencode-events/helpers.js +25 -26
  206. package/dist/react/use-opencode-events/helpers.js.map +1 -1
  207. package/dist/react/use-opencode-events/index.d.ts.map +1 -1
  208. package/dist/react/use-opencode-events/index.js +43 -26
  209. package/dist/react/use-opencode-events/index.js.map +1 -1
  210. package/dist/react/use-opencode-events/use-event-stream-refs.d.ts.map +1 -1
  211. package/dist/react/use-opencode-events/use-event-stream-refs.js +66 -3
  212. package/dist/react/use-opencode-events/use-event-stream-refs.js.map +1 -1
  213. package/dist/react/use-opencode-local.d.ts +17 -0
  214. package/dist/react/use-opencode-local.d.ts.map +1 -1
  215. package/dist/react/use-opencode-local.js +41 -7
  216. package/dist/react/use-opencode-local.js.map +1 -1
  217. package/dist/react/use-opencode-sessions/messages.d.ts +101 -1
  218. package/dist/react/use-opencode-sessions/messages.d.ts.map +1 -1
  219. package/dist/react/use-opencode-sessions/messages.js +481 -63
  220. package/dist/react/use-opencode-sessions/messages.js.map +1 -1
  221. package/dist/react/use-project-apps.d.ts +24 -1
  222. package/dist/react/use-project-apps.d.ts.map +1 -1
  223. package/dist/react/use-project-apps.js +5 -3
  224. package/dist/react/use-project-apps.js.map +1 -1
  225. package/dist/react/use-runtime-boot-stalled.d.ts +32 -0
  226. package/dist/react/use-runtime-boot-stalled.d.ts.map +1 -0
  227. package/dist/react/use-runtime-boot-stalled.js +48 -0
  228. package/dist/react/use-runtime-boot-stalled.js.map +1 -0
  229. package/dist/react/use-runtime-reconnect.d.ts +67 -1
  230. package/dist/react/use-runtime-reconnect.d.ts.map +1 -1
  231. package/dist/react/use-runtime-reconnect.js +116 -18
  232. package/dist/react/use-runtime-reconnect.js.map +1 -1
  233. package/dist/react/use-session-prompts.d.ts +113 -0
  234. package/dist/react/use-session-prompts.d.ts.map +1 -0
  235. package/dist/react/use-session-prompts.js +242 -0
  236. package/dist/react/use-session-prompts.js.map +1 -0
  237. package/dist/react/use-session-send.d.ts +169 -20
  238. package/dist/react/use-session-send.d.ts.map +1 -1
  239. package/dist/react/use-session-send.js +178 -29
  240. package/dist/react/use-session-send.js.map +1 -1
  241. package/dist/react/use-session-sync.d.ts +35 -2
  242. package/dist/react/use-session-sync.d.ts.map +1 -1
  243. package/dist/react/use-session-sync.js +46 -9
  244. package/dist/react/use-session-sync.js.map +1 -1
  245. package/dist/react/use-session-working.d.ts +57 -0
  246. package/dist/react/use-session-working.d.ts.map +1 -0
  247. package/dist/react/use-session-working.js +178 -0
  248. package/dist/react/use-session-working.js.map +1 -0
  249. package/dist/react/use-session.d.ts +122 -5
  250. package/dist/react/use-session.d.ts.map +1 -1
  251. package/dist/react/use-session.js +343 -69
  252. package/dist/react/use-session.js.map +1 -1
  253. package/package.json +2 -2
  254. package/dist/browser/stores/tab-store.d.ts +0 -91
  255. package/dist/browser/stores/tab-store.d.ts.map +0 -1
  256. package/dist/browser/stores/tab-store.js +0 -369
  257. package/dist/browser/stores/tab-store.js.map +0 -1
  258. package/dist/core/session/send-queue.d.ts +0 -70
  259. package/dist/core/session/send-queue.d.ts.map +0 -1
  260. package/dist/core/session/send-queue.js +0 -116
  261. package/dist/core/session/send-queue.js.map +0 -1
@@ -1,5 +1,9 @@
1
1
  import type { QueryClient } from '@tanstack/react-query';
2
- type SessionTitleQueryClient = Pick<QueryClient, 'getQueryData' | 'refetchQueries'>;
2
+ type SessionTitleQueryClient = Pick<QueryClient, 'getQueryData' | 'refetchQueries'> & {
3
+ /** Optional so lean test fakes (and older callers) keep working; a real
4
+ * QueryClient always has it. */
5
+ getQueryState?: QueryClient['getQueryState'];
6
+ };
3
7
  interface SessionTitleRefreshOptions {
4
8
  delaysMs?: number[];
5
9
  signal?: AbortSignal;
@@ -1 +1 @@
1
- {"version":3,"file":"session-title-sync.d.ts","sourceRoot":"","sources":["../../src/react/session-title-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,KAAK,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,GAAG,gBAAgB,CAAC,CAAC;AAEpF,UAAU,0BAA0B;IAClC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAuED;;;;;GAKG;AACH,wBAAsB,qCAAqC,CACzD,WAAW,EAAE,uBAAuB,EACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAYlB;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,EAAE,uBAAuB,EACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAIlB"}
1
+ {"version":3,"file":"session-title-sync.d.ts","sourceRoot":"","sources":["../../src/react/session-title-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKzD,KAAK,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,GAAG,gBAAgB,CAAC,GAAG;IACpF;qCACiC;IACjC,aAAa,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;CAC9C,CAAC;AAEF,UAAU,0BAA0B;IAClC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAmFD;;;;;GAKG;AACH,wBAAsB,qCAAqC,CACzD,WAAW,EAAE,uBAAuB,EACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAYlB;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,EAAE,uBAAuB,EACpC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAIlB"}
@@ -1,3 +1,4 @@
1
+ import { sessionStartKey } from '../core/rest/projects-client/index.js';
1
2
  import { qk } from './query-keys.js';
2
3
  const DEFAULT_TITLE_REFRESH_DELAYS_MS = [0, 5_000, 5_000, 10_000, 10_000, 15_000, 20_000];
3
4
  function readRealTitle(value) {
@@ -30,15 +31,26 @@ function cachedSessionHasTitle(queryClient, projectId, sessionId) {
30
31
  });
31
32
  }
32
33
  function refetchSessionTitleQueries(queryClient, projectId, sessionId) {
34
+ // An adopted warm session is revealed by /start dropping `metadata.warm`.
35
+ // While THIS session's /start is still in flight, a sessions-list refetch
36
+ // can beat it, observe the row still hidden, and overwrite the adopting
37
+ // tab's optimistic seed (warm-session-seed.ts in apps/web) — so the list
38
+ // half waits for the next ladder pass. The detail refetch stays: the
39
+ // session route serves the owner regardless of the marker.
40
+ const startFetching = queryClient.getQueryState?.(sessionStartKey(projectId, sessionId))?.fetchStatus === 'fetching';
33
41
  return Promise.all([
34
42
  // The broad sessions-family prefix, not just the default-scope list: it
35
43
  // reaches the list in EVERY scope plus every session/messages entry
36
44
  // beneath it, so a manager-only 'project'-scope reader picks up the
37
45
  // resolved title too, not only the default 'visible' one.
38
- queryClient.refetchQueries({
39
- queryKey: qk.project.sessionsScope(projectId),
40
- type: 'active',
41
- }),
46
+ ...(startFetching
47
+ ? []
48
+ : [
49
+ queryClient.refetchQueries({
50
+ queryKey: qk.project.sessionsScope(projectId),
51
+ type: 'active',
52
+ }),
53
+ ]),
42
54
  queryClient.refetchQueries({
43
55
  queryKey: qk.project.session(projectId, sessionId),
44
56
  type: 'active',
@@ -1 +1 @@
1
- {"version":3,"file":"session-title-sync.js","sourceRoot":"","sources":["../../src/react/session-title-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-title-sync.js","sourceRoot":"","sources":["../../src/react/session-title-sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `useCan` / `useCans` — the ONLY client-side authorization read.
3
+ *
4
+ * Relocated from `apps/web/src/lib/use-permission.ts` + `use-project-can.ts`.
5
+ * They were host-local, so every other host (the whitelabel demo, mobile) had
6
+ * no probe at all and fell back to reading a role literal off a row — which is
7
+ * exactly the bug the canonical model deletes: a member holding a custom role
8
+ * that grants `project.members.manage` is invisible to `role === 'manager'`.
9
+ *
10
+ * Ask for the LEAF the route asserts (`project.gitops.push`), never for a role.
11
+ * The engine answers; the client renders the answer.
12
+ *
13
+ * NOT a security boundary — the API re-checks every mutating route. This only
14
+ * decides what to show and enable.
15
+ *
16
+ * ### Cache contract
17
+ *
18
+ * A verdict is cached for 5 minutes. That is only safe because every
19
+ * assignment write calls `invalidatePermissionProbes`, so a revoke is visible
20
+ * on the next render rather than up to five minutes later. If you add a write
21
+ * path that changes an assignment, it calls that function. No exceptions.
22
+ */
23
+ import { type QueryClient } from '@tanstack/react-query';
24
+ import { type PermissionProbeInput, type ResourceType } from '../core/rest/projects-client/index.js';
25
+ /** A probe verdict. `allowed` is true only when the engine said so — loading
26
+ * and error both read false, so a gate defaults to hidden/disabled. */
27
+ export interface CanResult {
28
+ allowed: boolean;
29
+ /** The engine's reason, e.g. `super_admin`, `role`, `not_a_member`,
30
+ * `agent_scope_insufficient`. Drives "why can Alice do this?" tooltips. */
31
+ reason: string | null;
32
+ isLoading: boolean;
33
+ isError: boolean;
34
+ }
35
+ /**
36
+ * What the action is being asked about.
37
+ *
38
+ * - `{ accountId }` — an account-scope action (`member.update`, `role.create`).
39
+ * - `{ accountId, resource }` — narrowed to one account object (a group, a member).
40
+ * - `{ projectId }` — a project-scope action. The owning account is resolved
41
+ * from the shared project cache; pass `accountId` when you already hold it
42
+ * and the probe runs on the FIRST render instead of after a second fetch.
43
+ */
44
+ export type CanTarget = {
45
+ accountId: string | undefined;
46
+ projectId?: undefined;
47
+ resource?: {
48
+ type: Exclude<ResourceType, 'account'>;
49
+ id: string;
50
+ };
51
+ } | {
52
+ projectId: string | undefined;
53
+ accountId?: string;
54
+ resource?: undefined;
55
+ };
56
+ export interface CanOptions {
57
+ /** Probe as this user instead of the caller. A host with its own auth
58
+ * context passes it here rather than making the SDK depend on that context;
59
+ * omitted, the SDK resolves the caller from `GET /accounts/me`. */
60
+ userId?: string;
61
+ }
62
+ /** The single-probe cache key. Kept as a flat literal (not under `qk`) because
63
+ * it predates that factory and both `apps/web` and its tests address it. */
64
+ export declare const permissionProbeKey: (accountId: string | undefined, userId: string | undefined, action: string, target?: {
65
+ resourceType?: string;
66
+ resourceId?: string;
67
+ }) => readonly ["iam-permission", string | undefined, string | undefined, string, string | null, string | null];
68
+ /** The batch cache key. Keyed on the probe LIST so adding or removing a probe
69
+ * refetches; the signature is action+target per probe, never object identity,
70
+ * which React swaps every render. */
71
+ export declare const permissionProbeBatchKey: (accountId: string | undefined, userId: string | undefined, probes: readonly PermissionProbeInput[]) => readonly ["iam-permission-batch", string | undefined, string | undefined, string];
72
+ /** Can this principal perform `action` on this target? */
73
+ export declare function useCan(target: CanTarget, action: string, options?: CanOptions): CanResult;
74
+ /**
75
+ * Batch variant — ONE roundtrip for N actions on the same target. Returns a map
76
+ * keyed by action, so a caller reads `caps[ACTION].allowed`.
77
+ *
78
+ * `actions` must be stable across renders (module-level or memoized): the query
79
+ * key is derived from the list.
80
+ */
81
+ export declare function useCans(target: CanTarget, actions: readonly string[], options?: CanOptions): Record<string, CanResult>;
82
+ /**
83
+ * Probe on behalf of ANOTHER member — the "what can this person actually do"
84
+ * admin panels. Returns one result per input, in order. The caller needs
85
+ * `member.read` on the account; the server enforces it.
86
+ *
87
+ * Kept probe-shaped (not action-shaped like `useCans`) because these panels mix
88
+ * account and project targets in one request.
89
+ */
90
+ export declare function usePermissionsFor(accountId: string | undefined, memberUserId: string | undefined, probes: PermissionProbeInput[]): CanResult[];
91
+ /**
92
+ * Bust the probe cache after an assignment write. **Every** write that creates,
93
+ * changes or revokes an assignment calls this — a 5-minute stale window on an
94
+ * authorization verdict is a revoke that has not happened yet.
95
+ *
96
+ * Scope it as tightly as you honestly can: an account+user pair when you know
97
+ * whose access changed, the account when a group or a role definition changed
98
+ * (that moves verdicts for principals you cannot enumerate client-side), and
99
+ * everything only when you know nothing.
100
+ */
101
+ export declare function invalidatePermissionProbes(queryClient: Pick<QueryClient, 'invalidateQueries'>, scope: {
102
+ accountId?: string;
103
+ userId?: string;
104
+ }): Promise<void>;
105
+ //# sourceMappingURL=use-can.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-can.d.ts","sourceRoot":"","sources":["../../src/react/use-can.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAClB,MAAM,8BAA8B,CAAC;AAItC;wEACwE;AACxE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB;gFAC4E;IAC5E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GACjB;IACE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE,GACD;IAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhF,MAAM,WAAW,UAAU;IACzB;;wEAEoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;6EAC6E;AAC7E,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,MAAM,EACd,SAAS;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,8GAS7C,CAAC;AAEb;;sCAEsC;AACtC,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,GAAG,SAAS,EAC7B,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,SAAS,oBAAoB,EAAE,sFAO7B,CAAC;AAsDb,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAwBzF;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,CAAC,EAAE,UAAU,GACnB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CA8B3B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,MAAM,EAAE,oBAAoB,EAAE,GAC7B,SAAS,EAAE,CAoBb;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,EACnD,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,190 @@
1
+ 'use client';
2
+ /**
3
+ * `useCan` / `useCans` — the ONLY client-side authorization read.
4
+ *
5
+ * Relocated from `apps/web/src/lib/use-permission.ts` + `use-project-can.ts`.
6
+ * They were host-local, so every other host (the whitelabel demo, mobile) had
7
+ * no probe at all and fell back to reading a role literal off a row — which is
8
+ * exactly the bug the canonical model deletes: a member holding a custom role
9
+ * that grants `project.members.manage` is invisible to `role === 'manager'`.
10
+ *
11
+ * Ask for the LEAF the route asserts (`project.gitops.push`), never for a role.
12
+ * The engine answers; the client renders the answer.
13
+ *
14
+ * NOT a security boundary — the API re-checks every mutating route. This only
15
+ * decides what to show and enable.
16
+ *
17
+ * ### Cache contract
18
+ *
19
+ * A verdict is cached for 5 minutes. That is only safe because every
20
+ * assignment write calls `invalidatePermissionProbes`, so a revoke is visible
21
+ * on the next render rather than up to five minutes later. If you add a write
22
+ * path that changes an assignment, it calls that function. No exceptions.
23
+ */
24
+ import { useQuery } from '@tanstack/react-query';
25
+ import { getProject, probeEffectivePermission, probeEffectivePermissions, validateToken, } from '../core/rest/projects-client/index.js';
26
+ import { contract } from './query-contracts.js';
27
+ import { qk } from './query-keys.js';
28
+ const PROBE_STALE_MS = 5 * 60_000;
29
+ /** The single-probe cache key. Kept as a flat literal (not under `qk`) because
30
+ * it predates that factory and both `apps/web` and its tests address it. */
31
+ export const permissionProbeKey = (accountId, userId, action, target) => [
32
+ 'iam-permission',
33
+ accountId,
34
+ userId,
35
+ action,
36
+ target?.resourceType ?? null,
37
+ target?.resourceId ?? null,
38
+ ];
39
+ /** The batch cache key. Keyed on the probe LIST so adding or removing a probe
40
+ * refetches; the signature is action+target per probe, never object identity,
41
+ * which React swaps every render. */
42
+ export const permissionProbeBatchKey = (accountId, userId, probes) => [
43
+ 'iam-permission-batch',
44
+ accountId,
45
+ userId,
46
+ probes.map((p) => `${p.action}|${p.resourceType ?? ''}|${p.resourceId ?? ''}`).join(','),
47
+ ];
48
+ /** Identity of the calling principal, for hosts that do not inject one. */
49
+ const identityKey = ['iam-identity', 'me'];
50
+ function useProbeUserId(explicit) {
51
+ const query = useQuery({
52
+ queryKey: identityKey,
53
+ queryFn: async () => (await validateToken()).identity ?? null,
54
+ enabled: !explicit,
55
+ staleTime: Infinity,
56
+ });
57
+ return explicit ?? query.data?.user_id;
58
+ }
59
+ /**
60
+ * Resolve the account that owns a project. A DISABLED react-query query reports
61
+ * `isLoading === false` in v5, so callers must not read the unresolved window
62
+ * as a denial — `pending()` below converts it to "still loading".
63
+ */
64
+ function useOwningAccountId(projectId, accountIdHint) {
65
+ const { data } = useQuery({
66
+ queryKey: qk.project.summary(projectId ?? ''),
67
+ queryFn: () => getProject(projectId),
68
+ enabled: !!projectId && !accountIdHint,
69
+ ...contract('config'),
70
+ });
71
+ return projectId ? (accountIdHint ?? data?.account_id) : undefined;
72
+ }
73
+ const pending = () => ({
74
+ allowed: false,
75
+ reason: null,
76
+ isLoading: true,
77
+ isError: false,
78
+ });
79
+ /** Everything a target resolves to: the account to ask, and the probe target. */
80
+ function useResolvedTarget(target) {
81
+ const isProject = 'projectId' in target && target.projectId !== undefined;
82
+ const projectId = 'projectId' in target ? target.projectId : undefined;
83
+ const owningAccountId = useOwningAccountId(projectId, target.accountId);
84
+ const accountId = isProject ? owningAccountId : target.accountId;
85
+ const probeTarget = isProject
86
+ ? { resourceType: 'project', resourceId: projectId }
87
+ : target.resource
88
+ ? { resourceType: target.resource.type, resourceId: target.resource.id }
89
+ : undefined;
90
+ return { accountId, probeTarget, resolved: !!accountId && (!isProject || !!projectId) };
91
+ }
92
+ /** Can this principal perform `action` on this target? */
93
+ export function useCan(target, action, options) {
94
+ const userId = useProbeUserId(options?.userId);
95
+ const { accountId, probeTarget, resolved } = useResolvedTarget(target);
96
+ const enabled = resolved && !!userId;
97
+ const query = useQuery({
98
+ queryKey: permissionProbeKey(accountId, userId, action, probeTarget),
99
+ queryFn: () => probeEffectivePermission(accountId, userId, (probeTarget ? { action, ...probeTarget } : { action })),
100
+ enabled,
101
+ staleTime: PROBE_STALE_MS,
102
+ });
103
+ if (!enabled)
104
+ return pending();
105
+ return {
106
+ allowed: query.data?.allowed === true,
107
+ reason: query.data?.reason ?? null,
108
+ isLoading: query.isLoading,
109
+ isError: query.isError,
110
+ };
111
+ }
112
+ /**
113
+ * Batch variant — ONE roundtrip for N actions on the same target. Returns a map
114
+ * keyed by action, so a caller reads `caps[ACTION].allowed`.
115
+ *
116
+ * `actions` must be stable across renders (module-level or memoized): the query
117
+ * key is derived from the list.
118
+ */
119
+ export function useCans(target, actions, options) {
120
+ const userId = useProbeUserId(options?.userId);
121
+ const { accountId, probeTarget, resolved } = useResolvedTarget(target);
122
+ const probes = actions.map((action) => (probeTarget ? { action, ...probeTarget } : { action }));
123
+ const enabled = resolved && !!userId && probes.length > 0;
124
+ const query = useQuery({
125
+ queryKey: permissionProbeBatchKey(accountId, userId, probes),
126
+ queryFn: () => probeEffectivePermissions(accountId, userId, probes),
127
+ enabled,
128
+ staleTime: PROBE_STALE_MS,
129
+ });
130
+ const map = {};
131
+ actions.forEach((action, i) => {
132
+ if (!enabled) {
133
+ map[action] = pending();
134
+ return;
135
+ }
136
+ const hit = query.data?.[i];
137
+ map[action] = {
138
+ allowed: hit?.allowed === true,
139
+ reason: hit?.reason ?? null,
140
+ isLoading: query.isLoading,
141
+ isError: query.isError,
142
+ };
143
+ });
144
+ return map;
145
+ }
146
+ /**
147
+ * Probe on behalf of ANOTHER member — the "what can this person actually do"
148
+ * admin panels. Returns one result per input, in order. The caller needs
149
+ * `member.read` on the account; the server enforces it.
150
+ *
151
+ * Kept probe-shaped (not action-shaped like `useCans`) because these panels mix
152
+ * account and project targets in one request.
153
+ */
154
+ export function usePermissionsFor(accountId, memberUserId, probes) {
155
+ const enabled = !!accountId && !!memberUserId && probes.length > 0;
156
+ const query = useQuery({
157
+ queryKey: permissionProbeBatchKey(accountId, memberUserId, probes),
158
+ queryFn: () => probeEffectivePermissions(accountId, memberUserId, probes),
159
+ enabled,
160
+ staleTime: PROBE_STALE_MS,
161
+ });
162
+ return probes.map((_, i) => {
163
+ if (!enabled)
164
+ return pending();
165
+ const hit = query.data?.[i];
166
+ return {
167
+ allowed: hit?.allowed === true,
168
+ reason: hit?.reason ?? null,
169
+ isLoading: query.isLoading,
170
+ isError: query.isError,
171
+ };
172
+ });
173
+ }
174
+ /**
175
+ * Bust the probe cache after an assignment write. **Every** write that creates,
176
+ * changes or revokes an assignment calls this — a 5-minute stale window on an
177
+ * authorization verdict is a revoke that has not happened yet.
178
+ *
179
+ * Scope it as tightly as you honestly can: an account+user pair when you know
180
+ * whose access changed, the account when a group or a role definition changed
181
+ * (that moves verdicts for principals you cannot enumerate client-side), and
182
+ * everything only when you know nothing.
183
+ */
184
+ export function invalidatePermissionProbes(queryClient, scope) {
185
+ const suffix = [scope.accountId, scope.userId].filter((s) => !!s);
186
+ queryClient.invalidateQueries({ queryKey: ['iam-permission', ...suffix] });
187
+ queryClient.invalidateQueries({ queryKey: ['iam-permission-batch', ...suffix] });
188
+ return Promise.resolve();
189
+ }
190
+ //# sourceMappingURL=use-can.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-can.js","sourceRoot":"","sources":["../../src/react/use-can.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,12 @@ import { type Session } from './use-opencode-sessions/index.js';
8
8
  * the client (the old client-side `ensure-opencode` mutation caused the
9
9
  * "session replaced / data lost" drift). It just surfaces the pin:
10
10
  * 1. the value /start handed us this render (`pinFromStart`), else
11
- * 2. the persisted pin on the Kortix session row (`getProjectSession`).
11
+ * 2. the host's own warm-list pin (`initialPin`), else
12
+ * 3. the persisted row (`getProjectSession`) once its REST read resolves,
13
+ * or — before it does — this browser's own synchronous local mirror of
14
+ * the last canonical id seen for this session (T6). The local
15
+ * mirror exists ONLY to cover the gap before (3)'s network read lands;
16
+ * it never outranks it once loaded, and neither ever outranks (1).
12
17
  *
13
18
  * The OpenCode session list is still read (read-only) for ?oc deep-links and
14
19
  * sidebar sub-session rendering.
@@ -1 +1 @@
1
- {"version":3,"file":"use-canonical-opencode-session.d.ts","sourceRoot":"","sources":["../../src/react/use-canonical-opencode-session.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAG5E;;;;;;;;;;;;;GAaG;AAEH;gFACgF;AAChF,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,MAAM,WAAW,wBAAwB;IACvC,kFAAkF;IAClF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,wBAAwB,CAuC3B"}
1
+ {"version":3,"file":"use-canonical-opencode-session.d.ts","sourceRoot":"","sources":["../../src/react/use-canonical-opencode-session.ts"],"names":[],"mappings":"AAUA,OAAO,EAAuB,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAa5E;;;;;;;;;;;;;;;;;;GAkBG;AAEH;gFACgF;AAChF,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,MAAM,WAAW,wBAAwB;IACvC,kFAAkF;IAClF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,wBAAwB,CAqE3B"}
@@ -1,7 +1,17 @@
1
1
  'use client';
2
- import { resolveSessionPin } from './initial-session-pin.js';
2
+ import { useEffect, useLayoutEffect, useState } from 'react';
3
+ import { readPersistedSessionPin, resolvePersistedPin, resolveSessionPin, writePersistedSessionPin, } from './initial-session-pin.js';
3
4
  import { useOpenCodeSessions } from './use-opencode-sessions/index.js';
4
5
  import { useProjectSession } from './use-project-session.js';
6
+ // SSR-safe: `localStorage` does not exist on the server, and reading it
7
+ // during render (rather than an effect) would make the client's FIRST
8
+ // hydration render disagree with the server-rendered HTML. Mirrors
9
+ // `useSession`'s own `useIsomorphicLayoutEffect` (T5) for the exact
10
+ // same reason — a `useLayoutEffect` commits its state update before the
11
+ // browser paints, so the corrected value is on screen for the user's very
12
+ // first frame instead of trailing one visible "empty" frame behind a plain
13
+ // `useEffect`.
14
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
5
15
  /**
6
16
  * OpenCode ↔ Kortix session mapping — READ side.
7
17
  *
@@ -11,7 +21,12 @@ import { useProjectSession } from './use-project-session.js';
11
21
  * the client (the old client-side `ensure-opencode` mutation caused the
12
22
  * "session replaced / data lost" drift). It just surfaces the pin:
13
23
  * 1. the value /start handed us this render (`pinFromStart`), else
14
- * 2. the persisted pin on the Kortix session row (`getProjectSession`).
24
+ * 2. the host's own warm-list pin (`initialPin`), else
25
+ * 3. the persisted row (`getProjectSession`) once its REST read resolves,
26
+ * or — before it does — this browser's own synchronous local mirror of
27
+ * the last canonical id seen for this session (T6). The local
28
+ * mirror exists ONLY to cover the gap before (3)'s network read lands;
29
+ * it never outranks it once loaded, and neither ever outranks (1).
15
30
  *
16
31
  * The OpenCode session list is still read (read-only) for ?oc deep-links and
17
32
  * sidebar sub-session rendering.
@@ -44,12 +59,40 @@ export function useCanonicalOpenCodeSession(params) {
44
59
  const projectSessionQuery = useProjectSession(projectId, sessionId, {
45
60
  enabled: !pinFromStart && !initialPin,
46
61
  });
47
- const pin = projectSessionQuery.data?.opencode_session_id ?? null;
62
+ const networkPin = projectSessionQuery.data?.opencode_session_id ?? null;
63
+ // T6 — a synchronous local mirror of the persisted pin, so a cold
64
+ // mount (no /start response yet, no host-warm `initialPin`) can still
65
+ // resolve `rootSessionId` on frame one instead of waiting out a network
66
+ // round trip. `useState(null)` matches what a server render sees (no
67
+ // `localStorage`), so hydration never disagrees with the server-rendered
68
+ // HTML; the layout effect below then corrects it to the real cached value
69
+ // before the browser paints — see the module-level comment on
70
+ // `useIsomorphicLayoutEffect`.
71
+ const [cachedPin, setCachedPin] = useState(null);
72
+ useIsomorphicLayoutEffect(() => {
73
+ setCachedPin(readPersistedSessionPin(projectId, sessionId));
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, [projectId, sessionId]);
76
+ const pin = resolvePersistedPin({ networkPin, cachedPin });
48
77
  const rootSessionId = resolveSessionPin({
49
78
  startPin: pinFromStart,
50
79
  initialPin,
51
80
  persistedPin: pin,
52
81
  });
82
+ // Mirror a freshly-resolved canonical id back to disk so the NEXT mount of
83
+ // this (projectId, sessionId) can paint synchronously via `cachedPin`
84
+ // above. Scoped to the two AUTHORITATIVE, resolved-this-render sources —
85
+ // /start and the host's warm session-list pin — never to `pin` itself,
86
+ // which would just echo `cachedPin` back to its own key on every render
87
+ // this session is cold. This is also the write half of stale-pin
88
+ // convergence: a re-pinned session's fresh /start id overwrites whatever
89
+ // stale id a previous visit left behind, so the session AFTER this one
90
+ // reads the corrected value instead of the one that just got replaced.
91
+ const freshPin = pinFromStart ?? initialPin ?? null;
92
+ useEffect(() => {
93
+ if (freshPin)
94
+ writePersistedSessionPin(projectId, sessionId, freshPin);
95
+ }, [projectId, sessionId, freshPin]);
53
96
  return {
54
97
  rootSessionId,
55
98
  sessions: sessionsQuery.data ?? [],
@@ -1 +1 @@
1
- {"version":3,"file":"use-canonical-opencode-session.js","sourceRoot":"","sources":["../../src/react/use-canonical-opencode-session.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAgB,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AAEH;gFACgF;AAChF,MAAM,UAAU,wBAAwB;IACtC,WAAW;AACb,CAAC;AAaD,MAAM,UAAU,2BAA2B,CAAC,MAS3C;IACC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAC9F,MAAM,aAAa,GAAG,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;IAE/D,8EAA8E;IAC9E,sEAAsE;IACtE,8EAA8E;IAC9E,gFAAgF;IAChF,gFAAgF;IAChF,kFAAkF;IAClF,4EAA4E;IAC5E,kEAAkE;IAClE,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,sEAAsE;IACtE,4DAA4D;IAC5D,2EAA2E;IAC3E,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE;QAClE,OAAO,EAAE,CAAC,YAAY,IAAI,CAAC,UAAU;KACtC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,IAAI,IAAI,CAAC;IAClE,MAAM,aAAa,GAAG,iBAAiB,CAAC;QACtC,QAAQ,EAAE,YAAY;QACtB,UAAU;QACV,YAAY,EAAE,GAAG;KAClB,CAAC,CAAC;IAEH,OAAO;QACL,aAAa;QACb,QAAQ,EAAE,aAAa,CAAC,IAAI,IAAI,EAAE;QAClC,SAAS,EAAE,aAAa,CAAC,SAAS;QAClC,OAAO,EAAE,aAAa,CAAC,OAAO;QAC9B,MAAM,EAAE,aAAa,CAAC,SAAS;QAC/B,KAAK,EAAE,aAAa,CAAC,KAAK,IAAI,IAAI;KACnC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"use-canonical-opencode-session.js","sourceRoot":"","sources":["../../src/react/use-canonical-opencode-session.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ import type { RefObject } from 'react';
4
4
  import { getClient } from '../../core/runtime/client.js';
5
5
  import { type SessionSyncReason } from '../../core/session-sync/session-sync-controller.js';
6
6
  import type { NormalizeDiagnosticPaths, OpenCodeEvent } from './types.js';
7
+ /** See `createEventHandler`'s `userPartsGraceMs`. */
8
+ export declare const USER_PARTS_GRACE_MS = 1500;
7
9
  /** Builds the SSE event handler; all closure dependencies are injected. */
8
10
  export declare function createEventHandler(deps: {
9
11
  queryClient: QueryClient;
@@ -18,6 +20,14 @@ export declare function createEventHandler(deps: {
18
20
  markSessionAbortedLocally: RefObject<(sessionID: string, message?: string) => void>;
19
21
  fetchLspDiagnosticsDebounced: RefObject<() => void>;
20
22
  reconcileSessionTail?: (sessionID: string, reason: SessionSyncReason) => Promise<void>;
23
+ /**
24
+ * How long a USER `message.updated` may sit with no parts before the tail is
25
+ * re-read. The runtime emits the info frame and the text part separately;
26
+ * lose the part (a stream reconnect during the boot hand-off) and the
27
+ * transcript shows an empty bubble until a reload. Injected so tests can
28
+ * make it immediate.
29
+ */
30
+ userPartsGraceMs?: number;
21
31
  /** The route-scoped project this SSE connection belongs to — see
22
32
  * `refetchKortixSessionMirrors`'s doc comment for why this can't default
23
33
  * to "every project". Optional only so existing test harnesses that don't
@@ -1 +1 @@
1
- {"version":3,"file":"handle-event.d.ts","sourceRoot":"","sources":["../../../src/react/use-opencode-events/handle-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EACV,KAAK,IAAI,gBAAgB,EACzB,iBAAiB,EACjB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,iDAAiD,CAAC;AAczD,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvE,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;IACrC,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,WAAW,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,wBAAwB,EAAE,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC9D,yBAAyB,EAAE,SAAS,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;IACpF,4BAA4B,EAAE,SAAS,CAAC,MAAM,IAAI,CAAC,CAAC;IACpD,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF;;;;6DAIyD;IACzD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,WAoC6B,aAAa,UAsf1C"}
1
+ {"version":3,"file":"handle-event.d.ts","sourceRoot":"","sources":["../../../src/react/use-opencode-events/handle-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EACV,KAAK,IAAI,gBAAgB,EACzB,iBAAiB,EACjB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,iDAAiD,CAAC;AAazD,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvE,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AAEzC,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;IACrC,cAAc,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAChD,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,WAAW,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,wBAAwB,EAAE,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC9D,yBAAyB,EAAE,SAAS,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC;IACpF,4BAA4B,EAAE,SAAS,CAAC,MAAM,IAAI,CAAC,CAAC;IACpD,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;6DAIyD;IACzD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,WAqC6B,aAAa,UA0jB1C"}