@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
@@ -8,12 +8,13 @@ import json
8
8
  import logging
9
9
  import queue
10
10
  import sqlite3
11
+ import threading
11
12
  import time
12
13
  from contextlib import closing
13
14
  from pathlib import Path
14
15
 
15
16
  from ..errors import InvalidParams, RpcError
16
- from ..home import profile_home_param, scoped_home
17
+ from ..home import _PROCESS_HOME, _is_named_profile, profile_home_param, scoped_home
17
18
  from ..rpc import CallContext
18
19
 
19
20
  log = logging.getLogger("talaria_sidecar.process")
@@ -27,11 +28,23 @@ def _registry():
27
28
  return process_registry
28
29
 
29
30
 
31
+ def _is_child_process(row: dict) -> bool:
32
+ """TAL-933: a subagent's own process notice, which the server surfaces only when the owning profile opts in."""
33
+ try:
34
+ from tools.process_registry_notifications import is_child_process_notification
35
+ except Exception: # noqa: BLE001 - before Agent 0.21.7, the same rule inline
36
+ if row.get("type") in ("async_delegation", "async_delegation_work_closeout"):
37
+ return False
38
+ return str(row.get("owner_task_id") or row.get("task_id") or "").startswith("sa-")
39
+ return bool(is_child_process_notification(row))
40
+
41
+
30
42
  def _event_row(evt: dict) -> dict:
31
43
  registry = _registry()
32
44
  row = {str(k): v for k, v in evt.items()} if isinstance(evt, dict) else {}
33
45
  process_id = str(row.get("delegation_id") or row.get("session_id") or row.get("task_id") or "") if row.get("type") == "async_delegation" else str(row.get("session_id") or "")
34
46
  row["process_id"] = process_id
47
+ row["child_process"] = _is_child_process(row)
35
48
  if registry is not None and process_id and (not row.get("session_key") or not row.get("origin_ui_session_id")):
36
49
  try:
37
50
  proc = registry.get(process_id)
@@ -42,26 +55,186 @@ def _event_row(evt: dict) -> dict:
42
55
  if not row.get("origin_ui_session_id"):
43
56
  row["origin_ui_session_id"] = str(getattr(proc, "origin_ui_session_id", "") or "") or str(getattr(proc, "spawn_session_id", "") or "")
44
57
  try:
45
- row["consumed"] = bool(registry is not None and process_id and row.get("type") != "async_delegation" and registry.is_completion_consumed(process_id))
58
+ # Only the exit is consumed by the agent's wait or poll; a watch notice from the same process still wakes (TAL-724).
59
+ row["consumed"] = bool(registry is not None and process_id and row.get("type") in (None, "completion") and registry.is_completion_consumed(process_id))
46
60
  except Exception: # noqa: BLE001
47
61
  row["consumed"] = False
48
62
  return row
49
63
 
50
64
 
