@maudecode/talaria-web 1.17.2 → 1.19.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 (623) hide show
  1. package/_release.json +8 -8
  2. package/dist/api/auth-raw.d.ts +1 -1
  3. package/dist/api/auth-raw.js +20 -20
  4. package/dist/api/auth-raw.js.map +1 -1
  5. package/dist/api/automation-raw.d.ts +7 -1
  6. package/dist/api/automation-raw.js +74 -27
  7. package/dist/api/automation-raw.js.map +1 -1
  8. package/dist/api/automation-router.d.ts +128 -23
  9. package/dist/api/automation-router.js +29 -13
  10. package/dist/api/automation-router.js.map +1 -1
  11. package/dist/api/chat-router.d.ts +1845 -40
  12. package/dist/api/chat-router.js +196 -114
  13. package/dist/api/chat-router.js.map +1 -1
  14. package/dist/api/git-router.d.ts +37 -3
  15. package/dist/api/git-router.js +47 -48
  16. package/dist/api/git-router.js.map +1 -1
  17. package/dist/api/raw-routes.d.ts +9 -1
  18. package/dist/api/raw-routes.js +286 -73
  19. package/dist/api/raw-routes.js.map +1 -1
  20. package/dist/api/router.d.ts +16 -6
  21. package/dist/api/router.js +28 -26
  22. package/dist/api/router.js.map +1 -1
  23. package/dist/api/session-visibility.d.ts +4 -4
  24. package/dist/api/session-visibility.js +26 -16
  25. package/dist/api/session-visibility.js.map +1 -1
  26. package/dist/api/sessions-router.d.ts +3312 -131
  27. package/dist/api/sessions-router.js +287 -157
  28. package/dist/api/sessions-router.js.map +1 -1
  29. package/dist/api/settings-router.d.ts +457 -8
  30. package/dist/api/settings-router.js +54 -33
  31. package/dist/api/settings-router.js.map +1 -1
  32. package/dist/api/sse-routes.d.ts +7 -2
  33. package/dist/api/sse-routes.js +109 -62
  34. package/dist/api/sse-routes.js.map +1 -1
  35. package/dist/api/tools-raw.d.ts +5 -0
  36. package/dist/api/tools-raw.js +63 -55
  37. package/dist/api/tools-raw.js.map +1 -1
  38. package/dist/api/tools-router.d.ts +998 -85
  39. package/dist/api/tools-router.js +447 -61
  40. package/dist/api/tools-router.js.map +1 -1
  41. package/dist/api/transcribe-stream.d.ts +16 -0
  42. package/dist/api/transcribe-stream.js +172 -0
  43. package/dist/api/transcribe-stream.js.map +1 -0
  44. package/dist/app.d.ts +4 -1
  45. package/dist/app.js +98 -59
  46. package/dist/app.js.map +1 -1
  47. package/dist/auth/gate.js +15 -11
  48. package/dist/auth/gate.js.map +1 -1
  49. package/dist/auth/oidc.d.ts +5 -2
  50. package/dist/auth/oidc.js +8 -7
  51. package/dist/auth/oidc.js.map +1 -1
  52. package/dist/auth/passkeys.d.ts +15 -14
  53. package/dist/auth/passkeys.js +89 -91
  54. package/dist/auth/passkeys.js.map +1 -1
  55. package/dist/auth/store.d.ts +17 -7
  56. package/dist/auth/store.js +93 -52
  57. package/dist/auth/store.js.map +1 -1
  58. package/dist/bin/talaria-web.js +12 -81
  59. package/dist/bin/talaria-web.js.map +1 -1
  60. package/dist/cli/ctl.js +2 -1
  61. package/dist/cli/ctl.js.map +1 -1
  62. package/dist/cli/dotenv.d.ts +5 -0
  63. package/dist/cli/dotenv.js +15 -2
  64. package/dist/cli/dotenv.js.map +1 -1
  65. package/dist/cli/launcher.js +5 -5
  66. package/dist/cli/launcher.js.map +1 -1
  67. package/dist/cli/main.d.ts +1 -0
  68. package/dist/cli/main.js +85 -0
  69. package/dist/cli/main.js.map +1 -0
  70. package/dist/cli/supervise.d.ts +2 -0
  71. package/dist/cli/supervise.js +67 -4
  72. package/dist/cli/supervise.js.map +1 -1
  73. package/dist/config/agent-config.d.ts +34 -11
  74. package/dist/config/agent-config.js +116 -60
  75. package/dist/config/agent-config.js.map +1 -1
  76. package/dist/config.d.ts +4 -0
  77. package/dist/config.js +19 -6
  78. package/dist/config.js.map +1 -1
  79. package/dist/fs/atomic.d.ts +1 -0
  80. package/dist/fs/atomic.js +4 -1
  81. package/dist/fs/atomic.js.map +1 -1
  82. package/dist/fs/stat.d.ts +1 -0
  83. package/dist/fs/stat.js +11 -0
  84. package/dist/fs/stat.js.map +1 -0
  85. package/dist/http/context.d.ts +31 -12
  86. package/dist/http/context.js +60 -24
  87. package/dist/http/context.js.map +1 -1
  88. package/dist/http/csp.d.ts +2 -1
  89. package/dist/http/csp.js +3 -2
  90. package/dist/http/csp.js.map +1 -1
  91. package/dist/onboarding.d.ts +2 -2
  92. package/dist/onboarding.js +10 -8
  93. package/dist/onboarding.js.map +1 -1
  94. package/dist/profiles/profiles.d.ts +20 -4
  95. package/dist/profiles/profiles.js +115 -30
  96. package/dist/profiles/profiles.js.map +1 -1
  97. package/dist/projects.d.ts +15 -6
  98. package/dist/projects.js +55 -47
  99. package/dist/projects.js.map +1 -1
  100. package/dist/providers/catalog.d.ts +84 -14
  101. package/dist/providers/catalog.js +474 -151
  102. package/dist/providers/catalog.js.map +1 -1
  103. package/dist/providers/env-file.d.ts +10 -3
  104. package/dist/providers/env-file.js +24 -75
  105. package/dist/providers/env-file.js.map +1 -1
  106. package/dist/providers/runtime-env.d.ts +4 -2
  107. package/dist/providers/runtime-env.js +9 -6
  108. package/dist/providers/runtime-env.js.map +1 -1
  109. package/dist/providers/tables.d.ts +3 -1
  110. package/dist/providers/tables.js +4 -2
  111. package/dist/providers/tables.js.map +1 -1
  112. package/dist/raster-image.d.ts +7 -0
  113. package/dist/raster-image.js +267 -0
  114. package/dist/raster-image.js.map +1 -0
  115. package/dist/redact.js +342 -46
  116. package/dist/redact.js.map +1 -1
  117. package/dist/release.d.ts +7 -0
  118. package/dist/release.js +14 -0
  119. package/dist/release.js.map +1 -1
  120. package/dist/runtime.d.ts +5 -1
  121. package/dist/runtime.js +305 -96
  122. package/dist/runtime.js.map +1 -1
  123. package/dist/server.js +15 -0
  124. package/dist/server.js.map +1 -1
  125. package/dist/sessions/anchor.d.ts +9 -3
  126. package/dist/sessions/anchor.js +28 -36
  127. package/dist/sessions/anchor.js.map +1 -1
  128. package/dist/sessions/background-events.d.ts +2 -0
  129. package/dist/sessions/background-events.js +10 -0
  130. package/dist/sessions/background-events.js.map +1 -0
  131. package/dist/sessions/background-tasks.d.ts +26 -27
  132. package/dist/sessions/background-tasks.js +89 -97
  133. package/dist/sessions/background-tasks.js.map +1 -1
  134. package/dist/sessions/background-updates.d.ts +15 -8
  135. package/dist/sessions/background-updates.js +101 -25
  136. package/dist/sessions/background-updates.js.map +1 -1
  137. package/dist/sessions/cli-sessions.d.ts +27 -6
  138. package/dist/sessions/cli-sessions.js +152 -42
  139. package/dist/sessions/cli-sessions.js.map +1 -1
  140. package/dist/sessions/completions.d.ts +77 -9
  141. package/dist/sessions/completions.js +482 -45
  142. package/dist/sessions/completions.js.map +1 -1
  143. package/dist/sessions/compress.d.ts +8 -0
  144. package/dist/sessions/compress.js +2 -2
  145. package/dist/sessions/compress.js.map +1 -1
  146. package/dist/sessions/drafts.d.ts +14 -10
  147. package/dist/sessions/drafts.js +38 -43
  148. package/dist/sessions/drafts.js.map +1 -1
  149. package/dist/sessions/export.d.ts +5 -0
  150. package/dist/sessions/export.js +30 -0
  151. package/dist/sessions/export.js.map +1 -1
  152. package/dist/sessions/gateway-sessions.d.ts +46 -0
  153. package/dist/sessions/gateway-sessions.js +378 -0
  154. package/dist/sessions/gateway-sessions.js.map +1 -0
  155. package/dist/sessions/gateway-watcher.d.ts +7 -3
  156. package/dist/sessions/gateway-watcher.js +31 -19
  157. package/dist/sessions/gateway-watcher.js.map +1 -1
  158. package/dist/sessions/goals.d.ts +31 -0
  159. package/dist/sessions/goals.js +75 -0
  160. package/dist/sessions/goals.js.map +1 -0
  161. package/dist/sessions/handoff.d.ts +32 -0
  162. package/dist/sessions/handoff.js +114 -0
  163. package/dist/sessions/handoff.js.map +1 -0
  164. package/dist/sessions/journal.d.ts +5 -79
  165. package/dist/sessions/journal.js +15 -606
  166. package/dist/sessions/journal.js.map +1 -1
  167. package/dist/sessions/list.d.ts +31 -16
  168. package/dist/sessions/list.js +139 -129
  169. package/dist/sessions/list.js.map +1 -1
  170. package/dist/sessions/merge.d.ts +69 -13
  171. package/dist/sessions/merge.js +265 -64
  172. package/dist/sessions/merge.js.map +1 -1
  173. package/dist/sessions/notices.d.ts +22 -0
  174. package/dist/sessions/notices.js +72 -0
  175. package/dist/sessions/notices.js.map +1 -0
  176. package/dist/sessions/pending.d.ts +10 -4
  177. package/dist/sessions/pending.js +83 -7
  178. package/dist/sessions/pending.js.map +1 -1
  179. package/dist/sessions/prefix-index.d.ts +82 -0
  180. package/dist/sessions/prefix-index.js +232 -0
  181. package/dist/sessions/prefix-index.js.map +1 -0
  182. package/dist/sessions/recovery.d.ts +19 -0
  183. package/dist/sessions/recovery.js +322 -0
  184. package/dist/sessions/recovery.js.map +1 -0
  185. package/dist/sessions/relay.d.ts +38 -19
  186. package/dist/sessions/relay.js +302 -79
  187. package/dist/sessions/relay.js.map +1 -1
  188. package/dist/sessions/service.d.ts +260 -55
  189. package/dist/sessions/service.js +1448 -462
  190. package/dist/sessions/service.js.map +1 -1
  191. package/dist/sessions/session.d.ts +24 -2
  192. package/dist/sessions/session.js +69 -7
  193. package/dist/sessions/session.js.map +1 -1
  194. package/dist/sessions/shares.d.ts +7 -6
  195. package/dist/sessions/shares.js +22 -45
  196. package/dist/sessions/shares.js.map +1 -1
  197. package/dist/sessions/source-kind.js +1 -0
  198. package/dist/sessions/source-kind.js.map +1 -1
  199. package/dist/sessions/state-db-identity.d.ts +18 -0
  200. package/dist/sessions/state-db-identity.js +67 -0
  201. package/dist/sessions/state-db-identity.js.map +1 -0
  202. package/dist/sessions/state-db-worker.d.ts +1 -0
  203. package/dist/sessions/state-db-worker.js +35 -0
  204. package/dist/sessions/state-db-worker.js.map +1 -0
  205. package/dist/sessions/state-db.d.ts +113 -7
  206. package/dist/sessions/state-db.js +861 -44
  207. package/dist/sessions/state-db.js.map +1 -1
  208. package/dist/sessions/storage/backend.d.ts +162 -0
  209. package/dist/sessions/storage/backend.js +19 -0
  210. package/dist/sessions/storage/backend.js.map +1 -0
  211. package/dist/sessions/storage/conformance.d.ts +20 -0
  212. package/dist/sessions/storage/conformance.js +237 -0
  213. package/dist/sessions/storage/conformance.js.map +1 -0
  214. package/dist/sessions/storage/document.d.ts +16 -0
  215. package/dist/sessions/storage/document.js +44 -0
  216. package/dist/sessions/storage/document.js.map +1 -0
  217. package/dist/sessions/storage/json-documents.d.ts +23 -0
  218. package/dist/sessions/storage/json-documents.js +233 -0
  219. package/dist/sessions/storage/json-documents.js.map +1 -0
  220. package/dist/sessions/storage/json-files.d.ts +12 -0
  221. package/dist/sessions/storage/json-files.js +124 -0
  222. package/dist/sessions/storage/json-files.js.map +1 -0
  223. package/dist/sessions/storage/json-installation.d.ts +3 -0
  224. package/dist/sessions/storage/json-installation.js +31 -0
  225. package/dist/sessions/storage/json-installation.js.map +1 -0
  226. package/dist/sessions/storage/json-journal.d.ts +114 -0
  227. package/dist/sessions/storage/json-journal.js +686 -0
  228. package/dist/sessions/storage/json-journal.js.map +1 -0
  229. package/dist/sessions/storage/json-recovery.d.ts +4 -0
  230. package/dist/sessions/storage/json-recovery.js +195 -0
  231. package/dist/sessions/storage/json-recovery.js.map +1 -0
  232. package/dist/sessions/storage/json.d.ts +68 -0
  233. package/dist/sessions/storage/json.js +258 -0
  234. package/dist/sessions/storage/json.js.map +1 -0
  235. package/dist/sessions/storage/row.d.ts +6 -0
  236. package/dist/sessions/storage/row.js +49 -0
  237. package/dist/sessions/storage/row.js.map +1 -0
  238. package/dist/sessions/storage/search-text.d.ts +4 -0
  239. package/dist/sessions/storage/search-text.js +16 -0
  240. package/dist/sessions/storage/search-text.js.map +1 -0
  241. package/dist/sessions/store.d.ts +65 -68
  242. package/dist/sessions/store.js +305 -602
  243. package/dist/sessions/store.js.map +1 -1
  244. package/dist/sessions/streams.d.ts +4 -0
  245. package/dist/sessions/streams.js +9 -1
  246. package/dist/sessions/streams.js.map +1 -1
  247. package/dist/sessions/titles.d.ts +2 -0
  248. package/dist/sessions/titles.js +5 -1
  249. package/dist/sessions/titles.js.map +1 -1
  250. package/dist/sessions/todo.d.ts +5 -0
  251. package/dist/sessions/todo.js +28 -21
  252. package/dist/sessions/todo.js.map +1 -1
  253. package/dist/sessions/tool-display.d.ts +5 -4
  254. package/dist/sessions/tool-display.js +30 -50
  255. package/dist/sessions/tool-display.js.map +1 -1
  256. package/dist/sessions/turn-context.d.ts +11 -2
  257. package/dist/sessions/turn-context.js +18 -11
  258. package/dist/sessions/turn-context.js.map +1 -1
  259. package/dist/sessions/turn.d.ts +122 -17
  260. package/dist/sessions/turn.js +939 -358
  261. package/dist/sessions/turn.js.map +1 -1
  262. package/dist/sessions/wakeup-pause.d.ts +54 -0
  263. package/dist/sessions/wakeup-pause.js +163 -0
  264. package/dist/sessions/wakeup-pause.js.map +1 -0
  265. package/dist/sessions/window.js +13 -12
  266. package/dist/sessions/window.js.map +1 -1
  267. package/dist/settings.d.ts +19 -7
  268. package/dist/settings.js +178 -184
  269. package/dist/settings.js.map +1 -1
  270. package/dist/sidecar/client.d.ts +17 -3
  271. package/dist/sidecar/client.js +78 -8
  272. package/dist/sidecar/client.js.map +1 -1
  273. package/dist/sidecar/discover.d.ts +2 -1
  274. package/dist/sidecar/discover.js +93 -10
  275. package/dist/sidecar/discover.js.map +1 -1
  276. package/dist/sidecar/env.d.ts +4 -0
  277. package/dist/sidecar/env.js +3 -0
  278. package/dist/sidecar/env.js.map +1 -0
  279. package/dist/sidecar/fake.d.ts +2 -2
  280. package/dist/sidecar/fake.js +19 -2
  281. package/dist/sidecar/fake.js.map +1 -1
  282. package/dist/startup.d.ts +13 -5
  283. package/dist/startup.js +48 -0
  284. package/dist/startup.js.map +1 -1
  285. package/dist/test/env-file.d.ts +5 -0
  286. package/dist/test/env-file.js +88 -0
  287. package/dist/test/env-file.js.map +1 -0
  288. package/dist/test/harness.d.ts +1 -0
  289. package/dist/test/harness.js +1 -1
  290. package/dist/test/harness.js.map +1 -1
  291. package/dist/test/passkeys.d.ts +22 -0
  292. package/dist/test/passkeys.js +62 -0
  293. package/dist/test/passkeys.js.map +1 -0
  294. package/dist/test/storage.d.ts +15 -0
  295. package/dist/test/storage.js +26 -0
  296. package/dist/test/storage.js.map +1 -0
  297. package/dist/test/ts-worker-hooks.d.ts +1 -0
  298. package/dist/test/ts-worker-hooks.js +24 -0
  299. package/dist/test/ts-worker-hooks.js.map +1 -0
  300. package/dist/text/diff.d.ts +9 -14
  301. package/dist/text/diff.js +74 -108
  302. package/dist/text/diff.js.map +1 -1
  303. package/dist/tools/commands.d.ts +6 -1
  304. package/dist/tools/commands.js +8 -2
  305. package/dist/tools/commands.js.map +1 -1
  306. package/dist/tools/connectors.d.ts +33 -0
  307. package/dist/tools/connectors.js +121 -0
  308. package/dist/tools/connectors.js.map +1 -0
  309. package/dist/tools/crons.d.ts +38 -1
  310. package/dist/tools/crons.js +122 -18
  311. package/dist/tools/crons.js.map +1 -1
  312. package/dist/tools/dashboard-plugins.d.ts +53 -0
  313. package/dist/tools/dashboard-plugins.js +188 -0
  314. package/dist/tools/dashboard-plugins.js.map +1 -0
  315. package/dist/tools/extensions.d.ts +18 -5
  316. package/dist/tools/extensions.js +344 -266
  317. package/dist/tools/extensions.js.map +1 -1
  318. package/dist/tools/gateway-restart.d.ts +62 -0
  319. package/dist/tools/gateway-restart.js +142 -0
  320. package/dist/tools/gateway-restart.js.map +1 -0
  321. package/dist/tools/health.d.ts +44 -0
  322. package/dist/tools/health.js +201 -46
  323. package/dist/tools/health.js.map +1 -1
  324. package/dist/tools/hygiene.d.ts +4 -3
  325. package/dist/tools/hygiene.js +23 -9
  326. package/dist/tools/hygiene.js.map +1 -1
  327. package/dist/tools/kanban.d.ts +4 -2
  328. package/dist/tools/kanban.js +28 -19
  329. package/dist/tools/kanban.js.map +1 -1
  330. package/dist/tools/mcp.d.ts +13 -0
  331. package/dist/tools/mcp.js +88 -0
  332. package/dist/tools/mcp.js.map +1 -1
  333. package/dist/tools/memory-masks.d.ts +5 -0
  334. package/dist/tools/memory-masks.js +256 -0
  335. package/dist/tools/memory-masks.js.map +1 -0
  336. package/dist/tools/memory.d.ts +29 -3
  337. package/dist/tools/memory.js +69 -27
  338. package/dist/tools/memory.js.map +1 -1
  339. package/dist/tools/plugin-settings.d.ts +15 -0
  340. package/dist/tools/plugin-settings.js +90 -0
  341. package/dist/tools/plugin-settings.js.map +1 -0
  342. package/dist/tools/project-os.d.ts +11 -0
  343. package/dist/tools/project-os.js +267 -0
  344. package/dist/tools/project-os.js.map +1 -0
  345. package/dist/tools/skills.d.ts +14 -5
  346. package/dist/tools/skills.js +49 -16
  347. package/dist/tools/skills.js.map +1 -1
  348. package/dist/tools/update-notifications.d.ts +34 -15
  349. package/dist/tools/update-notifications.js +141 -57
  350. package/dist/tools/update-notifications.js.map +1 -1
  351. package/dist/tools/update-rollback.d.ts +11 -0
  352. package/dist/tools/update-rollback.js +28 -0
  353. package/dist/tools/update-rollback.js.map +1 -0
  354. package/dist/tools/updates.d.ts +110 -32
  355. package/dist/tools/updates.js +1112 -294
  356. package/dist/tools/updates.js.map +1 -1
  357. package/dist/tools/wiki.d.ts +68 -0
  358. package/dist/tools/wiki.js +413 -0
  359. package/dist/tools/wiki.js.map +1 -0
  360. package/dist/util.d.ts +2 -0
  361. package/dist/util.js +5 -0
  362. package/dist/util.js.map +1 -1
  363. package/dist/workspace/escape.d.ts +61 -0
  364. package/dist/workspace/escape.js +149 -0
  365. package/dist/workspace/escape.js.map +1 -0
  366. package/dist/workspace/extract.d.ts +22 -0
  367. package/dist/workspace/extract.js +308 -0
  368. package/dist/workspace/extract.js.map +1 -0
  369. package/dist/workspace/fs.d.ts +12 -0
  370. package/dist/workspace/fs.js +92 -23
  371. package/dist/workspace/fs.js.map +1 -1
  372. package/dist/workspace/git.d.ts +34 -1
  373. package/dist/workspace/git.js +328 -35
  374. package/dist/workspace/git.js.map +1 -1
  375. package/dist/workspace/media.d.ts +16 -13
  376. package/dist/workspace/media.js +43 -61
  377. package/dist/workspace/media.js.map +1 -1
  378. package/dist/workspace/rollback.d.ts +36 -22
  379. package/dist/workspace/rollback.js +178 -272
  380. package/dist/workspace/rollback.js.map +1 -1
  381. package/dist/workspace/unzip.js +4 -1
  382. package/dist/workspace/unzip.js.map +1 -1
  383. package/dist/workspace/upload.d.ts +7 -4
  384. package/dist/workspace/upload.js +5 -2
  385. package/dist/workspace/upload.js.map +1 -1
  386. package/dist/workspace/workspaces.d.ts +15 -19
  387. package/dist/workspace/workspaces.js +55 -127
  388. package/dist/workspace/workspaces.js.map +1 -1
  389. package/dist/workspace/worktrees.d.ts +2 -1
  390. package/dist/workspace/worktrees.js +179 -42
  391. package/dist/workspace/worktrees.js.map +1 -1
  392. package/package.json +7 -3
  393. package/sidecar/agent_dependency.json +4 -4
  394. package/sidecar/agent_support.json +26 -0
  395. package/sidecar/scripts/record-fixtures.py +12 -3
  396. package/sidecar/scripts/replay_sidecar.py +32 -0
  397. package/sidecar/scripts/suite_runner.py +302 -0
  398. package/sidecar/scripts/test.sh +12 -4
  399. package/sidecar/talaria_sidecar/__init__.py +6 -1
  400. package/sidecar/talaria_sidecar/__main__.py +4 -2
  401. package/sidecar/talaria_sidecar/agent_mutation.py +975 -0
  402. package/sidecar/talaria_sidecar/agent_services.py +1053 -0
  403. package/sidecar/talaria_sidecar/dependency_sync.py +85 -0
  404. package/sidecar/talaria_sidecar/home.py +56 -3
  405. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  406. package/sidecar/talaria_sidecar/methods/aux.py +166 -24
  407. package/sidecar/talaria_sidecar/methods/chat.py +852 -135
  408. package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
  409. package/sidecar/talaria_sidecar/methods/commands.py +294 -4
  410. package/sidecar/talaria_sidecar/methods/config.py +123 -4
  411. package/sidecar/talaria_sidecar/methods/connectors.py +343 -0
  412. package/sidecar/talaria_sidecar/methods/cron.py +395 -36
  413. package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
  414. package/sidecar/talaria_sidecar/methods/gateway.py +212 -55
  415. package/sidecar/talaria_sidecar/methods/gateway_sessions.py +234 -0
  416. package/sidecar/talaria_sidecar/methods/goals.py +81 -41
  417. package/sidecar/talaria_sidecar/methods/kanban.py +108 -34
  418. package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
  419. package/sidecar/talaria_sidecar/methods/memory.py +84 -0
  420. package/sidecar/talaria_sidecar/methods/oauth.py +5 -2
  421. package/sidecar/talaria_sidecar/methods/process.py +218 -15
  422. package/sidecar/talaria_sidecar/methods/profiles.py +121 -13
  423. package/sidecar/talaria_sidecar/methods/providers.py +217 -28
  424. package/sidecar/talaria_sidecar/methods/runtime.py +69 -1
  425. package/sidecar/talaria_sidecar/methods/skills.py +67 -4
  426. package/sidecar/talaria_sidecar/methods/state_db.py +316 -75
  427. package/sidecar/talaria_sidecar/methods/stt.py +159 -78
  428. package/sidecar/talaria_sidecar/methods/text.py +14 -1
  429. package/sidecar/talaria_sidecar/methods/toolsets.py +49 -0
  430. package/sidecar/talaria_sidecar/methods/usage.py +113 -14
  431. package/sidecar/talaria_sidecar/methods/worktree.py +26 -19
  432. package/sidecar/talaria_sidecar/methods/write_approval.py +181 -0
  433. package/sidecar/talaria_sidecar/rpc.py +45 -4
  434. package/sidecar/talaria_sidecar/runtime.py +98 -13
  435. package/sidecar/talaria_sidecar/speed.py +35 -0
  436. package/static/dist/FILES.txt +126 -126
  437. package/static/dist/assets/AppShell-cey33v9s.js +8 -0
  438. package/static/dist/assets/ChatPage-i8wnugr7.js +18 -0
  439. package/static/dist/assets/ConnectTargets-hg19553x.js +1 -0
  440. package/static/dist/assets/ExtensionRoute-cly4apm5.js +1 -0
  441. package/static/dist/assets/Field-j8uqoigz.js +9 -0
  442. package/static/dist/assets/HubRoute-e4bgz41j.js +2 -0
  443. package/static/dist/assets/Markdown-lahuichm.js +598 -0
  444. package/static/dist/assets/RightPanel-lbo4i0fn.js +1 -0
  445. package/static/dist/assets/Select-gqatm2e0.js +1 -0
  446. package/static/dist/assets/SessionListPanel-mfqayfjh.js +1 -0
  447. package/static/dist/assets/SettingsLayout-b8980u7k.js +1 -0
  448. package/static/dist/assets/States-e7dkgtxf.js +1 -0
  449. package/static/dist/assets/Toaster-bfqqy786.js +1 -0
  450. package/static/dist/assets/_app-n1n6pfx1.js +1 -0
  451. package/static/dist/assets/{_app.ext._extensionId-opyh9y0v.js → _app.ext._extensionId-buxjxav3.js} +1 -1
  452. package/static/dist/assets/_app.index-jt8zwiu2.js +1 -0
  453. package/static/dist/assets/_app.insights-cv76ml71.js +1 -0
  454. package/static/dist/assets/_app.kanban-h6ts53j7.js +1 -0
  455. package/static/dist/assets/_app.logs-dm873z90.js +1 -0
  456. package/static/dist/assets/_app.memory-glk5y6m8.js +1 -0
  457. package/static/dist/assets/_app.profiles-ka6zpstq.js +1 -0
  458. package/static/dist/assets/{_app.session._sessionId-bfd4h3id.js → _app.session._sessionId-g2vzghvc.js} +1 -1
  459. package/static/dist/assets/_app.settings-hgjdhzcd.js +1 -0
  460. package/static/dist/assets/_app.settings._section-jf2kmhae.js +3 -0
  461. package/static/dist/assets/_app.skills-cii852yq.js +1 -0
  462. package/static/dist/assets/_app.tasks-fp9q7atm.js +1 -0
  463. package/static/dist/assets/_app.todos-dw94qzue.js +1 -0
  464. package/static/dist/assets/_app.workspaces-inyygzze.js +1 -0
  465. package/static/dist/assets/{abnfDiagram-VCTEODGH-b2x1lqot.js → abnfDiagram-VCTEODGH-gjayq3ra.js} +1 -1
  466. package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +1 -0
  467. package/static/dist/assets/{architectureDiagram-5GKGNRK7-h38o5l7b.js → architectureDiagram-5GKGNRK7-nwb3xovn.js} +1 -1
  468. package/static/dist/assets/archive-b1j004vn.js +1 -0
  469. package/static/dist/assets/back-g74gkozu.js +1 -0
  470. package/static/dist/assets/{blockDiagram-I7D4REHJ-bqquxv2u.js → blockDiagram-I7D4REHJ-joedghi3.js} +1 -1
  471. package/static/dist/assets/{c4Diagram-7LVT6UL2-f3fb8w6z.js → c4Diagram-7LVT6UL2-hyrjrohe.js} +1 -1
  472. package/static/dist/assets/channel-es95o4y8.js +1 -0
  473. package/static/dist/assets/{chunk-4HAMMTFA-bzfyyzp3.js → chunk-4HAMMTFA-koouqbut.js} +1 -1
  474. package/static/dist/assets/{chunk-75Z2AOVW-ly99rfx5.js → chunk-75Z2AOVW-bl4bhdnt.js} +1 -1
  475. package/static/dist/assets/{chunk-DU6HZSFF-bg6ecfqf.js → chunk-DU6HZSFF-gpedttsp.js} +3 -3
  476. package/static/dist/assets/{chunk-F27PBJKO-e8k7mmqw.js → chunk-F27PBJKO-d8xxi0zi.js} +1 -1
  477. package/static/dist/assets/{chunk-GMAD6QVW-cmalnoaj.js → chunk-GMAD6QVW-jro0hl73.js} +1 -1
  478. package/static/dist/assets/{chunk-GVQU2GXP-gmif5o01.js → chunk-GVQU2GXP-djn6gbyu.js} +1 -1
  479. package/static/dist/assets/{chunk-IMKFNOWR-f1nwwh6u.js → chunk-IMKFNOWR-vw4p5p7o.js} +1 -1
  480. package/static/dist/assets/{chunk-L3NEJ4N5-oktfh5v7.js → chunk-L3NEJ4N5-n1hj77q8.js} +1 -1
  481. package/static/dist/assets/{chunk-OSK3NFVY-b1sdx96z.js → chunk-OSK3NFVY-oy0gwn2n.js} +1 -1
  482. package/static/dist/assets/{chunk-P2QGCYS3-fhu4nzxt.js → chunk-P2QGCYS3-onf1hz6c.js} +1 -1
  483. package/static/dist/assets/{chunk-POPQ4Y6H-em31rnef.js → chunk-POPQ4Y6H-m8sqn7cs.js} +1 -1
  484. package/static/dist/assets/{chunk-PWAF6VOD-bxhhyftw.js → chunk-PWAF6VOD-ek4smc8x.js} +1 -1
  485. package/static/dist/assets/{chunk-SHT3W25Y-fmrvwcav.js → chunk-SHT3W25Y-g9gfvefo.js} +3 -3
  486. package/static/dist/assets/{chunk-SVP7TREG-lymukl2j.js → chunk-SVP7TREG-f9nfv2cu.js} +1 -1
  487. package/static/dist/assets/{chunk-TICWLB2K-lmza6k23.js → chunk-TICWLB2K-bnk3m1mq.js} +1 -1
  488. package/static/dist/assets/classDiagram-ZZMXUADV-kmihoqxh.js +1 -0
  489. package/static/dist/assets/classDiagram-v2-VYDZK3BY-kmihoqxh.js +1 -0
  490. package/static/dist/assets/copied-bli9tbcx.js +1 -0
  491. package/static/dist/assets/{copy-fw5b1pc6.js → copy-jamtd1pf.js} +1 -1
  492. package/static/dist/assets/{cynefin-OW5HDTMX-czlhbyhs.js → cynefin-OW5HDTMX-f8xfdd3f.js} +1 -1
  493. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-gq7mvswb.js → cynefinDiagram-5FMLGOSQ-n9lb99h9.js} +1 -1
  494. package/static/dist/assets/{dagre-GXQ25YYZ-cqdbf7dr.js → dagre-GXQ25YYZ-dgailddi.js} +4 -4
  495. package/static/dist/assets/{diagram-S7CK7UJ4-o6arxa58.js → diagram-S7CK7UJ4-m6aeoe7c.js} +1 -1
  496. package/static/dist/assets/{diagram-UQ7AKVKN-nhnd68tz.js → diagram-UQ7AKVKN-jall3j0x.js} +1 -1
  497. package/static/dist/assets/{diagram-VSXAHHWV-dk2sclzn.js → diagram-VSXAHHWV-hbawi7hn.js} +1 -1
  498. package/static/dist/assets/{diagram-VX7I27RA-n3k5nyx3.js → diagram-VX7I27RA-hendl2tt.js} +1 -1
  499. package/static/dist/assets/{diagram-Z3DM3KII-m6q9cwdh.js → diagram-Z3DM3KII-igwc35p3.js} +1 -1
  500. package/static/dist/assets/{ebnfDiagram-PWID7BFC-lsa5641p.js → ebnfDiagram-PWID7BFC-c64knz08.js} +1 -1
  501. package/static/dist/assets/{erDiagram-RLTQ6QDP-fo2ckbe6.js → erDiagram-RLTQ6QDP-brdu9lcm.js} +1 -1
  502. package/static/dist/assets/eventmodeling-NTZA5JFV-njk1j8wl.js +1 -0
  503. package/static/dist/assets/flowDiagram-HODETNUW-nma6f0ga.js +1 -0
  504. package/static/dist/assets/folder-plus-lvt9kudb.js +1 -0
  505. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-nzqz6x9g.js → ganttDiagram-EL5Y4UJY-ma6jxj5i.js} +1 -1
  506. package/static/dist/assets/{gitGraph-4MIJSDKK-gukvcxe6.js → gitGraph-4MIJSDKK-naa9w6xa.js} +1 -1
  507. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-lqhdp8f0.js → gitGraphDiagram-WWUBYQGX-hzltvsod.js} +1 -1
  508. package/static/dist/assets/{highlighted-body-KPVGNVTW-igw6cp39.js → highlighted-body-KPVGNVTW-ghoavj8d.js} +1 -1
  509. package/static/dist/assets/host-hzm8n85n.js +1 -0
  510. package/static/dist/assets/index-drdcaqeq.css +2 -0
  511. package/static/dist/assets/index-jzm844he.js +11 -0
  512. package/static/dist/assets/{info-A6RAGUB7-gvu7a93l.js → info-A6RAGUB7-mn2ut5ko.js} +1 -1
  513. package/static/dist/assets/{infoDiagram-27XIBGKW-m8t8g6w1.js → infoDiagram-27XIBGKW-h6kuqvsb.js} +1 -1
  514. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-l07vkbgj.js → ishikawaDiagram-5VMMS53U-e7bnwq7h.js} +1 -1
  515. package/static/dist/assets/{journeyDiagram-3NMN7TZE-hybkgj8l.js → journeyDiagram-3NMN7TZE-etd1bi6p.js} +1 -1
  516. package/static/dist/assets/{kanban-definition-UXKFOSKX-by13uvtn.js → kanban-definition-UXKFOSKX-emb1g9dz.js} +4 -4
  517. package/static/dist/assets/lazyRouteComponent-o816ytzl.js +1 -0
  518. package/static/dist/assets/{line-osxq6fq7.js → line-h0ojzsvs.js} +1 -1
  519. package/static/dist/assets/link-k01ryaf9.js +1 -0
  520. package/static/dist/assets/login-bsae49ew.js +1 -0
  521. package/static/dist/assets/mermaid-HWGCJPDP-ihl34s74.js +1 -0
  522. package/static/dist/assets/{mermaid-parser.core-i91oxmou.js → mermaid-parser.core-bnre27u8.js} +3 -3
  523. package/static/dist/assets/{mindmap-definition-YA3MSWOX-m82mx8ww.js → mindmap-definition-YA3MSWOX-o1vathns.js} +1 -1
  524. package/static/dist/assets/onboarding-c65tysya.js +1 -0
  525. package/static/dist/assets/onboarding_model_label-gpn28uix.js +1 -0
  526. package/static/dist/assets/{packet-AYTQ26CC-nbtgge2o.js → packet-AYTQ26CC-hatq7m2e.js} +1 -1
  527. package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
  528. package/static/dist/assets/{pegDiagram-XKGWAZYB-f1zxzga1.js → pegDiagram-XKGWAZYB-gwisz8jv.js} +1 -1
  529. package/static/dist/assets/{pie-WAS4IAKB-of8vuhxs.js → pie-WAS4IAKB-fpftf0mk.js} +1 -1
  530. package/static/dist/assets/{pieDiagram-E7YTZNPT-o7y8ftio.js → pieDiagram-E7YTZNPT-khm1o9a6.js} +1 -1
  531. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-iqiwia1t.js → quadrantDiagram-AXDQQJYC-owqc4uqn.js} +1 -1
  532. package/static/dist/assets/{radar-RG4KPBEZ-delnsy2v.js → radar-RG4KPBEZ-cntfwjpq.js} +1 -1
  533. package/static/dist/assets/{railroad-74A4TZTK-nspnyq9u.js → railroad-74A4TZTK-a7t5eibp.js} +1 -1
  534. package/static/dist/assets/railroad-abnf-HS5TGJTU-ol8cdktf.js +1 -0
  535. package/static/dist/assets/railroad-ebnf-LZEXJU2U-i8vzd429.js +1 -0
  536. package/static/dist/assets/railroad-peg-WCYAUIDC-nw3ulewz.js +1 -0
  537. package/static/dist/assets/{railroadDiagram-O6MQD6OU-z41tqx5z.js → railroadDiagram-O6MQD6OU-etuh0qa0.js} +1 -1
  538. package/static/dist/assets/{refresh-cw-nt1dtsx4.js → refresh-cw-mqbkv2a3.js} +1 -1
  539. package/static/dist/assets/refresh-l29ljp76.js +1 -0
  540. package/static/dist/assets/{requirementDiagram-BXWQKSXE-bdvif194.js → requirementDiagram-BXWQKSXE-oq2ulk22.js} +1 -1
  541. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-h73e0rol.js → sankeyDiagram-P5KCCOFB-iqc02emv.js} +1 -1
  542. package/static/dist/assets/saved-ndd4t2xb.js +1 -0
  543. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-iv95mb1z.js → sequenceDiagram-WJ2MYXX4-i0v116q0.js} +2 -2
  544. package/static/dist/assets/settings_section_extensions_title-jhfo6o8z.js +1 -0
  545. package/static/dist/assets/share._token-c1ri0hy1.js +2 -0
  546. package/static/dist/assets/sliders-horizontal-j3jjt9tv.js +1 -0
  547. package/static/dist/assets/{stateDiagram-D77RDMKH-k417ixmf.js → stateDiagram-D77RDMKH-dnpt1ato.js} +1 -1
  548. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +1 -0
  549. package/static/dist/assets/{swimlanes-42K2YHIH-esola70w.js → swimlanes-42K2YHIH-loqbdazb.js} +1 -1
  550. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +8 -0
  551. package/static/dist/assets/{timeline-definition-24CTP7MA-getrz2y8.js → timeline-definition-24CTP7MA-ol2hnvma.js} +1 -1
  552. package/static/dist/assets/toast-i3h34v6o.js +64 -0
  553. package/static/dist/assets/{treeView-Q6P3EWNA-hc7ap1q7.js → treeView-Q6P3EWNA-idrj6iox.js} +1 -1
  554. package/static/dist/assets/{treemap-WGGIJYW6-b7k1syy5.js → treemap-WGGIJYW6-lzr49vv5.js} +1 -1
  555. package/static/dist/assets/untitled-oaoya4qj.js +1 -0
  556. package/static/dist/assets/useForm-nfaxgmsk.js +1 -0
  557. package/static/dist/assets/{useParams-lhg566ew.js → useParams-gjc70qru.js} +1 -1
  558. package/static/dist/assets/{useSearch-lbrh6ejn.js → useSearch-e6ae539u.js} +1 -1
  559. package/static/dist/assets/{vennDiagram-4TSXK5OY-n6pcy5qb.js → vennDiagram-4TSXK5OY-bvwr9o2z.js} +1 -1
  560. package/static/dist/assets/{wardley-WFR3VGLG-lpa18vz1.js → wardley-WFR3VGLG-31ui8tma.js} +1 -1
  561. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-kpclwwg7.js → wardleyDiagram-VM6X3IG4-k5wqtx4b.js} +1 -1
  562. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-paa2n8y0.js → xychartDiagram-S5SC5T6Z-aqbgvujg.js} +1 -1
  563. package/static/dist/index.html +1 -1
  564. package/static/dist/sw.js +1 -1
  565. package/static/dist/assets/AppShell-fb2td9zi.js +0 -1
  566. package/static/dist/assets/ChatPage-cj96kiy9.js +0 -25
  567. package/static/dist/assets/Dialog-kxcg7ard.js +0 -1
  568. package/static/dist/assets/ExtensionRoute-jf7h0754.js +0 -1
  569. package/static/dist/assets/HubRoute-gmanpd5y.js +0 -2
  570. package/static/dist/assets/Markdown-mdwlvj3w.js +0 -598
  571. package/static/dist/assets/RightPanel-ftvrjlcp.js +0 -1
  572. package/static/dist/assets/Select-bilaypej.js +0 -1
  573. package/static/dist/assets/SessionListPanel-og8n36qs.js +0 -1
  574. package/static/dist/assets/SettingsLayout-jonjefmj.js +0 -1
  575. package/static/dist/assets/States-nmejrwrm.js +0 -1
  576. package/static/dist/assets/Toaster-nnl8zkt9.js +0 -1
  577. package/static/dist/assets/_app-gdidzd94.js +0 -1
  578. package/static/dist/assets/_app.index-hbarzbjx.js +0 -1
  579. package/static/dist/assets/_app.insights-luywlarz.js +0 -1
  580. package/static/dist/assets/_app.kanban-kincnbr8.js +0 -1
  581. package/static/dist/assets/_app.logs-oi2gr3gs.js +0 -1
  582. package/static/dist/assets/_app.memory-b6jfivt6.js +0 -1
  583. package/static/dist/assets/_app.profiles-lpvixmqp.js +0 -1
  584. package/static/dist/assets/_app.settings-vowx3iq0.js +0 -1
  585. package/static/dist/assets/_app.settings._section-cnntg1tu.js +0 -3
  586. package/static/dist/assets/_app.skills-fb57lpau.js +0 -1
  587. package/static/dist/assets/_app.tasks-eszpx1q7.js +0 -2
  588. package/static/dist/assets/_app.todos-nr05ac6w.js +0 -1
  589. package/static/dist/assets/_app.workspaces-d2luomu6.js +0 -1
  590. package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +0 -1
  591. package/static/dist/assets/back-bemaer09.js +0 -1
  592. package/static/dist/assets/cancel-drd0g6d9.js +0 -1
  593. package/static/dist/assets/channel-cpdgf867.js +0 -1
  594. package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +0 -1
  595. package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +0 -1
  596. package/static/dist/assets/copied-ima3pysb.js +0 -1
  597. package/static/dist/assets/endpoints-d33siokx.js +0 -64
  598. package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +0 -1
  599. package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +0 -1
  600. package/static/dist/assets/index-cxq58xb1.js +0 -11
  601. package/static/dist/assets/index-hgtx08n5.css +0 -2
  602. package/static/dist/assets/lazyRouteComponent-af0lvgjx.js +0 -1
  603. package/static/dist/assets/link-jyxicdhg.js +0 -1
  604. package/static/dist/assets/login-ec21t39x.js +0 -1
  605. package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +0 -1
  606. package/static/dist/assets/onboarding-btnltvyz.js +0 -1
  607. package/static/dist/assets/onboarding_model_label-dp7xffoh.js +0 -1
  608. package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +0 -1
  609. package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +0 -1
  610. package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +0 -1
  611. package/static/dist/assets/refresh-b4uams1i.js +0 -1
  612. package/static/dist/assets/saved-kg52csy7.js +0 -1
  613. package/static/dist/assets/settings_section_extensions_title-ej0iapxe.js +0 -1
  614. package/static/dist/assets/share._token-f97e94r0.js +0 -2
  615. package/static/dist/assets/sliders-horizontal-fj18it47.js +0 -1
  616. package/static/dist/assets/sse-fwpqtzbg.js +0 -1
  617. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +0 -1
  618. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +0 -8
  619. package/static/dist/assets/todoStore-eginf9h4.js +0 -1
  620. package/static/dist/assets/tts-lxdxnmjd.js +0 -1
  621. package/static/dist/assets/untitled-ovpfthg4.js +0 -1
  622. package/static/dist/assets/useForm-bcblv1rx.js +0 -1
  623. package/static/dist/assets/useLocale-fi5jb8ma.js +0 -1
