@paperclipai/server 2026.824.0 → 2026.825.0-beta.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 (519) hide show
  1. package/dist/adapters/builtin-adapter-types.d.ts.map +1 -1
  2. package/dist/adapters/builtin-adapter-types.js +1 -0
  3. package/dist/adapters/builtin-adapter-types.js.map +1 -1
  4. package/dist/adapters/plugin-loader.d.ts +1 -0
  5. package/dist/adapters/plugin-loader.d.ts.map +1 -1
  6. package/dist/adapters/plugin-loader.js +12 -1
  7. package/dist/adapters/plugin-loader.js.map +1 -1
  8. package/dist/adapters/plugin-loader.test.d.ts +2 -0
  9. package/dist/adapters/plugin-loader.test.d.ts.map +1 -0
  10. package/dist/adapters/plugin-loader.test.js +37 -0
  11. package/dist/adapters/plugin-loader.test.js.map +1 -0
  12. package/dist/adapters/registry.d.ts.map +1 -1
  13. package/dist/adapters/registry.js +68 -5
  14. package/dist/adapters/registry.js.map +1 -1
  15. package/dist/adapters/registry.test.d.ts +2 -0
  16. package/dist/adapters/registry.test.d.ts.map +1 -0
  17. package/dist/adapters/registry.test.js +35 -0
  18. package/dist/adapters/registry.test.js.map +1 -0
  19. package/dist/app.d.ts.map +1 -1
  20. package/dist/app.js +5 -7
  21. package/dist/app.js.map +1 -1
  22. package/dist/auth/better-auth.d.ts +10 -0
  23. package/dist/auth/better-auth.d.ts.map +1 -1
  24. package/dist/auth/better-auth.js +47 -0
  25. package/dist/auth/better-auth.js.map +1 -1
  26. package/dist/auth/workspace-login-handoff-exchange.d.ts +75 -0
  27. package/dist/auth/workspace-login-handoff-exchange.d.ts.map +1 -0
  28. package/dist/auth/workspace-login-handoff-exchange.js +73 -0
  29. package/dist/auth/workspace-login-handoff-exchange.js.map +1 -0
  30. package/dist/auth/workspace-login-handoff-plugin.d.ts +40 -0
  31. package/dist/auth/workspace-login-handoff-plugin.d.ts.map +1 -0
  32. package/dist/auth/workspace-login-handoff-plugin.js +140 -0
  33. package/dist/auth/workspace-login-handoff-plugin.js.map +1 -0
  34. package/dist/auth/workspace-login-handoff.d.ts +189 -0
  35. package/dist/auth/workspace-login-handoff.d.ts.map +1 -0
  36. package/dist/auth/workspace-login-handoff.js +319 -0
  37. package/dist/auth/workspace-login-handoff.js.map +1 -0
  38. package/dist/build-info.json +3 -0
  39. package/dist/config.d.ts +1 -0
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +14 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/dev-runner-worktree.d.ts.map +1 -1
  44. package/dist/dev-runner-worktree.js +5 -0
  45. package/dist/dev-runner-worktree.js.map +1 -1
  46. package/dist/duplex-aggregate-ceiling-env.d.ts +21 -0
  47. package/dist/duplex-aggregate-ceiling-env.d.ts.map +1 -0
  48. package/dist/duplex-aggregate-ceiling-env.js +23 -0
  49. package/dist/duplex-aggregate-ceiling-env.js.map +1 -0
  50. package/dist/embedded-postgres-supervisor.d.ts +27 -0
  51. package/dist/embedded-postgres-supervisor.d.ts.map +1 -0
  52. package/dist/embedded-postgres-supervisor.js +72 -0
  53. package/dist/embedded-postgres-supervisor.js.map +1 -0
  54. package/dist/errors.d.ts +2 -0
  55. package/dist/errors.d.ts.map +1 -1
  56. package/dist/errors.js +6 -0
  57. package/dist/errors.js.map +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +115 -7
  60. package/dist/index.js.map +1 -1
  61. package/dist/instrumentation.d.ts +33 -0
  62. package/dist/instrumentation.d.ts.map +1 -1
  63. package/dist/instrumentation.js +68 -1
  64. package/dist/instrumentation.js.map +1 -1
  65. package/dist/middleware/auth.d.ts.map +1 -1
  66. package/dist/middleware/auth.js +35 -10
  67. package/dist/middleware/auth.js.map +1 -1
  68. package/dist/middleware/error-handler.d.ts.map +1 -1
  69. package/dist/middleware/error-handler.js +8 -1
  70. package/dist/middleware/error-handler.js.map +1 -1
  71. package/dist/middleware/logger.d.ts.map +1 -1
  72. package/dist/middleware/logger.js +5 -2
  73. package/dist/middleware/logger.js.map +1 -1
  74. package/dist/middleware/redact-sensitive.d.ts.map +1 -1
  75. package/dist/middleware/redact-sensitive.js +4 -0
  76. package/dist/middleware/redact-sensitive.js.map +1 -1
  77. package/dist/middleware/validate.d.ts +3 -1
  78. package/dist/middleware/validate.d.ts.map +1 -1
  79. package/dist/middleware/validate.js +37 -0
  80. package/dist/middleware/validate.js.map +1 -1
  81. package/dist/routes/access.d.ts.map +1 -1
  82. package/dist/routes/access.js +20 -0
  83. package/dist/routes/access.js.map +1 -1
  84. package/dist/routes/activity.js +4 -4
  85. package/dist/routes/activity.js.map +1 -1
  86. package/dist/routes/adapter-login-route-spine.d.ts +81 -0
  87. package/dist/routes/adapter-login-route-spine.d.ts.map +1 -0
  88. package/dist/routes/adapter-login-route-spine.js +40 -0
  89. package/dist/routes/adapter-login-route-spine.js.map +1 -0
  90. package/dist/routes/adapters.d.ts +33 -0
  91. package/dist/routes/adapters.d.ts.map +1 -1
  92. package/dist/routes/adapters.js +35 -1
  93. package/dist/routes/adapters.js.map +1 -1
  94. package/dist/routes/adapters.test.d.ts +2 -0
  95. package/dist/routes/adapters.test.d.ts.map +1 -0
  96. package/dist/routes/adapters.test.js +58 -0
  97. package/dist/routes/adapters.test.js.map +1 -0
  98. package/dist/routes/agents.d.ts.map +1 -1
  99. package/dist/routes/agents.js +281 -91
  100. package/dist/routes/agents.js.map +1 -1
  101. package/dist/routes/cases.d.ts +11 -22
  102. package/dist/routes/cases.d.ts.map +1 -1
  103. package/dist/routes/cases.js +13 -13
  104. package/dist/routes/companies.js +3 -3
  105. package/dist/routes/decision-training.js +6 -6
  106. package/dist/routes/decisions.js +4 -4
  107. package/dist/routes/decisions.js.map +1 -1
  108. package/dist/routes/environments.d.ts.map +1 -1
  109. package/dist/routes/environments.js +79 -6
  110. package/dist/routes/environments.js.map +1 -1
  111. package/dist/routes/execution-workspaces.d.ts.map +1 -1
  112. package/dist/routes/execution-workspaces.js +384 -16
  113. package/dist/routes/execution-workspaces.js.map +1 -1
  114. package/dist/routes/file-resources.js +2 -2
  115. package/dist/routes/health.d.ts.map +1 -1
  116. package/dist/routes/health.js +58 -4
  117. package/dist/routes/health.js.map +1 -1
  118. package/dist/routes/instance-settings.d.ts.map +1 -1
  119. package/dist/routes/instance-settings.js +40 -0
  120. package/dist/routes/instance-settings.js.map +1 -1
  121. package/dist/routes/issues.d.ts +10 -0
  122. package/dist/routes/issues.d.ts.map +1 -1
  123. package/dist/routes/issues.js +495 -65
  124. package/dist/routes/issues.js.map +1 -1
  125. package/dist/routes/openapi.d.ts.map +1 -1
  126. package/dist/routes/openapi.js +241 -122
  127. package/dist/routes/openapi.js.map +1 -1
  128. package/dist/routes/pipelines.js +19 -19
  129. package/dist/routes/plugins.d.ts.map +1 -1
  130. package/dist/routes/plugins.js +22 -0
  131. package/dist/routes/plugins.js.map +1 -1
  132. package/dist/routes/projects.d.ts.map +1 -1
  133. package/dist/routes/projects.js +2 -0
  134. package/dist/routes/projects.js.map +1 -1
  135. package/dist/routes/routines.js +2 -2
  136. package/dist/routes/routines.js.map +1 -1
  137. package/dist/routes/secrets.d.ts +1 -1
  138. package/dist/routes/secrets.d.ts.map +1 -1
  139. package/dist/routes/secrets.js +39 -50
  140. package/dist/routes/secrets.js.map +1 -1
  141. package/dist/routes/setup-token-route.test.js +50 -3
  142. package/dist/routes/setup-token-route.test.js.map +1 -1
  143. package/dist/services/adapter-login-lease.d.ts +50 -0
  144. package/dist/services/adapter-login-lease.d.ts.map +1 -0
  145. package/dist/services/adapter-login-lease.js +26 -0
  146. package/dist/services/adapter-login-lease.js.map +1 -0
  147. package/dist/services/adapter-login-lease.test.d.ts +2 -0
  148. package/dist/services/adapter-login-lease.test.d.ts.map +1 -0
  149. package/dist/services/adapter-login-lease.test.js +68 -0
  150. package/dist/services/adapter-login-lease.test.js.map +1 -0
  151. package/dist/services/agent-action-audit.js +1 -1
  152. package/dist/services/agent-secret-bindings.d.ts +15 -0
  153. package/dist/services/agent-secret-bindings.d.ts.map +1 -1
  154. package/dist/services/agent-secret-bindings.js +2 -2
  155. package/dist/services/agent-secret-bindings.js.map +1 -1
  156. package/dist/services/agents.d.ts +13 -13
  157. package/dist/services/agents.d.ts.map +1 -1
  158. package/dist/services/agents.js +46 -5
  159. package/dist/services/agents.js.map +1 -1
  160. package/dist/services/artifact-review-documents.d.ts +34 -0
  161. package/dist/services/artifact-review-documents.d.ts.map +1 -0
  162. package/dist/services/artifact-review-documents.js +196 -0
  163. package/dist/services/artifact-review-documents.js.map +1 -0
  164. package/dist/services/assets.d.ts +1 -1
  165. package/dist/services/codex-device-login-credential-read.d.ts +58 -0
  166. package/dist/services/codex-device-login-credential-read.d.ts.map +1 -0
  167. package/dist/services/codex-device-login-credential-read.js +118 -0
  168. package/dist/services/codex-device-login-credential-read.js.map +1 -0
  169. package/dist/services/codex-device-login-reaper.d.ts.map +1 -1
  170. package/dist/services/codex-device-login-reaper.js +8 -5
  171. package/dist/services/codex-device-login-reaper.js.map +1 -1
  172. package/dist/services/codex-device-login-service.d.ts +127 -36
  173. package/dist/services/codex-device-login-service.d.ts.map +1 -1
  174. package/dist/services/codex-device-login-service.js +204 -84
  175. package/dist/services/codex-device-login-service.js.map +1 -1
  176. package/dist/services/company-portability.d.ts +3 -1
  177. package/dist/services/company-portability.d.ts.map +1 -1
  178. package/dist/services/company-portability.js +76 -20
  179. package/dist/services/company-portability.js.map +1 -1
  180. package/dist/services/company-skill-policy.d.ts +1 -1
  181. package/dist/services/costs.d.ts +1 -1
  182. package/dist/services/decisions.d.ts +6 -6
  183. package/dist/services/duplex-observability-recorder.d.ts +62 -0
  184. package/dist/services/duplex-observability-recorder.d.ts.map +1 -0
  185. package/dist/services/duplex-observability-recorder.js +54 -0
  186. package/dist/services/duplex-observability-recorder.js.map +1 -0
  187. package/dist/services/environment-config.js +5 -5
  188. package/dist/services/environment-config.js.map +1 -1
  189. package/dist/services/environment-custom-image-runtime.d.ts +116 -0
  190. package/dist/services/environment-custom-image-runtime.d.ts.map +1 -1
  191. package/dist/services/environment-custom-image-runtime.js +255 -1
  192. package/dist/services/environment-custom-image-runtime.js.map +1 -1
  193. package/dist/services/environment-custom-images.d.ts +39 -0
  194. package/dist/services/environment-custom-images.d.ts.map +1 -1
  195. package/dist/services/environment-custom-images.js +219 -17
  196. package/dist/services/environment-custom-images.js.map +1 -1
  197. package/dist/services/environment-driver-traits.d.ts +109 -0
  198. package/dist/services/environment-driver-traits.d.ts.map +1 -0
  199. package/dist/services/environment-driver-traits.js +102 -0
  200. package/dist/services/environment-driver-traits.js.map +1 -0
  201. package/dist/services/environment-execution-target.d.ts +4 -0
  202. package/dist/services/environment-execution-target.d.ts.map +1 -1
  203. package/dist/services/environment-execution-target.js +63 -7
  204. package/dist/services/environment-execution-target.js.map +1 -1
  205. package/dist/services/environment-run-orchestrator.d.ts +27 -1
  206. package/dist/services/environment-run-orchestrator.d.ts.map +1 -1
  207. package/dist/services/environment-run-orchestrator.js +24 -4
  208. package/dist/services/environment-run-orchestrator.js.map +1 -1
  209. package/dist/services/environment-runtime.d.ts +118 -20
  210. package/dist/services/environment-runtime.d.ts.map +1 -1
  211. package/dist/services/environment-runtime.js +436 -91
  212. package/dist/services/environment-runtime.js.map +1 -1
  213. package/dist/services/environments.d.ts.map +1 -1
  214. package/dist/services/environments.js +20 -2
  215. package/dist/services/environments.js.map +1 -1
  216. package/dist/services/execution-workspace-branch-ownership.d.ts +4 -0
  217. package/dist/services/execution-workspace-branch-ownership.d.ts.map +1 -0
  218. package/dist/services/execution-workspace-branch-ownership.js +9 -0
  219. package/dist/services/execution-workspace-branch-ownership.js.map +1 -0
  220. package/dist/services/execution-workspace-policy.d.ts.map +1 -1
  221. package/dist/services/execution-workspace-policy.js +3 -0
  222. package/dist/services/execution-workspace-policy.js.map +1 -1
  223. package/dist/services/execution-workspaces.d.ts +2 -0
  224. package/dist/services/execution-workspaces.d.ts.map +1 -1
  225. package/dist/services/execution-workspaces.js +90 -2
  226. package/dist/services/execution-workspaces.js.map +1 -1
  227. package/dist/services/external-objects.d.ts +19 -19
  228. package/dist/services/finance.d.ts +1 -1
  229. package/dist/services/goals.d.ts +2 -2
  230. package/dist/services/heartbeat-policy.d.ts +3 -0
  231. package/dist/services/heartbeat-policy.d.ts.map +1 -0
  232. package/dist/services/heartbeat-policy.js +10 -0
  233. package/dist/services/heartbeat-policy.js.map +1 -0
  234. package/dist/services/heartbeat-run-runtime-status.test.js +1 -1
  235. package/dist/services/heartbeat-run-runtime-status.test.js.map +1 -1
  236. package/dist/services/heartbeat.d.ts +23 -1
  237. package/dist/services/heartbeat.d.ts.map +1 -1
  238. package/dist/services/heartbeat.js +192 -11
  239. package/dist/services/heartbeat.js.map +1 -1
  240. package/dist/services/index.d.ts +1 -0
  241. package/dist/services/index.d.ts.map +1 -1
  242. package/dist/services/index.js +1 -0
  243. package/dist/services/index.js.map +1 -1
  244. package/dist/services/instance-settings.d.ts.map +1 -1
  245. package/dist/services/instance-settings.js +2 -0
  246. package/dist/services/instance-settings.js.map +1 -1
  247. package/dist/services/issue-dependency-wakeups.d.ts +57 -9
  248. package/dist/services/issue-dependency-wakeups.d.ts.map +1 -1
  249. package/dist/services/issue-dependency-wakeups.js +146 -14
  250. package/dist/services/issue-dependency-wakeups.js.map +1 -1
  251. package/dist/services/issue-dependency-wakeups.test.d.ts +2 -0
  252. package/dist/services/issue-dependency-wakeups.test.d.ts.map +1 -0
  253. package/dist/services/issue-dependency-wakeups.test.js +183 -0
  254. package/dist/services/issue-dependency-wakeups.test.js.map +1 -0
  255. package/dist/services/issue-recovery-actions.d.ts +6 -1
  256. package/dist/services/issue-recovery-actions.d.ts.map +1 -1
  257. package/dist/services/issue-recovery-actions.js +123 -44
  258. package/dist/services/issue-recovery-actions.js.map +1 -1
  259. package/dist/services/issue-thread-interactions.d.ts +8 -0
  260. package/dist/services/issue-thread-interactions.d.ts.map +1 -1
  261. package/dist/services/issue-thread-interactions.js +220 -10
  262. package/dist/services/issue-thread-interactions.js.map +1 -1
  263. package/dist/services/issues.d.ts +8 -7
  264. package/dist/services/issues.d.ts.map +1 -1
  265. package/dist/services/issues.js +84 -12
  266. package/dist/services/issues.js.map +1 -1
  267. package/dist/services/local-service-supervisor.d.ts +61 -1
  268. package/dist/services/local-service-supervisor.d.ts.map +1 -1
  269. package/dist/services/local-service-supervisor.js +221 -25
  270. package/dist/services/local-service-supervisor.js.map +1 -1
  271. package/dist/services/login-command.d.ts +46 -0
  272. package/dist/services/login-command.d.ts.map +1 -0
  273. package/dist/services/login-command.js +87 -0
  274. package/dist/services/login-command.js.map +1 -0
  275. package/dist/services/login-command.test.d.ts +2 -0
  276. package/dist/services/login-command.test.d.ts.map +1 -0
  277. package/dist/services/login-command.test.js +71 -0
  278. package/dist/services/login-command.test.js.map +1 -0
  279. package/dist/services/managed-workspace-identity.d.ts +140 -0
  280. package/dist/services/managed-workspace-identity.d.ts.map +1 -0
  281. package/dist/services/managed-workspace-identity.js +277 -0
  282. package/dist/services/managed-workspace-identity.js.map +1 -0
  283. package/dist/services/plugin-environment-driver.d.ts +15 -1
  284. package/dist/services/plugin-environment-driver.d.ts.map +1 -1
  285. package/dist/services/plugin-environment-driver.js +23 -1
  286. package/dist/services/plugin-environment-driver.js.map +1 -1
  287. package/dist/services/plugin-host-services.d.ts +3 -2
  288. package/dist/services/plugin-host-services.d.ts.map +1 -1
  289. package/dist/services/plugin-host-services.js +11 -2
  290. package/dist/services/plugin-host-services.js.map +1 -1
  291. package/dist/services/plugin-loader.d.ts.map +1 -1
  292. package/dist/services/plugin-loader.js +39 -8
  293. package/dist/services/plugin-loader.js.map +1 -1
  294. package/dist/services/plugin-managed-agents.d.ts.map +1 -1
  295. package/dist/services/plugin-managed-agents.js +50 -5
  296. package/dist/services/plugin-managed-agents.js.map +1 -1
  297. package/dist/services/plugin-manifest-validator.d.ts.map +1 -1
  298. package/dist/services/plugin-manifest-validator.js +5 -2
  299. package/dist/services/plugin-manifest-validator.js.map +1 -1
  300. package/dist/services/plugin-registry.d.ts +5 -5
  301. package/dist/services/plugin-worker-manager.d.ts +158 -11
  302. package/dist/services/plugin-worker-manager.d.ts.map +1 -1
  303. package/dist/services/plugin-worker-manager.js +1139 -79
  304. package/dist/services/plugin-worker-manager.js.map +1 -1
  305. package/dist/services/projects.d.ts +1 -1
  306. package/dist/services/recovery/disposition-repair.d.ts +24 -0
  307. package/dist/services/recovery/disposition-repair.d.ts.map +1 -0
  308. package/dist/services/recovery/disposition-repair.js +125 -0
  309. package/dist/services/recovery/disposition-repair.js.map +1 -0
  310. package/dist/services/recovery/service.d.ts +6 -6
  311. package/dist/services/recovery/service.d.ts.map +1 -1
  312. package/dist/services/recovery/service.js +856 -384
  313. package/dist/services/recovery/service.js.map +1 -1
  314. package/dist/services/recovery/stranded-notice.js +4 -4
  315. package/dist/services/recovery/stranded-notice.js.map +1 -1
  316. package/dist/services/recovery/stranded-notice.test.js +1 -1
  317. package/dist/services/recovery/stranded-notice.test.js.map +1 -1
  318. package/dist/services/recovery/successful-run-handoff.d.ts +1 -1
  319. package/dist/services/recovery/successful-run-handoff.d.ts.map +1 -1
  320. package/dist/services/recovery/successful-run-handoff.js +6 -4
  321. package/dist/services/recovery/successful-run-handoff.js.map +1 -1
  322. package/dist/services/recovery/successful-run-handoff.test.js +1 -1
  323. package/dist/services/recovery/successful-run-handoff.test.js.map +1 -1
  324. package/dist/services/recovery-observability.d.ts +1 -1
  325. package/dist/services/recovery-observability.d.ts.map +1 -1
  326. package/dist/services/recovery-observability.js +4 -1
  327. package/dist/services/recovery-observability.js.map +1 -1
  328. package/dist/services/routines.d.ts.map +1 -1
  329. package/dist/services/routines.js +86 -1
  330. package/dist/services/routines.js.map +1 -1
  331. package/dist/services/runtime-exposure/loopback-listener.js +1 -1
  332. package/dist/services/runtime-exposure/loopback-listener.js.map +1 -1
  333. package/dist/services/runtime-exposure/loopback-listener.test.js +1 -1
  334. package/dist/services/runtime-exposure/loopback-listener.test.js.map +1 -1
  335. package/dist/services/scripts/codex-auth-read.cjs +93 -0
  336. package/dist/services/secret-proposal-authorization.d.ts +15 -0
  337. package/dist/services/secret-proposal-authorization.d.ts.map +1 -0
  338. package/dist/services/secret-proposal-authorization.js +29 -0
  339. package/dist/services/secret-proposal-authorization.js.map +1 -0
  340. package/dist/services/secret-proposal-notifications.d.ts +18 -0
  341. package/dist/services/secret-proposal-notifications.d.ts.map +1 -0
  342. package/dist/services/secret-proposal-notifications.js +37 -0
  343. package/dist/services/secret-proposal-notifications.js.map +1 -0
  344. package/dist/services/secret-proposals.d.ts +20 -10
  345. package/dist/services/secret-proposals.d.ts.map +1 -1
  346. package/dist/services/secret-proposals.js +205 -14
  347. package/dist/services/secret-proposals.js.map +1 -1
  348. package/dist/services/secrets.d.ts +3 -3
  349. package/dist/services/settings-visibility.d.ts +17 -0
  350. package/dist/services/settings-visibility.d.ts.map +1 -0
  351. package/dist/services/settings-visibility.js +29 -0
  352. package/dist/services/settings-visibility.js.map +1 -0
  353. package/dist/services/setup-token-reaper.d.ts +36 -0
  354. package/dist/services/setup-token-reaper.d.ts.map +1 -0
  355. package/dist/services/setup-token-reaper.js +38 -0
  356. package/dist/services/setup-token-reaper.js.map +1 -0
  357. package/dist/services/setup-token-reaper.test.d.ts +2 -0
  358. package/dist/services/setup-token-reaper.test.d.ts.map +1 -0
  359. package/dist/services/setup-token-reaper.test.js +123 -0
  360. package/dist/services/setup-token-reaper.test.js.map +1 -0
  361. package/dist/services/setup-token-session.d.ts +57 -45
  362. package/dist/services/setup-token-session.d.ts.map +1 -1
  363. package/dist/services/setup-token-session.js +118 -110
  364. package/dist/services/setup-token-session.js.map +1 -1
  365. package/dist/services/setup-token-session.test.js +88 -47
  366. package/dist/services/setup-token-session.test.js.map +1 -1
  367. package/dist/services/setup-token-transport-binding.d.ts +20 -12
  368. package/dist/services/setup-token-transport-binding.d.ts.map +1 -1
  369. package/dist/services/setup-token-transport-binding.js +59 -32
  370. package/dist/services/setup-token-transport-binding.js.map +1 -1
  371. package/dist/services/setup-token-transport-binding.test.js +50 -17
  372. package/dist/services/setup-token-transport-binding.test.js.map +1 -1
  373. package/dist/services/smoke-lab.d.ts +1 -1
  374. package/dist/services/stalled-review-decisions.d.ts +1 -1
  375. package/dist/services/status-cards.d.ts +168 -168
  376. package/dist/services/tool-access-policy.d.ts +2 -2
  377. package/dist/services/tool-gateway.d.ts +1 -1
  378. package/dist/services/work-products.d.ts +19 -0
  379. package/dist/services/work-products.d.ts.map +1 -1
  380. package/dist/services/work-products.js +50 -3
  381. package/dist/services/work-products.js.map +1 -1
  382. package/dist/services/workspace-git-operation-scheduler.test.js +10 -3
  383. package/dist/services/workspace-git-operation-scheduler.test.js.map +1 -1
  384. package/dist/services/workspace-instance-cleanup.d.ts +9 -0
  385. package/dist/services/workspace-instance-cleanup.d.ts.map +1 -1
  386. package/dist/services/workspace-instance-cleanup.js +29 -3
  387. package/dist/services/workspace-instance-cleanup.js.map +1 -1
  388. package/dist/services/workspace-login-handoff-issuer.d.ts +95 -0
  389. package/dist/services/workspace-login-handoff-issuer.d.ts.map +1 -0
  390. package/dist/services/workspace-login-handoff-issuer.js +147 -0
  391. package/dist/services/workspace-login-handoff-issuer.js.map +1 -0
  392. package/dist/services/workspace-operations.d.ts +4 -1
  393. package/dist/services/workspace-operations.d.ts.map +1 -1
  394. package/dist/services/workspace-operations.js +29 -15
  395. package/dist/services/workspace-operations.js.map +1 -1
  396. package/dist/services/workspace-readiness.d.ts +56 -0
  397. package/dist/services/workspace-readiness.d.ts.map +1 -0
  398. package/dist/services/workspace-readiness.js +250 -0
  399. package/dist/services/workspace-readiness.js.map +1 -0
  400. package/dist/services/workspace-realization.d.ts.map +1 -1
  401. package/dist/services/workspace-realization.js +5 -33
  402. package/dist/services/workspace-realization.js.map +1 -1
  403. package/dist/services/workspace-runtime-exposure.test.js +321 -12
  404. package/dist/services/workspace-runtime-exposure.test.js.map +1 -1
  405. package/dist/services/workspace-runtime-leases.d.ts.map +1 -1
  406. package/dist/services/workspace-runtime-leases.js +1 -1
  407. package/dist/services/workspace-runtime-leases.js.map +1 -1
  408. package/dist/services/workspace-runtime-read-model.d.ts +8 -8
  409. package/dist/services/workspace-runtime-read-model.test.js +20 -0
  410. package/dist/services/workspace-runtime-read-model.test.js.map +1 -1
  411. package/dist/services/workspace-runtime-ready-comment.test.js +1 -0
  412. package/dist/services/workspace-runtime-ready-comment.test.js.map +1 -1
  413. package/dist/services/workspace-runtime.d.ts +77 -4
  414. package/dist/services/workspace-runtime.d.ts.map +1 -1
  415. package/dist/services/workspace-runtime.js +1020 -126
  416. package/dist/services/workspace-runtime.js.map +1 -1
  417. package/dist/worktree-config.d.ts.map +1 -1
  418. package/dist/worktree-config.js +3 -76
  419. package/dist/worktree-config.js.map +1 -1
  420. package/dist/worktree-seed-manifest.d.ts +8 -0
  421. package/dist/worktree-seed-manifest.d.ts.map +1 -0
  422. package/dist/worktree-seed-manifest.js +47 -0
  423. package/dist/worktree-seed-manifest.js.map +1 -0
  424. package/package.json +34 -30
  425. package/skills/paperclip/SKILL.md +2 -0
  426. package/skills/paperclip/references/api-reference.md +44 -2
  427. package/ui-dist/assets/CompanyExport-DnEsO-St.js +4 -0
  428. package/ui-dist/assets/{abnfDiagram-N423BO3Z-BvgoV-u5.js → abnfDiagram-N423BO3Z-2ff8TsQ0.js} +1 -1
  429. package/ui-dist/assets/{arc-BInnUBUU.js → arc-B2AXsH34.js} +1 -1
  430. package/ui-dist/assets/architectureDiagram-T3A2C74G-BDxtKMxk.js +36 -0
  431. package/ui-dist/assets/{blockDiagram-VBNYF7ZC-D9kzAkLN.js → blockDiagram-VBNYF7ZC-BB8T5Mj5.js} +1 -1
  432. package/ui-dist/assets/{c4Diagram-5PPSVZJV-BwzsgV_0.js → c4Diagram-5PPSVZJV-lMo8qk-x.js} +1 -1
  433. package/ui-dist/assets/channel-DCTVZpyq.js +1 -0
  434. package/ui-dist/assets/{chunk-2GRJ4B5K-D2EQ9vrl.js → chunk-2GRJ4B5K-CsS7EB3G.js} +1 -1
  435. package/ui-dist/assets/{chunk-2Q5K7J3B-DOA_8fSd.js → chunk-2Q5K7J3B-DFyQPjoR.js} +1 -1
  436. package/ui-dist/assets/{chunk-5RXB4S5H-CT5ukRiT.js → chunk-5RXB4S5H-No2SHOQR.js} +1 -1
  437. package/ui-dist/assets/{chunk-5VM5RSS4-D2gOlkyB.js → chunk-5VM5RSS4-DdxQO7V9.js} +1 -1
  438. package/ui-dist/assets/{chunk-6Q2QTUOP-DUb6aG8c.js → chunk-6Q2QTUOP-gbHjh-aK.js} +1 -1
  439. package/ui-dist/assets/{chunk-GF5L2VYU-BPabG3Cx.js → chunk-GF5L2VYU-h-BBl6NY.js} +1 -1
  440. package/ui-dist/assets/{chunk-JWPE2WC7-scNfhlZz.js → chunk-JWPE2WC7-Q3pJdTz5.js} +1 -1
  441. package/ui-dist/assets/{chunk-KBJHAD2P-BcRAzRpq.js → chunk-KBJHAD2P-Ba8LGLSx.js} +1 -1
  442. package/ui-dist/assets/{chunk-RYQCIY6F-BiZ0-EGh.js → chunk-RYQCIY6F-9VcvykBo.js} +1 -1
  443. package/ui-dist/assets/{chunk-XXDRQBXY-lJULxFIE.js → chunk-XXDRQBXY-NVbjDH4W.js} +1 -1
  444. package/ui-dist/assets/classDiagram-JCYQIIEL-CzJ88u6h.js +1 -0
  445. package/ui-dist/assets/classDiagram-v2-OCEON4UE-CzJ88u6h.js +1 -0
  446. package/ui-dist/assets/cose-bilkent-JH36ORCC-Cdz4wtUh.js +1 -0
  447. package/ui-dist/assets/{cynefin-VYW2F7L2-B34jJY1x.js → cynefin-VYW2F7L2-MpzJkW5s.js} +1 -1
  448. package/ui-dist/assets/{cynefinDiagram-MW4NZA55-DQ8QK8JK.js → cynefinDiagram-MW4NZA55-_Nx8av7i.js} +1 -1
  449. package/ui-dist/assets/{dagre-VZM6K2ZE-B7lSKgxH.js → dagre-VZM6K2ZE-DcZTQ4Oh.js} +1 -1
  450. package/ui-dist/assets/{diagram-7IWD3JNH-B3hLAFGE.js → diagram-7IWD3JNH-UyLi2mee.js} +1 -1
  451. package/ui-dist/assets/{diagram-B4RE2ZJO-CH9Ro9e0.js → diagram-B4RE2ZJO-B6p0Ivqg.js} +1 -1
  452. package/ui-dist/assets/{diagram-LBJQPF4R-DdPVMii7.js → diagram-LBJQPF4R-C8u6pa97.js} +1 -1
  453. package/ui-dist/assets/{diagram-Q27KOJAE-f5VXswjc.js → diagram-Q27KOJAE-DbwyagW0.js} +1 -1
  454. package/ui-dist/assets/{diagram-UB23O5K3-DGqxixsR.js → diagram-UB23O5K3-Cn5BMEH7.js} +1 -1
  455. package/ui-dist/assets/{ebnfDiagram-BXEA7PRR-C-1klg_w.js → ebnfDiagram-BXEA7PRR-vE0IZma9.js} +1 -1
  456. package/ui-dist/assets/{erDiagram-JOGREHBK-Dt4qK4gv.js → erDiagram-JOGREHBK-3OZVIlVG.js} +1 -1
  457. package/ui-dist/assets/{flowDiagram-UKHOOZJN-9ZlrTuET.js → flowDiagram-UKHOOZJN-C94FiPyr.js} +1 -1
  458. package/ui-dist/assets/ganttDiagram-PKOTCBZU-DMVaPmF-.js +292 -0
  459. package/ui-dist/assets/{gitGraphDiagram-DS77QQ5N-HH2fSgad.js → gitGraphDiagram-DS77QQ5N-yeFtJ689.js} +1 -1
  460. package/ui-dist/assets/{index-Cm6iEYPt.js → index-B4ZF3vY8.js} +288 -224
  461. package/ui-dist/assets/{index-CHXG4GPL.js → index-Bi_vqkcQ.js} +1 -1
  462. package/ui-dist/assets/{index-Bc9g6q63.js → index-BjDGQg9-.js} +1 -1
  463. package/ui-dist/assets/{index-FHzrJ7iQ.js → index-BnpyUMTb.js} +1 -1
  464. package/ui-dist/assets/{index-C1mpWCcn.js → index-BoqQTVMA.js} +1 -1
  465. package/ui-dist/assets/index-Bq-Mr-4x.js +1 -0
  466. package/ui-dist/assets/{index-DceuCrr8.js → index-Brb2U7N4.js} +1 -1
  467. package/ui-dist/assets/{index-DQIrrn2L.js → index-BsBybVwH.js} +1 -1
  468. package/ui-dist/assets/{index-CQhaM6qd.js → index-C2iv4Wgu.js} +1 -1
  469. package/ui-dist/assets/{index-DV2FOgyD.js → index-CKxIu7uO.js} +1 -1
  470. package/ui-dist/assets/{index-DU4U1fuv.js → index-Cb4VGIwp.js} +1 -1
  471. package/ui-dist/assets/{index-B61ORkIb.js → index-ChpszwcO.js} +1 -1
  472. package/ui-dist/assets/{index-DlVBBF8o.js → index-CoQFdT8N.js} +1 -1
  473. package/ui-dist/assets/index-D6P8_f0W.css +1 -0
  474. package/ui-dist/assets/index-DKdmghUG.js +1 -0
  475. package/ui-dist/assets/{index-CVTL3aAL.js → index-DaBci7Bx.js} +1 -1
  476. package/ui-dist/assets/{index-diFs0CNC.js → index-DjYaNiXZ.js} +1 -1
  477. package/ui-dist/assets/{index-Bf7tyGxE.js → index-DxTBJl4h.js} +1 -1
  478. package/ui-dist/assets/{index-DsdVneqe.js → index-Dz3E8nn0.js} +1 -1
  479. package/ui-dist/assets/{index-DvFUXI-Q.js → index-J7dz_hap.js} +1 -1
  480. package/ui-dist/assets/{index-CLu3Tfpv.js → index-JMp2Gpnz.js} +1 -1
  481. package/ui-dist/assets/{index-CcRTJ93J.js → index-Kp6wavQt.js} +1 -1
  482. package/ui-dist/assets/{index-BvFx-4KW.js → index-LBhYZ0L4.js} +1 -1
  483. package/ui-dist/assets/{index-BbAhzCq8.js → index-c3C0P6l8.js} +4 -4
  484. package/ui-dist/assets/{index-BY5ITKmG.js → index-pBEPheTW.js} +1 -1
  485. package/ui-dist/assets/{infoDiagram-6WML65LV-2jai2fw-.js → infoDiagram-6WML65LV-BiL7jKLA.js} +1 -1
  486. package/ui-dist/assets/{ishikawaDiagram-WSZJBQD7-CUmGhmEb.js → ishikawaDiagram-WSZJBQD7-DYcQy41D.js} +1 -1
  487. package/ui-dist/assets/{journeyDiagram-NVQOT4AX-CGQuPDO1.js → journeyDiagram-NVQOT4AX-Bxva6JgA.js} +1 -1
  488. package/ui-dist/assets/{kanban-definition-27J2QSJJ-zgEPcbHl.js → kanban-definition-27J2QSJJ-B2HpN34Q.js} +1 -1
  489. package/ui-dist/assets/{linear-BVf5VtLS.js → linear-Dq8qSESw.js} +1 -1
  490. package/ui-dist/assets/{mermaid.core-D2TFkGfI.js → mermaid.core-Ikay8Cpl.js} +35 -35
  491. package/ui-dist/assets/{mindmap-definition-FAOFIHXS-xoAQ0Lb_.js → mindmap-definition-FAOFIHXS-C-SIdYSY.js} +1 -1
  492. package/ui-dist/assets/{pegDiagram-VL7TDLO6-CBR1PWLr.js → pegDiagram-VL7TDLO6-C7rIJ_13.js} +1 -1
  493. package/ui-dist/assets/{pieDiagram-7S7Q4E2Y-BMPnzK_e.js → pieDiagram-7S7Q4E2Y-3gx0a0ve.js} +1 -1
  494. package/ui-dist/assets/{quadrantDiagram-CIZ2JOQS-CPtVUPkR.js → quadrantDiagram-CIZ2JOQS-BiwM9QGv.js} +1 -1
  495. package/ui-dist/assets/{railroadDiagram-AXF67PYL-DC0lsZV1.js → railroadDiagram-AXF67PYL-rFS8XcFA.js} +1 -1
  496. package/ui-dist/assets/{requirementDiagram-LRYGKXZP-BF6k05SB.js → requirementDiagram-LRYGKXZP-CEAUDUTQ.js} +1 -1
  497. package/ui-dist/assets/{sankeyDiagram-W5VNT64P-CRN0ibiP.js → sankeyDiagram-W5VNT64P-B7anpjlM.js} +1 -1
  498. package/ui-dist/assets/{sequenceDiagram-SI44F4Z6-CQtTiPWm.js → sequenceDiagram-SI44F4Z6-CJEdJvm_.js} +1 -1
  499. package/ui-dist/assets/{sizeCapture-X5ZJPWSS-BdjuGZd1.js → sizeCapture-X5ZJPWSS-CU1Vm6c1.js} +1 -1
  500. package/ui-dist/assets/{stateDiagram-OKZ733FA-B99onxyh.js → stateDiagram-OKZ733FA-wxVV9WIi.js} +1 -1
  501. package/ui-dist/assets/stateDiagram-v2-UEYNNEHI-BIFLqlqR.js +1 -0
  502. package/ui-dist/assets/{swimlanes-SLNWSIFB-BIByPbrL.js → swimlanes-SLNWSIFB-Cd-C1Z_k.js} +2 -2
  503. package/ui-dist/assets/swimlanesDiagram-ULZ7WXOC-CoBROkkd.js +8 -0
  504. package/ui-dist/assets/{timeline-definition-Z64GVDOM-I1NgOcn9.js → timeline-definition-Z64GVDOM-SWngk4aR.js} +1 -1
  505. package/ui-dist/assets/{vennDiagram-T6HMQDX7-D6t2Njcl.js → vennDiagram-T6HMQDX7-DFdPUr36.js} +1 -1
  506. package/ui-dist/assets/{wardleyDiagram-T6FBY63Y-BiYKm9gh.js → wardleyDiagram-T6FBY63Y-C6IlrZ7t.js} +1 -1
  507. package/ui-dist/assets/{xychartDiagram-ELKLHX3M-BhwXKWIa.js → xychartDiagram-ELKLHX3M-BNm6hHGl.js} +1 -1
  508. package/ui-dist/index.html +2 -2
  509. package/ui-dist/assets/architectureDiagram-T3A2C74G-Dg4MziOm.js +0 -36
  510. package/ui-dist/assets/channel-qoSYbCn0.js +0 -1
  511. package/ui-dist/assets/classDiagram-JCYQIIEL-B24u1REs.js +0 -1
  512. package/ui-dist/assets/classDiagram-v2-OCEON4UE-B24u1REs.js +0 -1
  513. package/ui-dist/assets/cose-bilkent-JH36ORCC-Do8pe8JB.js +0 -1
  514. package/ui-dist/assets/ganttDiagram-PKOTCBZU-CE3lyvFq.js +0 -292
  515. package/ui-dist/assets/index-BHiUiToL.css +0 -1
  516. package/ui-dist/assets/index-hkanj4t6.js +0 -1
  517. package/ui-dist/assets/index-yRJnh4vx.js +0 -1
  518. package/ui-dist/assets/stateDiagram-v2-UEYNNEHI-bzFlYBZ1.js +0 -1
  519. package/ui-dist/assets/swimlanesDiagram-ULZ7WXOC-DgdkBnuK.js +0 -8
