@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
@@ -7,12 +7,13 @@
7
7
  * against repo-local config, and bounded by a timeout.
8
8
  */
9
9
  import { execFile } from 'node:child_process';
10
- import { closeSync, existsSync, fstatSync, lstatSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs';
11
- import { tmpdir } from 'node:os';
10
+ import { closeSync, constants as fsConstants, existsSync, fstatSync, futimesSync, lstatSync, mkdtempSync, mkdirSync, openSync, readdirSync, readFileSync, readSync, realpathSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
11
+ import { homedir, tmpdir } from 'node:os';
12
12
  import { dirname, join, relative, resolve, sep } from 'node:path';
13
- import { isWithin, resolvePathLikePython } from './paths.js';
13
+ import { expandHome, isWithin, resolvePathLikePython } from './paths.js';
14
14
  import { openAnchoredFd, rmtreeAnchored, safeResolveWs, unlinkAnchored } from './fs.js';
15
15
  import { str } from '../util.js';
16
+ import { diffHunks, hunkTotals } from '../text/diff.js';
16
17
  export const GIT_TIMEOUT_MS = 5_000;
17
18
  export const GIT_REMOTE_TIMEOUT_MS = 60_000;
18
19
  export const STATUS_FILE_LIMIT = 500;
@@ -22,8 +23,9 @@ export const DIFF_SIZE_LIMIT = 512 * 1024;
22
23
  export const COMMIT_MESSAGE_DIFF_LIMIT = 64 * 1024;
23
24
  export const WORKSPACE_GIT_DESTRUCTIVE_ENV = 'HERMES_WEBUI_WORKSPACE_GIT_DESTRUCTIVE';
24
25
  export const WORKSPACE_BUSY_MESSAGE = 'A Git operation is running in this workspace.';
26
+ const ACTIVE_RUN_MESSAGE = 'A session run is active. Wait for it to finish before running this Git operation.';
25
27
  export const WORKSPACE_WRITE_BUSY_MESSAGE = 'A file operation is running in this workspace.';
26
- const GIT_ENV_SCRUB_KEYS = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_CONFIG_GLOBAL', 'GIT_CONFIG_SYSTEM', 'GIT_CONFIG_COUNT', 'GIT_CONFIG_PARAMETERS', 'GIT_ASKPASS', 'SSH_ASKPASS', 'GIT_SSH', 'GIT_SSH_COMMAND'];
28
+ const GIT_ENV_SCRUB_KEYS = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_COMMON_DIR', 'GIT_CONFIG', 'GIT_CONFIG_GLOBAL', 'GIT_CONFIG_SYSTEM', 'GIT_CONFIG_COUNT', 'GIT_CONFIG_PARAMETERS', 'GIT_ASKPASS', 'SSH_ASKPASS', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_GLOB_PATHSPECS', 'GIT_NOGLOB_PATHSPECS', 'GIT_ICASE_PATHSPECS'];
27
29
  const GIT_ENV_SCRUB_PREFIXES = ['GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_'];
28
30
  const BRANCH_SWITCH_STASH_PREFIX = 'hermes-webui branch switch';
29
31
  const GIT_HARDENED_CONFIG = [
@@ -45,14 +47,43 @@ const GIT_DESTRUCTIVE_HARDENED_CONFIG = [
45
47
  ['core.alternateRefsCommand', ''],
46
48
  ];
47
49
  const CONFLICT_CODES = new Set(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
50
+ const STATUS_CONFIG_KEYS = /^(core\.(autocrlf|eol|filemode|ignorecase|symlinks|ignorestat|checkstat|trustctime|protecthfs|protectntfs|precomposeunicode|excludesfile|attributesfile|quotepath)|(?:diff|status)\.(renames|renamelimit))$/;
48
51
  export class GitWorkspaceError extends Error {
49
52
  code;
50
- constructor(message, code = 'git_failed') {
51
- super(message);
53
+ constructor(message, code = 'git_failed', options) {
54
+ super(message, options);
52
55
  this.code = code;
53
56
  this.name = 'GitWorkspaceError';
54
57
  }
55
58
  }
59
+ function gitDeadline(timeoutMs) {
60
+ const deadline = Date.now() + timeoutMs;
61
+ return () => {
62
+ const ms = deadline - Date.now();
63
+ if (ms <= 0)
64
+ throw new GitWorkspaceError('Git command timed out', 'timeout', { cause: Object.assign(new Error('git timed out'), { code: 'ETIMEDOUT' }) });
65
+ return ms;
66
+ };
67
+ }
68
+ /** Python `git_info_for_workspace`: the branch badge for a status, null outside a repo. */
69
+ export function gitBadge(status) {
70
+ if (!status.is_git)
71
+ return null;
72
+ const totals = status.totals ?? { changed: 0, staged: 0, unstaged: 0, untracked: 0, conflicts: 0 };
73
+ return { branch: status.branch ?? 'HEAD', dirty: totals.changed, modified: totals.staged + totals.unstaged, untracked: totals.untracked, ahead: status.ahead ?? 0, behind: status.behind ?? 0, is_git: true };
74
+ }
75
+ /** Porcelain v1 records retain their second NUL field for a rename/copy's source path. */
76
+ export function porcelainStatusEntries(output) {
77
+ const fields = output.split('\0');
78
+ const entries = [];
79
+ for (let i = 0; i < fields.length; i++) {
80
+ const record = fields[i];
81
+ if (!record)
82
+ continue;
83
+ entries.push(/[RC]/.test(record.slice(0, 2)) ? [record, fields[++i] ?? ''] : [record]);
84
+ }
85
+ return entries;
86
+ }
56
87
  /**
57
88
  * Run `git` without blocking the event loop, resolving like `spawnSync`: the exit status (null when a signal ended it),
58
89
  * plus the `error` that prevented a normal run (`ETIMEDOUT` when the timeout killed it). Without `env` the child
@@ -121,7 +152,7 @@ export function classifyGitError(message, args = []) {
121
152
  return 'timeout';
122
153
  if (text.includes('not installed') || text.includes("no such file or directory: 'git'"))
123
154
  return 'missing_git';
124
- if (text.includes('not a git repository'))
155
+ if (text.includes('not a git repository') || text.includes('can only be used inside a git repository'))
125
156
  return 'not_a_repo';
126
157
  if (text.includes('outside the workspace') || text.includes('outside the git repository'))
127
158
  return 'path_outside_workspace';
@@ -150,18 +181,11 @@ function unifiedDiffFromEmpty(lines, toFile) {
150
181
  // `difflib` writes `+1` for a single-line range and `+1,N` otherwise.
151
182
  return [`--- /dev/null`, `+++ ${toFile}`, `@@ -0,0 +${lines.length === 1 ? '1' : `1,${String(lines.length)}`} @@`, ...lines.map((l) => `+${l}`)];
152
183
  }
153
- function diffStats(diff) {
154
- let additions = 0;
155
- let deletions = 0;
156
- for (const line of diff.split('\n')) {
157
- if (line.startsWith('+++') || line.startsWith('---'))
158
- continue;
159
- if (line.startsWith('+'))
160
- additions += 1;
161
- else if (line.startsWith('-'))
162
- deletions += 1;
163
- }
164
- return [additions, deletions];
184
+ /** A diff's counts and its parsed hunks (TAL-604): a too-large diff's cut text still counts, but ships no hunks. */
185
+ function diffFields(diff, shown) {
186
+ const hunks = diffHunks(diff);
187
+ const [additions, deletions] = hunkTotals(hunks);
188
+ return { additions, deletions, hunks: shown ? hunks : [] };
165
189
  }
166
190
  function splitLines(text) {
167
191
  // Python `str.splitlines()`: no trailing empty element for a final newline.
@@ -193,6 +217,17 @@ export class GitRunner {
193
217
  workspaceBusy(path) {
194
218
  return overlapsAny(this.busy, path);
195
219
  }
220
+ /**
221
+ * A checkpoint diff or restore at `path` (TAL-913): refused while Git holds it or a run is active there, since the
222
+ * Agent's per-turn checkpoint capture stages into the same store index, and held busy so no run starts meanwhile.
223
+ */
224
+ holdCheckpoint(path, fn) {
225
+ if (this.workspaceBusy(path))
226
+ throw new GitWorkspaceError(WORKSPACE_BUSY_MESSAGE, 'operation_in_progress');
227
+ if (this.deps.activeRunIn?.(path))
228
+ throw new GitWorkspaceError(ACTIVE_RUN_MESSAGE, 'active_stream');
229
+ return this.holdWorkspace(path, fn);
230
+ }
196
231
  /** Run an asynchronous workspace writer at `path`: refused while Git holds it, and Git mutations refuse meanwhile. */
197
232
  holdWrite(path, fn) {
198
233
  if (this.workspaceBusy(path))
@@ -222,6 +257,8 @@ export class GitRunner {
222
257
  if (GIT_ENV_SCRUB_PREFIXES.some((p) => key.startsWith(p)))
223
258
  Reflect.deleteProperty(env, key);
224
259
  env.GIT_TERMINAL_PROMPT = '0';
260
+ // Every pathspec here is a resolved file path; a glob reading would let `a\b.txt` also match `ab.txt`.
261
+ env.GIT_LITERAL_PATHSPECS = '1';
225
262
  return env;
226
263
  }
227
264
  now() {
@@ -235,7 +272,7 @@ export class GitRunner {
235
272
  if (ignoreUnsupported)
236
273
  return names;
237
274
  const message = (result.stderr || result.stdout || 'Git command failed').trim();
238
- throw new GitWorkspaceError(message, classifyGitError(message, ['config']));
275
+ throw new GitWorkspaceError(message, classifyGitError(message, ['config']), { cause: result.error });
239
276
  }
240
277
  for (const line of (result.stdout || '').split('\n')) {
241
278
  const m = nameRe.exec(line.trim());
@@ -265,9 +302,267 @@ export class GitRunner {
265
302
  async hasRepoLocalFilters(cwd) {
266
303
  return (await this.filterNames(cwd, this.cleanEnv())).size > 0;
267
304
  }
305
+ /** Hold the opened inode and reject devices/FIFOs before reading repository-controlled metadata. */
306
+ static withStatusFile(path, read) {
307
+ let fd;
308
+ try {
309
+ fd = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0));
310
+ }
311
+ catch (error) {
312
+ if (error.code === 'ENOENT')
313
+ return null;
314
+ throw error;
315
+ }
316
+ try {
317
+ const stat = fstatSync(fd, { bigint: true });
318
+ if (!stat.isFile())
319
+ throw new GitWorkspaceError('Git status metadata must be a regular file', 'git_failed');
320
+ return read(fd, Number(stat.size), stat.mtimeNs);
321
+ }
322
+ finally {
323
+ closeSync(fd);
324
+ }
325
+ }
326
+ /** A private metadata view never loads executable repository/global configuration, even if the originals change. */
327
+ async withStatusRepository(cwd, remaining, read) {
328
+ const location = await this.run(cwd, ['rev-parse', '--absolute-git-dir', '--verify', 'HEAD'], { timeoutMs: remaining() });
329
+ const output = location.stdout.replace(/\r?\n$/, '');
330
+ const split = output.lastIndexOf('\n');
331
+ const gitDir = location.status === 0 ? output.slice(0, split).replace(/\r$/, '') : output;
332
+ const head = location.status === 0 ? output.slice(split + 1) : 'ref: refs/heads/status-probe';
333
+ if (!gitDir || (location.status !== 0 && location.status !== 128) || (location.status === 0 && !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(head))) {
334
+ const message = (location.stderr || 'Git repository metadata could not be read').trim();
335
+ throw new GitWorkspaceError(message, classifyGitError(message, ['rev-parse']));
336
+ }
337
+ let common = gitDir;
338
+ const commonPath = GitRunner.withStatusFile(join(gitDir, 'commondir'), (fd, size) => {
339
+ if (size > DIFF_SIZE_LIMIT)
340
+ throw new GitWorkspaceError('Git common directory path is too large', 'git_failed');
341
+ const bytes = Buffer.alloc(DIFF_SIZE_LIMIT + 1);
342
+ let offset = 0;
343
+ while (offset < bytes.length) {
344
+ remaining();
345
+ const count = readSync(fd, bytes, offset, bytes.length - offset, null);
346
+ if (!count)
347
+ break;
348
+ offset += count;
349
+ }
350
+ if (offset > DIFF_SIZE_LIMIT)
351
+ throw new GitWorkspaceError('Git common directory path is too large', 'git_failed');
352
+ return bytes.subarray(0, offset).toString('utf8');
353
+ });
354
+ if (commonPath !== null)
355
+ common = resolve(gitDir, commonPath.replace(/\r?\n$/, ''));
356
+ const config = await this.run(cwd, ['-c', `include.path=${join(cwd, '.gitmodules')}`, 'config', '--includes', '--null', '--list', '--show-scope'], { check: true, timeoutMs: remaining() });
357
+ const values = new Map();
358
+ const modules = new Map();
359
+ const entries = config.stdout.split('\0');
360
+ for (let i = 0; i + 1 < entries.length; i += 2) {
361
+ const scope = entries[i];
362
+ const entry = entries[i + 1] ?? '';
363
+ const split = entry.indexOf('\n');
364
+ if (split < 0)
365
+ continue;
366
+ const key = entry.slice(0, split);
367
+ if (scope === 'command') {
368
+ if (/^submodule\..+\.(path|ignore)$/.test(key))
369
+ modules.set(key, entry.slice(split + 1));
370
+ }
371
+ else
372
+ values.set(key, entry.slice(split + 1));
373
+ }
374
+ const ignores = new Map();
375
+ for (const [key, path] of modules) {
376
+ if (!key.endsWith('.path'))
377
+ continue;
378
+ const ignore = values.get(`${key.slice(0, -5)}.ignore`) ?? modules.get(`${key.slice(0, -5)}.ignore`) ?? values.get('diff.ignoresubmodules') ?? 'none';
379
+ if (!['none', 'untracked', 'dirty', 'all'].includes(ignore))
380
+ throw new GitWorkspaceError('Invalid submodule ignore setting', 'git_failed');
381
+ ignores.set(path, ignore);
382
+ }
383
+ const root = mkdtempSync(join(tmpdir(), 'talaria-git-status-'));
384
+ try {
385
+ const objectFormat = values.get('extensions.objectformat') ?? 'sha1';
386
+ if (!['sha1', 'sha256'].includes(objectFormat))
387
+ throw new GitWorkspaceError('Unsupported Git repository format', 'git_failed');
388
+ mkdirSync(join(root, 'info'));
389
+ mkdirSync(join(root, 'refs'));
390
+ writeFileSync(join(root, 'config'), `[core]\nrepositoryFormatVersion = ${objectFormat === 'sha256' ? '1' : '0'}\nbare = false\n${objectFormat === 'sha256' ? '[extensions]\nobjectFormat = sha256\n' : ''}`);
391
+ writeFileSync(join(root, 'HEAD'), `${head}\n`);
392
+ const copy = (directory, file) => {
393
+ GitRunner.withStatusFile(join(directory, file), (source, _size, mtimeNs) => {
394
+ const target = openSync(join(root, file), 'wx', 0o600);
395
+ try {
396
+ const chunk = Buffer.allocUnsafe(64 * 1024);
397
+ for (;;) {
398
+ remaining();
399
+ const bytes = readSync(source, chunk, 0, chunk.length, null);
400
+ if (!bytes)
401
+ break;
402
+ writeFileSync(target, chunk.subarray(0, bytes));
403
+ }
404
+ if (file === 'index' || file.startsWith('sharedindex.')) {
405
+ // A newer index timestamp would suppress Git's racy-stat content check; round conservatively earlier.
406
+ let margin = 1;
407
+ do {
408
+ remaining();
409
+ const stamp = new Date(Number(mtimeNs / 1000000n) - margin);
410
+ futimesSync(target, stamp, stamp);
411
+ margin *= 2;
412
+ } while (fstatSync(target, { bigint: true }).mtimeNs > mtimeNs);
413
+ }
414
+ }
415
+ finally {
416
+ closeSync(target);
417
+ }
418
+ });
419
+ };
420
+ for (const file of ['index', ...readdirSync(gitDir).filter((name) => /^sharedindex\.[a-f0-9]+$/.test(name))]) {
421
+ copy(gitDir, file);
422
+ }
423
+ for (const file of ['packed-refs', 'shallow', 'info/attributes', 'info/exclude', 'info/sparse-checkout']) {
424
+ copy(common, file);
425
+ }
426
+ symlinkSync(join(common, 'objects'), join(root, 'objects'), process.platform === 'win32' ? 'junction' : 'dir');
427
+ const originalEnv = this.cleanEnv();
428
+ const home = originalEnv.HOME || homedir();
429
+ const xdg = originalEnv.XDG_CONFIG_HOME || join(home, '.config');
430
+ if (!values.has('core.attributesfile'))
431
+ values.set('core.attributesfile', join(xdg, 'git', 'attributes'));
432
+ if (!values.has('core.excludesfile'))
433
+ values.set('core.excludesfile', join(xdg, 'git', 'ignore'));
434
+ const prefix = [`--git-dir=${root}`, `--work-tree=${cwd}`];
435
+ for (const [key, raw] of values) {
436
+ if (!STATUS_CONFIG_KEYS.test(key))
437
+ continue;
438
+ const value = key === 'core.attributesfile' || key === 'core.excludesfile' ? resolve(cwd, expandHome(raw, home)) : raw;
439
+ prefix.push('-c', `${key}=${value}`);
440
+ }
441
+ return await read(prefix, { HOME: root, XDG_CONFIG_HOME: root, GIT_CONFIG_NOSYSTEM: '1', GIT_OPTIONAL_LOCKS: '0', GIT_INDEX_FILE: join(root, 'index'), GIT_OBJECT_DIRECTORY: join(root, 'objects'), GIT_ALTERNATE_OBJECT_DIRECTORIES: '' }, ignores, { gitDir, commonDir: common, privateDir: root, refStorage: values.get('extensions.refstorage') ?? 'files' });
442
+ }
443
+ finally {
444
+ rmSync(root, { recursive: true, force: true });
445
+ }
446
+ }
447
+ /** Worktree lifecycle probes need only porcelain v1; child worktrees are inspected through the same private view. */
448
+ async worktreeStatus(cwd, timeoutMs = GIT_TIMEOUT_MS) {
449
+ const remaining = gitDeadline(timeoutMs);
450
+ const visited = new Set();
451
+ const inspect = async (path) => {
452
+ visited.add(resolvePathLikePython(path));
453
+ return this.withStatusRepository(path, remaining, async (prefix, env, ignores) => {
454
+ const run = (args) => this.run(path, [...prefix, ...args], { check: true, timeoutMs: remaining(), env });
455
+ // Commit differences remain visible; Git must not run a child status against live submodule configuration.
456
+ const result = await run(['status', '--porcelain', '-z', '--untracked-files=normal', '--ignore-submodules=dirty']);
457
+ const index = await run(['ls-files', '--stage', '-z']);
458
+ const gitlinks = index.stdout.split('\0').filter((entry) => entry.startsWith('160000 '));
459
+ const records = porcelainStatusEntries(result.stdout);
460
+ for (let i = records.length - 1; i >= 0; i--) {
461
+ const record = records[i]?.[0] ?? '';
462
+ if (ignores.get(record.slice(3)) !== 'all' || record.startsWith('??'))
463
+ continue;
464
+ if (record.startsWith(' '))
465
+ records.splice(i, 1);
466
+ else {
467
+ const fields = records[i];
468
+ if (fields)
469
+ fields[0] = `${record[0]} ${record.slice(2)}`;
470
+ }
471
+ }
472
+ result.stdout = records.length ? `${records.flat().join('\0')}\0` : '';
473
+ let trackedDirty = records.some(([record]) => !record?.startsWith('??'));
474
+ let untrackedDirty = records.some(([record]) => record?.startsWith('??'));
475
+ for (const entry of gitlinks) {
476
+ const match = /^160000 [a-f0-9]+ [0-3]\t(.+)$/s.exec(entry);
477
+ if (!match?.[1])
478
+ continue;
479
+ const ignore = ignores.get(match[1]) ?? 'none';
480
+ if (ignore === 'all' || ignore === 'dirty')
481
+ continue;
482
+ const child = safeResolveWs(path, match[1]);
483
+ if (visited.has(child) || !existsSync(join(child, '.git')))
484
+ continue;
485
+ const childStatus = await inspect(child);
486
+ const untracked = ignore !== 'untracked' && childStatus.untrackedDirty;
487
+ if (childStatus.trackedDirty || untracked)
488
+ result.stdout += ` M ${match[1]}\0`;
489
+ trackedDirty ||= childStatus.trackedDirty;
490
+ untrackedDirty ||= untracked;
491
+ }
492
+ return { ...result, hasGitlinks: gitlinks.length > 0, trackedDirty, untrackedDirty };
493
+ });
494
+ };
495
+ const { status, stdout, stderr, hasGitlinks } = await inspect(cwd);
496
+ return { status, stdout, stderr, hasGitlinks };
497
+ }
498
+ /** Keep Git's own final clean check, but make its inherited configuration private and non-executable. */
499
+ async removeWorktree(repoRoot, worktree, timeoutMs = GIT_TIMEOUT_MS) {
500
+ const remaining = gitDeadline(timeoutMs);
501
+ const target = resolvePathLikePython(worktree);
502
+ const listed = await this.run(repoRoot, ['worktree', 'list', '--porcelain', '-z'], { check: true, timeoutMs: remaining() });
503
+ const paths = listed.stdout.split('\0').filter((field) => field.startsWith('worktree ')).map((field) => resolvePathLikePython(field.slice(9)));
504
+ if (paths[0] === target || !paths.slice(1).includes(target))
505
+ throw new GitWorkspaceError('Not a registered linked worktree', 'git_failed');
506
+ const callerCommon = (await this.run(repoRoot, ['rev-parse', '--path-format=absolute', '--git-common-dir'], { check: true, timeoutMs: remaining() })).stdout.replace(/\r?\n$/, '');
507
+ const gitDir = (await this.run(target, ['rev-parse', '--absolute-git-dir'], { check: true, timeoutMs: remaining() })).stdout.replace(/\r?\n$/, '');
508
+ const registryRoot = join(callerCommon, 'worktrees');
509
+ if (dirname(resolvePathLikePython(gitDir)) !== resolvePathLikePython(registryRoot))
510
+ throw new GitWorkspaceError('Worktree Git directory does not belong to the repository', 'git_failed');
511
+ const lockPath = join(gitDir, 'index.lock');
512
+ let lock;
513
+ try {
514
+ lock = openSync(lockPath, 'wx', 0o600);
515
+ }
516
+ catch (error) {
517
+ if (error.code === 'EEXIST')
518
+ throw new GitWorkspaceError('The worktree index is locked by another Git operation', 'operation_in_progress');
519
+ throw error;
520
+ }
521
+ let identity;
522
+ try {
523
+ identity = fstatSync(lock);
524
+ return await this.withStatusRepository(target, remaining, async (prefix, env, _ignores, metadata) => {
525
+ if (metadata.gitDir !== gitDir || resolvePathLikePython(metadata.commonDir) !== resolvePathLikePython(callerCommon))
526
+ throw new GitWorkspaceError('Worktree metadata changed during removal', 'operation_in_progress');
527
+ const entries = await this.run(worktree, [...prefix, 'ls-files', '--stage', '-z'], { check: true, timeoutMs: remaining(), env });
528
+ // An uninitialized gitlink could become populated between Git's submodule guard and its internal status.
529
+ if (entries.stdout.split('\0').some((entry) => entry.startsWith('160000 ')))
530
+ return { status: 128, stdout: '', stderr: 'Worktrees containing submodules require force=true to remove' };
531
+ const linkType = process.platform === 'win32' ? 'junction' : 'dir';
532
+ rmdirSync(join(metadata.privateDir, 'refs'));
533
+ if (existsSync(join(metadata.commonDir, 'refs')))
534
+ symlinkSync(join(metadata.commonDir, 'refs'), join(metadata.privateDir, 'refs'), linkType);
535
+ else
536
+ mkdirSync(join(metadata.privateDir, 'refs'));
537
+ if (metadata.refStorage === 'reftable') {
538
+ writeFileSync(join(metadata.privateDir, 'config'), '\n[core]\nrepositoryFormatVersion = 1\n[extensions]\nrefStorage = reftable\n', { flag: 'a' });
539
+ symlinkSync(join(metadata.commonDir, 'reftable'), join(metadata.privateDir, 'reftable'), linkType);
540
+ }
541
+ else if (metadata.refStorage !== 'files')
542
+ throw new GitWorkspaceError('Unsupported Git reference format', 'git_failed');
543
+ const registry = join(metadata.privateDir, 'worktrees');
544
+ mkdirSync(registry);
545
+ symlinkSync(gitDir, join(registry, 'target'), linkType);
546
+ return this.run(repoRoot, [...prefix, 'worktree', 'remove', worktree], { timeoutMs: remaining(), env, commonDir: metadata.privateDir });
547
+ });
548
+ }
549
+ finally {
550
+ try {
551
+ const current = lstatSync(lockPath);
552
+ if (current.dev === identity?.dev && current.ino === identity.ino)
553
+ unlinkSync(lockPath);
554
+ }
555
+ catch { /* Git may already have removed its administrative directory */ }
556
+ finally {
557
+ closeSync(lock);
558
+ }
559
+ }
560
+ }
268
561
  async run(ctxOrCwd, args, opts = {}) {
269
562
  const cwd = typeof ctxOrCwd === 'string' ? ctxOrCwd : ctxOrCwd.repoRoot;
270
563
  const runEnv = this.cleanEnv(opts.env);
564
+ if (opts.commonDir)
565
+ runEnv.GIT_COMMON_DIR = opts.commonDir;
271
566
  const effectiveDestructive = Boolean(opts.destructive) && this.destructiveEnabled();
272
567
  const hardenedDestructive = effectiveDestructive || Boolean(opts.forceDestructiveHardening);
273
568
  let attributesFile = null;
@@ -333,10 +628,10 @@ export class GitRunner {
333
628
  if (result.error) {
334
629
  const err = result.error;
335
630
  if (err.code === 'ETIMEDOUT')
336
- throw new GitWorkspaceError('Git command timed out', 'timeout');
631
+ throw new GitWorkspaceError('Git command timed out', 'timeout', { cause: err });
337
632
  if (err.code === 'ENOENT')
338
- throw new GitWorkspaceError('Git is not installed or not available on PATH', 'missing_git');
339
- throw new GitWorkspaceError(err.message, classifyGitError(err.message, args));
633
+ throw new GitWorkspaceError('Git is not installed or not available on PATH', 'missing_git', { cause: err });
634
+ throw new GitWorkspaceError(err.message, classifyGitError(err.message, args), { cause: err });
340
635
  }
341
636
  const out = { status: result.status ?? 1, stdout: result.stdout, stderr: result.stderr };
342
637
  if (opts.check && out.status !== 0) {
@@ -394,15 +689,15 @@ export class GitRunner {
394
689
  const rel = relative(ctx.repoRoot, target).split(sep).join('/');
395
690
  return rel || '.';
396
691
  }
692
+ /** Git and `repoRel` both separate with `/`; a backslash is a literal POSIX filename character, never a separator. */
397
693
  static workspaceRel(ctx, repoRel) {
398
- const normalized = repoRel.replace(/\\/g, '/');
399
694
  if (!ctx.workspacePrefix)
400
- return normalized;
695
+ return repoRel;
401
696
  const prefix = `${ctx.workspacePrefix.replace(/\/+$/, '')}/`;
402
- if (normalized === ctx.workspacePrefix)
697
+ if (repoRel === ctx.workspacePrefix)
403
698
  return '.';
404
- if (normalized.startsWith(prefix))
405
- return normalized.slice(prefix.length);
699
+ if (repoRel.startsWith(prefix))
700
+ return repoRel.slice(prefix.length);
406
701
  return null;
407
702
  }
408
703
  // ── mutation lock ────────────────────────────────────────────────────────
@@ -420,7 +715,7 @@ export class GitRunner {
420
715
  }
421
716
  assertRepoFree(repoRoot) {
422
717
  if (this.deps.activeRunIn?.(repoRoot))
423
- throw new GitWorkspaceError('A session run is active. Wait for it to finish before running this Git operation.', 'active_stream');
718
+ throw new GitWorkspaceError(ACTIVE_RUN_MESSAGE, 'active_stream');
424
719
  if (overlapsAny(this.writing, repoRoot))
425
720
  throw new GitWorkspaceError(WORKSPACE_WRITE_BUSY_MESSAGE, 'operation_in_progress');
426
721
  }
@@ -1021,18 +1316,17 @@ export class GitRunner {
1021
1316
  if (!read)
1022
1317
  throw new GitWorkspaceError('Path is not a file');
1023
1318
  if ('tooLarge' in read)
1024
- return { binary: false, too_large: true, diff: '', additions: 0, deletions: 0 };
1319
+ return { binary: false, too_large: true, diff: '', additions: 0, deletions: 0, hunks: [] };
1025
1320
  const data = read.data;
1026
1321
  const text = data.toString('utf8');
1027
1322
  if (data.includes(0) || (text.includes('�') && !data.equals(Buffer.from(text, 'utf8'))))
1028
- return { binary: true, too_large: false, diff: '', additions: 0, deletions: 0 };
1323
+ return { binary: true, too_large: false, diff: '', additions: 0, deletions: 0, hunks: [] };
1029
1324
  const diffLines = unifiedDiffFromEmpty(splitLines(text), `b/${label}`);
1030
1325
  let diff = diffLines.join('\n') + (diffLines.length ? '\n' : '');
1031
1326
  const tooLarge = Buffer.byteLength(diff, 'utf8') > DIFF_SIZE_LIMIT;
1032
1327
  if (tooLarge)
1033
1328
  diff = diff.slice(0, DIFF_SIZE_LIMIT);
1034
- const [additions, deletions] = diffStats(diff);
1035
- return { binary: false, too_large: tooLarge, diff, additions, deletions };
1329
+ return { binary: false, too_large: tooLarge, diff, ...diffFields(diff, !tooLarge) };
1036
1330
  }
1037
1331
  async diff(workspace, path, kind = 'unstaged') {
1038
1332
  const ctx = await this.resolveContext(workspace);
@@ -1055,8 +1349,7 @@ export class GitRunner {
1055
1349
  const tooLarge = Buffer.byteLength(diff, 'utf8') > DIFF_SIZE_LIMIT;
1056
1350
  if (tooLarge)
1057
1351
  diff = diff.slice(0, DIFF_SIZE_LIMIT);
1058
- const [additions, deletions] = diffStats(diff);
1059
- return { path: workspaceRel, kind, binary, too_large: tooLarge, additions, deletions, diff: binary ? '' : diff };
1352
+ return { path: workspaceRel, kind, binary, too_large: tooLarge, ...diffFields(diff, !(binary || tooLarge)), diff: binary ? '' : diff };
1060
1353
  }
1061
1354
  // ── stage / unstage / discard ────────────────────────────────────────────
1062
1355
  static cleanPaths(paths) {