65
+ # Recovered sessions, held so their exit is seen even when an Agent call other than drain prunes them first.
66
+ _RECOVERED: dict[str, object] = {}
67
+ _RECOVERED_LOCK = threading.Lock()
68
+
69
+
70
+ def _reports_recovered_exits(registry) -> bool:
71
+ """Agent 0.21.6 (hermes-agent 7550800d) closes or prunes a recovered session whose PID is gone without queuing a
72
+ completion, since it never collected an exit status. Talaria still wakes the session with an unknown exit code."""
73
+ return callable(getattr(registry, "_detached_host_fate", None)) and callable(getattr(registry, "_publish_finished", None))
74
+
75
+
76
+ def track_recovered(registry) -> None:
77
+ if not _reports_recovered_exits(registry):
78
+ return
79
+ try:
80
+ with registry._lock:
81
+ detached = [s for s in registry._running.values() if getattr(s, "detached", False)]
82
+ except Exception: # noqa: BLE001 - recovery itself succeeded; only the exit report is lost
83
+ log.warning("Failed to track recovered processes", exc_info=True)
84
+ return
85
+ with _RECOVERED_LOCK:
86
+ _RECOVERED.update((s.id, s) for s in detached)
87
+
88
+
89
+ def _publish_recovered_exits(registry) -> None:
90
+ with _RECOVERED_LOCK:
91
+ exited = [s for s in _RECOVERED.values() if s.exited]
92
+ for session in exited:
93
+ del _RECOVERED[session.id]
94
+ for session in exited:
95
+ # A kill (or any path that collected a status) already queued its own completion through _move_to_finished.
96
+ if session.exit_code is not None or getattr(session, "_finish_claimed", False):
97
+ continue
98
+ try:
99
+ # Best effort, as in the Agent: a durable result must not block the live completion.
100
+ from tools.process_registry_results import save_completed_result
101
+
102
+ save_completed_result(session)
103
+ except Exception: # noqa: BLE001
104
+ log.debug("Failed to retain the result of recovered process %s", session.id, exc_info=True)
105
+ try:
106
+ with registry._lock:
107
+ registry._running.pop(session.id, None)
108
+ session.exited_at = session.exited_at or time.time()
109
+ registry._finished[session.id] = session
110
+ registry._publish_finished(session, True)
111
+ except Exception: # noqa: BLE001 - retried on the next drain
112
+ log.warning("Failed to report the exit of recovered process %s", session.id, exc_info=True)
113
+ with _RECOVERED_LOCK:
114
+ _RECOVERED.setdefault(session.id, session)
115
+
116
+
117
+ _COMPLETION_BASE_HOME: Path | None = None
118
+ _RESTORED_COMPLETION_HOMES: set[Path] = set()
119
+
120
+
121
+ def _completion_homes() -> list[Path]:
122
+ from hermes_constants import get_hermes_home
123
+ from .profiles import named_profile_homes
124
+
125
+ homes = [_PROCESS_HOME, Path(get_hermes_home())]
126
+ if _COMPLETION_BASE_HOME is not None:
127
+ # The server drains one global queue. Re-enumerate so new profiles join and removed/escaped ones leave.
128
+ homes += [_COMPLETION_BASE_HOME, *(home for _, home in named_profile_homes(_COMPLETION_BASE_HOME))]
129
+ return list(dict.fromkeys(home.resolve() for home in homes))
130
+
131
+
132
+ def _restore_and_sweep_completions(registry) -> None:
133
+ from tools import async_delegation
134
+
135
+ restore = getattr(registry, "restore_completions", None)
136
+ if not callable(restore):
137
+ return # Older Agents already restore at registry import and have no orphan sweep.
138
+ # The Agent binds the launch home itself, even when the first drain is in a named profile.
139
+ restore()
140
+ for home in _completion_homes():
141
+ try:
142
+ with scoped_home(home):
143
+ if _is_named_profile(home) and home not in _RESTORED_COMPLETION_HOMES:
144
+ async_delegation.restore_undelivered_completions(registry.completion_queue)
145
+ _RESTORED_COMPLETION_HOMES.add(home)
146
+ sweep = getattr(async_delegation, "maybe_sweep_orphaned_completions", None)
147
+ if callable(sweep):
148
+ sweep(registry.completion_queue)
149
+ except Exception: # noqa: BLE001 - retry this home without blocking other profiles or queued events
150
+ log.debug("Failed to restore or sweep delegation completions for %s", home, exc_info=True)
151
+
152
+
153
+ _DELIVERY_RECONCILE_INTERVAL_S = 30
154
+ _LAST_DELIVERY_RECONCILE: dict[Path, float] = {}
155
+ _DELIVERY_RECONCILE_CURSOR: dict[Path, str] = {}
156
+ _DELIVERY_RECONCILE_PAGE_SIZE = 200
157
+
158
+
159
+ def _delivered_notifications(home: Path, delegation_id: str | None = None, after: str = "") -> tuple[list[dict], str]:
160
+ """Only typed transcript deliveries, never every delivered ledger row. No Agent state is changed."""
161
+ db_path = home / "state.db"
162
+ if not db_path.exists():
163
+ return [], ""
164
+ with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
165
+ tables = {r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")}
166
+ if not {"async_delegations", "messages", "sessions"} <= tables:
167
+ return [], ""
168
+ rows = conn.execute("""WITH RECURSIVE candidates AS (
169
+ SELECT delegation_id, origin_ui_session_id, parent_session_id, origin_session_id, event_json
170
+ FROM async_delegations WHERE delivery_state='delivered' AND state NOT IN ('running','finalizing')
171
+ AND event_json IS NOT NULL AND origin_ui_session_id != '' AND delivered_at >= ?
172
+ AND (? IS NULL OR delegation_id=?) AND delegation_id > ?
173
+ ), lineage(delegation_id, id) AS (
174
+ SELECT delegation_id, parent_session_id FROM candidates WHERE parent_session_id != ''
175
+ UNION SELECT delegation_id, origin_session_id FROM candidates WHERE origin_session_id != ''
176
+ UNION SELECT l.delegation_id, s.id FROM lineage l
177
+ JOIN sessions parent ON parent.id=l.id AND parent.end_reason='compression'
178
+ JOIN sessions s ON s.parent_session_id=l.id
179
+ ) SELECT DISTINCT c.delegation_id, c.origin_ui_session_id, c.event_json
180
+ FROM candidates c JOIN lineage l ON l.delegation_id=c.delegation_id
181
+ JOIN messages m ON m.session_id=l.id
182
+ WHERE m.role='user' AND m.display_kind='async_delegation_complete'
183
+ AND json_valid(m.display_metadata)
184
+ AND json_extract(m.display_metadata, '$.delegation_id')=c.delegation_id
185
+ AND coalesce(json_extract(m.display_metadata, '$.delivery_notice'), '')=''
186
+ AND coalesce(json_extract(m.display_metadata, '$.presentation_suppressed'), 0)=0
187
+ ORDER BY c.delegation_id LIMIT ?""", (time.time() - 48 * 3600, delegation_id, delegation_id, after, _DELIVERY_RECONCILE_PAGE_SIZE)).fetchall()
188
+ events = []
189
+ for did, owner, payload in rows:
190
+ event = _json(payload)
191
+ if event.get("type") != "async_delegation" or event.get("delegation_id") != did or event.get("origin_ui_session_id") != owner or event.get("task_failure_notice"):
192
+ continue
193
+ events.append({**event, "reconciliation_profile_home": str(home.resolve())})
194
+ return events, rows[-1][0] if len(rows) == _DELIVERY_RECONCILE_PAGE_SIZE else ""
195
+
196
+
197
+ def _reconcile_delivered_notifications() -> list[dict]:
198
+ out = []
199
+ for home in _completion_homes():
200
+ now = time.monotonic()
201
+ if now - _LAST_DELIVERY_RECONCILE.get(home, -float("inf")) < _DELIVERY_RECONCILE_INTERVAL_S:
202
+ continue
203
+ try:
204
+ events, cursor = _delivered_notifications(home, after=_DELIVERY_RECONCILE_CURSOR.get(home, ""))
205
+ out.extend(events)
206
+ _DELIVERY_RECONCILE_CURSOR[home] = cursor
207
+ _LAST_DELIVERY_RECONCILE[home] = now
208
+ except Exception: # retry unreadable homes; never block live queue delivery
209
+ log.warning("Failed to reconcile typed delegation deliveries for %s", home, exc_info=True)
210
+ return out
211
+
212
+
51
213
  def drain(max_events: int) -> list[dict]:
52
214
  """Pop every queued completion event; the server requeues what it does not own."""
53
215
  registry = _registry()
54
216
  completion_queue = getattr(registry, "completion_queue", None) if registry is not None else None
55
217
  if completion_queue is None:
56
218
  return []
219
+ try:
220
+ _restore_and_sweep_completions(registry)
221
+ except Exception: # noqa: BLE001 - maintenance must not prevent delivery of live queued events
222
+ log.debug("Failed to reconcile delegation completions", exc_info=True)
57
223
  try:
58
224
  # A recovered process has no reader thread; only this probe notices its exit and queues its completion (TAL-533).
59
225
  with registry._lock:
60
226
  detached = [s for s in registry._running.values() if getattr(s, "detached", False)]
61
227
  for session in detached:
62
228
  registry._refresh_detached_session(session)
229
+ _publish_recovered_exits(registry)
63
230
  except Exception: # noqa: BLE001
64
231
  log.debug("Failed to reconcile recovered processes", exc_info=True)
232
+ # Use the existing queue and its bound, so reconciliation cannot starve live or foreign events.
233
+ try:
234
+ for event in _reconcile_delivered_notifications():
235
+ completion_queue.put_nowait(event)
236
+ except Exception:
237
+ log.warning("Failed to offer typed delegation deliveries", exc_info=True)
65
238
  out = []
66
239
  while len(out) < max_events:
67
240
  try:
@@ -83,7 +256,7 @@ def requeue(events: list[dict]) -> int:
83
256
  count = 0
84
257
  for evt in events:
85
258
  try:
86
- completion_queue.put_nowait({k: v for k, v in evt.items() if k not in ("process_id", "consumed")})
259
+ completion_queue.put_nowait({k: v for k, v in evt.items() if k not in ("process_id", "consumed", "child_process")})
87
260
  count += 1
88
261
  except Exception: # noqa: BLE001
89
262
  log.debug("Failed to requeue process completion", exc_info=True)
@@ -141,6 +314,15 @@ def claim_delivery(evt: dict, consumer: str) -> str | None:
141
314
  if api is None:
142
315
  return ""
143
316
  claim_id = api.claim_event_delivery(_ledger_event(evt), consumer)
317
+ if claim_id is None and consumer == "webui" and evt.get("origin_ui_session_id"):
318
+ from hermes_constants import get_hermes_home
319
+ home = Path(get_hermes_home()).resolve()
320
+ if evt.get("reconciliation_profile_home", str(home)) == str(home):
321
+ # A live queue copy can race the transcript-only consumer too. Recheck the same proof here.
322
+ transport = {"process_id", "consumed", "child_process", "restored", "reconciliation_profile_home"}
323
+ for candidate in _delivered_notifications(home, str(evt.get("delegation_id") or ""))[0]:
324
+ if {k: v for k, v in candidate.items() if k not in transport} == {k: v for k, v in evt.items() if k not in transport}:
325
+ return "" # Web's session receipt acknowledges this admission, not the Agent ledger.
144
326
  return None if claim_id is None else str(claim_id)
145
327
 
146
328
 
@@ -175,11 +357,14 @@ def defer_delivery(evt: dict, claim_id: str) -> bool:
175
357
 
176
358
 
177
359
  def format_notification(evt: dict) -> str:
178
- if evt.get("type") == "async_delegation":
360
+ if evt.get("type") in ("async_delegation", "heartbeat", "watch_match", "watch_disabled", "watch_overflow_tripped", "watch_overflow_released"):
179
361
  try:
180
- from tools.process_registry import format_process_notification
362
+ from tools import process_registry
181
363
 
182
- return format_process_notification(evt) or ""
364
+ # Older formatters fall through unknown events to an exit notification.
365
+ if evt.get("type") == "heartbeat" and not callable(getattr(getattr(process_registry, "ProcessRegistry", None), "_emit_heartbeat", None)):
366
+ return ""
367
+ return process_registry.format_process_notification(evt) or ""
183
368
  except Exception: # noqa: BLE001
184
369
  return ""
185
370
  if evt.get("type") != "completion":
@@ -349,12 +534,21 @@ def _goal_text(content) -> str:
349
534
  return ""
350
535
  if content.startswith(_CONTENT_JSON_PREFIX):
351
536
  try:
352
- parts = json.loads(content[len(_CONTENT_JSON_PREFIX):])
353
- except ValueError:
354
- return ""
355
- texts = [p.get("text") for p in parts if isinstance(p, dict) and p.get("type") == "text"] if isinstance(parts, list) else []
537
+ from hermes_state import SessionDB
538
+ decode = getattr(SessionDB, "_decode_content", None)
539
+ except ImportError:
540
+ decode = None
541
+ if callable(decode):
542
+ content = decode(content)
543
+ else:
544
+ try:
545
+ content = json.loads(content[len(_CONTENT_JSON_PREFIX):])
546
+ except ValueError:
547
+ pass # Legacy malformed marker rows remain literal text, as in the Agent.
548
+ if isinstance(content, list):
549
+ texts = [p.get("text") for p in content if isinstance(p, dict) and p.get("type") == "text"]
356
550
  return str(texts[0]) if texts and texts[0] else ""
357
- return content.split(_IMAGE_HINTS, 1)[0]
551
+ return content.split(_IMAGE_HINTS, 1)[0] if isinstance(content, str) else ""
358
552
 
359
553
 
360
554
  def unit_children(unit: dict, owner: str, live: list[dict], finished: list[dict], now: float) -> list[dict]:
@@ -415,10 +609,11 @@ def recover(base_home: Path) -> int:
415
609
  """TAL-533: re-adopt every profile's checkpointed processes when a sidecar starts, so a process in a profile nobody
416
610
  uses yet is watched (and its exit reported) from the start. Entering a home's scope recovers it once; a failed
417
611
  recovery fails the call, so the server retries."""
418
- from hermes_cli.profiles import _PROFILE_ID_RE
612
+ from .profiles import named_profile_homes
419
613
 
420
- profiles_root = base_home / "profiles"
421
- named = sorted(p for p in profiles_root.iterdir() if p.is_dir() and _PROFILE_ID_RE.match(p.name)) if profiles_root.is_dir() else []
614
+ global _COMPLETION_BASE_HOME
615
+ _COMPLETION_BASE_HOME = base_home
616
+ named = [home for _, home in named_profile_homes(base_home)]
422
617
  failed = []
423
618
  for home in [base_home, *named]:
424
619
  try:
@@ -479,7 +674,15 @@ def register(registry_) -> None:
479
674
  if not consumer:
480
675
  raise InvalidParams("consumer is required")
481
676
  with scoped_home(profile_home_param(params)):
482
- return {"claim_id": claim_delivery(evt, consumer)}
677
+ claim = claim_delivery(evt, consumer)
678
+ result = {"claim_id": claim}
679
+ if claim is None:
680
+ api = _delivery_api()
681
+ lookup = getattr(api, "get_durable_delegation", None)
682
+ row = lookup(str(evt.get("delegation_id") or "")) if callable(lookup) else None
683
+ if row and row.get("delivery_state") == "pending":
684
+ result["retryable"] = True
685
+ return result
483
686
 
484
687
  @registry_.method("process.complete_delivery")
485
688
  def complete_(ctx: CallContext, params: dict) -> dict:
@@ -24,6 +24,9 @@ log = logging.getLogger("talaria_sidecar.profiles")
24
24
  _PROFILE_ID_RE = re.compile(r"[a-z0-9][a-z0-9_-]{0,63}")
25
25
  _PROFILE_DIRS = ("skills", "memories", "sessions", "logs", "cron")
26
26
  _CLONE_CONFIG_FILES = ("config.yaml", ".env", "SOUL.md")
27
+ _AVATAR_EXTS = ("png", "jpg", "webp")
28
+ # The Agent's ``INTERNAL_LISTING_SOURCES``: worker sessions are never a profile's conversation.
29
+ _INTERNAL_SOURCES = ("kanban", "tool", "oneshot")
27
30
  _TERMINAL_ENV = {
28
31
  "backend": "TERMINAL_ENV", "env_type": "TERMINAL_ENV", "cwd": "TERMINAL_CWD", "timeout": "TERMINAL_TIMEOUT", "lifetime_seconds": "TERMINAL_LIFETIME_SECONDS",
29
32
  "modal_mode": "TERMINAL_MODAL_MODE", "docker_image": "TERMINAL_DOCKER_IMAGE", "docker_forward_env": "TERMINAL_DOCKER_FORWARD_ENV", "docker_env": "TERMINAL_DOCKER_ENV",
@@ -96,36 +99,141 @@ def _visible(profile_dir: Path) -> bool:
96
99
  return not (isinstance(data, dict) and data.get("visible") is False)
97
100
 
98
101
 
102
+ def _bounded(value, limit: int) -> str:
103
+ """One printable line of at most ``limit`` characters; anything but a string reads as empty."""
104
+ if not isinstance(value, str):
105
+ return ""
106
+ text = " ".join("".join(c if c.isprintable() else " " for c in value).split())
107
+ return text if len(text) <= limit else text[: limit - 1].rstrip() + "\u2026"
108
+
109
+
110
+ def bot_identity(home: Path) -> dict:
111
+ """The presentation-only Bot Mode identity from ``profile.yaml`` and ``assets/``; never the raw ``ui_meta``."""
112
+ meta = _yaml_load(home / "profile.yaml") or {}
113
+ ui_meta = meta.get("ui_meta") if isinstance(meta.get("ui_meta"), dict) else {}
114
+ bots = ui_meta.get("hermes-bots") if isinstance(ui_meta.get("hermes-bots"), dict) else {}
115
+ return {
116
+ # Bot Mode's roster name (``ui_meta['hermes-bots'].title``) wins over the profile's own display name.
117
+ "display_name": _bounded(bots.get("title"), 80) or _bounded(meta.get("display_name"), 80),
118
+ "description": _bounded(meta.get("description"), 280), "has_avatar": any((home / "assets" / f"avatar.{ext}").is_file() for ext in _AVATAR_EXTS),
119
+ }
120
+
121
+
122
+ def canonical_bot_chat(home: Path) -> dict | None:
123
+ """The profile's canonical Bot Chat: its session titled exactly ``Bot Chat`` (Bot Mode's registry) and live
124
+ compression tip, read through a read-only handle. Missing, unreadable, archived, worker-owned, or an Agent
125
+ without Bot Mode answers None; the gateway's resurrection of accidental archives is a write and stays there."""
126
+ db_path = home / "state.db"
127
+ if not db_path.is_file():
128
+ return None
129
+ try:
130
+ from hermes_state import SessionDB
131
+
132
+ title = getattr(SessionDB, "CANONICAL_BOT_CHAT_TITLE", None)
133
+ if not title:
134
+ return None
135
+ db = SessionDB(db_path=db_path, read_only=True)
136
+ except Exception: # noqa: BLE001
137
+ return None
138
+ try:
139
+ row = db.get_session_by_title(title)
140
+ if not row or row.get("title") != title or row.get("archived") or str(row.get("source") or "").strip().lower() in _INTERNAL_SOURCES:
141
+ return None
142
+ session_id = str(row["id"])
143
+ return {"session_id": session_id, "tip_session_id": str(db.get_compression_tip(session_id) or session_id)}
144
+ except Exception: # noqa: BLE001
145
+ log.debug("canonical Bot Chat lookup failed for %s", db_path, exc_info=True)
146
+ return None
147
+ finally:
148
+ try:
149
+ db.close()
150
+ except Exception: # noqa: BLE001
151
+ pass
152
+
153
+
154
+ GATEWAY_RUNNING_MODES = ("own", "served")
155
+
156
+
157
+ def gateway_mode(home: Path, name: str, is_default: bool) -> str:
158
+ """The profile's gateway under the host multiplexer (TAL-805): ``served`` by the live host gateway, ``own`` gateway
159
+ process, ``parked`` (``hermes -p X gateway stop`` left its marker and nothing runs; the marker records intent, so a
160
+ live ``--force`` gateway still counts, as in ``hermes -p X gateway status``), ``standalone`` (opted out of the host and
161
+ not running), else ``stopped``. The default profile is the host itself.
162
+ A helper the installed Agent lacks answers no, so an Agent without the multiplexer helpers reports ``own`` or
163
+ ``stopped``; a helper that fails answers no too, except the parked check, which fails closed to ``stopped``."""
164
+ from hermes_cli import profiles as agent
165
+
166
+ def probe(helper: str, arg) -> bool | None:
167
+ check = getattr(agent, helper, None)
168
+ if check is None:
169
+ return False
170
+ try:
171
+ return bool(check(arg))
172
+ except Exception: # noqa: BLE001
173
+ log.debug("gateway mode probe %s failed for %s", helper, home, exc_info=True)
174
+ return None
175
+
176
+ def asks(helper: str, arg) -> bool:
177
+ return probe(helper, arg) is True
178
+
179
+ named = not is_default
180
+ parked = probe("profile_is_parked", home) if named else False
181
+ if parked is None:
182
+ return "stopped"
183
+ if named and asks("_served_by_running_multiplexer", name):
184
+ return "served"
185
+ if asks("_check_gateway_running", home):
186
+ return "own"
187
+ if parked:
188
+ return "parked"
189
+ if named and asks("profile_is_standalone", home):
190
+ return "standalone"
191
+ return "stopped"
192
+
193
+
99
194
  def _row(home: Path, name: str, is_default: bool) -> dict:
100
- from hermes_cli.profiles import _check_gateway_running, _read_config_model
195
+ from hermes_cli.profiles import _read_config_model
101
196
 
102
197
  try:
103
198
  model, provider = _read_config_model(home)
104
199
  except Exception: # noqa: BLE001
105
200
  model, provider = None, None
106
- try:
107
- gateway_running = bool(_check_gateway_running(home))
108
- except Exception: # noqa: BLE001
109
- gateway_running = False
201
+ mode = gateway_mode(home, name, is_default)
110
202
  enabled, total = skills_stats(home)
111
203
  return {
112
- "name": name, "path": str(home), "is_default": is_default, "gateway_running": gateway_running, "model": model, "provider": provider,
204
+ "name": name, "path": str(home), "is_default": is_default, "gateway_running": mode in GATEWAY_RUNNING_MODES, "gateway_mode": mode,
205
+ "model": model, "provider": provider,
113
206
  "has_env": (home / ".env").exists(), "visible": _visible(home), "skill_count": enabled, "enabled_skills": enabled, "total_skills": total,
207
+ **bot_identity(home), "canonical_session": canonical_bot_chat(home),
114
208
  }
115
209
 
116
210
 
117
- def list_profiles(base_home: Path) -> list[dict]:
118
- """Profile rows for the base Hermes home: the root profile plus ``profiles/*``."""
211
+ def named_profile_homes(base_home: Path) -> list[tuple[str, Path]]:
212
+ """(name, home) for the ``profiles/*`` directories named like profiles, skipping any that resolve outside the resolved
213
+ profiles root (a symlink leading elsewhere is never read as a profile; TAL-579). A contained symlink answers the
214
+ target it was checked against, so retargeting the link afterwards cannot redirect the caller."""
119
215
  from hermes_cli.profiles import _PROFILE_ID_RE as UPSTREAM_ID_RE
120
216
 
217
+ profiles_root = base_home / "profiles"
218
+ if not profiles_root.is_dir():
219
+ return []
220
+ root = profiles_root.resolve()
221
+ homes = []
222
+ for entry in sorted(profiles_root.iterdir()):
223
+ if not (entry.is_dir() and UPSTREAM_ID_RE.match(entry.name)):
224
+ continue
225
+ real = entry.resolve()
226
+ if real.is_relative_to(root):
227
+ homes.append((entry.name, real if entry.is_symlink() else entry))
228
+ return homes
229
+
230
+
231
+ def list_profiles(base_home: Path) -> list[dict]:
232
+ """Profile rows for the base Hermes home: the root profile plus ``profiles/*``."""
121
233
  rows = []
122
234
  if base_home.is_dir():
123
235
  rows.append(_row(base_home, "default", True))
124
- profiles_root = base_home / "profiles"
125
- if profiles_root.is_dir():
126
- for entry in sorted(profiles_root.iterdir()):
127
- if entry.is_dir() and UPSTREAM_ID_RE.match(entry.name):
128
- rows.append(_row(entry, entry.name, False))
236
+ rows.extend(_row(home, name, False) for name, home in named_profile_homes(base_home))
129
237
  return rows
130
238
 
131
239