@moretea-labs/forge 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (685) hide show
  1. package/AGENTS.md +58 -62
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +42 -62
  4. package/README.md +2 -2
  5. package/SKILL.md +0 -1
  6. package/THIRD_PARTY_NOTICES.md +5 -2
  7. package/adapters/browser/index.ts +2 -0
  8. package/adapters/chatgpt/browser-delivery-host.ts +71 -0
  9. package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
  10. package/adapters/chatgpt/controller-binding-store.ts +55 -0
  11. package/adapters/chatgpt/controller-host.ts +119 -0
  12. package/adapters/chatgpt/controller-round-host.ts +78 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
  14. package/adapters/chatgpt/provider-delivery.ts +113 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/desktop-operator-contract.ts +7 -0
  18. package/adapters/computer/desktop-operator-discovery.ts +201 -0
  19. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  20. package/adapters/computer/desktop-operator-provider.ts +312 -0
  21. package/adapters/computer/index.ts +1 -0
  22. package/adapters/computer/interaction-target-authority.ts +944 -0
  23. package/adapters/controller-process/binding-store.ts +34 -0
  24. package/adapters/controller-process/controller-host.ts +26 -0
  25. package/adapters/mcp/access-mode.ts +132 -0
  26. package/adapters/mcp/audit.ts +32 -0
  27. package/adapters/mcp/auth.ts +407 -0
  28. package/adapters/mcp/controller-round-compatibility.ts +184 -0
  29. package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
  30. package/adapters/mcp/instructions.ts +36 -0
  31. package/adapters/mcp/legacy-context.ts +18 -0
  32. package/adapters/mcp/multi-repository.ts +437 -0
  33. package/adapters/mcp/oauth.ts +391 -0
  34. package/adapters/mcp/paths.ts +140 -0
  35. package/adapters/mcp/policy.ts +232 -0
  36. package/adapters/mcp/proxy-env.ts +62 -0
  37. package/adapters/mcp/readiness-projection.ts +24 -0
  38. package/adapters/mcp/redaction.ts +21 -0
  39. package/adapters/mcp/repo.ts +24 -0
  40. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  41. package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
  42. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
  43. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
  44. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  45. package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
  46. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  47. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
  48. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  49. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  50. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  51. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  52. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
  53. package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
  54. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
  55. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
  56. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  57. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  58. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
  59. package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
  60. package/adapters/mcp/runtime-gateway/router.ts +728 -0
  61. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
  62. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
  63. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  64. package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
  65. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
  66. package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
  67. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
  68. package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
  69. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
  70. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  71. package/adapters/mcp/server.ts +1048 -0
  72. package/adapters/mcp/setup.ts +791 -0
  73. package/adapters/mcp/tool-capability-cache.ts +87 -0
  74. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  75. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
  76. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
  77. package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
  78. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  79. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  80. package/adapters/mcp/toolset-names.ts +74 -0
  81. package/adapters/mcp/toolset-selection.ts +41 -0
  82. package/adapters/mcp/toolset.ts +336 -0
  83. package/adapters/mcp/transports/http.ts +1456 -0
  84. package/adapters/mcp/transports/session-registry.ts +513 -0
  85. package/adapters/mcp/transports/stdio.ts +8 -0
  86. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
  87. package/adapters/mcp/types.ts +41 -0
  88. package/assets/AGENTS.md +2 -16
  89. package/assets/CLAUDE.md +2 -16
  90. package/assets/hooks/AGENTS.md +3 -14
  91. package/assets/hooks/CLAUDE.md +3 -14
  92. package/assets/hooks/hook-input.sh +9 -2
  93. package/assets/hooks/lib/workflow-state.sh +28 -10
  94. package/assets/hooks/prompt-guard.sh +63 -37
  95. package/assets/hooks/security-sentinel.sh +2 -1
  96. package/assets/hooks/session-start-context.sh +7 -5
  97. package/assets/hooks/stop-orchestrator.sh +13 -3
  98. package/assets/initializer-question-pack.v4.json +11 -7
  99. package/assets/partials/05-workflow.partial.md +18 -73
  100. package/assets/partials/07-footer.partial.md +1 -1
  101. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  102. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  103. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  104. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  105. package/assets/plan-map.json +11 -11
  106. package/assets/plugin-registry.v1.json +9 -3
  107. package/assets/plugin-trust-policy.v1.json +6 -0
  108. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  109. package/assets/recovery/forge-wsl-rescue +271 -0
  110. package/assets/reference-configs/development-protocol.md +11 -15
  111. package/assets/reference-configs/document-generation.md +1 -1
  112. package/assets/reference-configs/global-working-rules.md +9 -1
  113. package/assets/reference-configs/harness-overview.md +22 -112
  114. package/assets/reference-configs/hook-operations.md +1 -1
  115. package/assets/reference-configs/sprint-contracts.md +2 -2
  116. package/assets/skill-version.json +2 -2
  117. package/assets/templates/helpers/architecture-event.ts +3 -34
  118. package/assets/templates/helpers/architecture-queue.sh +61 -10
  119. package/assets/templates/helpers/archive-workflow.sh +90 -180
  120. package/assets/templates/helpers/capability-config.ts +8 -11
  121. package/assets/templates/helpers/capability-resolver.ts +37 -19
  122. package/assets/templates/helpers/capture-plan.sh +2 -2
  123. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  124. package/assets/templates/helpers/check-task-sync.sh +82 -11
  125. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  126. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  127. package/assets/templates/helpers/contract-worktree.sh +26 -24
  128. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  129. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  130. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  131. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  132. package/assets/templates/helpers/new-plan.sh +2 -2
  133. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  134. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  135. package/assets/templates/helpers/switch-plan.sh +1 -1
  136. package/assets/templates/helpers/workflow-contract.ts +1 -0
  137. package/assets/workflow-contract.v1.json +63 -171
  138. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  139. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  140. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  141. package/docs/architecture/CURRENT.md +148 -13
  142. package/docs/architecture/EVOLUTION.md +13 -0
  143. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  144. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  145. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  146. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  147. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
  148. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
  149. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  150. package/docs/architecture/domains/verification.md +7 -0
  151. package/docs/architecture/domains/workflow-engine.md +1 -1
  152. package/docs/architecture/index.md +3 -3
  153. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  154. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  155. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  156. package/docs/forge-plugin-management.md +20 -4
  157. package/docs/forge-runtime-self-healing-loop.md +2 -0
  158. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  159. package/docs/operations/controller-browser-plugin.md +3 -1
  160. package/docs/operations/homebrew.md +2 -2
  161. package/docs/operations/platform-support.md +16 -8
  162. package/docs/operations/platform-support.zh-CN.md +2 -2
  163. package/docs/operations/standalone-disaster-recovery.md +26 -1
  164. package/docs/operations/uu-remote-rescue.md +41 -0
  165. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  166. package/docs/public-usage-guide.md +1 -1
  167. package/docs/public-usage-guide.zh-CN.md +1 -1
  168. package/docs/tutorials/01-install-and-start.md +1 -1
  169. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  170. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  171. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  172. package/docs/wiki/Implementation.md +1 -1
  173. package/package.json +23 -14
  174. package/packages/kernel/controller/api/index.ts +2 -0
  175. package/packages/kernel/controller/application/controller-round-service.ts +29 -0
  176. package/packages/kernel/controller/application/controller-service.ts +30 -0
  177. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
  178. package/packages/kernel/controller/domain/controller-round.ts +107 -0
  179. package/packages/kernel/controller/domain/execution-quality.ts +133 -0
  180. package/packages/kernel/controller/domain/types.ts +56 -0
  181. package/packages/kernel/controller/index.ts +1 -0
  182. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  183. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
  184. package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
  185. package/packages/kernel/controller/ports/controller-host.ts +16 -0
  186. package/packages/kernel/goal/api/index.ts +1 -0
  187. package/packages/kernel/goal/domain/types.ts +40 -0
  188. package/packages/kernel/goal/index.ts +1 -0
  189. package/packages/kernel/identity/api/index.ts +5 -0
  190. package/packages/kernel/identity/application/identity-service.ts +67 -0
  191. package/packages/kernel/identity/domain/scope.ts +63 -0
  192. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  193. package/packages/kernel/identity/domain/types.ts +44 -0
  194. package/packages/kernel/identity/domain/workspace.ts +56 -0
  195. package/packages/kernel/identity/index.ts +1 -0
  196. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  197. package/packages/kernel/memory/api/index.ts +5 -0
  198. package/packages/kernel/memory/application/experience-service.ts +118 -0
  199. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  200. package/packages/kernel/memory/domain/experience.ts +103 -0
  201. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  202. package/packages/kernel/memory/index.ts +1 -0
  203. package/packages/kernel/progression/api/index.ts +2 -0
  204. package/packages/kernel/progression/application/projector.ts +211 -0
  205. package/packages/kernel/progression/domain/types.ts +116 -0
  206. package/packages/kernel/progression/index.ts +1 -0
  207. package/packages/kernel/scheduler/api/index.ts +6 -0
  208. package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
  209. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  210. package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
  211. package/packages/kernel/scheduler/application/settlement.ts +197 -0
  212. package/packages/kernel/scheduler/domain/continuation.ts +38 -0
  213. package/packages/kernel/scheduler/domain/schedule.ts +149 -0
  214. package/packages/kernel/scheduler/index.ts +1 -0
  215. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
  216. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
  217. package/packages/kernel/work/api/index.ts +15 -0
  218. package/packages/kernel/work/application/work-service.ts +148 -0
  219. package/packages/kernel/work/domain/admission-policy.ts +34 -0
  220. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  221. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  222. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  223. package/packages/kernel/work/domain/engineering-design.ts +281 -0
  224. package/packages/kernel/work/domain/engineering-profile.ts +242 -0
  225. package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
  226. package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
  227. package/packages/kernel/work/domain/implementation-review.ts +510 -0
  228. package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
  229. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  230. package/packages/kernel/work/domain/state-machine.ts +259 -0
  231. package/packages/kernel/work/domain/types.ts +528 -0
  232. package/packages/kernel/work/index.ts +1 -0
  233. package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
  234. package/packages/kernel/work/ports/work-contract-store.ts +18 -0
  235. package/packages/plugin-runtime/browser/index.ts +3 -0
  236. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  237. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  238. package/packages/plugin-runtime/browser/session-authority.ts +61 -0
  239. package/packages/plugin-runtime/computer/index.ts +6 -0
  240. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  241. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  242. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  243. package/packages/plugin-runtime/computer/provider.ts +19 -0
  244. package/packages/plugin-runtime/computer/target-authority.ts +190 -0
  245. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  246. package/packages/plugin-runtime/external/index.ts +1 -0
  247. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
  248. package/packages/plugin-runtime/index.ts +3 -0
  249. package/packages/protocols/browser/index.ts +1 -0
  250. package/packages/protocols/browser/session.ts +98 -0
  251. package/packages/protocols/computer/contract.ts +199 -0
  252. package/packages/protocols/computer/index.ts +1 -0
  253. package/packages/protocols/failure/contract.ts +158 -0
  254. package/packages/protocols/failure/index.ts +1 -0
  255. package/packages/protocols/handoff/index.ts +1 -0
  256. package/packages/protocols/handoff/status.ts +22 -0
  257. package/packages/protocols/mcp/execution-context.ts +11 -0
  258. package/packages/protocols/mcp/index.ts +2 -0
  259. package/packages/protocols/mcp/tool-contract.ts +42 -0
  260. package/packages/workflow-runtime/api/index.ts +2 -0
  261. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  262. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  263. package/packages/workflow-runtime/index.ts +1 -0
  264. package/scripts/assemble-template.ts +1 -1
  265. package/scripts/benchmark-computer-hotpath.ts +255 -0
  266. package/scripts/benchmark-controller-round2.ts +54 -2
  267. package/scripts/benchmark-ios-agent-device.ts +3 -6
  268. package/scripts/benchmark-mcp-transport.ts +3 -4
  269. package/scripts/benchmark-operational-memory-activation.ts +235 -0
  270. package/scripts/benchmark-operational-shadow.ts +28 -0
  271. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  272. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  273. package/scripts/canary-controller-semantic-wait.ts +195 -0
  274. package/scripts/canary-global-hook.sh +2 -2
  275. package/scripts/check-mcp-compatibility.ts +173 -7
  276. package/scripts/check-platform-support.mjs +28 -2
  277. package/scripts/check-public-docs.sh +1 -1
  278. package/scripts/check-release-readiness.sh +1 -1
  279. package/scripts/check-repository-hygiene.mjs +92 -0
  280. package/scripts/check-runtime-architecture.mjs +1206 -95
  281. package/scripts/check-source-duplication.mjs +9 -1
  282. package/scripts/check-tarball-install-smoke.sh +98 -15
  283. package/scripts/controller-ngrok-rotation.sh +5 -4
  284. package/scripts/create-project-dirs.sh +10 -74
  285. package/scripts/forge-local-recovery-helper.mjs +204 -0
  286. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  287. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  288. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  289. package/scripts/hook-shim.sh +2 -2
  290. package/scripts/init-project.sh +12 -70
  291. package/scripts/install-desktop-operator-registration.ts +4 -2
  292. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  293. package/scripts/install-standalone-recovery.ts +122 -10
  294. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  295. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  296. package/scripts/lib/controller-home-env.sh +6 -3
  297. package/scripts/lib/project-init-lib.sh +39 -582
  298. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  299. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  300. package/scripts/open-source-audit-allowlist.txt +12 -1
  301. package/scripts/portability-bindings.mjs +70 -0
  302. package/scripts/run-bounded-command.mjs +55 -0
  303. package/scripts/run-bun-test-file.ts +22 -6
  304. package/scripts/run-governed-gate.ts +4 -1
  305. package/scripts/smoke-mcp-http-runtime.ts +1 -1
  306. package/scripts/smoke-runtime-recovery.ts +34 -1
  307. package/scripts/smoke-schedule-engine.ts +1 -1
  308. package/scripts/stage-runtime-release.ts +6 -1
  309. package/scripts/test-governance.ts +51 -2
  310. package/src/cli/agent-jobs/job-manager.ts +10 -1
  311. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  312. package/src/cli/chatgpt-browser/binding.ts +20 -10
  313. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  314. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  315. package/src/cli/chatgpt-browser/engine.ts +10 -11
  316. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  317. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  318. package/src/cli/chatgpt-browser/types.ts +1 -0
  319. package/src/cli/commands/bootstrap-control.ts +334 -0
  320. package/src/cli/commands/brain-assistant.ts +38 -0
  321. package/src/cli/commands/brain.ts +193 -40
  322. package/src/cli/commands/chatgpt.ts +127 -0
  323. package/src/cli/commands/computer.ts +437 -0
  324. package/src/cli/commands/doctor.ts +18 -1
  325. package/src/cli/commands/init-hook.ts +71 -61
  326. package/src/cli/commands/init.ts +3 -1
  327. package/src/cli/commands/mcp.ts +16 -10
  328. package/src/cli/commands/plugin.ts +38 -12
  329. package/src/cli/commands/recovery.ts +493 -80
  330. package/src/cli/commands/runtime.ts +9 -4
  331. package/src/cli/commands/setup-profile.ts +89 -39
  332. package/src/cli/commands/status.ts +2 -1
  333. package/src/cli/controller/check-runner.ts +487 -98
  334. package/src/cli/controller/context/types.ts +7 -1
  335. package/src/cli/controller/context-pack.ts +23 -3
  336. package/src/cli/controller/issue-store.ts +1 -1
  337. package/src/cli/controller/systemd-user.ts +110 -0
  338. package/src/cli/controller/types.ts +10 -70
  339. package/src/cli/controller/worklog.ts +67 -13
  340. package/src/cli/editing/edit-operation-contract.ts +92 -0
  341. package/src/cli/editing/edit-session.ts +289 -13
  342. package/src/cli/github/plugin.ts +17 -12
  343. package/src/cli/hook/route-registry.ts +3 -5
  344. package/src/cli/hook/runtime.ts +49 -3
  345. package/src/cli/index.ts +3 -0
  346. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  347. package/src/cli/local-bridge/console-automations.ts +2 -2
  348. package/src/cli/local-bridge/facade-api.ts +41 -14
  349. package/src/cli/local-bridge/job-store.ts +1 -1
  350. package/src/cli/local-bridge/server.ts +24 -27
  351. package/src/cli/mcp/access-mode.ts +2 -132
  352. package/src/cli/mcp/access-tools.ts +2 -387
  353. package/src/cli/mcp/audit.ts +2 -32
  354. package/src/cli/mcp/auth.ts +2 -404
  355. package/src/cli/mcp/instructions.ts +2 -34
  356. package/src/cli/mcp/legacy-context.ts +2 -16
  357. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  358. package/src/cli/mcp/multi-repository.ts +2 -436
  359. package/src/cli/mcp/oauth.ts +2 -375
  360. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  361. package/src/cli/mcp/paths.ts +2 -140
  362. package/src/cli/mcp/policy.ts +2 -232
  363. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  364. package/src/cli/mcp/proxy-env.ts +2 -62
  365. package/src/cli/mcp/readiness-projection.ts +2 -24
  366. package/src/cli/mcp/redaction.ts +2 -21
  367. package/src/cli/mcp/repo.ts +2 -24
  368. package/src/cli/mcp/repository-tools.ts +2 -1585
  369. package/src/cli/mcp/server.ts +2 -951
  370. package/src/cli/mcp/setup.ts +2 -737
  371. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  372. package/src/cli/mcp/tool-contract.ts +2 -14
  373. package/src/cli/mcp/tools.ts +2 -9
  374. package/src/cli/mcp/toolset-names.ts +2 -70
  375. package/src/cli/mcp/toolset-selection.ts +2 -41
  376. package/src/cli/mcp/toolset.ts +2 -336
  377. package/src/cli/mcp/transports/AGENTS.md +1 -10
  378. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  379. package/src/cli/mcp/transports/http.ts +2 -1414
  380. package/src/cli/mcp/transports/session-registry.ts +2 -490
  381. package/src/cli/mcp/transports/stdio.ts +2 -8
  382. package/src/cli/mcp/types.ts +2 -41
  383. package/src/cli/mcp/umbrella-tools.ts +2 -117
  384. package/src/cli/repositories/command-executor.ts +64 -18
  385. package/src/cli/repositories/command-scope.ts +117 -1
  386. package/src/cli/repositories/controller-home.ts +119 -1
  387. package/src/cli/repositories/identity.ts +13 -0
  388. package/src/cli/repositories/registry.ts +68 -49
  389. package/src/cli/repositories/repository-snapshot.ts +53 -7
  390. package/src/cli/repositories/runtime-storage.ts +27 -2
  391. package/src/cli/repositories/safe-patch.ts +34 -38
  392. package/src/cli/repositories/selected-path-actions.ts +22 -2
  393. package/src/cli/repositories/structured-git.ts +105 -25
  394. package/src/cli/runtime/helper-runner.ts +7 -0
  395. package/src/cli/runtime-invocation.ts +8 -1
  396. package/src/cli/tools/codegraph.ts +44 -25
  397. package/src/core/adoption/manifest-templates.ts +1 -1
  398. package/src/core/adoption/plan.ts +3 -26
  399. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  400. package/src/effects/process-runner.ts +8 -14
  401. package/src/runtime/context/assistant-context.ts +154 -0
  402. package/src/runtime/context/assistant-work-context.ts +147 -0
  403. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  404. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  405. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  406. package/src/runtime/context/context-closure.ts +268 -0
  407. package/src/runtime/context/context-plane.ts +211 -0
  408. package/src/runtime/context/project-engineering-contract.ts +60 -0
  409. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  410. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  411. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  412. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  413. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  414. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  415. package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
  416. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  417. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
  418. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
  419. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
  420. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
  421. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +417 -100
  422. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
  423. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  424. package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
  425. package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
  426. package/src/runtime/control-plane/execution/validation.ts +28 -3
  427. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
  428. package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
  429. package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
  430. package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
  431. package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
  432. package/src/runtime/control-plane/execution/work-handle-authority.ts +267 -16
  433. package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
  434. package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
  435. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
  436. package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
  437. package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
  438. package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
  439. package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
  440. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
  441. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
  442. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  443. package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
  444. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
  445. package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
  446. package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
  447. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  448. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
  449. package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
  450. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
  451. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
  452. package/src/runtime/control-plane/facade/index.ts +4 -2
  453. package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
  454. package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
  455. package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
  456. package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
  457. package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
  458. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
  459. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  460. package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
  461. package/src/runtime/control-plane/facade/types.ts +189 -532
  462. package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
  463. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  464. package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
  465. package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
  466. package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
  467. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
  468. package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
  469. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  470. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  471. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
  472. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  473. package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
  474. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  475. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
  476. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  477. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
  478. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
  479. package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
  480. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  481. package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
  482. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  483. package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
  484. package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
  485. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  486. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  487. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  488. package/src/runtime/control-plane/release-retention.ts +56 -16
  489. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  490. package/src/runtime/control-plane/routing/route-policy.ts +19 -0
  491. package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
  492. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  493. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
  494. package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
  495. package/src/runtime/diagnostics/incident-repair.ts +406 -0
  496. package/src/runtime/diagnostics/mcp-timing.ts +125 -6
  497. package/src/runtime/diagnostics/performance.ts +6 -14
  498. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  499. package/src/runtime/evidence/artifact-store.ts +65 -2
  500. package/src/runtime/evidence/operational-shadow.ts +38 -0
  501. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  502. package/src/runtime/evidence/sensitive-output.ts +4 -4
  503. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  504. package/src/runtime/execution/jobs/store.ts +121 -2
  505. package/src/runtime/execution/managed-workspace.ts +55 -5
  506. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  507. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  508. package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
  509. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  510. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  511. package/src/runtime/execution/process-runtime/check-result.ts +73 -2
  512. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
  513. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  514. package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
  515. package/src/runtime/execution/process-runtime/index.ts +1 -0
  516. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  517. package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
  518. package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
  519. package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
  520. package/src/runtime/execution/process-runtime/runtime.ts +132 -15
  521. package/src/runtime/execution/process-runtime/types.ts +22 -0
  522. package/src/runtime/execution/workers/executor.ts +16 -13
  523. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  524. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  525. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  526. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  527. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  528. package/src/runtime/gateway/mcp/router.ts +2 -1125
  529. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  530. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6651
  531. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  532. package/src/runtime/maintenance/cleanup.ts +38 -24
  533. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  534. package/src/runtime/platform/computer-platform.ts +14 -0
  535. package/src/runtime/platform/executable-discovery.ts +54 -0
  536. package/src/runtime/platform/fetch-http-server.ts +90 -0
  537. package/src/runtime/platform/service-manager.ts +138 -0
  538. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  539. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  540. package/src/runtime/plugins/action-application.ts +135 -0
  541. package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
  542. package/src/runtime/plugins/browser-adapter.ts +140 -100
  543. package/src/runtime/plugins/browser-automation-service.ts +15 -36
  544. package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
  545. package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
  546. package/src/runtime/plugins/browser-native-session-service.ts +1 -1
  547. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  548. package/src/runtime/plugins/browser-registration.ts +16 -0
  549. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  550. package/src/runtime/plugins/browser-runtime.ts +41 -9
  551. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  552. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  553. package/src/runtime/plugins/browser-session-store.ts +229 -36
  554. package/src/runtime/plugins/browser-session-types.ts +2 -98
  555. package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
  556. package/src/runtime/plugins/catalog-trust.ts +82 -0
  557. package/src/runtime/plugins/computer-registration.ts +839 -0
  558. package/src/runtime/plugins/config-store.ts +119 -0
  559. package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
  560. package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
  561. package/src/runtime/plugins/errors.ts +8 -1
  562. package/src/runtime/plugins/external-adapter.ts +45 -322
  563. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  564. package/src/runtime/plugins/external-registration.ts +9 -1
  565. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  566. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  567. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  568. package/src/runtime/plugins/first-party-registry.ts +3 -1
  569. package/src/runtime/plugins/github-adapter.ts +1 -2
  570. package/src/runtime/plugins/gmail-adapter.ts +6 -6
  571. package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
  572. package/src/runtime/plugins/google-shared.ts +31 -46
  573. package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
  574. package/src/runtime/plugins/ios-adapter.ts +1 -2
  575. package/src/runtime/plugins/local-system-adapter.ts +180 -0
  576. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  577. package/src/runtime/plugins/resend-adapter.ts +15 -24
  578. package/src/runtime/plugins/store.ts +493 -51
  579. package/src/runtime/plugins/types.ts +8 -1
  580. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  581. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  582. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  583. package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
  584. package/src/runtime/projections/controller-context.ts +12 -6
  585. package/src/runtime/projections/git-status-sampler.ts +73 -22
  586. package/src/runtime/projections/materialized-view.ts +14 -8
  587. package/src/runtime/recovery/capability-action-application.ts +228 -0
  588. package/src/runtime/recovery/index.ts +3 -0
  589. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  590. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  591. package/src/runtime/recovery/maintenance-executor.ts +358 -76
  592. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  593. package/src/runtime/release/identity.ts +6 -0
  594. package/src/runtime/resources/leases/store.ts +1 -1
  595. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  596. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  597. package/src/runtime/root/browser-session-composition.ts +59 -0
  598. package/src/runtime/root/computer-composition.ts +191 -0
  599. package/src/runtime/root/computer-target-composition.ts +16 -0
  600. package/src/runtime/root/computer-target-persistence.ts +39 -0
  601. package/src/runtime/root/controller-round-composition.ts +107 -0
  602. package/src/runtime/root/controller-services.ts +2 -0
  603. package/src/runtime/root/entry.ts +4 -3
  604. package/src/runtime/root/gateway-adapter.ts +3 -3
  605. package/src/runtime/root/local-bridge.ts +5 -2
  606. package/src/runtime/root/mcp-transport.ts +5 -5
  607. package/src/runtime/root/package-connector-service.ts +108 -56
  608. package/src/runtime/root/package-runtime-release.ts +86 -18
  609. package/src/runtime/root/package-runtime-service.ts +63 -86
  610. package/src/runtime/root/release-execution-canary.ts +71 -0
  611. package/src/runtime/root/release-loader.ts +68 -0
  612. package/src/runtime/root/release-manifest.ts +106 -2
  613. package/src/runtime/root/release-materialize.ts +103 -4
  614. package/src/runtime/root/release-store.ts +31 -2
  615. package/src/runtime/root/runtime.ts +240 -25
  616. package/src/runtime/root/scheduled-controller-composition.ts +51 -0
  617. package/src/runtime/root/scheduler.ts +12 -4
  618. package/src/runtime/root/service-runner.ts +1 -1
  619. package/src/runtime/root/service.ts +41 -16
  620. package/src/runtime/root/status.ts +1 -0
  621. package/src/runtime/root/types.ts +24 -1
  622. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  623. package/src/runtime/shared/process-environment.ts +19 -4
  624. package/src/runtime/shared/storage-capacity.ts +68 -0
  625. package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
  626. package/src/runtime/standalone-recovery/core.ts +984 -230
  627. package/src/runtime/standalone-recovery/entry.ts +182 -51
  628. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  629. package/src/runtime/standalone-recovery/installer.ts +212 -104
  630. package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
  631. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  632. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  633. package/src/runtime/standalone-recovery/performance.ts +135 -0
  634. package/src/runtime/standalone-recovery/release.ts +5 -0
  635. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  636. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
  637. package/src/runtime/workflow/schedules/engine.ts +191 -343
  638. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  639. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  640. package/src/runtime/workflow/schedules/store.ts +2 -607
  641. package/src/runtime/workflow/schedules/types.ts +2 -127
  642. package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
  643. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  644. package/src/runtime/workflows/runtime.ts +486 -0
  645. package/src/testing/test-governance.ts +90 -29
  646. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  647. package/docs/architecture/history/README.md +0 -7
  648. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  649. package/docs/architecture/history/global-hook-runtime.md +0 -166
  650. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  651. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  652. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  653. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  654. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  655. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  656. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  657. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  658. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  659. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  660. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  661. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  662. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  663. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  664. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  665. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  666. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  667. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  668. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  669. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  670. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  671. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  672. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  673. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  674. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  675. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  676. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  677. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  678. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  679. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  680. package/docs/architecture/requests/archive/2026/root.md +0 -68
  681. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  682. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  683. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  684. package/scripts/archive-architecture-request.sh +0 -4
  685. package/scripts/run-skill-evals.ts +0 -1022