@@ -10,7 +10,7 @@ import { platform } from 'node:os';
10
10
  import { spawn } from 'node:child_process';
11
11
  import { ifNoneMatchMatches } from '../http/context.js';
12
12
  import { SidecarError } from '../sidecar/client.js';
13
- import { HttpError, requireFields } from './router.js';
13
+ import { HttpError, RawResponse, requireFields } from './router.js';
14
14
  import { GitWorkspaceError, WORKSPACE_BUSY_MESSAGE } from '../workspace/git.js';
15
15
  import { requestSessionIdGuard } from './session-visibility.js';
16
16
  import { HttpFailure, sanitizePaths } from '../sessions/service.js';
@@ -25,6 +25,8 @@ import { randomUUID } from 'node:crypto';
25
25
  import { pyOsError, pyRepr, str } from '../util.js';
26
26
  import { ensureTrustedAuthSession, sessionCanManageServer } from '../auth/gate.js';
27
27
  import { RelayPairingError } from '../sessions/relay.js';
28
+ import { EscapeGrantExpired } from '../workspace/escape.js';
29
+ import { ProfileError } from '../profiles/profiles.js';
28
30
  const os = implement({ ...sessionsContract, ...workspacesContract }).$context().use(requestSessionIdGuard);
29
31
  /** Map service failures to the Python-shaped error body. */
