@ai-agent-forge/agent-forge 0.86.7 → 0.88.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 (717) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +10 -11
  3. package/dist/artifacts/artifact-store.d.ts.map +1 -1
  4. package/dist/artifacts/artifact-store.js +9 -5
  5. package/dist/artifacts/artifact-store.js.map +1 -1
  6. package/dist/bundle/.build-manifest.json +430 -413
  7. package/dist/bundle/chunks/{chunk-A7GUOX2H.js → chunk-6ZKDNZHA.js} +5 -1
  8. package/dist/bundle/chunks/chunk-HLJEVRWP.js +13 -0
  9. package/dist/bundle/chunks/chunk-YGHQME4V.js +1456 -0
  10. package/dist/bundle/chunks/image-resize-worker.js +12 -1
  11. package/dist/bundle/chunks/{virtual-modules-3IZ4MYSQ.js → virtual-modules-KZ5DRH7Q.js} +1 -1
  12. package/dist/bundle/cli.js +1 -1
  13. package/dist/bundle/client.js +1 -1
  14. package/dist/bundle/index.js +2 -2
  15. package/dist/bundle/rpc-entry.js +1 -1
  16. package/dist/cli/args.d.ts.map +1 -1
  17. package/dist/cli/args.js +231 -185
  18. package/dist/cli/args.js.map +1 -1
  19. package/dist/cli/auth-check.d.ts.map +1 -1
  20. package/dist/cli/auth-check.js +17 -4
  21. package/dist/cli/auth-check.js.map +1 -1
  22. package/dist/cli/auth-command.d.ts.map +1 -1
  23. package/dist/cli/auth-command.js +15 -9
  24. package/dist/cli/auth-command.js.map +1 -1
  25. package/dist/cli/credential-print.d.ts.map +1 -1
  26. package/dist/cli/credential-print.js +14 -7
  27. package/dist/cli/credential-print.js.map +1 -1
  28. package/dist/cli/experimental/command.d.ts.map +1 -1
  29. package/dist/cli/experimental/command.js +6 -5
  30. package/dist/cli/experimental/command.js.map +1 -1
  31. package/dist/cli/list-models.d.ts.map +1 -1
  32. package/dist/cli/list-models.js +9 -8
  33. package/dist/cli/list-models.js.map +1 -1
  34. package/dist/cli/plugin-manager-controller.d.ts.map +1 -1
  35. package/dist/cli/plugin-manager-controller.js +2 -1
  36. package/dist/cli/plugin-manager-controller.js.map +1 -1
  37. package/dist/cli/serve.d.ts.map +1 -1
  38. package/dist/cli/serve.js +8 -5
  39. package/dist/cli/serve.js.map +1 -1
  40. package/dist/client/remote-session.d.ts.map +1 -1
  41. package/dist/client/remote-session.js +16 -9
  42. package/dist/client/remote-session.js.map +1 -1
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +5 -3
  45. package/dist/config.js.map +1 -1
  46. package/dist/core/agent-capability-contract.d.ts +0 -7
  47. package/dist/core/agent-capability-contract.d.ts.map +1 -1
  48. package/dist/core/agent-capability-contract.js +72 -68
  49. package/dist/core/agent-capability-contract.js.map +1 -1
  50. package/dist/core/agent-session-bash.d.ts.map +1 -1
  51. package/dist/core/agent-session-bash.js +5 -4
  52. package/dist/core/agent-session-bash.js.map +1 -1
  53. package/dist/core/agent-session-compaction.d.ts +6 -10
  54. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  55. package/dist/core/agent-session-compaction.js +35 -133
  56. package/dist/core/agent-session-compaction.js.map +1 -1
  57. package/dist/core/agent-session-context.d.ts.map +1 -1
  58. package/dist/core/agent-session-context.js +18 -13
  59. package/dist/core/agent-session-context.js.map +1 -1
  60. package/dist/core/agent-session-internal.d.ts +0 -6
  61. package/dist/core/agent-session-internal.d.ts.map +1 -1
  62. package/dist/core/agent-session-internal.js.map +1 -1
  63. package/dist/core/agent-session-model.d.ts.map +1 -1
  64. package/dist/core/agent-session-model.js +2 -1
  65. package/dist/core/agent-session-model.js.map +1 -1
  66. package/dist/core/agent-session-retry.d.ts.map +1 -1
  67. package/dist/core/agent-session-retry.js +3 -3
  68. package/dist/core/agent-session-retry.js.map +1 -1
  69. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  70. package/dist/core/agent-session-runtime.js +7 -6
  71. package/dist/core/agent-session-runtime.js.map +1 -1
  72. package/dist/core/agent-session-services.d.ts +1 -1
  73. package/dist/core/agent-session-services.d.ts.map +1 -1
  74. package/dist/core/agent-session-services.js.map +1 -1
  75. package/dist/core/agent-session-tools.d.ts.map +1 -1
  76. package/dist/core/agent-session-tools.js +3 -2
  77. package/dist/core/agent-session-tools.js.map +1 -1
  78. package/dist/core/agent-session.d.ts +40 -22
  79. package/dist/core/agent-session.d.ts.map +1 -1
  80. package/dist/core/agent-session.js +147 -67
  81. package/dist/core/agent-session.js.map +1 -1
  82. package/dist/core/auth-guidance.d.ts.map +1 -1
  83. package/dist/core/auth-guidance.js +11 -5
  84. package/dist/core/auth-guidance.js.map +1 -1
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +9 -6
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/compaction/compaction.d.ts.map +1 -1
  89. package/dist/core/compaction/compaction.js +2 -1
  90. package/dist/core/compaction/compaction.js.map +1 -1
  91. package/dist/core/embedded-sdk.d.ts.map +1 -1
  92. package/dist/core/embedded-sdk.js +10 -6
  93. package/dist/core/embedded-sdk.js.map +1 -1
  94. package/dist/core/export-html/index.d.ts.map +1 -1
  95. package/dist/core/export-html/index.js +9 -4
  96. package/dist/core/export-html/index.js.map +1 -1
  97. package/dist/core/extensions/agent-composition.d.ts.map +1 -1
  98. package/dist/core/extensions/agent-composition.js +26 -19
  99. package/dist/core/extensions/agent-composition.js.map +1 -1
  100. package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -1
  101. package/dist/core/extensions/bootstrap-agent-loop.js +3 -2
  102. package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -1
  103. package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +1 -1
  104. package/dist/core/extensions/bootstrap-compaction-policy.js +0 -34
  105. package/dist/core/extensions/bootstrap-compaction-policy.js.map +1 -1
  106. package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
  107. package/dist/core/extensions/bootstrap-lifecycle.js +20 -17
  108. package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
  109. package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +1 -1
  110. package/dist/core/extensions/bootstrap-overflow-policy.js +2 -1
  111. package/dist/core/extensions/bootstrap-overflow-policy.js.map +1 -1
  112. package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +1 -1
  113. package/dist/core/extensions/bootstrap-retry-policy.js +4 -3
  114. package/dist/core/extensions/bootstrap-retry-policy.js.map +1 -1
  115. package/dist/core/extensions/bootstrap-skills.d.ts.map +1 -1
  116. package/dist/core/extensions/bootstrap-skills.js +7 -6
  117. package/dist/core/extensions/bootstrap-skills.js.map +1 -1
  118. package/dist/core/extensions/bootstrap-tools.d.ts.map +1 -1
  119. package/dist/core/extensions/bootstrap-tools.js +2 -1
  120. package/dist/core/extensions/bootstrap-tools.js.map +1 -1
  121. package/dist/core/extensions/capability-plugin.d.ts.map +1 -1
  122. package/dist/core/extensions/capability-plugin.js +37 -13
  123. package/dist/core/extensions/capability-plugin.js.map +1 -1
  124. package/dist/core/extensions/capability-runtime-events.d.ts.map +1 -1
  125. package/dist/core/extensions/capability-runtime-events.js +17 -13
  126. package/dist/core/extensions/capability-runtime-events.js.map +1 -1
  127. package/dist/core/extensions/capability-runtime-guards.d.ts.map +1 -1
  128. package/dist/core/extensions/capability-runtime-guards.js +16 -15
  129. package/dist/core/extensions/capability-runtime-guards.js.map +1 -1
  130. package/dist/core/extensions/capability-runtime-manifest.d.ts.map +1 -1
  131. package/dist/core/extensions/capability-runtime-manifest.js +52 -30
  132. package/dist/core/extensions/capability-runtime-manifest.js.map +1 -1
  133. package/dist/core/extensions/capability-runtime-validation.d.ts.map +1 -1
  134. package/dist/core/extensions/capability-runtime-validation.js +66 -65
  135. package/dist/core/extensions/capability-runtime-validation.js.map +1 -1
  136. package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
  137. package/dist/core/extensions/capability-runtime.js +147 -127
  138. package/dist/core/extensions/capability-runtime.js.map +1 -1
  139. package/dist/core/extensions/compaction-memory-failure-mode.d.ts.map +1 -1
  140. package/dist/core/extensions/compaction-memory-failure-mode.js +3 -2
  141. package/dist/core/extensions/compaction-memory-failure-mode.js.map +1 -1
  142. package/dist/core/extensions/context-strategy-binding.d.ts.map +1 -1
  143. package/dist/core/extensions/context-strategy-binding.js +7 -4
  144. package/dist/core/extensions/context-strategy-binding.js.map +1 -1
  145. package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
  146. package/dist/core/extensions/lifecycle-catalog.js +290 -279
  147. package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
  148. package/dist/core/extensions/lifecycle-delivery-queue.d.ts.map +1 -1
  149. package/dist/core/extensions/lifecycle-delivery-queue.js +3 -2
  150. package/dist/core/extensions/lifecycle-delivery-queue.js.map +1 -1
  151. package/dist/core/extensions/provider-failover.d.ts +7 -3
  152. package/dist/core/extensions/provider-failover.d.ts.map +1 -1
  153. package/dist/core/extensions/provider-failover.js +9 -8
  154. package/dist/core/extensions/provider-failover.js.map +1 -1
  155. package/dist/core/facade/coding-agent-facade.d.ts +6 -6
  156. package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
  157. package/dist/core/facade/coding-agent-facade.js +2 -3
  158. package/dist/core/facade/coding-agent-facade.js.map +1 -1
  159. package/dist/core/http-dispatcher.d.ts.map +1 -1
  160. package/dist/core/http-dispatcher.js +2 -1
  161. package/dist/core/http-dispatcher.js.map +1 -1
  162. package/dist/core/i18n/index.d.ts +14 -0
  163. package/dist/core/i18n/index.d.ts.map +1 -0
  164. package/dist/core/i18n/index.js +67 -0
  165. package/dist/core/i18n/index.js.map +1 -0
  166. package/dist/core/i18n/locales/en/example.d.ts +7 -0
  167. package/dist/core/i18n/locales/en/example.d.ts.map +1 -0
  168. package/dist/core/i18n/locales/en/example.js +7 -0
  169. package/dist/core/i18n/locales/en/example.js.map +1 -0
  170. package/dist/core/i18n/locales/zh-cn/cli.d.ts +8 -0
  171. package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -0
  172. package/dist/core/i18n/locales/zh-cn/cli.js +229 -0
  173. package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -0
  174. package/dist/core/i18n/locales/zh-cn/common.d.ts +6 -0
  175. package/dist/core/i18n/locales/zh-cn/common.d.ts.map +1 -0
  176. package/dist/core/i18n/locales/zh-cn/common.js +6 -0
  177. package/dist/core/i18n/locales/zh-cn/common.js.map +1 -0
  178. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts +13 -0
  179. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts.map +1 -0
  180. package/dist/core/i18n/locales/zh-cn/core-contract-core.js +173 -0
  181. package/dist/core/i18n/locales/zh-cn/core-contract-core.js.map +1 -0
  182. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts +12 -0
  183. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts.map +1 -0
  184. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js +582 -0
  185. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js.map +1 -0
  186. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts +9 -0
  187. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts.map +1 -0
  188. package/dist/core/i18n/locales/zh-cn/core-contract-final.js +55 -0
  189. package/dist/core/i18n/locales/zh-cn/core-contract-final.js.map +1 -0
  190. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts +11 -0
  191. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts.map +1 -0
  192. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js +455 -0
  193. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js.map +1 -0
  194. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts +9 -0
  195. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -0
  196. package/dist/core/i18n/locales/zh-cn/core-errors.js +191 -0
  197. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -0
  198. package/dist/core/i18n/locales/zh-cn/example.d.ts +7 -0
  199. package/dist/core/i18n/locales/zh-cn/example.d.ts.map +1 -0
  200. package/dist/core/i18n/locales/zh-cn/example.js +10 -0
  201. package/dist/core/i18n/locales/zh-cn/example.js.map +1 -0
  202. package/dist/core/i18n/locales/zh-cn/index.d.ts +2 -0
  203. package/dist/core/i18n/locales/zh-cn/index.d.ts.map +1 -0
  204. package/dist/core/i18n/locales/zh-cn/index.js +41 -0
  205. package/dist/core/i18n/locales/zh-cn/index.js.map +1 -0
  206. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts +10 -0
  207. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -0
  208. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +267 -0
  209. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -0
  210. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts +11 -0
  211. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts.map +1 -0
  212. package/dist/core/i18n/locales/zh-cn/keybindings.js +67 -0
  213. package/dist/core/i18n/locales/zh-cn/keybindings.js.map +1 -0
  214. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts +7 -0
  215. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts.map +1 -0
  216. package/dist/core/i18n/locales/zh-cn/slash-commands.js +39 -0
  217. package/dist/core/i18n/locales/zh-cn/slash-commands.js.map +1 -0
  218. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts +10 -0
  219. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts.map +1 -0
  220. package/dist/core/i18n/locales/zh-cn/tui-builtin.js +15 -0
  221. package/dist/core/i18n/locales/zh-cn/tui-builtin.js.map +1 -0
  222. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts +13 -0
  223. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts.map +1 -0
  224. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js +102 -0
  225. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js.map +1 -0
  226. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts +12 -0
  227. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts.map +1 -0
  228. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js +286 -0
  229. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js.map +1 -0
  230. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts +9 -0
  231. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts.map +1 -0
  232. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js +79 -0
  233. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js.map +1 -0
  234. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts +8 -0
  235. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts.map +1 -0
  236. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js +72 -0
  237. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js.map +1 -0
  238. package/dist/core/keybindings.d.ts +15 -0
  239. package/dist/core/keybindings.d.ts.map +1 -1
  240. package/dist/core/keybindings.js +18 -0
  241. package/dist/core/keybindings.js.map +1 -1
  242. package/dist/core/logger.d.ts.map +1 -1
  243. package/dist/core/logger.js +5 -1
  244. package/dist/core/logger.js.map +1 -1
  245. package/dist/core/model-config.d.ts +0 -1
  246. package/dist/core/model-config.d.ts.map +1 -1
  247. package/dist/core/model-config.js +9 -1
  248. package/dist/core/model-config.js.map +1 -1
  249. package/dist/core/model-registry.d.ts.map +1 -1
  250. package/dist/core/model-registry.js +2 -1
  251. package/dist/core/model-registry.js.map +1 -1
  252. package/dist/core/model-runtime.d.ts.map +1 -1
  253. package/dist/core/model-runtime.js +11 -7
  254. package/dist/core/model-runtime.js.map +1 -1
  255. package/dist/core/package-install-host.d.ts.map +1 -1
  256. package/dist/core/package-install-host.js +4 -3
  257. package/dist/core/package-install-host.js.map +1 -1
  258. package/dist/core/package-manager.d.ts.map +1 -1
  259. package/dist/core/package-manager.js +15 -10
  260. package/dist/core/package-manager.js.map +1 -1
  261. package/dist/core/package-resource-collection.d.ts.map +1 -1
  262. package/dist/core/package-resource-collection.js +2 -1
  263. package/dist/core/package-resource-collection.js.map +1 -1
  264. package/dist/core/package-source.d.ts.map +1 -1
  265. package/dist/core/package-source.js +3 -2
  266. package/dist/core/package-source.js.map +1 -1
  267. package/dist/core/provider-composer.d.ts +2 -2
  268. package/dist/core/provider-composer.d.ts.map +1 -1
  269. package/dist/core/provider-composer.js +29 -21
  270. package/dist/core/provider-composer.js.map +1 -1
  271. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  272. package/dist/core/remote-catalog-provider.js +6 -2
  273. package/dist/core/remote-catalog-provider.js.map +1 -1
  274. package/dist/core/resolve-config-value.d.ts.map +1 -1
  275. package/dist/core/resolve-config-value.js +15 -5
  276. package/dist/core/resolve-config-value.js.map +1 -1
  277. package/dist/core/sdk.d.ts +27 -5
  278. package/dist/core/sdk.d.ts.map +1 -1
  279. package/dist/core/sdk.js +80 -55
  280. package/dist/core/sdk.js.map +1 -1
  281. package/dist/core/session-cwd.d.ts.map +1 -1
  282. package/dist/core/session-cwd.js +5 -2
  283. package/dist/core/session-cwd.js.map +1 -1
  284. package/dist/core/session-export.d.ts.map +1 -1
  285. package/dist/core/session-export.js +2 -1
  286. package/dist/core/session-export.js.map +1 -1
  287. package/dist/core/session-manager.d.ts.map +1 -1
  288. package/dist/core/session-manager.js +39 -29
  289. package/dist/core/session-manager.js.map +1 -1
  290. package/dist/core/session-storage.d.ts.map +1 -1
  291. package/dist/core/session-storage.js +3 -3
  292. package/dist/core/session-storage.js.map +1 -1
  293. package/dist/core/settings-manager.d.ts +20 -20
  294. package/dist/core/settings-manager.d.ts.map +1 -1
  295. package/dist/core/settings-manager.js +47 -59
  296. package/dist/core/settings-manager.js.map +1 -1
  297. package/dist/core/slash-commands.d.ts.map +1 -1
  298. package/dist/core/slash-commands.js +53 -32
  299. package/dist/core/slash-commands.js.map +1 -1
  300. package/dist/core/system-prompt.d.ts.map +1 -1
  301. package/dist/core/system-prompt.js +25 -2
  302. package/dist/core/system-prompt.js.map +1 -1
  303. package/dist/core/tools/bash.d.ts.map +1 -1
  304. package/dist/core/tools/bash.js +11 -7
  305. package/dist/core/tools/bash.js.map +1 -1
  306. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  307. package/dist/core/tools/edit-diff.js +9 -4
  308. package/dist/core/tools/edit-diff.js.map +1 -1
  309. package/dist/core/tools/edit.d.ts.map +1 -1
  310. package/dist/core/tools/edit.js +4 -3
  311. package/dist/core/tools/edit.js.map +1 -1
  312. package/dist/core/tools/index.d.ts.map +1 -1
  313. package/dist/core/tools/index.js +3 -2
  314. package/dist/core/tools/index.js.map +1 -1
  315. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  316. package/dist/core/tools/output-accumulator.js +2 -1
  317. package/dist/core/tools/output-accumulator.js.map +1 -1
  318. package/dist/core/tools/read.d.ts.map +1 -1
  319. package/dist/core/tools/read.js +6 -2
  320. package/dist/core/tools/read.js.map +1 -1
  321. package/dist/core/tools/write.d.ts.map +1 -1
  322. package/dist/core/tools/write.js +2 -1
  323. package/dist/core/tools/write.js.map +1 -1
  324. package/dist/durable/durable-service.d.ts.map +1 -1
  325. package/dist/durable/durable-service.js +25 -18
  326. package/dist/durable/durable-service.js.map +1 -1
  327. package/dist/host/checkpoint-commit.d.ts.map +1 -1
  328. package/dist/host/checkpoint-commit.js +2 -1
  329. package/dist/host/checkpoint-commit.js.map +1 -1
  330. package/dist/host/manual-compaction.d.ts.map +1 -1
  331. package/dist/host/manual-compaction.js +2 -1
  332. package/dist/host/manual-compaction.js.map +1 -1
  333. package/dist/host/output-projection.d.ts +0 -13
  334. package/dist/host/output-projection.d.ts.map +1 -1
  335. package/dist/host/output-projection.js +16 -2
  336. package/dist/host/output-projection.js.map +1 -1
  337. package/dist/host/summary-retry.d.ts.map +1 -1
  338. package/dist/host/summary-retry.js +3 -2
  339. package/dist/host/summary-retry.js.map +1 -1
  340. package/dist/main.d.ts.map +1 -1
  341. package/dist/main.js +97 -60
  342. package/dist/main.js.map +1 -1
  343. package/dist/modes/interactive/components/agents-view.d.ts.map +1 -1
  344. package/dist/modes/interactive/components/agents-view.js +18 -15
  345. package/dist/modes/interactive/components/agents-view.js.map +1 -1
  346. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  347. package/dist/modes/interactive/components/assistant-message.js +6 -5
  348. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  349. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  350. package/dist/modes/interactive/components/bash-execution.js +12 -7
  351. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  352. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  353. package/dist/modes/interactive/components/bordered-loader.js +2 -1
  354. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  355. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  356. package/dist/modes/interactive/components/branch-summary-message.js +6 -5
  357. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  358. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  359. package/dist/modes/interactive/components/compaction-summary-message.js +9 -5
  360. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  361. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/config-selector.js +18 -15
  363. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  364. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/custom-entry.js +2 -1
  366. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  367. package/dist/modes/interactive/components/dialog-editor.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/dialog-editor.js +5 -4
  369. package/dist/modes/interactive/components/dialog-editor.js.map +1 -1
  370. package/dist/modes/interactive/components/dialog-input.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/dialog-input.js +2 -1
  372. package/dist/modes/interactive/components/dialog-input.js.map +1 -1
  373. package/dist/modes/interactive/components/dialog-selector.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/dialog-selector.js +4 -3
  375. package/dist/modes/interactive/components/dialog-selector.js.map +1 -1
  376. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/first-time-setup.js +11 -10
  378. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  379. package/dist/modes/interactive/components/footer.d.ts +0 -2
  380. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/footer.js +8 -10
  382. package/dist/modes/interactive/components/footer.js.map +1 -1
  383. package/dist/modes/interactive/components/index.d.ts +1 -0
  384. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/index.js +1 -0
  386. package/dist/modes/interactive/components/index.js.map +1 -1
  387. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/keybinding-hints.js +3 -2
  389. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  390. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/login-dialog.js +12 -11
  392. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  393. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/mermaid.js +6 -2
  395. package/dist/modes/interactive/components/mermaid.js.map +1 -1
  396. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.js +28 -16
  398. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  399. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/oauth-selector.js +10 -9
  401. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  402. package/dist/modes/interactive/components/plugin-manager-entry.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/plugin-manager-entry.js +22 -13
  404. package/dist/modes/interactive/components/plugin-manager-entry.js.map +1 -1
  405. package/dist/modes/interactive/components/plugin-selection-entry.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/plugin-selection-entry.js +27 -12
  407. package/dist/modes/interactive/components/plugin-selection-entry.js.map +1 -1
  408. package/dist/modes/interactive/components/queue-manager-selector.d.ts +60 -0
  409. package/dist/modes/interactive/components/queue-manager-selector.d.ts.map +1 -0
  410. package/dist/modes/interactive/components/queue-manager-selector.js +166 -0
  411. package/dist/modes/interactive/components/queue-manager-selector.js.map +1 -0
  412. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/scoped-models-selector.js +16 -14
  414. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  415. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/session-selector.js +43 -31
  417. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  418. package/dist/modes/interactive/components/settings-selector.d.ts +4 -6
  419. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/settings-selector.js +115 -120
  421. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  422. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/settings-submenu.js +5 -4
  424. package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
  425. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/show-images-selector.js +3 -2
  427. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  428. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/skill-invocation-message.js +4 -3
  430. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  431. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/status-indicator.js +14 -5
  433. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  434. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/theme-selector.js +2 -1
  436. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  437. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  438. package/dist/modes/interactive/components/thinking-selector.js +11 -4
  439. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  440. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  441. package/dist/modes/interactive/components/tool-execution.js +8 -3
  442. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  443. package/dist/modes/interactive/components/tree-list.d.ts.map +1 -1
  444. package/dist/modes/interactive/components/tree-list.js +7 -6
  445. package/dist/modes/interactive/components/tree-list.js.map +1 -1
  446. package/dist/modes/interactive/components/tree-render.d.ts.map +1 -1
  447. package/dist/modes/interactive/components/tree-render.js +28 -23
  448. package/dist/modes/interactive/components/tree-render.js.map +1 -1
  449. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/tree-selector.js +9 -7
  451. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  452. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  453. package/dist/modes/interactive/components/user-message-selector.js +5 -4
  454. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  455. package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -1
  456. package/dist/modes/interactive/components/workflow-view.js +36 -21
  457. package/dist/modes/interactive/components/workflow-view.js.map +1 -1
  458. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  459. package/dist/modes/interactive/external-editor.js +2 -1
  460. package/dist/modes/interactive/external-editor.js.map +1 -1
  461. package/dist/modes/interactive/interactive-auth.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-auth.js +71 -40
  463. package/dist/modes/interactive/interactive-auth.js.map +1 -1
  464. package/dist/modes/interactive/interactive-commands.d.ts +1 -0
  465. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-commands.js +3 -0
  467. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  468. package/dist/modes/interactive/interactive-dialog-ui.d.ts.map +1 -1
  469. package/dist/modes/interactive/interactive-dialog-ui.js +5 -5
  470. package/dist/modes/interactive/interactive-dialog-ui.js.map +1 -1
  471. package/dist/modes/interactive/interactive-format.d.ts.map +1 -1
  472. package/dist/modes/interactive/interactive-format.js +34 -18
  473. package/dist/modes/interactive/interactive-format.js.map +1 -1
  474. package/dist/modes/interactive/interactive-helpers.d.ts.map +1 -1
  475. package/dist/modes/interactive/interactive-helpers.js +7 -2
  476. package/dist/modes/interactive/interactive-helpers.js.map +1 -1
  477. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  478. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-mode.js +350 -279
  480. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  481. package/dist/modes/interactive/interactive-selectors.d.ts +9 -0
  482. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  483. package/dist/modes/interactive/interactive-selectors.js +79 -25
  484. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  485. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  486. package/dist/modes/interactive/theme/theme-controller.js +5 -1
  487. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  488. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  489. package/dist/modes/interactive/theme/theme.js +26 -23
  490. package/dist/modes/interactive/theme/theme.js.map +1 -1
  491. package/dist/modes/json-event.d.ts.map +1 -1
  492. package/dist/modes/json-event.js +3 -2
  493. package/dist/modes/json-event.js.map +1 -1
  494. package/dist/modes/print-mode.d.ts.map +1 -1
  495. package/dist/modes/print-mode.js +3 -2
  496. package/dist/modes/print-mode.js.map +1 -1
  497. package/dist/modes/rpc/rpc-client.d.ts +0 -8
  498. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  499. package/dist/modes/rpc/rpc-client.js +3 -14
  500. package/dist/modes/rpc/rpc-client.js.map +1 -1
  501. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  502. package/dist/modes/rpc/rpc-mode.js +0 -9
  503. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  504. package/dist/modes/rpc/rpc-types.d.ts +0 -19
  505. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  506. package/dist/modes/rpc/rpc-types.js.map +1 -1
  507. package/dist/package-manager-cli.d.ts.map +1 -1
  508. package/dist/package-manager-cli.js +28 -13
  509. package/dist/package-manager-cli.js.map +1 -1
  510. package/dist/plugins/ecosystem/catalog.d.ts.map +1 -1
  511. package/dist/plugins/ecosystem/catalog.js +3 -2
  512. package/dist/plugins/ecosystem/catalog.js.map +1 -1
  513. package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
  514. package/dist/plugins/ecosystem/cli.js +2 -1
  515. package/dist/plugins/ecosystem/cli.js.map +1 -1
  516. package/dist/plugins/ecosystem/default-plugin-market.d.ts.map +1 -1
  517. package/dist/plugins/ecosystem/default-plugin-market.js +3 -2
  518. package/dist/plugins/ecosystem/default-plugin-market.js.map +1 -1
  519. package/dist/plugins/ecosystem/distribution.d.ts.map +1 -1
  520. package/dist/plugins/ecosystem/distribution.js +2 -1
  521. package/dist/plugins/ecosystem/distribution.js.map +1 -1
  522. package/dist/plugins/ecosystem/installer.d.ts.map +1 -1
  523. package/dist/plugins/ecosystem/installer.js +3 -2
  524. package/dist/plugins/ecosystem/installer.js.map +1 -1
  525. package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
  526. package/dist/plugins/ecosystem/node-disk-registry-source.js +20 -17
  527. package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
  528. package/dist/plugins/ecosystem/node-distribution-host.d.ts.map +1 -1
  529. package/dist/plugins/ecosystem/node-distribution-host.js +35 -34
  530. package/dist/plugins/ecosystem/node-distribution-host.js.map +1 -1
  531. package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -1
  532. package/dist/plugins/ecosystem/node-npm-registry-source.js +35 -34
  533. package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -1
  534. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  535. package/dist/plugins/ecosystem/node-package-install-host.js +9 -8
  536. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  537. package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
  538. package/dist/plugins/ecosystem/node-package-install-persistence.js +4 -3
  539. package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
  540. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +9 -2
  541. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  542. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +44 -5
  543. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  544. package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts.map +1 -1
  545. package/dist/plugins/ecosystem/node-plugin-trust-root.js +10 -9
  546. package/dist/plugins/ecosystem/node-plugin-trust-root.js.map +1 -1
  547. package/dist/plugins/ecosystem/node-registry-source.d.ts.map +1 -1
  548. package/dist/plugins/ecosystem/node-registry-source.js +31 -30
  549. package/dist/plugins/ecosystem/node-registry-source.js.map +1 -1
  550. package/dist/plugins/ecosystem/node-release-package-source.d.ts.map +1 -1
  551. package/dist/plugins/ecosystem/node-release-package-source.js +30 -29
  552. package/dist/plugins/ecosystem/node-release-package-source.js.map +1 -1
  553. package/dist/plugins/ecosystem/node-tar-writer.d.ts.map +1 -1
  554. package/dist/plugins/ecosystem/node-tar-writer.js +11 -10
  555. package/dist/plugins/ecosystem/node-tar-writer.js.map +1 -1
  556. package/dist/plugins/ecosystem/node-url-package-source.d.ts.map +1 -1
  557. package/dist/plugins/ecosystem/node-url-package-source.js +21 -20
  558. package/dist/plugins/ecosystem/node-url-package-source.js.map +1 -1
  559. package/dist/plugins/ecosystem/plugin-acquisition.d.ts.map +1 -1
  560. package/dist/plugins/ecosystem/plugin-acquisition.js +3 -2
  561. package/dist/plugins/ecosystem/plugin-acquisition.js.map +1 -1
  562. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +9 -1
  563. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -1
  564. package/dist/plugins/ecosystem/plugin-bootstrap.js +5 -0
  565. package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -1
  566. package/dist/plugins/ecosystem/registry-cli-adapter.d.ts.map +1 -1
  567. package/dist/plugins/ecosystem/registry-cli-adapter.js +2 -1
  568. package/dist/plugins/ecosystem/registry-cli-adapter.js.map +1 -1
  569. package/dist/plugins/ecosystem/registry.d.ts.map +1 -1
  570. package/dist/plugins/ecosystem/registry.js +35 -32
  571. package/dist/plugins/ecosystem/registry.js.map +1 -1
  572. package/dist/plugins/ecosystem/resolver.d.ts.map +1 -1
  573. package/dist/plugins/ecosystem/resolver.js +3 -2
  574. package/dist/plugins/ecosystem/resolver.js.map +1 -1
  575. package/dist/plugins/ecosystem/selection-store.d.ts.map +1 -1
  576. package/dist/plugins/ecosystem/selection-store.js +11 -10
  577. package/dist/plugins/ecosystem/selection-store.js.map +1 -1
  578. package/dist/plugins/ecosystem/selection.d.ts.map +1 -1
  579. package/dist/plugins/ecosystem/selection.js +8 -7
  580. package/dist/plugins/ecosystem/selection.js.map +1 -1
  581. package/dist/plugins/ecosystem/state.d.ts.map +1 -1
  582. package/dist/plugins/ecosystem/state.js +24 -23
  583. package/dist/plugins/ecosystem/state.js.map +1 -1
  584. package/dist/plugins/ecosystem/updates.d.ts.map +1 -1
  585. package/dist/plugins/ecosystem/updates.js +5 -4
  586. package/dist/plugins/ecosystem/updates.js.map +1 -1
  587. package/dist/plugins/mcp-http.d.ts +0 -17
  588. package/dist/plugins/mcp-http.d.ts.map +1 -1
  589. package/dist/plugins/mcp-http.js +30 -7
  590. package/dist/plugins/mcp-http.js.map +1 -1
  591. package/dist/plugins/plugin-supervisor.d.ts.map +1 -1
  592. package/dist/plugins/plugin-supervisor.js +19 -1
  593. package/dist/plugins/plugin-supervisor.js.map +1 -1
  594. package/dist/profile/first-roles.d.ts.map +1 -1
  595. package/dist/profile/first-roles.js +10 -1
  596. package/dist/profile/first-roles.js.map +1 -1
  597. package/dist/profile/goal-plugin.d.ts.map +1 -1
  598. package/dist/profile/goal-plugin.js +24 -5
  599. package/dist/profile/goal-plugin.js.map +1 -1
  600. package/dist/profile/identity.d.ts.map +1 -1
  601. package/dist/profile/identity.js +7 -6
  602. package/dist/profile/identity.js.map +1 -1
  603. package/dist/profile/lock.d.ts.map +1 -1
  604. package/dist/profile/lock.js +15 -1
  605. package/dist/profile/lock.js.map +1 -1
  606. package/dist/profile/plan-plugin.d.ts.map +1 -1
  607. package/dist/profile/plan-plugin.js +26 -13
  608. package/dist/profile/plan-plugin.js.map +1 -1
  609. package/dist/profile/plugin-profiles.d.ts.map +1 -1
  610. package/dist/profile/plugin-profiles.js +2 -1
  611. package/dist/profile/plugin-profiles.js.map +1 -1
  612. package/dist/profile/policy-manifest.d.ts.map +1 -1
  613. package/dist/profile/policy-manifest.js +5 -4
  614. package/dist/profile/policy-manifest.js.map +1 -1
  615. package/dist/profile/profile-registry.d.ts +0 -12
  616. package/dist/profile/profile-registry.d.ts.map +1 -1
  617. package/dist/profile/profile-registry.js +14 -1
  618. package/dist/profile/profile-registry.js.map +1 -1
  619. package/dist/profile/role-catalog.d.ts +0 -1
  620. package/dist/profile/role-catalog.d.ts.map +1 -1
  621. package/dist/profile/role-catalog.js +17 -15
  622. package/dist/profile/role-catalog.js.map +1 -1
  623. package/dist/profile/role-orchestrator.d.ts.map +1 -1
  624. package/dist/profile/role-orchestrator.js +8 -7
  625. package/dist/profile/role-orchestrator.js.map +1 -1
  626. package/dist/profile/role-runner.d.ts.map +1 -1
  627. package/dist/profile/role-runner.js +3 -2
  628. package/dist/profile/role-runner.js.map +1 -1
  629. package/dist/profile/root-facade.d.ts.map +1 -1
  630. package/dist/profile/root-facade.js +2 -1
  631. package/dist/profile/root-facade.js.map +1 -1
  632. package/dist/profile/skill-discovery-wiring.d.ts.map +1 -1
  633. package/dist/profile/skill-discovery-wiring.js +7 -2
  634. package/dist/profile/skill-discovery-wiring.js.map +1 -1
  635. package/dist/profile/state-store.d.ts.map +1 -1
  636. package/dist/profile/state-store.js +13 -9
  637. package/dist/profile/state-store.js.map +1 -1
  638. package/dist/profile/todo-plugin.d.ts.map +1 -1
  639. package/dist/profile/todo-plugin.js +9 -4
  640. package/dist/profile/todo-plugin.js.map +1 -1
  641. package/dist/profiles/builtin-suites.d.ts +13 -6
  642. package/dist/profiles/builtin-suites.d.ts.map +1 -1
  643. package/dist/profiles/builtin-suites.js +15 -6
  644. package/dist/profiles/builtin-suites.js.map +1 -1
  645. package/dist/profiles/suite-loader.d.ts +25 -1
  646. package/dist/profiles/suite-loader.d.ts.map +1 -1
  647. package/dist/profiles/suite-loader.js +34 -9
  648. package/dist/profiles/suite-loader.js.map +1 -1
  649. package/dist/remote/server-entry.d.ts.map +1 -1
  650. package/dist/remote/server-entry.js +3 -2
  651. package/dist/remote/server-entry.js.map +1 -1
  652. package/dist/remote/session-source.d.ts.map +1 -1
  653. package/dist/remote/session-source.js +8 -4
  654. package/dist/remote/session-source.js.map +1 -1
  655. package/dist/share/session-share.d.ts.map +1 -1
  656. package/dist/share/session-share.js +14 -2
  657. package/dist/share/session-share.js.map +1 -1
  658. package/dist/testing/plugin-testkit.d.ts.map +1 -1
  659. package/dist/testing/plugin-testkit.js +4 -3
  660. package/dist/testing/plugin-testkit.js.map +1 -1
  661. package/dist/testing/runtime-delivery-queue.d.ts.map +1 -1
  662. package/dist/testing/runtime-delivery-queue.js +2 -1
  663. package/dist/testing/runtime-delivery-queue.js.map +1 -1
  664. package/dist/testing/session-plugin-testkit.d.ts.map +1 -1
  665. package/dist/testing/session-plugin-testkit.js +10 -7
  666. package/dist/testing/session-plugin-testkit.js.map +1 -1
  667. package/dist/utils/ansi.d.ts.map +1 -1
  668. package/dist/utils/ansi.js +2 -1
  669. package/dist/utils/ansi.js.map +1 -1
  670. package/dist/utils/clipboard.d.ts.map +1 -1
  671. package/dist/utils/clipboard.js +7 -6
  672. package/dist/utils/clipboard.js.map +1 -1
  673. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  674. package/dist/utils/image-resize-worker.js +3 -2
  675. package/dist/utils/image-resize-worker.js.map +1 -1
  676. package/dist/utils/photon.d.ts +2 -2
  677. package/dist/utils/photon.d.ts.map +1 -1
  678. package/dist/utils/photon.js +2 -2
  679. package/dist/utils/photon.js.map +1 -1
  680. package/dist/utils/shell.d.ts.map +1 -1
  681. package/dist/utils/shell.js +9 -8
  682. package/dist/utils/shell.js.map +1 -1
  683. package/dist/utils/tools-manager.d.ts.map +1 -1
  684. package/dist/utils/tools-manager.js +23 -13
  685. package/dist/utils/tools-manager.js.map +1 -1
  686. package/dist/utils/version-check.d.ts.map +1 -1
  687. package/dist/utils/version-check.js +5 -1
  688. package/dist/utils/version-check.js.map +1 -1
  689. package/docs/compaction.md +1 -3
  690. package/docs/docs.json +4 -0
  691. package/docs/index.md +1 -0
  692. package/docs/keybindings.md +3 -0
  693. package/docs/mcp.md +103 -0
  694. package/docs/models.md +14 -22
  695. package/docs/providers.md +0 -1
  696. package/docs/quickstart.md +1 -1
  697. package/docs/rpc.md +1 -26
  698. package/docs/sdk.md +4 -6
  699. package/docs/settings.md +10 -12
  700. package/docs/skills.md +1 -9
  701. package/docs/terminal-setup.md +2 -3
  702. package/docs/usage.md +7 -6
  703. package/examples/extensions/with-deps/package-lock.json +2 -2
  704. package/examples/extensions/with-deps/package.json +1 -1
  705. package/examples/extensions/with-deps/plugin.json +1 -1
  706. package/examples/sdk/10-settings.ts +2 -4
  707. package/examples/sdk/12-full-control.ts +1 -2
  708. package/package.json +1 -3
  709. package/dist/bundle/chunks/chunk-EALJ5DDX.js +0 -1452
  710. package/dist/core/extensions/microcompact.d.ts +0 -80
  711. package/dist/core/extensions/microcompact.d.ts.map +0 -1
  712. package/dist/core/extensions/microcompact.js +0 -134
  713. package/dist/core/extensions/microcompact.js.map +0 -1
  714. package/dist/server/create-harness.d.ts +0 -26
  715. package/dist/server/create-harness.d.ts.map +0 -1
  716. package/dist/server/create-harness.js +0 -106
  717. package/dist/server/create-harness.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAsBH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAOrE;;;;GAIG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACrD,YAAY,QAAQ,EAAE,MAAM,EAG3B;CACD;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,kEAAkE;IAClE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,4FAA4F;IAC5F,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IACvC,gHAAgH;IAChH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IAC/C,0DAA0D;IAC1D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3D,sFAAsF;IACtF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IACnE,mDAAmD;IACnD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iFAAiF;IACjF,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxC,iFAAiF;IACjF,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5D;AAkID;;;;;GAKG;AACH,qBAAa,0BAA2B,YAAW,sBAAsB;IACxE,8DAA8D;IAC9D,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAoC;IAExF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;IAEpD,YACC,OAAO,GAAE;QACR,4EAA4E;QAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,6EAA6E;QAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,6FAA6F;QAC7F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;KACxC,EAKN;IAED,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3B;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5B;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzB;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CA2CrC;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA2C7C;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CASzD;IAED,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CASjE;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAE3C;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEzC;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEzB;IAED,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKtC;IAED,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/B;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CA8CjC;CACF","sourcesContent":["/**\n * Storage seam for process sessions (D-067 phase 1).\n *\n * `SessionManager` owns the session tree, index, and context logic; a\n * `SessionStorageProvider` owns only physical record storage for one session\n * location. The default implementation is the current local JSONL file\n * protocol, byte-identical to the pre-seam behavior; alternative providers\n * (in-memory for tests, remote backends later) implement the same contract.\n *\n * Scope line: discovery/enrichment reads used by `SessionManager.list` /\n * `listAll` (async streamed `SessionInfo` construction) stay on the fs path\n * for now; they move behind this seam in a later phase.\n *\n * Cross-process write arbitration (D-067 phase 2 S5): providers may expose an\n * optional {@link SessionWriteGuardV1}. The default file provider implements\n * it as a `<sessionFile>.guard` sibling token ({ writerId, hostId, pid }): a\n * write fails explicitly while another LIVE process on the same host owns the\n * session, a dead owner (closed/crashed process) is taken over silently, and\n * same-process writers are never conflicted (in-process sequencing is the\n * caller's concern, unchanged). Cross-host guards are treated as live\n * foreign owners (conservative: no false takeover over shared filesystems).\n */\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadFileSync,\n\treadSync,\n\tstatSync,\n\ttruncateSync,\n\tunlinkSync,\n\twriteFileSync,\n\twriteSync,\n} from \"fs\";\nimport { hostname } from \"os\";\nimport { dirname, join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { sleepSync } from \"../utils/sync-sleep.ts\";\nimport type { FileEntry, SessionHeader } from \"./session-manager.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\nconst SESSION_HEADER_READ_BUFFER_SIZE = 4096;\n/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */\nconst MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024;\n\n/**\n * Thrown by `readHeader` when the header scan exceeds the bounded limit.\n * `SessionManager.open` treats it as a discovery optimization miss and falls\n * back to a full load; discovery callers swallow it.\n */\nexport class SessionHeaderScanLimitError extends Error {\n\tconstructor(filePath: string) {\n\t\tsuper(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`);\n\t\tthis.name = \"SessionHeaderScanLimitError\";\n\t}\n}\n\n/**\n * Record-level storage contract for sessions, keyed by location path. All\n * methods are synchronous and reflect the append-only JSONL protocol\n * semantics; implementations for other media map the same operations onto\n * their own storage model.\n */\nexport interface SessionStorageProvider {\n\t/** Ensure the storage location for a session directory exists. */\n\tensureDir(dir: string): void;\n\t/** True when the given session location exists. */\n\texists(path: string): boolean;\n\t/** Current byte size of a session location (rollback baseline). Missing locations throw. */\n\tsize(path: string): number;\n\t/**\n\t * Load all entries (header first). A missing location yields `[]`. When the\n\t * stored content ends with an unterminated final line, implementations\n\t * repair it by terminating it (the file protocol appends a `\\n`).\n\t */\n\tloadEntries(path: string): FileEntry[];\n\t/** Bounded header scan used for discovery and cwd resolution. Missing or header-less locations yield `null`. */\n\treadHeader(path: string): SessionHeader | null;\n\t/** Replace the whole stored content (header included). */\n\trewrite(path: string, entries: readonly FileEntry[]): void;\n\t/** Write initial content; fails when the location already exists (\"wx\" semantics). */\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void;\n\t/** Append one entry after the existing content. */\n\tappend(path: string, entry: FileEntry): void;\n\t/** Roll back to a previous byte size after a failed append. */\n\ttruncate(path: string, size: number): void;\n\t/** Remove a session location (used when a failed append created it). */\n\tremove(path: string): void;\n\t/** Enumerate session locations inside a directory. Missing directories throw. */\n\tlistSessionFiles(dir: string): string[];\n\t/** Modification time of a session location, used for recent-session ordering. */\n\tmodifiedTime(path: string): Date;\n\t/**\n\t * Optional cross-process write guard (D-067 phase 2 S5). When present,\n\t * `SessionManager` calls `ensureWritable` before every record write and a\n\t * conflicting live foreign owner fails the write explicitly; absent, no\n\t * arbitration happens (the pre-S5 behavior).\n\t */\n\treadonly guard?: SessionWriteGuardV1;\n}\n\n/**\n * Cross-process write guard for one session location (D-067 phase 2 S5).\n * Implementations must make \"check and take ownership\" atomic against other\n * processes, and must throw a descriptive error when another live process\n * owns the session instead of letting two writers interleave entries.\n */\nexport interface SessionWriteGuardV1 {\n\t/**\n\t * Verifies the caller may write the session and records (or refreshes) its\n\t * ownership. Throws when another live process owns the session.\n\t */\n\tensureWritable(sessionFile: string, writerId: string): void;\n}\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\t// Skip malformed lines\n\t\treturn null;\n\t}\n}\n\n/** Serialize one entry as a protocol line (JSON body plus terminating newline). */\nfunction serializeEntryLine(entry: FileEntry): string {\n\treturn `${JSON.stringify(entry)}\\n`;\n}\n\nfunction parseSessionHeaderCandidate(line: string): SessionHeader | null | undefined {\n\tif (!line.trim()) return undefined;\n\tconst entry = parseSessionEntryLine(line);\n\tif (!entry) return undefined;\n\tif (entry.type !== \"session\" || typeof (entry as { id?: unknown }).id !== \"string\") return null;\n\treturn entry;\n}\n\n/** Suffix of the session write-guard sibling file (`<sessionFile>.guard`). */\nconst GUARD_FILE_SUFFIX = \".guard\";\n/** Wall-clock wait between guard-lock acquisition attempts (ledger protocol value). */\nconst GUARD_LOCK_POLL_INTERVAL_MS = 25;\n/** Default ceiling on waiting for a competing guard-lock holder. */\nconst GUARD_LOCK_TIMEOUT_MS = 5000;\n/** Default age at which a guard-lock counts as abandoned and is stolen. */\nconst GUARD_STALE_LOCK_MS = 60_000;\n\ninterface SessionGuardTokenV1 {\n\treadonly writerId: string;\n\treadonly hostId: string;\n\treadonly pid: number;\n}\n\nfunction defaultPidAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\t// EPERM means the process exists but is owned by another user — alive.\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\n/** Writes every byte of `data` to the fd, looping over partial writeSync results. */\nfunction writeAllSync(fd: number, data: string): void {\n\tconst buffer = Buffer.from(data, \"utf8\");\n\tlet written = 0;\n\twhile (written < buffer.length) {\n\t\twritten += writeSync(fd, buffer, written, buffer.length - written);\n\t}\n}\n\n/**\n * Takes the guard's cross-process lock (same protocol as the memory ledger's\n * and the durable store's persist locks — exclusive create, poll, mtime\n * stale-lock stealing, token-guarded release; the three implementations stay\n * behaviorally identical by precedent, deliberately without module coupling).\n */\nfunction acquireGuardLock(lockPath: string, lockTimeoutMs: number, staleLockMs: number): string {\n\tconst deadline = Date.now() + lockTimeoutMs;\n\tfor (;;) {\n\t\tmkdirSync(dirname(lockPath), { recursive: true });\n\t\tconst token = `${process.pid}:${Date.now()}`;\n\t\ttry {\n\t\t\tconst fd = openSync(lockPath, \"wx\");\n\t\t\ttry {\n\t\t\t\twriteFileSync(fd, token);\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\t// POSIX maps a lost create race to EEXIST; Windows/libuv maps the\n\t\t\t// same concurrent-create sharing violation to EPERM. For EPERM a\n\t\t\t// vanished lock retries, a persistent permission failure propagates.\n\t\t\tif (code === \"EPERM\") {\n\t\t\t\tlet vanished = false;\n\t\t\t\ttry {\n\t\t\t\t\tstatSync(lockPath);\n\t\t\t\t} catch (statError) {\n\t\t\t\t\tif ((statError as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\t\t\tvanished = true;\n\t\t\t\t}\n\t\t\t\tif (vanished) {\n\t\t\t\t\tif (Date.now() >= deadline) throw error;\n\t\t\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (code !== \"EEXIST\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tstale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;\n\t\t\t} catch {\n\t\t\t\t// The holder released between the failed open and the stat.\n\t\t\t}\n\t\t\tif (stale) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(lockPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already stolen — retry.\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(`session guard lock timeout after ${lockTimeoutMs} ms: ${lockPath}`);\n\t\t\t}\n\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t}\n\t}\n}\n\n/** Releases the guard lock, but only if our own token still owns it. */\nfunction releaseGuardLock(lockPath: string, token: string): void {\n\ttry {\n\t\tif (readFileSync(lockPath, \"utf8\") === token) unlinkSync(lockPath);\n\t} catch {\n\t\t// Already gone (or unreadable) — nothing to release.\n\t}\n}\n\n/**\n * The default provider: the local JSONL file protocol, moved verbatim from\n * `SessionManager`. Stateless beyond the injectable guard identity options,\n * so a shared module-level instance is the default for every session manager\n * that does not inject its own provider.\n */\nexport class FileSessionStorageProvider implements SessionStorageProvider {\n\t/** Shared stateless instance used as the default provider. */\n\tstatic readonly instance: FileSessionStorageProvider = new FileSessionStorageProvider();\n\n\tprivate readonly hostId: string;\n\tprivate readonly ownPid: number;\n\tprivate readonly pidAlive: (pid: number) => boolean;\n\n\tconstructor(\n\t\toptions: {\n\t\t\t/** Host identity written into guard tokens; defaults to `os.hostname()`. */\n\t\t\treadonly hostId?: string;\n\t\t\t/** Process identity written into guard tokens; defaults to `process.pid`. */\n\t\t\treadonly ownPid?: number;\n\t\t\t/** Injectable liveness check for deterministic tests; defaults to `process.kill(pid, 0)`. */\n\t\t\treadonly pidAlive?: (pid: number) => boolean;\n\t\t} = {},\n\t) {\n\t\tthis.hostId = options.hostId ?? hostname();\n\t\tthis.ownPid = options.ownPid ?? process.pid;\n\t\tthis.pidAlive = options.pidAlive ?? defaultPidAlive;\n\t}\n\n\tensureDir(dir: string): void {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\texists(path: string): boolean {\n\t\treturn existsSync(path);\n\t}\n\n\tsize(path: string): number {\n\t\treturn statSync(path).size;\n\t}\n\n\tloadEntries(path: string): FileEntry[] {\n\t\tconst resolvedFilePath = normalizePath(path);\n\t\tif (!existsSync(resolvedFilePath)) return [];\n\n\t\tconst entries: FileEntry[] = [];\n\t\tlet pending = \"\";\n\t\tconst fd = openSync(resolvedFilePath, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tpending = pending.slice(lineStart);\n\t\t\t}\n\n\t\t\tpending += decoder.end();\n\t\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\t\tif (finalEntry) entries.push(finalEntry);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\n\t\t// Validate session header before repairing the file.\n\t\tif (entries.length === 0) return entries;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\" || typeof (header as { id?: unknown }).id !== \"string\") {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (pending) appendFileSync(resolvedFilePath, \"\\n\");\n\t\treturn entries;\n\t}\n\n\treadHeader(path: string): SessionHeader | null {\n\t\tif (!existsSync(path)) return null;\n\t\tconst fd = openSync(path, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE);\n\t\t\tconst lineChunks: string[] = [];\n\t\t\tlet scannedBytes = 0;\n\n\t\t\twhile (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) {\n\t\t\t\tconst readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes);\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, readLength, null);\n\t\t\t\tif (bytesRead === 0) {\n\t\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t\t}\n\t\t\t\tscannedBytes += bytesRead;\n\n\t\t\t\tconst chunk = decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tlineChunks.push(chunk.slice(lineStart, newlineIndex));\n\t\t\t\t\tconst header = parseSessionHeaderCandidate(lineChunks.join(\"\"));\n\t\t\t\t\tif (header !== undefined) return header;\n\t\t\t\t\tlineChunks.length = 0;\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tlineChunks.push(chunk.slice(lineStart));\n\t\t\t}\n\n\t\t\t// Probe for EOF so a final header without a newline is allowed when it ends\n\t\t\t// exactly at the scan limit. Any additional byte exceeds the bounded scan.\n\t\t\tconst probe = Buffer.allocUnsafe(1);\n\t\t\tif (readSync(fd, probe, 0, probe.length, null) === 0) {\n\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t}\n\t\t\tthrow new SessionHeaderScanLimitError(path);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\trewrite(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"w\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"wx\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tappend(path: string, entry: FileEntry): void {\n\t\tappendFileSync(path, serializeEntryLine(entry));\n\t}\n\n\ttruncate(path: string, size: number): void {\n\t\ttruncateSync(path, size);\n\t}\n\n\tremove(path: string): void {\n\t\tunlinkSync(path);\n\t}\n\n\tlistSessionFiles(dir: string): string[] {\n\t\tconst resolvedDir = normalizePath(dir);\n\t\treturn readdirSync(resolvedDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedDir, f));\n\t}\n\n\tmodifiedTime(path: string): Date {\n\t\treturn statSync(path).mtime;\n\t}\n\n\t/**\n\t * Cross-process write guard (D-067 phase 2 S5): `<sessionFile>.guard`\n\t * carries `{ writerId, hostId, pid }`. A write conflicts only while the\n\t * recorded owner is a live process on this host; a dead owner (closed or\n\t * crashed process) and same-process writers never conflict, and cross-host\n\t * guards are treated as live foreign owners (conservative over shared\n\t * filesystems). Check-and-take-ownership is atomic under the guard lock.\n\t */\n\treadonly guard: SessionWriteGuardV1 = {\n\t\tensureWritable: (sessionFile: string, writerId: string): void => {\n\t\t\tconst guardPath = `${sessionFile}${GUARD_FILE_SUFFIX}`;\n\t\t\tconst readToken = (): SessionGuardTokenV1 | undefined => {\n\t\t\t\ttry {\n\t\t\t\t\tconst value: unknown = JSON.parse(readFileSync(guardPath, \"utf8\"));\n\t\t\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n\t\t\t\t\tconst record = value as { writerId?: unknown; hostId?: unknown; pid?: unknown };\n\t\t\t\t\tif (typeof record.writerId !== \"string\" || record.writerId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.hostId !== \"string\" || record.hostId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.pid !== \"number\" || !Number.isInteger(record.pid)) return undefined;\n\t\t\t\t\treturn { writerId: record.writerId, hostId: record.hostId, pid: record.pid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst current = readToken();\n\t\t\t// Fast path: already ours (most appends), or no owner.\n\t\t\tif (current !== undefined && current.writerId === writerId) return;\n\t\t\tif (current !== undefined) {\n\t\t\t\tconst foreignLive =\n\t\t\t\t\tcurrent.hostId === this.hostId && current.pid !== this.ownPid && this.pidAlive(current.pid);\n\t\t\t\tif (foreignLive) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Session file is owned by another live process (pid ${current.pid}): ${sessionFile}. ` +\n\t\t\t\t\t\t\t\"Close that process first; concurrent writers would silently fork the session history.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Free, dead owner, or same-process writer: take (or refresh) ownership.\n\t\t\tconst token = acquireGuardLock(`${guardPath}.lock`, GUARD_LOCK_TIMEOUT_MS, GUARD_STALE_LOCK_MS);\n\t\t\ttry {\n\t\t\t\tconst fd = openSync(guardPath, \"w\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(\n\t\t\t\t\t\tfd,\n\t\t\t\t\t\tJSON.stringify({ writerId, hostId: this.hostId, pid: this.ownPid } satisfies SessionGuardTokenV1),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treleaseGuardLock(`${guardPath}.lock`, token);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Storage seam for process sessions (D-067 phase 1).\n *\n * `SessionManager` owns the session tree, index, and context logic; a\n * `SessionStorageProvider` owns only physical record storage for one session\n * location. The default implementation is the current local JSONL file\n * protocol, byte-identical to the pre-seam behavior; alternative providers\n * (in-memory for tests, remote backends later) implement the same contract.\n *\n * Scope line: discovery/enrichment reads used by `SessionManager.list` /\n * `listAll` (async streamed `SessionInfo` construction) stay on the fs path\n * for now; they move behind this seam in a later phase.\n *\n * Cross-process write arbitration (D-067 phase 2 S5): providers may expose an\n * optional {@link SessionWriteGuardV1}. The default file provider implements\n * it as a `<sessionFile>.guard` sibling token ({ writerId, hostId, pid }): a\n * write fails explicitly while another LIVE process on the same host owns the\n * session, a dead owner (closed/crashed process) is taken over silently, and\n * same-process writers are never conflicted (in-process sequencing is the\n * caller's concern, unchanged). Cross-host guards are treated as live\n * foreign owners (conservative: no false takeover over shared filesystems).\n */\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadFileSync,\n\treadSync,\n\tstatSync,\n\ttruncateSync,\n\tunlinkSync,\n\twriteFileSync,\n\twriteSync,\n} from \"fs\";\nimport { hostname } from \"os\";\nimport { dirname, join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { sleepSync } from \"../utils/sync-sleep.ts\";\nimport { t } from \"./i18n/index.ts\";\nimport type { FileEntry, SessionHeader } from \"./session-manager.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\nconst SESSION_HEADER_READ_BUFFER_SIZE = 4096;\n/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */\nconst MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024;\n\n/**\n * Thrown by `readHeader` when the header scan exceeds the bounded limit.\n * `SessionManager.open` treats it as a discovery optimization miss and falls\n * back to a full load; discovery callers swallow it.\n */\nexport class SessionHeaderScanLimitError extends Error {\n\tconstructor(filePath: string) {\n\t\tsuper(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`);\n\t\tthis.name = \"SessionHeaderScanLimitError\";\n\t}\n}\n\n/**\n * Record-level storage contract for sessions, keyed by location path. All\n * methods are synchronous and reflect the append-only JSONL protocol\n * semantics; implementations for other media map the same operations onto\n * their own storage model.\n */\nexport interface SessionStorageProvider {\n\t/** Ensure the storage location for a session directory exists. */\n\tensureDir(dir: string): void;\n\t/** True when the given session location exists. */\n\texists(path: string): boolean;\n\t/** Current byte size of a session location (rollback baseline). Missing locations throw. */\n\tsize(path: string): number;\n\t/**\n\t * Load all entries (header first). A missing location yields `[]`. When the\n\t * stored content ends with an unterminated final line, implementations\n\t * repair it by terminating it (the file protocol appends a `\\n`).\n\t */\n\tloadEntries(path: string): FileEntry[];\n\t/** Bounded header scan used for discovery and cwd resolution. Missing or header-less locations yield `null`. */\n\treadHeader(path: string): SessionHeader | null;\n\t/** Replace the whole stored content (header included). */\n\trewrite(path: string, entries: readonly FileEntry[]): void;\n\t/** Write initial content; fails when the location already exists (\"wx\" semantics). */\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void;\n\t/** Append one entry after the existing content. */\n\tappend(path: string, entry: FileEntry): void;\n\t/** Roll back to a previous byte size after a failed append. */\n\ttruncate(path: string, size: number): void;\n\t/** Remove a session location (used when a failed append created it). */\n\tremove(path: string): void;\n\t/** Enumerate session locations inside a directory. Missing directories throw. */\n\tlistSessionFiles(dir: string): string[];\n\t/** Modification time of a session location, used for recent-session ordering. */\n\tmodifiedTime(path: string): Date;\n\t/**\n\t * Optional cross-process write guard (D-067 phase 2 S5). When present,\n\t * `SessionManager` calls `ensureWritable` before every record write and a\n\t * conflicting live foreign owner fails the write explicitly; absent, no\n\t * arbitration happens (the pre-S5 behavior).\n\t */\n\treadonly guard?: SessionWriteGuardV1;\n}\n\n/**\n * Cross-process write guard for one session location (D-067 phase 2 S5).\n * Implementations must make \"check and take ownership\" atomic against other\n * processes, and must throw a descriptive error when another live process\n * owns the session instead of letting two writers interleave entries.\n */\nexport interface SessionWriteGuardV1 {\n\t/**\n\t * Verifies the caller may write the session and records (or refreshes) its\n\t * ownership. Throws when another live process owns the session.\n\t */\n\tensureWritable(sessionFile: string, writerId: string): void;\n}\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\t// Skip malformed lines\n\t\treturn null;\n\t}\n}\n\n/** Serialize one entry as a protocol line (JSON body plus terminating newline). */\nfunction serializeEntryLine(entry: FileEntry): string {\n\treturn `${JSON.stringify(entry)}\\n`;\n}\n\nfunction parseSessionHeaderCandidate(line: string): SessionHeader | null | undefined {\n\tif (!line.trim()) return undefined;\n\tconst entry = parseSessionEntryLine(line);\n\tif (!entry) return undefined;\n\tif (entry.type !== \"session\" || typeof (entry as { id?: unknown }).id !== \"string\") return null;\n\treturn entry;\n}\n\n/** Suffix of the session write-guard sibling file (`<sessionFile>.guard`). */\nconst GUARD_FILE_SUFFIX = \".guard\";\n/** Wall-clock wait between guard-lock acquisition attempts (ledger protocol value). */\nconst GUARD_LOCK_POLL_INTERVAL_MS = 25;\n/** Default ceiling on waiting for a competing guard-lock holder. */\nconst GUARD_LOCK_TIMEOUT_MS = 5000;\n/** Default age at which a guard-lock counts as abandoned and is stolen. */\nconst GUARD_STALE_LOCK_MS = 60_000;\n\ninterface SessionGuardTokenV1 {\n\treadonly writerId: string;\n\treadonly hostId: string;\n\treadonly pid: number;\n}\n\nfunction defaultPidAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\t// EPERM means the process exists but is owned by another user — alive.\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\n/** Writes every byte of `data` to the fd, looping over partial writeSync results. */\nfunction writeAllSync(fd: number, data: string): void {\n\tconst buffer = Buffer.from(data, \"utf8\");\n\tlet written = 0;\n\twhile (written < buffer.length) {\n\t\twritten += writeSync(fd, buffer, written, buffer.length - written);\n\t}\n}\n\n/**\n * Takes the guard's cross-process lock (same protocol as the memory ledger's\n * and the durable store's persist locks — exclusive create, poll, mtime\n * stale-lock stealing, token-guarded release; the three implementations stay\n * behaviorally identical by precedent, deliberately without module coupling).\n */\nfunction acquireGuardLock(lockPath: string, lockTimeoutMs: number, staleLockMs: number): string {\n\tconst deadline = Date.now() + lockTimeoutMs;\n\tfor (;;) {\n\t\tmkdirSync(dirname(lockPath), { recursive: true });\n\t\tconst token = `${process.pid}:${Date.now()}`;\n\t\ttry {\n\t\t\tconst fd = openSync(lockPath, \"wx\");\n\t\t\ttry {\n\t\t\t\twriteFileSync(fd, token);\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\t// POSIX maps a lost create race to EEXIST; Windows/libuv maps the\n\t\t\t// same concurrent-create sharing violation to EPERM. For EPERM a\n\t\t\t// vanished lock retries, a persistent permission failure propagates.\n\t\t\tif (code === \"EPERM\") {\n\t\t\t\tlet vanished = false;\n\t\t\t\ttry {\n\t\t\t\t\tstatSync(lockPath);\n\t\t\t\t} catch (statError) {\n\t\t\t\t\tif ((statError as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\t\t\tvanished = true;\n\t\t\t\t}\n\t\t\t\tif (vanished) {\n\t\t\t\t\tif (Date.now() >= deadline) throw error;\n\t\t\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (code !== \"EEXIST\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tstale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;\n\t\t\t} catch {\n\t\t\t\t// The holder released between the failed open and the stat.\n\t\t\t}\n\t\t\tif (stale) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(lockPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already stolen — retry.\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"session guard lock timeout after {timeoutMs} ms: {lockPath}\", { timeoutMs: lockTimeoutMs, lockPath }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t}\n\t}\n}\n\n/** Releases the guard lock, but only if our own token still owns it. */\nfunction releaseGuardLock(lockPath: string, token: string): void {\n\ttry {\n\t\tif (readFileSync(lockPath, \"utf8\") === token) unlinkSync(lockPath);\n\t} catch {\n\t\t// Already gone (or unreadable) — nothing to release.\n\t}\n}\n\n/**\n * The default provider: the local JSONL file protocol, moved verbatim from\n * `SessionManager`. Stateless beyond the injectable guard identity options,\n * so a shared module-level instance is the default for every session manager\n * that does not inject its own provider.\n */\nexport class FileSessionStorageProvider implements SessionStorageProvider {\n\t/** Shared stateless instance used as the default provider. */\n\tstatic readonly instance: FileSessionStorageProvider = new FileSessionStorageProvider();\n\n\tprivate readonly hostId: string;\n\tprivate readonly ownPid: number;\n\tprivate readonly pidAlive: (pid: number) => boolean;\n\n\tconstructor(\n\t\toptions: {\n\t\t\t/** Host identity written into guard tokens; defaults to `os.hostname()`. */\n\t\t\treadonly hostId?: string;\n\t\t\t/** Process identity written into guard tokens; defaults to `process.pid`. */\n\t\t\treadonly ownPid?: number;\n\t\t\t/** Injectable liveness check for deterministic tests; defaults to `process.kill(pid, 0)`. */\n\t\t\treadonly pidAlive?: (pid: number) => boolean;\n\t\t} = {},\n\t) {\n\t\tthis.hostId = options.hostId ?? hostname();\n\t\tthis.ownPid = options.ownPid ?? process.pid;\n\t\tthis.pidAlive = options.pidAlive ?? defaultPidAlive;\n\t}\n\n\tensureDir(dir: string): void {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\texists(path: string): boolean {\n\t\treturn existsSync(path);\n\t}\n\n\tsize(path: string): number {\n\t\treturn statSync(path).size;\n\t}\n\n\tloadEntries(path: string): FileEntry[] {\n\t\tconst resolvedFilePath = normalizePath(path);\n\t\tif (!existsSync(resolvedFilePath)) return [];\n\n\t\tconst entries: FileEntry[] = [];\n\t\tlet pending = \"\";\n\t\tconst fd = openSync(resolvedFilePath, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tpending = pending.slice(lineStart);\n\t\t\t}\n\n\t\t\tpending += decoder.end();\n\t\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\t\tif (finalEntry) entries.push(finalEntry);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\n\t\t// Validate session header before repairing the file.\n\t\tif (entries.length === 0) return entries;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\" || typeof (header as { id?: unknown }).id !== \"string\") {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (pending) appendFileSync(resolvedFilePath, \"\\n\");\n\t\treturn entries;\n\t}\n\n\treadHeader(path: string): SessionHeader | null {\n\t\tif (!existsSync(path)) return null;\n\t\tconst fd = openSync(path, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE);\n\t\t\tconst lineChunks: string[] = [];\n\t\t\tlet scannedBytes = 0;\n\n\t\t\twhile (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) {\n\t\t\t\tconst readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes);\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, readLength, null);\n\t\t\t\tif (bytesRead === 0) {\n\t\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t\t}\n\t\t\t\tscannedBytes += bytesRead;\n\n\t\t\t\tconst chunk = decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tlineChunks.push(chunk.slice(lineStart, newlineIndex));\n\t\t\t\t\tconst header = parseSessionHeaderCandidate(lineChunks.join(\"\"));\n\t\t\t\t\tif (header !== undefined) return header;\n\t\t\t\t\tlineChunks.length = 0;\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tlineChunks.push(chunk.slice(lineStart));\n\t\t\t}\n\n\t\t\t// Probe for EOF so a final header without a newline is allowed when it ends\n\t\t\t// exactly at the scan limit. Any additional byte exceeds the bounded scan.\n\t\t\tconst probe = Buffer.allocUnsafe(1);\n\t\t\tif (readSync(fd, probe, 0, probe.length, null) === 0) {\n\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t}\n\t\t\tthrow new SessionHeaderScanLimitError(path);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\trewrite(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"w\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"wx\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tappend(path: string, entry: FileEntry): void {\n\t\tappendFileSync(path, serializeEntryLine(entry));\n\t}\n\n\ttruncate(path: string, size: number): void {\n\t\ttruncateSync(path, size);\n\t}\n\n\tremove(path: string): void {\n\t\tunlinkSync(path);\n\t}\n\n\tlistSessionFiles(dir: string): string[] {\n\t\tconst resolvedDir = normalizePath(dir);\n\t\treturn readdirSync(resolvedDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedDir, f));\n\t}\n\n\tmodifiedTime(path: string): Date {\n\t\treturn statSync(path).mtime;\n\t}\n\n\t/**\n\t * Cross-process write guard (D-067 phase 2 S5): `<sessionFile>.guard`\n\t * carries `{ writerId, hostId, pid }`. A write conflicts only while the\n\t * recorded owner is a live process on this host; a dead owner (closed or\n\t * crashed process) and same-process writers never conflict, and cross-host\n\t * guards are treated as live foreign owners (conservative over shared\n\t * filesystems). Check-and-take-ownership is atomic under the guard lock.\n\t */\n\treadonly guard: SessionWriteGuardV1 = {\n\t\tensureWritable: (sessionFile: string, writerId: string): void => {\n\t\t\tconst guardPath = `${sessionFile}${GUARD_FILE_SUFFIX}`;\n\t\t\tconst readToken = (): SessionGuardTokenV1 | undefined => {\n\t\t\t\ttry {\n\t\t\t\t\tconst value: unknown = JSON.parse(readFileSync(guardPath, \"utf8\"));\n\t\t\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n\t\t\t\t\tconst record = value as { writerId?: unknown; hostId?: unknown; pid?: unknown };\n\t\t\t\t\tif (typeof record.writerId !== \"string\" || record.writerId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.hostId !== \"string\" || record.hostId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.pid !== \"number\" || !Number.isInteger(record.pid)) return undefined;\n\t\t\t\t\treturn { writerId: record.writerId, hostId: record.hostId, pid: record.pid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst current = readToken();\n\t\t\t// Fast path: already ours (most appends), or no owner.\n\t\t\tif (current !== undefined && current.writerId === writerId) return;\n\t\t\tif (current !== undefined) {\n\t\t\t\tconst foreignLive =\n\t\t\t\t\tcurrent.hostId === this.hostId && current.pid !== this.ownPid && this.pidAlive(current.pid);\n\t\t\t\tif (foreignLive) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\"Session file is owned by another live process (pid {pid}): {sessionFile}. Close that process first; concurrent writers would silently fork the session history.\",\n\t\t\t\t\t\t\t{ pid: current.pid, sessionFile },\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Free, dead owner, or same-process writer: take (or refresh) ownership.\n\t\t\tconst token = acquireGuardLock(`${guardPath}.lock`, GUARD_LOCK_TIMEOUT_MS, GUARD_STALE_LOCK_MS);\n\t\t\ttry {\n\t\t\t\tconst fd = openSync(guardPath, \"w\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(\n\t\t\t\t\t\tfd,\n\t\t\t\t\t\tJSON.stringify({ writerId, hostId: this.hostId, pid: this.ownPid } satisfies SessionGuardTokenV1),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treleaseGuardLock(`${guardPath}.lock`, token);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -26,6 +26,7 @@ import { dirname, join } from "path";
