@cats-inc/cats-platform 0.1.21 → 0.2.8

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 (448) hide show
  1. package/.env.example +3 -7
  2. package/README.md +18 -0
  3. package/build/desktop/bootstrapNavigation.d.ts.map +1 -1
  4. package/build/desktop/bootstrapNavigation.js +6 -13
  5. package/build/desktop/bootstrapNavigation.js.map +1 -1
  6. package/build/desktop/bootstrapPage.d.ts.map +1 -1
  7. package/build/desktop/bootstrapPage.js +114 -34
  8. package/build/desktop/bootstrapPage.js.map +1 -1
  9. package/build/desktop/cliInventoryProbe.d.ts +5 -2
  10. package/build/desktop/cliInventoryProbe.d.ts.map +1 -1
  11. package/build/desktop/cliInventoryProbe.js +14 -11
  12. package/build/desktop/cliInventoryProbe.js.map +1 -1
  13. package/build/desktop/config.d.ts +0 -4
  14. package/build/desktop/config.d.ts.map +1 -1
  15. package/build/desktop/config.js +1 -6
  16. package/build/desktop/config.js.map +1 -1
  17. package/build/desktop/contracts.d.ts +10 -2
  18. package/build/desktop/contracts.d.ts.map +1 -1
  19. package/build/desktop/contracts.js +1 -2
  20. package/build/desktop/contracts.js.map +1 -1
  21. package/build/desktop/hostVersion.d.ts.map +1 -1
  22. package/build/desktop/hostVersion.js +3 -5
  23. package/build/desktop/hostVersion.js.map +1 -1
  24. package/build/desktop/main.js +144 -249
  25. package/build/desktop/main.js.map +1 -1
  26. package/build/desktop/packaging.d.ts +2 -0
  27. package/build/desktop/packaging.d.ts.map +1 -1
  28. package/build/desktop/packaging.js +34 -14
  29. package/build/desktop/packaging.js.map +1 -1
  30. package/build/desktop/preload.cjs +7 -0
  31. package/build/desktop/preload.cjs.map +1 -1
  32. package/build/desktop/processSupervisor.d.ts.map +1 -1
  33. package/build/desktop/processSupervisor.js +3 -0
  34. package/build/desktop/processSupervisor.js.map +1 -1
  35. package/build/desktop/providerSelection.d.ts +37 -0
  36. package/build/desktop/providerSelection.d.ts.map +1 -0
  37. package/build/desktop/providerSelection.js +112 -0
  38. package/build/desktop/providerSelection.js.map +1 -0
  39. package/build/desktop/readiness.d.ts +2 -0
  40. package/build/desktop/readiness.d.ts.map +1 -1
  41. package/build/desktop/readiness.js +14 -33
  42. package/build/desktop/readiness.js.map +1 -1
  43. package/build/desktop/setupAssets.d.ts.map +1 -1
  44. package/build/desktop/setupAssets.js +16 -14
  45. package/build/desktop/setupAssets.js.map +1 -1
  46. package/build/desktop/setupAudit.d.ts +2 -3
  47. package/build/desktop/setupAudit.d.ts.map +1 -1
  48. package/build/desktop/setupAudit.js +14 -34
  49. package/build/desktop/setupAudit.js.map +1 -1
  50. package/build/desktop/updateDialog.d.ts +27 -1
  51. package/build/desktop/updateDialog.d.ts.map +1 -1
  52. package/build/desktop/updateDialog.js +85 -5
  53. package/build/desktop/updateDialog.js.map +1 -1
  54. package/build/renderer/assets/{ActiveSessionPermissionChip-DImsuR9T.js → ActiveSessionPermissionChip-CAgN-tXv.js} +1 -1
  55. package/build/renderer/assets/App-BZA23O2J.js +2 -0
  56. package/build/renderer/assets/App-DxsV-tKS.css +1 -0
  57. package/build/renderer/assets/{App-DKOSn0o_.js → App-YqprEL2n.js} +1 -1
  58. package/build/renderer/assets/App-rGw9J8PZ.js +1 -0
  59. package/build/renderer/assets/WorkspaceAppRoutes-BCHCikmJ.js +1 -0
  60. package/build/renderer/assets/app-BV3UAQkR.css +1 -0
  61. package/build/renderer/assets/app-CVPqqfZo.js +45 -0
  62. package/build/renderer/assets/conversationSidebarRecentEntries-iqaLqA1n.js +6 -0
  63. package/build/renderer/assets/{useDraftSessionChips-DCIId0eY.js → useDraftSessionChips-D9BX6X92.js} +1 -1
  64. package/build/renderer/index.html +2 -2
  65. package/build/server/app/renderer/providerCatalogClient.d.ts.map +1 -1
  66. package/build/server/app/renderer/providerCatalogClient.js +15 -6
  67. package/build/server/app/renderer/providerCatalogClient.js.map +1 -1
  68. package/build/server/app/renderer/providerRegistryClient.d.ts +1 -1
  69. package/build/server/app/renderer/providerRegistryClient.d.ts.map +1 -1
  70. package/build/server/app/renderer/providerRegistryClient.js +23 -41
  71. package/build/server/app/renderer/providerRegistryClient.js.map +1 -1
  72. package/build/server/app/server/appPackageRoutes.d.ts +2 -0
  73. package/build/server/app/server/appPackageRoutes.d.ts.map +1 -1
  74. package/build/server/app/server/appPackageRoutes.js +105 -0
  75. package/build/server/app/server/appPackageRoutes.js.map +1 -1
  76. package/build/server/app/server/authGatePolicy.js +7 -1
  77. package/build/server/app/server/authGatePolicy.js.map +1 -1
  78. package/build/server/app/server/contracts.d.ts +15 -0
  79. package/build/server/app/server/contracts.d.ts.map +1 -1
  80. package/build/server/app/server/dependencies.d.ts.map +1 -1
  81. package/build/server/app/server/dependencies.js +42 -1
  82. package/build/server/app/server/dependencies.js.map +1 -1
  83. package/build/server/app/server/goldenPathStartupResume.d.ts +55 -0
  84. package/build/server/app/server/goldenPathStartupResume.d.ts.map +1 -0
  85. package/build/server/app/server/goldenPathStartupResume.js +109 -0
  86. package/build/server/app/server/goldenPathStartupResume.js.map +1 -0
  87. package/build/server/app/server/platformSetupRoutes.d.ts.map +1 -1
  88. package/build/server/app/server/platformSetupRoutes.js +1 -13
  89. package/build/server/app/server/platformSetupRoutes.js.map +1 -1
  90. package/build/server/app/server/polling.d.ts.map +1 -1
  91. package/build/server/app/server/polling.js +2 -0
  92. package/build/server/app/server/polling.js.map +1 -1
  93. package/build/server/app/server/requestRouter.d.ts.map +1 -1
  94. package/build/server/app/server/requestRouter.js +30 -0
  95. package/build/server/app/server/requestRouter.js.map +1 -1
  96. package/build/server/app/server/runtimeSurfaceProxy.d.ts.map +1 -1
  97. package/build/server/app/server/runtimeSurfaceProxy.js +4 -2
  98. package/build/server/app/server/runtimeSurfaceProxy.js.map +1 -1
  99. package/build/server/app/server/startupRecovery.d.ts.map +1 -1
  100. package/build/server/app/server/startupRecovery.js +5 -0
  101. package/build/server/app/server/startupRecovery.js.map +1 -1
  102. package/build/server/app/server/telegramCommandSurface.d.ts +29 -0
  103. package/build/server/app/server/telegramCommandSurface.d.ts.map +1 -0
  104. package/build/server/app/server/telegramCommandSurface.js +123 -0
  105. package/build/server/app/server/telegramCommandSurface.js.map +1 -0
  106. package/build/server/app/server/transportWorkGoldenPath.d.ts +82 -0
  107. package/build/server/app/server/transportWorkGoldenPath.d.ts.map +1 -0
  108. package/build/server/app/server/transportWorkGoldenPath.js +307 -0
  109. package/build/server/app/server/transportWorkGoldenPath.js.map +1 -0
  110. package/build/server/config.d.ts +21 -0
  111. package/build/server/config.d.ts.map +1 -1
  112. package/build/server/config.js +12 -0
  113. package/build/server/config.js.map +1 -1
  114. package/build/server/design/components/providerModelFieldsSupport.d.ts +3 -3
  115. package/build/server/design/components/providerModelFieldsSupport.d.ts.map +1 -1
  116. package/build/server/design/components/providerModelFieldsSupport.js +37 -38
  117. package/build/server/design/components/providerModelFieldsSupport.js.map +1 -1
  118. package/build/server/design/components/useProviderCatalogState.d.ts +1 -0
  119. package/build/server/design/components/useProviderCatalogState.d.ts.map +1 -1
  120. package/build/server/design/components/useProviderCatalogState.js +1 -0
  121. package/build/server/design/components/useProviderCatalogState.js.map +1 -1
  122. package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts.map +1 -1
  123. package/build/server/design/components/useProviderRegistryAutoRecheck.js +20 -13
  124. package/build/server/design/components/useProviderRegistryAutoRecheck.js.map +1 -1
  125. package/build/server/design/components/useProviderRegistryState.d.ts.map +1 -1
  126. package/build/server/design/components/useProviderRegistryState.js +12 -25
  127. package/build/server/design/components/useProviderRegistryState.js.map +1 -1
  128. package/build/server/design/components/useProviderTargetReconciliation.d.ts.map +1 -1
  129. package/build/server/design/components/useProviderTargetReconciliation.js +2 -1
  130. package/build/server/design/components/useProviderTargetReconciliation.js.map +1 -1
  131. package/build/server/index.js +4 -0
  132. package/build/server/index.js.map +1 -1
  133. package/build/server/platform/apps/packageInstaller.d.ts +19 -0
  134. package/build/server/platform/apps/packageInstaller.d.ts.map +1 -0
  135. package/build/server/platform/apps/packageInstaller.js +92 -0
  136. package/build/server/platform/apps/packageInstaller.js.map +1 -0
  137. package/build/server/platform/apps/registry.d.ts +6 -0
  138. package/build/server/platform/apps/registry.d.ts.map +1 -1
  139. package/build/server/platform/apps/registry.js +24 -0
  140. package/build/server/platform/apps/registry.js.map +1 -1
  141. package/build/server/platform/apps/renderer.d.ts +6 -0
  142. package/build/server/platform/apps/renderer.d.ts.map +1 -0
  143. package/build/server/platform/apps/renderer.js +18 -0
  144. package/build/server/platform/apps/renderer.js.map +1 -0
  145. package/build/server/platform/runtime/deliveryClient.d.ts +111 -0
  146. package/build/server/platform/runtime/deliveryClient.d.ts.map +1 -0
  147. package/build/server/platform/runtime/deliveryClient.js +289 -0
  148. package/build/server/platform/runtime/deliveryClient.js.map +1 -0
  149. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts +1 -0
  150. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts.map +1 -1
  151. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js +10 -0
  152. package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js.map +1 -1
  153. package/build/server/platform/supervision/runtimeBoundary.d.ts +11 -1
  154. package/build/server/platform/supervision/runtimeBoundary.d.ts.map +1 -1
  155. package/build/server/platform/supervision/runtimeBoundary.js +2 -2
  156. package/build/server/platform/supervision/runtimeBoundary.js.map +1 -1
  157. package/build/server/platform/transports/telegram/bridge.d.ts +18 -0
  158. package/build/server/platform/transports/telegram/bridge.d.ts.map +1 -1
  159. package/build/server/platform/transports/telegram/bridge.js +176 -0
  160. package/build/server/platform/transports/telegram/bridge.js.map +1 -1
  161. package/build/server/platform/transports/telegram/commandPort.d.ts +40 -0
  162. package/build/server/platform/transports/telegram/commandPort.d.ts.map +1 -0
  163. package/build/server/platform/transports/telegram/commandPort.js +17 -0
  164. package/build/server/platform/transports/telegram/commandPort.js.map +1 -0
  165. package/build/server/platform/transports/telegram/commandRouter.d.ts +22 -0
  166. package/build/server/platform/transports/telegram/commandRouter.d.ts.map +1 -1
  167. package/build/server/platform/transports/telegram/commandRouter.js.map +1 -1
  168. package/build/server/platform/transports/telegram/commands/index.d.ts.map +1 -1
  169. package/build/server/platform/transports/telegram/commands/index.js +38 -0
  170. package/build/server/platform/transports/telegram/commands/index.js.map +1 -1
  171. package/build/server/platform/transports/telegram/ingressDispatch.d.ts +56 -0
  172. package/build/server/platform/transports/telegram/ingressDispatch.d.ts.map +1 -0
  173. package/build/server/platform/transports/telegram/ingressDispatch.js +93 -0
  174. package/build/server/platform/transports/telegram/ingressDispatch.js.map +1 -0
  175. package/build/server/platform/transports/telegram/polling.d.ts +34 -0
  176. package/build/server/platform/transports/telegram/polling.d.ts.map +1 -1
  177. package/build/server/platform/transports/telegram/polling.js +86 -9
  178. package/build/server/platform/transports/telegram/polling.js.map +1 -1
  179. package/build/server/platform/transports/telegram/relay/index.d.ts +3 -0
  180. package/build/server/platform/transports/telegram/relay/index.d.ts.map +1 -1
  181. package/build/server/platform/transports/telegram/relay/index.js +5 -1
  182. package/build/server/platform/transports/telegram/relay/index.js.map +1 -1
  183. package/build/server/platform/transports/telegram/relay/ingress.d.ts +2 -0
  184. package/build/server/platform/transports/telegram/relay/ingress.d.ts.map +1 -1
  185. package/build/server/platform/transports/telegram/relay/ingress.js +1 -1
  186. package/build/server/platform/transports/telegram/relay/ingress.js.map +1 -1
  187. package/build/server/platform/transports/work-delivery/actionTokens.d.ts +66 -0
  188. package/build/server/platform/transports/work-delivery/actionTokens.d.ts.map +1 -0
  189. package/build/server/platform/transports/work-delivery/actionTokens.js +145 -0
  190. package/build/server/platform/transports/work-delivery/actionTokens.js.map +1 -0
  191. package/build/server/platform/transports/work-delivery/contracts.d.ts +172 -0
  192. package/build/server/platform/transports/work-delivery/contracts.d.ts.map +1 -0
  193. package/build/server/platform/transports/work-delivery/contracts.js +26 -0
  194. package/build/server/platform/transports/work-delivery/contracts.js.map +1 -0
  195. package/build/server/platform/transports/work-delivery/deliveryGates.d.ts +32 -0
  196. package/build/server/platform/transports/work-delivery/deliveryGates.d.ts.map +1 -0
  197. package/build/server/platform/transports/work-delivery/deliveryGates.js +45 -0
  198. package/build/server/platform/transports/work-delivery/deliveryGates.js.map +1 -0
  199. package/build/server/platform/transports/work-delivery/inboundClassification.d.ts +31 -0
  200. package/build/server/platform/transports/work-delivery/inboundClassification.d.ts.map +1 -0
  201. package/build/server/platform/transports/work-delivery/inboundClassification.js +52 -0
  202. package/build/server/platform/transports/work-delivery/inboundClassification.js.map +1 -0
  203. package/build/server/platform/transports/work-delivery/outbox.d.ts +84 -0
  204. package/build/server/platform/transports/work-delivery/outbox.d.ts.map +1 -0
  205. package/build/server/platform/transports/work-delivery/outbox.js +270 -0
  206. package/build/server/platform/transports/work-delivery/outbox.js.map +1 -0
  207. package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts +57 -0
  208. package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts.map +1 -0
  209. package/build/server/platform/transports/work-delivery/permissionEnvelope.js +74 -0
  210. package/build/server/platform/transports/work-delivery/permissionEnvelope.js.map +1 -0
  211. package/build/server/platform/transports/work-delivery/port.d.ts +60 -0
  212. package/build/server/platform/transports/work-delivery/port.d.ts.map +1 -0
  213. package/build/server/platform/transports/work-delivery/port.js +15 -0
  214. package/build/server/platform/transports/work-delivery/port.js.map +1 -0
  215. package/build/server/platform/transports/work-delivery/proposal.d.ts +31 -0
  216. package/build/server/platform/transports/work-delivery/proposal.d.ts.map +1 -0
  217. package/build/server/platform/transports/work-delivery/proposal.js +59 -0
  218. package/build/server/platform/transports/work-delivery/proposal.js.map +1 -0
  219. package/build/server/platform/transports/work-delivery/readiness.d.ts +61 -0
  220. package/build/server/platform/transports/work-delivery/readiness.d.ts.map +1 -0
  221. package/build/server/platform/transports/work-delivery/readiness.js +123 -0
  222. package/build/server/platform/transports/work-delivery/readiness.js.map +1 -0
  223. package/build/server/platform/transports/work-delivery/stageProjection.d.ts +43 -0
  224. package/build/server/platform/transports/work-delivery/stageProjection.d.ts.map +1 -0
  225. package/build/server/platform/transports/work-delivery/stageProjection.js +132 -0
  226. package/build/server/platform/transports/work-delivery/stageProjection.js.map +1 -0
  227. package/build/server/platform/transports/work-delivery/stateStore.d.ts +21 -0
  228. package/build/server/platform/transports/work-delivery/stateStore.d.ts.map +1 -0
  229. package/build/server/platform/transports/work-delivery/stateStore.js +193 -0
  230. package/build/server/platform/transports/work-delivery/stateStore.js.map +1 -0
  231. package/build/server/platform/transports/work-delivery/telemetry.d.ts +49 -0
  232. package/build/server/platform/transports/work-delivery/telemetry.d.ts.map +1 -0
  233. package/build/server/platform/transports/work-delivery/telemetry.js +80 -0
  234. package/build/server/platform/transports/work-delivery/telemetry.js.map +1 -0
  235. package/build/server/products/chat/api/contracts.d.ts +10 -0
  236. package/build/server/products/chat/api/contracts.d.ts.map +1 -1
  237. package/build/server/products/chat/api/resources/channelRoutes.d.ts.map +1 -1
  238. package/build/server/products/chat/api/resources/channelRoutes.js +27 -1
  239. package/build/server/products/chat/api/resources/channelRoutes.js.map +1 -1
  240. package/build/server/products/chat/api/routeSupport.d.ts +8 -0
  241. package/build/server/products/chat/api/routeSupport.d.ts.map +1 -1
  242. package/build/server/products/chat/api/routeSupport.js +5 -4
  243. package/build/server/products/chat/api/routeSupport.js.map +1 -1
  244. package/build/server/products/chat/api/setupRoutes.d.ts.map +1 -1
  245. package/build/server/products/chat/api/setupRoutes.js +23 -8
  246. package/build/server/products/chat/api/setupRoutes.js.map +1 -1
  247. package/build/server/products/chat/state/model/channelExecutionTargets.d.ts +15 -0
  248. package/build/server/products/chat/state/model/channelExecutionTargets.d.ts.map +1 -1
  249. package/build/server/products/chat/state/model/channelExecutionTargets.js +25 -0
  250. package/build/server/products/chat/state/model/channelExecutionTargets.js.map +1 -1
  251. package/build/server/products/chat/state/model/index.d.ts +1 -1
  252. package/build/server/products/chat/state/model/index.d.ts.map +1 -1
  253. package/build/server/products/chat/state/model/index.js +1 -1
  254. package/build/server/products/chat/state/model/index.js.map +1 -1
  255. package/build/server/products/code/api/relayRoutes.d.ts.map +1 -1
  256. package/build/server/products/code/api/relayRoutes.js +36 -7
  257. package/build/server/products/code/api/relayRoutes.js.map +1 -1
  258. package/build/server/products/code/state/relayState.d.ts +2 -1
  259. package/build/server/products/code/state/relayState.d.ts.map +1 -1
  260. package/build/server/products/code/state/relayState.js +7 -7
  261. package/build/server/products/code/state/relayState.js.map +1 -1
  262. package/build/server/products/shared/api/workspaceContracts.d.ts +5 -0
  263. package/build/server/products/shared/api/workspaceContracts.d.ts.map +1 -1
  264. package/build/server/products/work/api/goldenPathProjection.d.ts +156 -0
  265. package/build/server/products/work/api/goldenPathProjection.d.ts.map +1 -0
  266. package/build/server/products/work/api/goldenPathProjection.js +252 -0
  267. package/build/server/products/work/api/goldenPathProjection.js.map +1 -0
  268. package/build/server/products/work/api/index.d.ts +54 -1
  269. package/build/server/products/work/api/index.d.ts.map +1 -1
  270. package/build/server/products/work/api/index.js +182 -4
  271. package/build/server/products/work/api/index.js.map +1 -1
  272. package/build/server/products/work/api/projection.d.ts +7 -1
  273. package/build/server/products/work/api/projection.d.ts.map +1 -1
  274. package/build/server/products/work/api/projection.js +7 -1
  275. package/build/server/products/work/api/projection.js.map +1 -1
  276. package/build/server/products/work/shared/apiPaths.d.ts +8 -0
  277. package/build/server/products/work/shared/apiPaths.d.ts.map +1 -1
  278. package/build/server/products/work/shared/apiPaths.js +8 -0
  279. package/build/server/products/work/shared/apiPaths.js.map +1 -1
  280. package/build/server/products/work/shared/workGoldenPathMessages.d.ts +83 -0
  281. package/build/server/products/work/shared/workGoldenPathMessages.d.ts.map +1 -0
  282. package/build/server/products/work/shared/workGoldenPathMessages.js +211 -0
  283. package/build/server/products/work/shared/workGoldenPathMessages.js.map +1 -0
  284. package/build/server/products/work/shared/workGoldenPathMetadata.d.ts +45 -0
  285. package/build/server/products/work/shared/workGoldenPathMetadata.d.ts.map +1 -0
  286. package/build/server/products/work/shared/workGoldenPathMetadata.js +122 -0
  287. package/build/server/products/work/shared/workGoldenPathMetadata.js.map +1 -0
  288. package/build/server/products/work/state/workCompletionEvidence.d.ts +42 -0
  289. package/build/server/products/work/state/workCompletionEvidence.d.ts.map +1 -0
  290. package/build/server/products/work/state/workCompletionEvidence.js +59 -0
  291. package/build/server/products/work/state/workCompletionEvidence.js.map +1 -0
  292. package/build/server/products/work/state/workExecutionTaskDelegate.d.ts +16 -0
  293. package/build/server/products/work/state/workExecutionTaskDelegate.d.ts.map +1 -1
  294. package/build/server/products/work/state/workExecutionTaskDelegate.js +91 -68
  295. package/build/server/products/work/state/workExecutionTaskDelegate.js.map +1 -1
  296. package/build/server/products/work/state/workGoldenPathAdmission.d.ts +78 -0
  297. package/build/server/products/work/state/workGoldenPathAdmission.d.ts.map +1 -0
  298. package/build/server/products/work/state/workGoldenPathAdmission.js +249 -0
  299. package/build/server/products/work/state/workGoldenPathAdmission.js.map +1 -0
  300. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts +33 -0
  301. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts.map +1 -0
  302. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js +161 -0
  303. package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js.map +1 -0
  304. package/build/server/products/work/state/workGoldenPathIntake.d.ts +34 -0
  305. package/build/server/products/work/state/workGoldenPathIntake.d.ts.map +1 -0
  306. package/build/server/products/work/state/workGoldenPathIntake.js +50 -0
  307. package/build/server/products/work/state/workGoldenPathIntake.js.map +1 -0
  308. package/build/server/products/work/state/workGoldenPathLifecycle.d.ts +44 -0
  309. package/build/server/products/work/state/workGoldenPathLifecycle.d.ts.map +1 -0
  310. package/build/server/products/work/state/workGoldenPathLifecycle.js +146 -0
  311. package/build/server/products/work/state/workGoldenPathLifecycle.js.map +1 -0
  312. package/build/server/products/work/state/workGoldenPathPublish.d.ts +84 -0
  313. package/build/server/products/work/state/workGoldenPathPublish.d.ts.map +1 -0
  314. package/build/server/products/work/state/workGoldenPathPublish.js +488 -0
  315. package/build/server/products/work/state/workGoldenPathPublish.js.map +1 -0
  316. package/build/server/products/work/state/workGoldenPathRunner.d.ts +121 -0
  317. package/build/server/products/work/state/workGoldenPathRunner.d.ts.map +1 -0
  318. package/build/server/products/work/state/workGoldenPathRunner.js +447 -0
  319. package/build/server/products/work/state/workGoldenPathRunner.js.map +1 -0
  320. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts +74 -0
  321. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts.map +1 -0
  322. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js +239 -0
  323. package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js.map +1 -0
  324. package/build/server/products/work/state/workGoldenPathService.d.ts +152 -0
  325. package/build/server/products/work/state/workGoldenPathService.d.ts.map +1 -0
  326. package/build/server/products/work/state/workGoldenPathService.js +1057 -0
  327. package/build/server/products/work/state/workGoldenPathService.js.map +1 -0
  328. package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts +87 -0
  329. package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts.map +1 -0
  330. package/build/server/products/work/state/workGoldenPathTelegramPort.js +220 -0
  331. package/build/server/products/work/state/workGoldenPathTelegramPort.js.map +1 -0
  332. package/build/server/runtime/client.d.ts +13 -0
  333. package/build/server/runtime/client.d.ts.map +1 -1
  334. package/build/server/runtime/client.js +39 -0
  335. package/build/server/runtime/client.js.map +1 -1
  336. package/build/server/runtime/setup.d.ts +10 -4
  337. package/build/server/runtime/setup.d.ts.map +1 -1
  338. package/build/server/runtime/setup.js +14 -18
  339. package/build/server/runtime/setup.js.map +1 -1
  340. package/build/server/runtime/usageSnapshot.d.ts +3 -0
  341. package/build/server/runtime/usageSnapshot.d.ts.map +1 -0
  342. package/build/server/runtime/usageSnapshot.js +69 -0
  343. package/build/server/runtime/usageSnapshot.js.map +1 -0
  344. package/build/server/server/routes/providerCapabilityBootstrap.d.ts +69 -0
  345. package/build/server/server/routes/providerCapabilityBootstrap.d.ts.map +1 -0
  346. package/build/server/server/routes/providerCapabilityBootstrap.js +165 -0
  347. package/build/server/server/routes/providerCapabilityBootstrap.js.map +1 -0
  348. package/build/server/server/routes/providers.d.ts.map +1 -1
  349. package/build/server/server/routes/providers.js +112 -81
  350. package/build/server/server/routes/providers.js.map +1 -1
  351. package/build/server/server/routes/telegram.d.ts +9 -0
  352. package/build/server/server/routes/telegram.d.ts.map +1 -1
  353. package/build/server/server/routes/telegram.js +79 -72
  354. package/build/server/server/routes/telegram.js.map +1 -1
  355. package/build/server/shared/catsAppManifest.d.ts +3 -1
  356. package/build/server/shared/catsAppManifest.d.ts.map +1 -1
  357. package/build/server/shared/catsAppManifest.js +2 -0
  358. package/build/server/shared/catsAppManifest.js.map +1 -1
  359. package/build/server/shared/executionLabel.d.ts.map +1 -1
  360. package/build/server/shared/executionLabel.js +8 -1
  361. package/build/server/shared/executionLabel.js.map +1 -1
  362. package/build/server/shared/i18n/catalogs/en.d.ts.map +1 -1
  363. package/build/server/shared/i18n/catalogs/en.js +198 -3
  364. package/build/server/shared/i18n/catalogs/en.js.map +1 -1
  365. package/build/server/shared/i18n/catalogs/zh-TW.d.ts.map +1 -1
  366. package/build/server/shared/i18n/catalogs/zh-TW.js +197 -3
  367. package/build/server/shared/i18n/catalogs/zh-TW.js.map +1 -1
  368. package/build/server/shared/i18n/index.d.ts +192 -0
  369. package/build/server/shared/i18n/index.d.ts.map +1 -1
  370. package/build/server/shared/i18n/messageKeys.d.ts +192 -0
  371. package/build/server/shared/i18n/messageKeys.d.ts.map +1 -1
  372. package/build/server/shared/i18n/messageKeys.js +192 -0
  373. package/build/server/shared/i18n/messageKeys.js.map +1 -1
  374. package/build/server/shared/platformSetupOperation.d.ts +2 -0
  375. package/build/server/shared/platformSetupOperation.d.ts.map +1 -0
  376. package/build/server/shared/platformSetupOperation.js +9 -0
  377. package/build/server/shared/platformSetupOperation.js.map +1 -0
  378. package/build/server/shared/providerCatalog.d.ts +5 -2
  379. package/build/server/shared/providerCatalog.d.ts.map +1 -1
  380. package/build/server/shared/providerCatalog.js +6 -0
  381. package/build/server/shared/providerCatalog.js.map +1 -1
  382. package/build/server/shared/providerCatalogData.d.ts +28 -22
  383. package/build/server/shared/providerCatalogData.d.ts.map +1 -1
  384. package/build/server/shared/providerCatalogData.js +22 -10
  385. package/build/server/shared/providerCatalogData.js.map +1 -1
  386. package/build/server/shared/providerCatalogInstances.d.ts.map +1 -1
  387. package/build/server/shared/providerCatalogInstances.js +3 -0
  388. package/build/server/shared/providerCatalogInstances.js.map +1 -1
  389. package/build/server/shared/providerModelLabelRegistry.d.ts +29 -0
  390. package/build/server/shared/providerModelLabelRegistry.d.ts.map +1 -0
  391. package/build/server/shared/providerModelLabelRegistry.js +49 -0
  392. package/build/server/shared/providerModelLabelRegistry.js.map +1 -0
  393. package/build/server/shared/providerSelection.d.ts.map +1 -1
  394. package/build/server/shared/providerSelection.js +10 -13
  395. package/build/server/shared/providerSelection.js.map +1 -1
  396. package/build/server/shared/runtimeSetup.d.ts +17 -4
  397. package/build/server/shared/runtimeSetup.d.ts.map +1 -1
  398. package/build/server/shared/runtimeStatusPresentation.js +1 -1
  399. package/build/server/shared/runtimeStatusPresentation.js.map +1 -1
  400. package/package.json +23 -1
  401. package/packages/app-sdk/browser.d.ts +51 -0
  402. package/packages/app-sdk/browser.js +48 -0
  403. package/packages/app-sdk/package.d.ts +14 -0
  404. package/packages/app-sdk/package.js +131 -0
  405. package/packages/app-sdk/package.json +7 -0
  406. package/scripts/README.md +43 -26
  407. package/scripts/build-desktop-installer.mjs +15 -0
  408. package/scripts/build-test-ui.mjs +1 -1
  409. package/scripts/bundle-server.mjs +23 -10
  410. package/scripts/linux/build-desktop-package.sh +6 -6
  411. package/scripts/linux/install-antigravity.sh +0 -0
  412. package/scripts/linux/install-devin.sh +0 -0
  413. package/scripts/linux/install-meta-cli.sh +8 -0
  414. package/scripts/linux/provider-cli-common.sh +103 -30
  415. package/scripts/linux/sync-agent-skills.sh +27 -5
  416. package/scripts/linux/test-linux-package-smoke.sh +4 -3
  417. package/scripts/linux/upgrade-cli-tools.sh +1 -1
  418. package/scripts/macos/build-desktop-package.sh +5 -5
  419. package/scripts/macos/install-antigravity.sh +0 -0
  420. package/scripts/macos/install-devin.sh +0 -0
  421. package/scripts/macos/install-meta-cli.sh +8 -0
  422. package/scripts/macos/provider-cli-common.sh +103 -30
  423. package/scripts/macos/sync-agent-skills.sh +27 -5
  424. package/scripts/macos/test-macos-package-smoke.sh +4 -3
  425. package/scripts/macos/upgrade-cli-tools.sh +1 -1
  426. package/scripts/package-desktop.mjs +15 -2
  427. package/scripts/testing/check-usage-app.mts +266 -0
  428. package/scripts/testing/usage-app-smoke-electron.cjs +10 -0
  429. package/scripts/testing/usage-app-smoke-renderer.tsx +8 -0
  430. package/scripts/verify-desktop-app-bundle.mjs +108 -0
  431. package/scripts/windows/Build-DesktopPackage.ps1 +9 -6
  432. package/scripts/windows/{Install-Aider.ps1 → Install-MetaCli.ps1} +180 -92
  433. package/scripts/windows/Install-Ollama.ps1 +4 -1
  434. package/scripts/windows/Sync-AgentSkills.ps1 +29 -6
  435. package/scripts/windows/Test-WindowsInstallerSmoke.ps1 +2 -1
  436. package/build/desktop/updateNotifications.d.ts +0 -64
  437. package/build/desktop/updateNotifications.d.ts.map +0 -1
  438. package/build/desktop/updateNotifications.js +0 -143
  439. package/build/desktop/updateNotifications.js.map +0 -1
  440. package/build/renderer/assets/App-BfOvMP2m.js +0 -1
  441. package/build/renderer/assets/App-DGwkRFZl.css +0 -1
  442. package/build/renderer/assets/App-DWd7I3Z2.js +0 -2
  443. package/build/renderer/assets/WorkspaceAppRoutes-CrbTA9En.js +0 -1
  444. package/build/renderer/assets/app-BTww3Hl4.js +0 -43
  445. package/build/renderer/assets/app-CPxWQwLA.css +0 -1
  446. package/build/renderer/assets/conversationSidebarRecentEntries-QuaOPxCb.js +0 -6
  447. package/scripts/linux/install-aider.sh +0 -8
  448. package/scripts/macos/install-aider.sh +0 -8