30
32
  export function failure(error) {
@@ -32,6 +34,8 @@ export function failure(error) {
32
34
  throw new HttpError(error.status, error.message, error.extra);
33
35
  if (error instanceof SessionNotFound)
34
36
  throw new HttpError(404, 'Session not found');
37
+ if (error instanceof ProfileError)
38
+ throw new HttpError(error.status, error.message);
35
39
  throw error;
36
40
  }
37
41
  /** Handler boundary: contract outputs are pinned loose objects (index signatures), so the concrete return type is erased here like the `as never` casts elsewhere. */
@@ -62,18 +66,37 @@ function queryPositiveInt(value, fallback, maximum) {
62
66
  }
63
67
  const sanitizeError = sanitizePaths;
64
68
  /** Python `_guard_request_session_visibility` for a body/query session id. */
65
- function guardVisibility(ctx, sid) {
66
- if (!ctx.deps.sessions.sessionIdVisible(sid))
69
+ async function guardVisibility(ctx, sid) {
70
+ if (!(await ctx.deps.sessions.sessionIdVisible(sid)))
67
71
  throw new HttpError(404, 'Session not found');
68
72
  }
73
+ /** Python `float(body["since"])` for the handoff dock: a unix time or numeric string; absent is null. */
74
+ function handoffSince(value) {
75
+ if (value === undefined || value === null)
76
+ return null;
77
+ const n = typeof value === 'number' ? value : typeof value === 'string' && value.trim() ? Number(value) : Number.NaN;
78
+ if (Number.isNaN(n))
79
+ throw new HttpError(400, 'since must be a unix timestamp (number)');
80
+ return n;
81
+ }
82
+ /** The handoff dock's session id and `since`, validated in the predecessor's order. */
83
+ async function handoffInput(ctx, input) {
84
+ requireFields(input, 'session_id');
85
+ const sid = str(input.session_id).trim();
86
+ if (!sid)
87
+ throw new HttpError(400, 'session_id is required');
88
+ const since = handoffSince(input.since);
89
+ await guardVisibility(ctx, sid);
90
+ return { sid, since };
91
+ }
69
92
  function allProfilesEnabled(ctx, value) {
70
93
  return queryFlag(value) && !ctx.deps.isolatedProfileMode();
71
94
  }
72
95
  /** Search inputs that select the sidebar rows rather than every stored session (TAL-308). */
73
96
  const SIDEBAR_SEARCH_KEYS = ['project_id', 'sidebar_source', 'include_archived', 'exclude_hidden', 'show_cli_sessions', 'show_claude_code_sessions', 'show_cron_sessions', 'show_webhook_sessions', 'show_kanban_sessions'];
74
97
  /** The `/api/sessions` row selection for a list or sidebar-search query (TAL-308 shares it). */
75
- function sidebarListParams(ctx, input) {
76
- const settings = ctx.deps.settings.load();
98
+ async function sidebarListParams(ctx, input) {
99
+ const settings = (await ctx.deps.settings.load());
77
100
  const overrideKeys = ['show_cli_sessions', 'show_claude_code_sessions', 'show_cron_sessions', 'show_webhook_sessions', 'show_kanban_sessions'];
78
101
  const sidebarSourceRaw = (input.sidebar_source ?? '').trim().toLowerCase();
79
102
  return {
@@ -95,32 +118,33 @@ function sidebarListParams(ctx, input) {
95
118
  }
96
119
  export const sessionsRouter = os.router({
97
120
  sessions: {
98
- list: os.sessions.list.handler(({ input, context: { ctx } }) => run(() => {
99
- const { body, etag } = ctx.deps.sessions.list(sidebarListParams(ctx, input));
121
+ list: os.sessions.list.handler(({ input, context: { ctx } }) => run(async () => {
122
+ const { body, etag } = await ctx.deps.sessions.list((await sidebarListParams(ctx, input)));
100
123
  ctx.extraResponseHeaders = { etag };
101
124
  if (ifNoneMatchMatches(ctx.header('if-none-match') ?? '', etag))
102
125
  ctx.notModified = true;
103
126
  return body;
104
127
  })),
105
- search: os.sessions.search.handler(({ input, context: { ctx } }) => run(() => {
128
+ search: os.sessions.search.handler(({ input, context: { ctx } }) => run(async () => {
106
129
  // Python: `int(depth)` falls back to 5 when malformed.
107
130
  const depthRaw = (input.depth ?? '5').trim();
108
131
  const depth = /^[+-]?\d+$/.test(depthRaw) ? Math.max(0, Number.parseInt(depthRaw, 10)) : 5;
109
132
  const opts = { content: (input.content ?? '1') === '1', depth, allProfiles: allProfilesEnabled(ctx, input.all_profiles) };
110
133
  // TAL-308: any sidebar filter answers from the sidebar's own rows; without one the store-only search stays.
111
134
  if (SIDEBAR_SEARCH_KEYS.some((k) => input[k] !== undefined)) {
112
- return ctx.deps.sessions.sidebarSearch(input.q ?? '', sidebarListParams(ctx, input), { ...opts, projectId: input.project_id?.trim() || null });
135
+ return await ctx.deps.sessions.sidebarSearch(input.q ?? '', (await sidebarListParams(ctx, input)), { ...opts, projectId: input.project_id?.trim() || null });
113
136
  }
114
- return ctx.deps.sessions.search(input.q ?? '', opts);
137
+ return await ctx.deps.sessions.search(input.q ?? '', opts);
115
138
  })),
116
- cleanupZeroMessage: os.sessions.cleanupZeroMessage.handler(({ context: { ctx } }) => run(() => ctx.deps.sessions.cleanup(true))),
139
+ cleanup: os.sessions.cleanup.handler(({ context: { ctx } }) => run(async () => (await ctx.deps.sessions.cleanup(false)))),
140
+ cleanupZeroMessage: os.sessions.cleanupZeroMessage.handler(({ context: { ctx } }) => run(async () => (await ctx.deps.sessions.cleanup(true)))),
117
141
  // TAL-627: each id runs the single-session route's own guards, lock and cleanup, one at a time; a failure becomes
118
142
  // that id's result and the rest still run.
119
143
  bulk: os.sessions.bulk.handler(async ({ input, context: { ctx } }) => {
120
144
  const results = [];
121
145
  for (const sid of input.session_ids) {
122
146
  try {
123
- guardVisibility(ctx, sid);
147
+ await guardVisibility(ctx, sid);
124
148
  if (input.action === 'delete') {
125
149
  const { state_db_cleanup_failed } = await ctx.deps.sessions.delete(sid);
126
150
  results.push({ session_id: sid, ok: true, state_db_cleanup_failed: Boolean(state_db_cleanup_failed) });
@@ -135,7 +159,8 @@ export const sessionsRouter = os.router({
135
159
  : error instanceof HttpFailure ? [error.status, error.message]
136
160
  : error instanceof SessionNotFound ? [404, 'Session not found']
137
161
  : [500, sanitizeError(error)];
138
- results.push({ session_id: sid, ok: false, status, error: message });
162
+ const refused = error instanceof HttpFailure ? error.extra.refused : undefined;
163
+ results.push({ session_id: sid, ok: false, status, error: message, ...(refused ? { refused } : {}) });
139
164
  }
140
165
  }
141
166
  return { results };
@@ -145,35 +170,39 @@ export const sessionsRouter = os.router({
145
170
  // No visibility guard here: `detail()` answers 409 `session_profile_mismatch` so the frontend can switch to the owning profile.
146
171
  get: os.session.get.handler(({ input, context: { ctx } }) => run(async () => {
147
172
  await ctx.deps.sessions.deps.warmModelOptions?.();
173
+ await ctx.deps.turns.loadGoal(input.session_id.trim());
148
174
  try {
149
- return { session: ctx.deps.sessions.detail(input.session_id, input) };
175
+ return { session: (await ctx.deps.sessions.detail(input.session_id, input)) };
150
176
  }
151
177
  catch (error) {
152
178
  // Runs left on the relay by a deletion that predates `clearRelayCompletions` clear when the id is next requested.
153
179
  const sid = input.session_id.trim();
154
- if (error instanceof HttpFailure && error.status === 404 && ctx.deps.sessionStore.wasDeleted(sid))
155
- ctx.deps.relay.clearDeleted(sid, ctx.deps.activeProfile());
180
+ if (error instanceof HttpFailure && error.status === 404 && (await ctx.deps.sessionStore.wasDeleted(sid)))
181
+ (await ctx.deps.relay.clearDeleted(sid, ctx.deps.activeProfile()));
156
182
  throw error;
157
183
  }
158
184
  })),
159
- status: os.session.status.handler(({ input, context: { ctx } }) => run(() => {
185
+ status: os.session.status.handler(({ input, context: { ctx } }) => run(async () => {
160
186
  if (!input.session_id)
161
187
  throw new HttpError(400, 'Missing session_id');
162
- guardVisibility(ctx, input.session_id);
163
- return ctx.deps.sessions.status(input.session_id);
188
+ await guardVisibility(ctx, input.session_id);
189
+ return (await ctx.deps.sessions.status(input.session_id));
164
190
  })),
165
- usage: os.session.usage.handler(({ input, context: { ctx } }) => run(() => {
191
+ usage: os.session.usage.handler(({ input, context: { ctx } }) => run(async () => {
166
192
  if (!input.session_id)
167
193
  throw new HttpError(400, 'Missing session_id');
168
- guardVisibility(ctx, input.session_id);
169
- return ctx.deps.sessions.usage(input.session_id);
194
+ await guardVisibility(ctx, input.session_id);
195
+ return (await ctx.deps.sessions.usage(input.session_id));
170
196
  })),
171
197
  new: os.session.new.handler(({ input, context: { ctx } }) => run(async () => {
172
198
  // Python: an invisible prev_session_id is ignored (the service drops it), never an error (#5420).
173
199
  const profile = input.profile || null;
174
200
  // The middleware warms only the cookie profile's config; new-session defaults read the target's synchronously.
175
- if (profile)
176
- await ctx.deps.agentConfig.read(ctx.deps.profileHome(profile)).catch(() => undefined);
201
+ // `profileHome` refuses a profile whose home escapes the profiles root (400) before any warm-up failure is swallowed.
202
+ if (profile) {
203
+ const home = ctx.deps.profileHome(profile);
204
+ await ctx.deps.agentConfig.read(home).catch(() => undefined);
205
+ }
177
206
  let worktree = null;
178
207
  let worktreeSkipped = null;
179
208
  const explicit = 'worktree' in input;
@@ -186,8 +215,8 @@ export const sessionsRouter = os.router({
186
215
  }
187
216
  if (requested) {
188
217
  try {
189
- let base = input.workspace ? ctx.deps.sessions.resolveNewSessionWorkspace(input, null, profile) : null;
190
- base ??= ctx.deps.workspaces.resolveTrusted(ctx.deps.workspaces.lastWorkspace(profile), profile);
218
+ let base = input.workspace ? (await ctx.deps.sessions.resolveNewSessionWorkspace(input, null, profile)) : null;
219
+ base ??= (await ctx.deps.workspaces.resolveTrusted((await ctx.deps.workspaces.lastWorkspace(profile)), profile));
191
220
  const repo = base;
192
221
  worktree = await ctx.deps.git.holdWrite(repo, () => ctx.deps.worktrees.create(repo));
193
222
  }
@@ -205,67 +234,69 @@ export const sessionsRouter = os.router({
205
234
  worktreeSkipped = error.message;
206
235
  }
207
236
  }
208
- const s = ctx.deps.sessions.create(input, { worktree });
209
- const payload = { session: ctx.deps.sessions.publicSession(s) };
237
+ const s = (await ctx.deps.sessions.create(input, { worktree }));
238
+ const payload = { session: (await ctx.deps.sessions.publicSession(s)) };
210
239
  if (worktreeSkipped)
211
240
  payload.worktree_skipped = worktreeSkipped;
212
241
  return payload;
213
242
  })),
214
243
  rename: os.session.rename.handler(({ input, context: { ctx } }) => run(async () => {
215
- guardVisibility(ctx, input.session_id);
244
+ await guardVisibility(ctx, input.session_id);
216
245
  if (!input.title)
217
246
  throw new HttpError(400, 'Missing required field(s): title');
218
- return ctx.deps.sessions.rename(input.session_id, input.title);
247
+ return (await ctx.deps.sessions.rename(input.session_id, input.title));
219
248
  })),
220
249
  delete: os.session.delete.handler(({ input, context: { ctx } }) => run(async () => {
221
- guardVisibility(ctx, input.session_id);
222
- return ctx.deps.sessions.delete(input.session_id);
250
+ await guardVisibility(ctx, input.session_id);
251
+ return (await ctx.deps.sessions.delete(input.session_id));
223
252
  })),
224
253
  pin: os.session.pin.handler(({ input, context: { ctx } }) => run(async () => {
225
- guardVisibility(ctx, input.session_id);
254
+ await guardVisibility(ctx, input.session_id);
226
255
  return ctx.deps.sessions.pin(input.session_id, input.pinned ?? true);
227
256
  })),
228
257
  archive: os.session.archive.handler(({ input, context: { ctx } }) => run(async () => {
229
- guardVisibility(ctx, input.session_id);
230
- return ctx.deps.sessions.archive(input.session_id, input.archived ?? true);
258
+ await guardVisibility(ctx, input.session_id);
259
+ return (await ctx.deps.sessions.archive(input.session_id, input.archived ?? true));
231
260
  })),
232
261
  move: os.session.move.handler(({ input, context: { ctx } }) => run(async () => {
233
- guardVisibility(ctx, input.session_id);
234
- return ctx.deps.sessions.move(input.session_id, input.project_id || null);
262
+ await guardVisibility(ctx, input.session_id);
263
+ return (await ctx.deps.sessions.move(input.session_id, input.project_id || null));
235
264
  })),
236
- duplicate: os.session.duplicate.handler(({ input, context: { ctx } }) => run(() => {
265
+ duplicate: os.session.duplicate.handler(({ input, context: { ctx } }) => run(async () => {
237
266
  if (!input.session_id)
238
267
  throw new HttpError(400, 'session_id is required');
239
- guardVisibility(ctx, input.session_id);
240
- return ctx.deps.sessions.duplicate(input.session_id);
268
+ await guardVisibility(ctx, input.session_id);
269
+ return (await ctx.deps.sessions.duplicate(input.session_id));
241
270
  })),
242
- branch: os.session.branch.handler(({ input, context: { ctx } }) => run(() => {
243
- guardVisibility(ctx, input.session_id);
244
- return ctx.deps.sessions.branch(input.session_id, input);
271
+ branch: os.session.branch.handler(({ input, context: { ctx } }) => run(async () => {
272
+ await guardVisibility(ctx, input.session_id);
273
+ return (await ctx.deps.sessions.branch(input.session_id, input));
245
274
  })),
246
275
  truncate: os.session.truncate.handler(({ input, context: { ctx } }) => run(async () => {
247
- guardVisibility(ctx, input.session_id);
248
- return ctx.deps.sessions.truncate(input.session_id, input.keep_count);
276
+ await guardVisibility(ctx, input.session_id);
277
+ // A body without `confirm_deep_truncate` is a released App's, which asked on the device before sending it.
278
+ const hold = input.confirm_deep_truncate === undefined ? null : { confirmToken: input.confirm_deep_truncate ? input.confirm_token ?? null : null };
279
+ return (await ctx.deps.sessions.truncate(input.session_id, input.keep_count, hold));
249
280
  })),
250
281
  clear: os.session.clear.handler(({ input, context: { ctx } }) => run(async () => {
251
- guardVisibility(ctx, input.session_id);
252
- return ctx.deps.sessions.clear(input.session_id);
282
+ await guardVisibility(ctx, input.session_id);
283
+ return (await ctx.deps.sessions.clear(input.session_id));
253
284
  })),
254
285
  retry: os.session.retry.handler(({ input, context: { ctx } }) => run(async () => {
255
- guardVisibility(ctx, input.session_id);
256
- return ctx.deps.sessions.retry(input.session_id);
286
+ await guardVisibility(ctx, input.session_id);
287
+ return (await ctx.deps.sessions.retry(input.session_id));
257
288
  })),
258
289
  undo: os.session.undo.handler(({ input, context: { ctx } }) => run(async () => {
259
- guardVisibility(ctx, input.session_id);
260
- return ctx.deps.sessions.undo(input.session_id);
290
+ await guardVisibility(ctx, input.session_id);
291
+ return (await ctx.deps.sessions.undo(input.session_id));
261
292
  })),
262
293
  update: os.session.update.handler(({ input, context: { ctx } }) => run(async () => {
263
- guardVisibility(ctx, input.session_id);
264
- return ctx.deps.sessions.update(input.session_id, input);
294
+ await guardVisibility(ctx, input.session_id);
295
+ return (await ctx.deps.sessions.update(input.session_id, input));
265
296
  })),
266
297
  toolsets: os.session.toolsets.handler(({ input, context: { ctx } }) => run(async () => {
267
- guardVisibility(ctx, input.session_id);
268
- return ctx.deps.sessions.setToolsets(input.session_id, input.toolsets);
298
+ await guardVisibility(ctx, input.session_id);
299
+ return (await ctx.deps.sessions.setToolsets(input.session_id, input.toolsets));
269
300
  })),
270
301
  yoloGet: os.session.yoloGet.handler(({ input, context: { ctx } }) => run(() => ctx.deps.sessions.yolo(input.session_id))),
271
302
  yoloSet: os.session.yoloSet.handler(({ input, context: { ctx } }) => run(async () => {
@@ -285,29 +316,52 @@ export const sessionsRouter = os.router({
285
316
  const released = await ctx.deps.turns.respondApproval(sid, 'once', '', true);
286
317
  return { ok: true, yolo_enabled: Boolean(released.yolo_enabled ?? ctx.deps.sessions.yolo(sid).yolo_enabled), ...(released.stale_cleared ? { stale_cleared: true } : {}) };
287
318
  })),
288
- import: os.session.import.handler(({ input, context: { ctx } }) => run(() => ctx.deps.sessions.import(input))),
319
+ import: os.session.import.handler(({ input, context: { ctx } }) => run(async () => (await ctx.deps.sessions.import(input)))),
289
320
  regenerateTitle: os.session.regenerateTitle.handler(({ input, context: { ctx } }) => run(async () => {
290
321
  const sid = input.session_id;
291
322
  let session;
292
323
  try {
293
- session = ctx.deps.sessionStore.get(sid);
324
+ session = (await ctx.deps.sessionStore.get(sid));
294
325
  }
295
326
  catch {
296
327
  throw new HttpError(404, 'Session not found');
297
328
  }
298
- if (ctx.deps.sessions.isReadOnly(session) || ctx.deps.sessions.isSubagentViewOnly(sid))
329
+ if ((await ctx.deps.sessions.isReadOnly(session)) || (await ctx.deps.sessions.isSubagentViewOnly(sid)))
299
330
  throw new HttpError(403, 'Read-only imported sessions cannot regenerate titles');
300
331
  const generated = await ctx.deps.turns.generateTitle(session, { preferLatest: Boolean(input.prefer_latest) });
301
332
  if (!generated.title)
302
333
  throw new HttpError(422, `Could not generate a better title (${generated.status || 'empty'})`);
303
334
  const current = await ctx.deps.sessions.persistGeneratedTitle(sid, generated.title, 'session_title_regenerate');
304
- return { session: ctx.deps.sessions.wireRow(current), title: current.title, status: generated.status, raw_preview: generated.rawPreview.slice(0, 240) };
335
+ return { session: (await ctx.deps.sessions.wireRow(current)), title: current.title, status: generated.status, raw_preview: generated.rawPreview.slice(0, 240) };
336
+ })),
337
+ recoveryAudit: os.session.recoveryAudit.handler(({ context: { ctx } }) => run(async () => (await ctx.deps.sessions.recoveryAudit()))),
338
+ recoveryRepairSafe: os.session.recoveryRepairSafe.handler(({ context: { ctx } }) => run(async () => {
339
+ const result = await ctx.deps.sessions.recoveryRepairSafe();
340
+ if (!result.clean)
341
+ throw new RawResponse(409, result);
342
+ return result;
343
+ })),
344
+ lineageReport: os.session.lineageReport.handler(({ input, context: { ctx } }) => run(() => {
345
+ const sid = input.session_id ?? '';
346
+ if (!sid)
347
+ throw new HttpError(400, 'session_id required');
348
+ const report = ctx.deps.sessions.lineageReport(sid);
349
+ if (!report.found)
350
+ throw new HttpError(404, 'Session not found');
351
+ return report;
352
+ })),
353
+ compressionRecoveryStart: os.session.compressionRecoveryStart.handler(({ input, context: { ctx } }) => run(async () => {
354
+ requireFields(input, 'session_id');
355
+ const sid = str(input.session_id).trim();
356
+ if (!sid)
357
+ throw new HttpError(400, 'session_id is required');
358
+ return (await ctx.deps.sessions.compressionRecoveryStart(sid));
305
359
  })),
306
360
  // TAL-255: the iOS `/compress` route joins or starts the session's compression job and answers with its result.
307
361
  compress: os.session.compress.handler(({ input, context: { ctx } }) => run(async () => {
308
362
  requireFields(input, 'session_id');
309
363
  const sid = str(input.session_id).trim();
310
- guardVisibility(ctx, sid);
364
+ await guardVisibility(ctx, sid);
311
365
  const job = await ctx.deps.sessions.startCompression(sid, input.focus_topic || input.topic);
312
366
  await job.done;
313
367
  if (job.status === 'error')
@@ -317,47 +371,56 @@ export const sessionsRouter = os.router({
317
371
  compressStart: os.session.compressStart.handler(({ input, context: { ctx } }) => run(async () => {
318
372
  requireFields(input, 'session_id');
319
373
  const sid = str(input.session_id).trim();
320
- guardVisibility(ctx, sid);
374
+ await guardVisibility(ctx, sid);
321
375
  return compressionStatusPayload(await ctx.deps.sessions.startCompression(sid, input.focus_topic || input.topic));
322
376
  })),
323
- compressStatus: os.session.compressStatus.handler(({ input, context: { ctx } }) => run(() => {
377
+ // TAL-258: the handoff dock. Rounds decides when to offer it; the summary is generated and appended to the transcript.
378
+ conversationRounds: os.session.conversationRounds.handler(({ input, context: { ctx } }) => run(async () => {
379
+ const { sid, since } = (await handoffInput(ctx, input));
380
+ return (await ctx.deps.sessions.conversationRounds(sid, since));
381
+ })),
382
+ handoffSummary: os.session.handoffSummary.handler(({ input, context: { ctx } }) => run(async () => {
383
+ const { sid, since } = (await handoffInput(ctx, input));
384
+ return (await ctx.deps.sessions.handoffSummary(sid, since));
385
+ })),
386
+ compressStatus: os.session.compressStatus.handler(({ input, context: { ctx } }) => run(async () => {
324
387
  const sid = input.session_id.trim();
325
388
  if (!sid)
326
389
  throw new HttpError(400, 'session_id is required');
327
- guardVisibility(ctx, sid);
328
- const job = ctx.deps.sessions.compressionJob(sid);
390
+ await guardVisibility(ctx, sid);
391
+ const job = (await ctx.deps.sessions.compressionJob(sid));
329
392
  return (job ? compressionStatusPayload(job) : { ok: true, status: 'idle', session_id: sid });
330
393
  })),
331
- draftGet: os.session.draftGet.handler(({ input, context: { ctx } }) => run(() => {
332
- guardVisibility(ctx, input.session_id);
333
- return ctx.deps.sessions.readDraft(input.session_id);
394
+ draftGet: os.session.draftGet.handler(({ input, context: { ctx } }) => run(async () => {
395
+ await guardVisibility(ctx, input.session_id);
396
+ return (await ctx.deps.sessions.readDraft(input.session_id));
334
397
  })),
335
398
  draftSave: os.session.draftSave.handler(({ input, context: { ctx } }) => run(async () => {
336
- guardVisibility(ctx, input.session_id);
337
- return ctx.deps.sessions.writeDraft(input);
399
+ await guardVisibility(ctx, input.session_id);
400
+ return (await ctx.deps.sessions.writeDraft(input));
338
401
  })),
339
- anchorSceneGet: os.session.anchorSceneGet.handler(({ input, context: { ctx } }) => run(() => {
340
- guardVisibility(ctx, input.session_id);
341
- return ctx.deps.sessions.readAnchorScene(input);
402
+ anchorSceneGet: os.session.anchorSceneGet.handler(({ input, context: { ctx } }) => run(async () => {
403
+ await guardVisibility(ctx, input.session_id);
404
+ return (await ctx.deps.sessions.readAnchorScene(input));
342
405
  })),
343
- toolResult: os.session.toolResult.handler(({ input, context: { ctx } }) => run(() => {
344
- guardVisibility(ctx, input.session_id);
345
- return ctx.deps.sessions.readToolResult(input);
406
+ toolResult: os.session.toolResult.handler(({ input, context: { ctx } }) => run(async () => {
407
+ await guardVisibility(ctx, input.session_id);
408
+ return (await ctx.deps.sessions.readToolResult(input));
346
409
  })),
347
410
  anchorSceneSave: os.session.anchorSceneSave.handler(({ input, context: { ctx } }) => run(async () => {
348
- guardVisibility(ctx, input.session_id);
349
- return ctx.deps.sessions.saveAnchorScene(input);
411
+ await guardVisibility(ctx, input.session_id);
412
+ return (await ctx.deps.sessions.saveAnchorScene(input));
350
413
  })),
351
414
  },
352
415
  projects: {
353
- list: os.projects.list.handler(({ input, context: { ctx } }) => run(() => {
416
+ list: os.projects.list.handler(({ input, context: { ctx } }) => run(async () => {
354
417
  const activeProfile = ctx.deps.activeProfile();
355
- const all = ctx.deps.projects.load();
418
+ const all = (await ctx.deps.projects.load());
356
419
  const allProfiles = allProfilesEnabled(ctx, input.all_profiles);
357
420
  const scoped = allProfiles ? all : all.filter((p) => ctx.deps.profilesMatch(p.profile ?? null, activeProfile));
358
421
  return { projects: scoped, all_profiles: allProfiles, active_profile: activeProfile, other_profile_count: allProfiles || ctx.deps.isolatedProfileMode() ? 0 : all.length - scoped.length };
359
422
  })),
360
- create: os.projects.create.handler(({ input, context: { ctx } }) => run(() => {
423
+ create: os.projects.create.handler(({ input, context: { ctx } }) => run(async () => {
361
424
  const name = input.name.trim().slice(0, 128);
362
425
  if (!name)
363
426
  throw new HttpError(400, 'name required');
@@ -367,43 +430,43 @@ export const sessionsRouter = os.router({
367
430
  const requested = (input.profile ?? '').trim();
368
431
  if (requested && requested !== 'default' && !/^[a-z0-9][a-z0-9_-]{0,63}$/.test(requested))
369
432
  throw new HttpError(400, 'invalid profile');
370
- const projects = ctx.deps.projects.load();
371
433
  const proj = { project_id: randomUUID().replace(/-/g, '').slice(0, 12), name, color, profile: requested || ctx.deps.activeProfile() || 'default', created_at: ctx.deps.auth.now() };
372
- projects.push(proj);
373
- ctx.deps.projects.save(projects);
434
+ await ctx.deps.projects.mutate((projects) => { projects.push(proj); });
374
435
  ctx.deps.events.publish('project_create', { profile: proj.profile });
375
436
  return { ok: true, project: proj };
376
437
  })),
377
- rename: os.projects.rename.handler(({ input, context: { ctx } }) => run(() => {
378
- const projects = ctx.deps.projects.load();
379
- const proj = projects.find((p) => p.project_id === input.project_id);
438
+ rename: os.projects.rename.handler(({ input, context: { ctx } }) => run(async () => {
380
439
  const activeProfile = ctx.deps.activeProfile();
381
- if (!proj || !ctx.deps.profilesMatch(proj.profile ?? null, activeProfile))
382
- throw new HttpError(404, 'Project not found');
383
- requireFields(input, 'project_id', 'name');
384
- proj.name = input.name.trim().slice(0, 128);
385
- if ('color' in input) {
386
- const color = input.color ?? null;
387
- if (color && !/^#[0-9a-fA-F]{3,8}$/.test(color))
388
- throw new HttpError(400, 'Invalid color format');
389
- proj.color = color;
390
- }
391
- ctx.deps.projects.save(projects);
440
+ const proj = await ctx.deps.projects.mutate((projects) => {
441
+ const proj = projects.find((p) => p.project_id === input.project_id);
442
+ if (!proj || !ctx.deps.profilesMatch(proj.profile ?? null, activeProfile))
443
+ throw new HttpError(404, 'Project not found');
444
+ requireFields(input, 'project_id', 'name');
445
+ proj.name = input.name.trim().slice(0, 128);
446
+ if ('color' in input) {
447
+ const color = input.color ?? null;
448
+ if (color && !/^#[0-9a-fA-F]{3,8}$/.test(color))
449
+ throw new HttpError(400, 'Invalid color format');
450
+ proj.color = color;
451
+ }
452
+ return proj;
453
+ });
392
454
  ctx.deps.events.publish('project_rename', { profile: activeProfile });
393
455
  return { ok: true, project: proj };
394
456
  })),
395
- delete: os.projects.delete.handler(({ input, context: { ctx } }) => run(() => {
396
- let projects = ctx.deps.projects.load();
397
- const proj = projects.find((p) => p.project_id === input.project_id);
457
+ delete: os.projects.delete.handler(({ input, context: { ctx } }) => run(async () => {
398
458
  const activeProfile = ctx.deps.activeProfile();
399
- if (!proj || !ctx.deps.profilesMatch(proj.profile ?? null, activeProfile))
400
- throw new HttpError(404, 'Project not found');
401
- projects = projects.filter((p) => p.project_id !== input.project_id);
402
- ctx.deps.projects.save(projects);
459
+ await ctx.deps.projects.mutate((projects) => {
460
+ const position = projects.findIndex((p) => p.project_id === input.project_id);
461
+ const proj = projects[position];
462
+ if (!proj || !ctx.deps.profilesMatch(proj.profile ?? null, activeProfile))
463
+ throw new HttpError(404, 'Project not found');
464
+ projects.splice(position, 1);
465
+ });
403
466
  const store = ctx.deps.sessionStore;
404
467
  const activeIds = ctx.deps.sessions.deps.runtime.activeStreamIds;
405
468
  try {
406
- for (const entry of store.readIndexEntries()) {
469
+ for (const entry of (await store.readIndexEntries())) {
407
470
  if (entry.project_id !== input.project_id)
408
471
  continue;
409
472
  const sid = str(entry.session_id);
@@ -415,9 +478,9 @@ export const sessionsRouter = os.router({
415
478
  continue;
416
479
  }
417
480
  }
418
- const s = store.get(sid);
481
+ const s = (await store.get(sid));
419
482
  s.project_id = null;
420
- store.save(s);
483
+ await store.save(s);
421
484
  }
422
485
  catch { /* one slow or failing session never aborts the request */ }
423
486
  }
@@ -442,36 +505,36 @@ export const sessionsRouter = os.router({
442
505
  const bound = str(session?.bound_profile).trim() || null;
443
506
  return relayCall(() => Promise.resolve(ctx.deps.relay.presence.update(input, ctx.deps.isRootProfile(bound ?? ctx.deps.activeProfile()) ? 'default' : (bound ?? ctx.deps.activeProfile()))));
444
507
  }),
445
- viewed: os.talaria.viewed.handler(({ input, context: { ctx } }) => run(() => {
446
- ctx.deps.relay.markViewed(input.session_id.trim());
508
+ viewed: os.talaria.viewed.handler(({ input, context: { ctx } }) => run(async () => {
509
+ await ctx.deps.relay.markViewed(input.session_id.trim());
447
510
  return { ok: true };
448
511
  })),
449
512
  },
450
513
  share: {
451
- create: os.share.create.handler(({ input, context: { ctx } }) => run(() => {
514
+ create: os.share.create.handler(({ input, context: { ctx } }) => run(async () => {
452
515
  const sid = input.session_id.trim();
453
516
  if (!sid)
454
517
  throw new HttpError(400, 'session_id is required');
455
- return ctx.deps.sessions.createShare(sid);
518
+ return (await ctx.deps.sessions.createShare(sid));
456
519
  })),
457
- revoke: os.share.revoke.handler(({ input, context: { ctx } }) => run(() => {
520
+ revoke: os.share.revoke.handler(({ input, context: { ctx } }) => run(async () => {
458
521
  const sid = input.session_id.trim();
459
522
  if (!sid)
460
523
  throw new HttpError(400, 'session_id is required');
461
- return ctx.deps.sessions.revokeShare(sid);
524
+ return (await ctx.deps.sessions.revokeShare(sid));
462
525
  })),
463
- read: os.share.read.handler(({ input, context: { ctx } }) => run(() => {
526
+ read: os.share.read.handler(({ input, context: { ctx } }) => run(async () => {
464
527
  ctx.extraResponseHeaders = { 'x-robots-tag': 'noindex, nofollow' };
465
- return ctx.deps.sessions.loadShare(input.token.trim());
528
+ return (await ctx.deps.sessions.loadShare(input.token.trim()));
466
529
  })),
467
530
  },
468
531
  workspaces: {
469
- list: os.workspaces.list.handler(({ context: { ctx } }) => run(() => {
532
+ list: os.workspaces.list.handler(({ context: { ctx } }) => run(async () => {
470
533
  const profile = ctx.deps.activeProfile();
471
- return { workspaces: ctx.deps.workspaces.load(profile), last: ctx.deps.workspaces.lastWorkspace(profile), terminal_remote_backend: ctx.deps.features().terminal_remote_backend };
534
+ return { workspaces: (await ctx.deps.workspaces.load(profile)), last: (await ctx.deps.workspaces.lastWorkspace(profile)), terminal_remote_backend: ctx.deps.features().terminal_remote_backend };
472
535
  })),
473
- suggest: os.workspaces.suggest.handler(({ input, context: { ctx } }) => run(() => ({ suggestions: ctx.deps.workspaces.suggest(input.prefix ?? '', 12, ctx.deps.activeProfile()), prefix: input.prefix ?? '' }))),
474
- add: os.workspaces.add.handler(({ input, context: { ctx } }) => run(() => {
536
+ suggest: os.workspaces.suggest.handler(({ input, context: { ctx } }) => run(async () => ({ suggestions: (await ctx.deps.workspaces.suggest(input.prefix ?? '', 12, ctx.deps.activeProfile())), prefix: input.prefix ?? '' }))),
537
+ add: os.workspaces.add.handler(({ input, context: { ctx } }) => run(async () => {
475
538
  const ws = ctx.deps.workspaces;
476
539
  const pathStr = stripSurroundingQuotes(input.path.trim());
477
540
  const name = (input.name ?? '').trim();
@@ -510,42 +573,42 @@ export const sessionsRouter = os.router({
510
573
  catch (error) {
511
574
  throw new HttpError(400, error.message);
512
575
  }
513
- const list = ws.load(profile);
576
+ const list = (await ws.load(profile));
514
577
  if (list.some((w) => w.path === p))
515
578
  throw new HttpError(400, 'Workspace already in list');
516
579
  list.push({ path: p, name: name || basename(p) });
517
- ws.save(list, profile);
580
+ await ws.save(list, profile);
518
581
  return { ok: true, workspaces: list };
519
582
  })),
520
- remove: os.workspaces.remove.handler(({ input, context: { ctx } }) => run(() => {
583
+ remove: os.workspaces.remove.handler(({ input, context: { ctx } }) => run(async () => {
521
584
  const pathStr = input.path.trim();
522
585
  if (!pathStr)
523
586
  throw new HttpError(400, 'path is required');
524
587
  const profile = ctx.deps.activeProfile();
525
- const list = ctx.deps.workspaces.load(profile).filter((w) => w.path !== pathStr);
526
- ctx.deps.workspaces.save(list, profile);
588
+ const list = (await ctx.deps.workspaces.load(profile)).filter((w) => w.path !== pathStr);
589
+ await ctx.deps.workspaces.save(list, profile);
527
590
  return { ok: true, workspaces: list };
528
591
  })),
529
- rename: os.workspaces.rename.handler(({ input, context: { ctx } }) => run(() => {
592
+ rename: os.workspaces.rename.handler(({ input, context: { ctx } }) => run(async () => {
530
593
  const pathStr = input.path.trim();
531
594
  const name = input.name.trim();
532
595
  if (!pathStr || !name)
533
596
  throw new HttpError(400, 'path and name are required');
534
597
  const profile = ctx.deps.activeProfile();
535
- const list = ctx.deps.workspaces.load(profile);
598
+ const list = (await ctx.deps.workspaces.load(profile));
536
599
  const entry = list.find((w) => w.path === pathStr);
537
600
  if (!entry)
538
601
  throw new HttpError(404, 'Workspace not found');
539
602
  entry.name = name;
540
- ctx.deps.workspaces.save(list, profile);
603
+ await ctx.deps.workspaces.save(list, profile);
541
604
  return { ok: true, workspaces: list };
542
605
  })),
543
- reorder: os.workspaces.reorder.handler(({ input, context: { ctx } }) => run(() => {
606
+ reorder: os.workspaces.reorder.handler(({ input, context: { ctx } }) => run(async () => {
544
607
  const paths = input.paths;
545
608
  if (!Array.isArray(paths) || !paths.length)
546
609
  throw new HttpError(400, 'paths is required and must be a list');
547
610
  const profile = ctx.deps.activeProfile();
548
- const list = ctx.deps.workspaces.load(profile);
611
+ const list = (await ctx.deps.workspaces.load(profile));
549
612
  const byPath = new Map(list.map((w) => [w.path, w]));
550
613
  const reordered = [];
551
614
  const seen = new Set();
@@ -560,18 +623,18 @@ export const sessionsRouter = os.router({
560
623
  for (const w of list)
561
624
  if (!seen.has(w.path))
562
625
  reordered.push(w);
563
- ctx.deps.workspaces.save(reordered, profile);
626
+ await ctx.deps.workspaces.save(reordered, profile);
564
627
  return { ok: true, workspaces: reordered };
565
628
  })),
566
629
  },
567
630
  files: {
568
- list: os.files.list.handler(({ input, context: { ctx } }) => run(() => {
631
+ list: os.files.list.handler(({ input, context: { ctx } }) => run(async () => {
569
632
  if (!input.session_id)
570
633
  throw new HttpError(400, 'session_id is required');
571
- guardVisibility(ctx, input.session_id);
634
+ await guardVisibility(ctx, input.session_id);
572
635
  // Python `_handle_list` resolved the workspace directly and answered 404 with the trust error; the POST file ops
573
636
  // keep `get_session_for_file_ops`' lenient fallback.
574
- const s = fileOpsSession(ctx, input.session_id, { strictWorkspace: true });
637
+ const s = (await fileOpsSession(ctx, input.session_id, { strictWorkspace: true }));
575
638
  const rel = input.path ?? '.';
576
639
  try {
577
640
  const entries = listDir(s.workspace, rel);
@@ -581,11 +644,11 @@ export const sessionsRouter = os.router({
581
644
  throw fileError(error, 404);
582
645
  }
583
646
  })),
584
- read: os.files.read.handler(({ input, context: { ctx } }) => run(() => {
647
+ read: os.files.read.handler(({ input, context: { ctx } }) => run(async () => {
585
648
  if (!input.session_id)
586
649
  throw new HttpError(400, 'session_id is required');
587
- guardVisibility(ctx, input.session_id);
588
- const s = fileOpsSession(ctx, input.session_id);
650
+ await guardVisibility(ctx, input.session_id);
651
+ const s = (await fileOpsSession(ctx, input.session_id));
589
652
  if (!input.path)
590
653
  throw new HttpError(400, 'path is required');
591
654
  try {
@@ -596,9 +659,9 @@ export const sessionsRouter = os.router({
596
659
  throw fileError(error, 404);
597
660
  }
598
661
  })),
599
- save: os.files.save.handler(({ input, context: { ctx } }) => run(() => {
662
+ save: os.files.save.handler(({ input, context: { ctx } }) => run(async () => {
600
663
  requireFields(input, 'session_id', 'path');
601
- const s = fileOpsSession(ctx, input.session_id);
664
+ const s = (await fileOpsSession(ctx, input.session_id));
602
665
  try {
603
666
  if (['.docx', '.xlsx', '.pptx'].includes(extname(input.path).toLowerCase()))
604
667
  throw new HttpError(400, 'Office documents cannot be saved from the Web UI');
@@ -625,9 +688,9 @@ export const sessionsRouter = os.router({
625
688
  throw fileError(error);
626
689
  }
627
690
  })),
628
- create: os.files.create.handler(({ input, context: { ctx } }) => run(() => {
691
+ create: os.files.create.handler(({ input, context: { ctx } }) => run(async () => {
629
692
  requireFields(input, 'session_id', 'path');
630
- const s = fileOpsSession(ctx, input.session_id);
693
+ const s = (await fileOpsSession(ctx, input.session_id));
631
694
  try {
632
695
  const root = s.workspace;
633
696
  assertWorkspaceFree(ctx, root);
@@ -650,9 +713,9 @@ export const sessionsRouter = os.router({
650
713
  throw fileError(error);
651
714
  }
652
715
  })),
653
- createDir: os.files.createDir.handler(({ input, context: { ctx } }) => run(() => {
716
+ createDir: os.files.createDir.handler(({ input, context: { ctx } }) => run(async () => {
654
717
  requireFields(input, 'session_id', 'path');
655
- const s = fileOpsSession(ctx, input.session_id);
718
+ const s = (await fileOpsSession(ctx, input.session_id));
656
719
  try {
657
720
  const root = s.workspace;
658
721
  assertWorkspaceFree(ctx, root);
@@ -666,9 +729,9 @@ export const sessionsRouter = os.router({
666
729
  throw fileError(error);
667
730
  }
668
731
  })),
669
- delete: os.files.delete.handler(({ input, context: { ctx } }) => run(() => {
732
+ delete: os.files.delete.handler(({ input, context: { ctx } }) => run(async () => {
670
733
  requireFields(input, 'session_id', 'path');
671
- const s = fileOpsSession(ctx, input.session_id);
734
+ const s = (await fileOpsSession(ctx, input.session_id));
672
735
  try {
673
736
  const root = s.workspace;
674
737
  assertWorkspaceFree(ctx, root);
@@ -690,9 +753,9 @@ export const sessionsRouter = os.router({
690
753
  throw fileError(error);
691
754
  }
692
755
  })),
693
- rename: os.files.rename.handler(({ input, context: { ctx } }) => run(() => {
756
+ rename: os.files.rename.handler(({ input, context: { ctx } }) => run(async () => {
694
757
  requireFields(input, 'session_id', 'path', 'new_name');
695
- const s = fileOpsSession(ctx, input.session_id);
758
+ const s = (await fileOpsSession(ctx, input.session_id));
696
759
  try {
697
760
  const root = s.workspace;
698
761
  assertWorkspaceFree(ctx, root);
@@ -717,10 +780,10 @@ export const sessionsRouter = os.router({
717
780
  throw fileError(error);
718
781
  }
719
782
  })),
720
- move: os.files.move.handler(({ input, context: { ctx } }) => run(() => {
783
+ move: os.files.move.handler(({ input, context: { ctx } }) => run(async () => {
721
784
  requireFields({ ...input, dest_dir: input.dest_dir ?? input.destination }, 'session_id', 'path', 'dest_dir');
722
785
  const destDirRaw = (input.dest_dir ?? input.destination ?? '.').trim() || '.';
723
- const s = fileOpsSession(ctx, input.session_id);
786
+ const s = (await fileOpsSession(ctx, input.session_id));
724
787
  try {
725
788
  const root = s.workspace;
726
789
  assertWorkspaceFree(ctx, root);
@@ -753,7 +816,7 @@ export const sessionsRouter = os.router({
753
816
  })),
754
817
  reveal: os.files.reveal.handler(({ input, context: { ctx } }) => run(async () => {
755
818
  requireFields(input, 'session_id', 'path');
756
- const s = fileOpsSession(ctx, input.session_id);
819
+ const s = (await fileOpsSession(ctx, input.session_id));
757
820
  try {
758
821
  const target = safeResolve(s.workspace, input.path);
759
822
  if (!existsSync(target))
@@ -772,9 +835,19 @@ export const sessionsRouter = os.router({
772
835
  throw fileError(error);
773
836
  }
774
837
  })),
838
+ path: os.files.path.handler(({ input, context: { ctx } }) => run(async () => {
839
+ requireFields(input, 'session_id', 'path');
840
+ const s = (await fileOpsSession(ctx, input.session_id));
841
+ try {
842
+ return { ok: true, path: safeResolve(s.workspace, input.path) };
843
+ }
844
+ catch (error) {
845
+ throw fileError(error);
846
+ }
847
+ })),
775
848
  openVsCode: os.files.openVsCode.handler(({ input, context: { ctx } }) => run(async () => {
776
849
  requireFields(input, 'session_id', 'path');
777
- const s = fileOpsSession(ctx, input.session_id);
850
+ const s = (await fileOpsSession(ctx, input.session_id));
778
851
  try {
779
852
  const target = safeResolve(s.workspace, input.path);
780
853
  if (!existsSync(target))
@@ -790,8 +863,65 @@ export const sessionsRouter = os.router({
790
863
  throw fileError(error);
791
864
  }
792
865
  })),
866
+ escapeAuthorize: os.files.escapeAuthorize.handler(({ input, context: { ctx } }) => run(async () => {
867
+ // A Referer alone is not a browser fetch: Python required `Origin`, which the global CSRF check has already matched.
868
+ if (!ctx.header('origin'))
869
+ throw new HttpError(403, 'browser origin required');
870
+ if (input.token?.trim())
871
+ throw new HttpError(400, 'token must not be provided');
872
+ const sid = (input.session_id ?? '').trim();
873
+ const rel = (input.path ?? '').trim();
874
+ if (!sid)
875
+ throw new HttpError(400, 'session_id is required');
876
+ if (!rel)
877
+ throw new HttpError(400, 'path is required');
878
+ const s = (await fileOpsSession(ctx, sid));
879
+ try {
880
+ return ctx.deps.escapeGrants.authorize(s.workspace, sid, s.profile, rel);
881
+ }
882
+ catch (error) {
883
+ throw fileError(error, 404);
884
+ }
885
+ })),
886
+ escapeList: os.files.escapeList.handler(({ input, context: { ctx } }) => run(async () => {
887
+ const req = (await escapeRequest(ctx, input.session_id, input.token, input.path ?? '.'));
888
+ try {
889
+ const listing = ctx.deps.escapeGrants.list(req);
890
+ return { ...listing, entries: serializeEntriesForBrowser(listing.entries) };
891
+ }
892
+ catch (error) {
893
+ throw fileError(error, 404);
894
+ }
895
+ })),
896
+ escapeRead: os.files.escapeRead.handler(({ input, context: { ctx } }) => run(async () => {
897
+ const req = (await escapeRequest(ctx, input.session_id, input.token, input.path ?? ''));
898
+ try {
899
+ const file = ctx.deps.escapeGrants.read(req);
900
+ // The frame loads the bytes through the same grant; the workspace preview grant would outlive and skip its re-checks.
901
+ return file.preview === 'html' ? { ...file, preview_url: `api/escape/file/raw?${new URLSearchParams({ session_id: String(input.session_id), token: String(input.token), path: req.requestPath, inline: '1' }).toString()}` } : file;
902
+ }
903
+ catch (error) {
904
+ throw fileError(error, 404);
905
+ }
906
+ })),
793
907
  },
794
908
  });
909
+ /** The escape routes' shared front half: required ids, the session, and the grant re-check (403 when it fails). */
910
+ export async function escapeRequest(ctx, sid, token, rel) {
911
+ if (!sid)
912
+ throw new HttpError(400, 'session_id is required');
913
+ if (!token)
914
+ throw new HttpError(400, 'token is required');
915
+ const s = (await fileOpsSession(ctx, sid));
916
+ try {
917
+ return ctx.deps.escapeGrants.resolve(s.workspace, sid, s.profile, token, rel);
918
+ }
919
+ catch (error) {
920
+ if (error instanceof EscapeGrantExpired)
921
+ throw new HttpError(403, error.message);
922
+ throw fileError(error, 404);
923
+ }
924
+ }
795
925
  function isSymlinkAt(path) {
796
926
  try {
797
927
  return lstatSync(path).isSymbolicLink();
@@ -805,7 +935,7 @@ function assertWorkspaceFree(ctx, path) {
805
935
  if (ctx.deps.git.workspaceBusy(path))
806
936
  throw new HttpError(409, WORKSPACE_BUSY_MESSAGE);
807
937
  }
808
- function fileError(error, notFoundStatus = 400) {
938
+ export function fileError(error, notFoundStatus = 400) {
809
939
  if (error instanceof HttpError)
810
940
  return error;
811
941
  if (error instanceof HttpFailure)
@@ -821,10 +951,10 @@ function fileError(error, notFoundStatus = 400) {
821
951
  * `state.db` but that has no `sessions/<sid>.json` (CLI, messaging) is served as an external view bound to the active
822
952
  * workspace, as the sidebar already lists it (issue #3280).
823
953
  */
824
- export function fileOpsSession(ctx, sid, opts = {}) {
954
+ export async function fileOpsSession(ctx, sid, opts = {}) {
825
955
  let session;
826
956
  try {
827
- session = ctx.deps.sessionStore.get(sid, { metadataOnly: true });
957
+ session = (await ctx.deps.sessionStore.get(sid, { metadataOnly: true }));
828
958
  }
829
959
  catch {
830
960
  const profile = ctx.deps.activeProfile();
@@ -832,7 +962,7 @@ export function fileOpsSession(ctx, sid, opts = {}) {
832
962
  throw new HttpError(404, 'Session not found');
833
963
  if (!ctx.deps.workspaces.profileSupportsLocalIo(profile))
834
964
  throw new HttpError(400, REMOTE_WORKSPACE_UNSUPPORTED_CODE, { message: REMOTE_WORKSPACE_UNSUPPORTED_MESSAGE });
835
- return { workspace: ctx.deps.workspaces.lastWorkspace(profile), profile, recovered: false, session: null };
965
+ return { workspace: (await ctx.deps.workspaces.lastWorkspace(profile)), profile, recovered: false, session: null };
836
966
  }
837
967
  if (!ctx.deps.profilesMatch(session.profile, ctx.deps.activeProfile()))
838
968
  throw new HttpError(404, 'Session not found');
@@ -842,7 +972,7 @@ export function fileOpsSession(ctx, sid, opts = {}) {
842
972
  let recovered = false;
843
973
  try {
844
974
  ;
845
- [workspace, recovered] = ctx.deps.workspaces.resolveImplicitWithRecovery(session.workspace, (p) => ctx.deps.workspaces.lastWorkspace(p), session.profile);
975
+ [workspace, recovered] = (await ctx.deps.workspaces.resolveImplicitWithRecovery(session.workspace, async (p) => (await ctx.deps.workspaces.lastWorkspace(p)), session.profile));
846
976
  }
847
977
  catch (error) {
848
978
  if (opts.strictWorkspace)
@@ -855,10 +985,10 @@ export function fileOpsSession(ctx, sid, opts = {}) {
855
985
  if (cached)
856
986
  cached.workspace = workspace;
857
987
  try {
858
- const full = ctx.deps.sessionStore.load(sid);
988
+ const full = (await ctx.deps.sessionStore.load(sid));
859
989
  if (full) {
860
990
  full.workspace = workspace;
861
- ctx.deps.sessionStore.save(full, { touchUpdatedAt: false });
991
+ (await ctx.deps.sessionStore.save(full, { touchUpdatedAt: false }));
862
992
  }
863
993
  }
864
994
  catch { /* recovery persistence is best effort */ }