@@ -4,13 +4,15 @@ import multer from "multer";
4
4
  import { z } from "zod";
5
5
  import { and, asc, desc, eq, inArray, isNull, notInArray } from "drizzle-orm";
6
6
  import { activityLog, agents, approvals, companyMemberships, documents, executionWorkspaces, heartbeatRuns, issueApprovals, issueComments, issueDocuments, issueExecutionDecisions, issueRelations, issueThreadInteractions, issues as issueRows, issueWorkProducts, pipelineCaseIssueLinks, pipelineCases, pipelineStages, pipelines, projectWorkspaces, } from "@paperclipai/db";
7
- import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, attachmentArtifactWorkProductMetadataSchema, cancelIssueThreadInteractionSchema, withdrawIssueThreadInteractionSchema, companySearchExtractQuerySchema, companySearchQuerySchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, createIssueLabelSchema, createAcceptedPlanDecompositionSchema, checkoutIssueSchema, createDocumentAnnotationCommentSchema, createDocumentAnnotationThreadSchema, createChildIssueSchema, createIssueSchema, resolveCreateIssueStatusDefault, resolveIssueRecoveryActionSchema, feedbackTargetTypeSchema, feedbackTraceStatusSchema, feedbackVoteValueSchema, upsertIssueFeedbackVoteSchema, upsertIssueWatchdogSchema, linkIssueApprovalSchema, issueDocumentKeySchema, ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY, ISSUE_WATCHDOG_DISCOVERY_KINDS, TASK_WATCHDOG_PRODUCT_BUG_ORIGIN_KIND, ONBOARDING_FIRST_TASK_ORIGIN_KIND, rejectIssueThreadInteractionSchema, restoreIssueDocumentRevisionSchema, respondIssueThreadInteractionSchema, stalledReviewDecisionSchema, submitIssueThreadInteractionVerdictsSchema, updateIssueWorkProductSchema, updateDocumentAnnotationThreadSchema, upsertIssueDocumentSchema, updateIssueSchema, isClosedIsolatedExecutionWorkspace, isUuidLike, normalizeIssueIdentifier as normalizeIssueReferenceIdentifier, issueWriteDenialCodeForResponsibleUserDenial, issueWriteDenialResponse, } from "@paperclipai/shared";
7
+ import { addIssueCommentSchema, acceptIssueThreadInteractionSchema, attachmentArtifactWorkProductMetadataSchema, cancelIssueThreadInteractionSchema, withdrawIssueThreadInteractionSchema, companySearchExtractQuerySchema, companySearchQuerySchema, createIssueAttachmentMetadataSchema, createIssueThreadInteractionSchema, createIssueWorkProductSchema, createIssueLabelSchema, createAcceptedPlanDecompositionSchema, checkoutIssueSchema, createDocumentAnnotationCommentSchema, createDocumentAnnotationThreadSchema, createChildIssueSchema, createIssueSchema, resolveCreateIssueStatusDefault, resolveIssueRecoveryActionSchema, feedbackTargetTypeSchema, feedbackTraceStatusSchema, feedbackVoteValueSchema, upsertIssueFeedbackVoteSchema, upsertIssueWatchdogSchema, linkIssueApprovalSchema, issueDocumentKeySchema, ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY, ISSUE_WATCHDOG_DISCOVERY_KINDS, TASK_WATCHDOG_PRODUCT_BUG_ORIGIN_KIND, ONBOARDING_FIRST_TASK_ORIGIN_KIND, rejectIssueThreadInteractionSchema, restoreIssueDocumentRevisionSchema, respondIssueThreadInteractionSchema, stalledReviewDecisionSchema, submitIssueThreadInteractionVerdictsSchema, updateIssueWorkProductSchema, updateDocumentAnnotationThreadSchema, upsertIssueDocumentSchema, updateIssueSchema, isClosedIsolatedExecutionWorkspace, isMarkdownArtifactWorkProduct, isMarkdownAttachmentContent, isUuidLike, normalizeIssueIdentifier as normalizeIssueReferenceIdentifier, issueWriteDenialCodeForResponsibleUserDenial, issueWriteDenialResponse, } from "@paperclipai/shared";
8
8
  import { trackAgentTaskCompleted } from "@paperclipai/shared/telemetry";
