@planu/cli 4.13.0 → 4.14.1

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 (787) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +24 -24
  3. package/dist/cli/commands/inspect.d.ts +4 -0
  4. package/dist/cli/commands/inspect.js +144 -0
  5. package/dist/cli/commands/install.d.ts +19 -1
  6. package/dist/cli/commands/install.js +145 -43
  7. package/dist/cli/commands/job.d.ts +3 -0
  8. package/dist/cli/commands/job.js +119 -0
  9. package/dist/cli/commands/offline.d.ts +15 -0
  10. package/dist/cli/commands/offline.js +132 -0
  11. package/dist/cli/commands/serve.js +4 -0
  12. package/dist/cli/commands/spec.js +1 -1
  13. package/dist/cli/commands/storage.d.ts +3 -0
  14. package/dist/cli/commands/storage.js +102 -0
  15. package/dist/cli/commands/uninstall.d.ts +10 -0
  16. package/dist/cli/commands/uninstall.js +93 -7
  17. package/dist/cli/commands/validate.js +1 -1
  18. package/dist/cli/index.js +2 -0
  19. package/dist/cli/router.js +9 -1
  20. package/dist/config/crash-shield-framework-rules.json +93 -0
  21. package/dist/config/environment-schema.json +149 -0
  22. package/dist/config/hardcode-exceptions.json +4 -0
  23. package/dist/config/license-plans.json +7 -0
  24. package/dist/config/native-release-public-key.pem +3 -0
  25. package/dist/config/native-targets.json +87 -0
  26. package/dist/config/product-public.d.ts +89 -0
  27. package/dist/config/product-public.js +140 -0
  28. package/dist/config/product-public.json +312 -0
  29. package/dist/config/product-public.ts +148 -0
  30. package/dist/config/release-compatibility.json +13 -0
  31. package/dist/config/release-policy.json +26 -0
  32. package/dist/config/runtime-policy.json +44 -0
  33. package/dist/config/skill-templates/planu-validate.md +7 -4
  34. package/dist/config/subagent-templates/planu-spec-implementer.md +7 -3
  35. package/dist/config/subagent-templates/planu-validator.md +10 -5
  36. package/dist/config/technology-registry.json +120 -0
  37. package/dist/config/version.js +7 -8
  38. package/dist/config/version.ts +10 -7
  39. package/dist/engine/abortable-process-runner.js +40 -9
  40. package/dist/engine/advanced-testing/accessibility-checker.js +12 -14
  41. package/dist/engine/advanced-testing/mutation-advisor.js +3 -4
  42. package/dist/engine/advanced-testing/property-based.js +2 -3
  43. package/dist/engine/advanced-testing/test-data-advisor.js +2 -3
  44. package/dist/engine/agent-generator.js +2 -1
  45. package/dist/engine/agent-handoff-store.js +2 -1
  46. package/dist/engine/agent-prompt-generator.js +7 -5
  47. package/dist/engine/agent-telemetry.js +2 -1
  48. package/dist/engine/ai-integration/gemini/extensions-advisor.js +41 -9
  49. package/dist/engine/ambiguity-detector.js +12 -11
  50. package/dist/engine/analyzer/db-engine.js +2 -3
  51. package/dist/engine/analyzer/detectors.js +13 -14
  52. package/dist/engine/analyzer/env-setup.js +9 -10
  53. package/dist/engine/analyzer-env-scanner/env-example.js +7 -8
  54. package/dist/engine/api-spec-generator/graphql-parser.js +2 -2
  55. package/dist/engine/api-validation/graphql-schema-validator.js +2 -1
  56. package/dist/engine/api-validation/openapi-impl-validator.js +6 -7
  57. package/dist/engine/architecture-detector.js +10 -8
  58. package/dist/engine/auth/config.d.ts +1 -1
  59. package/dist/engine/auth/config.js +4 -3
  60. package/dist/engine/auto-updater/atomic-upgrade.d.ts +8 -0
  61. package/dist/engine/auto-updater/atomic-upgrade.js +381 -0
  62. package/dist/engine/auto-updater/config-detector.js +9 -3
  63. package/dist/engine/auto-updater/config-patcher.d.ts +0 -4
  64. package/dist/engine/auto-updater/config-patcher.js +10 -15
  65. package/dist/engine/auto-updater/index.d.ts +3 -1
  66. package/dist/engine/auto-updater/index.js +44 -30
  67. package/dist/engine/auto-updater/npm-registry.js +10 -2
  68. package/dist/engine/auto-updater/release-verifier.d.ts +3 -0
  69. package/dist/engine/auto-updater/release-verifier.js +112 -0
  70. package/dist/engine/auto-updater/upgrade-lock.d.ts +6 -0
  71. package/dist/engine/auto-updater/upgrade-lock.js +28 -0
  72. package/dist/engine/auto-updater/upgrade-trust-state.d.ts +7 -0
  73. package/dist/engine/auto-updater/upgrade-trust-state.js +59 -0
  74. package/dist/engine/autopilot/audit-logger.js +2 -5
  75. package/dist/engine/best-practices-library.js +2 -1
  76. package/dist/engine/browser-validator.js +2 -1
  77. package/dist/engine/build-tool-detector.js +3 -2
  78. package/dist/engine/ci-generator/context-builders.js +7 -5
  79. package/dist/engine/ci-generator/stack-detector.js +0 -2
  80. package/dist/engine/ci-generator/yaml-builder.js +0 -2
  81. package/dist/engine/claude-config-auditor/rules-auditor.js +4 -3
  82. package/dist/engine/code-transforms/typescript/ast-utils.js +2 -3
  83. package/dist/engine/complexity-budget/trivial-detector.js +2 -1
  84. package/dist/engine/compliance-test-generator/index.js +7 -2
  85. package/dist/engine/compliance-test-generator/test-formatter.js +2 -2
  86. package/dist/engine/config-health/mcp-config-checker.js +2 -1
  87. package/dist/engine/config-health/ts-checker-scripts.js +4 -3
  88. package/dist/engine/config-health/ts-checker.js +3 -2
  89. package/dist/engine/convention-injector.js +5 -7
  90. package/dist/engine/convention-scanner/convention-parser.js +3 -2
  91. package/dist/engine/core-bridge-duplicate-blocks.d.ts +3 -0
  92. package/dist/engine/core-bridge-duplicate-blocks.js +57 -0
  93. package/dist/engine/core-bridge-gaps.d.ts +4 -0
  94. package/dist/engine/core-bridge-gaps.js +75 -0
  95. package/dist/engine/core-bridge.d.ts +9 -9
  96. package/dist/engine/core-bridge.js +196 -283
  97. package/dist/engine/cost-tracking/ledger.js +2 -5
  98. package/dist/engine/coverage-gap-analyzer.js +10 -1
  99. package/dist/engine/crash-shield/detectors/framework-detector.d.ts +2 -0
  100. package/dist/engine/crash-shield/detectors/framework-detector.js +110 -192
  101. package/dist/engine/crash-shield/detectors/typescript-detector.js +2 -1
  102. package/dist/engine/dashboard/data-loader.js +2 -1
  103. package/dist/engine/data-projects-gc/gc-runner.d.ts +7 -0
  104. package/dist/engine/data-projects-gc/gc-runner.js +113 -3
  105. package/dist/engine/data-projects-gc/scanner.js +5 -1
  106. package/dist/engine/decision-store.js +11 -14
  107. package/dist/engine/deleter/core.js +5 -5
  108. package/dist/engine/dep-auditor/abandonment-data.js +2 -1
  109. package/dist/engine/dep-auditor/abandonment-registry.js +3 -1
  110. package/dist/engine/dep-auditor/cve-fetcher.js +3 -0
  111. package/dist/engine/dep-auditor/dep-duplicates-detector.js +36 -4
  112. package/dist/engine/dep-auditor/license-registry.js +2 -0
  113. package/dist/engine/dependency-evaluator.d.ts +14 -0
  114. package/dist/engine/dependency-evaluator.js +81 -0
  115. package/dist/engine/deploy/providers/fly.js +7 -7
  116. package/dist/engine/deploy/providers/netlify.js +7 -7
  117. package/dist/engine/deploy/providers/railway.js +10 -12
  118. package/dist/engine/deploy/providers/vercel.js +7 -9
  119. package/dist/engine/deprecation-scanner/pattern-loader.js +0 -2
  120. package/dist/engine/desktop-detector.js +2 -3
  121. package/dist/engine/detect-duplication.js +2 -1
  122. package/dist/engine/detectors/advanced-framework-detector/css-state-detector.js +2 -1
  123. package/dist/engine/detectors/advanced-framework-detector/legacy-detector.js +2 -3
  124. package/dist/engine/detectors/advanced-framework-detector/modern-detector.js +3 -4
  125. package/dist/engine/detectors/agent-framework-detector.js +3 -2
  126. package/dist/engine/detectors/ai-native-detector.js +4 -6
  127. package/dist/engine/detectors/cache-db-detector.js +4 -3
  128. package/dist/engine/detectors/cicd-detector.js +4 -3
  129. package/dist/engine/detectors/deployment-detector.js +11 -12
  130. package/dist/engine/detectors/frontend-perf-detector.js +14 -9
  131. package/dist/engine/detectors/library-detector-multilang-a.js +16 -3
  132. package/dist/engine/detectors/library-detector-node.js +14 -4
  133. package/dist/engine/detectors/library-detector-python.js +7 -1
  134. package/dist/engine/detectors/library-detector.js +0 -2
  135. package/dist/engine/detectors/llm-detector/provider-maps.js +3 -2
  136. package/dist/engine/detectors/mcp-candidate-detector.js +12 -16
  137. package/dist/engine/detectors/mfe-detector.js +6 -7
  138. package/dist/engine/detectors/monitoring-detector.js +4 -3
  139. package/dist/engine/detectors/otel-detector.js +3 -2
  140. package/dist/engine/detectors/otel-ecosystem-detectors.d.ts +2 -2
  141. package/dist/engine/detectors/otel-ecosystem-detectors.js +4 -3
  142. package/dist/engine/detectors/platform-specialty-detector.js +3 -5
  143. package/dist/engine/detectors/template-engine-detector.js +3 -4
  144. package/dist/engine/doc-generator/builders.js +2 -3
  145. package/dist/engine/doc-generator/mobile-distribution-guide.js +13 -17
  146. package/dist/engine/doc-generator/portal/dirty-flag-map.d.ts +2 -2
  147. package/dist/engine/doc-generator/portal/portal-regenerator.js +3 -47
  148. package/dist/engine/domain-detector.js +3 -6
  149. package/dist/engine/dynamic-knowledge/knowledge-cache.js +3 -4
  150. package/dist/engine/dynamic-knowledge/knowledge-merger.js +17 -2
  151. package/dist/engine/dynamic-knowledge/web-researcher.js +15 -14
  152. package/dist/engine/dynamic-migration-detector/manifest-readers-extended.js +0 -2
  153. package/dist/engine/e2e-test-generator.js +4 -3
  154. package/dist/engine/ecosystem-absorber/knowledge-cache.js +2 -1
  155. package/dist/engine/ecosystem-absorber/platform-crawler.js +2 -2
  156. package/dist/engine/escalator/retry-counter.js +2 -1
  157. package/dist/engine/evidence-gates/artifact-reader.d.ts +6 -0
  158. package/dist/engine/evidence-gates/artifact-reader.js +105 -21
  159. package/dist/engine/evidence-gates/lifecycle-gate.d.ts +2 -1
  160. package/dist/engine/evidence-gates/lifecycle-gate.js +54 -0
  161. package/dist/engine/execution/context.d.ts +19 -0
  162. package/dist/engine/execution/context.js +127 -0
  163. package/dist/engine/execution/deadline-io.d.ts +21 -0
  164. package/dist/engine/execution/deadline-io.js +45 -0
  165. package/dist/engine/execution/job-executor.d.ts +16 -0
  166. package/dist/engine/execution/job-executor.js +140 -0
  167. package/dist/engine/execution/job-runtime.d.ts +78 -0
  168. package/dist/engine/execution/job-runtime.js +220 -0
  169. package/dist/engine/execution/native-relevance-scorer.d.ts +6 -0
  170. package/dist/engine/execution/native-relevance-scorer.js +64 -0
  171. package/dist/engine/execution/native-worker-dispatch.d.ts +3 -0
  172. package/dist/engine/execution/native-worker-dispatch.js +46 -0
  173. package/dist/engine/execution/native-worker-protocol.d.ts +6 -0
  174. package/dist/engine/execution/native-worker-protocol.js +33 -0
  175. package/dist/engine/execution/native-worker-runtime.d.ts +4 -0
  176. package/dist/engine/execution/native-worker-runtime.js +177 -0
  177. package/dist/engine/execution/native-worker-thread.d.ts +2 -0
  178. package/dist/engine/execution/native-worker-thread.js +66 -0
  179. package/dist/engine/execution/operation-journal.d.ts +66 -0
  180. package/dist/engine/execution/operation-journal.js +260 -0
  181. package/dist/engine/execution/outbox-worker.d.ts +31 -0
  182. package/dist/engine/execution/outbox-worker.js +192 -0
  183. package/dist/engine/execution/validate-job-executor.d.ts +24 -0
  184. package/dist/engine/execution/validate-job-executor.js +342 -0
  185. package/dist/engine/execution/validate-job-payload.d.ts +5 -0
  186. package/dist/engine/execution/validate-job-payload.js +87 -0
  187. package/dist/engine/execution/validate-job-recovery.d.ts +4 -0
  188. package/dist/engine/execution/validate-job-recovery.js +47 -0
  189. package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
  190. package/dist/engine/execution/validate-worker-runtime.js +100 -0
  191. package/dist/engine/execution/validate-worker-thread.d.ts +2 -0
  192. package/dist/engine/execution/validate-worker-thread.js +71 -0
  193. package/dist/engine/execution-plan/desktop-distribution.js +3 -7
  194. package/dist/engine/execution-plan/mobile-distribution.js +4 -9
  195. package/dist/engine/execution-plan/phases.js +4 -5
  196. package/dist/engine/execution-plan/plan-utils.js +8 -5
  197. package/dist/engine/facilitate/context-loader.js +3 -9
  198. package/dist/engine/figma/code-connect-fetcher.js +9 -8
  199. package/dist/engine/filesystem-watcher/index.d.ts +3 -3
  200. package/dist/engine/filesystem-watcher/index.js +5 -5
  201. package/dist/engine/framework-detector.js +14 -15
  202. package/dist/engine/git/planu-autocommit.js +8 -7
  203. package/dist/engine/git/repository-probe.d.ts +3 -0
  204. package/dist/engine/git/repository-probe.js +13 -0
  205. package/dist/engine/global-rules-manager.js +2 -2
  206. package/dist/engine/handoff-artifacts/io.js +2 -3
  207. package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.js +8 -1
  208. package/dist/engine/handoff-artifacts/schemas.d.ts +4 -0
  209. package/dist/engine/handoff-artifacts/schemas.js +5 -0
  210. package/dist/engine/hook-generator/stack-hook-templates.js +4 -4
  211. package/dist/engine/hooks/config-loader.js +1 -1
  212. package/dist/engine/hooks/context-injector.js +4 -7
  213. package/dist/engine/hooks/file-watcher.js +1 -14
  214. package/dist/engine/hooks-reconciler.js +6 -4
  215. package/dist/engine/infrastructure/component-mapper.js +5 -7
  216. package/dist/engine/infrastructure/docker-compose-generator.js +8 -10
  217. package/dist/engine/infrastructure/index.js +6 -3
  218. package/dist/engine/infrastructure/kubernetes-generator.js +2 -2
  219. package/dist/engine/infrastructure/railway-generator.js +10 -8
  220. package/dist/engine/infrastructure/signal-detector.js +6 -8
  221. package/dist/engine/infrastructure/terraform-generator.js +6 -8
  222. package/dist/engine/legal-compliance/detector.js +3 -2
  223. package/dist/engine/local-first/storage-advisor.js +5 -6
  224. package/dist/engine/macos-keychain-native-port.d.ts +5 -0
  225. package/dist/engine/macos-keychain-native-port.js +26 -0
  226. package/dist/engine/marketplace-fetcher/cache.js +3 -1
  227. package/dist/engine/mcp-config/mcp-config-writer.d.ts +2 -2
  228. package/dist/engine/mcp-config/mcp-config-writer.js +10 -8
  229. package/dist/engine/migration/db-migrator.js +2 -5
  230. package/dist/engine/migration/equivalence-mapper.js +12 -13
  231. package/dist/engine/migration/service-catalogs.js +6 -7
  232. package/dist/engine/migration/service-migrator-catalogs.js +6 -7
  233. package/dist/engine/mobile-detector-ios.js +2 -3
  234. package/dist/engine/mobile-detector.js +16 -4
  235. package/dist/engine/model-router/complexity-analyzer.js +4 -7
  236. package/dist/engine/multi-agent-review/security-analyzer.js +3 -1
  237. package/dist/engine/multi-app-detector/app-type.js +26 -21
  238. package/dist/engine/multi-app-detector/core.js +3 -2
  239. package/dist/engine/multi-teammate-review/disagreement-counter.js +2 -1
  240. package/dist/engine/multi-teammate-review/panel-orchestrator.js +0 -2
  241. package/dist/engine/mutation-config-generator.js +10 -7
  242. package/dist/engine/native-capability-policy.d.ts +4 -0
  243. package/dist/engine/native-capability-policy.js +24 -0
  244. package/dist/engine/native-platform.d.ts +3 -0
  245. package/dist/engine/native-platform.js +44 -0
  246. package/dist/engine/native-runtime-handshake.d.ts +20 -0
  247. package/dist/engine/native-runtime-handshake.js +230 -0
  248. package/dist/engine/network-policy.d.ts +27 -0
  249. package/dist/engine/network-policy.js +174 -0
  250. package/dist/engine/next-spec-resolver/dependency-filter.js +2 -22
  251. package/dist/engine/nfr-profile-detector.js +12 -16
  252. package/dist/engine/nli-keywords.js +8 -9
  253. package/dist/engine/oauth/config-guard.js +4 -5
  254. package/dist/engine/observability/local-execution-spans.d.ts +15 -0
  255. package/dist/engine/observability/local-execution-spans.js +63 -0
  256. package/dist/engine/orchestrator/runtime.js +0 -1
  257. package/dist/engine/package-registry.js +2 -6
  258. package/dist/engine/performance-analyzer.js +2 -5
  259. package/dist/engine/permissions-merger/stack-detectors.js +5 -5
  260. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +39 -0
  261. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +1310 -0
  262. package/dist/engine/planu-core.darwin-x64.node.manifest.json +39 -0
  263. package/dist/engine/planu-core.darwin-x64.node.sbom.json +1310 -0
  264. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +38 -0
  265. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +1310 -0
  266. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +38 -0
  267. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +1310 -0
  268. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +38 -0
  269. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +1310 -0
  270. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +38 -0
  271. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +1310 -0
  272. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +38 -0
  273. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +1310 -0
  274. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +38 -0
  275. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +1310 -0
  276. package/dist/engine/platform-detector/desktop.js +15 -2
  277. package/dist/engine/platform-detector/extensions.js +2 -1
  278. package/dist/engine/platform-detector/games.js +2 -1
  279. package/dist/engine/platform-detector.js +3 -2
  280. package/dist/engine/platform-engineering/idp-detector.js +2 -1
  281. package/dist/engine/plugin-installer/stack-matcher.js +9 -17
  282. package/dist/engine/plugins/lifecycle.js +6 -2
  283. package/dist/engine/prior-decisions/contradiction-detector.js +60 -30
  284. package/dist/engine/project-dna/stack-detector.js +15 -17
  285. package/dist/engine/project-graph/builder.js +11 -0
  286. package/dist/engine/project-graph/cache.js +1 -1
  287. package/dist/engine/project-graph/freshness-projection.d.ts +16 -0
  288. package/dist/engine/project-graph/freshness-projection.js +95 -0
  289. package/dist/engine/project-graph/index.d.ts +1 -0
  290. package/dist/engine/project-graph/index.js +1 -0
  291. package/dist/engine/project-graph/query.js +15 -25
  292. package/dist/engine/project-health-checker.js +3 -4
  293. package/dist/engine/project-scanner/stack-detector.js +17 -17
  294. package/dist/engine/property-test-generator.js +2 -3
  295. package/dist/engine/qa-gate.d.ts +1 -0
  296. package/dist/engine/qa-gate.js +6 -4
  297. package/dist/engine/quality-gates/gate-catalog.js +47 -46
  298. package/dist/engine/quality-gates/gate-injector.js +9 -4
  299. package/dist/engine/registry/core.js +0 -2
  300. package/dist/engine/registry/loader.js +9 -12
  301. package/dist/engine/registry/matcher.js +129 -24
  302. package/dist/engine/resilience-detector/concurrency-detector.js +4 -4
  303. package/dist/engine/resilience-detector/middleware-detector.js +11 -13
  304. package/dist/engine/resilience-detector/validation-detector.js +17 -4
  305. package/dist/engine/reverse-engineer/config-analyzer.js +9 -8
  306. package/dist/engine/reverse-engineer/orchestrator.js +5 -9
  307. package/dist/engine/reverse-engineer/test-file-parser.js +6 -3
  308. package/dist/engine/rules-generator/index.js +6 -5
  309. package/dist/engine/runtime-policy.d.ts +7 -0
  310. package/dist/engine/runtime-policy.js +101 -0
  311. package/dist/engine/runtime-security/checkers/content-security.js +21 -6
  312. package/dist/engine/runtime-security/checkers/file-permissions.js +0 -2
  313. package/dist/engine/safety/atomic-write-file.js +20 -9
  314. package/dist/engine/safety/atomic-writer.js +10 -3
  315. package/dist/engine/safety/cross-process-lock.d.ts +3 -40
  316. package/dist/engine/safety/cross-process-lock.js +268 -272
  317. package/dist/engine/safety/file-mutex.d.ts +2 -9
  318. package/dist/engine/safety/file-mutex.js +155 -87
  319. package/dist/engine/safety/health-monitor.js +15 -6
  320. package/dist/engine/safety/lock-lease-store.d.ts +79 -0
  321. package/dist/engine/safety/lock-lease-store.js +372 -0
  322. package/dist/engine/safety/lock-scavenger.d.ts +2 -5
  323. package/dist/engine/safety/lock-scavenger.js +158 -76
  324. package/dist/engine/safety/lock-staleness.js +4 -1
  325. package/dist/engine/safety/obsolete-lock-guard.d.ts +8 -0
  326. package/dist/engine/safety/obsolete-lock-guard.js +31 -0
  327. package/dist/engine/safety/transaction.js +3 -3
  328. package/dist/engine/sandbox/index.js +2 -1
  329. package/dist/engine/sandbox/runner-detect.js +3 -2
  330. package/dist/engine/sandbox/runner-docker.js +4 -3
  331. package/dist/engine/scan-project/module-discoverer.js +3 -2
  332. package/dist/engine/schema-generator.js +16 -9
  333. package/dist/engine/security-analyzer/criteria-generator.js +2 -3
  334. package/dist/engine/self-healing/healer.js +4 -4
  335. package/dist/engine/session-safeguard/learnings-buffer.d.ts +1 -2
  336. package/dist/engine/session-safeguard/learnings-buffer.js +42 -65
  337. package/dist/engine/shared-agent-memory.js +66 -74
  338. package/dist/engine/skill-adapter/command-rewriter.js +3 -2
  339. package/dist/engine/skill-adapter/index.js +3 -3
  340. package/dist/engine/skill-adapter/placeholder-map.js +3 -2
  341. package/dist/engine/skill-bootstrap/stack-matcher.js +47 -48
  342. package/dist/engine/skill-generation/stack-content-generator.js +0 -2
  343. package/dist/engine/skill-generator/workflow-skill-generator.js +2 -1
  344. package/dist/engine/skill-registry/ttl-refresh.js +3 -1
  345. package/dist/engine/skills/skills-fetcher.js +3 -6
  346. package/dist/engine/skills-reconciler.js +19 -39
  347. package/dist/engine/spec-coverage/test-finder.js +7 -6
  348. package/dist/engine/spec-diff-engine.js +3 -3
  349. package/dist/engine/spec-generator/api-key-resolver.d.ts +1 -1
  350. package/dist/engine/spec-generator/api-key-resolver.js +3 -24
  351. package/dist/engine/spec-generators/issue-extractor.js +9 -3
  352. package/dist/engine/spec-migrator/drift-detector.js +5 -13
  353. package/dist/engine/spec-migrator/filesystem-import.js +14 -10
  354. package/dist/engine/spec-migrator/planu-canonical-policy.js +10 -1
  355. package/dist/engine/spec-migrator/strict-planu-cleanup.js +24 -25
  356. package/dist/engine/spec-registry/adapter.d.ts +0 -5
  357. package/dist/engine/spec-registry/adapter.js +45 -46
  358. package/dist/engine/spec-registry/client.js +2 -0
  359. package/dist/engine/spec-source-sync.js +3 -2
  360. package/dist/engine/spec-templates/custom-loader.js +2 -3
  361. package/dist/engine/spec-templates/templates-domain-specific.js +8 -5
  362. package/dist/engine/spec-templates/templates-perf-integration.js +2 -3
  363. package/dist/engine/stack-auditor/manifest-reader.js +0 -2
  364. package/dist/engine/staleness/stale-implementing.js +7 -7
  365. package/dist/engine/tdd-scaffold-generator.js +24 -11
  366. package/dist/engine/teammate-router/green-phase-verifier.js +2 -1
  367. package/dist/engine/teammate-router/red-phase-verifier.js +2 -1
  368. package/dist/engine/technology-registry.d.ts +11 -0
  369. package/dist/engine/technology-registry.js +142 -0
  370. package/dist/engine/telemetry/error-reporter.d.ts +1 -2
  371. package/dist/engine/telemetry/error-reporter.js +11 -18
  372. package/dist/engine/telemetry/health-checker.js +5 -14
  373. package/dist/engine/telemetry/index.d.ts +0 -1
  374. package/dist/engine/telemetry/index.js +0 -1
  375. package/dist/engine/telemetry/stance.js +3 -2
  376. package/dist/engine/telemetry/telemetry-client.d.ts +1 -5
  377. package/dist/engine/telemetry/telemetry-client.js +45 -29
  378. package/dist/engine/telemetry/telemetry-store.d.ts +3 -3
  379. package/dist/engine/telemetry/telemetry-store.js +30 -23
  380. package/dist/engine/test-framework-detector/detectors.js +8 -8
  381. package/dist/engine/test-framework-detector/strategy.js +8 -9
  382. package/dist/engine/test-generators/agent-test-templates.js +2 -3
  383. package/dist/engine/test-generators/contract-test-generator.d.ts +1 -1
  384. package/dist/engine/test-generators/contract-test-generator.js +9 -202
  385. package/dist/engine/test-generators/platform-test-templates-b.js +2 -3
  386. package/dist/engine/test-generators/platform-test-templates.js +2 -5
  387. package/dist/engine/test-mocks-generator.js +4 -3
  388. package/dist/engine/test-plan-generator.js +30 -12
  389. package/dist/engine/test-scaffold-generator/advanced-scaffold.js +4 -5
  390. package/dist/engine/test-scaffold-generator/unit-scaffold.js +5 -6
  391. package/dist/engine/timing/budget.d.ts +6 -17
  392. package/dist/engine/timing/budget.js +80 -64
  393. package/dist/engine/tool-groups/group-manager.js +3 -3
  394. package/dist/engine/universal-rules/installer.js +1 -1
  395. package/dist/engine/universal-rules/rules/planu-modes.js +2 -0
  396. package/dist/engine/universal-rules/rules/planu-release-policy.js +1 -0
  397. package/dist/engine/update-notifier.js +8 -5
  398. package/dist/engine/validation/approved-spec-portability.d.ts +12 -0
  399. package/dist/engine/validation/approved-spec-portability.js +107 -0
  400. package/dist/engine/validation/durable-validation.d.ts +36 -0
  401. package/dist/engine/validation/durable-validation.js +335 -0
  402. package/dist/engine/validation/validation-freshness.d.ts +36 -0
  403. package/dist/engine/validation/validation-freshness.js +427 -0
  404. package/dist/engine/validation/validation-receipt-codec.d.ts +13 -0
  405. package/dist/engine/validation/validation-receipt-codec.js +182 -0
  406. package/dist/engine/validation/validation-receipt.d.ts +59 -0
  407. package/dist/engine/validation/validation-receipt.js +282 -0
  408. package/dist/engine/validation/validation-source-policy.d.ts +4 -0
  409. package/dist/engine/validation/validation-source-policy.js +16 -0
  410. package/dist/engine/validation/validation-submission.d.ts +36 -0
  411. package/dist/engine/validation/validation-submission.js +128 -0
  412. package/dist/engine/validation-evidence-ledger.js +41 -0
  413. package/dist/engine/validation-impact-planner.js +25 -9
  414. package/dist/engine/validator/spec-compliance-runner.d.ts +1 -1
  415. package/dist/engine/validator/spec-compliance-runner.js +17 -8
  416. package/dist/engine/validator/validation-report-writer.js +1 -1
  417. package/dist/engine/validator.js +2 -1
  418. package/dist/engine/verify-integrations/index.js +1 -1
  419. package/dist/engine/version-resolver.js +5 -6
  420. package/dist/engine/web-fetcher/stack-advisor.js +156 -92
  421. package/dist/engine/web-fetcher/stack-detector.js +114 -54
  422. package/dist/engine/workspace-manager.js +3 -2
  423. package/dist/errors/classified-degradation.d.ts +3 -0
  424. package/dist/errors/classified-degradation.js +10 -0
  425. package/dist/errors/degradation-sink.d.ts +3 -0
  426. package/dist/errors/degradation-sink.js +5 -0
  427. package/dist/errors/error-taxonomy.d.ts +12 -0
  428. package/dist/errors/error-taxonomy.js +62 -0
  429. package/dist/hosts/claude-code/ux/mcp-prompts.js +10 -4
  430. package/dist/hosts/claude-code/ux/mcp-resources.js +4 -2
  431. package/dist/hosts/codex/config-scaffold.js +4 -3
  432. package/dist/hosts/gemini/config-scaffold.js +2 -1
  433. package/dist/hosts/gemini/multimodal-spec-flow.js +7 -4
  434. package/dist/index.js +21 -6
  435. package/dist/resources/patterns.js +3 -1
  436. package/dist/resources/specs.js +3 -1
  437. package/dist/resources/templates.js +3 -1
  438. package/dist/security/adapters/environment.d.ts +12 -0
  439. package/dist/security/adapters/environment.js +25 -0
  440. package/dist/security/adapters/linux-secret-service.d.ts +8 -0
  441. package/dist/security/adapters/linux-secret-service.js +60 -0
  442. package/dist/security/adapters/macos-keychain.d.ts +18 -0
  443. package/dist/security/adapters/macos-keychain.js +90 -0
  444. package/dist/security/adapters/windows-credential-manager.d.ts +8 -0
  445. package/dist/security/adapters/windows-credential-manager.js +58 -0
  446. package/dist/security/redactor.d.ts +5 -0
  447. package/dist/security/redactor.js +58 -0
  448. package/dist/security/secret-field.d.ts +5 -0
  449. package/dist/security/secret-field.js +17 -0
  450. package/dist/security/secret-migration.d.ts +3 -0
  451. package/dist/security/secret-migration.js +123 -0
  452. package/dist/security/secret-provider.d.ts +23 -0
  453. package/dist/security/secret-provider.js +145 -0
  454. package/dist/security/secure-json.d.ts +3 -0
  455. package/dist/security/secure-json.js +29 -0
  456. package/dist/storage/api-surfaces-store.js +2 -2
  457. package/dist/storage/approval-operation-lock.d.ts +1 -1
  458. package/dist/storage/approval-operation-lock.js +5 -3
  459. package/dist/storage/audit-trail-store.d.ts +1 -1
  460. package/dist/storage/audit-trail-store.js +48 -62
  461. package/dist/storage/autopilot-log-store.js +12 -5
  462. package/dist/storage/base-store.d.ts +8 -8
  463. package/dist/storage/base-store.js +57 -20
  464. package/dist/storage/checkpoint-store.js +5 -2
  465. package/dist/storage/comments-store.js +2 -2
  466. package/dist/storage/compliance-audit-store.js +5 -2
  467. package/dist/storage/compliance-gate-config-store.js +7 -4
  468. package/dist/storage/compliance-store.js +2 -2
  469. package/dist/storage/context-profile-store.js +2 -2
  470. package/dist/storage/crash-shield-store.js +8 -4
  471. package/dist/storage/feedback-store.js +10 -6
  472. package/dist/storage/figma-store.js +8 -4
  473. package/dist/storage/file-mutex.js +9 -1
  474. package/dist/storage/gaps-log.js +7 -4
  475. package/dist/storage/global-projects-store.js +4 -2
  476. package/dist/storage/ideas-store.js +2 -2
  477. package/dist/storage/index.d.ts +5 -0
  478. package/dist/storage/index.js +5 -0
  479. package/dist/storage/knowledge-store/knowledge.js +27 -33
  480. package/dist/storage/lessons-store.js +2 -2
  481. package/dist/storage/license-store.js +13 -41
  482. package/dist/storage/migrations/canonical-storage.d.ts +15 -0
  483. package/dist/storage/migrations/canonical-storage.js +599 -0
  484. package/dist/storage/oauth-store.js +36 -8
  485. package/dist/storage/path-resolver.js +16 -3
  486. package/dist/storage/pr-store.js +2 -2
  487. package/dist/storage/project-drift-store.js +20 -8
  488. package/dist/storage/project-identity.d.ts +32 -0
  489. package/dist/storage/project-identity.js +194 -0
  490. package/dist/storage/project-resolver.js +14 -7
  491. package/dist/storage/release-notes-store.js +2 -6
  492. package/dist/storage/runtime-db.d.ts +186 -0
  493. package/dist/storage/runtime-db.js +1054 -0
  494. package/dist/storage/schema-registry.d.ts +5 -0
  495. package/dist/storage/schema-registry.js +74 -0
  496. package/dist/storage/sentry-store.js +20 -9
  497. package/dist/storage/session-state-store.js +12 -5
  498. package/dist/storage/session-store.js +12 -4
  499. package/dist/storage/skill-registry-storage.d.ts +1 -1
  500. package/dist/storage/skill-registry-storage.js +13 -11
  501. package/dist/storage/slack-store.js +10 -5
  502. package/dist/storage/spec-store.js +31 -24
  503. package/dist/storage/status-store/file-lock.js +31 -9
  504. package/dist/storage/status-store/self-healing.js +10 -5
  505. package/dist/storage/status-store/version-sync.js +8 -6
  506. package/dist/storage/steering-store.js +2 -2
  507. package/dist/storage/storage-bundle.d.ts +5 -0
  508. package/dist/storage/storage-bundle.js +117 -0
  509. package/dist/storage/storage-catalog-validation.d.ts +3 -0
  510. package/dist/storage/storage-catalog-validation.js +20 -0
  511. package/dist/storage/storage-catalog.d.ts +25 -0
  512. package/dist/storage/storage-catalog.js +390 -0
  513. package/dist/storage/storage-layout.d.ts +24 -0
  514. package/dist/storage/storage-layout.js +101 -0
  515. package/dist/storage/storybook-store.js +2 -2
  516. package/dist/storage/supabase-store.js +24 -6
  517. package/dist/storage/tdd-policy-store.js +8 -6
  518. package/dist/storage/tech-debt-store.js +3 -3
  519. package/dist/storage/technology-selection-store.d.ts +1 -1
  520. package/dist/storage/technology-selection-store.js +6 -3
  521. package/dist/storage/token-cache-store.js +3 -3
  522. package/dist/storage/token-ledger-store.js +13 -8
  523. package/dist/storage/transition-log.js +7 -4
  524. package/dist/storage/trash-store.js +15 -6
  525. package/dist/storage/vector-store/migrator.js +7 -2
  526. package/dist/storage/vector-store/sqlite-adapter.js +4 -1
  527. package/dist/storage/webhook-server-store.js +2 -2
  528. package/dist/tools/api-spec-generator-handler.js +3 -2
  529. package/dist/tools/auto-update-handler.d.ts +1 -1
  530. package/dist/tools/auto-update-handler.js +1 -1
  531. package/dist/tools/browser-validate-handler.js +2 -1
  532. package/dist/tools/challenge-spec/platform-challenge-scenarios-b.js +8 -5
  533. package/dist/tools/challenge-spec/scenarios-desktop.js +8 -3
  534. package/dist/tools/challenge-spec/scenarios-failure.js +7 -2
  535. package/dist/tools/challenge-spec/security-challenge-scenarios.js +3 -3
  536. package/dist/tools/clarify-requirements/multiple-choice.js +53 -41
  537. package/dist/tools/comments-handler.js +2 -3
  538. package/dist/tools/compliance-test-handler.js +2 -2
  539. package/dist/tools/configure-telemetry.js +11 -11
  540. package/dist/tools/create-spec/auto-pipeline.js +13 -1
  541. package/dist/tools/create-spec/autopilot-analyzer.d.ts +1 -1
  542. package/dist/tools/create-spec/autopilot-analyzer.js +2 -4
  543. package/dist/tools/create-spec/mcp-tool-contract.d.ts +10 -4
  544. package/dist/tools/create-spec/mcp-tool-contract.js +7 -36
  545. package/dist/tools/create-spec/post-creation.d.ts +2 -1
  546. package/dist/tools/create-spec/post-creation.js +56 -54
  547. package/dist/tools/create-spec/relevance-scorer.d.ts +1 -1
  548. package/dist/tools/create-spec/relevance-scorer.js +17 -1
  549. package/dist/tools/create-spec/spec-builder.js +2 -12
  550. package/dist/tools/create-spec/spec-created-outbox-consumer.d.ts +14 -0
  551. package/dist/tools/create-spec/spec-created-outbox-consumer.js +176 -0
  552. package/dist/tools/create-spec.js +385 -520
  553. package/dist/tools/define-ui-contract/agent-interaction-contract.js +2 -4
  554. package/dist/tools/define-ui-contract-desktop/desktop-contract.js +12 -4
  555. package/dist/tools/define-ui-contract-desktop/native-contract.js +7 -3
  556. package/dist/tools/define-ui-contract-extensions.js +2 -3
  557. package/dist/tools/define-ui-contract-mobile.js +5 -9
  558. package/dist/tools/define-ui-contract-web/component-tree.js +4 -3
  559. package/dist/tools/define-ui-contract.js +7 -4
  560. package/dist/tools/deploy/deploy-spec.js +2 -4
  561. package/dist/tools/deploy/deploy-status.js +2 -4
  562. package/dist/tools/design-schema/nosql-category-schemas.js +2 -3
  563. package/dist/tools/design-schema-sql/migrations.js +3 -2
  564. package/dist/tools/design-schema-sql/tables.js +4 -1
  565. package/dist/tools/design-schema.js +4 -3
  566. package/dist/tools/feedback-handler.js +2 -4
  567. package/dist/tools/gc-data-projects.js +5 -5
  568. package/dist/tools/generate-execution-plan.js +3 -4
  569. package/dist/tools/generate-sub-agent.js +2 -1
  570. package/dist/tools/generate-tests/generators/a11y-tests-generator.js +5 -4
  571. package/dist/tools/generate-tests/generators/advanced-testing-generator.js +5 -4
  572. package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +9 -4
  573. package/dist/tools/generate-tests/generators/concurrency-test-generator.js +8 -4
  574. package/dist/tools/generate-tests/generators/database/detectors.js +9 -3
  575. package/dist/tools/generate-tests/generators/database/index.js +2 -4
  576. package/dist/tools/generate-tests/generators/event-test-generator.js +5 -4
  577. package/dist/tools/generate-tests/generators/game-tests-generator.js +2 -1
  578. package/dist/tools/generate-tests/generators/graphql-test-generator.js +11 -5
  579. package/dist/tools/generate-tests/generators/grpc-test-generator.js +2 -3
  580. package/dist/tools/generate-tests/generators/llm-security-test-generator.js +4 -3
  581. package/dist/tools/generate-tests/generators/local-first-test-generator.js +4 -3
  582. package/dist/tools/generate-tests/generators/mcp-tests-generator.js +11 -9
  583. package/dist/tools/generate-tests/generators/microservices/index.js +2 -3
  584. package/dist/tools/generate-tests/generators/otel-test-generator.js +6 -3
  585. package/dist/tools/generate-tests/generators/security/detectors.js +2 -5
  586. package/dist/tools/generate-tests/generators/security/index.js +2 -4
  587. package/dist/tools/generate-tests/generators/supply-chain-test-generator.js +2 -3
  588. package/dist/tools/generate-tests/generators/visual-regression-generator.js +14 -17
  589. package/dist/tools/generate-tests/generators/websocket-test-generator.js +2 -3
  590. package/dist/tools/generate-tests/test-helpers.js +11 -12
  591. package/dist/tools/generate-tests-content.js +4 -3
  592. package/dist/tools/generate-tests.js +7 -4
  593. package/dist/tools/git/git-helpers.d.ts +0 -2
  594. package/dist/tools/git/git-helpers.js +7 -7
  595. package/dist/tools/github-pr-handler.js +10 -3
  596. package/dist/tools/heal-spec-docs.js +6 -53
  597. package/dist/tools/hook-generator-handler.js +6 -6
  598. package/dist/tools/init-constitution/platform-principles.js +4 -2
  599. package/dist/tools/init-project/agents-md-writer.js +14 -5
  600. package/dist/tools/init-project/claude-md-generator.js +19 -12
  601. package/dist/tools/init-project/handler.js +165 -73
  602. package/dist/tools/init-project/host-assets-writer.d.ts +1 -1
  603. package/dist/tools/init-project/host-assets-writer.js +4 -2
  604. package/dist/tools/init-project/lifecycle-helpers.js +3 -5
  605. package/dist/tools/init-project/lint-scaffolder.js +6 -2
  606. package/dist/tools/init-project/migration-runner.d.ts +10 -1
  607. package/dist/tools/init-project/migration-runner.js +68 -3
  608. package/dist/tools/init-project/per-client-files-writer.js +14 -4
  609. package/dist/tools/init-project/rules-generator.js +10 -2
  610. package/dist/tools/init-project/scaffold-writer.d.ts +1 -1
  611. package/dist/tools/init-project/scaffold-writer.js +17 -15
  612. package/dist/tools/jobs/handlers.d.ts +15 -0
  613. package/dist/tools/jobs/handlers.js +126 -0
  614. package/dist/tools/list-specs.js +35 -34
  615. package/dist/tools/manage-plugins-handler.js +17 -9
  616. package/dist/tools/migrate-tech/advanced-handlers.js +3 -2
  617. package/dist/tools/migrate-tech/build-tool-handlers.js +3 -2
  618. package/dist/tools/migrate-tech/core-handlers.js +5 -2
  619. package/dist/tools/migrate-tech/data-handlers.js +6 -3
  620. package/dist/tools/migrate-tech-advanced/rollback-handlers.js +3 -2
  621. package/dist/tools/migrate-tech-advanced/service-handlers.js +5 -2
  622. package/dist/tools/oauth-handler.js +7 -1
  623. package/dist/tools/register-sdd-tools.d.ts +1 -1
  624. package/dist/tools/register-sdd-tools.js +5 -0
  625. package/dist/tools/register-spec-tools/core-spec-tools.js +31 -0
  626. package/dist/tools/registry/auth.js +13 -5
  627. package/dist/tools/registry/publish.js +7 -4
  628. package/dist/tools/resolve-project-path.d.ts +2 -2
  629. package/dist/tools/resolve-project-path.js +7 -7
  630. package/dist/tools/reverse-engineer/analyzer.js +14 -14
  631. package/dist/tools/rollback-release.js +3 -2
  632. package/dist/tools/safe-handler.js +295 -171
  633. package/dist/tools/schema-generator-handler.js +2 -1
  634. package/dist/tools/schemas/output-schemas.d.ts +88 -82
  635. package/dist/tools/schemas/output-schemas.js +12 -94
  636. package/dist/tools/schemas/plugins-schemas.js +0 -2
  637. package/dist/tools/schemas/registry.js +0 -2
  638. package/dist/tools/schemas/skill-registry-schemas.js +0 -2
  639. package/dist/tools/schemas/validate-output-schema.d.ts +256 -0
  640. package/dist/tools/schemas/validate-output-schema.js +212 -0
  641. package/dist/tools/sentry-handler.js +6 -1
  642. package/dist/tools/skill-bootstrap-handler.js +7 -4
  643. package/dist/tools/status-handler.js +4 -0
  644. package/dist/tools/storage-bundle-handler.d.ts +9 -0
  645. package/dist/tools/storage-bundle-handler.js +29 -0
  646. package/dist/tools/storage-paths-handler.d.ts +9 -0
  647. package/dist/tools/storage-paths-handler.js +17 -0
  648. package/dist/tools/suggest-mcp-server.js +11 -5
  649. package/dist/tools/suggest-mcps.js +29 -9
  650. package/dist/tools/suggest-tooling/advanced-testing-catalog.js +4 -5
  651. package/dist/tools/suggest-tooling/build-tool-catalog.js +2 -1
  652. package/dist/tools/suggest-tooling/css-framework-catalog.js +13 -7
  653. package/dist/tools/suggest-tooling/framework-catalog.js +3 -5
  654. package/dist/tools/suggest-tooling/frontend-perf-catalog.js +2 -6
  655. package/dist/tools/suggest-tooling/handler.js +10 -4
  656. package/dist/tools/suggest-tooling/hypermedia-catalog.js +11 -3
  657. package/dist/tools/suggest-tooling/linting-catalog.js +4 -7
  658. package/dist/tools/suggest-tooling/llm-security-catalog.js +2 -1
  659. package/dist/tools/suggest-tooling/local-first-catalog.js +7 -5
  660. package/dist/tools/suggest-tooling/observability-catalog.js +3 -4
  661. package/dist/tools/suggest-tooling/otel-catalog.js +4 -1
  662. package/dist/tools/suggest-tooling/resilience-catalog.js +16 -5
  663. package/dist/tools/suggest-tooling/skills-catalog.js +10 -9
  664. package/dist/tools/suggest-tooling/supply-chain-catalog.js +2 -3
  665. package/dist/tools/supabase-handler.js +2 -1
  666. package/dist/tools/token-intelligence-handler.js +2 -2
  667. package/dist/tools/token-recording.js +2 -2
  668. package/dist/tools/token-savings-handler.js +2 -2
  669. package/dist/tools/tool-registry/core-tools.js +131 -0
  670. package/dist/tools/tool-registry/group-infra.js +10 -12
  671. package/dist/tools/tool-registry/group-quality-compliance.js +5 -12
  672. package/dist/tools/update-status/approval-gates.d.ts +13 -0
  673. package/dist/tools/update-status/approval-gates.js +81 -0
  674. package/dist/tools/update-status/batch.js +4 -2
  675. package/dist/tools/update-status/dod-gates.d.ts +6 -24
  676. package/dist/tools/update-status/dod-gates.js +60 -135
  677. package/dist/tools/update-status/done-receipt-verifier.d.ts +15 -0
  678. package/dist/tools/update-status/done-receipt-verifier.js +145 -0
  679. package/dist/tools/update-status/evidence-gate.js +154 -2
  680. package/dist/tools/update-status/file-sync.js +23 -12
  681. package/dist/tools/update-status/gate-harness.d.ts +7 -0
  682. package/dist/tools/update-status/gate-harness.js +8 -0
  683. package/dist/tools/update-status/index.js +228 -325
  684. package/dist/tools/update-status/qa-gate.d.ts +1 -1
  685. package/dist/tools/update-status/qa-gate.js +2 -2
  686. package/dist/tools/update-status/side-effects.js +30 -37
  687. package/dist/tools/update-status/transition-guard.d.ts +4 -2
  688. package/dist/tools/update-status/transition-guard.js +57 -10
  689. package/dist/tools/update-status-convention-gate.js +24 -23
  690. package/dist/tools/update-status-reconcile.js +2 -2
  691. package/dist/tools/validate-api-contract-handler.js +3 -3
  692. package/dist/tools/validate-lint.js +20 -13
  693. package/dist/tools/validate.d.ts +3 -1
  694. package/dist/tools/validate.js +213 -5
  695. package/dist/transports/http-transport.js +84 -61
  696. package/dist/transports/middleware/with-telemetry.js +3 -2
  697. package/dist/types/abortable-process.d.ts +2 -0
  698. package/dist/types/advanced-framework.js +0 -2
  699. package/dist/types/atomic-upgrade.d.ts +71 -0
  700. package/dist/types/atomic-upgrade.js +2 -0
  701. package/dist/types/ci.js +0 -2
  702. package/dist/types/core-bridge.d.ts +78 -1
  703. package/dist/types/crash-shield-framework-rules.d.ts +24 -0
  704. package/dist/types/crash-shield-framework-rules.js +2 -0
  705. package/dist/types/data-gc.d.ts +16 -0
  706. package/dist/types/deploy.js +0 -2
  707. package/dist/types/durable-job.d.ts +78 -0
  708. package/dist/types/durable-job.js +2 -0
  709. package/dist/types/durable-validation.d.ts +60 -0
  710. package/dist/types/durable-validation.js +2 -0
  711. package/dist/types/estimation.d.ts +2 -0
  712. package/dist/types/evidence-gates.d.ts +9 -1
  713. package/dist/types/execution.d.ts +43 -0
  714. package/dist/types/handoff-artifacts.d.ts +2 -0
  715. package/dist/types/index.d.ts +2 -0
  716. package/dist/types/index.js +2 -0
  717. package/dist/types/infrastructure.js +0 -2
  718. package/dist/types/lifecycle.js +0 -2
  719. package/dist/types/migration/canonical-storage.d.ts +37 -0
  720. package/dist/types/migration/canonical-storage.js +2 -0
  721. package/dist/types/migration/index.d.ts +1 -1
  722. package/dist/types/native-worker.d.ts +48 -0
  723. package/dist/types/native-worker.js +2 -0
  724. package/dist/types/network-policy.d.ts +12 -0
  725. package/dist/types/network-policy.js +2 -0
  726. package/dist/types/oauth.js +0 -2
  727. package/dist/types/observability.d.ts +32 -0
  728. package/dist/types/outbox-worker.d.ts +35 -0
  729. package/dist/types/outbox-worker.js +11 -0
  730. package/dist/types/permissions-config.d.ts +0 -27
  731. package/dist/types/permissions-config.js +0 -2
  732. package/dist/types/plugin-install.d.ts +0 -11
  733. package/dist/types/plugin-install.js +0 -2
  734. package/dist/types/portal.d.ts +1 -0
  735. package/dist/types/product-public.d.ts +75 -0
  736. package/dist/types/product-public.js +2 -0
  737. package/dist/types/project/inputs.d.ts +2 -0
  738. package/dist/types/project/mfe-detection.js +0 -2
  739. package/dist/types/project/platform-mobile.js +0 -2
  740. package/dist/types/project/platform-specialty.js +0 -2
  741. package/dist/types/project-knowledge-graph.d.ts +7 -0
  742. package/dist/types/runtime-database.d.ts +10 -0
  743. package/dist/types/runtime-database.js +2 -0
  744. package/dist/types/runtime-policy.d.ts +40 -0
  745. package/dist/types/runtime-policy.js +2 -0
  746. package/dist/types/safety.d.ts +4 -5
  747. package/dist/types/schema-registry.d.ts +12 -0
  748. package/dist/types/schema-registry.js +2 -0
  749. package/dist/types/security.d.ts +52 -0
  750. package/dist/types/skill-registry.js +0 -2
  751. package/dist/types/spec/inputs.d.ts +2 -0
  752. package/dist/types/spec-lock-v2.d.ts +13 -7
  753. package/dist/types/spec-lock-v2.js +1 -1
  754. package/dist/types/spec-registry.js +0 -2
  755. package/dist/types/storage-bundle.d.ts +13 -0
  756. package/dist/types/storage-bundle.js +2 -0
  757. package/dist/types/storage-catalog.d.ts +18 -0
  758. package/dist/types/storage-catalog.js +2 -0
  759. package/dist/types/team-planner.js +0 -2
  760. package/dist/types/technology-registry.d.ts +35 -0
  761. package/dist/types/technology-registry.js +2 -0
  762. package/dist/types/technology-symbols.generated.d.ts +117 -0
  763. package/dist/types/technology-symbols.generated.js +2 -0
  764. package/dist/types/telemetry.d.ts +3 -3
  765. package/dist/types/transport.d.ts +2 -0
  766. package/dist/types/validate-worker.d.ts +44 -0
  767. package/dist/types/validate-worker.js +2 -0
  768. package/dist/types/validation-evidence.d.ts +1 -1
  769. package/dist/types/validation-receipt.d.ts +104 -0
  770. package/dist/types/validation-receipt.js +2 -0
  771. package/package.json +31 -20
  772. package/planu-native.json +1 -1
  773. package/planu-plugin.json +1 -1
  774. package/dist/engine/telemetry/geo-resolver.d.ts +0 -9
  775. package/dist/engine/telemetry/geo-resolver.js +0 -43
  776. package/dist/storage/data-dir-discovery.d.ts +0 -31
  777. package/dist/storage/data-dir-discovery.js +0 -140
  778. package/dist/storage/migrations/cwd-to-absolute.d.ts +0 -25
  779. package/dist/storage/migrations/cwd-to-absolute.js +0 -281
  780. package/dist/tools/create-spec/adapters/prior-decisions-hint.d.ts +0 -14
  781. package/dist/tools/create-spec/adapters/prior-decisions-hint.js +0 -30
  782. package/dist/tools/init-project/ancestor-data-detector.d.ts +0 -22
  783. package/dist/tools/init-project/ancestor-data-detector.js +0 -81
  784. package/dist/tools/update-status/force-done-audit.d.ts +0 -2
  785. package/dist/tools/update-status/force-done-audit.js +0 -23
  786. package/dist/types/migration/cwd-to-absolute.d.ts +0 -8
  787. package/dist/types/migration/cwd-to-absolute.js +0 -3
