@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,10 +10,14 @@ composes: ``hermes_cli.auth``, ``hermes_cli.models``, ``hermes_cli.runtime_provi
10
10
  from __future__ import annotations
11
11
 
12
12
  import dataclasses
13
+ import json
13
14
  import logging
15
+ import sqlite3
14
16
  import sys
15
17
  import threading
18
+ from contextlib import closing
16
19
  from pathlib import Path
20
+ from types import SimpleNamespace
17
21
  from typing import Any
18
22
 
19
23
  from ..errors import InvalidParams, RpcError
@@ -94,11 +98,57 @@ def _user_plugin_profiles() -> dict[int, tuple[Any, Path]]:
94
98
  return out
95
99
 
96
100
 
101
+ def _hosted_plugin_profiles(roots: dict[Path, str]) -> dict[Path, list] | None:
102
+ """Resolve enabled directories through the Agent's host metadata and scoped provider registry."""
103
+ import providers
104
+
105
+ if not hasattr(providers, "_home_layer") or not callable(getattr(providers, "provider_source", None)):
106
+ return None
107
+ try:
108
+ from hermes_cli.plugin_isolation import ISOLATION_HOST, isolation_mode
109
+ except ImportError: # per-home registries predate host isolation
110
+ return None
111
+
112
+ if isolation_mode() != ISOLATION_HOST:
113
+ return None
114
+ from hermes_cli.plugin_host_profiles import load_hosted_profiles
115
+ from hermes_constants import get_hermes_home, hermes_home_key
116
+
117
+ registered = {p.name: p for p in providers.list_providers() if providers.provider_source(p.name) == "user"}
118
+ home_key = hermes_home_key(get_hermes_home())
119
+ # Discovery also loads disabled and manifest-less category plugins. Include them when checking name collisions.
120
+ directories = set(roots)
121
+ for parent, flat in ((providers._user_plugins_dir(), False), (providers._installed_plugins_dir(), True)):
122
+ if parent is not None:
123
+ directories.update(child.resolve() for child in parent.iterdir()
124
+ if child.is_dir() and not child.name.startswith(("_", "."))
125
+ and (not flat or (child.name != "model-providers" and providers._declares_model_provider_kind(child))))
126
+ owners: dict[str, set[Path]] = {}
127
+ for root in directories:
128
+ try:
129
+ # Reuse the Agent's cached extraction; provider ids need not match manifest or directory names.
130
+ profiles = load_hosted_profiles(root, providers._user_module_name(root, home_key))
131
+ for profile in profiles:
132
+ owners.setdefault(profile.name, set()).add(root)
133
+ except Exception: # noqa: BLE001 - failed extraction keeps the manifest's not_loaded row
134
+ log.debug("reading hosted provider plugin %s failed", root, exc_info=True)
135
+ found: dict[Path, list] = {root: [] for root in roots}
136
+ for name, profile in registered.items():
137
+ origins = owners.get(name, set())
138
+ if len(origins) == 1:
139
+ root = next(iter(origins))
140
+ if root in found:
141
+ found[root].append(profile)
142
+ return found
143
+
144
+
97
145
  def _load_plugin(root: Path) -> None:
98
146
  """Load the scoped profile's installed plugin that the process-wide discovery (run under the launch profile) skipped.
99
147
 
100
- Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory. A plugin never displaces a provider
101
- that is already registered, so one profile's plugin cannot change what another profile's provider id runs.
148
+ Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory. On an Agent with one process-wide
149
+ provider registry, a plugin never displaces a provider that is already registered, so one profile's plugin cannot
150
+ change what another profile's provider id runs. An Agent with per-home provider layers registers it in this home's
151
+ layer instead, so the same id is that profile's own provider.
102
152
  """
103
153
  import providers
104
154
 
@@ -121,12 +171,13 @@ def _load_plugin(root: Path) -> None:
121
171
  def installed_plugin_profiles() -> list[tuple[str, Any]]:
122
172
  """``(manifest name, profile | None)`` for each enabled model-provider plugin installed in the scoped home.
123
173
 
124
- Bundled providers are built-ins, not plugins. The Agent keeps one provider registry per process, so a profile
125
- belongs to this home only when the plugin module that registered it lives in one of this home's plugin
126
- directories; a plugin the launch discovery skipped is loaded here. ``None`` means the plugin could not be loaded:
127
- it failed to import, its provider id is taken by another plugin, or another profile's plugin has the same directory
128
- name. ponytail: ownership is read from the plugin module's globals, so a plugin that registers an unbound inline
129
- profile also reads as not loaded; track registrations in the Agent if one ever does.
174
+ Bundled providers are built-ins, not plugins. Hosted profiles come from the Agent's scoped user registry and
175
+ directory metadata. In-process profiles belong to this home when their module lives in its plugin directories;
176
+ a plugin the launch discovery skipped is loaded here.
177
+ ``None`` means the plugin could not be loaded or attributed: it failed to import, its hosted provider id is
178
+ ambiguous, or a registration/module name collided in an older process-wide registry.
179
+ shortcut: in-process ownership uses module globals, so an unbound inline profile reads as not
180
+ loaded; use Agent registration metadata if it becomes available.
130
181
  """
131
182
  try:
132
183
  from hermes_cli.plugins_discovery import _get_disabled_plugins, _get_enabled_plugins, collect_directory_manifests, gate_manifest
@@ -141,6 +192,10 @@ def installed_plugin_profiles() -> list[tuple[str, Any]]:
141
192
  if not roots:
142
193
  return []
143
194
 
195
+ hosted = _hosted_plugin_profiles(roots)
196
+ if hosted is not None:
197
+ return [(name, p) for root, name in roots.items() for p in (hosted[root] or [None])]
198
+
144
199
  def owned() -> dict[Path, list]:
145
200
  found: dict[Path, list] = {root: [] for root in roots}
146
201
  for profile, directory in _user_plugin_profiles().values():
@@ -158,18 +213,40 @@ def installed_plugin_profiles() -> list[tuple[str, Any]]:
158
213
  return [(name, p) for root, name in roots.items() for p in (found[root] or [None])]
159
214
 
160
215
 
161
- def _other_profile_plugin(provider_id: str) -> bool:
162
- """Whether ``provider_id`` is a plugin provider that another profile installed (the registry is process-wide)."""
163
- from providers import get_provider_profile
216
+ def _unavailable_plugin(provider_id: str) -> bool:
217
+ """Whether a registered plugin lacks an enabled, unambiguous owner in this home."""
218
+ import providers
164
219
 
165
- profile = get_provider_profile(provider_id)
166
- if profile is None or id(profile) not in _user_plugin_profiles():
220
+ profile = providers.get_provider_profile(provider_id)
221
+ if profile is None:
222
+ return False
223
+ if hasattr(providers, "_home_layer") and callable(getattr(providers, "provider_source", None)):
224
+ if providers.provider_source(profile.name) != "user":
225
+ return False
226
+ elif id(profile) not in _user_plugin_profiles():
167
227
  return False
168
228
  return not any(p is profile for _name, p in installed_plugin_profiles())
169
229
 
170
230
 
231
+ def _agent_knows_provider(name: str) -> bool:
232
+ """Whether the Agent's auth layer can resolve this provider id at all.
233
+
234
+ Agent 0.21.3 resolves providers from a ``PROVIDER_REGISTRY`` snapshot of its import-time plugin discovery, so a
235
+ named profile's plugin loaded here on demand is unknown to it and cannot run. Registering it there would expose it
236
+ to every profile, so it reads as unavailable instead. Newer Agents look providers up live (``_registry_lookup``).
237
+ """
238
+ try:
239
+ import hermes_cli.auth as auth
240
+ except Exception: # noqa: BLE001 - auth_status reports an unavailable auth layer
241
+ return True
242
+ registry = getattr(auth, "PROVIDER_REGISTRY", None)
243
+ return hasattr(auth, "_registry_lookup") or not isinstance(registry, dict) or name.strip().lower() in registry
244
+
245
+
171
246
  def _setup_state(profile: Any) -> str:
172
247
  """The Agent's own setup verdict for a plugin provider, without spawning its CLI or reading its credentials."""
248
+ if not _agent_knows_provider(str(profile.name)):
249
+ return "unavailable"
173
250
  try:
174
251
  status = auth_status(profile.name)
175
252
  except RpcError:
@@ -178,6 +255,9 @@ def _setup_state(profile: Any) -> str:
178
255
  return "unavailable"
179
256
  if profile.auth_type == "external_process":
180
257
  return "ready" if status.get("configured") else "missing_cli"
258
+ if profile.auth_type in ("oauth_external", "oauth_device_code"):
259
+ # The Agent's ``configured`` only means registered here; a pooled token, live or refreshable on use, means signed in.
260
+ return "ready" if status.get("logged_in") or status.get("needs_refresh") else "needs_setup"
181
261
  return "ready" if status.get("logged_in") or status.get("configured") else "needs_setup"
182
262
 
183
263
 
@@ -193,47 +273,97 @@ def plugin_providers() -> list[dict]:
193
273
  return rows
194
274
 
195
275
 
196
- def _plugin_catalog(provider_id: str) -> list[str]:
276
+ def _plugin_catalog(provider_id: str) -> tuple[list[str], bool]:
197
277
  """Discovery for an installed plugin the Agent's catalog has no fetcher for: the profile's own listing, then its fallback.
198
278
 
199
279
  API-key plugins already get both from ``provider_model_ids`` with their key, so only keyless auth types list here.
280
+ Like the Agent, the fallback list is a placeholder only when a listing was tried and declined.
200
281
  """
201
282
  for _name, profile in installed_plugin_profiles():
202
283
  if profile is None or str(profile.name).strip().lower() != provider_id.strip().lower():
203
284
  continue
285
+ lists = bool(profile.supports_model_listing) and profile.auth_type != "api_key"
204
286
  try:
205
- live = profile.fetch_models(timeout=8.0) if profile.supports_model_listing and profile.auth_type != "api_key" else None
287
+ live = profile.fetch_models(timeout=8.0) if lists else None
206
288
  except Exception: # noqa: BLE001 - a broken plugin answers with its fallback catalog
207
289
  log.debug("fetch_models(%r) failed", provider_id, exc_info=True)
208
290
  live = None
209
- return [str(m) for m in (live or profile.fallback_models or ()) if m]
210
- return []
291
+ return [str(m) for m in (live or profile.fallback_models or ()) if m], lists and not live
292
+ return [], False
211
293
 
212
294
 
213
- def model_ids(provider_id: str, *, force_refresh: bool) -> list[str]:
295
+ def model_ids(provider_id: str, *, force_refresh: bool) -> tuple[list[str], bool]:
296
+ """The Agent's catalog for a provider, and whether it is the curated placeholder for an unreachable one (TAL-799)."""
214
297
  try:
215
- from hermes_cli.models import provider_model_ids
298
+ from hermes_cli import models
216
299
  except Exception as exc: # noqa: BLE001
217
300
  raise RpcError(f"model catalog unavailable: {exc}", condition="providers_unavailable") from exc
218
301
  try:
219
- if _other_profile_plugin(provider_id):
220
- return []
221
- except Exception: # noqa: BLE001 - no readable provider registry means no plugin to keep apart
302
+ if _unavailable_plugin(provider_id):
303
+ return [], False
304
+ except Exception: # noqa: BLE001 - an unverified owner must not expose another plugin's models
222
305
  log.debug("plugin ownership check for %r failed", provider_id, exc_info=True)
306
+ return [], False
223
307
  try:
224
- ids = provider_model_ids(provider_id, force_refresh=force_refresh)
308
+ ids = models.provider_model_ids(provider_id, force_refresh=force_refresh)
225
309
  except TypeError:
226
- ids = provider_model_ids(provider_id)
310
+ ids = models.provider_model_ids(provider_id)
227
311
  except Exception: # noqa: BLE001
228
312
  log.debug("provider_model_ids(%r) failed", provider_id, exc_info=True)
229
313
  ids = []
314
+ # Agents before the marker never flag a fallback.
315
+ curated = getattr(models, "CuratedFallbackModels", None)
316
+ fallback = isinstance(curated, type) and isinstance(ids, curated)
230
317
  ids = [str(m) for m in (ids or []) if m]
231
318
  if ids:
232
- return ids
319
+ return ids, fallback
233
320
  try:
234
321
  return _plugin_catalog(provider_id)
235
322
  except RpcError:
236
- return []
323
+ return [], False
324
+
325
+
326
+ def _requests_local_runtime(requested: str | None) -> bool:
327
+ """Whether a resolution asks for the Agent's managed llama.cpp provider, picked the way the Agent picks it: the
328
+ named provider, else the profile's, else ``HERMES_INFERENCE_PROVIDER``."""
329
+ try:
330
+ from hermes_cli.providers import LLAMACPP_ALIASES
331
+ from hermes_cli.runtime_provider import resolve_requested_provider
332
+
333
+ return resolve_requested_provider(requested) in LLAMACPP_ALIASES
334
+ except Exception: # noqa: BLE001 - an Agent without the local runtime
335
+ return False
336
+
337
+
338
+ def runtime_failure(exc: Exception, requested: str | None, message: str | None = None) -> RpcError:
339
+ """The typed error for a runtime that did not resolve. The local llama.cpp provider has no credential to add: it
340
+ fails because its server is off or still starting, and the Agent's message says which (TAL-798)."""
341
+ condition = "local_runtime_unavailable" if _requests_local_runtime(requested) else "credential_missing"
342
+ return RpcError(message or str(exc), condition=condition, data={"error_type": type(exc).__name__})
343
+
344
+
345
+ def local_models() -> dict:
346
+ """The Agent's managed llama.cpp runtime (TAL-798): the downloaded GGUFs, selectable before its server runs (a turn
347
+ on one boots it), and the endpoint the running server recorded. An Agent without the local runtime lists none."""
348
+ try:
349
+ from hermes_cli.local_runtime.bootstrap import staged_model_ids
350
+ from hermes_cli.providers import LLAMACPP_PROVIDER_ID
351
+ except Exception: # noqa: BLE001 - predates the local runtime
352
+ return {"provider_id": "", "models": [], "current_endpoint": None}
353
+ try:
354
+ models = [str(m) for m in staged_model_ids() if m]
355
+ except Exception: # noqa: BLE001 - an unreadable models directory lists none
356
+ log.debug("staged_model_ids failed", exc_info=True)
357
+ models = []
358
+ endpoint = None
359
+ try:
360
+ from hermes_cli.local_runtime.endpoint import _state_endpoint
361
+
362
+ state = _state_endpoint()
363
+ endpoint = str(state["base_url"]) if state else None
364
+ except Exception: # noqa: BLE001 - no recorded server
365
+ log.debug("local runtime endpoint failed", exc_info=True)
366
+ return {"provider_id": str(LLAMACPP_PROVIDER_ID), "models": models, "current_endpoint": endpoint}
237
367
 
238
368
 
239
369
  def resolve_runtime(*, requested: str | None, api_key: str | None, base_url: str | None, target_model: str | None) -> dict:
@@ -248,7 +378,7 @@ def resolve_runtime(*, requested: str | None, api_key: str | None, base_url: str
248
378
  message = format_runtime_provider_error(exc)
249
379
  except Exception: # noqa: BLE001
250
380
  message = str(exc)
251
- raise RpcError(message, condition="credential_missing", data={"error_type": type(exc).__name__})
381
+ raise runtime_failure(exc, requested, message) from exc
252
382
  return _plain(runtime) if isinstance(runtime, dict) else {"runtime": _plain(runtime)}
253
383
 
254
384
 
@@ -346,7 +476,60 @@ def capabilities(provider: str, model: str) -> dict | None:
346
476
  return row if isinstance(row, dict) else None
347
477
 
348
478
 
479
+ def selection_warnings(home: Path, params: dict) -> dict:
480
+ """Read-only guard decision using the same route resolver as a turn; never builds or evicts an Agent."""
481
+ from . import chat
482
+ try:
483
+ from hermes_cli.model_selection_guards import SelectionContext, selection_context_for_agent, selection_warnings as warnings
484
+ except ImportError as exc:
485
+ raise RpcError("Model selection warnings are unavailable in this Agent", condition="unsupported") from exc
486
+
487
+ model = str(params["model"])
488
+ runtime = chat._resolve_runtime(params.get("provider"), model)
489
+ provider = str(runtime.get("provider") or params.get("provider") or "")
490
+ base_url = str(runtime.get("base_url") or "")
491
+ tokens = params.get("context_tokens")
492
+ if not tokens:
493
+ with chat._AGENT_CACHE_LOCK:
494
+ entry = chat._AGENT_CACHE.get(params["session_id"])
495
+ if entry and Path(entry[2]).resolve() == home.resolve():
496
+ context = selection_context_for_agent(entry[0])
497
+ tokens = context.context_tokens if context else None
498
+ if not tokens and (home / "state.db").exists():
499
+ from agent.usage_anchor import persisted_anchor_tokens
500
+
501
+ # SessionDB opens and migrates its database. The guard needs only this read-only accessor.
502
+ with closing(sqlite3.connect(f"{(home / 'state.db').resolve().as_uri()}?mode=ro", uri=True)) as db:
503
+ row = db.execute("SELECT model_config FROM sessions WHERE id=?", (params["session_id"],)).fetchone()
504
+ config = json.loads(row[0] or "{}") if row else {}
505
+ reader = SimpleNamespace(get_session_model_config_value=lambda sid, key, default=None: config.get(key, default))
506
+ tokens = persisted_anchor_tokens(reader, params["session_id"], params.get("messages", []))
507
+ # A same-model pick on another provider still abandons the previous route's prompt cache.
508
+ current = params.get("current_model") if params.get("current_provider") == params.get("provider") else None
509
+ context = SelectionContext(context_tokens=tokens, current_model=current)
510
+ result = warnings(model, provider=provider, base_url=base_url, api_key=runtime.get("api_key"), selection_context=context)
511
+ return {"warnings": [_plain(w) for w in result], "context_tokens": tokens or None, "provider": provider, "base_url": base_url}
512
+
513
+
349
514
  def register(reg) -> None:
515
+ @reg.method("models.selection_warnings")
516
+ def selection(ctx: CallContext, params: dict) -> dict:
517
+ if not params.get("model") or not params.get("session_id"):
518
+ raise InvalidParams("model and session_id are required")
519
+ home = profile_home_param(params)
520
+ with scoped_home(home):
521
+ return selection_warnings(home, params)
522
+
523
+ @reg.method("models.speed_tiers")
524
+ def speed_tiers(ctx: CallContext, params: dict) -> dict:
525
+ from ..speed import supported_speed_tiers
526
+
527
+ routes = params.get("routes")
528
+ if not isinstance(routes, list) or any(not isinstance(route, dict) or not route.get("model") for route in routes):
529
+ raise InvalidParams("routes must contain model routes")
530
+ with scoped_home(profile_home_param(params)):
531
+ return {"tiers": [supported_speed_tiers(str(route["model"]), str(route.get("provider") or ""), str(route.get("base_url") or "")) for route in routes]}
532
+
350
533
  @reg.method("providers.registry")
351
534
  def registry_(ctx: CallContext, params: dict) -> dict:
352
535
  with scoped_home(profile_home_param(params)):
@@ -363,7 +546,13 @@ def register(reg) -> None:
363
546
  if not provider:
364
547
  raise InvalidParams("provider is required")
365
548
  with scoped_home(profile_home_param(params)):
366
- return {"provider": provider, "model_ids": model_ids(provider, force_refresh=bool(params.get("force_refresh", False)))}
549
+ ids, fallback = model_ids(provider, force_refresh=bool(params.get("force_refresh", False)))
550
+ return {"provider": provider, "model_ids": ids, "fallback": fallback}
551
+
552
+ @reg.method("providers.local_models")
553
+ def local(ctx: CallContext, params: dict) -> dict:
554
+ with scoped_home(profile_home_param(params)):
555
+ return local_models()
367
556
 
368
557
  @reg.method("providers.resolve_runtime")
369
558
  def runtime(ctx: CallContext, params: dict) -> dict:
@@ -2,18 +2,83 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  import os
6
7
  import re
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+ import time
12
+ from pathlib import Path
7
13
 
8
14
  from .. import SIDECAR_RPC_VERSION
9
15
  from ..errors import InvalidParams, RpcError
10
- from ..home import edit_launch_env
16
+ from ..home import edit_launch_env, _frozen_launch_env
11
17
  from ..rpc import CallContext
12
18
 
13
19
 
14
20
  def register(registry) -> None:
15
21
  runtime = registry.runtime
16
22
 
23
+ @registry.method("runtime.agent_mutation", requires_agent=False)
24
+ def agent_mutation(ctx: CallContext, params: dict) -> dict:
25
+ from ..agent_mutation import call
26
+ return call(runtime, ctx, params)
27
+
28
+ @registry.method("runtime.agent_recovery", requires_agent=False)
29
+ def agent_recovery(ctx: CallContext, params: dict) -> dict:
30
+ from ..agent_mutation import recover_retained
31
+ return recover_retained(runtime, ctx, probe=params.get('probe') is True, complete_revision=params.get('complete_revision'))
32
+
33
+ @registry.method("runtime.sync_dependencies", requires_agent=False)
34
+ def sync_dependencies(ctx: CallContext, params: dict) -> dict:
35
+ # Source has already advanced; loading it or checking the old revision would prevent repair.
36
+ expected = params.get("agent_dir")
37
+ root = runtime.agent_dir
38
+ if not isinstance(expected, str) or not expected or root is None or Path(expected).resolve() != root.resolve():
39
+ raise InvalidParams("agent_dir must match the sidecar's configured Agent checkout")
40
+ if not (root / "hermes_cli" / "venv_sync.py").is_file():
41
+ return {"ok": True, "skipped": True, "detail": ""}
42
+ env = _frozen_launch_env()
43
+ env.update(HERMES_HOME=str(runtime.hermes_home), HERMES_DISABLE_LAZY_INSTALLS="1",
44
+ PYTHONPATH=str(root) + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else ""))
45
+ # The supervisor's stdin is a lifetime lease: closing it also covers a killed sidecar.
46
+ with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr:
47
+ try:
48
+ proc = subprocess.Popen(
49
+ [sys.executable, str(Path(__file__).parents[1] / "dependency_sync.py"), str(root)],
50
+ cwd=root, env=env, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr,
51
+ )
52
+ except OSError as exc:
53
+ return {"ok": False, "skipped": False, "detail": str(exc)}
54
+ deadline = time.monotonic() + 300
55
+ try:
56
+ while proc.poll() is None:
57
+ ctx.check_cancelled()
58
+ if time.monotonic() >= deadline:
59
+ return {"ok": False, "skipped": False, "detail": "Agent dependency sync timed out."}
60
+ ctx.cancel_event.wait(0.05)
61
+ finally:
62
+ proc.stdin.close()
63
+ proc.wait() # The supervisor terminates and reaps its writer before releasing the lock.
64
+ stdout.seek(0)
65
+ output = stdout.read(4 * 1024 * 1024 + 1)
66
+ stderr.seek(0, os.SEEK_END)
67
+ stderr.seek(max(0, stderr.tell() - 4000))
68
+ diagnostic = stderr.read().decode("utf-8", errors="replace")
69
+ if proc.returncode != 0:
70
+ return {"ok": False, "skipped": False, "detail": diagnostic or output[-4000:].decode("utf-8", errors="replace") or "Agent dependency sync failed."}
71
+ try:
72
+ if len(output) > 4 * 1024 * 1024:
73
+ raise ValueError("sync output exceeds limit")
74
+ response = json.loads(output)
75
+ if not isinstance(response, dict):
76
+ raise ValueError("not an object")
77
+ except ValueError:
78
+ return {"ok": False, "skipped": False, "detail": "Agent dependency sync returned an invalid result."}
79
+ return {"ok": response.get("ok") is True, "skipped": False,
80
+ "detail": str(response.get("detail") or "")[-4000:]}
81
+
17
82
  @registry.method("runtime.handshake", requires_agent=False)