9
9
  import { getTelemetryClient } from "../telemetry.js";
10
10
  import { isUniqueViolation } from "../db-errors.js";
11
- import { validate } from "../middleware/validate.js";
11
+ import { validate, validateIssueMutationBody } from "../middleware/validate.js";
12
12
  import * as serviceIndex from "../services/index.js";
13
- import { accessService, agentService, companySkillService, companyService, companySearchService, goalService, heartbeatService, issueApprovalService, issueRecoveryActionService, issueThreadInteractionService, ISSUE_LIST_DEFAULT_LIMIT, ISSUE_LIST_MAX_LIMIT, issueReferenceService, issueService, clampIssueListLimit, documentService, documentAnnotationService, logActivity, publishActivity, projectService, routineService, workProductService, } from "../services/index.js";
13
+ import { accessService, agentService, budgetService, companySkillService, companyService, companySearchService, goalService, heartbeatService, issueApprovalService, issueRecoveryActionService, issueThreadInteractionService, ISSUE_LIST_DEFAULT_LIMIT, ISSUE_LIST_MAX_LIMIT, issueReferenceService, issueService, clampIssueListLimit, documentService, documentAnnotationService, logActivity, publishActivity, projectService, routineService, workProductService, } from "../services/index.js";
14
+ import { artifactReviewDocumentService } from "../services/artifact-review-documents.js";
15
+ import { assertCanResolveProposal } from "../services/secret-proposal-authorization.js";
14
16
  import { buildDocumentReviewContext, buildPlanReviewContext } from "../services/plan-review-context.js";