@@ -0,0 +1,1054 @@
1
+ /* eslint-disable max-lines -- runtime schema, migrations and transactional operations remain co-located for auditability */
2
+ import { mkdirSync } from 'node:fs';
3
+ import { dirname, isAbsolute, resolve } from 'node:path';
4
+ import { DatabaseSync } from 'node:sqlite';
5
+ const CURRENT_SCHEMA_VERSION = 4;
6
+ const DEFAULT_BUSY_TIMEOUT_MS = 5_000;
7
+ const DEFAULT_MAX_CONNECTIONS = 4;
8
+ const openConnections = new Map();
9
+ export const DURABLE_JOB_STATES = [
10
+ 'accepted',
11
+ 'running',
12
+ 'checkpointed',
13
+ 'cancelled',
14
+ 'failed',
15
+ 'dead-letter',
16
+ 'completed',
17
+ ];
18
+ export class RuntimeDatabaseCorruptionError extends Error {
19
+ databasePath;
20
+ detail;
21
+ constructor(databasePath, detail) {
22
+ super(`[Planu] Runtime database failed integrity check: ${detail}`);
23
+ this.databasePath = databasePath;
24
+ this.detail = detail;
25
+ this.name = 'RuntimeDatabaseCorruptionError';
26
+ }
27
+ }
28
+ export class RuntimeDatabaseResourceExhaustedError extends Error {
29
+ databasePath;
30
+ maxConnections;
31
+ constructor(databasePath, maxConnections) {
32
+ super(`[Planu] Runtime database connection limit reached (${String(maxConnections)})`);
33
+ this.databasePath = databasePath;
34
+ this.maxConnections = maxConnections;
35
+ this.name = 'RuntimeDatabaseResourceExhaustedError';
36
+ }
37
+ }
38
+ function requireIdentifier(value, label) {
39
+ if (!value || value.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value)) {
40
+ throw new Error(`[Planu] Invalid ${label}`);
41
+ }
42
+ }
43
+ function parseJson(raw) {
44
+ if (typeof raw !== 'string') {
45
+ throw new Error('[Planu] Runtime database contains invalid JSON');
46
+ }
47
+ return JSON.parse(raw);
48
+ }
49
+ function stringifyJson(value) {
50
+ const serialized = JSON.stringify(value);
51
+ if (serialized === undefined) {
52
+ throw new Error('[Planu] Runtime value is not JSON serializable');
53
+ }
54
+ return serialized;
55
+ }
56
+ /** A bounded (one connection), transactional SQLite runtime store. */
57
+ export class RuntimeDatabase {
58
+ path;
59
+ schemaVersion = CURRENT_SCHEMA_VERSION;
60
+ database;
61
+ connectionKey;
62
+ closed = false;
63
+ inTransaction = false;
64
+ // eslint-disable-next-line complexity -- constructor owns fail-safe acquisition and rollback
65
+ constructor(options) {
66
+ if (!options.path.trim()) {
67
+ throw new Error('[Planu] Runtime database path is required');
68
+ }
69
+ const busyTimeoutMs = options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS;
70
+ if (!Number.isSafeInteger(busyTimeoutMs) || busyTimeoutMs < 0 || busyTimeoutMs > 60_000) {
71
+ throw new Error('[Planu] busyTimeoutMs must be an integer between 0 and 60000');
72
+ }
73
+ if (options.path !== ':memory:' && !isAbsolute(options.path)) {
74
+ throw new Error('[Planu] Runtime database path must be absolute');
75
+ }
76
+ const maxConnections = options.maxConnections ?? DEFAULT_MAX_CONNECTIONS;
77
+ if (!Number.isSafeInteger(maxConnections) || maxConnections < 1 || maxConnections > 32) {
78
+ throw new Error('[Planu] maxConnections must be an integer between 1 and 32');
79
+ }
80
+ this.path = options.path === ':memory:' ? options.path : resolve(options.path);
81
+ this.connectionKey =
82
+ this.path === ':memory:' ? `${this.path}:${randomConnectionKey()}` : this.path;
83
+ const currentConnections = openConnections.get(this.connectionKey) ?? 0;
84
+ if (currentConnections >= maxConnections) {
85
+ throw new RuntimeDatabaseResourceExhaustedError(this.path, maxConnections);
86
+ }
87
+ openConnections.set(this.connectionKey, currentConnections + 1);
88
+ let database;
89
+ try {
90
+ if (this.path !== ':memory:') {
91
+ mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
92
+ }
93
+ database = new DatabaseSync(this.path, {
94
+ timeout: busyTimeoutMs,
95
+ enableForeignKeyConstraints: true,
96
+ enableDoubleQuotedStringLiterals: false,
97
+ allowExtension: false,
98
+ });
99
+ this.database = database;
100
+ this.database.exec('PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL;');
101
+ this.migrate();
102
+ if (options.checkIntegrity !== false) {
103
+ this.assertIntegrity();
104
+ }
105
+ }
106
+ catch (error) {
107
+ database?.close();
108
+ this.releaseConnection();
109
+ if (error instanceof Error && /malformed|not a database|disk image/i.test(error.message)) {
110
+ throw new RuntimeDatabaseCorruptionError(this.path, error.message);
111
+ }
112
+ throw error;
113
+ }
114
+ }
115
+ releaseConnection() {
116
+ const count = openConnections.get(this.connectionKey) ?? 0;
117
+ if (count <= 1) {
118
+ openConnections.delete(this.connectionKey);
119
+ }
120
+ else {
121
+ openConnections.set(this.connectionKey, count - 1);
122
+ }
123
+ }
124
+ assertOpen() {
125
+ if (this.closed) {
126
+ throw new Error('[Planu] Runtime database is closed');
127
+ }
128
+ }
129
+ // eslint-disable-next-line max-lines-per-function -- versioned DDL is intentionally auditable in one transaction
130
+ migrate() {
131
+ const row = this.database.prepare('PRAGMA user_version').get();
132
+ if (row.user_version > CURRENT_SCHEMA_VERSION) {
133
+ throw new Error(`[Planu] Runtime schema ${String(row.user_version)} is newer than supported`);
134
+ }
135
+ if (row.user_version === 0) {
136
+ this.database.exec(`
137
+ BEGIN IMMEDIATE;
138
+ CREATE TABLE runtime_records (
139
+ project_id TEXT NOT NULL,
140
+ workspace_id TEXT NOT NULL,
141
+ kind TEXT NOT NULL CHECK (kind IN ('mutation', 'index', 'audit-state')),
142
+ record_key TEXT NOT NULL,
143
+ value_json TEXT NOT NULL,
144
+ source_digest TEXT,
145
+ updated_at TEXT NOT NULL,
146
+ PRIMARY KEY (project_id, workspace_id, kind, record_key)
147
+ ) STRICT;
148
+ CREATE TABLE jobs (
149
+ id TEXT NOT NULL,
150
+ project_id TEXT NOT NULL,
151
+ workspace_id TEXT NOT NULL,
152
+ state TEXT NOT NULL CHECK (state IN ('accepted', 'running', 'checkpointed', 'cancelled', 'failed', 'dead-letter', 'completed')),
153
+ payload_json TEXT NOT NULL,
154
+ checkpoint_json TEXT,
155
+ result_json TEXT,
156
+ error_message TEXT,
157
+ attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
158
+ max_attempts INTEGER NOT NULL DEFAULT 3 CHECK (max_attempts >= 1),
159
+ cancel_requested INTEGER NOT NULL DEFAULT 0 CHECK (cancel_requested IN (0, 1)),
160
+ lease_owner TEXT,
161
+ lease_expires_at TEXT,
162
+ fencing_token INTEGER NOT NULL DEFAULT 0 CHECK (fencing_token >= 0),
163
+ created_at TEXT NOT NULL,
164
+ updated_at TEXT NOT NULL,
165
+ PRIMARY KEY (project_id, workspace_id, id)
166
+ ) STRICT;
167
+ CREATE TABLE outbox (
168
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
169
+ project_id TEXT NOT NULL,
170
+ workspace_id TEXT NOT NULL,
171
+ topic TEXT NOT NULL,
172
+ payload_json TEXT NOT NULL,
173
+ created_at TEXT NOT NULL,
174
+ delivered_at TEXT
175
+ ) STRICT;
176
+ CREATE TABLE audit_log (
177
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
178
+ project_id TEXT NOT NULL,
179
+ workspace_id TEXT NOT NULL,
180
+ event_type TEXT NOT NULL,
181
+ payload_json TEXT NOT NULL,
182
+ created_at TEXT NOT NULL
183
+ ) STRICT;
184
+ CREATE INDEX jobs_project_state ON jobs(project_id, workspace_id, state);
185
+ CREATE INDEX jobs_lease_expiry ON jobs(state, lease_expires_at);
186
+ CREATE INDEX outbox_pending ON outbox(project_id, workspace_id, delivered_at, id);
187
+ CREATE INDEX audit_project_time ON audit_log(project_id, workspace_id, created_at);
188
+ PRAGMA user_version = 4;
189
+ COMMIT;
190
+ `);
191
+ }
192
+ if (row.user_version === 1) {
193
+ this.database.exec(`
194
+ BEGIN IMMEDIATE;
195
+ CREATE TABLE jobs_v2 (
196
+ id TEXT NOT NULL,
197
+ project_id TEXT NOT NULL,
198
+ workspace_id TEXT NOT NULL,
199
+ state TEXT NOT NULL CHECK (state IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
200
+ payload_json TEXT NOT NULL,
201
+ created_at TEXT NOT NULL,
202
+ updated_at TEXT NOT NULL,
203
+ PRIMARY KEY (project_id, workspace_id, id)
204
+ ) STRICT;
205
+ INSERT INTO jobs_v2 SELECT id, project_id, workspace_id, state, payload_json, created_at, updated_at FROM jobs;
206
+ DROP TABLE jobs;
207
+ ALTER TABLE jobs_v2 RENAME TO jobs;
208
+ CREATE TABLE outbox_v2 (
209
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
210
+ project_id TEXT NOT NULL,
211
+ workspace_id TEXT NOT NULL,
212
+ topic TEXT NOT NULL,
213
+ payload_json TEXT NOT NULL,
214
+ created_at TEXT NOT NULL,
215
+ delivered_at TEXT
216
+ ) STRICT;
217
+ INSERT INTO outbox_v2 (id, project_id, workspace_id, topic, payload_json, created_at, delivered_at)
218
+ SELECT id, '__legacy__', 'global', topic, payload_json, created_at, delivered_at FROM outbox;
219
+ DROP TABLE outbox;
220
+ ALTER TABLE outbox_v2 RENAME TO outbox;
221
+ CREATE INDEX jobs_project_state ON jobs(project_id, workspace_id, state);
222
+ CREATE INDEX outbox_pending ON outbox(project_id, workspace_id, delivered_at, id);
223
+ PRAGMA user_version = 2;
224
+ COMMIT;
225
+ `);
226
+ }
227
+ const migrated = this.database.prepare('PRAGMA user_version').get();
228
+ if (migrated.user_version === 2) {
229
+ this.database.exec(`
230
+ BEGIN IMMEDIATE;
231
+ CREATE TABLE jobs_v3 (
232
+ id TEXT NOT NULL,
233
+ project_id TEXT NOT NULL,
234
+ workspace_id TEXT NOT NULL,
235
+ state TEXT NOT NULL CHECK (state IN ('accepted', 'running', 'checkpointed', 'cancelled', 'failed', 'dead-letter', 'completed')),
236
+ payload_json TEXT NOT NULL,
237
+ checkpoint_json TEXT,
238
+ result_json TEXT,
239
+ error_message TEXT,
240
+ attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
241
+ max_attempts INTEGER NOT NULL DEFAULT 3 CHECK (max_attempts >= 1),
242
+ cancel_requested INTEGER NOT NULL DEFAULT 0 CHECK (cancel_requested IN (0, 1)),
243
+ lease_owner TEXT,
244
+ lease_expires_at TEXT,
245
+ created_at TEXT NOT NULL,
246
+ updated_at TEXT NOT NULL,
247
+ PRIMARY KEY (project_id, workspace_id, id)
248
+ ) STRICT;
249
+ INSERT INTO jobs_v3
250
+ (id, project_id, workspace_id, state, payload_json, created_at, updated_at)
251
+ SELECT id, project_id, workspace_id,
252
+ CASE state
253
+ WHEN 'pending' THEN 'accepted'
254
+ WHEN 'running' THEN 'checkpointed'
255
+ ELSE state
256
+ END,
257
+ payload_json, created_at, updated_at
258
+ FROM jobs;
259
+ DROP TABLE jobs;
260
+ ALTER TABLE jobs_v3 RENAME TO jobs;
261
+ CREATE INDEX jobs_project_state ON jobs(project_id, workspace_id, state);
262
+ CREATE INDEX jobs_lease_expiry ON jobs(state, lease_expires_at);
263
+ PRAGMA user_version = 3;
264
+ COMMIT;
265
+ `);
266
+ }
267
+ const current = this.database.prepare('PRAGMA user_version').get();
268
+ if (current.user_version === 3) {
269
+ this.database.exec(`
270
+ BEGIN IMMEDIATE;
271
+ ALTER TABLE jobs ADD COLUMN fencing_token INTEGER NOT NULL DEFAULT 0 CHECK (fencing_token >= 0);
272
+ UPDATE jobs SET
273
+ state = CASE
274
+ WHEN cancel_requested = 1 THEN 'cancelled'
275
+ WHEN attempts >= max_attempts THEN 'dead-letter'
276
+ ELSE 'checkpointed'
277
+ END,
278
+ error_message = CASE
279
+ WHEN attempts >= max_attempts THEN 'Lease expired after maximum attempts'
280
+ ELSE error_message
281
+ END,
282
+ lease_owner = NULL,
283
+ lease_expires_at = NULL
284
+ WHERE state = 'running';
285
+ PRAGMA user_version = 4;
286
+ COMMIT;
287
+ `);
288
+ }
289
+ }
290
+ assertIntegrity() {
291
+ this.assertOpen();
292
+ const rows = this.database.prepare('PRAGMA quick_check').all();
293
+ const failures = rows.map((row) => row.quick_check).filter((value) => value !== 'ok');
294
+ if (failures.length > 0) {
295
+ throw new RuntimeDatabaseCorruptionError(this.path, failures.join('; '));
296
+ }
297
+ }
298
+ journalMode() {
299
+ this.assertOpen();
300
+ const row = this.database.prepare('PRAGMA journal_mode').get();
301
+ return row.journal_mode;
302
+ }
303
+ transaction(operation) {
304
+ this.assertOpen();
305
+ if (this.inTransaction) {
306
+ throw new Error('[Planu] Nested runtime transactions are not supported');
307
+ }
308
+ this.database.exec('BEGIN IMMEDIATE');
309
+ this.inTransaction = true;
310
+ try {
311
+ const result = operation();
312
+ this.database.exec('COMMIT');
313
+ return result;
314
+ }
315
+ catch (error) {
316
+ this.database.exec('ROLLBACK');
317
+ throw error;
318
+ }
319
+ finally {
320
+ this.inTransaction = false;
321
+ }
322
+ }
323
+ /** Run a consistent read-only snapshot without acquiring an immediate writer lock. */
324
+ snapshot(operation) {
325
+ this.assertOpen();
326
+ if (this.inTransaction) {
327
+ throw new Error('[Planu] Nested runtime transactions are not supported');
328
+ }
329
+ this.database.exec('BEGIN');
330
+ this.inTransaction = true;
331
+ try {
332
+ const result = operation();
333
+ this.database.exec('COMMIT');
334
+ return result;
335
+ }
336
+ catch (error) {
337
+ this.database.exec('ROLLBACK');
338
+ throw error;
339
+ }
340
+ finally {
341
+ this.inTransaction = false;
342
+ }
343
+ }
344
+ putRecord(record) {
345
+ this.assertOpen();
346
+ requireIdentifier(record.projectId, 'projectId');
347
+ requireIdentifier(record.workspaceId, 'workspaceId');
348
+ requireIdentifier(record.key, 'record key');
349
+ const valueJson = stringifyJson(record.value);
350
+ this.database
351
+ .prepare(`
352
+ INSERT INTO runtime_records
353
+ (project_id, workspace_id, kind, record_key, value_json, source_digest, updated_at)
354
+ VALUES (?, ?, ?, ?, ?, ?, ?)
355
+ ON CONFLICT(project_id, workspace_id, kind, record_key) DO UPDATE SET
356
+ value_json = excluded.value_json,
357
+ source_digest = excluded.source_digest,
358
+ updated_at = excluded.updated_at
359
+ WHERE excluded.updated_at >= runtime_records.updated_at
360
+ `)
361
+ .run(record.projectId, record.workspaceId, record.kind, record.key, valueJson, record.sourceDigest ?? null, record.updatedAt ?? new Date().toISOString());
362
+ }
363
+ getRecord(projectId, workspaceId, kind, key) {
364
+ this.assertOpen();
365
+ requireIdentifier(projectId, 'projectId');
366
+ requireIdentifier(workspaceId, 'workspaceId');
367
+ requireIdentifier(key, 'record key');
368
+ const row = this.database
369
+ .prepare(`
370
+ SELECT project_id, workspace_id, kind, record_key, value_json, source_digest, updated_at
371
+ FROM runtime_records
372
+ WHERE project_id = ? AND workspace_id = ? AND kind = ? AND record_key = ?
373
+ `)
374
+ .get(projectId, workspaceId, kind, key);
375
+ return row ? mapRuntimeRecordRow(row) : undefined;
376
+ }
377
+ listRecords(projectId, workspaceId, kind) {
378
+ this.assertOpen();
379
+ requireIdentifier(projectId, 'projectId');
380
+ requireIdentifier(workspaceId, 'workspaceId');
381
+ const rows = (kind
382
+ ? this.database
383
+ .prepare(`SELECT project_id, workspace_id, kind, record_key, value_json, source_digest, updated_at
384
+ FROM runtime_records
385
+ WHERE project_id = ? AND workspace_id = ? AND kind = ?
386
+ ORDER BY record_key`)
387
+ .all(projectId, workspaceId, kind)
388
+ : this.database
389
+ .prepare(`SELECT project_id, workspace_id, kind, record_key, value_json, source_digest, updated_at
390
+ FROM runtime_records
391
+ WHERE project_id = ? AND workspace_id = ?
392
+ ORDER BY kind, record_key`)
393
+ .all(projectId, workspaceId));
394
+ return rows.map(mapRuntimeRecordRow);
395
+ }
396
+ enqueueJob(input) {
397
+ this.assertOpen();
398
+ requireIdentifier(input.id, 'job id');
399
+ requireIdentifier(input.projectId, 'projectId');
400
+ requireIdentifier(input.workspaceId, 'workspaceId');
401
+ const maxAttempts = input.maxAttempts ?? 3;
402
+ if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
403
+ throw new Error('[Planu] maxAttempts must be an integer between 1 and 100');
404
+ }
405
+ const now = new Date().toISOString();
406
+ this.database
407
+ .prepare(`
408
+ INSERT INTO jobs
409
+ (id, project_id, workspace_id, state, payload_json, max_attempts, created_at, updated_at)
410
+ VALUES (?, ?, ?, 'accepted', ?, ?, ?, ?)
411
+ `)
412
+ .run(input.id, input.projectId, input.workspaceId, stringifyJson(input.payload), maxAttempts, now, now);
413
+ }
414
+ getJob(projectId, workspaceId, id) {
415
+ this.assertOpen();
416
+ requireIdentifier(projectId, 'projectId');
417
+ requireIdentifier(workspaceId, 'workspaceId');
418
+ requireIdentifier(id, 'job id');
419
+ const row = this.database
420
+ .prepare(`SELECT * FROM jobs WHERE project_id = ? AND workspace_id = ? AND id = ?`)
421
+ .get(projectId, workspaceId, id);
422
+ return row ? mapJobRow(row) : undefined;
423
+ }
424
+ listJobs(projectId, workspaceId) {
425
+ this.assertOpen();
426
+ requireIdentifier(projectId, 'projectId');
427
+ requireIdentifier(workspaceId, 'workspaceId');
428
+ const rows = this.database
429
+ .prepare(`SELECT * FROM jobs WHERE project_id = ? AND workspace_id = ? ORDER BY created_at, id`)
430
+ .all(projectId, workspaceId);
431
+ return rows.map(mapJobRow);
432
+ }
433
+ listRunnableJobScopes() {
434
+ this.assertOpen();
435
+ return this.database
436
+ .prepare(`SELECT DISTINCT project_id AS projectId, workspace_id AS workspaceId
437
+ FROM jobs WHERE state IN ('accepted', 'checkpointed', 'running')
438
+ ORDER BY project_id, workspace_id`)
439
+ .all();
440
+ }
441
+ claimJob(input) {
442
+ this.assertOpen();
443
+ validateLeaseInput(input);
444
+ const now = input.now ?? new Date().toISOString();
445
+ const expiresAt = new Date(Date.parse(now) + input.leaseMs).toISOString();
446
+ return this.transaction(() => {
447
+ this.recoverExpiredJobs(now, input.projectId, input.workspaceId);
448
+ const result = this.database
449
+ .prepare(`UPDATE jobs
450
+ SET state = 'running', attempts = attempts + 1, fencing_token = fencing_token + 1, lease_owner = ?,
451
+ lease_expires_at = ?, updated_at = ?
452
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
453
+ AND state IN ('accepted', 'checkpointed') AND cancel_requested = 0
454
+ AND attempts < max_attempts`)
455
+ .run(input.workerId, expiresAt, now, input.projectId, input.workspaceId, input.id);
456
+ return result.changes === 1
457
+ ? this.getJob(input.projectId, input.workspaceId, input.id)
458
+ : undefined;
459
+ });
460
+ }
461
+ renewJobLease(input) {
462
+ this.assertOpen();
463
+ validateLeaseInput(input);
464
+ const now = input.now ?? new Date().toISOString();
465
+ const expiresAt = new Date(Date.parse(now) + input.leaseMs).toISOString();
466
+ const result = this.database
467
+ .prepare(`UPDATE jobs SET lease_expires_at = ?, updated_at = ?
468
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
469
+ AND state = 'running' AND lease_owner = ? AND fencing_token = ? AND cancel_requested = 0
470
+ AND lease_expires_at > ?`)
471
+ .run(expiresAt, now, input.projectId, input.workspaceId, input.id, input.workerId, requireFencingToken(input), now);
472
+ return result.changes === 1;
473
+ }
474
+ recordJobProgress(input) {
475
+ this.requireLeasedJob(input);
476
+ const now = input.now ?? new Date().toISOString();
477
+ const result = this.database
478
+ .prepare(`UPDATE jobs SET checkpoint_json = ?, updated_at = ?
479
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
480
+ AND state = 'running' AND lease_owner = ? AND fencing_token = ? AND cancel_requested = 0
481
+ AND lease_expires_at > ?`)
482
+ .run(stringifyJson(input.checkpoint), now, input.projectId, input.workspaceId, input.id, input.workerId, requireFencingToken(input), now);
483
+ if (result.changes !== 1) {
484
+ throw new Error('[Planu] Job lease was lost before checkpoint');
485
+ }
486
+ return requireJobRecord(this.getJob(input.projectId, input.workspaceId, input.id), input.id);
487
+ }
488
+ checkpointJob(input) {
489
+ return this.finishLeasedUpdate(input, 'checkpointed', {
490
+ checkpointJson: stringifyJson(input.checkpoint),
491
+ });
492
+ }
493
+ completeJob(input) {
494
+ return this.finishLeasedUpdate(input, 'completed', {
495
+ resultJson: stringifyJson(input.result),
496
+ });
497
+ }
498
+ failJob(input) {
499
+ if (!input.error.trim() || input.error.length > 4_096) {
500
+ throw new Error('[Planu] job error must contain between 1 and 4096 characters');
501
+ }
502
+ const current = this.requireLeasedJob(input);
503
+ const state = current.attempts >= current.maxAttempts ? 'dead-letter' : 'failed';
504
+ return this.finishLeasedUpdate(input, state, { errorMessage: input.error });
505
+ }
506
+ retryJob(projectId, workspaceId, id) {
507
+ this.assertOpen();
508
+ requireIdentifier(projectId, 'projectId');
509
+ requireIdentifier(workspaceId, 'workspaceId');
510
+ requireIdentifier(id, 'job id');
511
+ const now = new Date().toISOString();
512
+ const result = this.database
513
+ .prepare(`UPDATE jobs SET state = 'accepted', error_message = NULL, updated_at = ?
514
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
515
+ AND state = 'failed' AND cancel_requested = 0 AND attempts < max_attempts`)
516
+ .run(now, projectId, workspaceId, id);
517
+ if (result.changes !== 1) {
518
+ throw new Error('[Planu] Job is not retryable');
519
+ }
520
+ return requireJobRecord(this.getJob(projectId, workspaceId, id), id);
521
+ }
522
+ restartJob(projectId, workspaceId, id) {
523
+ this.assertOpen();
524
+ requireIdentifier(projectId, 'projectId');
525
+ requireIdentifier(workspaceId, 'workspaceId');
526
+ requireIdentifier(id, 'job id');
527
+ const now = new Date().toISOString();
528
+ this.transaction(() => {
529
+ const current = this.getJob(projectId, workspaceId, id);
530
+ if (!current ||
531
+ !['accepted', 'checkpointed', 'failed', 'dead-letter', 'cancelled', 'completed'].includes(current.state)) {
532
+ throw new Error('[Planu] Job is not restartable');
533
+ }
534
+ this.archiveCheckpointBeforeRestart(projectId, workspaceId, id, now);
535
+ const result = this.database
536
+ .prepare(`UPDATE jobs SET state = 'accepted', attempts = 0, cancel_requested = 0,
537
+ checkpoint_json = NULL, result_json = NULL, error_message = NULL,
538
+ lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
539
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
540
+ AND state IN ('accepted', 'checkpointed', 'failed', 'dead-letter', 'cancelled', 'completed')`)
541
+ .run(now, projectId, workspaceId, id);
542
+ if (result.changes !== 1) {
543
+ throw new Error('[Planu] Job is not restartable');
544
+ }
545
+ });
546
+ return requireJobRecord(this.getJob(projectId, workspaceId, id), id);
547
+ }
548
+ restartJobWithPayload(projectId, workspaceId, id, payload) {
549
+ this.assertOpen();
550
+ requireIdentifier(projectId, 'projectId');
551
+ requireIdentifier(workspaceId, 'workspaceId');
552
+ requireIdentifier(id, 'job id');
553
+ const now = new Date().toISOString();
554
+ this.transaction(() => {
555
+ const current = this.getJob(projectId, workspaceId, id);
556
+ if (!current ||
557
+ !['failed', 'dead-letter', 'cancelled', 'completed'].includes(current.state)) {
558
+ throw new Error('[Planu] Job payload is not replaceable while work is active');
559
+ }
560
+ this.archiveCheckpointBeforeRestart(projectId, workspaceId, id, now);
561
+ const result = this.database
562
+ .prepare(`UPDATE jobs SET state = 'accepted', attempts = 0, cancel_requested = 0,
563
+ payload_json = ?, checkpoint_json = NULL, result_json = NULL, error_message = NULL,
564
+ lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
565
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
566
+ AND state IN ('failed', 'dead-letter', 'cancelled', 'completed')`)
567
+ .run(stringifyJson(payload), now, projectId, workspaceId, id);
568
+ if (result.changes !== 1) {
569
+ throw new Error('[Planu] Job payload is not replaceable while work is active');
570
+ }
571
+ });
572
+ return requireJobRecord(this.getJob(projectId, workspaceId, id), id);
573
+ }
574
+ cancelJob(projectId, workspaceId, id) {
575
+ this.assertOpen();
576
+ requireIdentifier(projectId, 'projectId');
577
+ requireIdentifier(workspaceId, 'workspaceId');
578
+ requireIdentifier(id, 'job id');
579
+ const current = this.getJob(projectId, workspaceId, id);
580
+ if (!current) {
581
+ throw new Error(`[Planu] Job ${id} was not found`);
582
+ }
583
+ if (current.state === 'completed' ||
584
+ current.state === 'cancelled' ||
585
+ current.state === 'dead-letter') {
586
+ return current;
587
+ }
588
+ const now = new Date().toISOString();
589
+ this.database
590
+ .prepare(`UPDATE jobs SET state = 'cancelled', cancel_requested = 1,
591
+ lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
592
+ WHERE project_id = ? AND workspace_id = ? AND id = ?`)
593
+ .run(now, projectId, workspaceId, id);
594
+ return requireJobRecord(this.getJob(projectId, workspaceId, id), id);
595
+ }
596
+ archiveCheckpointBeforeRestart(projectId, workspaceId, id, restartedAt) {
597
+ const current = this.getJob(projectId, workspaceId, id);
598
+ if (current?.checkpoint === undefined) {
599
+ return;
600
+ }
601
+ this.putRecord({
602
+ projectId,
603
+ workspaceId,
604
+ kind: 'audit-state',
605
+ key: `job-attempt:${id.slice(0, 64)}:${process.hrtime.bigint().toString()}`,
606
+ value: {
607
+ operationId: id,
608
+ attempts: current.attempts,
609
+ previousState: current.state,
610
+ checkpoint: current.checkpoint,
611
+ restartedAt,
612
+ },
613
+ });
614
+ }
615
+ recoverExpiredJobs(now = new Date().toISOString(), projectId, workspaceId) {
616
+ this.assertOpen();
617
+ const scope = projectId && workspaceId ? ' AND project_id = ? AND workspace_id = ?' : '';
618
+ if ((projectId && !workspaceId) || (!projectId && workspaceId)) {
619
+ throw new Error('[Planu] projectId and workspaceId must be provided together');
620
+ }
621
+ if (projectId && workspaceId) {
622
+ requireIdentifier(projectId, 'projectId');
623
+ requireIdentifier(workspaceId, 'workspaceId');
624
+ }
625
+ const statement = this.database.prepare(`UPDATE jobs
626
+ SET state = CASE
627
+ WHEN cancel_requested = 1 THEN 'cancelled'
628
+ WHEN attempts >= max_attempts THEN 'dead-letter'
629
+ ELSE 'checkpointed'
630
+ END,
631
+ error_message = CASE WHEN attempts >= max_attempts THEN 'Lease expired after maximum attempts' ELSE error_message END,
632
+ lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
633
+ WHERE state = 'running' AND lease_expires_at <= ?${scope}`);
634
+ const result = projectId && workspaceId
635
+ ? statement.run(now, now, projectId, workspaceId)
636
+ : statement.run(now, now);
637
+ return Number(result.changes);
638
+ }
639
+ requireLeasedJob(input) {
640
+ validateLeaseInput(input);
641
+ const current = this.getJob(input.projectId, input.workspaceId, input.id);
642
+ if (current?.state !== 'running' ||
643
+ current.leaseOwner !== input.workerId ||
644
+ current.fencingToken !== requireFencingToken(input)) {
645
+ throw new Error('[Planu] Job lease is stale or not owned by this worker');
646
+ }
647
+ const now = input.now ?? new Date().toISOString();
648
+ if (!current.leaseExpiresAt || current.leaseExpiresAt <= now) {
649
+ throw new Error('[Planu] Job lease has expired');
650
+ }
651
+ return current;
652
+ }
653
+ finishLeasedUpdate(input, state, values) {
654
+ this.requireLeasedJob(input);
655
+ const now = input.now ?? new Date().toISOString();
656
+ const result = this.database
657
+ .prepare(`UPDATE jobs SET state = ?, checkpoint_json = COALESCE(?, checkpoint_json),
658
+ result_json = COALESCE(?, result_json), error_message = ?,
659
+ lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
660
+ WHERE project_id = ? AND workspace_id = ? AND id = ?
661
+ AND state = 'running' AND lease_owner = ? AND fencing_token = ?
662
+ AND lease_expires_at > ? AND cancel_requested = 0`)
663
+ .run(state, values.checkpointJson ?? null, values.resultJson ?? null, values.errorMessage ?? null, now, input.projectId, input.workspaceId, input.id, input.workerId, requireFencingToken(input), now);
664
+ if (result.changes !== 1) {
665
+ throw new Error('[Planu] Job lease was lost before commit');
666
+ }
667
+ return requireJobRecord(this.getJob(input.projectId, input.workspaceId, input.id), input.id);
668
+ }
669
+ enqueueOutbox(input) {
670
+ this.assertOpen();
671
+ requireIdentifier(input.projectId, 'projectId');
672
+ requireIdentifier(input.workspaceId, 'workspaceId');
673
+ requireIdentifier(input.topic, 'outbox topic');
674
+ const result = this.database
675
+ .prepare('INSERT INTO outbox (project_id, workspace_id, topic, payload_json, created_at) VALUES (?, ?, ?, ?, ?)')
676
+ .run(input.projectId, input.workspaceId, input.topic, stringifyJson(input.payload), new Date().toISOString());
677
+ return Number(result.lastInsertRowid);
678
+ }
679
+ listOutbox(projectId, workspaceId, options = {}) {
680
+ this.assertOpen();
681
+ requireIdentifier(projectId, 'projectId');
682
+ requireIdentifier(workspaceId, 'workspaceId');
683
+ const limit = options.limit ?? 100;
684
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1_000) {
685
+ throw new Error('[Planu] outbox limit must be an integer between 1 and 1000');
686
+ }
687
+ const pendingClause = options.pendingOnly === false ? '' : ' AND delivered_at IS NULL';
688
+ const topics = options.topics ?? [];
689
+ for (const topic of topics) {
690
+ requireIdentifier(topic, 'outbox topic');
691
+ }
692
+ const topicsClause = topics.length > 0 ? ` AND topic IN (${topics.map(() => '?').join(', ')})` : '';
693
+ const rows = this.database
694
+ .prepare(`SELECT id, project_id, workspace_id, topic, payload_json, created_at, delivered_at
695
+ FROM outbox
696
+ WHERE project_id = ? AND workspace_id = ?${pendingClause}${topicsClause}
697
+ ORDER BY id LIMIT ?`)
698
+ .all(projectId, workspaceId, ...topics, limit);
699
+ return rows.map(mapOutboxRow);
700
+ }
701
+ listPendingOutboxScopes() {
702
+ this.assertOpen();
703
+ return this.database
704
+ .prepare(`SELECT DISTINCT project_id AS projectId, workspace_id AS workspaceId
705
+ FROM outbox WHERE delivered_at IS NULL ORDER BY project_id, workspace_id`)
706
+ .all();
707
+ }
708
+ markOutboxDelivered(projectId, workspaceId, id, deliveredAt = new Date().toISOString()) {
709
+ this.assertOpen();
710
+ requireIdentifier(projectId, 'projectId');
711
+ requireIdentifier(workspaceId, 'workspaceId');
712
+ if (!Number.isSafeInteger(id) || id < 1) {
713
+ throw new Error('[Planu] outbox id must be a positive integer');
714
+ }
715
+ if (!Number.isFinite(Date.parse(deliveredAt))) {
716
+ throw new Error('[Planu] deliveredAt must be a valid ISO date');
717
+ }
718
+ const result = this.database
719
+ .prepare(`UPDATE outbox SET delivered_at = ?
720
+ WHERE id = ? AND project_id = ? AND workspace_id = ? AND delivered_at IS NULL`)
721
+ .run(deliveredAt, id, projectId, workspaceId);
722
+ return result.changes === 1;
723
+ }
724
+ appendAudit(input) {
725
+ this.assertOpen();
726
+ requireIdentifier(input.projectId, 'projectId');
727
+ requireIdentifier(input.workspaceId, 'workspaceId');
728
+ requireIdentifier(input.eventType, 'event type');
729
+ const result = this.database
730
+ .prepare(`
731
+ INSERT INTO audit_log (project_id, workspace_id, event_type, payload_json, created_at)
732
+ VALUES (?, ?, ?, ?, ?)
733
+ `)
734
+ .run(input.projectId, input.workspaceId, input.eventType, stringifyJson(input.payload), new Date().toISOString());
735
+ return Number(result.lastInsertRowid);
736
+ }
737
+ listAudit(projectId, workspaceId, options = {}) {
738
+ this.assertOpen();
739
+ requireIdentifier(projectId, 'projectId');
740
+ requireIdentifier(workspaceId, 'workspaceId');
741
+ const afterId = options.afterId ?? 0;
742
+ const limit = options.limit ?? 100;
743
+ if (!Number.isSafeInteger(afterId) || afterId < 0) {
744
+ throw new Error('[Planu] audit afterId must be a non-negative integer');
745
+ }
746
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1_000) {
747
+ throw new Error('[Planu] audit limit must be an integer between 1 and 1000');
748
+ }
749
+ const rows = this.database
750
+ .prepare(`SELECT id, project_id, workspace_id, event_type, payload_json, created_at
751
+ FROM audit_log
752
+ WHERE project_id = ? AND workspace_id = ? AND id > ?
753
+ ORDER BY id LIMIT ?`)
754
+ .all(projectId, workspaceId, afterId, limit);
755
+ return rows.map(mapAuditRow);
756
+ }
757
+ /** Recover every authoritative mutable family for one isolated workspace. */
758
+ recoverWorkspace(projectId, workspaceId) {
759
+ this.assertOpen();
760
+ requireIdentifier(projectId, 'projectId');
761
+ requireIdentifier(workspaceId, 'workspaceId');
762
+ this.recoverExpiredJobs(new Date().toISOString(), projectId, workspaceId);
763
+ return this.snapshot(() => {
764
+ const outbox = this.database
765
+ .prepare(`SELECT id, project_id, workspace_id, topic, payload_json, created_at, delivered_at
766
+ FROM outbox WHERE project_id = ? AND workspace_id = ? ORDER BY id`)
767
+ .all(projectId, workspaceId);
768
+ const audit = this.database
769
+ .prepare(`SELECT id, project_id, workspace_id, event_type, payload_json, created_at
770
+ FROM audit_log WHERE project_id = ? AND workspace_id = ? ORDER BY id`)
771
+ .all(projectId, workspaceId);
772
+ return {
773
+ schemaVersion: CURRENT_SCHEMA_VERSION,
774
+ projectId,
775
+ workspaceId,
776
+ records: this.listRecords(projectId, workspaceId),
777
+ jobs: this.listJobs(projectId, workspaceId),
778
+ outbox: outbox.map(mapOutboxRow),
779
+ audit: audit.map(mapAuditRow),
780
+ };
781
+ });
782
+ }
783
+ exportProject(projectId) {
784
+ this.assertOpen();
785
+ requireIdentifier(projectId, 'projectId');
786
+ return this.snapshot(() => this.exportProjectSnapshot(projectId));
787
+ }
788
+ exportProjectSnapshot(projectId) {
789
+ const records = this.database
790
+ .prepare('SELECT project_id, workspace_id, kind, record_key, value_json, source_digest, updated_at FROM runtime_records WHERE project_id = ? ORDER BY workspace_id, kind, record_key')
791
+ .all(projectId);
792
+ const mapRows = (rows) => rows.map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [
793
+ key,
794
+ key.endsWith('_json') && value !== null ? parseJson(value) : value,
795
+ ])));
796
+ const mappedRecords = records.map((row) => ({
797
+ projectId: String(row.project_id),
798
+ workspaceId: String(row.workspace_id),
799
+ kind: row.kind,
800
+ key: String(row.record_key),
801
+ value: parseJson(row.value_json),
802
+ sourceDigest: typeof row.source_digest === 'string' ? row.source_digest : undefined,
803
+ updatedAt: String(row.updated_at),
804
+ }));
805
+ const jobs = this.database
806
+ .prepare('SELECT * FROM jobs WHERE project_id = ? ORDER BY workspace_id, id')
807
+ .all(projectId);
808
+ const outbox = this.database
809
+ .prepare('SELECT * FROM outbox WHERE project_id = ? ORDER BY workspace_id, id')
810
+ .all(projectId);
811
+ const audit = this.database
812
+ .prepare('SELECT * FROM audit_log WHERE project_id = ? ORDER BY workspace_id, id')
813
+ .all(projectId);
814
+ return {
815
+ schemaVersion: 1,
816
+ projectId,
817
+ records: mappedRecords,
818
+ jobs: mapRows(jobs),
819
+ outbox: mapRows(outbox),
820
+ audit: mapRows(audit),
821
+ };
822
+ }
823
+ // eslint-disable-next-line complexity -- portable job validation is intentionally explicit
824
+ importJob(job) {
825
+ requireIdentifier(String(job.id), 'job id');
826
+ requireIdentifier(String(job.workspace_id), 'workspaceId');
827
+ const importedState = String(job.state);
828
+ const attempts = job.attempts === undefined ? 0 : Number(job.attempts);
829
+ const maxAttempts = job.max_attempts === undefined ? 3 : Number(job.max_attempts);
830
+ const fencingToken = job.fencing_token === undefined ? 0 : Number(job.fencing_token);
831
+ if (!Number.isSafeInteger(attempts) ||
832
+ attempts < 0 ||
833
+ !Number.isSafeInteger(maxAttempts) ||
834
+ maxAttempts < 1 ||
835
+ maxAttempts > 100 ||
836
+ !Number.isSafeInteger(fencingToken) ||
837
+ fencingToken < 0) {
838
+ throw new Error('[Planu] Runtime bundle contains invalid job attempts');
839
+ }
840
+ const importedRunning = importedState === 'running';
841
+ const state = importedState === 'pending'
842
+ ? 'accepted'
843
+ : importedRunning
844
+ ? job.cancel_requested === 1
845
+ ? 'cancelled'
846
+ : attempts >= maxAttempts
847
+ ? 'dead-letter'
848
+ : 'checkpointed'
849
+ : importedState;
850
+ if (!DURABLE_JOB_STATES.includes(state)) {
851
+ throw new Error('[Planu] Runtime bundle contains invalid job state');
852
+ }
853
+ this.database
854
+ .prepare(`INSERT INTO jobs
855
+ (id, project_id, workspace_id, state, payload_json, checkpoint_json, result_json,
856
+ error_message, attempts, max_attempts, cancel_requested, lease_owner,
857
+ lease_expires_at, fencing_token, created_at, updated_at)
858
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
859
+ ON CONFLICT(project_id, workspace_id, id) DO UPDATE SET
860
+ state = excluded.state, payload_json = excluded.payload_json,
861
+ checkpoint_json = excluded.checkpoint_json, result_json = excluded.result_json,
862
+ error_message = excluded.error_message, attempts = excluded.attempts,
863
+ max_attempts = excluded.max_attempts, cancel_requested = excluded.cancel_requested,
864
+ lease_owner = excluded.lease_owner, lease_expires_at = excluded.lease_expires_at,
865
+ fencing_token = MAX(jobs.fencing_token, excluded.fencing_token),
866
+ updated_at = excluded.updated_at
867
+ WHERE jobs.state != 'running' AND excluded.updated_at >= jobs.updated_at
868
+ AND NOT (
869
+ jobs.state IN ('cancelled', 'failed', 'dead-letter', 'completed')
870
+ AND excluded.state IN ('accepted', 'running', 'checkpointed')
871
+ )`)
872
+ .run(String(job.id), String(job.project_id), String(job.workspace_id), state, stringifyJson(job.payload_json), job.checkpoint_json === null || job.checkpoint_json === undefined
873
+ ? null
874
+ : stringifyJson(job.checkpoint_json), job.result_json === null || job.result_json === undefined
875
+ ? null
876
+ : stringifyJson(job.result_json), typeof job.error_message === 'string' ? job.error_message : null, attempts, maxAttempts, job.cancel_requested === 1 ? 1 : 0, !importedRunning && typeof job.lease_owner === 'string' ? job.lease_owner : null, !importedRunning && typeof job.lease_expires_at === 'string' ? job.lease_expires_at : null, fencingToken, String(job.created_at), String(job.updated_at));
877
+ }
878
+ assertLedgerIdAvailable(table, id, projectId, workspaceId) {
879
+ const existing = this.database
880
+ .prepare(`SELECT project_id, workspace_id FROM ${table} WHERE id = ?`)
881
+ .get(id);
882
+ if (existing && (existing.project_id !== projectId || existing.workspace_id !== workspaceId)) {
883
+ throw new Error(`[Planu] Runtime bundle ${table} id collides with another project`);
884
+ }
885
+ }
886
+ importOutbox(item, projectId) {
887
+ const id = Number(item.id);
888
+ if (!Number.isSafeInteger(id) || id < 1) {
889
+ throw new Error('[Planu] Runtime bundle contains invalid outbox id');
890
+ }
891
+ const workspaceId = String(item.workspace_id);
892
+ requireIdentifier(workspaceId, 'workspaceId');
893
+ requireIdentifier(String(item.topic), 'outbox topic');
894
+ this.assertLedgerIdAvailable('outbox', id, projectId, workspaceId);
895
+ this.database
896
+ .prepare(`INSERT INTO outbox
897
+ (id, project_id, workspace_id, topic, payload_json, created_at, delivered_at)
898
+ VALUES (?, ?, ?, ?, ?, ?, ?)
899
+ ON CONFLICT(id) DO UPDATE SET
900
+ topic = excluded.topic,
901
+ payload_json = excluded.payload_json,
902
+ delivered_at = excluded.delivered_at
903
+ WHERE outbox.project_id = excluded.project_id
904
+ AND outbox.workspace_id = excluded.workspace_id`)
905
+ .run(id, projectId, workspaceId, String(item.topic), stringifyJson(item.payload_json), String(item.created_at), typeof item.delivered_at === 'string' ? item.delivered_at : null);
906
+ }
907
+ importAudit(event, projectId) {
908
+ const id = Number(event.id);
909
+ if (!Number.isSafeInteger(id) || id < 1) {
910
+ throw new Error('[Planu] Runtime bundle contains invalid audit id');
911
+ }
912
+ const workspaceId = String(event.workspace_id);
913
+ requireIdentifier(workspaceId, 'workspaceId');
914
+ requireIdentifier(String(event.event_type), 'event type');
915
+ this.assertLedgerIdAvailable('audit_log', id, projectId, workspaceId);
916
+ this.database
917
+ .prepare(`INSERT INTO audit_log
918
+ (id, project_id, workspace_id, event_type, payload_json, created_at)
919
+ VALUES (?, ?, ?, ?, ?, ?)
920
+ ON CONFLICT(id) DO UPDATE SET
921
+ event_type = excluded.event_type,
922
+ payload_json = excluded.payload_json
923
+ WHERE audit_log.project_id = excluded.project_id
924
+ AND audit_log.workspace_id = excluded.workspace_id`)
925
+ .run(id, projectId, workspaceId, String(event.event_type), stringifyJson(event.payload_json), String(event.created_at));
926
+ }
927
+ importProject(bundle) {
928
+ requireIdentifier(bundle.projectId, 'projectId');
929
+ const foreignProject = [
930
+ ...bundle.records.map((item) => item.projectId),
931
+ ...bundle.jobs.map((item) => String(item.project_id)),
932
+ ...bundle.outbox.map((item) => String(item.project_id)),
933
+ ...bundle.audit.map((item) => String(item.project_id)),
934
+ ].find((projectId) => projectId !== bundle.projectId);
935
+ if (foreignProject) {
936
+ throw new Error('[Planu] Runtime bundle contains cross-project data');
937
+ }
938
+ this.transaction(() => {
939
+ for (const record of bundle.records) {
940
+ this.putRecord(record);
941
+ }
942
+ for (const job of bundle.jobs) {
943
+ this.importJob(job);
944
+ }
945
+ for (const item of bundle.outbox) {
946
+ this.importOutbox(item, bundle.projectId);
947
+ }
948
+ for (const event of bundle.audit) {
949
+ this.importAudit(event, bundle.projectId);
950
+ }
951
+ });
952
+ }
953
+ close() {
954
+ if (this.closed) {
955
+ return;
956
+ }
957
+ try {
958
+ this.database.close();
959
+ }
960
+ finally {
961
+ this.releaseConnection();
962
+ this.closed = true;
963
+ }
964
+ }
965
+ [Symbol.dispose]() {
966
+ this.close();
967
+ }
968
+ }
969
+ let inMemoryConnectionSequence = 0;
970
+ function randomConnectionKey() {
971
+ inMemoryConnectionSequence += 1;
972
+ return String(inMemoryConnectionSequence);
973
+ }
974
+ function validateLeaseInput(input) {
975
+ requireIdentifier(input.id, 'job id');
976
+ requireIdentifier(input.projectId, 'projectId');
977
+ requireIdentifier(input.workspaceId, 'workspaceId');
978
+ requireIdentifier(input.workerId, 'worker id');
979
+ if (!Number.isSafeInteger(input.leaseMs) || input.leaseMs < 100 || input.leaseMs > 300_000) {
980
+ throw new Error('[Planu] leaseMs must be an integer between 100 and 300000');
981
+ }
982
+ if (input.now !== undefined && !Number.isFinite(Date.parse(input.now))) {
983
+ throw new Error('[Planu] lease timestamp must be a valid ISO date');
984
+ }
985
+ }
986
+ function requireFencingToken(input) {
987
+ const token = input.fencingToken;
988
+ if (token === undefined || !Number.isSafeInteger(token) || token < 1) {
989
+ throw new Error('[Planu] Job lease requires a positive fencing token');
990
+ }
991
+ return token;
992
+ }
993
+ function optionalJson(value) {
994
+ return typeof value === 'string' ? parseJson(value) : undefined;
995
+ }
996
+ function mapRuntimeRecordRow(row) {
997
+ return {
998
+ projectId: row.project_id,
999
+ workspaceId: row.workspace_id,
1000
+ kind: row.kind,
1001
+ key: row.record_key,
1002
+ value: parseJson(row.value_json),
1003
+ sourceDigest: row.source_digest ?? undefined,
1004
+ updatedAt: row.updated_at,
1005
+ };
1006
+ }
1007
+ function mapOutboxRow(row) {
1008
+ return {
1009
+ id: Number(row.id),
1010
+ projectId: String(row.project_id),
1011
+ workspaceId: String(row.workspace_id),
1012
+ topic: String(row.topic),
1013
+ payload: parseJson(row.payload_json),
1014
+ createdAt: String(row.created_at),
1015
+ deliveredAt: typeof row.delivered_at === 'string' ? row.delivered_at : undefined,
1016
+ };
1017
+ }
1018
+ function mapAuditRow(row) {
1019
+ return {
1020
+ id: Number(row.id),
1021
+ projectId: String(row.project_id),
1022
+ workspaceId: String(row.workspace_id),
1023
+ eventType: String(row.event_type),
1024
+ payload: parseJson(row.payload_json),
1025
+ createdAt: String(row.created_at),
1026
+ };
1027
+ }
1028
+ function requireJobRecord(job, id) {
1029
+ if (!job) {
1030
+ throw new Error(`[Planu] Job ${id} disappeared after a committed update`);
1031
+ }
1032
+ return job;
1033
+ }
1034
+ function mapJobRow(row) {
1035
+ return {
1036
+ id: String(row.id),
1037
+ projectId: String(row.project_id),
1038
+ workspaceId: String(row.workspace_id),
1039
+ state: row.state,
1040
+ payload: parseJson(row.payload_json),
1041
+ checkpoint: optionalJson(row.checkpoint_json),
1042
+ result: optionalJson(row.result_json),
1043
+ error: typeof row.error_message === 'string' ? row.error_message : undefined,
1044
+ attempts: Number(row.attempts),
1045
+ maxAttempts: Number(row.max_attempts),
1046
+ cancelRequested: row.cancel_requested === 1,
1047
+ leaseOwner: typeof row.lease_owner === 'string' ? row.lease_owner : undefined,
1048
+ fencingToken: Number(row.fencing_token),
1049
+ leaseExpiresAt: typeof row.lease_expires_at === 'string' ? row.lease_expires_at : undefined,
1050
+ createdAt: String(row.created_at),
1051
+ updatedAt: String(row.updated_at),
1052
+ };
1053
+ }
1054
+ //# sourceMappingURL=runtime-db.js.map