@bevel-software/platform-core-backend 0.29.0 → 0.31.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 (307) hide show
  1. package/agent-guide/access-control.md +49 -4
  2. package/agent-guide/html-views.md +76 -2
  3. package/dist/__tests__/test-repositories.d.ts +3 -0
  4. package/dist/__tests__/test-repositories.d.ts.map +1 -0
  5. package/dist/__tests__/test-repositories.js +18 -0
  6. package/dist/__tests__/test-repositories.js.map +1 -0
  7. package/dist/core/catalog-revision.js +3 -3
  8. package/dist/core/create-core-server.d.ts.map +1 -1
  9. package/dist/core/create-core-server.js +3 -2
  10. package/dist/core/create-core-server.js.map +1 -1
  11. package/dist/core/create-core-services.d.ts +3 -0
  12. package/dist/core/create-core-services.d.ts.map +1 -1
  13. package/dist/core/create-core-services.js +35 -5
  14. package/dist/core/create-core-services.js.map +1 -1
  15. package/dist/core/lifecycle.d.ts +8 -0
  16. package/dist/core/lifecycle.d.ts.map +1 -1
  17. package/dist/core/lifecycle.js +4 -0
  18. package/dist/core/lifecycle.js.map +1 -1
  19. package/dist/modules/access/access-control.interface.d.ts +20 -0
  20. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  21. package/dist/modules/access/access-control.service.d.ts +32 -1
  22. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  23. package/dist/modules/access/access-control.service.js +135 -17
  24. package/dist/modules/access/access-control.service.js.map +1 -1
  25. package/dist/modules/access/access.routes.d.ts.map +1 -1
  26. package/dist/modules/access/access.routes.js +20 -6
  27. package/dist/modules/access/access.routes.js.map +1 -1
  28. package/dist/modules/agent-instructions/shared-file-rules.d.ts +1 -1
  29. package/dist/modules/agent-instructions/shared-file-rules.js +1 -1
  30. package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
  31. package/dist/modules/audit/agent-audit.service.js +66 -6
  32. package/dist/modules/audit/agent-audit.service.js.map +1 -1
  33. package/dist/modules/audit/audit.contract.d.ts +52 -8
  34. package/dist/modules/audit/audit.contract.d.ts.map +1 -1
  35. package/dist/modules/audit/audit.contract.js +37 -1
  36. package/dist/modules/audit/audit.contract.js.map +1 -1
  37. package/dist/modules/audit/audit.routes.d.ts +1 -1
  38. package/dist/modules/audit/audit.routes.d.ts.map +1 -1
  39. package/dist/modules/audit/audit.routes.js +15 -13
  40. package/dist/modules/audit/audit.routes.js.map +1 -1
  41. package/dist/modules/audit/request-audit.d.ts +21 -10
  42. package/dist/modules/audit/request-audit.d.ts.map +1 -1
  43. package/dist/modules/audit/request-audit.js +26 -10
  44. package/dist/modules/audit/request-audit.js.map +1 -1
  45. package/dist/modules/database/core-schema.d.ts +22 -3
  46. package/dist/modules/database/core-schema.d.ts.map +1 -1
  47. package/dist/modules/database/core-schema.js +15 -6
  48. package/dist/modules/database/core-schema.js.map +1 -1
  49. package/dist/modules/diff/diff.service.d.ts.map +1 -1
  50. package/dist/modules/diff/diff.service.js +7 -1
  51. package/dist/modules/diff/diff.service.js.map +1 -1
  52. package/dist/modules/kb-fs/git-dir.d.ts +21 -0
  53. package/dist/modules/kb-fs/git-dir.d.ts.map +1 -0
  54. package/dist/modules/kb-fs/git-dir.js +58 -0
  55. package/dist/modules/kb-fs/git-dir.js.map +1 -0
  56. package/dist/modules/kb-fs/index.d.ts +3 -0
  57. package/dist/modules/kb-fs/index.d.ts.map +1 -1
  58. package/dist/modules/kb-fs/index.js +3 -1
  59. package/dist/modules/kb-fs/index.js.map +1 -1
  60. package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
  61. package/dist/modules/kb-fs/locking-filesystem.js +3 -12
  62. package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
  63. package/dist/modules/kb-fs/mutex.d.ts +11 -0
  64. package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
  65. package/dist/modules/kb-fs/mutex.js +14 -0
  66. package/dist/modules/kb-fs/mutex.js.map +1 -1
  67. package/dist/modules/kb-fs/ref-files.d.ts +63 -0
  68. package/dist/modules/kb-fs/ref-files.d.ts.map +1 -0
  69. package/dist/modules/kb-fs/ref-files.js +249 -0
  70. package/dist/modules/kb-fs/ref-files.js.map +1 -0
  71. package/dist/modules/mcp/mcp-auth.middleware.d.ts +14 -0
  72. package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
  73. package/dist/modules/mcp/mcp-auth.middleware.js +4 -0
  74. package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
  75. package/dist/modules/mcp/mcp.routes.d.ts +5 -0
  76. package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
  77. package/dist/modules/mcp/mcp.routes.js +67 -0
  78. package/dist/modules/mcp/mcp.routes.js.map +1 -1
  79. package/dist/modules/mcp/mcp.service.d.ts +107 -27
  80. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  81. package/dist/modules/mcp/mcp.service.js +257 -50
  82. package/dist/modules/mcp/mcp.service.js.map +1 -1
  83. package/dist/modules/mcp/request-context.d.ts +25 -0
  84. package/dist/modules/mcp/request-context.d.ts.map +1 -0
  85. package/dist/modules/mcp/request-context.js +11 -0
  86. package/dist/modules/mcp/request-context.js.map +1 -0
  87. package/dist/modules/mcp/shared-definitions.d.ts +56 -0
  88. package/dist/modules/mcp/shared-definitions.d.ts.map +1 -0
  89. package/dist/modules/mcp/shared-definitions.js +117 -0
  90. package/dist/modules/mcp/shared-definitions.js.map +1 -0
  91. package/dist/modules/mcp/surface-cache.d.ts +70 -0
  92. package/dist/modules/mcp/surface-cache.d.ts.map +1 -0
  93. package/dist/modules/mcp/surface-cache.js +104 -0
  94. package/dist/modules/mcp/surface-cache.js.map +1 -0
  95. package/dist/modules/plugins/teams.routes.d.ts +4 -0
  96. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  97. package/dist/modules/plugins/teams.routes.js +9 -1
  98. package/dist/modules/plugins/teams.routes.js.map +1 -1
  99. package/dist/modules/tool-auth/internal-token.service.d.ts +1 -1
  100. package/dist/modules/tool-auth/tool-auth.middleware.d.ts +1 -2
  101. package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
  102. package/dist/modules/tool-auth/tool-auth.middleware.js +1 -2
  103. package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
  104. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  105. package/dist/modules/tool-helpers/tool-context.js +33 -12
  106. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  107. package/dist/modules/tool-helpers/tool-handler.d.ts +2 -2
  108. package/dist/modules/tool-helpers/tool.contract.d.ts +10 -2
  109. package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
  110. package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
  111. package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
  112. package/dist/modules/workflow/agent-tools/change-request-read.tools.js +3 -14
  113. package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
  114. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +18 -1
  115. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  116. package/dist/modules/workflow/agent-tools/workflow.tools.js +453 -2
  117. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  118. package/dist/modules/workflow/git/git.service.d.ts +142 -4
  119. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  120. package/dist/modules/workflow/git/git.service.js +645 -128
  121. package/dist/modules/workflow/git/git.service.js.map +1 -1
  122. package/dist/modules/workflow/read-on-branch.d.ts +35 -0
  123. package/dist/modules/workflow/read-on-branch.d.ts.map +1 -0
  124. package/dist/modules/workflow/read-on-branch.js +58 -0
  125. package/dist/modules/workflow/read-on-branch.js.map +1 -0
  126. package/dist/modules/workflow/workflow-hooks.d.ts +1 -1
  127. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  128. package/dist/modules/workflow/workflow.routes.js +22 -41
  129. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  130. package/dist/modules/workflow/workflow.service.d.ts +48 -8
  131. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  132. package/dist/modules/workflow/workflow.service.js +163 -16
  133. package/dist/modules/workflow/workflow.service.js.map +1 -1
  134. package/dist/modules/workspace/agent-access.gate.d.ts +2 -2
  135. package/dist/modules/workspace/agent-access.gate.js +2 -2
  136. package/dist/modules/workspace/bounded-matcher.d.ts +79 -0
  137. package/dist/modules/workspace/bounded-matcher.d.ts.map +1 -0
  138. package/dist/modules/workspace/bounded-matcher.js +261 -0
  139. package/dist/modules/workspace/bounded-matcher.js.map +1 -0
  140. package/dist/modules/workspace/fetch-instant.d.ts +9 -0
  141. package/dist/modules/workspace/fetch-instant.d.ts.map +1 -0
  142. package/dist/modules/workspace/fetch-instant.js +13 -0
  143. package/dist/modules/workspace/fetch-instant.js.map +1 -0
  144. package/dist/modules/workspace/index.d.ts +9 -0
  145. package/dist/modules/workspace/index.d.ts.map +1 -1
  146. package/dist/modules/workspace/index.js +10 -0
  147. package/dist/modules/workspace/index.js.map +1 -1
  148. package/dist/modules/workspace/kb-repository.contract.d.ts +124 -0
  149. package/dist/modules/workspace/kb-repository.contract.d.ts.map +1 -0
  150. package/dist/modules/workspace/kb-repository.contract.js +19 -0
  151. package/dist/modules/workspace/kb-repository.contract.js.map +1 -0
  152. package/dist/modules/workspace/kb-repository.d.ts +236 -0
  153. package/dist/modules/workspace/kb-repository.d.ts.map +1 -0
  154. package/dist/modules/workspace/kb-repository.js +811 -0
  155. package/dist/modules/workspace/kb-repository.js.map +1 -0
  156. package/dist/modules/workspace/pattern-matcher.contract.d.ts +50 -0
  157. package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -0
  158. package/dist/modules/workspace/pattern-matcher.contract.js +27 -0
  159. package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -0
  160. package/dist/modules/workspace/routine-write-policy.d.ts +0 -7
  161. package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
  162. package/dist/modules/workspace/routine-write-policy.js +0 -5
  163. package/dist/modules/workspace/routine-write-policy.js.map +1 -1
  164. package/dist/modules/workspace/set-aside-clone.d.ts +40 -0
  165. package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -1
  166. package/dist/modules/workspace/set-aside-clone.js +139 -0
  167. package/dist/modules/workspace/set-aside-clone.js.map +1 -1
  168. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +99 -0
  169. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  170. package/dist/modules/workspace/startup/kb-startup-runner.js +407 -29
  171. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  172. package/dist/modules/workspace/workspace.service.d.ts +68 -32
  173. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  174. package/dist/modules/workspace/workspace.service.js +240 -161
  175. package/dist/modules/workspace/workspace.service.js.map +1 -1
  176. package/dist/modules/workspace/workspace.tools.d.ts +13 -4
  177. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  178. package/dist/modules/workspace/workspace.tools.js +162 -336
  179. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  180. package/dist/shared/git.contract.d.ts +7 -0
  181. package/dist/shared/git.contract.d.ts.map +1 -1
  182. package/dist/shared/git.contract.js +9 -0
  183. package/dist/shared/git.contract.js.map +1 -1
  184. package/dist/shared/no-link-below.d.ts +10 -0
  185. package/dist/shared/no-link-below.d.ts.map +1 -0
  186. package/dist/shared/no-link-below.js +30 -0
  187. package/dist/shared/no-link-below.js.map +1 -0
  188. package/dist/test-setup.js +4 -0
  189. package/dist/test-setup.js.map +1 -1
  190. package/migrations/0018_agent_events_page.sql +11 -0
  191. package/migrations/meta/0018_snapshot.json +2429 -0
  192. package/migrations/meta/_journal.json +8 -1
  193. package/package.json +3 -3
  194. package/src/__tests__/test-repositories.ts +22 -0
  195. package/src/__tests__/workspace-service.ts +69 -0
  196. package/src/core/__tests__/lifecycle.test.ts +16 -0
  197. package/src/core/catalog-revision.ts +3 -3
  198. package/src/core/create-core-server.ts +3 -2
  199. package/src/core/create-core-services.ts +39 -4
  200. package/src/core/lifecycle.ts +10 -0
  201. package/src/modules/access/__tests__/access-control.atref-cache.test.ts +144 -4
  202. package/src/modules/access/__tests__/access-control.group-read.test.ts +73 -0
  203. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +66 -0
  204. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
  205. package/src/modules/access/access-control.interface.ts +22 -0
  206. package/src/modules/access/access-control.service.ts +138 -15
  207. package/src/modules/access/access.routes.ts +22 -6
  208. package/src/modules/agent-guide/__tests__/agent-guide.test.ts +22 -1
  209. package/src/modules/agent-instructions/shared-file-rules.ts +1 -1
  210. package/src/modules/audit/__tests__/agent-audit.service.test.ts +78 -1
  211. package/src/modules/audit/__tests__/audit.routes.test.ts +43 -5
  212. package/src/modules/audit/agent-audit.service.ts +66 -6
  213. package/src/modules/audit/audit.contract.ts +62 -10
  214. package/src/modules/audit/audit.routes.ts +14 -12
  215. package/src/modules/audit/request-audit.ts +28 -13
  216. package/src/modules/database/core-schema.ts +15 -6
  217. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  218. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  219. package/src/modules/diff/__tests__/diff.service.worktree.test.ts +76 -0
  220. package/src/modules/diff/diff.service.ts +6 -4
  221. package/src/modules/kb-fs/__tests__/git-dir.test.ts +77 -0
  222. package/src/modules/kb-fs/__tests__/mutex.canonical.test.ts +57 -0
  223. package/src/modules/kb-fs/__tests__/ref-files.test.ts +338 -0
  224. package/src/modules/kb-fs/git-dir.ts +53 -0
  225. package/src/modules/kb-fs/index.ts +3 -0
  226. package/src/modules/kb-fs/locking-filesystem.ts +3 -12
  227. package/src/modules/kb-fs/mutex.ts +16 -0
  228. package/src/modules/kb-fs/ref-files.ts +279 -0
  229. package/src/modules/mcp/__tests__/mcp-routes-harness.ts +7 -1
  230. package/src/modules/mcp/__tests__/mcp.routes.page.test.ts +115 -0
  231. package/src/modules/mcp/__tests__/mcp.surface-cache.test.ts +521 -0
  232. package/src/modules/mcp/__tests__/shared-definitions.test.ts +28 -0
  233. package/src/modules/mcp/__tests__/surface-cache.test.ts +127 -0
  234. package/src/modules/mcp/mcp-auth.middleware.ts +21 -0
  235. package/src/modules/mcp/mcp.routes.ts +68 -0
  236. package/src/modules/mcp/mcp.service.ts +315 -51
  237. package/src/modules/mcp/request-context.ts +34 -0
  238. package/src/modules/mcp/shared-definitions.ts +121 -0
  239. package/src/modules/mcp/surface-cache.ts +154 -0
  240. package/src/modules/plugins/__tests__/teams.routes.test.ts +55 -0
  241. package/src/modules/plugins/teams.routes.ts +14 -1
  242. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +2 -2
  243. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +2 -2
  244. package/src/modules/tool-auth/internal-token.service.ts +1 -1
  245. package/src/modules/tool-auth/tool-auth.middleware.ts +1 -2
  246. package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +2 -2
  247. package/src/modules/tool-helpers/tool-context.ts +37 -18
  248. package/src/modules/tool-helpers/tool-handler.ts +2 -2
  249. package/src/modules/tool-helpers/tool.contract.ts +10 -2
  250. package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +1 -1
  251. package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +7 -2
  252. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +23 -12
  253. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +250 -4
  254. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +464 -4
  255. package/src/modules/workflow/agent-tools/change-request-read.tools.ts +4 -22
  256. package/src/modules/workflow/agent-tools/workflow.tools.ts +462 -1
  257. package/src/modules/workflow/git/__tests__/git.service.changedPathsMemo.test.ts +175 -0
  258. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +76 -11
  259. package/src/modules/workflow/git/__tests__/git.service.mergeIntoBranch.test.ts +721 -0
  260. package/src/modules/workflow/git/__tests__/git.service.worktrees.test.ts +284 -0
  261. package/src/modules/workflow/git/git.service.ts +719 -126
  262. package/src/modules/workflow/read-on-branch.ts +79 -0
  263. package/src/modules/workflow/workflow-hooks.ts +1 -1
  264. package/src/modules/workflow/workflow.routes.ts +21 -39
  265. package/src/modules/workflow/workflow.service.ts +203 -16
  266. package/src/modules/workspace/__tests__/agent-downloads.test.ts +2 -2
  267. package/src/modules/workspace/__tests__/bounded-matcher.test.ts +213 -0
  268. package/src/modules/workspace/__tests__/git-internals.security.test.ts +22 -7
  269. package/src/modules/workspace/__tests__/kb-repository.test.ts +481 -0
  270. package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +14 -0
  271. package/src/modules/workspace/__tests__/routine-write-policy.test.ts +0 -11
  272. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +2 -2
  273. package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +2 -2
  274. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +4 -3
  275. package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +2 -2
  276. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  277. package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +12 -5
  278. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -6
  279. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  280. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  281. package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +2 -1
  282. package/src/modules/workspace/__tests__/workspace.service.test.ts +24 -23
  283. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
  284. package/src/modules/workspace/__tests__/workspace.service.worktrees.test.ts +620 -0
  285. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +15 -1
  286. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +71 -5
  287. package/src/modules/workspace/__tests__/workspace.tools.test.ts +153 -298
  288. package/src/modules/workspace/agent-access.gate.ts +2 -2
  289. package/src/modules/workspace/bounded-matcher.ts +328 -0
  290. package/src/modules/workspace/fetch-instant.ts +13 -0
  291. package/src/modules/workspace/index.ts +23 -0
  292. package/src/modules/workspace/kb-repository.contract.ts +124 -0
  293. package/src/modules/workspace/kb-repository.ts +844 -0
  294. package/src/modules/workspace/pattern-matcher.contract.ts +69 -0
  295. package/src/modules/workspace/routine-write-policy.ts +0 -15
  296. package/src/modules/workspace/set-aside-clone.ts +140 -0
  297. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +3 -2
  298. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +12 -10
  299. package/src/modules/workspace/startup/__tests__/kb-startup-runner.worktrees.test.ts +613 -0
  300. package/src/modules/workspace/startup/kb-startup-runner.ts +422 -29
  301. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +4 -3
  302. package/src/modules/workspace/workspace.service.ts +247 -169
  303. package/src/modules/workspace/workspace.tools.ts +179 -365
  304. package/src/shared/__tests__/no-link-below.test.ts +45 -0
  305. package/src/shared/git.contract.ts +10 -0
  306. package/src/shared/no-link-below.ts +29 -0
  307. package/src/test-setup.ts +5 -0