15
17
  import { decideIssueReviewPathRecovery, ISSUE_REVIEW_PATH_LOST_WAKE_REASON, isReviewPathRecoveryIdempotencyConflict, REVIEW_PATH_RECOVERY_INSTRUCTION, } from "../services/recovery/review-path-recovery.js";
16
18
  import { hydrateSuccessfulRunHandoffLiveness } from "../services/successful-run-handoff-state.js";
@@ -24,8 +26,10 @@ import { assertNoAgentHostWorkspaceCommandMutation, collectIssueWorkspaceCommand
24
26
  import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js";
25
27
  import { GENERIC_ATTACHMENT_CONTENT_TYPES, isInlineAttachmentContentType, normalizeIssueAttachmentMaxBytes, normalizeContentType, normalizeUploadAttachmentContentType, SVG_CONTENT_TYPE, } from "../attachment-types.js";
26
28
  import { queueIssueAssignmentWakeup } from "../services/issue-assignment-wakeup.js";
29
+ import { createSecretProposalsService } from "../services/secret-proposals.js";
30
+ import { notifySecretProposalResolution } from "../services/secret-proposal-notifications.js";
27
31
  import { buildOnboardingGreeting, ONBOARDING_GREETING_AUTHORIZATION_REASON, } from "../services/onboarding-greeting.js";
28
- import { ISSUE_BLOCKERS_RESOLVED_WAKE_REASON, buildIssueBlockersResolvedWakeIdempotencyKey, findExistingIssueBlockersResolvedWake, } from "../services/issue-dependency-wakeups.js";
32
+ import { ISSUE_BLOCKERS_RESOLVED_WAKE_REASON, buildIssueBlockersResolvedWakeStateKey, findExistingIssueBlockersResolvedWakeForReadyState, } from "../services/issue-dependency-wakeups.js";
29
33
  import { assertEnvironmentSelectionForCompany } from "./environment-selection.js";
30
34
  import { executionWorkspaceService as executionWorkspaceServiceDirect, STALE_REOPEN_PENDING_CONSUMPTION_GRACE_MS, } from "../services/execution-workspaces.js";
31
35
  import { decisionTrainingService } from "../services/decision-training.js";
@@ -59,17 +63,17 @@ function prefersMinimalIssueUpdateResponse(req) {
59
63
  .some((preference) => preference.trim().toLowerCase() === "return=minimal");
60
64
  }
61
65
  const refreshExternalObjectsSchema = z.object({
62
- objectIds: z.array(z.string().uuid()).max(50).optional(),
66
+ objectIds: z.array(z.string().guid()).max(50).optional(),
63
67
  }).strict();
64
68
  const inboxArchiveBodySchema = z.object({
65
69
  userId: z.string().trim().min(1).optional(),
66
70
  }).strict().default({});
67
71
  const externalObjectSummariesSchema = z.object({
68
- issueIds: z.array(z.string().uuid()).max(1000),
72
+ issueIds: z.array(z.string().guid()).max(1000),
69
73
  }).strict();
70
74
  const promoteLowTrustOutputSchema = z.object({
71
75
  sourceArtifactKind: z.enum(["comment", "document", "work_product", "issue"]),
72
- sourceArtifactId: z.string().uuid(),
76
+ sourceArtifactId: z.string().guid(),
73
77
  title: z.string().trim().min(1).max(200),
74
78
  summary: z.string().trim().min(1).max(8_000),
75
79
  });
@@ -189,7 +193,7 @@ function requiresPaperclipAttachmentMetadata(input, fallback) {
189
193
  return type === "artifact" && provider === "paperclip";
190
194
  }
191
195
  const attachmentArtifactMetadataInputSchema = z.object({
192
- attachmentId: z.string().uuid(),
196
+ attachmentId: z.string().guid(),
193
197
  }).passthrough();
194
198
  function buildCreateIssueActivityStatusDetails(issue, res) {
195
199
  const statusDefault = res.locals.createIssueStatusDefault;
@@ -1211,6 +1215,13 @@ function readToolActionExecutionStatus(value) {
1211
1215
  ? value
1212
1216
  : null;
1213
1217
  }
1218
+ function secretProposalExecutionErrorCode(error) {
1219
+ if (error instanceof HttpError) {
1220
+ const details = readObject(error.details);
1221
+ return readNonEmptyString(details.code) ?? `http_${error.status}`;
1222
+ }
1223
+ return "secret_proposal_execution_failed";
1224
+ }
1214
1225
  function readToolActionContinuationContext(interaction) {
1215
1226
  const payload = readObject(interaction.payload);
1216
1227
  const toolActionPayload = readObject(payload.toolAction);
@@ -1269,6 +1280,49 @@ function readToolActionContinuationContext(interaction) {
1269
1280
  instructions: `the approved ${toolName} action is ${executionStatus}; do not call the tool again while this approval is being processed.`,
1270
1281
  };
1271
1282
  }
1283
+ function readSecretProposalContinuationContext(interaction) {
1284
+ const payload = readObject(interaction.payload);
1285
+ const proposal = readObject(payload.secretProposal);
1286
+ const proposalId = readNonEmptyString(proposal.proposalId);
1287
+ const configPath = readNonEmptyString(proposal.configPath);
1288
+ if (!proposalId || !configPath)
1289
+ return null;
1290
+ const result = readObject(interaction.result);
1291
+ const execution = readObject(result.secretProposal);
1292
+ const executionStatus = readNonEmptyString(execution.status);
1293
+ const errorCode = readNonEmptyString(execution.errorCode);
1294
+ const sourceSecretLabel = readNonEmptyString(proposal.sourceSecretLabel);
1295
+ if (interaction.status === "rejected") {
1296
+ return {
1297
+ proposalId,
1298
+ configPath,
1299
+ decision: "rejected",
1300
+ executionStatus: "rejected",
1301
+ instructions: "the secret binding proposal was rejected; do not assume the alias exists.",
1302
+ };
1303
+ }
1304
+ if (interaction.status !== "accepted" || (executionStatus !== "executed" && executionStatus !== "failed")) {
1305
+ return null;
1306
+ }
1307
+ if (executionStatus === "executed") {
1308
+ return {
1309
+ proposalId,
1310
+ configPath,
1311
+ decision: "accepted",
1312
+ executionStatus,
1313
+ ...(sourceSecretLabel ? { sourceSecretLabel } : {}),
1314
+ instructions: `the binding was created at ${configPath}; verify it with GET /api/agents/me/secrets before using it.`,
1315
+ };
1316
+ }
1317
+ return {
1318
+ proposalId,
1319
+ configPath,
1320
+ decision: "accepted",
1321
+ executionStatus,
1322
+ ...(errorCode ? { errorCode } : {}),
1323
+ instructions: "the binding was not created; inspect the failure comment and submit a fresh proposal after fixing the cause.",
1324
+ };
1325
+ }
1272
1326
  const REQUEST_ITEM_VERDICTS_WAKE_COALESCE_WINDOW_MS = 2_000;
1273
1327
  function buildRequestItemVerdictsWakeIdempotencyKey(args) {
1274
1328
  const now = args.at ?? new Date();
@@ -1306,6 +1360,7 @@ async function queueResolvedInteractionContinuationWakeup(input) {
1306
1360
  const interactionResult = readConfirmationResultForWake(input.interaction.result);
1307
1361
  const checkboxSelection = readCheckboxSelectionForWake(input.interaction);
1308
1362
  const toolAction = readToolActionContinuationContext(input.interaction);
1363
+ const secretProposal = readSecretProposalContinuationContext(input.interaction);
1309
1364
  const newlyResolvedItemIds = input.newlyResolvedItemIds?.filter((value) => value.length > 0) ?? [];
1310
1365
  const itemVerdicts = newlyResolvedItemIds.length > 0
1311
1366
  ? {
@@ -1337,6 +1392,7 @@ async function queueResolvedInteractionContinuationWakeup(input) {
1337
1392
  ...(planReviewInteraction ? { planReviewInteraction } : {}),
1338
1393
  ...(checkboxSelection ? { checkboxSelection } : {}),
1339
1394
  ...(toolAction ? { toolAction } : {}),
1395
+ ...(secretProposal ? { secretProposal } : {}),
1340
1396
  ...(itemVerdicts ? { itemVerdicts, newlyResolvedItemIds } : {}),
1341
1397
  ...(reviewPathContext ?? {}),
1342
1398
  mutation: "interaction",
@@ -1355,6 +1411,7 @@ async function queueResolvedInteractionContinuationWakeup(input) {
1355
1411
  ...(planReviewInteraction ? { planReviewInteraction } : {}),
1356
1412
  ...(checkboxSelection ? { checkboxSelection } : {}),
1357
1413
  ...(toolAction ? { toolAction } : {}),
1414
+ ...(secretProposal ? { secretProposal } : {}),
1358
1415
  ...(itemVerdicts ? { itemVerdicts, newlyResolvedItemIds } : {}),
1359
1416
  ...(reviewPathContext ?? {}),
1360
1417
  wakeReason: "issue_commented",
@@ -1835,6 +1892,7 @@ export function issueRoutes(db, storage, opts = {}) {
1835
1892
  const svc = issueService(db);
1836
1893
  const runRedactions = createRunSecretRedactionRegistry(db);
1837
1894
  const access = accessService(db);
1895
+ const secretProposals = createSecretProposalsService(db);
1838
1896
  const heartbeat = heartbeatService(db, {
1839
1897
  pluginWorkerManager: opts.pluginWorkerManager,
1840
1898
  });
@@ -1857,6 +1915,7 @@ export function issueRoutes(db, storage, opts = {}) {
1857
1915
  const executionWorkspacesSvc = executionWorkspaceServiceDirect(db);
1858
1916
  const workProductsSvc = workProductService(db);
1859
1917
  const documentsSvc = documentService(db);
1918
+ const artifactReviewDocumentsSvc = artifactReviewDocumentService(db, storage);
1860
1919
  const companySkillsSvc = companySkillService(db);
1861
1920
  const documentAnnotationsSvc = documentAnnotationService(db);
1862
1921
  const decisionTrainingSvc = decisionTrainingService(db);
@@ -2408,8 +2467,8 @@ export function issueRoutes(db, storage, opts = {}) {
2408
2467
  && !(interaction.kind === "request_confirmation"
2409
2468
  && interaction.payload
2410
2469
  && typeof interaction.payload === "object"
2411
- && "toolAction" in interaction.payload
2412
- && interaction.payload.toolAction !== undefined));
2470
+ && (("toolAction" in interaction.payload && interaction.payload.toolAction !== undefined)
2471
+ || ("secretProposal" in interaction.payload && interaction.payload.secretProposal !== undefined))));
2413
2472
  if (!designatedReviewConfirmation) {
2414
2473
  const creatorDescription = input.actorType === "agent"
2415
2474
  ? "this agent run"
@@ -3066,7 +3125,8 @@ export function issueRoutes(db, storage, opts = {}) {
3066
3125
  : { type: "user", userId: actor.actorId },
3067
3126
  interaction,
3068
3127
  additionalRestriction: resolverPolicyRestriction,
3069
- governedAction: interaction.kind === "request_confirmation" && payload?.toolAction !== undefined,
3128
+ governedAction: interaction.kind === "request_confirmation"
3129
+ && (payload?.toolAction !== undefined || payload?.secretProposal !== undefined),
3070
3130
  });
3071
3131
  if (!decision.allowed) {
3072
3132
  return denyIssueThreadInteractionResolution(res, {
@@ -3688,15 +3748,14 @@ export function issueRoutes(db, storage, opts = {}) {
3688
3748
  });
3689
3749
  return false;
3690
3750
  }
3691
- async function assertRecoveryActionAuthority(req, res, issue, activeRecoveryAction, input) {
3751
+ async function requireRecoveryActionAuthority(req, issue, activeRecoveryAction, input) {
3692
3752
  if (req.actor.type !== "agent")
3693
3753
  return true;
3694
3754
  if (!activeRecoveryAction)
3695
3755
  return true;
3696
3756
  const actorAgentId = req.actor.agentId;
3697
3757
  if (!actorAgentId) {
3698
- res.status(403).json({ error: "Agent authentication required" });
3699
- return false;
3758
+ throw forbidden("Agent authentication required");
3700
3759
  }
3701
3760
  if (issue.assigneeAgentId === actorAgentId)
3702
3761
  return true;
@@ -3710,19 +3769,123 @@ export function issueRoutes(db, storage, opts = {}) {
3710
3769
  await hasActiveCheckoutManagementOverride(actorAgentId, issue.companyId, activeRecoveryAction.ownerAgentId)) {
3711
3770
  return true;
3712
3771
  }
3713
- res.status(403).json({
3714
- error: "Agent cannot resolve another owner's recovery action",
3715
- details: {
3772
+ throw forbidden("Agent cannot resolve another owner's recovery action", {
3773
+ issueId: issue.id,
3774
+ recoveryActionId: activeRecoveryAction.id,
3775
+ actorAgentId,
3776
+ assigneeAgentId: issue.assigneeAgentId,
3777
+ recoveryOwnerAgentId: activeRecoveryAction.ownerAgentId,
3778
+ source: input.source,
3779
+ securityPrinciples: ["Least Privilege", "Complete Mediation", "Secure Defaults"],
3780
+ });
3781
+ }
3782
+ function activeExecutionParticipantAgentId(issue) {
3783
+ const state = parseIssueExecutionState(issue.executionState);
3784
+ return state?.status === "pending" && state.currentParticipant?.type === "agent"
3785
+ ? state.currentParticipant.agentId
3786
+ : null;
3787
+ }
3788
+ async function requireRecoverySourceMutationAuthority(req, issue) {
3789
+ if (req.actor.type !== "agent")
3790
+ return;
3791
+ const actorAgentId = req.actor.agentId;
3792
+ if (!actorAgentId)
3793
+ throw forbidden("Agent authentication required");
3794
+ const isSourceOwner = issue.assigneeAgentId === actorAgentId;
3795
+ const isExecutionParticipant = activeExecutionParticipantAgentId(issue) === actorAgentId;
3796
+ const hasPolicyGrant = Boolean(issue.assigneeAgentId &&
3797
+ await hasActiveCheckoutManagementOverride(actorAgentId, issue.companyId, issue.assigneeAgentId));
3798
+ if (!isSourceOwner && !isExecutionParticipant && !hasPolicyGrant) {
3799
+ throw forbidden("Recovery ownership does not authorize this source issue mutation", {
3800
+ code: "recovery_source_authority_required",
3716
3801
  issueId: issue.id,
3717
- recoveryActionId: activeRecoveryAction.id,
3718
3802
  actorAgentId,
3719
3803
  assigneeAgentId: issue.assigneeAgentId,
3720
- recoveryOwnerAgentId: activeRecoveryAction.ownerAgentId,
3721
- source: input.source,
3804
+ currentExecutionParticipantAgentId: activeExecutionParticipantAgentId(issue),
3805
+ remediation: "Have the source owner, current execution participant, board, or a policy-authorized agent perform the source mutation.",
3722
3806
  securityPrinciples: ["Least Privilege", "Complete Mediation", "Secure Defaults"],
3723
- },
3724
- });
3725
- return false;
3807
+ });
3808
+ }
3809
+ const actorRunId = req.actor.runId?.trim() || null;
3810
+ const conflictingRunId = [issue.checkoutRunId, issue.executionRunId]
3811
+ .find((runId) => runId && runId !== actorRunId);
3812
+ if (conflictingRunId && !hasPolicyGrant) {
3813
+ throw conflict("Source issue mutation is locked by another active checkout or run", {
3814
+ code: "recovery_source_run_lock",
3815
+ issueId: issue.id,
3816
+ actorAgentId,
3817
+ actorRunId,
3818
+ checkoutRunId: issue.checkoutRunId ?? null,
3819
+ executionRunId: issue.executionRunId ?? null,
3820
+ });
3821
+ }
3822
+ if (isSourceOwner && issue.status === "in_progress" && !actorRunId && !hasPolicyGrant) {
3823
+ throw unauthorized("Agent run id required");
3824
+ }
3825
+ }
3826
+ async function assertSafeRecoveryHandBackGates(input) {
3827
+ const returnOwnerAgentId = input.recoveryAction.returnOwnerAgentId;
3828
+ if (!returnOwnerAgentId || input.issue.assigneeAgentId !== returnOwnerAgentId) {
3829
+ throw forbidden("Safe recovery hand-back requires the recorded original owner to remain assigned", {
3830
+ code: "recovery_safe_hand_back_owner_mismatch",
3831
+ issueId: input.issue.id,
3832
+ assigneeAgentId: input.issue.assigneeAgentId,
3833
+ returnOwnerAgentId,
3834
+ });
3835
+ }
3836
+ const actorRunId = input.req.actor.type === "agent"
3837
+ ? input.req.actor.runId?.trim() || null
3838
+ : null;
3839
+ const conflictingRunId = [input.issue.checkoutRunId, input.issue.executionRunId]
3840
+ .find((runId) => runId && runId !== actorRunId);
3841
+ if (conflictingRunId) {
3842
+ throw conflict("Safe recovery hand-back is locked by another active checkout or run", {
3843
+ code: "recovery_source_run_lock",
3844
+ issueId: input.issue.id,
3845
+ actorRunId,
3846
+ checkoutRunId: input.issue.checkoutRunId ?? null,
3847
+ executionRunId: input.issue.executionRunId ?? null,
3848
+ });
3849
+ }
3850
+ if (parseIssueExecutionState(input.issue.executionState)?.status === "pending") {
3851
+ throw conflict("Safe recovery hand-back cannot bypass a pending execution review or approval stage", {
3852
+ code: "recovery_governed_stage_pending",
3853
+ issueId: input.issue.id,
3854
+ });
3855
+ }
3856
+ const activePauseHold = await treeControlSvc.getActivePauseHoldGate(input.issue.companyId, input.issue.id);
3857
+ if (activePauseHold) {
3858
+ throw conflict("Safe recovery hand-back blocked by active subtree pause hold", {
3859
+ issueId: input.issue.id,
3860
+ holdId: activePauseHold.holdId,
3861
+ rootIssueId: activePauseHold.rootIssueId,
3862
+ mode: activePauseHold.mode,
3863
+ });
3864
+ }
3865
+ if (input.issue.projectId) {
3866
+ const project = await projectsSvc.getById(input.issue.projectId);
3867
+ if (project?.pausedAt) {
3868
+ throw conflict(project.pauseReason === "budget"
3869
+ ? "Project is paused because its budget hard-stop was reached"
3870
+ : "Project is paused");
3871
+ }
3872
+ }
3873
+ const approvals = await issueApprovalsSvc.listApprovalsForIssue(input.issue.id);
3874
+ if (approvals.some((approval) => ACTIVE_REVIEW_APPROVAL_STATUSES.has(String(approval.status)))) {
3875
+ throw conflict("Safe recovery hand-back cannot bypass a pending governed approval", {
3876
+ code: "recovery_governed_approval_pending",
3877
+ issueId: input.issue.id,
3878
+ });
3879
+ }
3880
+ const budgetBlock = await budgetService(db).getInvocationBlock(input.issue.companyId, returnOwnerAgentId, { issueId: input.issue.id, projectId: input.issue.projectId });
3881
+ if (budgetBlock) {
3882
+ throw conflict("Safe recovery hand-back is blocked by the source owner's budget or pause gate", {
3883
+ code: "recovery_safe_hand_back_budget_blocked",
3884
+ issueId: input.issue.id,
3885
+ returnOwnerAgentId,
3886
+ budgetBlock,
3887
+ });
3888
+ }
3726
3889
  }
3727
3890
  async function resolveActiveIssueRun(issue) {
3728
3891
  let runToInterrupt = issue.executionRunId ? await heartbeat.getRun(issue.executionRunId) : null;
@@ -5089,39 +5252,45 @@ export function issueRoutes(db, storage, opts = {}) {
5089
5252
  const existing = await getAccessibleResource(req, res, svc.getById(id), "Issue not found");
5090
5253
  if (!existing)
5091
5254
  return;
5092
- if (!(await assertAgentIssueMutationAllowed(req, res, existing)))
5255
+ if (!(await assertIssueReadAllowed(req, res, existing)))
5093
5256
  return;
5094
- const activeRecoveryAction = await recoveryActionsSvc.getActiveForIssue(existing.companyId, existing.id);
5095
- if (!(await assertRecoveryActionAuthority(req, res, existing, activeRecoveryAction, { source: "recovery_action_resolution" }))) {
5257
+ if (await assertLowTrustControlPlaneDenied(req, res, existing.companyId, existing))
5096
5258
  return;
5259
+ if (req.actor.type === "agent") {
5260
+ const boundaryDecision = await decideIssueAccess(req, existing, "issue:mutate");
5261
+ if (!boundaryDecision.allowed) {
5262
+ await denyIssueWrite(req, res, existing, issueWriteDenialCodeForDecision(boundaryDecision));
5263
+ return;
5264
+ }
5265
+ if (!requireAgentRunId(req, res))
5266
+ return;
5267
+ if (!(await assertCrossIssueInfluenceWithinRunCap(req, res, existing, "update")))
5268
+ return;
5097
5269
  }
5098
5270
  const { actionId, outcome, sourceIssueStatus, resolutionNote } = req.body;
5099
5271
  if (outcome === "false_positive" || outcome === "cancelled") {
5100
5272
  assertBoard(req);
5101
5273
  }
5102
5274
  const actor = getActorInfo(req);
5103
- const handBackAgentId = outcome === "restored" && sourceIssueStatus === "todo"
5104
- ? activeRecoveryAction?.returnOwnerAgentId ?? null
5105
- : null;
5106
- const recordedOutcome = handBackAgentId
5107
- ? "handed_back"
5108
- : outcome === "restored" && sourceIssueStatus === "done"
5109
- ? "owner_completed"
5110
- : outcome;
5111
- const updateFields = sourceIssueStatus ? { status: sourceIssueStatus } : {};
5112
- await assertInReviewReviewPath({
5113
- existing,
5114
- updateFields,
5115
- actorType: actor.actorType,
5116
- actorId: actor.actorId,
5117
- actorAgentId: actor.agentId,
5118
- actorRunId: actor.runId,
5119
- });
5120
5275
  const actionStatus = outcome === "cancelled" ? "cancelled" : "resolved";
5121
5276
  const postCommitActivityPublications = [];
5122
5277
  const result = await db.transaction(async (tx) => {
5123
- let issue = existing;
5124
- if (outcome === "blocked") {
5278
+ const lockedIssue = await tx
5279
+ .select()
5280
+ .from(issueRows)
5281
+ .where(and(eq(issueRows.companyId, existing.companyId), eq(issueRows.id, existing.id)))
5282
+ .for("update")
5283
+ .then((rows) => rows[0] ?? null);
5284
+ if (!lockedIssue)
5285
+ throw notFound("Issue not found");
5286
+ const activeRecoveryAction = await recoveryActionsSvc.getActiveForIssue(lockedIssue.companyId, lockedIssue.id, tx);
5287
+ if (!activeRecoveryAction || (actionId && activeRecoveryAction.id !== actionId)) {
5288
+ throw notFound("Active recovery action not found");
5289
+ }
5290
+ await requireRecoveryActionAuthority(req, lockedIssue, activeRecoveryAction, { source: "recovery_action_resolution" });
5291
+ let issue = lockedIssue;
5292
+ const sourceStatusChanged = sourceIssueStatus !== lockedIssue.status;
5293
+ if (outcome === "blocked" && sourceStatusChanged) {
5125
5294
  const unresolvedBlockers = await tx
5126
5295
  .select({ id: issueRows.id })
5127
5296
  .from(issueRelations)
@@ -5132,10 +5301,78 @@ export function issueRoutes(db, storage, opts = {}) {
5132
5301
  throw unprocessable("Blocked recovery resolution requires an unresolved first-class blocker on the source issue");
5133
5302
  }
5134
5303
  }
5135
- if (sourceIssueStatus) {
5304
+ if (sourceStatusChanged) {
5305
+ const safeHandBack = outcome === "restored" &&
5306
+ sourceIssueStatus === "todo" &&
5307
+ activeRecoveryAction.returnOwnerAgentId != null &&
5308
+ lockedIssue.assigneeAgentId === activeRecoveryAction.returnOwnerAgentId;
5309
+ if (safeHandBack) {
5310
+ await assertSafeRecoveryHandBackGates({
5311
+ req,
5312
+ issue: lockedIssue,
5313
+ recoveryAction: activeRecoveryAction,
5314
+ });
5315
+ }
5316
+ else {
5317
+ await requireRecoverySourceMutationAuthority(req, lockedIssue);
5318
+ }
5319
+ if (lockedIssue.status === "in_review" &&
5320
+ (sourceIssueStatus === "done" || sourceIssueStatus === "cancelled") &&
5321
+ lockedIssue.reviewPolicy != null &&
5322
+ lockedIssue.reviewPolicy !== "anyone") {
5323
+ await assertIssueReviewVerdictActorAllowed(tx, {
5324
+ issue: lockedIssue,
5325
+ actor: { type: actor.actorType, id: actor.actorId },
5326
+ });
5327
+ }
5328
+ const updateFields = { status: sourceIssueStatus };
5329
+ if (!safeHandBack) {
5330
+ await assertInReviewReviewPath({
5331
+ existing: lockedIssue,
5332
+ updateFields,
5333
+ actorType: actor.actorType,
5334
+ actorId: actor.actorId,
5335
+ actorAgentId: actor.agentId,
5336
+ actorRunId: actor.runId,
5337
+ });
5338
+ const executionPolicy = normalizeIssueExecutionPolicy(lockedIssue.executionPolicy ?? null);
5339
+ const transition = applyIssueExecutionPolicyTransition({
5340
+ issue: lockedIssue,
5341
+ policy: executionPolicy,
5342
+ previousPolicy: executionPolicy,
5343
+ requestedStatus: sourceIssueStatus,
5344
+ requestedAssigneePatch: {},
5345
+ actor: {
5346
+ agentId: actor.agentId ?? null,
5347
+ userId: actor.actorType === "user" ? actor.actorId : null,
5348
+ },
5349
+ allowBoardOverride: req.actor.type === "board",
5350
+ commentBody: resolutionNote ?? null,
5351
+ });
5352
+ Object.assign(updateFields, transition.patch);
5353
+ if (transition.decision) {
5354
+ const decisionId = randomUUID();
5355
+ const nextExecutionState = updateFields.executionState;
5356
+ if (!nextExecutionState || typeof nextExecutionState !== "object") {
5357
+ throw new Error("Execution policy decision patch is missing executionState");
5358
+ }
5359
+ updateFields.executionState = { ...nextExecutionState, lastDecisionId: decisionId };
5360
+ await tx.insert(issueExecutionDecisions).values({
5361
+ id: decisionId,
5362
+ companyId: lockedIssue.companyId,
5363
+ issueId: lockedIssue.id,
5364
+ stageId: transition.decision.stageId,
5365
+ stageType: transition.decision.stageType,
5366
+ actorAgentId: actor.agentId ?? null,
5367
+ actorUserId: actor.actorType === "user" ? actor.actorId : null,
5368
+ outcome: transition.decision.outcome,
5369
+ body: transition.decision.body,
5370
+ createdByRunId: actor.runId ?? null,
5371
+ });
5372
+ }
5373
+ }
5136
5374
  const updatedIssue = await svc.update(id, {
5137
- status: sourceIssueStatus,
5138
- ...(handBackAgentId ? { assigneeAgentId: handBackAgentId } : {}),
5375
+ ...updateFields,
5139
5376
  actorAgentId: actor.agentId ?? null,
5140
5377
  actorUserId: actor.actorType === "user" ? actor.actorId : null,
5141
5378
  }, tx, postCommitActivityPublications);
@@ -5143,10 +5380,17 @@ export function issueRoutes(db, storage, opts = {}) {
5143
5380
  throw notFound("Issue not found");
5144
5381
  issue = updatedIssue;
5145
5382
  }
5383
+ const recordedOutcome = outcome === "restored" && issue.status === "todo" &&
5384
+ activeRecoveryAction.returnOwnerAgentId != null &&
5385
+ issue.assigneeAgentId === activeRecoveryAction.returnOwnerAgentId
5386
+ ? "handed_back"
5387
+ : outcome === "restored" && issue.status === "done"
5388
+ ? "owner_completed"
5389
+ : outcome;
5146
5390
  const recoveryAction = await recoveryActionsSvc.resolveActiveForIssue({
5147
5391
  companyId: existing.companyId,
5148
5392
  sourceIssueId: existing.id,
5149
- actionId: actionId ?? null,
5393
+ actionId: activeRecoveryAction.id,
5150
5394
  status: actionStatus,
5151
5395
  outcome: recordedOutcome,
5152
5396
  resolutionNote: resolutionNote ?? null,
@@ -5974,8 +6218,101 @@ export function issueRoutes(db, storage, opts = {}) {
5974
6218
  actor,
5975
6219
  workProductChanged: true,
5976
6220
  });
6221
+ await materializeArtifactReviewDocumentBestEffort({ issue, workProduct: product, actor });
5977
6222
  res.status(201).json(product);
5978
6223
  });
6224
+ async function ensureArtifactReviewDocumentForWorkProduct(input) {
6225
+ const { issue, workProduct, actor } = input;
6226
+ const result = await artifactReviewDocumentsSvc.ensureForWorkProduct({
6227
+ issue: { id: issue.id, companyId: issue.companyId },
6228
+ workProduct,
6229
+ });
6230
+ if (!result.revisionChanged)
6231
+ return result;
6232
+ const doc = result.document;
6233
+ await issueReferencesSvc.syncDocument(doc.id);
6234
+ await externalObjectsSvc.syncDocumentSafely(doc.id);
6235
+ await logActivity(db, {
6236
+ companyId: issue.companyId,
6237
+ actorType: actor.actorType,
6238
+ actorId: actor.actorId,
6239
+ agentId: actor.agentId,
6240
+ runId: actor.runId,
6241
+ agentApiKeyId: actor.agentApiKeyId,
6242
+ action: result.created ? "issue.document_created" : "issue.document_updated",
6243
+ entityType: "issue",
6244
+ entityId: issue.id,
6245
+ details: {
6246
+ key: doc.key,
6247
+ documentId: doc.id,
6248
+ title: doc.title,
6249
+ format: doc.format,
6250
+ revisionNumber: doc.latestRevisionNumber,
6251
+ workProductId: workProduct.id,
6252
+ artifactReviewDocument: true,
6253
+ },
6254
+ });
6255
+ for (const remap of result.remappedAnnotations) {
6256
+ await logActivity(db, {
6257
+ companyId: issue.companyId,
6258
+ actorType: actor.actorType,
6259
+ actorId: actor.actorId,
6260
+ agentId: actor.agentId,
6261
+ runId: actor.runId,
6262
+ agentApiKeyId: actor.agentApiKeyId,
6263
+ action: "issue.document_annotation_remapped",
6264
+ entityType: "issue",
6265
+ entityId: issue.id,
6266
+ details: {
6267
+ key: doc.key,
6268
+ documentId: doc.id,
6269
+ threadId: remap.thread.id,
6270
+ revisionNumber: doc.latestRevisionNumber,
6271
+ anchorState: remap.thread.anchorState,
6272
+ anchorConfidence: remap.thread.anchorConfidence,
6273
+ snapshotId: remap.snapshot.id,
6274
+ },
6275
+ });
6276
+ }
6277
+ await revalidateActiveSourceRecoveryAfterCommittedWrite({
6278
+ issue,
6279
+ trigger: "document",
6280
+ actor,
6281
+ documentChanged: true,
6282
+ });
6283
+ return result;
6284
+ }
6285
+ async function materializeArtifactReviewDocumentBestEffort(input) {
6286
+ if (!isMarkdownArtifactWorkProduct(input.workProduct))
6287
+ return;
6288
+ try {
6289
+ await ensureArtifactReviewDocumentForWorkProduct(input);
6290
+ }
6291
+ catch (error) {
6292
+ // Work-product writes stay fail-open: raw open and download remain
6293
+ // available, and the explicit review-document endpoint is the retry path.
6294
+ logger.warn({ err: error, issueId: input.issue.id, workProductId: input.workProduct.id }, "markdown work product review-document materialization failed");
6295
+ }
6296
+ }
6297
+ router.post("/issues/:id/work-products/:workProductId/review-document", async (req, res) => {
6298
+ const id = req.params.id;
6299
+ const workProductId = req.params.workProductId;
6300
+ const issue = await getAccessibleResource(req, res, svc.getById(id), "Issue not found");
6301
+ if (!issue)
6302
+ return;
6303
+ if (!(await assertAgentIssueMutationAllowed(req, res, issue)))
6304
+ return;
6305
+ if (!(await assertDeliverableMutationAllowedByRunContext(req, res, issue)))
6306
+ return;
6307
+ const workProduct = await workProductsSvc.getById(workProductId);
6308
+ if (!workProduct || workProduct.issueId !== issue.id || workProduct.companyId !== issue.companyId) {
6309
+ res.status(404).json({ error: "Work product not found" });
6310
+ return;
6311
+ }
6312
+ const actor = getActorInfo(req);
6313
+ const result = await ensureArtifactReviewDocumentForWorkProduct({ issue, workProduct, actor });
6314
+ res.status(result.created ? 201 : 200).json(result.document);
6315
+ });
5979
6316
  router.post("/issues/:id/low-trust/promotions", validate(promoteLowTrustOutputSchema), async (req, res) => {
5980
6317
  const id = req.params.id;
5981
6318
  const issue = await getAccessibleResource(req, res, svc.getById(id), "Issue not found");
@@ -6164,6 +6501,11 @@ export function issueRoutes(db, storage, opts = {}) {
6164
6501
  actor,
6165
6502
  workProductChanged: true,
6166
6503
  });
6504
+ const reviewDocumentInputChanged = ["type", "provider", "metadata", "title", "createdByRunId"]
6505
+ .some((key) => Object.prototype.hasOwnProperty.call(patch, key));
6506
+ if (reviewDocumentInputChanged || sourceTrust) {
6507
+ await materializeArtifactReviewDocumentBestEffort({ issue, workProduct: product, actor });
6508
+ }
6167
6509
  res.json(product);
6168
6510
  });
6169
6511
  router.delete("/work-products/:id", async (req, res) => {
@@ -6434,7 +6776,7 @@ export function issueRoutes(db, storage, opts = {}) {
6434
6776
  });
6435
6777
  res.json({ ok: true });
6436
6778
  });
6437
- router.post("/companies/:companyId/issues", applyCreateIssueStatusDefault, validate(createIssueSchema), async (req, res) => {
6779
+ router.post("/companies/:companyId/issues", applyCreateIssueStatusDefault, validateIssueMutationBody(createIssueSchema), async (req, res) => {
6438
6780
  const companyId = req.params.companyId;
6439
6781
  assertCompanyAccess(req, companyId);
6440
6782
  if (isSkillTestScopedActor(req)) {
@@ -6742,7 +7084,7 @@ export function issueRoutes(db, storage, opts = {}) {
6742
7084
  referencedIssueIdentifiers: referenceSummary.outbound.map((item) => item.issue.identifier ?? item.issue.id),
6743
7085
  });
6744
7086
  });
6745
- router.post("/issues/:id/children", applyCreateIssueStatusDefault, validate(createChildIssueSchema), async (req, res) => {
7087
+ router.post("/issues/:id/children", applyCreateIssueStatusDefault, validateIssueMutationBody(createChildIssueSchema), async (req, res) => {
6746
7088
  const parentId = req.params.id;
6747
7089
  const parent = await getAccessibleResource(req, res, svc.getById(parentId), "Parent issue not found");
6748
7090
  if (!parent)
@@ -6916,7 +7258,7 @@ export function issueRoutes(db, storage, opts = {}) {
6916
7258
  const decompositions = await svc.listAcceptedPlanDecompositions(sourceIssue.id);
6917
7259
  res.json(decompositions);
6918
7260
  });
6919
- router.post("/issues/:id/accepted-plan-decompositions", validate(createAcceptedPlanDecompositionSchema), async (req, res) => {
7261
+ router.post("/issues/:id/accepted-plan-decompositions", validateIssueMutationBody(createAcceptedPlanDecompositionSchema), async (req, res) => {
6920
7262
  const sourceIssueId = req.params.id;
6921
7263
  const sourceIssue = await getAccessibleResource(req, res, svc.getById(sourceIssueId), "Issue not found");
6922
7264
  if (!sourceIssue)
@@ -7251,7 +7593,7 @@ export function issueRoutes(db, storage, opts = {}) {
7251
7593
  wakeQueued,
7252
7594
  });
7253
7595
  });
7254
- router.patch("/issues/:id", validate(updateIssueRouteSchema), async (req, res) => {
7596
+ router.patch("/issues/:id", validateIssueMutationBody(updateIssueRouteSchema), async (req, res) => {
7255
7597
  const id = req.params.id;
7256
7598
  const existing = await getAccessibleResource(req, res, svc.getById(id), "Issue not found");
7257
7599
  if (!existing)
@@ -7335,9 +7677,19 @@ export function issueRoutes(db, storage, opts = {}) {
7335
7677
  const activeRecoveryActionBeforeUpdate = recoveryRelevantSourceMutationRequested
7336
7678
  ? await recoveryActionsSvc.getActiveForIssue(existing.companyId, existing.id)
7337
7679
  : null;
7338
- if (recoveryRelevantSourceMutationRequested &&
7339
- !(await assertRecoveryActionAuthority(req, res, existing, activeRecoveryActionBeforeUpdate, { source: "issue_update" }))) {
7340
- return;
7680
+ if (recoveryRelevantSourceMutationRequested) {
7681
+ await requireRecoveryActionAuthority(req, existing, activeRecoveryActionBeforeUpdate, { source: "issue_update" });
7682
+ const recoveryRestrictedSourceMutationRequested = activeRecoveryActionBeforeUpdate != null &&
7683
+ (updateFields.status === "done" ||
7684
+ updateFields.status === "cancelled" ||
7685
+ normalizedAssigneeAgentId !== undefined ||
7686
+ req.body.assigneeUserId !== undefined ||
7687
+ (activeExecutionParticipantAgentId(existing) != null &&
7688
+ typeof updateFields.status === "string" &&
7689
+ updateFields.status !== existing.status));
7690
+ if (recoveryRestrictedSourceMutationRequested) {
7691
+ await requireRecoverySourceMutationAuthority(req, existing);
7692
+ }
7341
7693
  }
7342
7694
  if (resumeRequested !== true &&
7343
7695
  agentStatusTransitionRequiresResumeAuthority &&
@@ -8259,14 +8611,17 @@ export function issueRoutes(db, storage, opts = {}) {
8259
8611
  wakeups.set(`${agentId}:${wakeIssueId}`, { agentId, wakeup });
8260
8612
  };
8261
8613
  const addDependencyResolvedWakeup = async (input) => {
8262
- const idempotencyKey = buildIssueBlockersResolvedWakeIdempotencyKey({
8614
+ const idempotencyKey = buildIssueBlockersResolvedWakeStateKey({
8263
8615
  dependentIssueId: input.dependentIssueId,
8264
- resolvedBlockerIssueId: input.resolvedBlockerIssueId,
8616
+ blockerIssueIds: input.blockerIssueIds,
8617
+ blockedTransitionAt: input.blockedTransitionAt,
8265
8618
  });
8266
8619
  try {
8267
- const existingWake = await findExistingIssueBlockersResolvedWake(db, {
8620
+ const existingWake = await findExistingIssueBlockersResolvedWakeForReadyState(db, {
8268
8621
  companyId: issue.companyId,
8269
- idempotencyKey,
8622
+ dependentIssueId: input.dependentIssueId,
8623
+ blockerIssueIds: input.blockerIssueIds,
8624
+ blockedTransitionAt: input.blockedTransitionAt,
8270
8625
  });
8271
8626
  if (existingWake)
8272
8627
  return;
@@ -8442,6 +8797,7 @@ export function issueRoutes(db, storage, opts = {}) {
8442
8797
  dependentIssueId: dependent.id,
8443
8798
  resolvedBlockerIssueId: issue.id,
8444
8799
  blockerIssueIds: dependent.blockerIssueIds,
8800
+ blockedTransitionAt: dependent.blockedTransitionAt,
8445
8801
  source: "issue.blockers_resolved",
8446
8802
  mutation: "blocker_done",
8447
8803
  });
@@ -8463,6 +8819,7 @@ export function issueRoutes(db, storage, opts = {}) {
8463
8819
  dependentIssueId: issue.id,
8464
8820
  resolvedBlockerIssueId,
8465
8821
  blockerIssueIds: readiness.blockerIssueIds,
8822
+ blockedTransitionAt: issue.blockedTransitionAt,
8466
8823
  source: "issue.blockers_restored",
8467
8824
  mutation: "blocked_dependency_restored",
8468
8825
  });
@@ -8882,6 +9239,9 @@ export function issueRoutes(db, storage, opts = {}) {
8882
9239
  if (req.body.kind === "request_confirmation" && req.body.payload?.toolAction !== undefined) {
8883
9240
  throw unprocessable("payload.toolAction is server-owned metadata and cannot be supplied when creating an interaction");
8884
9241
  }
9242
+ if (req.body.kind === "request_confirmation" && req.body.payload?.secretProposal !== undefined) {
9243
+ throw unprocessable("payload.secretProposal is server-owned metadata and cannot be supplied when creating an interaction");
9244
+ }
8885
9245
  // Plan-document confirmation targets are validated authoritatively inside
8886
9246
  // issueThreadInteractionService.create, which re-reads the plan document's
8887
9247
  // latest revision and rejects a stale/missing target under the same insert
@@ -8987,6 +9347,9 @@ export function issueRoutes(db, storage, opts = {}) {
8987
9347
  const toolAction = interaction.payload && typeof interaction.payload === "object"
8988
9348
  ? interaction.payload.toolAction
8989
9349
  : null;
9350
+ const secretProposal = interaction.payload && typeof interaction.payload === "object"
9351
+ ? interaction.payload.secretProposal
9352
+ : null;
8990
9353
  let continuationInteraction = interaction;
8991
9354
  if (interaction.kind === "request_confirmation"
8992
9355
  && interaction.status === "accepted"
@@ -9021,6 +9384,63 @@ export function issueRoutes(db, storage, opts = {}) {
9021
9384
  };
9022
9385
  }
9023
9386
  }
9387
+ if (interaction.kind === "request_confirmation"
9388
+ && interaction.status === "accepted"
9389
+ && typeof secretProposal?.proposalId === "string") {
9390
+ const resolvedByUserId = actor.actorType === "user" ? actor.actorId : "board";
9391
+ try {
9392
+ if (opts.approveSecretProposal) {
9393
+ await opts.approveSecretProposal({
9394
+ companyId: issue.companyId,
9395
+ issueId: issue.id,
9396
+ interactionId: interaction.id,
9397
+ proposalId: secretProposal.proposalId,
9398
+ actor: { agentId: actor.agentId, userId: actor.actorType === "user" ? actor.actorId : null },
9399
+ });
9400
+ }
9401
+ else {
9402
+ const proposal = await secretProposals.getById(issue.companyId, secretProposal.proposalId);
9403
+ if (!proposal
9404
+ || proposal.kind !== "binding"
9405
+ || proposal.originIssueId !== issue.id
9406
+ || proposal.interactionId !== interaction.id) {
9407
+ throw notFound("Secret proposal not found");
9408
+ }
9409
+ await secretProposals.approve(issue.companyId, proposal.id, {
9410
+ resolvedByUserId,
9411
+ assertCanResolve: (lockedProposal, txDb) => assertCanResolveProposal({
9412
+ db: txDb,
9413
+ actor: req.actor,
9414
+ companyId: issue.companyId,
9415
+ proposal: lockedProposal,
9416
+ }),
9417
+ });
9418
+ await notifySecretProposalResolution({
9419
+ proposal,
9420
+ status: "approved",
9421
+ userId: resolvedByUserId,
9422
+ issues: svc,
9423
+ heartbeat,
9424
+ });
9425
+ }
9426
+ continuationInteraction = await interactionSvc.recordSecretProposalExecutionResult(issue, interaction.id, secretProposal.proposalId, { status: "executed" });
9427
+ }
9428
+ catch (error) {
9429
+ const errorCode = secretProposalExecutionErrorCode(error);
9430
+ continuationInteraction = await interactionSvc.recordSecretProposalExecutionResult(issue, interaction.id, secretProposal.proposalId, { status: "failed", errorCode });
9431
+ const recordedResult = readObject(continuationInteraction.result);
9432
+ const recordedSecretProposal = readObject(recordedResult.secretProposal);
9433
+ if (recordedSecretProposal.status !== "executed") {
9434
+ const configPath = typeof secretProposal.configPath === "string" ? secretProposal.configPath : "unknown";
9435
+ try {
9436
+ await svc.addComment(issue.id, `Secret binding execution failed\n\n- Config path: \`${configPath}\`\n- Error code: \`${errorCode}\`\n- Binding created: **no**`, { userId: resolvedByUserId });
9437
+ }
9438
+ catch (commentError) {
9439
+ logger.warn({ err: commentError, issueId: issue.id, interactionId: interaction.id, errorCode }, "failed to post secret proposal execution failure comment");
9440
+ }
9441
+ }
9442
+ }
9443
+ }
9024
9444
  const continuationWakeIssue = continuationIssue ?? issue;
9025
9445
  await logActivity(db, {
9026
9446
  companyId: issue.companyId,
@@ -10022,14 +10442,17 @@ export function issueRoutes(db, storage, opts = {}) {
10022
10442
  wakeups.set(key, { agentId, wakeup });
10023
10443
  };
10024
10444
  const addDependencyResolvedWakeup = async (input) => {
10025
- const idempotencyKey = buildIssueBlockersResolvedWakeIdempotencyKey({
10445
+ const idempotencyKey = buildIssueBlockersResolvedWakeStateKey({
10026
10446
  dependentIssueId: input.dependentIssueId,
10027
- resolvedBlockerIssueId: input.resolvedBlockerIssueId,
10447
+ blockerIssueIds: input.blockerIssueIds,
10448
+ blockedTransitionAt: input.blockedTransitionAt,
10028
10449
  });
10029
10450
  try {
10030
- const existingWake = await findExistingIssueBlockersResolvedWake(db, {
10451
+ const existingWake = await findExistingIssueBlockersResolvedWakeForReadyState(db, {
10031
10452
  companyId: currentIssue.companyId,
10032
- idempotencyKey,
10453
+ dependentIssueId: input.dependentIssueId,
10454
+ blockerIssueIds: input.blockerIssueIds,
10455
+ blockedTransitionAt: input.blockedTransitionAt,
10033
10456
  });
10034
10457
  if (existingWake)
10035
10458
  return;
@@ -10187,6 +10610,7 @@ export function issueRoutes(db, storage, opts = {}) {
10187
10610
  dependentIssueId: dependent.id,
10188
10611
  resolvedBlockerIssueId: currentIssue.id,
10189
10612
  blockerIssueIds: dependent.blockerIssueIds,
10613
+ blockedTransitionAt: dependent.blockedTransitionAt,
10190
10614
  });
10191
10615
  }
10192
10616
  }
@@ -10484,7 +10908,13 @@ export function issueRoutes(db, storage, opts = {}) {
10484
10908
  objectContentType: object.contentType,
10485
10909
  originalFilename: attachment.originalFilename,
10486
10910
  });
10487
- res.setHeader("Content-Type", responseContentType);
10911
+ // Markdown bodies are stored as UTF-8; declare the charset so inline
10912
+ // (raw) views do not mojibake. SVG/inline checks below stay on the bare type.
10913
+ const isMarkdownResponse = isMarkdownAttachmentContent({
10914
+ contentType: responseContentType,
10915
+ originalFilename: attachment.originalFilename,
10916
+ });
10917
+ res.setHeader("Content-Type", isMarkdownResponse ? `${responseContentType}; charset=utf-8` : responseContentType);
10488
10918
  res.setHeader("Cache-Control", "private, max-age=60");
10489
10919
  res.setHeader("X-Content-Type-Options", "nosniff");
10490
10920
  if (responseContentType === SVG_CONTENT_TYPE) {