@@ -0,0 +1,1057 @@
1
+ /**
2
+ * Transport work-delivery golden path (SPEC-114, ADR-112).
3
+ *
4
+ * This is the product-owned coordinator: it owns intent capture, scope
5
+ * versioning, authorization, completion judgment, and delivery policy, and it
6
+ * owns none of the transport mechanics or provider execution. The transport
7
+ * calls in through `TransportWorkGoldenPathPort`; the supervised run reports
8
+ * execution facts in and receives no authority in return.
9
+ *
10
+ * Every method here is idempotent by construction. That is not defensive
11
+ * programming: a Telegram callback can arrive twice, a process can restart
12
+ * mid-flight, and either must converge on the same Task, Run, and single
13
+ * outbound message.
14
+ */
15
+ import { appendCoreActivity, upsertCoreArtifact, upsertCoreOutcome, upsertCoreRun, upsertCoreTask, upsertCoreWorkItem, } from '../../../core/model/index.js';
16
+ import { createTransportWorkActionTokenStore, encodeTransportWorkCallbackData, isTransportWorkCallbackDataWithinLimit, } from '../../../platform/transports/work-delivery/actionTokens.js';
17
+ import { resolveOutstandingDeliveryGates } from '../../../platform/transports/work-delivery/deliveryGates.js';
18
+ import { buildTransportWorkDeliveryKey, } from '../../../platform/transports/work-delivery/outbox.js';
19
+ import { buildTransportWorkProposal } from '../../../platform/transports/work-delivery/proposal.js';
20
+ import { projectTransportWorkStage } from '../../../platform/transports/work-delivery/stageProjection.js';
21
+ import { readWorkGoldenPathMetadata, writeWorkGoldenPathMetadata, } from '../shared/workGoldenPathMetadata.js';
22
+ import { captureGoldenPathWorkItem } from './workGoldenPathIntake.js';
23
+ import { evaluateWorkCompletionEvidence, } from './workCompletionEvidence.js';
24
+ import { admitTransportWorkExecution, buildWorkGoldenPathAdmissionKey, resolveWorkGoldenPathRunId, } from './workGoldenPathAdmission.js';
25
+ import { applyWorkGoldenPathLifecycleAction } from './workGoldenPathLifecycle.js';
26
+ import { applyWorkGoldenPathPublishDecision, requestWorkGoldenPathPublishApproval, WORK_GOLDEN_PATH_PUBLISH_METADATA_KEY, } from './workGoldenPathPublish.js';
27
+ import { assertSafeTransportPayload, createWorkGoldenPathTranslator, describeDeliverySideEffectsLocalized, localizeTransportWorkAction, renderAcceptedMessage, renderDecisionMessage, renderNotReadyMessage, renderProgressMessage, renderProposalMessage, renderRefusalMessage, renderResultMessage, } from '../shared/workGoldenPathMessages.js';
28
+ import { messageKeys } from '../../../shared/i18n/index.js';
29
+ /**
30
+ * Outbox rows are keyed by work item, but a refusal happens before any work
31
+ * item exists. A synthetic key keeps refusals in the same durable, idempotent
32
+ * pipeline instead of becoming an untracked side-channel send.
33
+ */
34
+ function pendingWorkItemKey(externalUpdateRef) {
35
+ return `pending:${externalUpdateRef}`;
36
+ }
37
+ const DEFAULT_ACTOR_REF = 'actor-work-golden-path';
38
+ /** Actions offered alongside a fresh proposal. */
39
+ /**
40
+ * Actions a proposal offers.
41
+ *
42
+ * `adjust` is deliberately absent. It was offered on every proposal but
43
+ * `authorize` never handled it, so tapping it returned `action_not_allowed` — a
44
+ * button that refuses is worse than no button. Restoring it needs the FR-16
45
+ * clarification loop: intake captures per Telegram update ref, so a follow-up
46
+ * message currently opens a *parallel* Work Item instead of revising this one.
47
+ */
48
+ const PROPOSAL_ACTIONS = ['start_work', 'cancel'];
49
+ /** Offered alongside a gated result preview (FR-41). */
50
+ const PUBLISH_DECISION_ACTIONS = ['publish', 'deny'];
51
+ /**
52
+ * Gates cleared by a completed publish approval.
53
+ *
54
+ * Reading the approval Task rather than a flag is deliberate: the authoritative
55
+ * record of "the owner allowed this" is the Core approval, so nothing can clear
56
+ * a gate without leaving one (FR-40).
57
+ */
58
+ function readSatisfiedGates(core, workItemId) {
59
+ const approvalTask = core.tasks.find((task) => {
60
+ const envelope = task.metadata[WORK_GOLDEN_PATH_PUBLISH_METADATA_KEY];
61
+ return typeof envelope === 'object'
62
+ && envelope !== null
63
+ && !Array.isArray(envelope)
64
+ && envelope.workItemId === workItemId;
65
+ });
66
+ if (!approvalTask || approvalTask.approval.status !== 'approved') {
67
+ return [];
68
+ }
69
+ const envelope = approvalTask.metadata[WORK_GOLDEN_PATH_PUBLISH_METADATA_KEY];
70
+ return Array.isArray(envelope.effectiveGates)
71
+ ? envelope.effectiveGates
72
+ : [];
73
+ }
74
+ export function createWorkGoldenPathService(options) {
75
+ const now = options.now ?? (() => new Date());
76
+ const actorRef = options.actorRef ?? DEFAULT_ACTOR_REF;
77
+ const telemetry = options.telemetry;
78
+ const tokenStore = options.tokenStore ?? createTransportWorkActionTokenStore({ now });
79
+ const { coreStore, outbox } = options;
80
+ async function buildStage(workItemId) {
81
+ return projectStageFromCore(await coreStore.readCore(), workItemId);
82
+ }
83
+ /**
84
+ * The stage derivation, synchronous over a snapshot the caller already holds.
85
+ *
86
+ * `authorize` needs it inside a synchronous scope resolver, and deriving the
87
+ * allowed actions from anywhere other than the stage is how `publish` and
88
+ * `deny` silently stopped being accepted.
89
+ */
90
+ function projectStageFromCore(core, workItemId) {
91
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId) ?? null;
92
+ if (workItem === null) {
93
+ return null;
94
+ }
95
+ const metadata = readWorkGoldenPathMetadata(workItem.metadata);
96
+ const proposal = metadata?.proposal ?? null;
97
+ const task = workItem.taskId
98
+ ? core.tasks.find((candidate) => candidate.id === workItem.taskId) ?? null
99
+ : null;
100
+ const run = task
101
+ ? core.runs.find((candidate) => candidate.taskId === task.id) ?? null
102
+ : null;
103
+ const outcome = run
104
+ ? core.outcomes.find((candidate) => candidate.runId === run.id) ?? null
105
+ : null;
106
+ const artifacts = core.artifacts.filter((artifact) => artifact.workItemId === workItem.id);
107
+ const commitId = readCommitId(outcome?.metadata);
108
+ const outstandingGates = proposal === null
109
+ ? []
110
+ : resolveOutstandingDeliveryGates({
111
+ deliveryMode: proposal.deliveryMode,
112
+ effectiveGates: proposal.deliveryGates,
113
+ // A completed publish approval is what clears the gates; nothing else
114
+ // may do it implicitly (FR-40).
115
+ satisfiedGates: readSatisfiedGates(core, workItem.id),
116
+ publishesPublicArtifact: false,
117
+ });
118
+ return projectTransportWorkStage({
119
+ workItem,
120
+ proposal,
121
+ task,
122
+ run,
123
+ outcome,
124
+ artifacts,
125
+ commitId,
126
+ outstandingGates,
127
+ deliveryRows: outbox.list(workItem.id),
128
+ awaitingOwnerDecision: task?.approval.status === 'pending' && run !== null,
129
+ });
130
+ }
131
+ function offerActions(input) {
132
+ return input.actions.map((action) => {
133
+ const token = tokenStore.issue({
134
+ bindingId: input.bindingId,
135
+ ownerActorId: input.ownerActorId,
136
+ externalUserRef: input.externalUserRef,
137
+ workItemId: input.workItemId,
138
+ proposalRevision: input.proposal.revision,
139
+ proposalDigest: input.proposal.digest,
140
+ action,
141
+ });
142
+ const callbackData = encodeTransportWorkCallbackData(token.token);
143
+ if (!isTransportWorkCallbackDataWithinLimit(callbackData)) {
144
+ // Would be silently dropped by Telegram; fail loudly at build time.
145
+ throw new Error(`Golden-path callback data exceeds the Telegram limit: ${callbackData}`);
146
+ }
147
+ return { action, callbackData, label: localizeTransportWorkAction(input.t, action) };
148
+ });
149
+ }
150
+ async function enqueueAndFlush(input, defer = false) {
151
+ // Enforced here rather than in each renderer so a future caller cannot
152
+ // bypass it by assembling a payload of its own.
153
+ assertSafeTransportPayload(input.payload);
154
+ const idempotencyKey = buildTransportWorkDeliveryKey({
155
+ bindingId: input.bindingId,
156
+ workItemId: input.workItemId,
157
+ purpose: input.purpose,
158
+ discriminator: input.discriminator,
159
+ });
160
+ outbox.enqueue({
161
+ idempotencyKey,
162
+ bindingId: input.bindingId,
163
+ externalConversationRef: input.externalConversationRef,
164
+ workItemId: input.workItemId,
165
+ taskId: input.taskId ?? null,
166
+ runId: input.runId ?? null,
167
+ purpose: input.purpose,
168
+ payload: input.payload,
169
+ });
170
+ if (defer) {
171
+ void outbox.flushWorkItem(input.workItemId).catch(() => {
172
+ // The durable row records ambiguity/failure and remains visible for
173
+ // startup recovery or explicit owner retry.
174
+ });
175
+ return null;
176
+ }
177
+ const flushed = await outbox.flush(idempotencyKey);
178
+ return flushed.row.externalMessageRef;
179
+ }
180
+ /**
181
+ * Offers only the recovery actions the projected stage actually permits.
182
+ *
183
+ * FR-35: a message must never hand the owner a button the product would then
184
+ * refuse, so the offer is derived from the same stage the authorization check
185
+ * will use.
186
+ */
187
+ function offerRecoveryActions(input) {
188
+ const workItem = input.core.workItems.find((candidate) => candidate.id === input.workItemId);
189
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
190
+ const origin = metadata?.origin ?? null;
191
+ if (origin === null) {
192
+ return [];
193
+ }
194
+ const stage = projectStageFromCore(input.core, input.workItemId);
195
+ const allowed = (stage?.allowedActions ?? []).filter((action) => action === 'retry' || action === 'resume' || action === 'cancel');
196
+ return allowed.length === 0 ? [] : offerActions({
197
+ t: input.t,
198
+ bindingId: origin.bindingId,
199
+ ownerActorId: input.core.ownerProfile.actorId,
200
+ externalUserRef: metadata?.externalUserRef ?? origin.externalConversationRef,
201
+ workItemId: input.workItemId,
202
+ proposal: input.proposal,
203
+ actions: allowed,
204
+ });
205
+ }
206
+ /**
207
+ * Owner-initiated cancellation (SPEC-114 FR-35).
208
+ *
209
+ * Cancels the authoritative Task and Run rather than only stopping the
210
+ * transport conversation, so an in-flight supervised step sees a terminal
211
+ * state on its next boundary check and stops.
212
+ */
213
+ async function cancelGoldenPathWork(workItemId, ownerActorId) {
214
+ const core = await coreStore.readCore();
215
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId) ?? null;
216
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
217
+ const origin = metadata?.origin ?? null;
218
+ if (workItem === null || origin === null) {
219
+ return { status: 'rejected', rejection: 'work_item_not_found', stage: null };
220
+ }
221
+ await coreStore.updateCore((state) => {
222
+ let next = state;
223
+ const task = state.tasks.find((candidate) => candidate.id === workItem.taskId) ?? null;
224
+ const run = task
225
+ ? state.runs.find((candidate) => candidate.taskId === task.id) ?? null
226
+ : null;
227
+ if (run !== null && run.status !== 'completed') {
228
+ next = upsertCoreRun(next, {
229
+ ...run,
230
+ status: 'cancelled',
231
+ summary: 'Cancelled by the owner from Telegram.',
232
+ completedAt: now().toISOString(),
233
+ }, now()).core;
234
+ }
235
+ if (task !== null && task.status !== 'completed') {
236
+ next = upsertCoreTask(next, { ...task, status: 'cancelled' }, now()).core;
237
+ }
238
+ return appendCoreActivity(next, {
239
+ id: `activity-wgp-cancelled-${workItem.id}`,
240
+ kind: 'status_change',
241
+ actorId: ownerActorId,
242
+ projectId: workItem.projectId,
243
+ workItemId: workItem.id,
244
+ conversationId: workItem.conversationId,
245
+ taskId: workItem.taskId,
246
+ runId: run?.id ?? null,
247
+ message: 'Owner cancelled the work from Telegram.',
248
+ metadata: { workGoldenPath: { schemaVersion: 1, reason: 'owner_cancelled' } },
249
+ }, now()).core;
250
+ });
251
+ tokenStore.invalidateWorkItem(workItem.id);
252
+ await enqueueAndFlush({
253
+ bindingId: origin.bindingId,
254
+ externalConversationRef: origin.externalConversationRef,
255
+ workItemId: workItem.id,
256
+ purpose: 'decision',
257
+ discriminator: `cancelled:${origin.proposalRevision}`,
258
+ payload: renderProgressMessage({
259
+ t: createWorkGoldenPathTranslator(metadata?.locale ?? null),
260
+ workItemId: workItem.id,
261
+ stageKey: messageKeys.workDeliveryStageCancelled,
262
+ milestoneKey: messageKeys.workDeliveryMilestoneCancelled,
263
+ }),
264
+ });
265
+ return {
266
+ status: 'cancelled',
267
+ stage: (await buildStage(workItem.id))?.stage ?? null,
268
+ };
269
+ }
270
+ /**
271
+ * Applies an owner publish decision and, when approved, finally delivers.
272
+ *
273
+ * Delivery happens only after the external actions succeeded: telling the
274
+ * owner "published" before the push landed would be the exact dishonesty
275
+ * ADR-112 section 6 is written against.
276
+ */
277
+ async function decidePublication(workItemId, ownerActorId, action) {
278
+ const core = await coreStore.readCore();
279
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId) ?? null;
280
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
281
+ const origin = metadata?.origin ?? null;
282
+ const proposal = metadata?.proposal ?? null;
283
+ if (workItem === null || origin === null || proposal === null) {
284
+ return { status: 'rejected', rejection: 'work_item_not_found', stage: null };
285
+ }
286
+ const t = createWorkGoldenPathTranslator(metadata?.locale ?? null);
287
+ const decision = await applyWorkGoldenPathPublishDecision(coreStore, {
288
+ workItemId,
289
+ decision: action === 'publish' ? 'approve' : 'deny',
290
+ ownerActorId,
291
+ actorRef,
292
+ deliveryClient: options.deliveryClient,
293
+ }, now);
294
+ if (decision.status === 'denied') {
295
+ tokenStore.invalidateWorkItem(workItemId);
296
+ await enqueueAndFlush({
297
+ bindingId: origin.bindingId,
298
+ externalConversationRef: origin.externalConversationRef,
299
+ workItemId,
300
+ purpose: 'decision',
301
+ discriminator: `publish-denied:${decision.publishKey}`,
302
+ payload: renderRefusalMessage({ t, reasonKey: messageKeys.workDeliveryPublishDenied }),
303
+ });
304
+ return {
305
+ status: 'publish_denied',
306
+ stage: (await buildStage(workItemId))?.stage ?? null,
307
+ };
308
+ }
309
+ if (decision.status === 'pending_checks') {
310
+ // The external work is under way; nothing was refused and nothing is
311
+ // delivered. The approval stays open so the owner can finish it later.
312
+ await enqueueAndFlush({
313
+ bindingId: origin.bindingId,
314
+ externalConversationRef: origin.externalConversationRef,
315
+ workItemId,
316
+ purpose: 'decision',
317
+ discriminator: `publish-pending:${decision.pendingOperationId ?? decision.publishKey}`,
318
+ payload: renderDecisionMessage({
319
+ t,
320
+ workItemId,
321
+ reason: t(messageKeys.workDeliveryPublishPendingChecks, {
322
+ actions: decision.performedActions.join(', ') || 'none',
323
+ }),
324
+ consequence: t(messageKeys.workDeliveryPublishPendingConsequence),
325
+ }),
326
+ });
327
+ return {
328
+ status: 'publish_blocked',
329
+ rejection: 'pending_checks',
330
+ stage: (await buildStage(workItemId))?.stage ?? null,
331
+ };
332
+ }
333
+ if (decision.status === 'blocked' || decision.status === 'not_pending') {
334
+ await enqueueAndFlush({
335
+ bindingId: origin.bindingId,
336
+ externalConversationRef: origin.externalConversationRef,
337
+ workItemId,
338
+ purpose: 'decision',
339
+ discriminator: `publish-blocked:${decision.blockedReasons.join('+')}`,
340
+ payload: renderDecisionMessage({
341
+ t,
342
+ workItemId,
343
+ reason: t(messageKeys.workDeliveryPublishBlocked, {
344
+ reasons: decision.blockedReasons.join(', '),
345
+ }),
346
+ consequence: t(messageKeys.workDeliveryPublishConsequence),
347
+ }),
348
+ });
349
+ return {
350
+ status: 'publish_blocked',
351
+ rejection: decision.blockedReasons.join(','),
352
+ stage: (await buildStage(workItemId))?.stage ?? null,
353
+ };
354
+ }
355
+ // Approved and the external actions landed: now the result may be delivered.
356
+ const evidence = evaluateWorkCompletionEvidence({
357
+ deliveryMode: proposal.deliveryMode,
358
+ acceptanceCriteria: proposal.acceptanceCriteria,
359
+ satisfiedCriteria: proposal.acceptanceCriteria,
360
+ outcomeStatus: 'succeeded',
361
+ artifacts: [],
362
+ commit: null,
363
+ });
364
+ await enqueueAndFlush({
365
+ bindingId: origin.bindingId,
366
+ externalConversationRef: origin.externalConversationRef,
367
+ workItemId,
368
+ purpose: 'publish_result',
369
+ discriminator: `publish:${decision.publishKey}`,
370
+ payload: renderResultMessage({
371
+ t,
372
+ workItemId,
373
+ proposal,
374
+ summary: decision.performedActions.length > 0
375
+ ? t(messageKeys.workDeliveryPublishDone, {
376
+ actions: decision.performedActions.join(', '),
377
+ })
378
+ : t(messageKeys.workDeliveryPublishDone, { actions: 'none' }),
379
+ evidence,
380
+ commitId: decision.reference,
381
+ artifactTitle: null,
382
+ outstandingGates: [],
383
+ actions: [],
384
+ }),
385
+ taskId: workItem.taskId,
386
+ runId: null,
387
+ });
388
+ return {
389
+ status: 'published',
390
+ stage: (await buildStage(workItemId))?.stage ?? null,
391
+ };
392
+ }
393
+ /**
394
+ * Applies an owner retry/resume and hands the Run back to the driver.
395
+ *
396
+ * The service does not drive the run itself: the caller that owns the runner
397
+ * does, which keeps this module free of execution concerns and keeps the
398
+ * ingress non-blocking (FR-14).
399
+ */
400
+ async function applyLifecycle(workItemId, ownerActorId, action) {
401
+ const core = await coreStore.readCore();
402
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId) ?? null;
403
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
404
+ const origin = metadata?.origin ?? null;
405
+ if (workItem === null || origin === null) {
406
+ return { status: 'rejected', rejection: 'work_item_not_found', stage: null };
407
+ }
408
+ const t = createWorkGoldenPathTranslator(metadata?.locale ?? null);
409
+ const result = await applyWorkGoldenPathLifecycleAction(coreStore, { workItemId, action, actorRef, ownerActorId }, now);
410
+ if (result.status === 'refused') {
411
+ await enqueueAndFlush({
412
+ bindingId: origin.bindingId,
413
+ externalConversationRef: origin.externalConversationRef,
414
+ workItemId,
415
+ purpose: 'decision',
416
+ discriminator: `${action}-refused:${result.reason}`,
417
+ payload: renderRefusalMessage({
418
+ t,
419
+ reasonKey: messageKeys.workDeliveryLifecycleRefused,
420
+ values: { reason: result.reason ?? action },
421
+ }),
422
+ });
423
+ return {
424
+ status: 'rejected',
425
+ rejection: result.reason ?? 'lifecycle_refused',
426
+ stage: (await buildStage(workItemId))?.stage ?? null,
427
+ };
428
+ }
429
+ await enqueueAndFlush({
430
+ bindingId: origin.bindingId,
431
+ externalConversationRef: origin.externalConversationRef,
432
+ workItemId,
433
+ runId: result.runId,
434
+ purpose: 'progress',
435
+ discriminator: `${action}:${result.runId}`,
436
+ payload: renderProgressMessage({
437
+ t,
438
+ workItemId,
439
+ stageKey: messageKeys.workDeliveryStageRunning,
440
+ milestoneKey: action === 'retry'
441
+ ? messageKeys.workDeliveryRetryStarted
442
+ : messageKeys.workDeliveryResumeStarted,
443
+ }),
444
+ });
445
+ return {
446
+ status: action === 'retry' ? 'retried' : 'resumed',
447
+ redriveRunId: result.runId,
448
+ workItemId,
449
+ stage: (await buildStage(workItemId))?.stage ?? null,
450
+ };
451
+ }
452
+ return {
453
+ tokenStore,
454
+ async receiveRequest(input) {
455
+ const t = createWorkGoldenPathTranslator(input.locale);
456
+ // FR-3: never claim work is queued when admission cannot happen, and never
457
+ // leave the owner in silence either — say what is missing.
458
+ if (!input.readiness.ready) {
459
+ telemetry?.record('readiness_failure', 'not_ready');
460
+ await enqueueAndFlush({
461
+ bindingId: input.bindingId,
462
+ externalConversationRef: input.externalConversationRef,
463
+ workItemId: pendingWorkItemKey(input.externalUpdateRef),
464
+ purpose: 'ack',
465
+ discriminator: `not-ready:${input.externalUpdateRef}`,
466
+ payload: renderNotReadyMessage({ t, readiness: input.readiness }),
467
+ }, input.deferDelivery);
468
+ return {
469
+ status: 'not_ready',
470
+ workItemId: null,
471
+ proposal: null,
472
+ offers: [],
473
+ readiness: input.readiness,
474
+ };
475
+ }
476
+ const capture = await captureGoldenPathWorkItem(coreStore, {
477
+ goal: input.goal,
478
+ bindingId: input.bindingId,
479
+ conversationId: input.conversationId,
480
+ externalMessageRef: input.externalMessageRef,
481
+ externalUpdateRef: input.externalUpdateRef,
482
+ }, { actorRef }, now);
483
+ if (capture.status !== 'captured') {
484
+ return {
485
+ status: 'not_ready',
486
+ workItemId: null,
487
+ proposal: null,
488
+ offers: [],
489
+ readiness: input.readiness,
490
+ };
491
+ }
492
+ const workItemId = capture.workItemId;
493
+ const existing = readWorkGoldenPathMetadata(capture.workItem.metadata);
494
+ const nextRevision = (existing?.proposal?.revision ?? 0) + 1;
495
+ const proposal = buildTransportWorkProposal({
496
+ revision: nextRevision,
497
+ goal: input.goal,
498
+ targetLabel: input.targetLabel,
499
+ projectId: input.projectId,
500
+ workspacePath: input.workspacePath,
501
+ acceptanceCriteria: input.acceptanceCriteria,
502
+ deliveryMode: input.deliveryMode,
503
+ deliveryGates: input.deliveryGates,
504
+ sideEffects: [describeDeliverySideEffectsLocalized(t, input.deliveryMode)],
505
+ openQuestion: input.openQuestion,
506
+ createdAt: now(),
507
+ });
508
+ // An unchanged digest must not bump the revision, or every duplicate
509
+ // update would invalidate the owner's outstanding buttons.
510
+ const effectiveProposal = existing?.proposal && existing.proposal.digest === proposal.digest
511
+ ? existing.proposal
512
+ : proposal;
513
+ const origin = {
514
+ version: 1,
515
+ transport: 'telegram',
516
+ bindingId: input.bindingId,
517
+ externalConversationRef: input.externalConversationRef,
518
+ externalUpdateRef: input.externalUpdateRef,
519
+ externalMessageRef: input.externalMessageRef,
520
+ conversationId: input.conversationId,
521
+ workItemId,
522
+ proposalRevision: effectiveProposal.revision,
523
+ proposalDigest: effectiveProposal.digest,
524
+ };
525
+ if (existing?.proposal?.digest !== effectiveProposal.digest) {
526
+ // Scope changed: outstanding tokens for the old revision must die.
527
+ tokenStore.invalidateWorkItem(workItemId);
528
+ }
529
+ const requestLocale = input.locale;
530
+ const requestUserRef = input.externalUserRef;
531
+ await coreStore.updateCore((core) => {
532
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId);
533
+ if (!workItem) {
534
+ return core;
535
+ }
536
+ const write = upsertCoreWorkItem(core, {
537
+ id: workItem.id,
538
+ title: workItem.title,
539
+ // Triage the scope to `ready` so execution admission has a legal
540
+ // starting status. This is still pre-authorization (FR-19).
541
+ status: 'ready',
542
+ ownerActorId: workItem.ownerActorId,
543
+ projectId: input.projectId,
544
+ conversationId: workItem.conversationId,
545
+ taskId: workItem.taskId,
546
+ parentWorkItemId: workItem.parentWorkItemId,
547
+ summary: workItem.summary,
548
+ assignedActorIds: workItem.assignedActorIds,
549
+ metadata: writeWorkGoldenPathMetadata(workItem.metadata, {
550
+ origin,
551
+ proposal: effectiveProposal,
552
+ locale: requestLocale,
553
+ externalUserRef: requestUserRef,
554
+ }),
555
+ }, now());
556
+ return write.core;
557
+ });
558
+ await enqueueAndFlush({
559
+ bindingId: input.bindingId,
560
+ externalConversationRef: input.externalConversationRef,
561
+ workItemId,
562
+ purpose: 'ack',
563
+ discriminator: input.externalUpdateRef,
564
+ payload: renderAcceptedMessage({ t, workItemId, goal: input.goal }),
565
+ }, input.deferDelivery);
566
+ const offers = offerActions({
567
+ t,
568
+ bindingId: input.bindingId,
569
+ ownerActorId: input.ownerActorId,
570
+ externalUserRef: input.externalUserRef,
571
+ workItemId,
572
+ proposal: effectiveProposal,
573
+ actions: PROPOSAL_ACTIONS,
574
+ });
575
+ await enqueueAndFlush({
576
+ bindingId: input.bindingId,
577
+ externalConversationRef: input.externalConversationRef,
578
+ workItemId,
579
+ purpose: 'proposal',
580
+ discriminator: `r${effectiveProposal.revision}:${effectiveProposal.digest}`,
581
+ payload: renderProposalMessage({
582
+ t,
583
+ workItemId,
584
+ proposal: effectiveProposal,
585
+ actions: offers,
586
+ }),
587
+ }, input.deferDelivery);
588
+ return {
589
+ status: 'accepted',
590
+ workItemId,
591
+ proposal: effectiveProposal,
592
+ offers,
593
+ readiness: input.readiness,
594
+ };
595
+ },
596
+ async authorize(input) {
597
+ // The token is resolved before anything else so a forged or replayed
598
+ // button never reaches product state. Scope is looked up for the work item
599
+ // the token names, so one binding can carry several open requests.
600
+ const core = await coreStore.readCore();
601
+ const resolution = tokenStore.resolve({
602
+ callbackData: input.callbackData,
603
+ bindingId: input.bindingId,
604
+ externalUserRef: input.externalUserRef,
605
+ resolveScope: (workItemId) => {
606
+ const workItem = core.workItems.find((candidate) => candidate.id === workItemId)
607
+ ?? null;
608
+ if (workItem === null) {
609
+ return null;
610
+ }
611
+ const metadata = readWorkGoldenPathMetadata(workItem.metadata);
612
+ const proposal = metadata?.proposal ?? null;
613
+ if (proposal === null || metadata?.origin?.bindingId !== input.bindingId) {
614
+ return null;
615
+ }
616
+ const stage = projectStageFromCore(core, workItemId);
617
+ const allowedActions = [
618
+ ...(stage?.allowedActions ?? PROPOSAL_ACTIONS),
619
+ ];
620
+ // A replayed `Start work` must converge on the existing Task and Run
621
+ // (FR-26) rather than be refused because the stage has moved past
622
+ // `scope_proposed`. Allowing it back in is safe only when this exact
623
+ // revision is already admitted, checked against the deterministic
624
+ // admission Run id rather than a stage label.
625
+ const admittedRunId = resolveWorkGoldenPathRunId(buildWorkGoldenPathAdmissionKey({
626
+ bindingId: input.bindingId,
627
+ workItemId,
628
+ proposalRevision: proposal.revision,
629
+ proposalDigest: proposal.digest,
630
+ }));
631
+ if (core.runs.some((run) => run.id === admittedRunId)) {
632
+ allowedActions.push('start_work');
633
+ }
634
+ return {
635
+ proposalRevision: proposal.revision,
636
+ proposalDigest: proposal.digest,
637
+ allowedActions,
638
+ };
639
+ },
640
+ });
641
+ if (resolution.status === 'rejected') {
642
+ telemetry?.record('admission_result', 'rejected');
643
+ return { status: 'rejected', rejection: resolution.reason, stage: null };
644
+ }
645
+ const token = resolution.token;
646
+ // How long the proposal sat before the owner acted. Bucketed, so this is
647
+ // an operational signal rather than a timeline of someone's day.
648
+ const proposedAt = Date.parse(token.issuedAt ?? '');
649
+ if (Number.isFinite(proposedAt)) {
650
+ telemetry?.recordDecisionLatency(now().getTime() - proposedAt);
651
+ }
652
+ if (token.action === 'cancel') {
653
+ return cancelGoldenPathWork(token.workItemId, token.ownerActorId);
654
+ }
655
+ if (token.action === 'publish' || token.action === 'deny') {
656
+ return decidePublication(token.workItemId, token.ownerActorId, token.action);
657
+ }
658
+ if (token.action === 'retry' || token.action === 'resume') {
659
+ return applyLifecycle(token.workItemId, token.ownerActorId, token.action);
660
+ }
661
+ if (token.action !== 'start_work') {
662
+ return {
663
+ status: 'rejected',
664
+ rejection: 'action_not_allowed',
665
+ stage: (await buildStage(token.workItemId))?.stage ?? null,
666
+ };
667
+ }
668
+ const admission = await admitTransportWorkExecution(coreStore, {
669
+ workItemId: token.workItemId,
670
+ bindingId: token.bindingId,
671
+ ownerActorId: token.ownerActorId,
672
+ proposalRevision: token.proposalRevision,
673
+ proposalDigest: token.proposalDigest,
674
+ readiness: input.readiness,
675
+ ownerEventRef: input.ownerEventRef,
676
+ actorRef,
677
+ executionTarget: input.executionTarget,
678
+ toolScope: input.toolScope,
679
+ workspaceHeadOid: input.workspaceHeadOid,
680
+ }, now);
681
+ telemetry?.record('admission_result', admission.status);
682
+ if (admission.status === 'blocked') {
683
+ return {
684
+ status: 'rejected',
685
+ rejection: admission.reason,
686
+ admission,
687
+ stage: (await buildStage(token.workItemId))?.stage ?? null,
688
+ };
689
+ }
690
+ const admittedWorkItem = core.workItems.find((candidate) => candidate.id === token.workItemId);
691
+ const admittedMetadata = readWorkGoldenPathMetadata(admittedWorkItem?.metadata);
692
+ const admittedOrigin = admittedMetadata?.origin;
693
+ if (admittedOrigin) {
694
+ await enqueueAndFlush({
695
+ bindingId: token.bindingId,
696
+ externalConversationRef: admittedOrigin.externalConversationRef,
697
+ workItemId: token.workItemId,
698
+ taskId: admission.taskId,
699
+ runId: admission.runId,
700
+ purpose: 'progress',
701
+ discriminator: `admitted:${admission.admissionKey}`,
702
+ payload: renderProgressMessage({
703
+ t: createWorkGoldenPathTranslator(admittedMetadata?.locale ?? null),
704
+ workItemId: token.workItemId,
705
+ stageKey: messageKeys.workDeliveryStageAdmitted,
706
+ milestoneKey: messageKeys.workDeliveryMilestoneAdmitted,
707
+ }),
708
+ }, input.deferDelivery);
709
+ }
710
+ return {
711
+ status: admission.status,
712
+ admission,
713
+ stage: (await buildStage(token.workItemId))?.stage ?? null,
714
+ };
715
+ },
716
+ async describeStage(workItemId) {
717
+ return buildStage(workItemId);
718
+ },
719
+ async markRunStatus(input) {
720
+ if (input.status === 'blocked' || input.status === 'failed' || input.status === 'cancelled') {
721
+ telemetry?.record('run_terminal_state', input.status);
722
+ }
723
+ const core = await coreStore.readCore();
724
+ const run = core.runs.find((candidate) => candidate.id === input.runId) ?? null;
725
+ if (run === null) {
726
+ return;
727
+ }
728
+ const workItem = core.workItems.find((candidate) => candidate.id === input.workItemId);
729
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
730
+ const origin = metadata?.origin ?? null;
731
+ await coreStore.updateCore((state) => upsertCoreRun(state, {
732
+ id: run.id,
733
+ title: run.title,
734
+ status: input.status,
735
+ conversationId: run.conversationId,
736
+ taskId: run.taskId,
737
+ orchestratorActorId: run.orchestratorActorId,
738
+ summary: run.summary,
739
+ startedAt: input.status === 'running' ? now().toISOString() : run.startedAt,
740
+ metadata: run.metadata,
741
+ }, now()).core);
742
+ if (origin !== null) {
743
+ await enqueueAndFlush({
744
+ bindingId: origin.bindingId,
745
+ externalConversationRef: origin.externalConversationRef,
746
+ workItemId: input.workItemId,
747
+ runId: input.runId,
748
+ purpose: 'progress',
749
+ discriminator: `${input.status}:${String(input.milestoneKey)}`,
750
+ payload: renderProgressMessage({
751
+ t: createWorkGoldenPathTranslator(metadata?.locale ?? null),
752
+ workItemId: input.workItemId,
753
+ stageKey: input.stageKey,
754
+ milestoneKey: input.milestoneKey,
755
+ }),
756
+ });
757
+ }
758
+ },
759
+ async notifyDecisionNeeded(input) {
760
+ const core = await coreStore.readCore();
761
+ const workItem = core.workItems.find((candidate) => candidate.id === input.workItemId);
762
+ const metadata = workItem ? readWorkGoldenPathMetadata(workItem.metadata) : null;
763
+ const origin = metadata?.origin ?? null;
764
+ const proposal = metadata?.proposal ?? null;
765
+ if (origin === null || proposal === null) {
766
+ return;
767
+ }
768
+ const t = createWorkGoldenPathTranslator(metadata?.locale ?? null);
769
+ const offered = offerRecoveryActions({ t, core, workItemId: input.workItemId, proposal });
770
+ await enqueueAndFlush({
771
+ bindingId: origin.bindingId,
772
+ externalConversationRef: origin.externalConversationRef,
773
+ workItemId: input.workItemId,
774
+ runId: input.runId,
775
+ purpose: 'decision',
776
+ discriminator: input.discriminator,
777
+ payload: {
778
+ ...renderDecisionMessage({
779
+ t,
780
+ workItemId: input.workItemId,
781
+ reason: t(input.reasonKey, input.reasonValues),
782
+ consequence: t(input.consequenceKey),
783
+ }),
784
+ actions: offered,
785
+ },
786
+ });
787
+ },
788
+ async sendRefusal(input) {
789
+ await enqueueAndFlush({
790
+ bindingId: input.bindingId,
791
+ externalConversationRef: input.externalConversationRef,
792
+ workItemId: pendingWorkItemKey(input.externalUpdateRef),
793
+ purpose: 'ack',
794
+ discriminator: `refusal:${String(input.reasonKey)}`,
795
+ payload: renderRefusalMessage({
796
+ t: createWorkGoldenPathTranslator(input.locale),
797
+ reasonKey: input.reasonKey,
798
+ }),
799
+ });
800
+ },
801
+ async completeRun(input) {
802
+ const core = await coreStore.readCore();
803
+ const workItem = core.workItems.find((candidate) => candidate.id === input.workItemId)
804
+ ?? null;
805
+ if (workItem === null) {
806
+ throw new Error(`Unknown golden-path work item: ${input.workItemId}`);
807
+ }
808
+ const metadata = readWorkGoldenPathMetadata(workItem.metadata);
809
+ const proposal = metadata?.proposal ?? null;
810
+ const origin = metadata?.origin ?? null;
811
+ if (proposal === null || origin === null) {
812
+ throw new Error(`Work item ${input.workItemId} has no authorized golden-path scope.`);
813
+ }
814
+ const t = createWorkGoldenPathTranslator(metadata?.locale ?? null);
815
+ const artifacts = core.artifacts.filter((artifact) => artifact.workItemId === workItem.id);
816
+ const projectedArtifacts = input.artifact === null
817
+ ? artifacts
818
+ : [
819
+ ...artifacts,
820
+ {
821
+ id: goldenPathArtifactId(input.runId),
822
+ title: input.artifact.title,
823
+ kind: 'document',
824
+ status: 'ready',
825
+ projectId: workItem.projectId,
826
+ workItemId: workItem.id,
827
+ conversationId: workItem.conversationId,
828
+ taskId: workItem.taskId,
829
+ runId: input.runId,
830
+ path: input.artifact.path,
831
+ mimeType: input.artifact.mimeType,
832
+ sizeBytes: null,
833
+ summary: input.summary,
834
+ createdAt: now().toISOString(),
835
+ updatedAt: now().toISOString(),
836
+ metadata: {},
837
+ },
838
+ ];
839
+ const evidence = evaluateWorkCompletionEvidence({
840
+ deliveryMode: proposal.deliveryMode,
841
+ acceptanceCriteria: proposal.acceptanceCriteria,
842
+ satisfiedCriteria: input.satisfiedCriteria,
843
+ // The outcome is not written until evidence passes, so evaluate against
844
+ // the *claim* first; writing `succeeded` before checking would make the
845
+ // check circular.
846
+ outcomeStatus: 'succeeded',
847
+ artifacts: projectedArtifacts,
848
+ commit: input.commit,
849
+ });
850
+ if (!evidence.accepted) {
851
+ // FR-30: a returned provider step that fails the evidence bar leaves the
852
+ // run blocked for an owner decision rather than completing.
853
+ await coreStore.updateCore((state) => {
854
+ const run = state.runs.find((candidate) => candidate.id === input.runId);
855
+ if (!run) {
856
+ return state;
857
+ }
858
+ return upsertCoreRun(state, { ...run, status: 'blocked', metadata: run.metadata }, now()).core;
859
+ });
860
+ await enqueueAndFlush({
861
+ bindingId: origin.bindingId,
862
+ externalConversationRef: origin.externalConversationRef,
863
+ workItemId: workItem.id,
864
+ runId: input.runId,
865
+ purpose: 'decision',
866
+ discriminator: `evidence:${evidence.gaps.join('+')}`,
867
+ payload: {
868
+ ...renderDecisionMessage({
869
+ t,
870
+ workItemId: workItem.id,
871
+ reason: t(messageKeys.workDeliveryDecisionEvidenceReason, {
872
+ gaps: evidence.gaps.join(', '),
873
+ }),
874
+ consequence: t(messageKeys.workDeliveryDecisionEvidenceConsequence),
875
+ }),
876
+ actions: offerRecoveryActions({
877
+ t,
878
+ core: await coreStore.readCore(),
879
+ workItemId: workItem.id,
880
+ proposal,
881
+ }),
882
+ },
883
+ });
884
+ const blockedStage = await buildStage(workItem.id);
885
+ return {
886
+ status: 'insufficient_evidence',
887
+ evidence,
888
+ stage: blockedStage?.stage ?? 'decision_needed',
889
+ outstandingGates: blockedStage?.outstandingGates ?? [],
890
+ deliveredMessageRef: null,
891
+ };
892
+ }
893
+ // Evidence accepted: materialize Outcome + Artifact, then complete the Run.
894
+ await coreStore.updateCore((state) => {
895
+ let next = state;
896
+ if (input.artifact !== null) {
897
+ next = upsertCoreArtifact(next, {
898
+ id: goldenPathArtifactId(input.runId),
899
+ title: input.artifact.title,
900
+ kind: 'document',
901
+ // SPEC-092 / FR-38: a declaration may reach `ready`, never
902
+ // `published`. Publication is a separate authorized action.
903
+ status: 'ready',
904
+ projectId: workItem.projectId,
905
+ workItemId: workItem.id,
906
+ conversationId: workItem.conversationId,
907
+ taskId: workItem.taskId,
908
+ runId: input.runId,
909
+ path: input.artifact.path,
910
+ mimeType: input.artifact.mimeType,
911
+ summary: input.summary,
912
+ }, now()).core;
913
+ }
914
+ next = upsertCoreOutcome(next, {
915
+ id: goldenPathOutcomeId(input.runId),
916
+ title: proposal.goal,
917
+ status: 'succeeded',
918
+ conversationId: workItem.conversationId,
919
+ runId: input.runId,
920
+ taskId: workItem.taskId,
921
+ summary: input.summary,
922
+ metadata: {
923
+ acceptanceCriteria: [...proposal.acceptanceCriteria],
924
+ satisfiedCriteria: [...input.satisfiedCriteria],
925
+ deliveryMode: proposal.deliveryMode,
926
+ ...(input.commit === null ? {} : {
927
+ commitId: input.commit.commitId,
928
+ changeSummary: input.commit.changeSummary,
929
+ validation: input.commit.validation,
930
+ }),
931
+ },
932
+ }, now()).core;
933
+ const run = next.runs.find((candidate) => candidate.id === input.runId);
934
+ if (run) {
935
+ next = upsertCoreRun(next, { ...run, status: 'completed', completedAt: now().toISOString() }, now()).core;
936
+ }
937
+ return appendCoreActivity(next, {
938
+ id: `activity-wgp-result-${input.runId}`,
939
+ kind: 'artifact_recorded',
940
+ actorId: actorRef,
941
+ projectId: workItem.projectId,
942
+ workItemId: workItem.id,
943
+ conversationId: workItem.conversationId,
944
+ taskId: workItem.taskId,
945
+ runId: input.runId,
946
+ message: `Result evidence accepted for "${proposal.goal}".`,
947
+ metadata: { deliveryMode: proposal.deliveryMode },
948
+ }, now()).core;
949
+ });
950
+ const outstandingGates = resolveOutstandingDeliveryGates({
951
+ deliveryMode: proposal.deliveryMode,
952
+ effectiveGates: proposal.deliveryGates,
953
+ satisfiedGates: [],
954
+ publishesPublicArtifact: false,
955
+ });
956
+ if (outstandingGates.length > 0) {
957
+ // FR-40/FR-41: a gate still needs an owner decision. The result preview
958
+ // goes out; the deliverable does not. The decision itself is a Core
959
+ // approval so it is auditable and visible in Desktop.
960
+ await requestWorkGoldenPathPublishApproval(coreStore, {
961
+ workItemId: workItem.id,
962
+ runId: input.runId,
963
+ taskId: workItem.taskId,
964
+ bindingId: origin.bindingId,
965
+ proposal,
966
+ outstandingGates,
967
+ ownerActorId: (await coreStore.readCore()).ownerProfile.actorId,
968
+ actorRef,
969
+ deliveryWorkspacePath: input.commit?.deliveryWorkspacePath
970
+ ?? input.artifact?.deliveryWorkspacePath
971
+ ?? null,
972
+ deliverySessionId: input.commit?.deliverySessionId
973
+ ?? input.artifact?.deliverySessionId
974
+ ?? null,
975
+ }, now);
976
+ const publishOffers = offerActions({
977
+ t,
978
+ bindingId: origin.bindingId,
979
+ ownerActorId: (await coreStore.readCore()).ownerProfile.actorId,
980
+ // Bound to the same external user that authorized execution, so a
981
+ // different chat member cannot publish someone else's result.
982
+ externalUserRef: metadata?.externalUserRef ?? origin.externalConversationRef,
983
+ workItemId: workItem.id,
984
+ proposal,
985
+ actions: PUBLISH_DECISION_ACTIONS,
986
+ });
987
+ await enqueueAndFlush({
988
+ bindingId: origin.bindingId,
989
+ externalConversationRef: origin.externalConversationRef,
990
+ workItemId: workItem.id,
991
+ runId: input.runId,
992
+ purpose: 'decision',
993
+ discriminator: `gates:${outstandingGates.join('+')}`,
994
+ payload: renderResultMessage({
995
+ t,
996
+ actions: publishOffers,
997
+ workItemId: workItem.id,
998
+ proposal,
999
+ summary: input.summary,
1000
+ evidence,
1001
+ commitId: input.commit?.commitId ?? null,
1002
+ artifactTitle: input.artifact?.title ?? null,
1003
+ outstandingGates,
1004
+ }),
1005
+ });
1006
+ return {
1007
+ status: 'result_ready',
1008
+ evidence,
1009
+ stage: 'result_ready',
1010
+ outstandingGates,
1011
+ deliveredMessageRef: null,
1012
+ };
1013
+ }
1014
+ // FR-43: deliver to the *recorded* binding, not a currently-selected one.
1015
+ const messageRef = await enqueueAndFlush({
1016
+ bindingId: origin.bindingId,
1017
+ externalConversationRef: origin.externalConversationRef,
1018
+ workItemId: workItem.id,
1019
+ runId: input.runId,
1020
+ purpose: 'result',
1021
+ discriminator: `run:${input.runId}`,
1022
+ payload: renderResultMessage({
1023
+ t,
1024
+ workItemId: workItem.id,
1025
+ proposal,
1026
+ summary: input.summary,
1027
+ evidence,
1028
+ commitId: input.commit?.commitId ?? null,
1029
+ artifactTitle: input.artifact?.title ?? null,
1030
+ outstandingGates: [],
1031
+ }),
1032
+ });
1033
+ const finalStage = await buildStage(workItem.id);
1034
+ return {
1035
+ status: finalStage?.stage === 'delivered' ? 'delivered' : 'result_ready',
1036
+ evidence,
1037
+ stage: finalStage?.stage ?? 'result_ready',
1038
+ outstandingGates: [],
1039
+ deliveredMessageRef: messageRef,
1040
+ };
1041
+ },
1042
+ };
1043
+ }
1044
+ function goldenPathArtifactId(runId) {
1045
+ return `artifact-wgp-${runId}`;
1046
+ }
1047
+ function goldenPathOutcomeId(runId) {
1048
+ return `outcome-wgp-${runId}`;
1049
+ }
1050
+ function readCommitId(metadata) {
1051
+ if (typeof metadata !== 'object' || metadata === null) {
1052
+ return null;
1053
+ }
1054
+ const value = metadata.commitId;
1055
+ return typeof value === 'string' && value.trim() !== '' ? value : null;
1056
+ }
1057
+ //# sourceMappingURL=workGoldenPathService.js.map