@@ -0,0 +1,79 @@
1
+ import { type IPatternMatcher, type IPatternSearch } from './pattern-matcher.contract.js';
2
+ /**
3
+ * The one place a CALLER'S regular expression is run (see
4
+ * {@link IPatternMatcher}).
5
+ *
6
+ * The pattern never runs on the request thread: the lines go to a worker, the
7
+ * worker answers which of them match, and a search whose matching outlives its
8
+ * budget has its worker terminated (which interrupts even a regex
9
+ * mid-backtrack) and is answered with {@link PatternTooExpensiveError}.
10
+ * Nothing partial is returned: a search that blew its budget answers the 400
11
+ * alone.
12
+ *
13
+ * The semantics are the engine's own — the worker compiles the same source with
14
+ * the same flags and calls `test` on each line — so a pattern within budget
15
+ * matches exactly the lines it always did.
16
+ *
17
+ * The pool is this instance's own: the composition root builds one per graph
18
+ * and closes it when the graph stops, so no worker outlives what it served.
19
+ */
20
+ export interface WorkerPatternMatcherOptions {
21
+ /** Matching time one search may spend, summed over every file it searches. Default 5 s. */
22
+ budgetMs?: number;
23
+ /** Workers kept for searches at once; a search past this waits for one to free up. Default 4. */
24
+ poolMax?: number;
25
+ /**
26
+ * Searches that may wait for a worker at once. A waiting search holds the
27
+ * lines it was handed, so the queue is capped: past it a search is refused
28
+ * with a 503 rather than piling file contents up in memory. Default 16.
29
+ */
30
+ maxWaiting?: number;
31
+ /**
32
+ * Longest pattern accepted. The pattern is compiled on the request thread
33
+ * (that is how a syntax error earns its 400 there), so its length is what
34
+ * bounds that. Default 1000.
35
+ */
36
+ maxPatternLength?: number;
37
+ /**
38
+ * Characters handed to the worker in one message. Posting a message copies
39
+ * it on the request thread, so a large file goes over in pieces of about
40
+ * this size rather than in one copy that would stall every other request.
41
+ * A single line longer than this is not posted at all: it is written into
42
+ * shared memory this many characters at a time, yielding between pieces,
43
+ * and the worker reads it from there. Default 1 MB.
44
+ */
45
+ batchChars?: number;
46
+ /**
47
+ * Lines handed to the worker in one message. Each line is an entry the
48
+ * copy has to walk whatever its length, so a file of empty lines is bounded
49
+ * by this, not by `batchChars`. Default 10,000.
50
+ */
51
+ batchLines?: number;
52
+ }
53
+ export declare class WorkerPatternMatcher implements IPatternMatcher {
54
+ private readonly budgetMs;
55
+ private readonly poolMax;
56
+ private readonly maxWaiting;
57
+ private readonly maxPatternLength;
58
+ private readonly batchChars;
59
+ private readonly batchLines;
60
+ private readonly idle;
61
+ private readonly all;
62
+ private readonly waiting;
63
+ private closed;
64
+ private readonly lease;
65
+ constructor(opts?: WorkerPatternMatcherOptions);
66
+ search(source: string, flags: string): IPatternSearch;
67
+ close(): Promise<void>;
68
+ /**
69
+ * Where the batch starting at `from` ends: about `batchChars` characters on,
70
+ * at most `batchLines` lines on, and never empty. A batch of one line past
71
+ * `batchChars` is handed over through shared memory instead (see
72
+ * {@link BoundedSearch.matchBatch}).
73
+ */
74
+ private batchEnd;
75
+ private acquire;
76
+ private release;
77
+ private spawn;
78
+ }
79
+ //# sourceMappingURL=bounded-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-matcher.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/bounded-matcher.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,2BAA2B;IAC1C,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4CD,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,IAAI,GAAE,2BAAgC;IAiBlD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAgB/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;YAQF,OAAO;IAcrB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,KAAK;CAcd"}
@@ -0,0 +1,261 @@
1
+ import { Worker } from 'node:worker_threads';
2
+ import { ToolError } from '../tool-helpers/tool.contract.js';
3
+ import { PatternTooExpensiveError, SearchBusyError, } from './pattern-matcher.contract.js';
4
+ /**
5
+ * Slack on top of the remaining budget before a batch is given up on: the
6
+ * budget counts only the worker's own matching time, so handing the lines over
7
+ * and the answer back must not be what trips it.
8
+ */
9
+ const TRANSFER_GRACE_MS = 250;
10
+ /** The worker: compiles the pattern it is handed and answers the indices of the lines it matches. */
11
+ const WORKER_SOURCE = `
12
+ const { parentPort } = require('node:worker_threads');
13
+ const { performance } = require('node:perf_hooks');
14
+ let cached = { key: null, re: null };
15
+ parentPort.on('message', ({ source, flags, lines, shared, limit }) => {
16
+ try {
17
+ // One line too long to post, written into shared memory as raw UTF-16 code units.
18
+ if (shared) lines = [Buffer.from(shared).toString('utf16le')];
19
+ const key = flags + '/' + source;
20
+ if (cached.key !== key) cached = { key, re: new RegExp(source, flags) };
21
+ const re = cached.re;
22
+ const started = performance.now();
23
+ const hits = [];
24
+ for (let i = 0; i < lines.length && hits.length < limit; i++) {
25
+ re.lastIndex = 0;
26
+ if (re.test(lines[i])) hits.push(i);
27
+ }
28
+ parentPort.postMessage({ hits, elapsed: performance.now() - started });
29
+ } catch (err) {
30
+ parentPort.postMessage({ error: String(err && err.message ? err.message : err) });
31
+ }
32
+ });
33
+ `;
34
+ export class WorkerPatternMatcher {
35
+ budgetMs;
36
+ poolMax;
37
+ maxWaiting;
38
+ maxPatternLength;
39
+ batchChars;
40
+ batchLines;
41
+ idle = [];
42
+ all = new Set();
43
+ waiting = [];
44
+ closed = false;
45
+ lease;
46
+ constructor(opts = {}) {
47
+ this.budgetMs = option('budgetMs', opts.budgetMs ?? 5_000, 1, MAX_TIMER_MS - TRANSFER_GRACE_MS);
48
+ // A pool of none would leave every search waiting for a worker that never comes.
49
+ this.poolMax = option('poolMax', opts.poolMax ?? 4, 1);
50
+ // No queue at all is allowed: every search past the pool is refused busy.
51
+ this.maxWaiting = option('maxWaiting', opts.maxWaiting ?? 16, 0);
52
+ this.maxPatternLength = option('maxPatternLength', opts.maxPatternLength ?? 1_000, 1);
53
+ this.batchChars = option('batchChars', opts.batchChars ?? 1_000_000, 1);
54
+ this.batchLines = option('batchLines', opts.batchLines ?? 10_000, 1);
55
+ this.lease = {
56
+ acquire: () => this.acquire(),
57
+ release: (w) => this.release(w),
58
+ batchEnd: (lines, from) => this.batchEnd(lines, from),
59
+ batchChars: this.batchChars,
60
+ };
61
+ }
62
+ search(source, flags) {
63
+ if (source.length > this.maxPatternLength) {
64
+ throw new ToolError(`Pattern too long: ${source.length} characters, and a search pattern may have at most ${this.maxPatternLength}.`, 400, { code: 'pattern_too_long' });
65
+ }
66
+ try {
67
+ new RegExp(source, flags);
68
+ }
69
+ catch (err) {
70
+ throw new ToolError(`Invalid regex: ${err.message}`, 400);
71
+ }
72
+ return new BoundedSearch(this.lease, source, flags, this.budgetMs);
73
+ }
74
+ async close() {
75
+ this.closed = true;
76
+ for (const waiter of this.waiting.splice(0))
77
+ waiter.reject(new SearchBusyError());
78
+ const workers = [...this.all];
79
+ this.idle.length = 0;
80
+ await Promise.all(workers.map((w) => w.terminate()));
81
+ }
82
+ /**
83
+ * Where the batch starting at `from` ends: about `batchChars` characters on,
84
+ * at most `batchLines` lines on, and never empty. A batch of one line past
85
+ * `batchChars` is handed over through shared memory instead (see
86
+ * {@link BoundedSearch.matchBatch}).
87
+ */
88
+ batchEnd(lines, from) {
89
+ let to = from;
90
+ let chars = 0;
91
+ const last = Math.min(lines.length, from + this.batchLines);
92
+ while (to < last && (to === from || chars + lines[to].length <= this.batchChars))
93
+ chars += lines[to++].length;
94
+ return to;
95
+ }
96
+ async acquire() {
97
+ if (this.closed)
98
+ throw new SearchBusyError();
99
+ if (this.idle.length === 0 && this.all.size >= this.poolMax && this.waiting.length >= this.maxWaiting) {
100
+ throw new SearchBusyError();
101
+ }
102
+ const w = this.idle.pop() ??
103
+ (this.all.size < this.poolMax
104
+ ? this.spawn()
105
+ : await new Promise((resolve, reject) => this.waiting.push({ resolve, reject })));
106
+ w.ref();
107
+ return w;
108
+ }
109
+ release(w) {
110
+ const next = this.waiting.shift();
111
+ if (next)
112
+ return next.resolve(w);
113
+ // An idle worker must not hold a process's exit open.
114
+ w.unref();
115
+ this.idle.push(w);
116
+ }
117
+ spawn() {
118
+ const w = new Worker(WORKER_SOURCE, { eval: true });
119
+ this.all.add(w);
120
+ // A worker that dies — terminated over budget, or out of memory — leaves the pool.
121
+ w.once('exit', () => {
122
+ this.all.delete(w);
123
+ const at = this.idle.indexOf(w);
124
+ if (at >= 0)
125
+ this.idle.splice(at, 1);
126
+ if (this.closed)
127
+ return;
128
+ const next = this.waiting.shift();
129
+ if (next)
130
+ next.resolve(this.spawn());
131
+ });
132
+ return w;
133
+ }
134
+ }
135
+ /** One search: a caller's pattern, matched off the event loop under one budget. */
136
+ class BoundedSearch {
137
+ pool;
138
+ source;
139
+ flags;
140
+ budgetMs;
141
+ spentMs = 0;
142
+ constructor(pool, source, flags, budgetMs) {
143
+ this.pool = pool;
144
+ this.source = source;
145
+ this.flags = flags;
146
+ this.budgetMs = budgetMs;
147
+ }
148
+ async matchingLines(lines, limit) {
149
+ if (limit <= 0 || lines.length === 0)
150
+ return [];
151
+ if (this.spentMs >= this.budgetMs)
152
+ throw new PatternTooExpensiveError(this.source, this.budgetMs);
153
+ const w = await this.pool.acquire();
154
+ const hits = [];
155
+ try {
156
+ for (let from = 0; from < lines.length && hits.length < limit;) {
157
+ const to = this.pool.batchEnd(lines, from);
158
+ for (const i of await this.matchBatch(w, lines.slice(from, to), limit - hits.length))
159
+ hits.push(from + i);
160
+ from = to;
161
+ }
162
+ }
163
+ catch (err) {
164
+ // Terminating is what stops a regex mid-backtrack; the pool replaces it.
165
+ void w.terminate();
166
+ throw err;
167
+ }
168
+ this.pool.release(w);
169
+ return hits;
170
+ }
171
+ /**
172
+ * One handoff to the worker, charged to this search's budget. No handoff
173
+ * copies more than `batchChars` characters on the request thread at once: a
174
+ * line longer than that goes over in shared memory, not in the message.
175
+ */
176
+ async matchBatch(w, lines, limit) {
177
+ if (this.spentMs >= this.budgetMs)
178
+ throw new PatternTooExpensiveError(this.source, this.budgetMs);
179
+ const oversized = lines.length === 1 && lines[0].length > this.pool.batchChars;
180
+ const payload = oversized ? { shared: await shareLine(w, lines[0], this.pool.batchChars) } : { lines };
181
+ const remaining = this.budgetMs - this.spentMs;
182
+ const reply = await run(w, { source: this.source, flags: this.flags, ...payload, limit }, remaining + TRANSFER_GRACE_MS);
183
+ if (reply === 'timeout') {
184
+ this.spentMs = this.budgetMs;
185
+ throw new PatternTooExpensiveError(this.source, this.budgetMs);
186
+ }
187
+ if ('error' in reply)
188
+ throw new ToolError(`Invalid regex: ${reply.error}`, 400);
189
+ this.spentMs += reply.elapsed;
190
+ if (this.spentMs > this.budgetMs)
191
+ throw new PatternTooExpensiveError(this.source, this.budgetMs);
192
+ return reply.hits;
193
+ }
194
+ }
195
+ /**
196
+ * A line as raw UTF-16 code units in shared memory, which a message hands the
197
+ * worker without copying. It is written `chunkChars` at a time with a turn of
198
+ * the event loop between, so a line of any length never holds the request
199
+ * thread for longer than one chunk. Raw code units keep the line exact, lone
200
+ * surrogates included.
201
+ *
202
+ * The worker it is meant for can be terminated by `close()` during those turns,
203
+ * before {@link run} listens for its exit; a message posted to it then would
204
+ * never be answered. So its exit is watched here too: writing stops and the
205
+ * search is answered busy, as one terminated under `run` is.
206
+ */
207
+ async function shareLine(w, line, chunkChars) {
208
+ let exited = false;
209
+ const onExit = () => {
210
+ exited = true;
211
+ };
212
+ w.once('exit', onExit);
213
+ try {
214
+ const shared = new SharedArrayBuffer(line.length * 2);
215
+ const bytes = Buffer.from(shared);
216
+ for (let at = 0; at < line.length && !exited; at += chunkChars) {
217
+ if (at > 0)
218
+ await new Promise((resolve) => setImmediate(resolve));
219
+ bytes.write(line.slice(at, at + chunkChars), at * 2, 'utf16le');
220
+ }
221
+ if (exited)
222
+ throw new SearchBusyError();
223
+ return shared;
224
+ }
225
+ finally {
226
+ w.off('exit', onExit);
227
+ }
228
+ }
229
+ function run(w, message, timeoutMs) {
230
+ return new Promise((resolve, reject) => {
231
+ const settle = (fn) => {
232
+ clearTimeout(timer);
233
+ w.off('message', onMessage);
234
+ w.off('error', onError);
235
+ w.off('exit', onExit);
236
+ fn();
237
+ };
238
+ const onMessage = (reply) => settle(() => resolve(reply));
239
+ const onError = (err) => settle(() => reject(err));
240
+ // Terminated under the search by `close()`: answered as busy, not left hanging until the timer.
241
+ const onExit = () => settle(() => reject(new SearchBusyError()));
242
+ const timer = setTimeout(() => settle(() => resolve('timeout')), timeoutMs);
243
+ w.on('message', onMessage);
244
+ w.on('error', onError);
245
+ w.on('exit', onExit);
246
+ w.postMessage(message);
247
+ });
248
+ }
249
+ /**
250
+ * The longest delay `setTimeout` honours. A longer one fires after 1 ms, so a
251
+ * budget past this (less the grace added to it) would stop every search at once.
252
+ */
253
+ const MAX_TIMER_MS = 2_147_483_647;
254
+ /** A sizing option as given, refused at construction unless it is an integer from `min` to `max`. */
255
+ function option(name, value, min, max = Number.MAX_SAFE_INTEGER) {
256
+ if (!Number.isInteger(value) || value < min || value > max) {
257
+ throw new RangeError(`WorkerPatternMatcher: ${name} must be an integer from ${min} to ${max}, got ${value}.`);
258
+ }
259
+ return value;
260
+ }
261
+ //# sourceMappingURL=bounded-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-matcher.js","sourceRoot":"","sources":["../../../src/modules/workspace/bounded-matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * An instant on the one clock that fetch starts and `fetchedSince` instants
3
+ * are read from. Milliseconds, like `Date.now()`, but strictly increasing in
4
+ * call order: two readings never compare equal, so a fetch started in the
5
+ * same millisecond as a listing is still ordered before or after it, and
6
+ * "started after the instant" is a strict comparison that cannot be a tie.
7
+ */
8
+ export declare function fetchInstant(): number;
9
+ //# sourceMappingURL=fetch-instant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-instant.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/fetch-instant.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAGrC"}
@@ -0,0 +1,13 @@
1
+ let last = 0;
2
+ /**
3
+ * An instant on the one clock that fetch starts and `fetchedSince` instants
4
+ * are read from. Milliseconds, like `Date.now()`, but strictly increasing in
5
+ * call order: two readings never compare equal, so a fetch started in the
6
+ * same millisecond as a listing is still ordered before or after it, and
7
+ * "started after the instant" is a strict comparison that cannot be a tie.
8
+ */
9
+ export function fetchInstant() {
10
+ last = Math.max(Date.now(), last + 0.001);
11
+ return last;
12
+ }
13
+ //# sourceMappingURL=fetch-instant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-instant.js","sourceRoot":"","sources":["../../../src/modules/workspace/fetch-instant.ts"],"names":[],"mappings":"AAAA,IAAI,IAAI,GAAG,CAAC,CAAC;AAEb;;;;;;GAMG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -7,4 +7,13 @@
7
7
  */
