@hcu-lab.me/mcp-device 1.0.9 → 1.0.12

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 (506) hide show
  1. package/PRIVACY.md +21 -0
  2. package/README.md +58 -3
  3. package/SECURITY.md +67 -0
  4. package/config/revoked-approvals.json +1 -0
  5. package/control-plane/config/mcp-servers.example.toml +52 -0
  6. package/control-plane/config/mcp-servers.toml +52 -0
  7. package/control-plane/contracts/device-public-contract-v1.json +733 -0
  8. package/control-plane/scripts/GATEWAY-LICENSE.txt +7 -0
  9. package/control-plane/scripts/account-http.mjs +220 -0
  10. package/control-plane/scripts/account-store.mjs +368 -0
  11. package/control-plane/scripts/admin.mjs +68 -0
  12. package/control-plane/scripts/auth-session.mjs +411 -0
  13. package/control-plane/scripts/authenticated-mcp-wrapper.mjs +1281 -0
  14. package/control-plane/scripts/benchmark-command.mjs +9 -0
  15. package/control-plane/scripts/benchmark-mcp-catalog.mjs +174 -0
  16. package/control-plane/scripts/catalog-budget.mjs +87 -0
  17. package/control-plane/scripts/check-device-public-contract.mjs +20 -0
  18. package/control-plane/scripts/custom-tools/index.mjs +221 -0
  19. package/control-plane/scripts/custom-tools/path-utils.mjs +117 -0
  20. package/control-plane/scripts/custom-tools/response-utils.mjs +34 -0
  21. package/control-plane/scripts/dashboard-http.mjs +598 -0
  22. package/control-plane/scripts/device-access-policy.mjs +205 -0
  23. package/control-plane/scripts/device-admin.mjs +43 -0
  24. package/control-plane/scripts/device-audit.mjs +63 -0
  25. package/control-plane/scripts/device-broker.mjs +1461 -0
  26. package/control-plane/scripts/device-execution-tool-definitions.mjs +217 -0
  27. package/control-plane/scripts/device-inventory.mjs +128 -0
  28. package/control-plane/scripts/device-pairing-http.mjs +109 -0
  29. package/control-plane/scripts/device-pairing-store.mjs +245 -0
  30. package/control-plane/scripts/device-public-contract.mjs +165 -0
  31. package/control-plane/scripts/device-release.mjs +95 -0
  32. package/control-plane/scripts/device-secure-transport.mjs +128 -0
  33. package/control-plane/scripts/device-store.mjs +283 -0
  34. package/control-plane/scripts/device-usage.mjs +657 -0
  35. package/control-plane/scripts/embedded-device.mjs +149 -0
  36. package/control-plane/scripts/enduser-ui.mjs +45 -0
  37. package/control-plane/scripts/external-tool-broker.mjs +179 -0
  38. package/control-plane/scripts/oauth-authorization-server.mjs +589 -0
  39. package/control-plane/scripts/operator-dashboard-http.mjs +81 -0
  40. package/control-plane/scripts/project-inspection.mjs +227 -0
  41. package/control-plane/scripts/projects/project-id.mjs +69 -0
  42. package/control-plane/scripts/projects/trusted-roots-projects.mjs +375 -0
  43. package/control-plane/scripts/prompts/index.mjs +60 -0
  44. package/control-plane/scripts/remote-process-sessions.mjs +78 -0
  45. package/control-plane/scripts/remote-tool-result.mjs +9 -0
  46. package/control-plane/scripts/resources/index.mjs +166 -0
  47. package/control-plane/scripts/runtime-profile.mjs +54 -0
  48. package/control-plane/scripts/shell-tool-descriptor.mjs +17 -0
  49. package/control-plane/scripts/skill-source-safety.mjs +46 -0
  50. package/control-plane/scripts/skills/README.md +70 -0
  51. package/control-plane/scripts/skills/_upstream_licenses/anthropic-THIRD_PARTY_NOTICES.md +405 -0
  52. package/control-plane/scripts/skills/_upstream_licenses/hallmark-LICENSE.txt +21 -0
  53. package/control-plane/scripts/skills/_upstream_licenses/ponytail-LICENSE.txt +21 -0
  54. package/control-plane/scripts/skills/_upstream_licenses/stitch-skills-LICENSE.txt +201 -0
  55. package/control-plane/scripts/skills/_upstream_licenses/superpowers-LICENSE.txt +21 -0
  56. package/control-plane/scripts/skills/algorithmic-art/LICENSE.txt +202 -0
  57. package/control-plane/scripts/skills/algorithmic-art/SKILL.md +405 -0
  58. package/control-plane/scripts/skills/algorithmic-art/templates/generator_template.js +223 -0
  59. package/control-plane/scripts/skills/algorithmic-art/templates/viewer.html +599 -0
  60. package/control-plane/scripts/skills/brainstorming/SKILL.md +285 -0
  61. package/control-plane/scripts/skills/brainstorming/scripts/frame-template.html +213 -0
  62. package/control-plane/scripts/skills/brainstorming/scripts/helper.js +167 -0
  63. package/control-plane/scripts/skills/brainstorming/scripts/server.cjs +723 -0
  64. package/control-plane/scripts/skills/brainstorming/scripts/start-server.sh +209 -0
  65. package/control-plane/scripts/skills/brainstorming/scripts/stop-server.sh +120 -0
  66. package/control-plane/scripts/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  67. package/control-plane/scripts/skills/brainstorming/visual-companion.md +299 -0
  68. package/control-plane/scripts/skills/brand-guidelines/LICENSE.txt +202 -0
  69. package/control-plane/scripts/skills/brand-guidelines/SKILL.md +73 -0
  70. package/control-plane/scripts/skills/claude-api/LICENSE.txt +202 -0
  71. package/control-plane/scripts/skills/claude-api/SKILL.md +570 -0
  72. package/control-plane/scripts/skills/claude-api/csharp/claude-api/README.md +362 -0
  73. package/control-plane/scripts/skills/claude-api/csharp/claude-api/batches.md +14 -0
  74. package/control-plane/scripts/skills/claude-api/csharp/claude-api/files-api.md +25 -0
  75. package/control-plane/scripts/skills/claude-api/csharp/claude-api/streaming.md +28 -0
  76. package/control-plane/scripts/skills/claude-api/csharp/claude-api/tool-use.md +164 -0
  77. package/control-plane/scripts/skills/claude-api/curl/examples.md +256 -0
  78. package/control-plane/scripts/skills/claude-api/curl/managed-agents.md +371 -0
  79. package/control-plane/scripts/skills/claude-api/go/claude-api/README.md +188 -0
  80. package/control-plane/scripts/skills/claude-api/go/claude-api/files-api.md +23 -0
  81. package/control-plane/scripts/skills/claude-api/go/claude-api/streaming.md +43 -0
  82. package/control-plane/scripts/skills/claude-api/go/claude-api/tool-use.md +220 -0
  83. package/control-plane/scripts/skills/claude-api/go/managed-agents/README.md +564 -0
  84. package/control-plane/scripts/skills/claude-api/java/claude-api/README.md +238 -0
  85. package/control-plane/scripts/skills/claude-api/java/claude-api/files-api.md +27 -0
  86. package/control-plane/scripts/skills/claude-api/java/claude-api/streaming.md +24 -0
  87. package/control-plane/scripts/skills/claude-api/java/claude-api/tool-use.md +228 -0
  88. package/control-plane/scripts/skills/claude-api/java/managed-agents/README.md +443 -0
  89. package/control-plane/scripts/skills/claude-api/php/claude-api/README.md +174 -0
  90. package/control-plane/scripts/skills/claude-api/php/claude-api/batches.md +15 -0
  91. package/control-plane/scripts/skills/claude-api/php/claude-api/files-api.md +13 -0
  92. package/control-plane/scripts/skills/claude-api/php/claude-api/streaming.md +27 -0
  93. package/control-plane/scripts/skills/claude-api/php/claude-api/tool-use.md +253 -0
  94. package/control-plane/scripts/skills/claude-api/php/managed-agents/README.md +443 -0
  95. package/control-plane/scripts/skills/claude-api/python/claude-api/README.md +565 -0
  96. package/control-plane/scripts/skills/claude-api/python/claude-api/batches.md +198 -0
  97. package/control-plane/scripts/skills/claude-api/python/claude-api/files-api.md +170 -0
  98. package/control-plane/scripts/skills/claude-api/python/claude-api/sdk-upgrade.md +286 -0
  99. package/control-plane/scripts/skills/claude-api/python/claude-api/streaming.md +179 -0
  100. package/control-plane/scripts/skills/claude-api/python/claude-api/tool-use.md +627 -0
  101. package/control-plane/scripts/skills/claude-api/python/managed-agents/README.md +335 -0
  102. package/control-plane/scripts/skills/claude-api/ruby/claude-api/README.md +134 -0
  103. package/control-plane/scripts/skills/claude-api/ruby/claude-api/streaming.md +16 -0
  104. package/control-plane/scripts/skills/claude-api/ruby/claude-api/tool-use.md +41 -0
  105. package/control-plane/scripts/skills/claude-api/ruby/managed-agents/README.md +394 -0
  106. package/control-plane/scripts/skills/claude-api/shared/admin-api.md +179 -0
  107. package/control-plane/scripts/skills/claude-api/shared/agent-design.md +101 -0
  108. package/control-plane/scripts/skills/claude-api/shared/anthropic-cli.md +269 -0
  109. package/control-plane/scripts/skills/claude-api/shared/claude-platform-on-aws.md +59 -0
  110. package/control-plane/scripts/skills/claude-api/shared/cost-optimization.md +233 -0
  111. package/control-plane/scripts/skills/claude-api/shared/error-codes.md +262 -0
  112. package/control-plane/scripts/skills/claude-api/shared/live-sources.md +166 -0
  113. package/control-plane/scripts/skills/claude-api/shared/managed-agents-api-reference.md +455 -0
  114. package/control-plane/scripts/skills/claude-api/shared/managed-agents-client-patterns.md +220 -0
  115. package/control-plane/scripts/skills/claude-api/shared/managed-agents-core.md +359 -0
  116. package/control-plane/scripts/skills/claude-api/shared/managed-agents-environments.md +226 -0
  117. package/control-plane/scripts/skills/claude-api/shared/managed-agents-events.md +290 -0
  118. package/control-plane/scripts/skills/claude-api/shared/managed-agents-memory.md +201 -0
  119. package/control-plane/scripts/skills/claude-api/shared/managed-agents-multiagent.md +260 -0
  120. package/control-plane/scripts/skills/claude-api/shared/managed-agents-onboarding.md +82 -0
  121. package/control-plane/scripts/skills/claude-api/shared/managed-agents-outcomes.md +108 -0
  122. package/control-plane/scripts/skills/claude-api/shared/managed-agents-overview.md +77 -0
  123. package/control-plane/scripts/skills/claude-api/shared/managed-agents-scheduled-deployments.md +156 -0
  124. package/control-plane/scripts/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +282 -0
  125. package/control-plane/scripts/skills/claude-api/shared/managed-agents-tools.md +504 -0
  126. package/control-plane/scripts/skills/claude-api/shared/managed-agents-webhooks.md +143 -0
  127. package/control-plane/scripts/skills/claude-api/shared/model-migration.md +1872 -0
  128. package/control-plane/scripts/skills/claude-api/shared/models.md +142 -0
  129. package/control-plane/scripts/skills/claude-api/shared/platform-availability.md +52 -0
  130. package/control-plane/scripts/skills/claude-api/shared/prompt-audit.md +223 -0
  131. package/control-plane/scripts/skills/claude-api/shared/prompt-caching.md +292 -0
  132. package/control-plane/scripts/skills/claude-api/shared/token-counting.md +56 -0
  133. package/control-plane/scripts/skills/claude-api/shared/tool-use-concepts.md +526 -0
  134. package/control-plane/scripts/skills/claude-api/typescript/claude-api/README.md +405 -0
  135. package/control-plane/scripts/skills/claude-api/typescript/claude-api/batches.md +106 -0
  136. package/control-plane/scripts/skills/claude-api/typescript/claude-api/files-api.md +98 -0
  137. package/control-plane/scripts/skills/claude-api/typescript/claude-api/streaming.md +178 -0
  138. package/control-plane/scripts/skills/claude-api/typescript/claude-api/tool-use.md +586 -0
  139. package/control-plane/scripts/skills/claude-api/typescript/managed-agents/README.md +359 -0
  140. package/control-plane/scripts/skills/dispatching-parallel-agents/SKILL.md +167 -0
  141. package/control-plane/scripts/skills/enhance-prompt/README.md +34 -0
  142. package/control-plane/scripts/skills/enhance-prompt/SKILL.md +204 -0
  143. package/control-plane/scripts/skills/enhance-prompt/references/KEYWORDS.md +114 -0
  144. package/control-plane/scripts/skills/executing-plans/SKILL.md +371 -0
  145. package/control-plane/scripts/skills/executing-plans/scripts/task-done +52 -0
  146. package/control-plane/scripts/skills/executing-plans/scripts/task-start +28 -0
  147. package/control-plane/scripts/skills/extract-design-md/SKILL.md +358 -0
  148. package/control-plane/scripts/skills/extract-design-md/examples/DESIGN.md +210 -0
  149. package/control-plane/scripts/skills/extract-design-md/references/angular.md +122 -0
  150. package/control-plane/scripts/skills/extract-design-md/references/plain-css.md +161 -0
  151. package/control-plane/scripts/skills/extract-design-md/references/react-tailwind.md +157 -0
  152. package/control-plane/scripts/skills/extract-design-md/references/svelte.md +101 -0
  153. package/control-plane/scripts/skills/extract-design-md/references/vue.md +107 -0
  154. package/control-plane/scripts/skills/finishing-a-development-branch/SKILL.md +225 -0
  155. package/control-plane/scripts/skills/frontend-design/LICENSE.txt +177 -0
  156. package/control-plane/scripts/skills/frontend-design/SKILL.md +71 -0
  157. package/control-plane/scripts/skills/hallmark/SKILL.md +558 -0
  158. package/control-plane/scripts/skills/hallmark/references/anti-patterns.md +418 -0
  159. package/control-plane/scripts/skills/hallmark/references/assets.md +406 -0
  160. package/control-plane/scripts/skills/hallmark/references/color.md +95 -0
  161. package/control-plane/scripts/skills/hallmark/references/component-cookbook.md +263 -0
  162. package/control-plane/scripts/skills/hallmark/references/components/c1-outlined-chip.md +12 -0
  163. package/control-plane/scripts/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -0
  164. package/control-plane/scripts/skills/hallmark/references/components/c3-typographic-link.md +8 -0
  165. package/control-plane/scripts/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -0
  166. package/control-plane/scripts/skills/hallmark/references/components/f1-bento-grid.md +20 -0
  167. package/control-plane/scripts/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -0
  168. package/control-plane/scripts/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -0
  169. package/control-plane/scripts/skills/hallmark/references/components/f4-step-sequence.md +11 -0
  170. package/control-plane/scripts/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -0
  171. package/control-plane/scripts/skills/hallmark/references/components/f6-product-card-grid.md +41 -0
  172. package/control-plane/scripts/skills/hallmark/references/components/ft1-mast-headed.md +13 -0
  173. package/control-plane/scripts/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -0
  174. package/control-plane/scripts/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -0
  175. package/control-plane/scripts/skills/hallmark/references/components/ft4-dense-typographic.md +10 -0
  176. package/control-plane/scripts/skills/hallmark/references/components/ft5-statement.md +21 -0
  177. package/control-plane/scripts/skills/hallmark/references/components/ft6-letter-close.md +19 -0
  178. package/control-plane/scripts/skills/hallmark/references/components/ft7-newsletter-first.md +27 -0
  179. package/control-plane/scripts/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -0
  180. package/control-plane/scripts/skills/hallmark/references/components/h1-marquee.md +15 -0
  181. package/control-plane/scripts/skills/hallmark/references/components/h2-split-diptych.md +15 -0
  182. package/control-plane/scripts/skills/hallmark/references/components/h3-quote-led.md +11 -0
  183. package/control-plane/scripts/skills/hallmark/references/components/h4-stat-led.md +14 -0
  184. package/control-plane/scripts/skills/hallmark/references/components/h5-letter-hero.md +11 -0
  185. package/control-plane/scripts/skills/hallmark/references/components/h6-photographic-fold.md +16 -0
  186. package/control-plane/scripts/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -0
  187. package/control-plane/scripts/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -0
  188. package/control-plane/scripts/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -0
  189. package/control-plane/scripts/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -0
  190. package/control-plane/scripts/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -0
  191. package/control-plane/scripts/skills/hallmark/references/components/n11-mega-menu.md +40 -0
  192. package/control-plane/scripts/skills/hallmark/references/components/n12-banner-retract.md +34 -0
  193. package/control-plane/scripts/skills/hallmark/references/components/n13-inline-cmdk-pill.md +39 -0
  194. package/control-plane/scripts/skills/hallmark/references/components/n1b-saas-three-section.md +35 -0
  195. package/control-plane/scripts/skills/hallmark/references/components/n2-floating-chip.md +14 -0
  196. package/control-plane/scripts/skills/hallmark/references/components/n3-side-rail.md +14 -0
  197. package/control-plane/scripts/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -0
  198. package/control-plane/scripts/skills/hallmark/references/components/n5-floating-pill.md +28 -0
  199. package/control-plane/scripts/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -0
  200. package/control-plane/scripts/skills/hallmark/references/components/n7-brutal-slab.md +22 -0
  201. package/control-plane/scripts/skills/hallmark/references/components/n8-terminal-command.md +21 -0
  202. package/control-plane/scripts/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -0
  203. package/control-plane/scripts/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -0
  204. package/control-plane/scripts/skills/hallmark/references/components/s2-hanging.md +13 -0
  205. package/control-plane/scripts/skills/hallmark/references/components/s3-sticky-pinned.md +19 -0
  206. package/control-plane/scripts/skills/hallmark/references/components/s4-inline-no-break.md +11 -0
  207. package/control-plane/scripts/skills/hallmark/references/components/s5-bottom-anchored.md +13 -0
  208. package/control-plane/scripts/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -0
  209. package/control-plane/scripts/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -0
  210. package/control-plane/scripts/skills/hallmark/references/components/t3-single-huge-quote.md +11 -0
  211. package/control-plane/scripts/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -0
  212. package/control-plane/scripts/skills/hallmark/references/contract.md +25 -0
  213. package/control-plane/scripts/skills/hallmark/references/copy.md +182 -0
  214. package/control-plane/scripts/skills/hallmark/references/custom-craft.md +626 -0
  215. package/control-plane/scripts/skills/hallmark/references/custom-theme.md +367 -0
  216. package/control-plane/scripts/skills/hallmark/references/design-md.md +116 -0
  217. package/control-plane/scripts/skills/hallmark/references/export-formats.md +329 -0
  218. package/control-plane/scripts/skills/hallmark/references/floating-nav.md +89 -0
  219. package/control-plane/scripts/skills/hallmark/references/genres/atmospheric.md +69 -0
  220. package/control-plane/scripts/skills/hallmark/references/genres/editorial.md +72 -0
  221. package/control-plane/scripts/skills/hallmark/references/genres/modern-minimal.md +71 -0
  222. package/control-plane/scripts/skills/hallmark/references/genres/playful.md +70 -0
  223. package/control-plane/scripts/skills/hallmark/references/hero-enrichment.md +475 -0
  224. package/control-plane/scripts/skills/hallmark/references/imagery-kit.md +170 -0
  225. package/control-plane/scripts/skills/hallmark/references/interaction-and-states.md +208 -0
  226. package/control-plane/scripts/skills/hallmark/references/layout-and-space.md +112 -0
  227. package/control-plane/scripts/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -0
  228. package/control-plane/scripts/skills/hallmark/references/macrostructures/02-long-document.md +34 -0
  229. package/control-plane/scripts/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -0
  230. package/control-plane/scripts/skills/hallmark/references/macrostructures/04-stat-led.md +34 -0
  231. package/control-plane/scripts/skills/hallmark/references/macrostructures/05-workbench.md +32 -0
  232. package/control-plane/scripts/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -0
  233. package/control-plane/scripts/skills/hallmark/references/macrostructures/07-manifesto.md +32 -0
  234. package/control-plane/scripts/skills/hallmark/references/macrostructures/08-photographic.md +34 -0
  235. package/control-plane/scripts/skills/hallmark/references/macrostructures/09-quote-led.md +32 -0
  236. package/control-plane/scripts/skills/hallmark/references/macrostructures/10-specimen.md +32 -0
  237. package/control-plane/scripts/skills/hallmark/references/macrostructures/11-catalogue.md +23 -0
  238. package/control-plane/scripts/skills/hallmark/references/macrostructures/12-letter.md +23 -0
  239. package/control-plane/scripts/skills/hallmark/references/macrostructures/13-index-first.md +23 -0
  240. package/control-plane/scripts/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -0
  241. package/control-plane/scripts/skills/hallmark/references/macrostructures/15-split-studio.md +23 -0
  242. package/control-plane/scripts/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
  243. package/control-plane/scripts/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -0
  244. package/control-plane/scripts/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -0
  245. package/control-plane/scripts/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -0
  246. package/control-plane/scripts/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -0
  247. package/control-plane/scripts/skills/hallmark/references/macrostructures/21-component-playground.md +23 -0
  248. package/control-plane/scripts/skills/hallmark/references/macrostructures.md +89 -0
  249. package/control-plane/scripts/skills/hallmark/references/microinteractions.md +258 -0
  250. package/control-plane/scripts/skills/hallmark/references/motion.md +109 -0
  251. package/control-plane/scripts/skills/hallmark/references/preview-examples.md +49 -0
  252. package/control-plane/scripts/skills/hallmark/references/responsive.md +138 -0
  253. package/control-plane/scripts/skills/hallmark/references/slop-test.md +192 -0
  254. package/control-plane/scripts/skills/hallmark/references/structure.md +160 -0
  255. package/control-plane/scripts/skills/hallmark/references/study.md +509 -0
  256. package/control-plane/scripts/skills/hallmark/references/themes/carnival.md +301 -0
  257. package/control-plane/scripts/skills/hallmark/references/themes/cobalt.md +146 -0
  258. package/control-plane/scripts/skills/hallmark/references/themes/grid.md +161 -0
  259. package/control-plane/scripts/skills/hallmark/references/themes/hum.md +403 -0
  260. package/control-plane/scripts/skills/hallmark/references/themes/lumen.md +478 -0
  261. package/control-plane/scripts/skills/hallmark/references/typography.md +243 -0
  262. package/control-plane/scripts/skills/hallmark/references/upstream/recipes.md +186 -0
  263. package/control-plane/scripts/skills/hallmark/references/upstream/study-examples.md +176 -0
  264. package/control-plane/scripts/skills/hallmark/references/upstream/tokens.css +1221 -0
  265. package/control-plane/scripts/skills/hallmark/references/verbs/audit.md +25 -0
  266. package/control-plane/scripts/skills/hallmark/references/verbs/redesign.md +269 -0
  267. package/control-plane/scripts/skills/human-comms/SKILL.md +29 -0
  268. package/control-plane/scripts/skills/index.mjs +432 -0
  269. package/control-plane/scripts/skills/internal-comms/LICENSE.txt +202 -0
  270. package/control-plane/scripts/skills/internal-comms/SKILL.md +32 -0
  271. package/control-plane/scripts/skills/internal-comms/examples/3p-updates.md +47 -0
  272. package/control-plane/scripts/skills/internal-comms/examples/company-newsletter.md +65 -0
  273. package/control-plane/scripts/skills/internal-comms/examples/faq-answers.md +30 -0
  274. package/control-plane/scripts/skills/internal-comms/examples/general-comms.md +16 -0
  275. package/control-plane/scripts/skills/mcp-adapter.mjs +94 -0
  276. package/control-plane/scripts/skills/mcp-builder/LICENSE.txt +202 -0
  277. package/control-plane/scripts/skills/mcp-builder/SKILL.md +236 -0
  278. package/control-plane/scripts/skills/mcp-builder/reference/evaluation.md +602 -0
  279. package/control-plane/scripts/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  280. package/control-plane/scripts/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  281. package/control-plane/scripts/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  282. package/control-plane/scripts/skills/mcp-builder/scripts/connections.py +151 -0
  283. package/control-plane/scripts/skills/mcp-builder/scripts/evaluation.py +373 -0
  284. package/control-plane/scripts/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  285. package/control-plane/scripts/skills/mcp-builder/scripts/requirements.txt +2 -0
  286. package/control-plane/scripts/skills/ponytail/SKILL.md +120 -0
  287. package/control-plane/scripts/skills/ponytail-audit/SKILL.md +41 -0
  288. package/control-plane/scripts/skills/ponytail-debt/SKILL.md +44 -0
  289. package/control-plane/scripts/skills/ponytail-gain/SKILL.md +50 -0
  290. package/control-plane/scripts/skills/ponytail-help/SKILL.md +71 -0
  291. package/control-plane/scripts/skills/ponytail-review/SKILL.md +57 -0
  292. package/control-plane/scripts/skills/receiving-code-review/SKILL.md +205 -0
  293. package/control-plane/scripts/skills/requesting-code-review/SKILL.md +95 -0
  294. package/control-plane/scripts/skills/requesting-code-review/code-reviewer.md +198 -0
  295. package/control-plane/scripts/skills/skill-creator/LICENSE.txt +202 -0
  296. package/control-plane/scripts/skills/skill-creator/SKILL.md +485 -0
  297. package/control-plane/scripts/skills/skill-creator/agents/analyzer.md +274 -0
  298. package/control-plane/scripts/skills/skill-creator/agents/comparator.md +202 -0
  299. package/control-plane/scripts/skills/skill-creator/agents/grader.md +223 -0
  300. package/control-plane/scripts/skills/skill-creator/assets/eval_review.html +146 -0
  301. package/control-plane/scripts/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  302. package/control-plane/scripts/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  303. package/control-plane/scripts/skills/skill-creator/references/schemas.md +430 -0
  304. package/control-plane/scripts/skills/skill-creator/scripts/__init__.py +0 -0
  305. package/control-plane/scripts/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  306. package/control-plane/scripts/skills/skill-creator/scripts/generate_report.py +326 -0
  307. package/control-plane/scripts/skills/skill-creator/scripts/improve_description.py +247 -0
  308. package/control-plane/scripts/skills/skill-creator/scripts/package_skill.py +136 -0
  309. package/control-plane/scripts/skills/skill-creator/scripts/quick_validate.py +103 -0
  310. package/control-plane/scripts/skills/skill-creator/scripts/run_eval.py +310 -0
  311. package/control-plane/scripts/skills/skill-creator/scripts/run_loop.py +328 -0
  312. package/control-plane/scripts/skills/skill-creator/scripts/utils.py +47 -0
  313. package/control-plane/scripts/skills/slack-gif-creator/LICENSE.txt +202 -0
  314. package/control-plane/scripts/skills/slack-gif-creator/SKILL.md +254 -0
  315. package/control-plane/scripts/skills/slack-gif-creator/core/easing.py +234 -0
  316. package/control-plane/scripts/skills/slack-gif-creator/core/frame_composer.py +176 -0
  317. package/control-plane/scripts/skills/slack-gif-creator/core/gif_builder.py +269 -0
  318. package/control-plane/scripts/skills/slack-gif-creator/core/validators.py +136 -0
  319. package/control-plane/scripts/skills/slack-gif-creator/requirements.txt +4 -0
  320. package/control-plane/scripts/skills/sources.json +401 -0
  321. package/control-plane/scripts/skills/sources.lock.json +416 -0
  322. package/control-plane/scripts/skills/subagent-driven-development/SKILL.md +570 -0
  323. package/control-plane/scripts/skills/subagent-driven-development/implementer-prompt.md +154 -0
  324. package/control-plane/scripts/skills/subagent-driven-development/re-review-prompt.md +115 -0
  325. package/control-plane/scripts/skills/subagent-driven-development/scripts/review-package +53 -0
  326. package/control-plane/scripts/skills/subagent-driven-development/scripts/sdd-workspace +82 -0
  327. package/control-plane/scripts/skills/subagent-driven-development/scripts/task-brief +43 -0
  328. package/control-plane/scripts/skills/subagent-driven-development/task-reviewer-prompt.md +207 -0
  329. package/control-plane/scripts/skills/systematic-debugging/CREATION-LOG.md +119 -0
  330. package/control-plane/scripts/skills/systematic-debugging/SKILL.md +283 -0
  331. package/control-plane/scripts/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  332. package/control-plane/scripts/skills/systematic-debugging/condition-based-waiting.md +115 -0
  333. package/control-plane/scripts/skills/systematic-debugging/defense-in-depth.md +122 -0
  334. package/control-plane/scripts/skills/systematic-debugging/find-polluter.sh +72 -0
  335. package/control-plane/scripts/skills/systematic-debugging/root-cause-tracing.md +169 -0
  336. package/control-plane/scripts/skills/systematic-debugging/test-academic.md +14 -0
  337. package/control-plane/scripts/skills/systematic-debugging/test-pressure-1.md +58 -0
  338. package/control-plane/scripts/skills/systematic-debugging/test-pressure-2.md +68 -0
  339. package/control-plane/scripts/skills/systematic-debugging/test-pressure-3.md +69 -0
  340. package/control-plane/scripts/skills/test-driven-development/SKILL.md +330 -0
  341. package/control-plane/scripts/skills/test-driven-development/writing-good-tests.md +198 -0
  342. package/control-plane/scripts/skills/theme-factory/LICENSE.txt +202 -0
  343. package/control-plane/scripts/skills/theme-factory/SKILL.md +59 -0
  344. package/control-plane/scripts/skills/theme-factory/theme-showcase.pdf +0 -0
  345. package/control-plane/scripts/skills/theme-factory/themes/arctic-frost.md +19 -0
  346. package/control-plane/scripts/skills/theme-factory/themes/botanical-garden.md +19 -0
  347. package/control-plane/scripts/skills/theme-factory/themes/desert-rose.md +19 -0
  348. package/control-plane/scripts/skills/theme-factory/themes/forest-canopy.md +19 -0
  349. package/control-plane/scripts/skills/theme-factory/themes/golden-hour.md +19 -0
  350. package/control-plane/scripts/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  351. package/control-plane/scripts/skills/theme-factory/themes/modern-minimalist.md +19 -0
  352. package/control-plane/scripts/skills/theme-factory/themes/ocean-depths.md +19 -0
  353. package/control-plane/scripts/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  354. package/control-plane/scripts/skills/theme-factory/themes/tech-innovation.md +19 -0
  355. package/control-plane/scripts/skills/using-git-worktrees/SKILL.md +167 -0
  356. package/control-plane/scripts/skills/verification-before-completion/SKILL.md +120 -0
  357. package/control-plane/scripts/skills/web-artifacts-builder/LICENSE.txt +202 -0
  358. package/control-plane/scripts/skills/web-artifacts-builder/SKILL.md +74 -0
  359. package/control-plane/scripts/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  360. package/control-plane/scripts/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  361. package/control-plane/scripts/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  362. package/control-plane/scripts/skills/webapp-testing/LICENSE.txt +202 -0
  363. package/control-plane/scripts/skills/webapp-testing/SKILL.md +96 -0
  364. package/control-plane/scripts/skills/webapp-testing/examples/console_logging.py +35 -0
  365. package/control-plane/scripts/skills/webapp-testing/examples/element_discovery.py +40 -0
  366. package/control-plane/scripts/skills/webapp-testing/examples/static_html_automation.py +33 -0
  367. package/control-plane/scripts/skills/webapp-testing/scripts/with_server.py +106 -0
  368. package/control-plane/scripts/skills/writing-plans/SKILL.md +192 -0
  369. package/control-plane/scripts/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  370. package/control-plane/scripts/skills/writing-skills/SKILL.md +681 -0
  371. package/control-plane/scripts/skills/writing-skills/anthropic-best-practices.md +1150 -0
  372. package/control-plane/scripts/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  373. package/control-plane/scripts/skills/writing-skills/graphviz-conventions.dot +172 -0
  374. package/control-plane/scripts/skills/writing-skills/persuasion-principles.md +187 -0
  375. package/control-plane/scripts/skills/writing-skills/render-graphs.js +169 -0
  376. package/control-plane/scripts/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  377. package/control-plane/scripts/smoke-mcp-schemas.mjs +30 -0
  378. package/control-plane/scripts/surface-config.mjs +45 -0
  379. package/control-plane/scripts/sync-skills.mjs +370 -0
  380. package/control-plane/scripts/tool-metrics.mjs +74 -0
  381. package/control-plane/scripts/tool-risk.mjs +156 -0
  382. package/control-plane/scripts/tool-surface-stability.mjs +38 -0
  383. package/control-plane/scripts/upstreams/catalog-cache.mjs +28 -0
  384. package/control-plane/scripts/upstreams/config.mjs +158 -0
  385. package/control-plane/scripts/upstreams/diagnostics.mjs +21 -0
  386. package/control-plane/scripts/upstreams/http-client.mjs +67 -0
  387. package/control-plane/scripts/upstreams/manager.mjs +535 -0
  388. package/control-plane/scripts/upstreams/names.mjs +55 -0
  389. package/control-plane/scripts/upstreams/presets.mjs +60 -0
  390. package/control-plane/scripts/upstreams/resource-uri.mjs +29 -0
  391. package/control-plane/scripts/upstreams/stdio-client.mjs +73 -0
  392. package/control-plane/scripts/workspace-registry.mjs +147 -0
  393. package/dist/context/authorization.d.ts +5 -0
  394. package/dist/context/authorization.js +70 -0
  395. package/dist/context/capture.d.ts +46 -0
  396. package/dist/context/capture.js +187 -0
  397. package/dist/context/cli.d.ts +47 -0
  398. package/dist/context/cli.js +287 -0
  399. package/dist/context/device-adapter.d.ts +102 -0
  400. package/dist/context/device-adapter.js +79 -0
  401. package/dist/context/gateway.d.ts +354 -0
  402. package/dist/context/gateway.js +50 -0
  403. package/dist/context/graph.d.ts +63 -0
  404. package/dist/context/graph.js +214 -0
  405. package/dist/context/importer.d.ts +8 -0
  406. package/dist/context/importer.js +69 -0
  407. package/dist/context/indexer.d.ts +14 -0
  408. package/dist/context/indexer.js +57 -0
  409. package/dist/context/migrations/index.d.ts +4 -0
  410. package/dist/context/migrations/index.js +87 -0
  411. package/dist/context/redact.d.ts +15 -0
  412. package/dist/context/redact.js +43 -0
  413. package/dist/context/repositories.d.ts +9 -0
  414. package/dist/context/repositories.js +45 -0
  415. package/dist/context/search.d.ts +63 -0
  416. package/dist/context/search.js +207 -0
  417. package/dist/context/service.d.ts +278 -0
  418. package/dist/context/service.js +160 -0
  419. package/dist/context/store.d.ts +134 -0
  420. package/dist/context/store.js +722 -0
  421. package/dist/context/text.d.ts +4 -0
  422. package/dist/context/text.js +14 -0
  423. package/dist/context/tool-contract.d.ts +534 -0
  424. package/dist/context/tool-contract.js +45 -0
  425. package/dist/device/core.d.ts +29 -0
  426. package/dist/device/core.js +66 -0
  427. package/dist/device/device.d.ts +5 -0
  428. package/dist/device/device.js +94 -37
  429. package/dist/device/gateway-channel.d.ts +8 -1
  430. package/dist/device/gateway-channel.js +51 -3
  431. package/dist/device/gateway-tool-adapter.d.ts +11 -3
  432. package/dist/device/gateway-tool-adapter.js +141 -98
  433. package/dist/device/official-trust.d.ts +12 -0
  434. package/dist/device/official-trust.js +25 -1
  435. package/dist/device/runtime-owner.js +9 -1
  436. package/dist/distribution.d.ts +4 -0
  437. package/dist/distribution.js +8 -0
  438. package/dist/handlers/filesystem-handlers.js +53 -37
  439. package/dist/handlers/search-handlers.d.ts +1 -0
  440. package/dist/handlers/search-handlers.js +51 -1
  441. package/dist/log-palace/cli.d.ts +7 -0
  442. package/dist/log-palace/cli.js +89 -0
  443. package/dist/log-palace/formats.d.ts +26 -0
  444. package/dist/log-palace/formats.js +126 -0
  445. package/dist/log-palace/store.d.ts +129 -0
  446. package/dist/log-palace/store.js +856 -0
  447. package/dist/mcp-device.d.ts +1 -1
  448. package/dist/mcp-device.js +28 -6
  449. package/dist/npm-scripts/remote.js +1 -0
  450. package/dist/platform/activity-journal.d.ts +16 -0
  451. package/dist/platform/activity-journal.js +68 -0
  452. package/dist/platform/cli.d.ts +1 -0
  453. package/dist/platform/cli.js +300 -0
  454. package/dist/platform/coordination-lock.d.ts +45 -0
  455. package/dist/platform/coordination-lock.js +233 -0
  456. package/dist/platform/device-runtime.d.ts +16 -0
  457. package/dist/platform/device-runtime.js +83 -0
  458. package/dist/platform/knowledge-contract.d.ts +46 -0
  459. package/dist/platform/knowledge-contract.js +1 -0
  460. package/dist/platform/knowledge-mcp.d.ts +29 -0
  461. package/dist/platform/knowledge-mcp.js +95 -0
  462. package/dist/platform/knowledge-router.d.ts +64 -0
  463. package/dist/platform/knowledge-router.js +320 -0
  464. package/dist/platform/plugin-child.d.ts +1 -0
  465. package/dist/platform/plugin-child.js +115 -0
  466. package/dist/platform/plugin-contract.d.ts +76 -0
  467. package/dist/platform/plugin-contract.js +53 -0
  468. package/dist/platform/plugin-driver.d.ts +8 -0
  469. package/dist/platform/plugin-driver.js +443 -0
  470. package/dist/platform/plugin-host.d.ts +88 -0
  471. package/dist/platform/plugin-host.js +817 -0
  472. package/dist/platform/plugin-policy.d.ts +3 -0
  473. package/dist/platform/plugin-policy.js +29 -0
  474. package/dist/platform/plugin-services.d.ts +14 -0
  475. package/dist/platform/plugin-services.js +32 -0
  476. package/dist/platform/process-identity.d.ts +9 -0
  477. package/dist/platform/process-identity.js +68 -0
  478. package/dist/platform/runtime-lease.d.ts +4 -0
  479. package/dist/platform/runtime-lease.js +58 -0
  480. package/dist/platform/runtime-root.d.ts +9 -0
  481. package/dist/platform/runtime-root.js +61 -0
  482. package/dist/platform/state-backup.d.ts +92 -0
  483. package/dist/platform/state-backup.js +399 -0
  484. package/dist/plugins/reference/plugin.d.ts +3 -0
  485. package/dist/plugins/reference/plugin.js +13 -0
  486. package/dist/search-manager.d.ts +6 -0
  487. package/dist/search-manager.js +123 -0
  488. package/dist/server.d.ts +13 -0
  489. package/dist/server.js +7 -3
  490. package/dist/terminal-manager.d.ts +16 -0
  491. package/dist/terminal-manager.js +137 -42
  492. package/dist/tool-dispatcher.d.ts +11 -0
  493. package/dist/tool-dispatcher.js +228 -0
  494. package/dist/tools/edit.js +72 -1
  495. package/dist/tools/filesystem.d.ts +1 -1
  496. package/dist/tools/filesystem.js +2 -2
  497. package/dist/tools/improved-process-tools.js +91 -108
  498. package/dist/tools/schemas.d.ts +94 -2
  499. package/dist/tools/schemas.js +20 -6
  500. package/dist/types.d.ts +8 -0
  501. package/dist/utils/output-budget.d.ts +6 -0
  502. package/dist/utils/output-budget.js +15 -0
  503. package/dist/version.d.ts +1 -1
  504. package/dist/version.js +1 -1
  505. package/package.json +29 -6
  506. package/skills/mcp-device-context/SKILL.md +40 -0