26
26
  import { StringDecoder } from "string_decoder";
27
27
  import { normalizePath } from "../utils/paths.js";
28
28
  import { sleepSync } from "../utils/sync-sleep.js";
29
+ import { t } from "./i18n/index.js";
29
30
  const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
30
31
  const SESSION_HEADER_READ_BUFFER_SIZE = 4096;
31
32
  /** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */
@@ -155,7 +156,7 @@ function acquireGuardLock(lockPath, lockTimeoutMs, staleLockMs) {
155
156
  continue;
156
157
  }
157
158
  if (Date.now() >= deadline) {
158
- throw new Error(`session guard lock timeout after ${lockTimeoutMs} ms: ${lockPath}`);
159
+ throw new Error(t("session guard lock timeout after {timeoutMs} ms: {lockPath}", { timeoutMs: lockTimeoutMs, lockPath }));
159
160
  }
160
161
  sleepSync(GUARD_LOCK_POLL_INTERVAL_MS);
161
162
  }
@@ -362,8 +363,7 @@ export class FileSessionStorageProvider {
362
363
  if (current !== undefined) {
363
364
  const foreignLive = current.hostId === this.hostId && current.pid !== this.ownPid && this.pidAlive(current.pid);
364
365
  if (foreignLive) {
365
- throw new Error(`Session file is owned by another live process (pid ${current.pid}): ${sessionFile}. ` +
366
- "Close that process first; concurrent writers would silently fork the session history.");
366
+ throw new Error(t("Session file is owned by another live process (pid {pid}): {sessionFile}. Close that process first; concurrent writers would silently fork the session history.", { pid: current.pid, sessionFile }));
367
367
  }
368
368
  }
369
369
  // Free, dead owner, or same-process writer: take (or refresh) ownership.
@@ -1 +1 @@
1
- {"version":3,"file":"session-storage.js","sourceRoot":"","sources":["../../src/core/session-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Storage seam for process sessions (D-067 phase 1).\n *\n * `SessionManager` owns the session tree, index, and context logic; a\n * `SessionStorageProvider` owns only physical record storage for one session\n * location. The default implementation is the current local JSONL file\n * protocol, byte-identical to the pre-seam behavior; alternative providers\n * (in-memory for tests, remote backends later) implement the same contract.\n *\n * Scope line: discovery/enrichment reads used by `SessionManager.list` /\n * `listAll` (async streamed `SessionInfo` construction) stay on the fs path\n * for now; they move behind this seam in a later phase.\n *\n * Cross-process write arbitration (D-067 phase 2 S5): providers may expose an\n * optional {@link SessionWriteGuardV1}. The default file provider implements\n * it as a `<sessionFile>.guard` sibling token ({ writerId, hostId, pid }): a\n * write fails explicitly while another LIVE process on the same host owns the\n * session, a dead owner (closed/crashed process) is taken over silently, and\n * same-process writers are never conflicted (in-process sequencing is the\n * caller's concern, unchanged). Cross-host guards are treated as live\n * foreign owners (conservative: no false takeover over shared filesystems).\n */\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadFileSync,\n\treadSync,\n\tstatSync,\n\ttruncateSync,\n\tunlinkSync,\n\twriteFileSync,\n\twriteSync,\n} from \"fs\";\nimport { hostname } from \"os\";\nimport { dirname, join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { sleepSync } from \"../utils/sync-sleep.ts\";\nimport type { FileEntry, SessionHeader } from \"./session-manager.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\nconst SESSION_HEADER_READ_BUFFER_SIZE = 4096;\n/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */\nconst MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024;\n\n/**\n * Thrown by `readHeader` when the header scan exceeds the bounded limit.\n * `SessionManager.open` treats it as a discovery optimization miss and falls\n * back to a full load; discovery callers swallow it.\n */\nexport class SessionHeaderScanLimitError extends Error {\n\tconstructor(filePath: string) {\n\t\tsuper(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`);\n\t\tthis.name = \"SessionHeaderScanLimitError\";\n\t}\n}\n\n/**\n * Record-level storage contract for sessions, keyed by location path. All\n * methods are synchronous and reflect the append-only JSONL protocol\n * semantics; implementations for other media map the same operations onto\n * their own storage model.\n */\nexport interface SessionStorageProvider {\n\t/** Ensure the storage location for a session directory exists. */\n\tensureDir(dir: string): void;\n\t/** True when the given session location exists. */\n\texists(path: string): boolean;\n\t/** Current byte size of a session location (rollback baseline). Missing locations throw. */\n\tsize(path: string): number;\n\t/**\n\t * Load all entries (header first). A missing location yields `[]`. When the\n\t * stored content ends with an unterminated final line, implementations\n\t * repair it by terminating it (the file protocol appends a `\\n`).\n\t */\n\tloadEntries(path: string): FileEntry[];\n\t/** Bounded header scan used for discovery and cwd resolution. Missing or header-less locations yield `null`. */\n\treadHeader(path: string): SessionHeader | null;\n\t/** Replace the whole stored content (header included). */\n\trewrite(path: string, entries: readonly FileEntry[]): void;\n\t/** Write initial content; fails when the location already exists (\"wx\" semantics). */\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void;\n\t/** Append one entry after the existing content. */\n\tappend(path: string, entry: FileEntry): void;\n\t/** Roll back to a previous byte size after a failed append. */\n\ttruncate(path: string, size: number): void;\n\t/** Remove a session location (used when a failed append created it). */\n\tremove(path: string): void;\n\t/** Enumerate session locations inside a directory. Missing directories throw. */\n\tlistSessionFiles(dir: string): string[];\n\t/** Modification time of a session location, used for recent-session ordering. */\n\tmodifiedTime(path: string): Date;\n\t/**\n\t * Optional cross-process write guard (D-067 phase 2 S5). When present,\n\t * `SessionManager` calls `ensureWritable` before every record write and a\n\t * conflicting live foreign owner fails the write explicitly; absent, no\n\t * arbitration happens (the pre-S5 behavior).\n\t */\n\treadonly guard?: SessionWriteGuardV1;\n}\n\n/**\n * Cross-process write guard for one session location (D-067 phase 2 S5).\n * Implementations must make \"check and take ownership\" atomic against other\n * processes, and must throw a descriptive error when another live process\n * owns the session instead of letting two writers interleave entries.\n */\nexport interface SessionWriteGuardV1 {\n\t/**\n\t * Verifies the caller may write the session and records (or refreshes) its\n\t * ownership. Throws when another live process owns the session.\n\t */\n\tensureWritable(sessionFile: string, writerId: string): void;\n}\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\t// Skip malformed lines\n\t\treturn null;\n\t}\n}\n\n/** Serialize one entry as a protocol line (JSON body plus terminating newline). */\nfunction serializeEntryLine(entry: FileEntry): string {\n\treturn `${JSON.stringify(entry)}\\n`;\n}\n\nfunction parseSessionHeaderCandidate(line: string): SessionHeader | null | undefined {\n\tif (!line.trim()) return undefined;\n\tconst entry = parseSessionEntryLine(line);\n\tif (!entry) return undefined;\n\tif (entry.type !== \"session\" || typeof (entry as { id?: unknown }).id !== \"string\") return null;\n\treturn entry;\n}\n\n/** Suffix of the session write-guard sibling file (`<sessionFile>.guard`). */\nconst GUARD_FILE_SUFFIX = \".guard\";\n/** Wall-clock wait between guard-lock acquisition attempts (ledger protocol value). */\nconst GUARD_LOCK_POLL_INTERVAL_MS = 25;\n/** Default ceiling on waiting for a competing guard-lock holder. */\nconst GUARD_LOCK_TIMEOUT_MS = 5000;\n/** Default age at which a guard-lock counts as abandoned and is stolen. */\nconst GUARD_STALE_LOCK_MS = 60_000;\n\ninterface SessionGuardTokenV1 {\n\treadonly writerId: string;\n\treadonly hostId: string;\n\treadonly pid: number;\n}\n\nfunction defaultPidAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\t// EPERM means the process exists but is owned by another user — alive.\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\n/** Writes every byte of `data` to the fd, looping over partial writeSync results. */\nfunction writeAllSync(fd: number, data: string): void {\n\tconst buffer = Buffer.from(data, \"utf8\");\n\tlet written = 0;\n\twhile (written < buffer.length) {\n\t\twritten += writeSync(fd, buffer, written, buffer.length - written);\n\t}\n}\n\n/**\n * Takes the guard's cross-process lock (same protocol as the memory ledger's\n * and the durable store's persist locks — exclusive create, poll, mtime\n * stale-lock stealing, token-guarded release; the three implementations stay\n * behaviorally identical by precedent, deliberately without module coupling).\n */\nfunction acquireGuardLock(lockPath: string, lockTimeoutMs: number, staleLockMs: number): string {\n\tconst deadline = Date.now() + lockTimeoutMs;\n\tfor (;;) {\n\t\tmkdirSync(dirname(lockPath), { recursive: true });\n\t\tconst token = `${process.pid}:${Date.now()}`;\n\t\ttry {\n\t\t\tconst fd = openSync(lockPath, \"wx\");\n\t\t\ttry {\n\t\t\t\twriteFileSync(fd, token);\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\t// POSIX maps a lost create race to EEXIST; Windows/libuv maps the\n\t\t\t// same concurrent-create sharing violation to EPERM. For EPERM a\n\t\t\t// vanished lock retries, a persistent permission failure propagates.\n\t\t\tif (code === \"EPERM\") {\n\t\t\t\tlet vanished = false;\n\t\t\t\ttry {\n\t\t\t\t\tstatSync(lockPath);\n\t\t\t\t} catch (statError) {\n\t\t\t\t\tif ((statError as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\t\t\tvanished = true;\n\t\t\t\t}\n\t\t\t\tif (vanished) {\n\t\t\t\t\tif (Date.now() >= deadline) throw error;\n\t\t\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (code !== \"EEXIST\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tstale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;\n\t\t\t} catch {\n\t\t\t\t// The holder released between the failed open and the stat.\n\t\t\t}\n\t\t\tif (stale) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(lockPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already stolen — retry.\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(`session guard lock timeout after ${lockTimeoutMs} ms: ${lockPath}`);\n\t\t\t}\n\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t}\n\t}\n}\n\n/** Releases the guard lock, but only if our own token still owns it. */\nfunction releaseGuardLock(lockPath: string, token: string): void {\n\ttry {\n\t\tif (readFileSync(lockPath, \"utf8\") === token) unlinkSync(lockPath);\n\t} catch {\n\t\t// Already gone (or unreadable) — nothing to release.\n\t}\n}\n\n/**\n * The default provider: the local JSONL file protocol, moved verbatim from\n * `SessionManager`. Stateless beyond the injectable guard identity options,\n * so a shared module-level instance is the default for every session manager\n * that does not inject its own provider.\n */\nexport class FileSessionStorageProvider implements SessionStorageProvider {\n\t/** Shared stateless instance used as the default provider. */\n\tstatic readonly instance: FileSessionStorageProvider = new FileSessionStorageProvider();\n\n\tprivate readonly hostId: string;\n\tprivate readonly ownPid: number;\n\tprivate readonly pidAlive: (pid: number) => boolean;\n\n\tconstructor(\n\t\toptions: {\n\t\t\t/** Host identity written into guard tokens; defaults to `os.hostname()`. */\n\t\t\treadonly hostId?: string;\n\t\t\t/** Process identity written into guard tokens; defaults to `process.pid`. */\n\t\t\treadonly ownPid?: number;\n\t\t\t/** Injectable liveness check for deterministic tests; defaults to `process.kill(pid, 0)`. */\n\t\t\treadonly pidAlive?: (pid: number) => boolean;\n\t\t} = {},\n\t) {\n\t\tthis.hostId = options.hostId ?? hostname();\n\t\tthis.ownPid = options.ownPid ?? process.pid;\n\t\tthis.pidAlive = options.pidAlive ?? defaultPidAlive;\n\t}\n\n\tensureDir(dir: string): void {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\texists(path: string): boolean {\n\t\treturn existsSync(path);\n\t}\n\n\tsize(path: string): number {\n\t\treturn statSync(path).size;\n\t}\n\n\tloadEntries(path: string): FileEntry[] {\n\t\tconst resolvedFilePath = normalizePath(path);\n\t\tif (!existsSync(resolvedFilePath)) return [];\n\n\t\tconst entries: FileEntry[] = [];\n\t\tlet pending = \"\";\n\t\tconst fd = openSync(resolvedFilePath, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tpending = pending.slice(lineStart);\n\t\t\t}\n\n\t\t\tpending += decoder.end();\n\t\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\t\tif (finalEntry) entries.push(finalEntry);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\n\t\t// Validate session header before repairing the file.\n\t\tif (entries.length === 0) return entries;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\" || typeof (header as { id?: unknown }).id !== \"string\") {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (pending) appendFileSync(resolvedFilePath, \"\\n\");\n\t\treturn entries;\n\t}\n\n\treadHeader(path: string): SessionHeader | null {\n\t\tif (!existsSync(path)) return null;\n\t\tconst fd = openSync(path, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE);\n\t\t\tconst lineChunks: string[] = [];\n\t\t\tlet scannedBytes = 0;\n\n\t\t\twhile (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) {\n\t\t\t\tconst readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes);\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, readLength, null);\n\t\t\t\tif (bytesRead === 0) {\n\t\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t\t}\n\t\t\t\tscannedBytes += bytesRead;\n\n\t\t\t\tconst chunk = decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tlineChunks.push(chunk.slice(lineStart, newlineIndex));\n\t\t\t\t\tconst header = parseSessionHeaderCandidate(lineChunks.join(\"\"));\n\t\t\t\t\tif (header !== undefined) return header;\n\t\t\t\t\tlineChunks.length = 0;\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tlineChunks.push(chunk.slice(lineStart));\n\t\t\t}\n\n\t\t\t// Probe for EOF so a final header without a newline is allowed when it ends\n\t\t\t// exactly at the scan limit. Any additional byte exceeds the bounded scan.\n\t\t\tconst probe = Buffer.allocUnsafe(1);\n\t\t\tif (readSync(fd, probe, 0, probe.length, null) === 0) {\n\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t}\n\t\t\tthrow new SessionHeaderScanLimitError(path);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\trewrite(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"w\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"wx\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tappend(path: string, entry: FileEntry): void {\n\t\tappendFileSync(path, serializeEntryLine(entry));\n\t}\n\n\ttruncate(path: string, size: number): void {\n\t\ttruncateSync(path, size);\n\t}\n\n\tremove(path: string): void {\n\t\tunlinkSync(path);\n\t}\n\n\tlistSessionFiles(dir: string): string[] {\n\t\tconst resolvedDir = normalizePath(dir);\n\t\treturn readdirSync(resolvedDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedDir, f));\n\t}\n\n\tmodifiedTime(path: string): Date {\n\t\treturn statSync(path).mtime;\n\t}\n\n\t/**\n\t * Cross-process write guard (D-067 phase 2 S5): `<sessionFile>.guard`\n\t * carries `{ writerId, hostId, pid }`. A write conflicts only while the\n\t * recorded owner is a live process on this host; a dead owner (closed or\n\t * crashed process) and same-process writers never conflict, and cross-host\n\t * guards are treated as live foreign owners (conservative over shared\n\t * filesystems). Check-and-take-ownership is atomic under the guard lock.\n\t */\n\treadonly guard: SessionWriteGuardV1 = {\n\t\tensureWritable: (sessionFile: string, writerId: string): void => {\n\t\t\tconst guardPath = `${sessionFile}${GUARD_FILE_SUFFIX}`;\n\t\t\tconst readToken = (): SessionGuardTokenV1 | undefined => {\n\t\t\t\ttry {\n\t\t\t\t\tconst value: unknown = JSON.parse(readFileSync(guardPath, \"utf8\"));\n\t\t\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n\t\t\t\t\tconst record = value as { writerId?: unknown; hostId?: unknown; pid?: unknown };\n\t\t\t\t\tif (typeof record.writerId !== \"string\" || record.writerId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.hostId !== \"string\" || record.hostId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.pid !== \"number\" || !Number.isInteger(record.pid)) return undefined;\n\t\t\t\t\treturn { writerId: record.writerId, hostId: record.hostId, pid: record.pid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst current = readToken();\n\t\t\t// Fast path: already ours (most appends), or no owner.\n\t\t\tif (current !== undefined && current.writerId === writerId) return;\n\t\t\tif (current !== undefined) {\n\t\t\t\tconst foreignLive =\n\t\t\t\t\tcurrent.hostId === this.hostId && current.pid !== this.ownPid && this.pidAlive(current.pid);\n\t\t\t\tif (foreignLive) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Session file is owned by another live process (pid ${current.pid}): ${sessionFile}. ` +\n\t\t\t\t\t\t\t\"Close that process first; concurrent writers would silently fork the session history.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Free, dead owner, or same-process writer: take (or refresh) ownership.\n\t\t\tconst token = acquireGuardLock(`${guardPath}.lock`, GUARD_LOCK_TIMEOUT_MS, GUARD_STALE_LOCK_MS);\n\t\t\ttry {\n\t\t\t\tconst fd = openSync(guardPath, \"w\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(\n\t\t\t\t\t\tfd,\n\t\t\t\t\t\tJSON.stringify({ writerId, hostId: this.hostId, pid: this.ownPid } satisfies SessionGuardTokenV1),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treleaseGuardLock(`${guardPath}.lock`, token);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"session-storage.js","sourceRoot":"","sources":["../../src/core/session-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Storage seam for process sessions (D-067 phase 1).\n *\n * `SessionManager` owns the session tree, index, and context logic; a\n * `SessionStorageProvider` owns only physical record storage for one session\n * location. The default implementation is the current local JSONL file\n * protocol, byte-identical to the pre-seam behavior; alternative providers\n * (in-memory for tests, remote backends later) implement the same contract.\n *\n * Scope line: discovery/enrichment reads used by `SessionManager.list` /\n * `listAll` (async streamed `SessionInfo` construction) stay on the fs path\n * for now; they move behind this seam in a later phase.\n *\n * Cross-process write arbitration (D-067 phase 2 S5): providers may expose an\n * optional {@link SessionWriteGuardV1}. The default file provider implements\n * it as a `<sessionFile>.guard` sibling token ({ writerId, hostId, pid }): a\n * write fails explicitly while another LIVE process on the same host owns the\n * session, a dead owner (closed/crashed process) is taken over silently, and\n * same-process writers are never conflicted (in-process sequencing is the\n * caller's concern, unchanged). Cross-host guards are treated as live\n * foreign owners (conservative: no false takeover over shared filesystems).\n */\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadFileSync,\n\treadSync,\n\tstatSync,\n\ttruncateSync,\n\tunlinkSync,\n\twriteFileSync,\n\twriteSync,\n} from \"fs\";\nimport { hostname } from \"os\";\nimport { dirname, join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { sleepSync } from \"../utils/sync-sleep.ts\";\nimport { t } from \"./i18n/index.ts\";\nimport type { FileEntry, SessionHeader } from \"./session-manager.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\nconst SESSION_HEADER_READ_BUFFER_SIZE = 4096;\n/** Bound synchronous header discovery while allowing large cwd and custom metadata fields. */\nconst MAX_SESSION_HEADER_SCAN_BYTES = 1024 * 1024;\n\n/**\n * Thrown by `readHeader` when the header scan exceeds the bounded limit.\n * `SessionManager.open` treats it as a discovery optimization miss and falls\n * back to a full load; discovery callers swallow it.\n */\nexport class SessionHeaderScanLimitError extends Error {\n\tconstructor(filePath: string) {\n\t\tsuper(`Session header exceeds ${MAX_SESSION_HEADER_SCAN_BYTES}-byte scan limit: ${filePath}`);\n\t\tthis.name = \"SessionHeaderScanLimitError\";\n\t}\n}\n\n/**\n * Record-level storage contract for sessions, keyed by location path. All\n * methods are synchronous and reflect the append-only JSONL protocol\n * semantics; implementations for other media map the same operations onto\n * their own storage model.\n */\nexport interface SessionStorageProvider {\n\t/** Ensure the storage location for a session directory exists. */\n\tensureDir(dir: string): void;\n\t/** True when the given session location exists. */\n\texists(path: string): boolean;\n\t/** Current byte size of a session location (rollback baseline). Missing locations throw. */\n\tsize(path: string): number;\n\t/**\n\t * Load all entries (header first). A missing location yields `[]`. When the\n\t * stored content ends with an unterminated final line, implementations\n\t * repair it by terminating it (the file protocol appends a `\\n`).\n\t */\n\tloadEntries(path: string): FileEntry[];\n\t/** Bounded header scan used for discovery and cwd resolution. Missing or header-less locations yield `null`. */\n\treadHeader(path: string): SessionHeader | null;\n\t/** Replace the whole stored content (header included). */\n\trewrite(path: string, entries: readonly FileEntry[]): void;\n\t/** Write initial content; fails when the location already exists (\"wx\" semantics). */\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void;\n\t/** Append one entry after the existing content. */\n\tappend(path: string, entry: FileEntry): void;\n\t/** Roll back to a previous byte size after a failed append. */\n\ttruncate(path: string, size: number): void;\n\t/** Remove a session location (used when a failed append created it). */\n\tremove(path: string): void;\n\t/** Enumerate session locations inside a directory. Missing directories throw. */\n\tlistSessionFiles(dir: string): string[];\n\t/** Modification time of a session location, used for recent-session ordering. */\n\tmodifiedTime(path: string): Date;\n\t/**\n\t * Optional cross-process write guard (D-067 phase 2 S5). When present,\n\t * `SessionManager` calls `ensureWritable` before every record write and a\n\t * conflicting live foreign owner fails the write explicitly; absent, no\n\t * arbitration happens (the pre-S5 behavior).\n\t */\n\treadonly guard?: SessionWriteGuardV1;\n}\n\n/**\n * Cross-process write guard for one session location (D-067 phase 2 S5).\n * Implementations must make \"check and take ownership\" atomic against other\n * processes, and must throw a descriptive error when another live process\n * owns the session instead of letting two writers interleave entries.\n */\nexport interface SessionWriteGuardV1 {\n\t/**\n\t * Verifies the caller may write the session and records (or refreshes) its\n\t * ownership. Throws when another live process owns the session.\n\t */\n\tensureWritable(sessionFile: string, writerId: string): void;\n}\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\t// Skip malformed lines\n\t\treturn null;\n\t}\n}\n\n/** Serialize one entry as a protocol line (JSON body plus terminating newline). */\nfunction serializeEntryLine(entry: FileEntry): string {\n\treturn `${JSON.stringify(entry)}\\n`;\n}\n\nfunction parseSessionHeaderCandidate(line: string): SessionHeader | null | undefined {\n\tif (!line.trim()) return undefined;\n\tconst entry = parseSessionEntryLine(line);\n\tif (!entry) return undefined;\n\tif (entry.type !== \"session\" || typeof (entry as { id?: unknown }).id !== \"string\") return null;\n\treturn entry;\n}\n\n/** Suffix of the session write-guard sibling file (`<sessionFile>.guard`). */\nconst GUARD_FILE_SUFFIX = \".guard\";\n/** Wall-clock wait between guard-lock acquisition attempts (ledger protocol value). */\nconst GUARD_LOCK_POLL_INTERVAL_MS = 25;\n/** Default ceiling on waiting for a competing guard-lock holder. */\nconst GUARD_LOCK_TIMEOUT_MS = 5000;\n/** Default age at which a guard-lock counts as abandoned and is stolen. */\nconst GUARD_STALE_LOCK_MS = 60_000;\n\ninterface SessionGuardTokenV1 {\n\treadonly writerId: string;\n\treadonly hostId: string;\n\treadonly pid: number;\n}\n\nfunction defaultPidAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\t// EPERM means the process exists but is owned by another user — alive.\n\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\";\n\t}\n}\n\n/** Writes every byte of `data` to the fd, looping over partial writeSync results. */\nfunction writeAllSync(fd: number, data: string): void {\n\tconst buffer = Buffer.from(data, \"utf8\");\n\tlet written = 0;\n\twhile (written < buffer.length) {\n\t\twritten += writeSync(fd, buffer, written, buffer.length - written);\n\t}\n}\n\n/**\n * Takes the guard's cross-process lock (same protocol as the memory ledger's\n * and the durable store's persist locks — exclusive create, poll, mtime\n * stale-lock stealing, token-guarded release; the three implementations stay\n * behaviorally identical by precedent, deliberately without module coupling).\n */\nfunction acquireGuardLock(lockPath: string, lockTimeoutMs: number, staleLockMs: number): string {\n\tconst deadline = Date.now() + lockTimeoutMs;\n\tfor (;;) {\n\t\tmkdirSync(dirname(lockPath), { recursive: true });\n\t\tconst token = `${process.pid}:${Date.now()}`;\n\t\ttry {\n\t\t\tconst fd = openSync(lockPath, \"wx\");\n\t\t\ttry {\n\t\t\t\twriteFileSync(fd, token);\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t\treturn token;\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\t// POSIX maps a lost create race to EEXIST; Windows/libuv maps the\n\t\t\t// same concurrent-create sharing violation to EPERM. For EPERM a\n\t\t\t// vanished lock retries, a persistent permission failure propagates.\n\t\t\tif (code === \"EPERM\") {\n\t\t\t\tlet vanished = false;\n\t\t\t\ttry {\n\t\t\t\t\tstatSync(lockPath);\n\t\t\t\t} catch (statError) {\n\t\t\t\t\tif ((statError as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\t\t\tvanished = true;\n\t\t\t\t}\n\t\t\t\tif (vanished) {\n\t\t\t\t\tif (Date.now() >= deadline) throw error;\n\t\t\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if (code !== \"EEXIST\") {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tstale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;\n\t\t\t} catch {\n\t\t\t\t// The holder released between the failed open and the stat.\n\t\t\t}\n\t\t\tif (stale) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(lockPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Already stolen — retry.\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Date.now() >= deadline) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tt(\"session guard lock timeout after {timeoutMs} ms: {lockPath}\", { timeoutMs: lockTimeoutMs, lockPath }),\n\t\t\t\t);\n\t\t\t}\n\t\t\tsleepSync(GUARD_LOCK_POLL_INTERVAL_MS);\n\t\t}\n\t}\n}\n\n/** Releases the guard lock, but only if our own token still owns it. */\nfunction releaseGuardLock(lockPath: string, token: string): void {\n\ttry {\n\t\tif (readFileSync(lockPath, \"utf8\") === token) unlinkSync(lockPath);\n\t} catch {\n\t\t// Already gone (or unreadable) — nothing to release.\n\t}\n}\n\n/**\n * The default provider: the local JSONL file protocol, moved verbatim from\n * `SessionManager`. Stateless beyond the injectable guard identity options,\n * so a shared module-level instance is the default for every session manager\n * that does not inject its own provider.\n */\nexport class FileSessionStorageProvider implements SessionStorageProvider {\n\t/** Shared stateless instance used as the default provider. */\n\tstatic readonly instance: FileSessionStorageProvider = new FileSessionStorageProvider();\n\n\tprivate readonly hostId: string;\n\tprivate readonly ownPid: number;\n\tprivate readonly pidAlive: (pid: number) => boolean;\n\n\tconstructor(\n\t\toptions: {\n\t\t\t/** Host identity written into guard tokens; defaults to `os.hostname()`. */\n\t\t\treadonly hostId?: string;\n\t\t\t/** Process identity written into guard tokens; defaults to `process.pid`. */\n\t\t\treadonly ownPid?: number;\n\t\t\t/** Injectable liveness check for deterministic tests; defaults to `process.kill(pid, 0)`. */\n\t\t\treadonly pidAlive?: (pid: number) => boolean;\n\t\t} = {},\n\t) {\n\t\tthis.hostId = options.hostId ?? hostname();\n\t\tthis.ownPid = options.ownPid ?? process.pid;\n\t\tthis.pidAlive = options.pidAlive ?? defaultPidAlive;\n\t}\n\n\tensureDir(dir: string): void {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n\n\texists(path: string): boolean {\n\t\treturn existsSync(path);\n\t}\n\n\tsize(path: string): number {\n\t\treturn statSync(path).size;\n\t}\n\n\tloadEntries(path: string): FileEntry[] {\n\t\tconst resolvedFilePath = normalizePath(path);\n\t\tif (!existsSync(resolvedFilePath)) return [];\n\n\t\tconst entries: FileEntry[] = [];\n\t\tlet pending = \"\";\n\t\tconst fd = openSync(resolvedFilePath, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tpending = pending.slice(lineStart);\n\t\t\t}\n\n\t\t\tpending += decoder.end();\n\t\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\t\tif (finalEntry) entries.push(finalEntry);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\n\t\t// Validate session header before repairing the file.\n\t\tif (entries.length === 0) return entries;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\" || typeof (header as { id?: unknown }).id !== \"string\") {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (pending) appendFileSync(resolvedFilePath, \"\\n\");\n\t\treturn entries;\n\t}\n\n\treadHeader(path: string): SessionHeader | null {\n\t\tif (!existsSync(path)) return null;\n\t\tconst fd = openSync(path, \"r\");\n\t\ttry {\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\tconst buffer = Buffer.allocUnsafe(SESSION_HEADER_READ_BUFFER_SIZE);\n\t\t\tconst lineChunks: string[] = [];\n\t\t\tlet scannedBytes = 0;\n\n\t\t\twhile (scannedBytes < MAX_SESSION_HEADER_SCAN_BYTES) {\n\t\t\t\tconst readLength = Math.min(buffer.length, MAX_SESSION_HEADER_SCAN_BYTES - scannedBytes);\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, readLength, null);\n\t\t\t\tif (bytesRead === 0) {\n\t\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t\t}\n\t\t\t\tscannedBytes += bytesRead;\n\n\t\t\t\tconst chunk = decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tlet lineStart = 0;\n\t\t\t\tlet newlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\t\tlineChunks.push(chunk.slice(lineStart, newlineIndex));\n\t\t\t\t\tconst header = parseSessionHeaderCandidate(lineChunks.join(\"\"));\n\t\t\t\t\tif (header !== undefined) return header;\n\t\t\t\t\tlineChunks.length = 0;\n\t\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\t\tnewlineIndex = chunk.indexOf(\"\\n\", lineStart);\n\t\t\t\t}\n\t\t\t\tlineChunks.push(chunk.slice(lineStart));\n\t\t\t}\n\n\t\t\t// Probe for EOF so a final header without a newline is allowed when it ends\n\t\t\t// exactly at the scan limit. Any additional byte exceeds the bounded scan.\n\t\t\tconst probe = Buffer.allocUnsafe(1);\n\t\t\tif (readSync(fd, probe, 0, probe.length, null) === 0) {\n\t\t\t\tlineChunks.push(decoder.end());\n\t\t\t\treturn parseSessionHeaderCandidate(lineChunks.join(\"\")) ?? null;\n\t\t\t}\n\t\t\tthrow new SessionHeaderScanLimitError(path);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\trewrite(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"w\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tcreateExclusive(path: string, entries: readonly FileEntry[]): void {\n\t\tconst fd = openSync(path, \"wx\");\n\t\ttry {\n\t\t\tfor (const entry of entries) {\n\t\t\t\twriteFileSync(fd, serializeEntryLine(entry));\n\t\t\t}\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tappend(path: string, entry: FileEntry): void {\n\t\tappendFileSync(path, serializeEntryLine(entry));\n\t}\n\n\ttruncate(path: string, size: number): void {\n\t\ttruncateSync(path, size);\n\t}\n\n\tremove(path: string): void {\n\t\tunlinkSync(path);\n\t}\n\n\tlistSessionFiles(dir: string): string[] {\n\t\tconst resolvedDir = normalizePath(dir);\n\t\treturn readdirSync(resolvedDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedDir, f));\n\t}\n\n\tmodifiedTime(path: string): Date {\n\t\treturn statSync(path).mtime;\n\t}\n\n\t/**\n\t * Cross-process write guard (D-067 phase 2 S5): `<sessionFile>.guard`\n\t * carries `{ writerId, hostId, pid }`. A write conflicts only while the\n\t * recorded owner is a live process on this host; a dead owner (closed or\n\t * crashed process) and same-process writers never conflict, and cross-host\n\t * guards are treated as live foreign owners (conservative over shared\n\t * filesystems). Check-and-take-ownership is atomic under the guard lock.\n\t */\n\treadonly guard: SessionWriteGuardV1 = {\n\t\tensureWritable: (sessionFile: string, writerId: string): void => {\n\t\t\tconst guardPath = `${sessionFile}${GUARD_FILE_SUFFIX}`;\n\t\t\tconst readToken = (): SessionGuardTokenV1 | undefined => {\n\t\t\t\ttry {\n\t\t\t\t\tconst value: unknown = JSON.parse(readFileSync(guardPath, \"utf8\"));\n\t\t\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n\t\t\t\t\tconst record = value as { writerId?: unknown; hostId?: unknown; pid?: unknown };\n\t\t\t\t\tif (typeof record.writerId !== \"string\" || record.writerId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.hostId !== \"string\" || record.hostId.length === 0) return undefined;\n\t\t\t\t\tif (typeof record.pid !== \"number\" || !Number.isInteger(record.pid)) return undefined;\n\t\t\t\t\treturn { writerId: record.writerId, hostId: record.hostId, pid: record.pid };\n\t\t\t\t} catch {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst current = readToken();\n\t\t\t// Fast path: already ours (most appends), or no owner.\n\t\t\tif (current !== undefined && current.writerId === writerId) return;\n\t\t\tif (current !== undefined) {\n\t\t\t\tconst foreignLive =\n\t\t\t\t\tcurrent.hostId === this.hostId && current.pid !== this.ownPid && this.pidAlive(current.pid);\n\t\t\t\tif (foreignLive) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\n\t\t\t\t\t\t\t\"Session file is owned by another live process (pid {pid}): {sessionFile}. Close that process first; concurrent writers would silently fork the session history.\",\n\t\t\t\t\t\t\t{ pid: current.pid, sessionFile },\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Free, dead owner, or same-process writer: take (or refresh) ownership.\n\t\t\tconst token = acquireGuardLock(`${guardPath}.lock`, GUARD_LOCK_TIMEOUT_MS, GUARD_STALE_LOCK_MS);\n\t\t\ttry {\n\t\t\t\tconst fd = openSync(guardPath, \"w\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(\n\t\t\t\t\t\tfd,\n\t\t\t\t\t\tJSON.stringify({ writerId, hostId: this.hostId, pid: this.ownPid } satisfies SessionGuardTokenV1),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treleaseGuardLock(`${guardPath}.lock`, token);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -1,17 +1,15 @@
1
1
  import type { ThinkingLevel } from "@agent-forge/agent-core";
2
2
  import { type Model, type Transport } from "@agent-forge/ai";
3
3
  import type { TuiMode as RendererTuiMode, ScrollViewScrollbar, TerminalCapabilities, WheelScrollLines } from "@agent-forge/tui";
4
+ import { type I18nLanguage } from "./i18n/index.ts";
4
5
  export interface CompactionModelOverride {
5
6
  reserveTokens?: number;
6
7
  keepRecentTokens?: number;
7
8
  }
8
9
  export interface CompactionSettings {
9
- enabled?: boolean;
10
10
  reserveTokens?: number;
11
11
  keepRecentTokens?: number;
12
12
  modelOverrides?: Record<string, CompactionModelOverride>;
13
- microcompactEnabled?: boolean;
14
- summarySelfCritique?: boolean;
15
13
  }
16
14
  export interface BranchSummarySettings {
17
15
  reserveTokens?: number;
@@ -23,7 +21,6 @@ export interface ProviderRetrySettings {
23
21
  maxRetryDelayMs?: number;
24
22
  }
25
23
  export interface RetrySettings {
26
- enabled?: boolean;
27
24
  maxRetries?: number;
28
25
  baseDelayMs?: number;
29
26
  maxAgentDelayMs?: number;
@@ -31,6 +28,13 @@ export interface RetrySettings {
31
28
  }
32
29
  export type TuiMode = RendererTuiMode;
33
30
  export type FullscreenExitOutput = "transcript" | "resume-hint";
31
+ /**
32
+ * 界面语言(docs/design/国际化与本地化.md):`ui.language` 持久配置面。
33
+ * env `AGENT_FORGE_I18N_LANG` 是更高优先的临时覆盖。
34
+ */
35
+ export interface UiSettings {
36
+ language?: I18nLanguage;
37
+ }
34
38
  export interface TerminalSettings {
35
39
  showImages?: boolean;
36
40
  imageWidthCells?: number;
@@ -95,7 +99,6 @@ export interface FailoverChainSettings {
95
99
  * Phase 2 的 provider-failover 插件的事,本层不实现、不校验、不消费。
96
100
  */
97
101
  export interface FailoverSettings {
98
- enabled?: boolean;
99
102
  backupProviders?: string[];
100
103
  failureThreshold?: number;
101
104
  cooldown?: FailoverCooldownSettings;
@@ -131,6 +134,7 @@ export interface Settings {
131
134
  steeringMode?: "all" | "one-at-a-time";
132
135
  followUpMode?: "all" | "one-at-a-time";
133
136
  theme?: string;
137
+ ui?: UiSettings;
134
138
  compaction?: CompactionSettings;
135
139
  branchSummary?: BranchSummarySettings;
136
140
  retry?: RetrySettings;
@@ -148,13 +152,12 @@ export interface Settings {
148
152
  skills?: string[];
149
153
  prompts?: string[];
150
154
  themes?: string[];
151
- enableSkillCommands?: boolean;
152
155
  terminal?: TerminalSettings;
153
156
  images?: ImageSettings;
154
157
  enabledModels?: string[];
155
158
  defaultTools?: string[];
156
159
  suite?: string;
157
- doubleEscapeAction?: "fork" | "tree" | "none";
160
+ doubleEscapeAction?: "tree" | "none";
158
161
  treeFilterMode?: "default" | "no-tools" | "user-only" | "labeled-only" | "all";
159
162
  thinkingBudgets?: ThinkingBudgetsSettings;
160
163
  editorPaddingX?: number;
@@ -302,28 +305,25 @@ export declare class SettingsManager {
302
305
  removeModelThinkingLevel(provider: string, modelId: string): void;
303
306
  getTransport(): TransportSetting;
304
307
  setTransport(transport: TransportSetting): void;
305
- getCompactionEnabled(): boolean;
306
- setCompactionEnabled(enabled: boolean): void;
307
308
  private getCompactionTokenSetting;
308
309
  getCompactionReserveTokens(model?: Pick<Model<string>, "provider" | "id">): number;
309
310
  getCompactionKeepRecentTokens(model?: Pick<Model<string>, "provider" | "id">): number;
310
- getSummarySelfCritique(): boolean;
311
- getMicrocompactEnabled(): boolean;
312
311
  /** Resolve each token setting through model override, ordinary setting, then built-in default. */
313
312
  getCompactionSettings(model?: Pick<Model<string>, "provider" | "id">): {
314
- enabled: boolean;
315
313
  reserveTokens: number;
316
314
  keepRecentTokens: number;
317
- microcompactEnabled: boolean;
318
- summarySelfCritique: boolean;
319
315
  };
320
316
  getBranchSummarySettings(): {
321
317
  reserveTokens: number;
322
318
  skipPrompt: boolean;
323
319
  };
324
320
  getBranchSummarySkipPrompt(): boolean;
325
- getRetryEnabled(): boolean;
326
- setRetryEnabled(enabled: boolean): void;
321
+ /**
322
+ * 聚合 retry 数值面。返回体带常量 `enabled: true`:pi-ai `RetryPolicy`
323
+ * (packages/ai/src/utils/retry.ts)的 `enabled` 是 required 字段,库层形状
324
+ * 不动;产品侧自动重试无条件生效(2026-10-03 D-079,settings
325
+ * `retry.enabled` 开关已移除),残留旧键按 settings 层宽松契约静默忽略。
326
+ */
327
327
  getRetrySettings(): {
328
328
  enabled: boolean;
329
329
  maxRetries: number;
@@ -373,8 +373,6 @@ export declare class SettingsManager {
373
373
  setProjectSuite(id: string | undefined): void;
374
374
  /** Global-scope active suite (方案) id; undefined clears the key (设计 §9). */
375
375
  setSuite(id: string | undefined): void;
376
- getEnableSkillCommands(): boolean;
377
- setEnableSkillCommands(enabled: boolean): void;
378
376
  getThinkingBudgets(): ThinkingBudgetsSettings | undefined;
379
377
  getTerminalCapabilityOverrides(): Partial<TerminalCapabilities>;
380
378
  getShowImages(): boolean;
@@ -403,8 +401,10 @@ export declare class SettingsManager {
403
401
  /** The resolved `defaultTools` selection, or undefined when no settings layer sets it. */
404
402
  getDefaultTools(): string[] | undefined;
405
403
  setEnabledModels(patterns: string[] | undefined): void;
406
- getDoubleEscapeAction(): "fork" | "tree" | "none";
407
- setDoubleEscapeAction(action: "fork" | "tree" | "none"): void;
404
+ getDoubleEscapeAction(): "tree" | "none";
405
+ getUiLanguage(): I18nLanguage;
406
+ setUiLanguage(language: I18nLanguage): void;
407
+ setDoubleEscapeAction(action: "tree" | "none"): void;
408
408
  getTreeFilterMode(): "default" | "no-tools" | "user-only" | "labeled-only" | "all";
409
409
  setTreeFilterMode(mode: "default" | "no-tools" | "user-only" | "labeled-only" | "all"): void;
410
410
  getShowHardwareCursor(): boolean;