8
8
  export { FileReaderRegistry, type FileKind, type FileReader, type ReadResult, } from './file-readers/file-reader.js';
9
9
  export { OCTET_STREAM, rawExtensionOf, rawMimeFor } from './file-readers/raw-mime.js';
10
+ /**
11
+ * The knowledge base's one repository, every branch workspace a worktree of
12
+ * it. (Where a checkout's git directory is — its `.git` folder, or the
13
+ * folder its `.git` FILE names — is `kb-fs/git-dir.ts`.)
14
+ */
15
+ export { KbRepository, repositoryDirFor, workingCopyShape, type WorkingCopyShape, } from './kb-repository.js';
16
+ export type { AddWorktreeOptions, FetchOptions, IKbRepository } from './kb-repository.contract.js';
17
+ export { assertAgentWriteAllowed, notifyAgentRead, SESSION_ID_DESCRIPTION, SESSION_ID_INPUT, ToolDescriptionNotes, type AgentAccessGate, } from './agent-access.gate.js';
18
+ export { SpillStore } from './spill-store.js';
10
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACtF;;;;GAIG;AACH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGnG,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,eAAe,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -7,4 +7,14 @@
7
7
  */
8
8
  export { FileReaderRegistry, } from './file-readers/file-reader.js';
9
9
  export { OCTET_STREAM, rawExtensionOf, rawMimeFor } from './file-readers/raw-mime.js';