@@ -0,0 +1,722 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, chmodSync, existsSync, renameSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { hostname } from 'node:os';
5
+ import { execFileSync } from 'node:child_process';
6
+ import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
7
+ import { migrate } from './migrations/index.js';
8
+ import { redact } from './redact.js';
9
+ /** Only context connection constructor, including read-only handles. */
10
+ export function createContextDatabase(path, options = {}) {
11
+ const db = new DatabaseSync(path, options);
12
+ try {
13
+ db.exec('PRAGMA secure_delete=ON; PRAGMA busy_timeout=5000; PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL');
14
+ return db;
15
+ }
16
+ catch (error) {
17
+ db.close();
18
+ throw error;
19
+ }
20
+ }
21
+ const opaque = () => randomBytes(24).toString('hex');
22
+ const hash = (text) => createHash('sha256').update(text).digest('hex');
23
+ const MAX_STORES = 256;
24
+ const MAX_OPEN_STORES = 16;
25
+ /** Device-local storage. State root is supplied by trusted runtime configuration, never tool input. */
26
+ export class ContextStore {
27
+ /** Registry provides lease liveness only; repo.sqlite owns epoch authority.
28
+ * Registry and repository are separate SQLite files, not an atomic transaction. */
29
+ assertReservationLive() {
30
+ if (!this.activeReservation)
31
+ throw new Error('RESERVATION_FENCED_OFF');
32
+ const row = this.registry.prepare('SELECT expires_at FROM reservations WHERE reservation_id=?').get(this.activeReservation);
33
+ if (!row || Number(row.expires_at) <= Date.now())
34
+ throw new Error('RESERVATION_FENCED_OFF');
35
+ }
36
+ /** Store-owned commit gate: stale caller tokens cannot commit repository work. */
37
+ commitRepository(db, token = this.activeReservation) {
38
+ if (!token || token !== this.activeReservation || !db.isTransaction || !this.registry.isTransaction)
39
+ throw new Error('RESERVATION_FENCED_OFF');
40
+ // Compare the cached admission epoch inside the worker's repo transaction,
41
+ // before consulting the possibly reaped registry lease.
42
+ if (this.activeEpoch !== undefined) {
43
+ const fence = db.prepare('SELECT authoritative_epoch,active_reservation_id FROM fencing_state WHERE singleton=1').get();
44
+ if (Number(fence?.authoritative_epoch) !== this.activeEpoch || fence?.active_reservation_id !== token)
45
+ throw new Error('RESERVATION_FENCED_OFF');
46
+ }
47
+ this.assertReservationLive();
48
+ const row = this.registry.prepare(`SELECT r.owner_key,r.repo_uuid,e.epoch FROM reservations r
49
+ JOIN reservation_epochs e USING(reservation_id) WHERE r.reservation_id=?`).get(token);
50
+ if (db !== this.repoDatabase(String(row.owner_key), String(row.repo_uuid)))
51
+ throw new Error('RESERVATION_FENCED_OFF');
52
+ // Callers begin repo work with BEGIN IMMEDIATE. The conditional watermark
53
+ // check/update is one SQL statement in that same transaction (never autocommit).
54
+ // The watermark and receipt commit with evidence, so recovery can distinguish
55
+ // a repo commit from an abandoned allocation even after registry rollback.
56
+ const updated = db.prepare(`UPDATE repository_fence SET last_committed_epoch=?,reservation_id=?
57
+ WHERE singleton=1 AND last_committed_epoch<? AND EXISTS (
58
+ SELECT 1 FROM fencing_state WHERE singleton=1 AND authoritative_epoch=? AND active_reservation_id=?)`).run(row.epoch, token, row.epoch, row.epoch, token);
59
+ if (!updated.changes)
60
+ throw new Error('RESERVATION_FENCED_OFF');
61
+ db.exec('COMMIT');
62
+ }
63
+ constructor(stateRoot, options = {}) {
64
+ this.stateRoot = stateRoot;
65
+ this.databases = new Map();
66
+ this.root = join(stateRoot, 'context');
67
+ if (options.readOnly) {
68
+ try {
69
+ this.key = readFileSync(join(stateRoot, 'context-hmac.key'));
70
+ if (this.key.length !== 32)
71
+ throw new Error();
72
+ this.cursorKey = createHmac('sha256', this.key).update('cursor_signing_v1').digest();
73
+ this.registry = createContextDatabase(join(this.root, 'registry.sqlite'), { readOnly: true });
74
+ if (Number(this.registry.prepare('PRAGMA user_version').get()?.user_version) !== 3) {
75
+ this.registry.close();
76
+ throw new Error('INDEX_INCOMPATIBLE');
77
+ }
78
+ }
79
+ catch (error) {
80
+ if (error instanceof Error && error.message === 'INDEX_INCOMPATIBLE')
81
+ throw error;
82
+ throw new Error('LOCAL_STATE_UNAVAILABLE');
83
+ }
84
+ return;
85
+ }
86
+ mkdirSync(this.root, { recursive: true, mode: 0o700 });
87
+ const keyPath = join(stateRoot, 'context-hmac.key');
88
+ try {
89
+ writeFileSync(keyPath, randomBytes(32), { flag: 'wx', mode: 0o600 });
90
+ }
91
+ catch (error) {
92
+ if (error.code !== 'EEXIST')
93
+ throw new Error('LOCAL_STATE_UNAVAILABLE');
94
+ }
95
+ if (process.platform === 'win32') {
96
+ // Inheritance removal plus explicit current-user grant, rather than ineffective chmod on Windows.
97
+ const user = execFileSync('whoami', [], { encoding: 'utf8' }).trim();
98
+ execFileSync('icacls', [keyPath, '/inheritance:r', '/grant:r', `${user}:F`], { stdio: 'ignore' });
99
+ }
100
+ else
101
+ chmodSync(keyPath, 0o600);
102
+ this.key = readFileSync(keyPath);
103
+ if (this.key.length !== 32)
104
+ throw new Error('LOCAL_STATE_UNAVAILABLE');
105
+ this.cursorKey = createHmac('sha256', this.key).update('cursor_signing_v1').digest();
106
+ this.registry = this.open(join(this.root, 'registry.sqlite'), 'registry');
107
+ this.registry.exec(`CREATE TABLE IF NOT EXISTS quota_policy(singleton INTEGER PRIMARY KEY CHECK(singleton=1), device_bytes INTEGER NOT NULL, owner_bytes INTEGER NOT NULL, repo_bytes INTEGER NOT NULL, metadata_reserve_bytes INTEGER NOT NULL);
108
+ INSERT OR IGNORE INTO quota_policy VALUES(1,8589934592,8589934592,2147483648,134217728);
109
+ CREATE TABLE IF NOT EXISTS cursor_epochs(repo_uuid TEXT PRIMARY KEY, epoch INTEGER NOT NULL);
110
+ CREATE TABLE IF NOT EXISTS admin_audit(audit_id TEXT PRIMARY KEY, repo_uuid TEXT NOT NULL, created_at INTEGER NOT NULL, purged INTEGER NOT NULL);
111
+ CREATE TABLE IF NOT EXISTS purge_retries(repo_uuid TEXT PRIMARY KEY, checkpoint_pending INTEGER NOT NULL);
112
+ CREATE TABLE IF NOT EXISTS quota_usage(repo_uuid TEXT PRIMARY KEY REFERENCES repositories(repo_uuid), evidence_bytes INTEGER NOT NULL);
113
+ CREATE TABLE IF NOT EXISTS reservations(reservation_id TEXT PRIMARY KEY, repo_uuid TEXT NOT NULL REFERENCES repositories(repo_uuid), pid INTEGER NOT NULL, host TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL, allocation INTEGER NOT NULL, owner_key TEXT NOT NULL);
114
+ CREATE TABLE IF NOT EXISTS epoch_counter(repo_uuid TEXT PRIMARY KEY REFERENCES repositories(repo_uuid), epoch INTEGER NOT NULL);
115
+ CREATE TABLE IF NOT EXISTS reservation_epochs(reservation_id TEXT PRIMARY KEY REFERENCES reservations(reservation_id) ON DELETE CASCADE, epoch INTEGER NOT NULL);`);
116
+ try {
117
+ this.reconcileQuota();
118
+ }
119
+ catch (error) {
120
+ this.registry.close();
121
+ throw error;
122
+ }
123
+ }
124
+ open(path, kind) {
125
+ mkdirSync(join(path, '..'), { recursive: true, mode: 0o700 });
126
+ const db = createContextDatabase(path);
127
+ try {
128
+ // Set before the first schema allocation. Existing NONE databases are not
129
+ // converted with a blocking VACUUM: reclaimed pages remain reusable there.
130
+ db.exec('PRAGMA busy_timeout=5000; PRAGMA auto_vacuum=INCREMENTAL; PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL; PRAGMA secure_delete=ON');
131
+ migrate(db, kind);
132
+ if (kind === 'repo') {
133
+ db.exec(`CREATE TABLE IF NOT EXISTS repository_fence(singleton INTEGER PRIMARY KEY CHECK(singleton=1), last_committed_epoch INTEGER NOT NULL, reservation_id TEXT);
134
+ INSERT OR IGNORE INTO repository_fence VALUES(1,0,NULL);
135
+ CREATE TABLE IF NOT EXISTS fencing_state(singleton INTEGER PRIMARY KEY CHECK(singleton=1), authoritative_epoch INTEGER NOT NULL, active_reservation_id TEXT);
136
+ INSERT OR IGNORE INTO fencing_state SELECT 1,last_committed_epoch,NULL FROM repository_fence; `);
137
+ db.exec(`BEGIN IMMEDIATE;
138
+ DELETE FROM generations WHERE status='shadow' AND generation_id NOT IN (SELECT generation FROM manifest WHERE generation IS NOT NULL);
139
+ UPDATE events SET execution_status='unknown_after_restart',recording_status='degraded' WHERE execution_status='pending';
140
+ COMMIT;`);
141
+ const row = db.prepare('SELECT g.indexed_through_event FROM manifest m JOIN generations g ON g.generation_id=m.generation').get();
142
+ const max = Number(db.prepare('SELECT coalesce(max(event_id),0) AS n FROM events').get()?.n);
143
+ if (row && Number(row.indexed_through_event) > max)
144
+ throw new Error('INDEX_INCOMPATIBLE');
145
+ }
146
+ return db;
147
+ }
148
+ catch (error) {
149
+ db.close();
150
+ throw error;
151
+ }
152
+ }
153
+ activeOwner() {
154
+ return this.registry.prepare("SELECT owner_key FROM owners WHERE status='active'").get()?.owner_key ?? null;
155
+ }
156
+ activateOwner(trustedPairingIdentity) {
157
+ if (!trustedPairingIdentity)
158
+ throw new Error('ACCESS_DENIED');
159
+ const pairing = hash(trustedPairingIdentity);
160
+ const active = this.registry.prepare("SELECT owner_key,pairing_hash FROM owners WHERE status='active'").get();
161
+ if (active?.pairing_hash === pairing)
162
+ return active.owner_key;
163
+ const owner = opaque();
164
+ this.registry.exec('BEGIN IMMEDIATE');
165
+ try {
166
+ this.registry.prepare("UPDATE owners SET status='sealed' WHERE status='active'").run();
167
+ this.registry.prepare("INSERT INTO owners(owner_key,pairing_hash,status,created_at) VALUES(?,?,'active',?)").run(owner, pairing, Date.now());
168
+ this.registry.exec('COMMIT');
169
+ }
170
+ catch (error) {
171
+ this.registry.exec('ROLLBACK');
172
+ throw error;
173
+ }
174
+ // Sealed namespace handles must not remain live through default service access.
175
+ for (const db of this.databases.values())
176
+ db.close();
177
+ this.databases.clear();
178
+ return owner;
179
+ }
180
+ authorize(owner, repo) {
181
+ if (!owner || this.activeOwner() !== owner)
182
+ throw new Error('ACCESS_DENIED');
183
+ if (repo && !this.registry.prepare('SELECT 1 FROM repositories WHERE repo_uuid=? AND owner_key=?').get(repo, owner))
184
+ throw new Error('ACCESS_DENIED');
185
+ }
186
+ findRepository(owner, identity) {
187
+ this.authorize(owner);
188
+ return this.registry.prepare('SELECT repo_uuid FROM repositories WHERE owner_key=? AND identity=?').get(owner, identity)?.repo_uuid ?? null;
189
+ }
190
+ repository(owner, identity) {
191
+ this.authorize(owner);
192
+ const existing = this.registry.prepare('SELECT repo_uuid FROM repositories WHERE owner_key=? AND identity=?').get(owner, identity);
193
+ if (existing)
194
+ return existing.repo_uuid;
195
+ this.registry.exec('BEGIN IMMEDIATE');
196
+ try {
197
+ const count = Number(this.registry.prepare('SELECT count(*) AS n FROM repositories WHERE owner_key=?').get(owner)?.n);
198
+ if (count >= MAX_STORES)
199
+ throw new Error('BUDGET_EXCEEDED');
200
+ const repo = opaque();
201
+ this.registry.prepare('INSERT INTO repositories(repo_uuid,owner_key,identity,created_at) VALUES(?,?,?,?)').run(repo, owner, identity, Date.now());
202
+ this.registry.exec('COMMIT');
203
+ return repo;
204
+ }
205
+ catch (error) {
206
+ this.registry.exec('ROLLBACK');
207
+ throw error;
208
+ }
209
+ }
210
+ repositoryPath(owner, repo) {
211
+ const directory = join(this.root, 'owners', owner, 'repos', repo);
212
+ return existsSync(join(directory, 'repo.sqlite')) ? join(directory, 'repo.sqlite') : join(directory, 'context.sqlite');
213
+ }
214
+ database(owner, repo, localAdmin = false) {
215
+ if (localAdmin) {
216
+ if (!repo || !this.registry.prepare('SELECT 1 FROM repositories WHERE owner_key=? AND repo_uuid=?').get(owner, repo))
217
+ throw new Error('ACCESS_DENIED');
218
+ }
219
+ else
220
+ this.authorize(owner, repo);
221
+ const path = repo ? join(this.root, 'owners', owner, 'repos', repo, 'repo.sqlite') : join(this.root, 'owners', owner, 'unscoped.sqlite');
222
+ if (repo && !existsSync(path)) {
223
+ const legacy = join(this.root, 'owners', owner, 'repos', repo, 'context.sqlite');
224
+ // Preserve crash WAL frames when upgrading a closed legacy repository.
225
+ if (existsSync(legacy))
226
+ for (const suffix of ['', '-wal', '-shm']) {
227
+ if (existsSync(legacy + suffix))
228
+ renameSync(legacy + suffix, path + suffix);
229
+ }
230
+ }
231
+ const existing = this.databases.get(path);
232
+ if (existing) {
233
+ this.databases.delete(path);
234
+ this.databases.set(path, existing);
235
+ return existing;
236
+ }
237
+ if (this.databases.size >= MAX_OPEN_STORES) {
238
+ const oldest = this.databases.keys().next().value;
239
+ this.databases.get(oldest).close();
240
+ this.databases.delete(oldest);
241
+ }
242
+ const db = this.open(path, repo ? 'repo' : 'unscoped');
243
+ this.databases.set(path, db);
244
+ return db;
245
+ }
246
+ /** Internal modules only; adapters must use authorized domain methods. */
247
+ repoDatabase(owner, repo) { return this.database(owner, repo); }
248
+ unscopedDatabase(owner) { return this.database(owner); }
249
+ recordUnscoped(owner, reason) {
250
+ const db = this.unscopedDatabase(owner), now = Date.now();
251
+ // A transaction makes dedup safe across processes; preserve other gap reasons.
252
+ db.exec('BEGIN IMMEDIATE');
253
+ try {
254
+ if (reason !== 'quota' || !db.prepare("SELECT 1 FROM gaps WHERE reason='quota' AND created_at>? LIMIT 1").get(now - 60000)) {
255
+ db.prepare('INSERT INTO gaps VALUES(?,?,?)').run(opaque(), now, reason);
256
+ }
257
+ db.exec('COMMIT');
258
+ }
259
+ catch (error) {
260
+ db.exec('ROLLBACK');
261
+ throw error;
262
+ }
263
+ }
264
+ append(owner, repo, input, options = {}) {
265
+ this.authorize(owner, repo);
266
+ const safe = this.redactPayload(input, options); // No raw text, path, or raw hash enters SQLite.
267
+ return this.quotaWrite(owner, repo, safe.retainedBytes * 4 + 65536, () => {
268
+ const db = this.repoDatabase(owner, repo), ref = opaque();
269
+ db.exec('BEGIN IMMEDIATE');
270
+ try {
271
+ db.prepare('INSERT INTO evidence(ref,text,hash,original_bytes,retained_bytes,redaction_state,partial) VALUES(?,?,?,?,?,?,?)').run(ref, safe.text, safe.hash, safe.originalBytes, safe.retainedBytes, safe.state, Number(safe.partial));
272
+ db.prepare('INSERT INTO events(invocation_id,accepted_at,payload_ref) VALUES(?,?,?)').run(opaque(), Date.now(), ref);
273
+ if (options.checkpoint)
274
+ db.prepare("INSERT INTO activity_nodes(node_id,kind,evidence_ref) VALUES(?,'checkpoint',?)").run(opaque(), ref);
275
+ this.commitRepository(db);
276
+ return ref;
277
+ }
278
+ catch (error) {
279
+ db.exec('ROLLBACK');
280
+ if (error instanceof Error && error.message === 'RESERVATION_FENCED_OFF')
281
+ throw error;
282
+ throw new Error('STORAGE_DEGRADED');
283
+ }
284
+ });
285
+ }
286
+ /** All persistence callers share the protected installation key; raw secrets are never hashed unkeyed. */
287
+ redactPayload(input, options = {}) {
288
+ return redact(input, { ...options, key: this.key });
289
+ }
290
+ read(owner, repo, ref) {
291
+ const row = this.repoDatabase(owner, repo).prepare('SELECT * FROM evidence WHERE ref=?').get(ref);
292
+ if (!row)
293
+ throw new Error('REF_NOT_FOUND');
294
+ return row;
295
+ }
296
+ inspectManifest(owner, repo) {
297
+ this.authorize(owner, repo);
298
+ const path = this.repositoryPath(owner, repo);
299
+ const empty = { generation: null, indexed_through_event: 0, manifest_hash: null, pending_events: 0, unknown_events: 0, fts_documents: 0 };
300
+ if (!existsSync(path))
301
+ return empty;
302
+ const existing = this.databases.get(path);
303
+ const db = existing ?? createContextDatabase(path, { readOnly: true });
304
+ try {
305
+ const version = Number(db.prepare('PRAGMA user_version').get()?.user_version);
306
+ if (version !== 3)
307
+ throw new Error('INDEX_INCOMPATIBLE');
308
+ return this.readManifest(db);
309
+ }
310
+ finally {
311
+ if (!existing)
312
+ db.close();
313
+ }
314
+ }
315
+ /** Query-only access never creates, migrates or opens a writable handle. */
316
+ withReadDatabase(owner, repo, action) {
317
+ this.authorize(owner, repo);
318
+ const path = this.repositoryPath(owner, repo);
319
+ if (!existsSync(path))
320
+ throw new Error('INDEX_MISSING');
321
+ const db = createContextDatabase(path, { readOnly: true });
322
+ try {
323
+ if (Number(db.prepare('PRAGMA user_version').get()?.user_version) !== 3)
324
+ throw new Error('INDEX_INCOMPATIBLE');
325
+ return action(db);
326
+ }
327
+ finally {
328
+ db.close();
329
+ }
330
+ }
331
+ signReadCursor(owner, repo, state) {
332
+ this.authorize(owner, repo);
333
+ const body = Buffer.from(JSON.stringify({ ...state, sealed: false, epoch: this.cursorEpoch(repo) })).toString('base64url');
334
+ return `${body}.${createHmac('sha256', this.cursorKey).update(JSON.stringify(['v1', 'read', owner, repo, state.generation, state.key, state.expires_at, false, body])).digest('hex')}`;
335
+ }
336
+ /** Authenticate bounded metadata only. search/read continuation() enforces expiry,
337
+ * operation/query identity and generation policy after successful authentication.
338
+ */
339
+ parseReadCursor(owner, repo, token) {
340
+ this.authorize(owner, repo);
341
+ if (token.length > 4096 || !/^[A-Za-z0-9_-]+\.(?:[a-f0-9]{2})+$/.test(token))
342
+ throw new Error('CURSOR_INVALID');
343
+ const [body, signature] = token.split('.');
344
+ const supplied = Buffer.from(signature, 'hex');
345
+ if (supplied.length !== 32)
346
+ throw new Error('CURSOR_INVALID');
347
+ try {
348
+ // Decode only bounded metadata for the MAC input; do not use state until authenticated.
349
+ const state = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
350
+ if (state?.sealed !== false)
351
+ throw new Error('CURSOR_INVALID');
352
+ const expected = createHmac('sha256', this.cursorKey).update(JSON.stringify(['v1', 'read', owner, repo,
353
+ state.generation, state.key, state.expires_at, false, body])).digest();
354
+ if (!timingSafeEqual(supplied, expected))
355
+ throw new Error('CURSOR_INVALID');
356
+ if (Number(state.epoch ?? 0) !== this.cursorEpoch(repo))
357
+ throw new Error('CURSOR_STALE');
358
+ return state;
359
+ }
360
+ catch (error) {
361
+ if (error instanceof Error && error.message === 'CURSOR_STALE')
362
+ throw error;
363
+ throw new Error('CURSOR_INVALID');
364
+ }
365
+ }
366
+ manifest(owner, repo) {
367
+ return this.readManifest(this.repoDatabase(owner, repo));
368
+ }
369
+ readManifest(db) {
370
+ const row = db.prepare('SELECT g.generation_id,g.indexed_through_event,g.manifest_hash FROM manifest m LEFT JOIN generations g ON g.generation_id=m.generation').get();
371
+ const indexed = Number(row.indexed_through_event ?? 0);
372
+ const pending = Number(db.prepare('SELECT count(*) AS n FROM events WHERE event_id>?').get(indexed)?.n);
373
+ const unknown = Number(db.prepare("SELECT count(*) AS n FROM events WHERE execution_status='unknown_after_restart'").get()?.n);
374
+ return { generation: row.generation_id == null ? null : Number(row.generation_id), indexed_through_event: indexed, manifest_hash: row.manifest_hash ?? null, pending_events: pending, unknown_events: unknown,
375
+ fts_documents: Number(db.prepare('SELECT count(*) AS n FROM search_documents WHERE deleted_generation IS NULL').get()?.n) };
376
+ }
377
+ beginGeneration(owner, repo) {
378
+ return Number(this.repoDatabase(owner, repo).prepare("INSERT INTO generations(status,created_at) VALUES('shadow',?)").run(Date.now()).lastInsertRowid);
379
+ }
380
+ publishGeneration(owner, repo, generation, through, manifestHash) {
381
+ const db = this.repoDatabase(owner, repo);
382
+ const current = this.manifest(owner, repo);
383
+ const maxEvent = Number(db.prepare('SELECT max(event_id) AS n FROM events').get()?.n ?? 0);
384
+ if (!Number.isInteger(through) || through < current.indexed_through_event || through > maxEvent || (current.generation !== null && generation <= current.generation))
385
+ throw new Error('INDEX_INCOMPATIBLE');
386
+ db.exec('BEGIN IMMEDIATE');
387
+ try {
388
+ const result = db.prepare("UPDATE generations SET status='published',indexed_through_event=?,manifest_hash=? WHERE generation_id=? AND status='shadow'").run(through, manifestHash, generation);
389
+ if (!result.changes)
390
+ throw new Error('INDEX_INCOMPATIBLE');
391
+ db.prepare('UPDATE manifest SET generation=? WHERE singleton=1').run(generation);
392
+ db.exec('COMMIT');
393
+ }
394
+ catch (error) {
395
+ db.exec('ROLLBACK');
396
+ throw error;
397
+ }
398
+ }
399
+ cursor(owner, repo, query, position, ttl = 600000) {
400
+ this.authorize(owner, repo);
401
+ if (!Number.isInteger(ttl) || ttl < 1 || ttl > 1800000 || !Number.isSafeInteger(position) || position < 0)
402
+ throw new Error('BUDGET_EXCEEDED');
403
+ const id = opaque(), generation = this.manifest(owner, repo).generation;
404
+ const queryHash = hash(query), expiry = Date.now() + ttl;
405
+ this.repoDatabase(owner, repo).prepare('INSERT INTO cursors VALUES(?,?,?,?,?)').run(id, generation, queryHash, position, expiry);
406
+ const signature = createHmac('sha256', this.cursorKey).update(JSON.stringify(['v1', 'durable', owner, repo, generation, queryHash, expiry, false, id, position])).digest('hex');
407
+ return `${id}.${signature}`;
408
+ }
409
+ validateCursor(owner, repo, token, query, now = Date.now()) {
410
+ this.authorize(owner, repo);
411
+ if (!/^[a-f0-9]{48}\.[a-f0-9]{64}$/.test(token))
412
+ throw new Error('CURSOR_INVALID');
413
+ const [id, signature] = token.split('.');
414
+ const row = this.repoDatabase(owner, repo).prepare('SELECT * FROM cursors WHERE token_id=?').get(id);
415
+ if (!row || row.query_hash !== hash(query))
416
+ throw new Error('CURSOR_INVALID');
417
+ const expected = createHmac('sha256', this.cursorKey).update(JSON.stringify(['v1', 'durable', owner, repo, row.generation, row.query_hash, row.expires_at, false, id, row.position])).digest();
418
+ const supplied = Buffer.from(signature, 'hex');
419
+ if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected))
420
+ throw new Error('CURSOR_INVALID');
421
+ if (Number(row.expires_at) <= now || row.generation !== this.manifest(owner, repo).generation)
422
+ throw new Error('CURSOR_STALE');
423
+ return { generation: row.generation == null ? null : Number(row.generation), position: Number(row.position), expires_at: Number(row.expires_at), query_hash: row.query_hash };
424
+ }
425
+ /** Explicit maintenance accounting includes DB/WAL/blob files in active and sealed namespaces.
426
+ * This is not invoked by status and is not quota enforcement (Phase 5).
427
+ */
428
+ refreshAccounting() {
429
+ const size = (path) => {
430
+ if (!existsSync(path))
431
+ return 0;
432
+ const info = statSync(path);
433
+ if (!info.isDirectory())
434
+ return info.size;
435
+ return readdirSync(path).reduce((sum, entry) => sum + size(join(path, entry)), 0);
436
+ };
437
+ for (const row of this.registry.prepare('SELECT repo_uuid,owner_key FROM repositories').all()) {
438
+ this.registry.prepare('UPDATE repositories SET accounted_bytes=? WHERE repo_uuid=?').run(size(join(this.root, 'owners', String(row.owner_key), 'repos', String(row.repo_uuid))), row.repo_uuid);
439
+ }
440
+ for (const row of this.registry.prepare('SELECT owner_key FROM owners').all()) {
441
+ const owner = row.owner_key;
442
+ this.registry.prepare('UPDATE owners SET accounted_bytes=? WHERE owner_key=?').run(size(join(this.root, 'owners', owner)), owner);
443
+ }
444
+ const ownerBytes = Number(this.registry.prepare('SELECT coalesce(sum(accounted_bytes),0) AS n FROM owners').get()?.n);
445
+ const overhead = size(this.root) - ownerBytes + size(join(this.stateRoot, 'context-hmac.key'));
446
+ this.registry.prepare('UPDATE device_accounting SET overhead_bytes=? WHERE singleton=1').run(overhead);
447
+ return this.accountedBytes();
448
+ }
449
+ /** Accounted allocation includes sealed namespaces; actual-file scan is explicit, never status IO. */
450
+ accountedBytes() {
451
+ return Number(this.registry.prepare('SELECT coalesce(sum(accounted_bytes),0) + (SELECT overhead_bytes FROM device_accounting WHERE singleton=1) AS n FROM owners').get()?.n);
452
+ }
453
+ /** Explicit recovery also works in a continuing process (no restart required).
454
+ * Admission and foreground sync invoke this sweep; no hidden timer is added.
455
+ * Registry-first snapshots retain durable living reservations, and reap only
456
+ * expired leases or demonstrably dead same-host PIDs. Committed evidence is
457
+ * always tallied from repo DBs, never inferred from reservation allocations.
458
+ * Logical evidence totals
459
+ * include sealed stores; physical allocation still gates admission. */
460
+ reconcileQuota() {
461
+ this.registry.exec('BEGIN IMMEDIATE');
462
+ try {
463
+ const total = this.reconcileQuotaLocked();
464
+ this.refreshAccounting();
465
+ this.registry.exec('COMMIT');
466
+ return total;
467
+ }
468
+ catch (error) {
469
+ this.registry.exec('ROLLBACK');
470
+ throw error;
471
+ }
472
+ }
473
+ reconcileQuotaLocked() {
474
+ for (const row of this.registry.prepare('SELECT reservation_id,repo_uuid,owner_key,pid,host,expires_at FROM reservations').all()) {
475
+ let active = Date.now() < Number(row.expires_at);
476
+ if (active && row.host === hostname()) {
477
+ try {
478
+ process.kill(Number(row.pid), 0);
479
+ }
480
+ catch (error) {
481
+ // EPERM means the PID exists but is inaccessible: retain its lease.
482
+ if (error.code === 'ESRCH')
483
+ active = false;
484
+ }
485
+ }
486
+ if (!active) {
487
+ // Revoke before returning quota, atomically under the registry writer lock.
488
+ // Deletion cascades to the token epoch; the monotonic bump also invalidates
489
+ // any cached epoch even when no replacement writer has been admitted.
490
+ const db = this.repoDatabase(String(row.owner_key), String(row.repo_uuid));
491
+ db.exec('BEGIN IMMEDIATE');
492
+ try {
493
+ const epoch = db.prepare('UPDATE fencing_state SET authoritative_epoch=authoritative_epoch+1,active_reservation_id=NULL WHERE singleton=1 RETURNING authoritative_epoch').get().authoritative_epoch;
494
+ db.exec('COMMIT');
495
+ this.registry.prepare('INSERT INTO epoch_counter VALUES(?,?) ON CONFLICT(repo_uuid) DO UPDATE SET epoch=excluded.epoch').run(row.repo_uuid, epoch);
496
+ }
497
+ catch (error) {
498
+ if (db.isTransaction)
499
+ db.exec('ROLLBACK');
500
+ throw error;
501
+ }
502
+ this.registry.prepare('DELETE FROM reservations WHERE reservation_id=?').run(row.reservation_id);
503
+ }
504
+ }
505
+ let total = 0;
506
+ for (const row of this.registry.prepare('SELECT repo_uuid,owner_key FROM repositories').all()) {
507
+ const path = this.repositoryPath(String(row.owner_key), String(row.repo_uuid));
508
+ let bytes = 0;
509
+ if (existsSync(path)) {
510
+ const db = createContextDatabase(path, { readOnly: true });
511
+ try {
512
+ bytes = Number(db.prepare('SELECT coalesce(sum(retained_bytes),0) AS n FROM evidence').get()?.n);
513
+ }
514
+ finally {
515
+ db.close();
516
+ }
517
+ }
518
+ this.registry.prepare('INSERT INTO quota_usage VALUES(?,?) ON CONFLICT(repo_uuid) DO UPDATE SET evidence_bytes=excluded.evidence_bytes').run(row.repo_uuid, bytes);
519
+ total += bytes;
520
+ }
521
+ return total;
522
+ }
523
+ /** Trusted local configuration only. Caps can be lowered; no tool exposes this method. */
524
+ configureQuota(input) {
525
+ const current = this.registry.prepare('SELECT * FROM quota_policy WHERE singleton=1').get();
526
+ const keys = ['device_bytes', 'owner_bytes', 'repo_bytes', 'metadata_reserve_bytes'];
527
+ const values = keys.map(key => input[key] ?? Number(current[key]));
528
+ if (values.some(n => !Number.isSafeInteger(n) || n < 0))
529
+ throw new Error('BUDGET_EXCEEDED');
530
+ this.registry.prepare('UPDATE quota_policy SET device_bytes=?,owner_bytes=?,repo_bytes=?,metadata_reserve_bytes=? WHERE singleton=1').run(...values);
531
+ }
532
+ /** Two-phase, recoverable protocol across separate Registry and Repo SQLite files:
533
+ * Single-writer invariant: each repository has at most one active writer lease.
534
+ * Admission checks for a living lease under registry BEGIN IMMEDIATE; the same
535
+ * mutex serializes reconciliation and is held through repo COMMIT/finalization.
536
+ * The durable allocation/reacquisition gap cannot admit a second writer lease.
537
+ * 1. Repo durably increments fencing_state; registry reserves allocation and
538
+ * records that epoch as a non-authoritative lease token/cache.
539
+ * 2. Repo checks exact epoch/id in its writer transaction and commits evidence.
540
+ * 3. Registry finalizes quota under the same registry-first writer mutex.
541
+ * This is not distributed atomic commit; reconcileQuota repairs the crash gap.
542
+ * Includes DB/WAL/SHM and sealed allocations; reserve is never payload space.
543
+ */
544
+ quotaWrite(owner, repo, allocation, action) {
545
+ this.authorize(owner, repo);
546
+ this.repoDatabase(owner, repo);
547
+ this.unscopedDatabase(owner); // Coverage reserve exists before admission.
548
+ if (this.activeReservation)
549
+ throw new Error('RESERVATION_FENCED_OFF');
550
+ const reservation = opaque();
551
+ this.registry.exec('BEGIN IMMEDIATE');
552
+ try {
553
+ // Automatic reconciliation precedes every quota decision, including failed
554
+ // allocations: dead leases cannot cause a spurious QUOTA_EXCEEDED.
555
+ this.reconcileQuotaLocked();
556
+ if (this.registry.prepare('SELECT 1 FROM reservations WHERE repo_uuid=? LIMIT 1').get(repo))
557
+ throw new Error('BUSY');
558
+ const total = this.refreshAccounting();
559
+ const policy = this.registry.prepare('SELECT * FROM quota_policy WHERE singleton=1').get();
560
+ const ownerSize = Number(this.registry.prepare('SELECT accounted_bytes FROM owners WHERE owner_key=?').get(owner)?.accounted_bytes);
561
+ const repoSize = Number(this.registry.prepare('SELECT accounted_bytes FROM repositories WHERE repo_uuid=?').get(repo)?.accounted_bytes);
562
+ const reserve = Number(policy.metadata_reserve_bytes);
563
+ const pending = this.registry.prepare('SELECT coalesce(sum(allocation),0) AS device, coalesce(sum(CASE WHEN owner_key=? THEN allocation ELSE 0 END),0) AS owner, coalesce(sum(CASE WHEN repo_uuid=? THEN allocation ELSE 0 END),0) AS repo FROM reservations').get(owner, repo);
564
+ if (total + Number(pending.device) + allocation > Number(policy.device_bytes) || ownerSize + Number(pending.owner) + allocation > Number(policy.owner_bytes) || repoSize + Number(pending.repo) + allocation > Number(policy.repo_bytes) - reserve) {
565
+ this.registry.exec('COMMIT');
566
+ this.recordUnscoped(owner, 'quota');
567
+ throw new Error('QUOTA_EXCEEDED');
568
+ }
569
+ const now = Date.now();
570
+ this.registry.prepare('INSERT INTO reservations VALUES(?,?,?,?,?,?,?,?)').run(reservation, repo, process.pid, hostname(), now, now + 300000, allocation, owner);
571
+ const db = this.repoDatabase(owner, repo);
572
+ db.exec('BEGIN IMMEDIATE');
573
+ let epoch;
574
+ try {
575
+ epoch = db.prepare('UPDATE fencing_state SET authoritative_epoch=authoritative_epoch+1,active_reservation_id=? WHERE singleton=1 RETURNING authoritative_epoch').get(reservation).authoritative_epoch;
576
+ db.exec('COMMIT'); // Repository authority is durable before registry admission.
577
+ }
578
+ catch (error) {
579
+ if (db.isTransaction)
580
+ db.exec('ROLLBACK');
581
+ throw error;
582
+ }
583
+ this.registry.prepare('INSERT INTO epoch_counter VALUES(?,?) ON CONFLICT(repo_uuid) DO UPDATE SET epoch=excluded.epoch').run(repo, epoch);
584
+ this.registry.prepare('INSERT INTO reservation_epochs VALUES(?,?)').run(reservation, epoch);
585
+ this.activeReservation = reservation;
586
+ this.activeEpoch = Number(this.registry.prepare('SELECT epoch FROM reservation_epochs WHERE reservation_id=?').get(reservation).epoch);
587
+ // Deterministic interleaving boundary: lease acquired and registry epoch
588
+ // read, admission COMMIT releases registry; NO evidence BEGIN IMMEDIATE
589
+ // has started. A reaper can now revoke in its own repo transaction.
590
+ this.registry.exec('COMMIT'); // Durable before repo writes.
591
+ this.registry.exec('BEGIN IMMEDIATE'); // Preserve registry -> repo lock order.
592
+ const result = action();
593
+ // Do not unlock after repo COMMIT. Expiry fences the repo commit, not
594
+ // accounting for evidence already committed under a valid token.
595
+ this.registry.prepare('DELETE FROM reservations WHERE reservation_id=?').run(reservation);
596
+ this.reconcileQuotaLocked();
597
+ this.refreshAccounting();
598
+ this.registry.exec('COMMIT');
599
+ return result;
600
+ }
601
+ catch (error) {
602
+ try {
603
+ this.registry.exec('ROLLBACK');
604
+ }
605
+ catch { /* Admission rejection already committed accounting. */ }
606
+ this.registry.prepare('DELETE FROM reservations WHERE reservation_id=?').run(reservation);
607
+ throw error;
608
+ }
609
+ finally {
610
+ this.activeReservation = undefined;
611
+ this.activeEpoch = undefined;
612
+ }
613
+ }
614
+ quotaGaps(owner) {
615
+ this.authorize(owner);
616
+ const path = join(this.root, 'owners', owner, 'unscoped.sqlite');
617
+ if (!existsSync(path))
618
+ return 0;
619
+ const db = createContextDatabase(path, { readOnly: true });
620
+ try {
621
+ return Number(db.prepare("SELECT count(*) AS n FROM gaps WHERE reason='quota'").get()?.n);
622
+ }
623
+ finally {
624
+ db.close();
625
+ }
626
+ }
627
+ cursorEpoch(repo) {
628
+ const exists = this.registry.prepare("SELECT 1 FROM sqlite_master WHERE name='cursor_epochs'").get();
629
+ return exists ? Number(this.registry.prepare('SELECT epoch FROM cursor_epochs WHERE repo_uuid=?').get(repo)?.epoch ?? 0) : 0;
630
+ }
631
+ /** Destructive domain seam for trusted local owner administration, never MCP dispatch. */
632
+ cleanup(owner, repo, input) {
633
+ if (input.localAdmin !== true)
634
+ throw new Error('ACCESS_DENIED');
635
+ if (!Number.isSafeInteger(input.before) || input.before < 0)
636
+ throw new Error('BUDGET_EXCEEDED');
637
+ // Purge and capture/sync admission share a registry writer mutex, always
638
+ // acquired before the repo lock. Contention waits up to busy_timeout; on
639
+ // timeout maintenance fails without deleting anything, never steals a writer.
640
+ if (!this.registry.prepare('SELECT 1 FROM repositories WHERE owner_key=? AND repo_uuid=?').get(owner, repo))
641
+ throw new Error('ACCESS_DENIED');
642
+ // Commit intent/epoch separately before repo deletion, so a crash between
643
+ // repo and registry commits cannot erase audit history or revive cursors.
644
+ this.registry.prepare('INSERT INTO cursor_epochs VALUES(?,1) ON CONFLICT(repo_uuid) DO UPDATE SET epoch=epoch+1').run(repo);
645
+ const audit = opaque();
646
+ this.registry.prepare('INSERT INTO admin_audit VALUES(?,?,?,?)').run(audit, repo, Date.now(), 0);
647
+ // Durable before deletion: retries (including after crash) repeat the
648
+ // idempotent deletion/optimize/checkpoint, even when no refs remain.
649
+ this.registry.prepare('INSERT OR REPLACE INTO purge_retries VALUES(?,1)').run(repo);
650
+ this.registry.exec('BEGIN IMMEDIATE');
651
+ try {
652
+ const db = this.database(owner, repo, true);
653
+ db.exec('BEGIN IMMEDIATE');
654
+ let purged = 0;
655
+ try {
656
+ db.prepare('DELETE FROM cursors WHERE expires_at<=?').run(Date.now());
657
+ if (db.prepare('SELECT 1 FROM cursors LIMIT 1').get())
658
+ throw new Error('BUSY');
659
+ const refs = db.prepare("SELECT DISTINCT p.ref FROM evidence p JOIN events e ON e.payload_ref=p.ref WHERE e.accepted_at<? AND p.retention_state='available'").all(input.before);
660
+ for (const row of refs) {
661
+ db.prepare('DELETE FROM evidence_fts WHERE ref=?').run(row.ref);
662
+ db.prepare('DELETE FROM search_documents WHERE ref=?').run(row.ref);
663
+ db.prepare("UPDATE evidence SET text='',retained_bytes=0,retention_state='expired' WHERE ref=?").run(row.ref);
664
+ }
665
+ // secure_delete=ON zeroes deleted content in main SQLite pages. FTS5
666
+ // optimize merges segments into one new tree without old tombstones,
667
+ // removing shared-prefix/suffix fragments that whole-token scans miss.
668
+ // Success additionally requires checked WAL TRUNCATE below.
669
+ db.exec("INSERT INTO evidence_fts(evidence_fts) VALUES('optimize')");
670
+ purged = refs.length;
671
+ db.exec('COMMIT');
672
+ }
673
+ catch (error) {
674
+ db.exec('ROLLBACK');
675
+ throw error;
676
+ }
677
+ this.registry.prepare('UPDATE admin_audit SET purged=? WHERE audit_id=?').run(purged, audit);
678
+ // Bounded reclaim requires no second full-size DB. Registry serialization
679
+ // excludes capture writers; a reader blocking WAL erasure returns BUSY.
680
+ // Legacy NONE stores reuse pages.
681
+ for (let page = 0; page < 256; page++) {
682
+ if (!Number(db.prepare('PRAGMA freelist_count').get()?.freelist_count))
683
+ break;
684
+ db.exec('PRAGMA incremental_vacuum(1)');
685
+ }
686
+ const checkpoint = db.prepare('PRAGMA wal_checkpoint(TRUNCATE)').get();
687
+ // SQLite retries internally for the configured busy_timeout. Missing or
688
+ // nonzero status cannot establish erasure and must fail closed.
689
+ if (!checkpoint || Number(checkpoint.busy) !== 0)
690
+ throw new Error('PURGE_CHECKPOINT_BUSY');
691
+ this.registry.prepare('DELETE FROM purge_retries WHERE repo_uuid=?').run(repo);
692
+ this.reconcileQuotaLocked();
693
+ const accounted_bytes = this.refreshAccounting();
694
+ this.registry.exec('COMMIT');
695
+ return { purged, accounted_bytes };
696
+ }
697
+ catch (error) {
698
+ this.registry.exec('ROLLBACK');
699
+ throw error;
700
+ }
701
+ }
702
+ databaseFiles() {
703
+ const result = [];
704
+ const walk = (path) => {
705
+ for (const entry of readdirSync(path)) {
706
+ const child = join(path, entry);
707
+ if (statSync(child).isDirectory())
708
+ walk(child);
709
+ else if (/\.sqlite(?:-wal|-shm)?$/.test(entry))
710
+ result.push(child);
711
+ }
712
+ };
713
+ walk(this.root);
714
+ return result;
715
+ }
716
+ close() {
717
+ for (const db of this.databases.values())
718
+ db.close();
719
+ this.databases.clear();
720
+ this.registry.close();
721
+ }
722
+ }