18
83
  def handshake(ctx: CallContext, params: dict) -> dict:
19
84
  requested = params.get("rpc_version")
@@ -50,6 +115,7 @@ def register(registry) -> None:
50
115
  raise InvalidParams("set must map variable names to strings")
51
116
  if not isinstance(to_unset, list) or not all(isinstance(k, str) and names.match(k) for k in to_unset):
52
117
  raise InvalidParams("unset must list variable names")
118
+ to_set = {**to_set, "HERMES_DISABLE_LAZY_INSTALLS": "1"}
53
119
  for name in to_unset:
54
120
  os.environ.pop(name, None)
55
121
  for name, value in to_set.items():
@@ -67,7 +133,9 @@ def register(registry) -> None:
67
133
  if not isinstance(code, int):
68
134
  raise InvalidParams("exit_code must be an integer")
69
135
  from .chat import drain_agents
136
+ from ..agent_mutation import close_all
70
137
 
138
+ close_all()
71
139
  drain_agents()
72
140
  ctx.server.request_shutdown(exit_code=code)
73
141
  return {"ok": True}
@@ -54,6 +54,25 @@ def _category(skill_md: Path, dirs: list[Path], local: Path) -> str | None:
54
54
 
55
55
 
56
56
  def list_skills(profile_home: Path, category: str | None = None) -> dict:
57
+ try:
58
+ from agent.skill_utils import get_disabled_skill_names, is_disabled_entry
59
+ from tools.skills_tool import _skill_catalog, _sort_skills
60
+ except ImportError:
61
+ return _legacy_list_skills(profile_home, category)
62
+ disabled = get_disabled_skill_names("webui")
63
+ # Keep disabled rows manageable, but never advertise shadowed or unloadable copies.
64
+ out = [{"name": entry["load_name"], "load_name": entry["load_name"],
65
+ "declared_name": entry["name"], "status": entry["status"], "tier": entry["tier"],
66
+ "description": entry.get("description") or "", "category": entry.get("category"),
67
+ "disabled": is_disabled_entry(entry, disabled)}
68
+ for entry in _skill_catalog(skip_disabled=True) if entry["load_name"]]
69
+ if category:
70
+ out = [s for s in out if s.get("category") == category]
71
+ out = _sort_skills(out)
72
+ return {"success": True, "skills": out, "categories": sorted({s["category"] for s in out if s.get("category")}), "count": len(out)}
73
+
74
+
75
+ def _legacy_list_skills(profile_home: Path, category: str | None = None) -> dict:
57
76
  skills_dir = profile_home / "skills"
58
77
  try:
59
78
  from agent.skill_utils import iter_skill_index_files
@@ -99,6 +118,27 @@ def list_skills(profile_home: Path, category: str | None = None) -> dict:
99
118
 
100
119
 
101
120
  def find_skill(name: str, dirs: list[Path]) -> tuple[Path | None, Path | None]:
121
+ try:
122
+ from agent.skill_utils import resolve_skill_catalog # noqa: F401 - capability gate for the matching lookup API
123
+ from tools.skills_tool import _locate_skill, _skill_lookup_path_error, _skill_search_dirs
124
+ except ImportError:
125
+ return _legacy_find_skill(name, dirs)
126
+ if error := _skill_lookup_path_error(name):
127
+ raise InvalidParams(error)
128
+ local_name = name.replace(":", "/", 1) if ":" in name else None
129
+ if local_name and (error := _skill_lookup_path_error(local_name)):
130
+ raise InvalidParams(error)
131
+ roots, _ = _skill_search_dirs()
132
+ error, skill_dir, skill_md = _locate_skill(name, local_name, roots)
133
+ if error:
134
+ result = json.loads(error)
135
+ if result.get("matches"):
136
+ raise InvalidParams(result["error"])
137
+ return None, None
138
+ return skill_dir, skill_md
139
+
140
+
141
+ def _legacy_find_skill(name: str, dirs: list[Path]) -> tuple[Path | None, Path | None]:
102
142
  from agent.skill_utils import iter_skill_index_files
103
143
  from tools.skills_tool import _EXCLUDED_SKILL_DIRS, _parse_frontmatter
104
144
 
@@ -208,9 +248,15 @@ def view_from_file(skill_dir: Path | None, skill_md: Path) -> dict:
208
248
  def view_skill(profile_home: Path, name: str) -> dict:
209
249
  skills_dir = profile_home / "skills"
210
250
  dirs = search_dirs(skills_dir)
211
- skill_dir, skill_md = find_skill(name, dirs)
251
+ try:
252
+ skill_dir, skill_md = find_skill(name, dirs)
253
+ except InvalidParams as exc:
254
+ return {"success": False, "error": str(exc)}
212
255
  if skill_md:
213
- return view_from_file(skill_dir, skill_md)
256
+ result = view_from_file(skill_dir, skill_md)
257
+ if result.get("success"):
258
+ result["editable"] = skill_md.name == "SKILL.md" and _within(skills_dir, skill_md)
259
+ return result
214
260
  if ":" in str(name or ""):
215
261
  try:
216
262
  from agent.skill_utils import is_valid_namespace, parse_qualified_name
@@ -223,7 +269,10 @@ def view_skill(profile_home: Path, name: str) -> dict:
223
269
  pm = get_plugin_manager()
224
270
  if pm.find_plugin_skill(name) is not None or pm.list_plugin_skills(namespace):
225
271
  raw = skill_view(name)
226
- return json.loads(raw) if isinstance(raw, str) else raw
272
+ result = json.loads(raw) if isinstance(raw, str) else raw
273
+ if result.get("success"):
274
+ result["editable"] = False
275
+ return result
227
276
  except Exception: # noqa: BLE001
228
277
  pass
229
278
  names = [s["name"] for s in list_skills(profile_home).get("skills", [])]
@@ -259,4 +308,18 @@ def register(registry) -> None:
259
308
  raise InvalidParams("name is required")
260
309
  with scoped_home(home):
261
310
  skill_dir, skill_md = find_skill(name, search_dirs(home / "skills"))
262
- return {"found": skill_md is not None, "skill_dir": str(skill_dir) if skill_dir else None, "skill_md": str(skill_md) if skill_md else None}
311
+ identity = {}
312
+ if skill_md:
313
+ try:
314
+ from tools.skills_tool import _skill_catalog
315
+ except ImportError:
316
+ pass
317
+ else:
318
+ catalog = _skill_catalog(skip_disabled=True, include_hidden=True)
319
+ entry = next((e for e in catalog if e["path"] == skill_md), None)
320
+ if entry is not None:
321
+ if not entry["load_name"]:
322
+ raise InvalidParams("Skill has no unambiguous catalog identity")
323
+ identity = {"load_name": entry["load_name"], "declared_name": entry["name"],
324
+ "sibling_load_names": [e["load_name"] for e in catalog if e["name"] == entry["name"] and e["load_name"]]}
325
+ return {"found": skill_md is not None, "skill_dir": str(skill_dir) if skill_dir else None, "skill_md": str(skill_md) if skill_md else None, **identity}