10
+ /**
11
+ * The knowledge base's one repository, every branch workspace a worktree of
12
+ * it. (Where a checkout's git directory is — its `.git` folder, or the
13
+ * folder its `.git` FILE names — is `kb-fs/git-dir.ts`.)
14
+ */
15
+ export { KbRepository, repositoryDirFor, workingCopyShape, } from './kb-repository.js';
16
+ // The agent-access gate: what a module's agent tools take to tell the
17
+ // deployment's hooks of a read or a write, and the session input they declare.
18
+ export { assertAgentWriteAllowed, notifyAgentRead, SESSION_ID_DESCRIPTION, SESSION_ID_INPUT, ToolDescriptionNotes, } from './agent-access.gate.js';
19
+ export { SpillStore } from './spill-store.js';
10
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,kBAAkB,GAInB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,kBAAkB,GAInB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACtF;;;;GAIG;AACH,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAE5B,sEAAsE;AACtE,+EAA+E;AAC/E,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,GAErB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The knowledge base's one repository, every branch workspace a worktree of
3
+ * it — the contract its consumers (the workspace service, the KB startup
4
+ * phase, the set-aside of a moved repository, the git layer) depend on.
5
+ * `KbRepository` (`kb-repository.ts`) is the implementation, a bare
6
+ * repository driven by the git CLI; the composition root makes ONE and
7
+ * injects it everywhere, so that one fetch serves every workspace and the
8
+ * repository's administration — its creation, the writes to its one config
9
+ * file, its worktree list, a set-aside — is serialized in one place.
10
+ *
11
+ * Nothing else is serialized by the application: git was built for many
12
+ * worktrees of one repository, and protects the refs, `packed-refs` and the
13
+ * object store with its own locks. The worktree list (`worktrees/`) it does
14
+ * not lock, which is why adding, removing and pruning worktrees are
15
+ * administration here. Two worktrees run side by side, as two developers'
16
+ * worktrees of one repository do.
17
+ */
18
+ export interface FetchOptions {
19
+ /** A fetch that SUCCEEDED this recently is fresh enough; nothing runs. */
20
+ maxAgeMs: number;
21
+ /**
22
+ * An instant (`fetchInstant()`): a success counts as fresh only when
23
+ * its fetch started after it, however young it is (a fetch from before a listing may lack
24
+ * what the listing saw). A fetch in flight that started before it is
25
+ * waited for, not joined, and a fresh one runs.
26
+ */
27
+ fetchedSince?: number;
28
+ /** Skip the freshness window (the in-flight join still applies). */
29
+ force?: boolean;
30
+ /**
31
+ * A fetch that FAILED this recently is not retried: the answer is the
32
+ * failure. For pollers, so a host that is down is not asked again on every
33
+ * request. Absent, a failure is retried on the next call.
34
+ */
35
+ failureBackoffMs?: number;
36
+ }
37
+ export interface AddWorktreeOptions {
38
+ /** A converted clone whose branch was never pushed: its local ref is enough. */
39
+ allowLocalOnly?: boolean;
40
+ /** A conversion: a local ref behind the remote stays where it is. */
41
+ keepLocalTip?: boolean;
42
+ /**
43
+ * Reuse the repository's own fetch when it started after this instant
44
+ * (`fetchInstant()`)
45
+ * and knows the branch. An instant, not an age: it is compared inside the
46
+ * reservation, so time spent waiting for it cannot age a fetch out.
47
+ */
48
+ fetchedSince?: number;
49
+ }
50
+ export interface IKbRepository {
51
+ /** The bare repository's folder. */
52
+ readonly dir: string;
53
+ /**
54
+ * Run `fn` as the repository's one administrator for its duration: no
55
+ * creation of the repository, no write to its one config file (git refuses
56
+ * a second writer of it rather than waiting), no change to its worktree
57
+ * list (which git does not lock: a prune beside a `worktree add` can take
58
+ * the entry being made), and no set-aside runs beside it. Git operations
59
+ * in the worktrees are not held back — git's own locks cover them — so a
60
+ * caller that moves the repository's folders holds those off itself (the
61
+ * startup phase runs before anything is served; the workspace service
62
+ * wraps a set-aside in what surrounds one). Never nested: `fn` must not
63
+ * call `exclusive` again, nor a method that does (`ensure`,
64
+ * `refreshCredentials`, `writeConfig`, `addWorktree`, `removeWorktree`,
65
+ * `forgetWorktrees`, `prune`, `setOriginUrl`).
66
+ */
67
+ exclusive<T>(fn: () => Promise<T>): Promise<T>;
68
+ /** Whether the repository is on disk and finished (its first fetch landed). */
69
+ exists(): Promise<boolean>;
70
+ /** Create the repository and fetch it once when it is not there. Throws with git's words, redacted. */
71
+ ensure(): Promise<void>;
72
+ /** Re-stamp the credential helper when the deployment's credential changed. */
73
+ refreshCredentials(): Promise<void>;
74
+ /** Run `git config` writes against the shared configuration, one writer at a time. */
75
+ writeConfig(cwd: string, argLists: readonly string[][]): Promise<void>;
76
+ /**
77
+ * Garbage-collect the repository when git finds it worth it (`gc --auto`
78
+ * with git's default thresholds). Automatic collection is off on the
79
+ * repository — a commit in one worktree would otherwise collect beside
80
+ * every other worktree's work — so the startup phase runs this once, when
81
+ * nothing else does. Never throws.
82
+ */
83
+ maintain(): Promise<void>;
84
+ /** The last time the repository reached the remote and whether it worked; null before any attempt. */
85
+ lastContact(): {
86
+ at: number;
87
+ ok: boolean;
88
+ } | null;
89
+ /** Why the last fetch failed, redacted; null when it did not. */
90
+ lastFetchFailure(): string | null;
91
+ /**
92
+ * `git fetch --prune origin`, once for every workspace: throttled, and
93
+ * joined while in flight. Answers whether the remote-tracking refs are
94
+ * provably fresh. Never throws.
95
+ */
96
+ fetch(opts: FetchOptions): Promise<boolean>;
97
+ /** Forget what this process knows of the repository (it was set aside). */
98
+ forget(): void;
99
+ /** Add the worktree of `branch` at `checkoutDir`, copying no history. */
100
+ addWorktree(branch: string, checkoutDir: string, opts?: AddWorktreeOptions): Promise<void>;
101
+ /** Remove and prune the worktree at `checkoutDir`, with its branch's local ref; nothing else. */
102
+ removeWorktree(checkoutDir: string, branch: string): Promise<void>;
103
+ /** Prune worktrees whose folders were deleted by hand, and drop their branches' local refs. */
104
+ forgetWorktrees(branches: readonly string[]): Promise<void>;
105
+ /** `git worktree prune`. */
106
+ prune(): Promise<void>;
107
+ /** The folders of the repository's worktrees, as git lists them. */
108
+ listWorktrees(): Promise<string[]>;
109
+ /** Import the tip of `branch` from the full clone at `cloneDir` (unpushed commits included); answers the tip. */
110
+ importBranch(cloneDir: string, branch: string): Promise<string>;
111
+ /**
112
+ * Import the full clone's stash entries and its other local refs (not its own
113
+ * branch, not remote-tracking ones) under `refs/kb-converted/<workspaceId>/`,
114
+ * leaving the clone as it was; answers the refs written.
115
+ */
116
+ importLocalRefs(cloneDir: string, branch: string, workspaceId: string): Promise<string[]>;
117
+ /** The address the repository fetches from, or null when it cannot be read. */
118
+ originUrl(): Promise<string | null>;
119
+ /** Re-link a repository and its worktrees that were moved together; answers the worktrees that could not be. */
120
+ relinkMovedCopy(movedRepository: string, movedWorktrees: readonly string[]): Promise<string[]>;
121
+ /** Point the repository at `url`, forgetting the old address's fetch freshness. */
122
+ setOriginUrl(url: string): Promise<void>;
123
+ }
124
+ //# sourceMappingURL=kb-repository.contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kb-repository.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/workspace/kb-repository.contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The knowledge base's one repository, every branch workspace a worktree of
3
+ * it — the contract its consumers (the workspace service, the KB startup
4
+ * phase, the set-aside of a moved repository, the git layer) depend on.
5
+ * `KbRepository` (`kb-repository.ts`) is the implementation, a bare
6
+ * repository driven by the git CLI; the composition root makes ONE and
7
+ * injects it everywhere, so that one fetch serves every workspace and the
8
+ * repository's administration — its creation, the writes to its one config
9
+ * file, its worktree list, a set-aside — is serialized in one place.
10
+ *
11
+ * Nothing else is serialized by the application: git was built for many
12
+ * worktrees of one repository, and protects the refs, `packed-refs` and the
13
+ * object store with its own locks. The worktree list (`worktrees/`) it does
14
+ * not lock, which is why adding, removing and pruning worktrees are
15
+ * administration here. Two worktrees run side by side, as two developers'
16
+ * worktrees of one repository do.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=kb-repository.contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kb-repository.contract.js","sourceRoot":"","sources":["../../../src/modules/workspace/kb-repository.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}