@@ -1,20 +1,19 @@
1
1
  import { createHash, randomUUID } from 'crypto';
2
2
  import {
3
3
  chmodSync,
4
- closeSync,
5
4
  copyFileSync,
6
5
  existsSync,
7
6
  lstatSync,
8
7
  mkdirSync,
9
- openSync,
10
8
  readFileSync,
11
9
  readdirSync,
12
10
  renameSync,
13
11
  rmSync,
14
12
  writeFileSync,
15
13
  } from 'fs';
16
- import { dirname, join, resolve } from 'path';
14
+ import { dirname, join, relative, resolve } from 'path';
17
15
  import { runProcess, type ProcessRunResult } from '../../effects/process-runner';
16
+ import { resolveBunExecutable } from '../shared/process-environment';
18
17
  import {
19
18
  installLaunchAgent,
20
19
  launchAgentPath,
@@ -23,9 +22,16 @@ import {
23
22
  type SafeHandoffResult,
24
23
  } from '../../cli/controller/launch-agents';
25
24
  import { isProcessAlive } from '../shared/process-tree';
25
+ import {
26
+ installSystemdUserUnit,
27
+ systemdUserAvailable,
28
+ systemdUserServicePid,
29
+ type SystemdUserUnitInput,
30
+ } from '../../cli/controller/systemd-user';
26
31
  import { FORGE_VERSION } from '../../version';
27
- import { initializeStandaloneRecovery, loadRecoveryConfig, type PrimaryConnectorServiceConfig, type PrimaryRuntimeServiceConfig, type PublicTunnelServiceConfig, type RecoveryConfig } from './core';
32
+ import { initializeStandaloneRecovery, loadRecoveryConfig, type LaunchdPrimaryConnectorServiceConfig, type LaunchdPublicTunnelServiceConfig, type PrimaryConnectorServiceConfig, type PrimaryRuntimeServiceConfig, type PublicTunnelServiceConfig, type RecoveryConfig, type RecoveryTunnelServiceConfig } from './core';
28
33
  import { RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
34
+ import { acquireRecoveryOperationLock } from './operation-lock';
29
35
  export { RECOVERY_GATEWAY_LABEL, RECOVERY_WATCHDOG_LABEL } from './service-labels';
30
36
  import {
31
37
  RECOVERY_RELEASE_BINARIES,
@@ -129,14 +135,24 @@ export interface RecoveryActivationVerification {
129
135
  healthStatus?: number;
130
136
  }
131
137
 
138
+
139
+ export interface RecoveryServiceHandoffResult {
140
+ platform: 'launchd' | 'systemd-user';
141
+ serviceRegistered: boolean;
142
+ pidReady: boolean;
143
+ portReady: boolean;
144
+ servicePath?: string;
145
+ pid?: number;
146
+ launchd?: SafeHandoffResult;
147
+ }
132
148
  export interface RecoveryActivationResult {
133
149
  release: RecoveryReleaseDescriptor;
134
150
  previous?: RecoveryReleaseDescriptor;
135
151
  migratedLegacy?: RecoveryReleaseDescriptor;
136
152
  noOp?: boolean;
137
153
  handoff?: {
138
- gateway: SafeHandoffResult;
139
- watchdog: SafeHandoffResult;
154
+ gateway: RecoveryServiceHandoffResult;
155
+ watchdog: RecoveryServiceHandoffResult;
140
156
  };
141
157
  verification: RecoveryActivationVerification;
142
158
  rollback?: {
@@ -153,10 +169,14 @@ export interface RecoveryInstallResult {
153
169
  }
154
170
 
155
171
  export interface RecoveryInstallerDependencies {
172
+ platform?: NodeJS.Platform;
173
+ systemdEnv?: NodeJS.ProcessEnv;
174
+ systemdAvailable?: typeof systemdUserAvailable;
175
+ installSystemdUnit?: typeof installSystemdUserUnit;
156
176
  now?: () => number;
157
177
  uuid?: () => string;
158
178
  compileBinary?: (input: { sourceRoot: string; outputPath: string }) => ProcessRunResult;
159
- runCanary?: (input: { binaryPath: string; controllerHome: string; role?: RecoveryRuntimeRole }) => ProcessRunResult;
179
+ runCanary?: (input: { binaryPath: string; controllerHome: string; productVersion: string; role?: RecoveryRuntimeRole }) => ProcessRunResult;
160
180
  installAgent?: typeof installLaunchAgent;
161
181
  handoff?: typeof safeLaunchdHandoff;
162
182
  verify?: (input: {
@@ -175,6 +195,28 @@ function sha256(path: string): string {
175
195
  return createHash('sha256').update(readFileSync(path)).digest('hex');
176
196
  }
177
197
 
198
+ function recoverySourceProductVersion(sourceRoot: string): string {
199
+ const packagePath = join(resolve(sourceRoot), 'package.json');
200
+ try {
201
+ const parsed = JSON.parse(readFileSync(packagePath, 'utf8')) as { version?: unknown };
202
+ const version = typeof parsed.version === 'string' ? parsed.version.trim() : '';
203
+ if (version && version !== '0.0.0-unknown') return version;
204
+ } catch {
205
+ // Fall through to one explicit release-build error.
206
+ }
207
+ throw new Error(`RECOVERY_RELEASE_PRODUCT_VERSION_INVALID: ${packagePath}`);
208
+ }
209
+
210
+ function recoveryReleaseProductVersion(release: RecoveryReleaseDescriptor): string {
211
+ const bound = release.productVersion?.trim();
212
+ if (bound) return bound;
213
+ // Compatibility for Recovery releases created before productVersion became
214
+ // manifest-bound. Prefer their exact recorded source package when it still
215
+ // exists; only the legacy fallback may inherit the current process version.
216
+ try { return recoverySourceProductVersion(release.sourceRoot); } catch { /* legacy/retired source */ }
217
+ return FORGE_VERSION;
218
+ }
219
+
178
220
  function gitText(root: string, args: string[]): string {
179
221
  const result = runProcess('git', ['-C', root, ...args], { timeoutMs: 15_000, maxOutputBytes: 128 * 1024 });
180
222
  if (!result.ok) throw new Error(`RECOVERY_RELEASE_GIT_FAILED: ${result.stderr || result.stdout || result.error}`.slice(0, 2_000));
@@ -190,9 +232,20 @@ export function recoverySourceIdentity(sourceRoot: string): RecoverySourceIdenti
190
232
  return { sourceCommit, releaseRevision: sourceCommit, cleanWorkspace: true, sourceRoot: root };
191
233
  }
192
234
 
235
+ export function resolveRecoveryCompilerExecutable(
236
+ execPath: string = process.execPath,
237
+ env: NodeJS.ProcessEnv = process.env,
238
+ accountHome?: string,
239
+ ): string {
240
+ const configured = env.FORGE_BUN_BIN?.trim();
241
+ if (configured) return configured;
242
+ return accountHome === undefined
243
+ ? resolveBunExecutable(execPath, env)
244
+ : resolveBunExecutable(execPath, env, accountHome);
245
+ }
246
+
193
247
  function defaultCompileBinary(input: { sourceRoot: string; outputPath: string }): ProcessRunResult {
194
- const configured = process.env.FORGE_BUN_BIN?.trim();
195
- const bun = configured || (process.versions.bun && /(?:^|\/)bun(?:$|-)/.test(process.execPath) ? process.execPath : 'bun');
248
+ const bun = resolveRecoveryCompilerExecutable();
196
249
  return runProcess(bun, [
197
250
  'build',
198
251
  join(input.sourceRoot, 'src/runtime/standalone-recovery/entry.ts'),
@@ -202,14 +255,14 @@ function defaultCompileBinary(input: { sourceRoot: string; outputPath: string })
202
255
  ], { cwd: input.sourceRoot, timeoutMs: 180_000, maxOutputBytes: 512 * 1024 });
203
256
  }
204
257
 
205
- function defaultRunCanary(input: { binaryPath: string; controllerHome: string; role?: RecoveryRuntimeRole }): ProcessRunResult {
258
+ function defaultRunCanary(input: { binaryPath: string; controllerHome: string; productVersion: string; role?: RecoveryRuntimeRole }): ProcessRunResult {
206
259
  const args = input.role
207
260
  ? [input.role, RECOVERY_RELEASE_ROLE_CANARY_ARG, '--controller-home', input.controllerHome]
208
261
  : ['status', '--controller-home', input.controllerHome];
209
262
  return runProcess(input.binaryPath, args, {
210
263
  timeoutMs: 30_000,
211
264
  maxOutputBytes: 128 * 1024,
212
- env: { PATH: '/usr/bin:/bin:/usr/sbin:/sbin', FORGE_BUILD_VERSION: FORGE_VERSION },
265
+ env: { PATH: '/usr/bin:/bin:/usr/sbin:/sbin', FORGE_BUILD_VERSION: input.productVersion },
213
266
  replaceEnv: true,
214
267
  });
215
268
  }
@@ -225,6 +278,7 @@ export function stageRecoveryRelease(input: {
225
278
  sourceRoot: string;
226
279
  }, dependencies: RecoveryInstallerDependencies = {}): StagedRecoveryRelease {
227
280
  const identity = recoverySourceIdentity(input.sourceRoot);
281
+ const productVersion = recoverySourceProductVersion(identity.sourceRoot);
228
282
  const now = dependencies.now ?? Date.now;
229
283
  const uuid = dependencies.uuid ?? randomUUID;
230
284
  const releasesRoot = recoveryReleasesRoot(input.controllerHome);
@@ -247,6 +301,7 @@ export function stageRecoveryRelease(input: {
247
301
  schemaVersion: 1,
248
302
  ...identity,
249
303
  builtAt: new Date(now()).toISOString(),
304
+ productVersion,
250
305
  artifacts: completeArtifacts(staging),
251
306
  });
252
307
  const runCanary = dependencies.runCanary ?? defaultRunCanary;
@@ -260,6 +315,7 @@ export function stageRecoveryRelease(input: {
260
315
  const canaryResult = runCanary({
261
316
  binaryPath: canary.binaryPath,
262
317
  controllerHome: resolve(input.controllerHome),
318
+ productVersion,
263
319
  role: canary.role,
264
320
  });
265
321
  if (!canaryResult.ok) {
@@ -331,16 +387,45 @@ export function captureLegacyRecoveryRelease(
331
387
  return release;
332
388
  }
333
389
 
390
+ export function recoverySystemdUserUnitInput(
391
+ controllerHome: string,
392
+ role: RecoveryRuntimeRole,
393
+ env: NodeJS.ProcessEnv = process.env,
394
+ productVersion: string = FORGE_VERSION,
395
+ ): SystemdUserUnitInput {
396
+ const binary = role === 'gateway' ? 'forge-recovery-gateway' : 'forge-recovery-watchdog';
397
+ return {
398
+ description: role === 'gateway' ? 'Forge Standalone Recovery Gateway' : 'Forge Standalone Recovery Watchdog',
399
+ executable: join(recoveryCurrentPath(controllerHome), binary),
400
+ args: [role, '--controller-home', resolve(controllerHome)],
401
+ environment: {
402
+ PATH: env.PATH?.trim() || '/usr/bin:/bin:/usr/sbin:/sbin',
403
+ FORGE_BUILD_VERSION: productVersion,
404
+ // Recovery can be running from its own compiled binary. Never let that
405
+ // binary become the interpreter for the primary OAuth Connector after a
406
+ // Controller Home cutover; preserve the explicit package executable.
407
+ FORGE_CONNECTOR_EXECUTABLE: env.FORGE_CONNECTOR_EXECUTABLE?.trim() || process.execPath,
408
+ },
409
+ restart: 'always',
410
+ restartSec: 5,
411
+ };
412
+ }
413
+
414
+ function recoverySystemdLabel(role: RecoveryRuntimeRole): string {
415
+ return role === 'gateway' ? RECOVERY_GATEWAY_LABEL : RECOVERY_WATCHDOG_LABEL;
416
+ }
417
+
334
418
  function recoveryPlist(input: {
335
419
  label: string;
336
420
  executable: string;
337
421
  command: RecoveryRuntimeRole;
338
422
  controllerHome: string;
339
423
  logPath: string;
424
+ productVersion: string;
340
425
  }): string {
341
426
  const xml = (value: string) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
342
427
  const argumentsList = [
343
- '/usr/bin/env', '-i', 'PATH=/usr/bin:/bin:/usr/sbin:/sbin', `FORGE_BUILD_VERSION=${FORGE_VERSION}`, `FORGE_CONNECTOR_EXECUTABLE=${process.execPath}`,
428
+ '/usr/bin/env', '-i', 'PATH=/usr/bin:/bin:/usr/sbin:/sbin', `FORGE_BUILD_VERSION=${input.productVersion}`, `FORGE_CONNECTOR_EXECUTABLE=${process.execPath}`,
344
429
  input.executable, input.command, '--controller-home', input.controllerHome,
345
430
  ];
346
431
  return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict><key>Label</key><string>${xml(input.label)}</string><key>ProgramArguments</key><array>${argumentsList.map((argument) => `<string>${xml(argument)}</string>`).join('')}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>ThrottleInterval</key><integer>5</integer><key>StandardOutPath</key><string>${xml(input.logPath)}</string><key>StandardErrorPath</key><string>${xml(input.logPath)}</string></dict></plist>\n`;
@@ -375,7 +460,7 @@ export async function verifyRecoveryReleaseActivation(input: {
375
460
  config: RecoveryConfig;
376
461
  expectedRelease: RecoveryReleaseDescriptor;
377
462
  timeoutMs?: number;
378
- }, dependencies: Pick<RecoveryInstallerDependencies, 'servicePid'> = {}): Promise<RecoveryActivationVerification> {
463
+ }, dependencies: Pick<RecoveryInstallerDependencies, 'servicePid' | 'platform' | 'systemdEnv'> = {}): Promise<RecoveryActivationVerification> {
379
464
  const deadline = Date.now() + (input.timeoutMs ?? 60_000);
380
465
  let last: RecoveryActivationVerification = {
381
466
  ok: false,
@@ -391,16 +476,21 @@ export async function verifyRecoveryReleaseActivation(input: {
391
476
  const gateway = readRecoveryRuntimeIdentity(input.controllerHome, 'gateway');
392
477
  const watchdog = readRecoveryRuntimeIdentity(input.controllerHome, 'watchdog');
393
478
  const observedPids: Partial<Record<RecoveryRuntimeRole, number>> = {};
394
- const servicePid = dependencies.servicePid ?? ((_controllerHome: string, role: RecoveryRuntimeRole) => recoveryLaunchdPid(role));
479
+ const platform = dependencies.platform ?? process.platform;
480
+ const servicePid = dependencies.servicePid ?? ((_controllerHome: string, role: RecoveryRuntimeRole) => (
481
+ platform === 'linux'
482
+ ? systemdUserServicePid(recoverySystemdLabel(role), dependencies.systemdEnv ?? process.env)
483
+ : recoveryLaunchdPid(role)
484
+ ));
395
485
  for (const [role, identity] of [['gateway', gateway], ['watchdog', watchdog]] as const) {
396
486
  const managedPid = servicePid(input.controllerHome, role);
397
- if (!managedPid) failures.push(`${role} launchd PID is unavailable`);
487
+ if (!managedPid) failures.push(`${role} service-owner PID is unavailable`);
398
488
  else observedPids[role] = managedPid;
399
489
  if (input.expectedRelease.legacy) continue;
400
490
  if (!identity) failures.push(`${role} runtime identity missing`);
401
491
  else {
402
492
  if (!isProcessAlive(identity.pid)) failures.push(`${role} PID ${identity.pid} is not alive`);
403
- if (managedPid !== undefined && identity.pid !== managedPid) failures.push(`${role} runtime identity PID does not match launchd PID`);
493
+ if (managedPid !== undefined && identity.pid !== managedPid) failures.push(`${role} runtime identity PID does not match service-owner PID`);
404
494
  if (identity.releasePath !== input.expectedRelease.releasePath) failures.push(`${role} release path mismatch`);
405
495
  if (identity.releaseRevision !== input.expectedRelease.releaseRevision) failures.push(`${role} release revision mismatch`);
406
496
  if (identity.manifestSha256 !== input.expectedRelease.manifestSha256) failures.push(`${role} manifest hash mismatch`);
@@ -412,8 +502,9 @@ export async function verifyRecoveryReleaseActivation(input: {
412
502
  signal: AbortSignal.timeout(3_000),
413
503
  });
414
504
  healthStatus = response.status;
415
- const health = await response.json() as { status?: unknown; releaseRevision?: unknown; manifestSha256?: unknown };
505
+ const health = await response.json() as { status?: unknown; version?: unknown; releaseRevision?: unknown; manifestSha256?: unknown };
416
506
  if (response.status !== 200 || health.status !== 'ok') failures.push(`gateway health HTTP ${response.status}`);
507
+ if (input.expectedRelease.productVersion && health.version !== input.expectedRelease.productVersion) failures.push('gateway health product version mismatch');
417
508
  if (!input.expectedRelease.legacy || health.releaseRevision !== undefined) {
418
509
  if (health.releaseRevision !== input.expectedRelease.releaseRevision) failures.push('gateway health release revision mismatch');
419
510
  }
@@ -437,79 +528,17 @@ export async function verifyRecoveryReleaseActivation(input: {
437
528
  return last;
438
529
  }
439
530
 
440
- interface RecoveryReleaseLockRecord {
441
- schemaVersion?: 1;
442
- pid: number;
443
- instanceId: string;
444
- processStartTime?: string;
445
- acquiredAt: string;
446
- action?: string;
447
- }
448
-
449
- function processStartTime(pid: number): string | undefined {
450
- const result = runProcess('/bin/ps', ['-o', 'lstart=', '-p', String(pid)], { timeoutMs: 2_000, maxOutputBytes: 4_096 });
451
- return result.ok ? result.stdout.trim() || undefined : undefined;
452
- }
453
-
454
- function readRecoveryReleaseLock(path: string): RecoveryReleaseLockRecord | undefined {
455
- try {
456
- const parsed = JSON.parse(readFileSync(path, 'utf8')) as RecoveryReleaseLockRecord;
457
- return Number.isInteger(parsed.pid) && typeof parsed.instanceId === 'string' && typeof parsed.acquiredAt === 'string'
458
- ? parsed
459
- : undefined;
460
- } catch {
461
- return undefined;
462
- }
463
- }
464
-
465
- function recoveryReleaseLockOwnerAlive(lock: RecoveryReleaseLockRecord): boolean {
466
- if (!isProcessAlive(lock.pid)) return false;
467
- if (!lock.processStartTime) return true;
468
- const observed = processStartTime(lock.pid);
469
- return observed === undefined || observed === lock.processStartTime;
470
- }
471
-
472
531
  function acquireRecoveryReleaseLock(controllerHome: string): { path: string; instanceId: string; close: () => void } {
473
- const path = join(recoveryRoot(controllerHome), 'locks', 'operation.lock');
474
- mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
475
- const instanceId = `recovery-release-${randomUUID()}`;
476
- const record: RecoveryReleaseLockRecord = {
477
- schemaVersion: 1,
478
- pid: process.pid,
479
- instanceId,
480
- processStartTime: processStartTime(process.pid),
481
- acquiredAt: new Date().toISOString(),
532
+ const attempt = acquireRecoveryOperationLock({
533
+ controllerHome,
482
534
  action: 'install_recovery_release',
483
- };
484
- let fd: number | undefined;
485
- for (let attempt = 0; attempt < 2; attempt += 1) {
486
- try {
487
- fd = openSync(path, 'wx', 0o600);
488
- writeFileSync(fd, JSON.stringify(record));
489
- break;
490
- } catch (error) {
491
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
492
- const existing = readRecoveryReleaseLock(path);
493
- if (!existing) throw new Error('RECOVERY_OPERATION_LOCK_UNCERTAIN');
494
- if (recoveryReleaseLockOwnerAlive(existing)) throw new Error('RECOVERY_OPERATION_LOCK_BUSY');
495
- const latest = readRecoveryReleaseLock(path);
496
- if (!latest || latest.instanceId !== existing.instanceId || recoveryReleaseLockOwnerAlive(latest)) {
497
- if (attempt === 1) throw new Error('RECOVERY_OPERATION_LOCK_BUSY');
498
- continue;
499
- }
500
- try { writeFileSync(`${path}.stale-${Date.now()}-${existing.instanceId}`, readFileSync(path)); } catch { /* evidence best effort */ }
501
- rmSync(path, { force: true });
502
- }
503
- }
504
- if (fd === undefined) throw new Error('RECOVERY_OPERATION_LOCK_BUSY');
535
+ instanceIdPrefix: 'recovery-release-',
536
+ });
537
+ if (!attempt.acquired) throw new Error('RECOVERY_OPERATION_LOCK_BUSY');
505
538
  return {
506
- path,
507
- instanceId,
508
- close: () => {
509
- closeSync(fd);
510
- const current = readRecoveryReleaseLock(path);
511
- if (current?.instanceId === instanceId) rmSync(path, { force: true });
512
- },
539
+ path: attempt.handle.path,
540
+ instanceId: attempt.handle.record.instanceId,
541
+ close: attempt.handle.close,
513
542
  };
514
543
  }
515
544
 
@@ -518,8 +547,53 @@ async function handoffRecoveryServices(input: {
518
547
  config: RecoveryConfig;
519
548
  expectedRelease: RecoveryReleaseDescriptor;
520
549
  dependencies: RecoveryInstallerDependencies;
521
- }): Promise<{ gateway: SafeHandoffResult; watchdog: SafeHandoffResult; verification: RecoveryActivationVerification }> {
522
- if (process.platform !== 'darwin' && !input.dependencies.handoff) throw new Error('RECOVERY_RELEASE_ACTIVATION_REQUIRES_MACOS');
550
+ }): Promise<{ gateway: RecoveryServiceHandoffResult; watchdog: RecoveryServiceHandoffResult; verification: RecoveryActivationVerification }> {
551
+ const platform = input.dependencies.platform ?? process.platform;
552
+ if (platform === 'linux' && !input.dependencies.handoff) {
553
+ const env = input.dependencies.systemdEnv ?? process.env;
554
+ const available = input.dependencies.systemdAvailable ?? systemdUserAvailable;
555
+ if (!available(env)) throw new Error('RECOVERY_SYSTEMD_USER_UNAVAILABLE');
556
+ const installUnit = input.dependencies.installSystemdUnit ?? installSystemdUserUnit;
557
+ const gatewayPath = installUnit({
558
+ unitName: RECOVERY_GATEWAY_LABEL,
559
+ unit: recoverySystemdUserUnitInput(input.controllerHome, 'gateway', env, recoveryReleaseProductVersion(input.expectedRelease)),
560
+ env,
561
+ errorPrefix: 'RECOVERY_SYSTEMD_INSTALL_FAILED',
562
+ });
563
+ const watchdogPath = installUnit({
564
+ unitName: RECOVERY_WATCHDOG_LABEL,
565
+ unit: recoverySystemdUserUnitInput(input.controllerHome, 'watchdog', env, recoveryReleaseProductVersion(input.expectedRelease)),
566
+ env,
567
+ errorPrefix: 'RECOVERY_SYSTEMD_INSTALL_FAILED',
568
+ });
569
+ const verification = await (input.dependencies.verify
570
+ ?? ((verifyInput) => verifyRecoveryReleaseActivation(verifyInput, input.dependencies)))({
571
+ controllerHome: input.controllerHome,
572
+ config: input.config,
573
+ expectedRelease: input.expectedRelease,
574
+ });
575
+ if (!verification.ok) throw new Error(`RECOVERY_RELEASE_VERIFICATION_FAILED: ${verification.failures.join('; ')}`);
576
+ return {
577
+ gateway: {
578
+ platform: 'systemd-user',
579
+ serviceRegistered: true,
580
+ pidReady: Boolean(verification.gatewayPid),
581
+ portReady: verification.healthStatus === 200,
582
+ servicePath: gatewayPath,
583
+ pid: verification.gatewayPid,
584
+ },
585
+ watchdog: {
586
+ platform: 'systemd-user',
587
+ serviceRegistered: true,
588
+ pidReady: Boolean(verification.watchdogPid),
589
+ portReady: true,
590
+ servicePath: watchdogPath,
591
+ pid: verification.watchdogPid,
592
+ },
593
+ verification,
594
+ };
595
+ }
596
+ if (platform !== 'darwin' && !input.dependencies.handoff) throw new Error(`RECOVERY_RELEASE_ACTIVATION_UNSUPPORTED_PLATFORM: ${platform}`);
523
597
  const uid = (input.dependencies.uid ?? (() => typeof process.getuid === 'function' ? process.getuid() : Number.NaN))();
524
598
  if (!Number.isInteger(uid)) throw new Error('RECOVERY_LAUNCHD_UID_UNAVAILABLE');
525
599
  const domain = `gui/${uid}`;
@@ -533,8 +607,9 @@ async function handoffRecoveryServices(input: {
533
607
  mkdirSync(auditRoot, { recursive: true, mode: 0o700 });
534
608
  const gatewayPath = join(generatedRoot, `${RECOVERY_GATEWAY_LABEL}.plist`);
535
609
  const watchdogPath = join(generatedRoot, `${RECOVERY_WATCHDOG_LABEL}.plist`);
536
- writeFileSync(gatewayPath, recoveryPlist({ label: RECOVERY_GATEWAY_LABEL, executable: join(recoveryCurrentPath(input.controllerHome), 'forge-recovery-gateway'), command: 'gateway', controllerHome: resolve(input.controllerHome), logPath: join(auditRoot, 'gateway.log') }), { mode: 0o600 });
537
- writeFileSync(watchdogPath, recoveryPlist({ label: RECOVERY_WATCHDOG_LABEL, executable: join(recoveryCurrentPath(input.controllerHome), 'forge-recovery-watchdog'), command: 'watchdog', controllerHome: resolve(input.controllerHome), logPath: join(auditRoot, 'watchdog.log') }), { mode: 0o600 });
610
+ const productVersion = recoveryReleaseProductVersion(input.expectedRelease);
611
+ writeFileSync(gatewayPath, recoveryPlist({ label: RECOVERY_GATEWAY_LABEL, executable: join(recoveryCurrentPath(input.controllerHome), 'forge-recovery-gateway'), command: 'gateway', controllerHome: resolve(input.controllerHome), logPath: join(auditRoot, 'gateway.log'), productVersion }), { mode: 0o600 });
612
+ writeFileSync(watchdogPath, recoveryPlist({ label: RECOVERY_WATCHDOG_LABEL, executable: join(recoveryCurrentPath(input.controllerHome), 'forge-recovery-watchdog'), command: 'watchdog', controllerHome: resolve(input.controllerHome), logPath: join(auditRoot, 'watchdog.log'), productVersion }), { mode: 0o600 });
538
613
  return {
539
614
  gateway: installAgent(gatewayPath, RECOVERY_GATEWAY_LABEL).path,
540
615
  watchdog: installAgent(watchdogPath, RECOVERY_WATCHDOG_LABEL).path,
@@ -542,7 +617,7 @@ async function handoffRecoveryServices(input: {
542
617
  })();
543
618
  const currentPid = input.dependencies.currentPid ?? defaultCurrentPid;
544
619
  const handoff = input.dependencies.handoff ?? safeLaunchdHandoff;
545
- const gateway = await handoff({
620
+ const gatewayLaunchd = await handoff({
546
621
  label: RECOVERY_GATEWAY_LABEL,
547
622
  plistPath: generated.gateway,
548
623
  domain,
@@ -551,10 +626,10 @@ async function handoffRecoveryServices(input: {
551
626
  maxBootoutWaitMs: 20_000,
552
627
  maxBootstrapRetry: 3,
553
628
  });
554
- if (!gateway.serviceRegistered || !gateway.pidWaitClean || !gateway.portWaitClean) {
629
+ if (!gatewayLaunchd.serviceRegistered || !gatewayLaunchd.pidWaitClean || !gatewayLaunchd.portWaitClean) {
555
630
  throw new Error('RECOVERY_GATEWAY_HANDOFF_FAILED');
556
631
  }
557
- const watchdog = await handoff({
632
+ const watchdogLaunchd = await handoff({
558
633
  label: RECOVERY_WATCHDOG_LABEL,
559
634
  plistPath: generated.watchdog,
560
635
  domain,
@@ -562,7 +637,7 @@ async function handoffRecoveryServices(input: {
562
637
  maxBootoutWaitMs: 20_000,
563
638
  maxBootstrapRetry: 3,
564
639
  });
565
- if (!watchdog.serviceRegistered || !watchdog.pidWaitClean) throw new Error('RECOVERY_WATCHDOG_HANDOFF_FAILED');
640
+ if (!watchdogLaunchd.serviceRegistered || !watchdogLaunchd.pidWaitClean) throw new Error('RECOVERY_WATCHDOG_HANDOFF_FAILED');
566
641
  const verification = await (input.dependencies.verify
567
642
  ?? ((verifyInput) => verifyRecoveryReleaseActivation(verifyInput, input.dependencies)))({
568
643
  controllerHome: input.controllerHome,
@@ -570,13 +645,18 @@ async function handoffRecoveryServices(input: {
570
645
  expectedRelease: input.expectedRelease,
571
646
  });
572
647
  if (!verification.ok) throw new Error(`RECOVERY_RELEASE_VERIFICATION_FAILED: ${verification.failures.join('; ')}`);
573
- return { gateway, watchdog, verification };
648
+ return {
649
+ gateway: { platform: 'launchd', serviceRegistered: gatewayLaunchd.serviceRegistered, pidReady: gatewayLaunchd.pidWaitClean, portReady: gatewayLaunchd.portWaitClean, servicePath: generated.gateway, launchd: gatewayLaunchd },
650
+ watchdog: { platform: 'launchd', serviceRegistered: watchdogLaunchd.serviceRegistered, pidReady: watchdogLaunchd.pidWaitClean, portReady: watchdogLaunchd.portWaitClean, servicePath: generated.watchdog, launchd: watchdogLaunchd },
651
+ verification,
652
+ };
574
653
  }
575
654
 
576
655
  function sameRecoveryReleasePayload(left: RecoveryReleaseDescriptor, right: RecoveryReleaseDescriptor): boolean {
577
656
  return left.releaseRevision === right.releaseRevision
578
657
  && left.sourceCommit === right.sourceCommit
579
658
  && left.cleanWorkspace === right.cleanWorkspace
659
+ && left.productVersion === right.productVersion
580
660
  && RECOVERY_RELEASE_BINARIES.every((binary) => left.artifacts[binary].sha256 === right.artifacts[binary].sha256);
581
661
  }
582
662
 
@@ -647,7 +727,7 @@ export interface RecoveryTunnelLaunchdContract {
647
727
  }
648
728
 
649
729
  function inspectLaunchdRestartContract(
650
- service: Pick<PublicTunnelServiceConfig | PrimaryConnectorServiceConfig, 'platform' | 'label' | 'plistPath'>,
730
+ service: { platform: 'launchd'; label: string; plistPath?: string },
651
731
  allowConditionalKeepAlive: boolean,
652
732
  ): RecoveryTunnelLaunchdContract {
653
733
  const plistPath = service.plistPath ?? launchAgentPath(service.label);
@@ -670,31 +750,50 @@ function inspectLaunchdRestartContract(
670
750
  }
671
751
 
672
752
  export function inspectRecoveryTunnelLaunchdContract(
673
- service: Pick<PublicTunnelServiceConfig | PrimaryConnectorServiceConfig, 'platform' | 'label' | 'plistPath'>,
753
+ service: LaunchdPublicTunnelServiceConfig,
674
754
  ): RecoveryTunnelLaunchdContract {
675
755
  return inspectLaunchdRestartContract(service, false);
676
756
  }
677
757
 
678
758
  export function inspectPrimaryConnectorLaunchdContract(
679
- service: Pick<PrimaryConnectorServiceConfig, 'platform' | 'label' | 'plistPath'>,
759
+ service: LaunchdPrimaryConnectorServiceConfig,
680
760
  ): RecoveryTunnelLaunchdContract {
681
761
  return inspectLaunchdRestartContract(service, true);
682
762
  }
683
763
 
684
764
  export function inspectPrimaryPublicTunnelLaunchdContract(
685
- service: Pick<PublicTunnelServiceConfig, 'platform' | 'label' | 'plistPath'>,
765
+ service: LaunchdPublicTunnelServiceConfig,
686
766
  ): RecoveryTunnelLaunchdContract {
687
767
  return inspectLaunchdRestartContract(service, true);
688
768
  }
689
769
 
770
+ function pathInside(parent: string, candidate: string): boolean {
771
+ const rel = relative(resolve(parent), resolve(candidate));
772
+ return rel === '' || (!rel.startsWith('..') && !rel.startsWith('/') && !rel.startsWith('\\'));
773
+ }
774
+
775
+ function assertDurablePrimaryRuntimeSourceRoot(controllerHome: string, sourceRoot: string, env: NodeJS.ProcessEnv = process.env): void {
776
+ const root = resolve(sourceRoot);
777
+ const normalizedSegments = root.replace(/\\/g, '/').split('/').filter(Boolean);
778
+ const explicitWorktreeHome = env.FORGE_WORKTREE_HOME?.trim();
779
+ const underExplicitWorktreeHome = explicitWorktreeHome ? pathInside(explicitWorktreeHome, root) : false;
780
+ if (normalizedSegments.includes('managed-worktrees') || underExplicitWorktreeHome) {
781
+ throw new Error(`RECOVERY_PRIMARY_RUNTIME_SOURCE_ROOT_DURABLE_REQUIRED: ${root}`);
782
+ }
783
+ if (pathInside(resolve(controllerHome, 'managed-worktrees'), root)) {
784
+ throw new Error(`RECOVERY_PRIMARY_RUNTIME_SOURCE_ROOT_DURABLE_REQUIRED: ${root}`);
785
+ }
786
+ }
787
+
690
788
  export async function installStandaloneRecovery(input: {
691
789
  controllerHome: string;
692
790
  repoRoot: string;
791
+ primaryRuntimeSourceRepositoryId?: string;
693
792
  sourceRoot?: string;
694
793
  port?: number;
695
794
  publicMcpUrl?: string;
696
795
  recoveryPublicUrl?: string;
697
- recoveryTunnelService?: PublicTunnelServiceConfig;
796
+ recoveryTunnelService?: RecoveryTunnelServiceConfig;
698
797
  primaryPublicTunnelService?: PublicTunnelServiceConfig;
699
798
  primaryRuntimeService?: PrimaryRuntimeServiceConfig;
700
799
  primaryConnectorService?: PrimaryConnectorServiceConfig;
@@ -702,6 +801,12 @@ export async function installStandaloneRecovery(input: {
702
801
  }, dependencies: RecoveryInstallerDependencies = {}): Promise<RecoveryInstallResult> {
703
802
  const controllerHome = resolve(input.controllerHome);
704
803
  const sourceRoot = resolve(input.sourceRoot ?? input.repoRoot);
804
+ const primaryRuntimeSourceRoot = resolve(input.repoRoot);
805
+ const primaryRuntimeSourceRepositoryId = input.primaryRuntimeSourceRepositoryId?.trim();
806
+ if (!input.stageOnly) {
807
+ assertDurablePrimaryRuntimeSourceRoot(controllerHome, primaryRuntimeSourceRoot);
808
+ if (!primaryRuntimeSourceRepositoryId) throw new Error('RECOVERY_PRIMARY_RUNTIME_SOURCE_REPOSITORY_ID_REQUIRED');
809
+ }
705
810
  if (input.recoveryTunnelService?.platform === 'launchd') {
706
811
  const tunnelContract = inspectRecoveryTunnelLaunchdContract(input.recoveryTunnelService);
707
812
  if (!tunnelContract.plistInstalled) {
@@ -729,17 +834,20 @@ export async function installStandaloneRecovery(input: {
729
834
  throw new Error('RECOVERY_PRIMARY_CONNECTOR_LAUNCHD_RESTART_CONTRACT_REQUIRED: RunAtLoad=true and KeepAlive=true or KeepAlive.SuccessfulExit=false');
730
835
  }
731
836
  }
837
+ const staged = stageRecoveryRelease({ controllerHome, sourceRoot }, dependencies);
838
+ if (input.stageOnly) {
839
+ return { controllerHome, staged, config: loadRecoveryConfig(controllerHome) };
840
+ }
732
841
  const config = initializeStandaloneRecovery(controllerHome, input.port ?? 8787, {
733
842
  ...(input.publicMcpUrl ? { publicMcpUrl: input.publicMcpUrl } : {}),
734
843
  ...(input.recoveryPublicUrl ? { recoveryPublicUrl: input.recoveryPublicUrl } : {}),
735
844
  ...(input.recoveryTunnelService ? { recoveryTunnelService: input.recoveryTunnelService } : {}),
736
845
  ...(input.primaryPublicTunnelService ? { primaryPublicTunnelService: input.primaryPublicTunnelService } : {}),
737
846
  ...(input.primaryRuntimeService ? { primaryRuntimeService: input.primaryRuntimeService } : {}),
738
- primaryRuntimeSourceRoot: resolve(input.repoRoot),
847
+ primaryRuntimeSourceRoot,
848
+ ...(primaryRuntimeSourceRepositoryId ? { primaryRuntimeSourceRepositoryId } : {}),
739
849
  ...(input.primaryConnectorService ? { primaryConnectorService: input.primaryConnectorService } : {}),
740
850
  });
741
- const staged = stageRecoveryRelease({ controllerHome, sourceRoot }, dependencies);
742
- if (input.stageOnly) return { controllerHome, staged, config };
743
851
  const activated = await activateRecoveryRelease({ controllerHome, config, candidate: staged.release }, dependencies);
744
852
  return { controllerHome, staged, activated, config };
745
853
  }