@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.
- package/.env.example +3 -7
- package/README.md +18 -0
- package/build/desktop/bootstrapNavigation.d.ts.map +1 -1
- package/build/desktop/bootstrapNavigation.js +6 -13
- package/build/desktop/bootstrapNavigation.js.map +1 -1
- package/build/desktop/bootstrapPage.d.ts.map +1 -1
- package/build/desktop/bootstrapPage.js +114 -34
- package/build/desktop/bootstrapPage.js.map +1 -1
- package/build/desktop/cliInventoryProbe.d.ts +5 -2
- package/build/desktop/cliInventoryProbe.d.ts.map +1 -1
- package/build/desktop/cliInventoryProbe.js +14 -11
- package/build/desktop/cliInventoryProbe.js.map +1 -1
- package/build/desktop/config.d.ts +0 -4
- package/build/desktop/config.d.ts.map +1 -1
- package/build/desktop/config.js +1 -6
- package/build/desktop/config.js.map +1 -1
- package/build/desktop/contracts.d.ts +10 -2
- package/build/desktop/contracts.d.ts.map +1 -1
- package/build/desktop/contracts.js +1 -2
- package/build/desktop/contracts.js.map +1 -1
- package/build/desktop/hostVersion.d.ts.map +1 -1
- package/build/desktop/hostVersion.js +3 -5
- package/build/desktop/hostVersion.js.map +1 -1
- package/build/desktop/main.js +144 -249
- package/build/desktop/main.js.map +1 -1
- package/build/desktop/packaging.d.ts +2 -0
- package/build/desktop/packaging.d.ts.map +1 -1
- package/build/desktop/packaging.js +34 -14
- package/build/desktop/packaging.js.map +1 -1
- package/build/desktop/preload.cjs +7 -0
- package/build/desktop/preload.cjs.map +1 -1
- package/build/desktop/processSupervisor.d.ts.map +1 -1
- package/build/desktop/processSupervisor.js +3 -0
- package/build/desktop/processSupervisor.js.map +1 -1
- package/build/desktop/providerSelection.d.ts +37 -0
- package/build/desktop/providerSelection.d.ts.map +1 -0
- package/build/desktop/providerSelection.js +112 -0
- package/build/desktop/providerSelection.js.map +1 -0
- package/build/desktop/readiness.d.ts +2 -0
- package/build/desktop/readiness.d.ts.map +1 -1
- package/build/desktop/readiness.js +14 -33
- package/build/desktop/readiness.js.map +1 -1
- package/build/desktop/setupAssets.d.ts.map +1 -1
- package/build/desktop/setupAssets.js +16 -14
- package/build/desktop/setupAssets.js.map +1 -1
- package/build/desktop/setupAudit.d.ts +2 -3
- package/build/desktop/setupAudit.d.ts.map +1 -1
- package/build/desktop/setupAudit.js +14 -34
- package/build/desktop/setupAudit.js.map +1 -1
- package/build/desktop/updateDialog.d.ts +27 -1
- package/build/desktop/updateDialog.d.ts.map +1 -1
- package/build/desktop/updateDialog.js +85 -5
- package/build/desktop/updateDialog.js.map +1 -1
- package/build/renderer/assets/{ActiveSessionPermissionChip-DImsuR9T.js → ActiveSessionPermissionChip-CAgN-tXv.js} +1 -1
- package/build/renderer/assets/App-BZA23O2J.js +2 -0
- package/build/renderer/assets/App-DxsV-tKS.css +1 -0
- package/build/renderer/assets/{App-DKOSn0o_.js → App-YqprEL2n.js} +1 -1
- package/build/renderer/assets/App-rGw9J8PZ.js +1 -0
- package/build/renderer/assets/WorkspaceAppRoutes-BCHCikmJ.js +1 -0
- package/build/renderer/assets/app-BV3UAQkR.css +1 -0
- package/build/renderer/assets/app-CVPqqfZo.js +45 -0
- package/build/renderer/assets/conversationSidebarRecentEntries-iqaLqA1n.js +6 -0
- package/build/renderer/assets/{useDraftSessionChips-DCIId0eY.js → useDraftSessionChips-D9BX6X92.js} +1 -1
- package/build/renderer/index.html +2 -2
- package/build/server/app/renderer/providerCatalogClient.d.ts.map +1 -1
- package/build/server/app/renderer/providerCatalogClient.js +15 -6
- package/build/server/app/renderer/providerCatalogClient.js.map +1 -1
- package/build/server/app/renderer/providerRegistryClient.d.ts +1 -1
- package/build/server/app/renderer/providerRegistryClient.d.ts.map +1 -1
- package/build/server/app/renderer/providerRegistryClient.js +23 -41
- package/build/server/app/renderer/providerRegistryClient.js.map +1 -1
- package/build/server/app/server/appPackageRoutes.d.ts +2 -0
- package/build/server/app/server/appPackageRoutes.d.ts.map +1 -1
- package/build/server/app/server/appPackageRoutes.js +105 -0
- package/build/server/app/server/appPackageRoutes.js.map +1 -1
- package/build/server/app/server/authGatePolicy.js +7 -1
- package/build/server/app/server/authGatePolicy.js.map +1 -1
- package/build/server/app/server/contracts.d.ts +15 -0
- package/build/server/app/server/contracts.d.ts.map +1 -1
- package/build/server/app/server/dependencies.d.ts.map +1 -1
- package/build/server/app/server/dependencies.js +42 -1
- package/build/server/app/server/dependencies.js.map +1 -1
- package/build/server/app/server/goldenPathStartupResume.d.ts +55 -0
- package/build/server/app/server/goldenPathStartupResume.d.ts.map +1 -0
- package/build/server/app/server/goldenPathStartupResume.js +109 -0
- package/build/server/app/server/goldenPathStartupResume.js.map +1 -0
- package/build/server/app/server/platformSetupRoutes.d.ts.map +1 -1
- package/build/server/app/server/platformSetupRoutes.js +1 -13
- package/build/server/app/server/platformSetupRoutes.js.map +1 -1
- package/build/server/app/server/polling.d.ts.map +1 -1
- package/build/server/app/server/polling.js +2 -0
- package/build/server/app/server/polling.js.map +1 -1
- package/build/server/app/server/requestRouter.d.ts.map +1 -1
- package/build/server/app/server/requestRouter.js +30 -0
- package/build/server/app/server/requestRouter.js.map +1 -1
- package/build/server/app/server/runtimeSurfaceProxy.d.ts.map +1 -1
- package/build/server/app/server/runtimeSurfaceProxy.js +4 -2
- package/build/server/app/server/runtimeSurfaceProxy.js.map +1 -1
- package/build/server/app/server/startupRecovery.d.ts.map +1 -1
- package/build/server/app/server/startupRecovery.js +5 -0
- package/build/server/app/server/startupRecovery.js.map +1 -1
- package/build/server/app/server/telegramCommandSurface.d.ts +29 -0
- package/build/server/app/server/telegramCommandSurface.d.ts.map +1 -0
- package/build/server/app/server/telegramCommandSurface.js +123 -0
- package/build/server/app/server/telegramCommandSurface.js.map +1 -0
- package/build/server/app/server/transportWorkGoldenPath.d.ts +82 -0
- package/build/server/app/server/transportWorkGoldenPath.d.ts.map +1 -0
- package/build/server/app/server/transportWorkGoldenPath.js +307 -0
- package/build/server/app/server/transportWorkGoldenPath.js.map +1 -0
- package/build/server/config.d.ts +21 -0
- package/build/server/config.d.ts.map +1 -1
- package/build/server/config.js +12 -0
- package/build/server/config.js.map +1 -1
- package/build/server/design/components/providerModelFieldsSupport.d.ts +3 -3
- package/build/server/design/components/providerModelFieldsSupport.d.ts.map +1 -1
- package/build/server/design/components/providerModelFieldsSupport.js +37 -38
- package/build/server/design/components/providerModelFieldsSupport.js.map +1 -1
- package/build/server/design/components/useProviderCatalogState.d.ts +1 -0
- package/build/server/design/components/useProviderCatalogState.d.ts.map +1 -1
- package/build/server/design/components/useProviderCatalogState.js +1 -0
- package/build/server/design/components/useProviderCatalogState.js.map +1 -1
- package/build/server/design/components/useProviderRegistryAutoRecheck.d.ts.map +1 -1
- package/build/server/design/components/useProviderRegistryAutoRecheck.js +20 -13
- package/build/server/design/components/useProviderRegistryAutoRecheck.js.map +1 -1
- package/build/server/design/components/useProviderRegistryState.d.ts.map +1 -1
- package/build/server/design/components/useProviderRegistryState.js +12 -25
- package/build/server/design/components/useProviderRegistryState.js.map +1 -1
- package/build/server/design/components/useProviderTargetReconciliation.d.ts.map +1 -1
- package/build/server/design/components/useProviderTargetReconciliation.js +2 -1
- package/build/server/design/components/useProviderTargetReconciliation.js.map +1 -1
- package/build/server/index.js +4 -0
- package/build/server/index.js.map +1 -1
- package/build/server/platform/apps/packageInstaller.d.ts +19 -0
- package/build/server/platform/apps/packageInstaller.d.ts.map +1 -0
- package/build/server/platform/apps/packageInstaller.js +92 -0
- package/build/server/platform/apps/packageInstaller.js.map +1 -0
- package/build/server/platform/apps/registry.d.ts +6 -0
- package/build/server/platform/apps/registry.d.ts.map +1 -1
- package/build/server/platform/apps/registry.js +24 -0
- package/build/server/platform/apps/registry.js.map +1 -1
- package/build/server/platform/apps/renderer.d.ts +6 -0
- package/build/server/platform/apps/renderer.d.ts.map +1 -0
- package/build/server/platform/apps/renderer.js +18 -0
- package/build/server/platform/apps/renderer.js.map +1 -0
- package/build/server/platform/runtime/deliveryClient.d.ts +111 -0
- package/build/server/platform/runtime/deliveryClient.d.ts.map +1 -0
- package/build/server/platform/runtime/deliveryClient.js +289 -0
- package/build/server/platform/runtime/deliveryClient.js.map +1 -0
- package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts +1 -0
- package/build/server/platform/supervision/providerCapabilityBootstrapYaml.d.ts.map +1 -1
- package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js +10 -0
- package/build/server/platform/supervision/providerCapabilityBootstrapYaml.js.map +1 -1
- package/build/server/platform/supervision/runtimeBoundary.d.ts +11 -1
- package/build/server/platform/supervision/runtimeBoundary.d.ts.map +1 -1
- package/build/server/platform/supervision/runtimeBoundary.js +2 -2
- package/build/server/platform/supervision/runtimeBoundary.js.map +1 -1
- package/build/server/platform/transports/telegram/bridge.d.ts +18 -0
- package/build/server/platform/transports/telegram/bridge.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/bridge.js +176 -0
- package/build/server/platform/transports/telegram/bridge.js.map +1 -1
- package/build/server/platform/transports/telegram/commandPort.d.ts +40 -0
- package/build/server/platform/transports/telegram/commandPort.d.ts.map +1 -0
- package/build/server/platform/transports/telegram/commandPort.js +17 -0
- package/build/server/platform/transports/telegram/commandPort.js.map +1 -0
- package/build/server/platform/transports/telegram/commandRouter.d.ts +22 -0
- package/build/server/platform/transports/telegram/commandRouter.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/commandRouter.js.map +1 -1
- package/build/server/platform/transports/telegram/commands/index.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/commands/index.js +38 -0
- package/build/server/platform/transports/telegram/commands/index.js.map +1 -1
- package/build/server/platform/transports/telegram/ingressDispatch.d.ts +56 -0
- package/build/server/platform/transports/telegram/ingressDispatch.d.ts.map +1 -0
- package/build/server/platform/transports/telegram/ingressDispatch.js +93 -0
- package/build/server/platform/transports/telegram/ingressDispatch.js.map +1 -0
- package/build/server/platform/transports/telegram/polling.d.ts +34 -0
- package/build/server/platform/transports/telegram/polling.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/polling.js +86 -9
- package/build/server/platform/transports/telegram/polling.js.map +1 -1
- package/build/server/platform/transports/telegram/relay/index.d.ts +3 -0
- package/build/server/platform/transports/telegram/relay/index.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/relay/index.js +5 -1
- package/build/server/platform/transports/telegram/relay/index.js.map +1 -1
- package/build/server/platform/transports/telegram/relay/ingress.d.ts +2 -0
- package/build/server/platform/transports/telegram/relay/ingress.d.ts.map +1 -1
- package/build/server/platform/transports/telegram/relay/ingress.js +1 -1
- package/build/server/platform/transports/telegram/relay/ingress.js.map +1 -1
- package/build/server/platform/transports/work-delivery/actionTokens.d.ts +66 -0
- package/build/server/platform/transports/work-delivery/actionTokens.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/actionTokens.js +145 -0
- package/build/server/platform/transports/work-delivery/actionTokens.js.map +1 -0
- package/build/server/platform/transports/work-delivery/contracts.d.ts +172 -0
- package/build/server/platform/transports/work-delivery/contracts.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/contracts.js +26 -0
- package/build/server/platform/transports/work-delivery/contracts.js.map +1 -0
- package/build/server/platform/transports/work-delivery/deliveryGates.d.ts +32 -0
- package/build/server/platform/transports/work-delivery/deliveryGates.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/deliveryGates.js +45 -0
- package/build/server/platform/transports/work-delivery/deliveryGates.js.map +1 -0
- package/build/server/platform/transports/work-delivery/inboundClassification.d.ts +31 -0
- package/build/server/platform/transports/work-delivery/inboundClassification.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/inboundClassification.js +52 -0
- package/build/server/platform/transports/work-delivery/inboundClassification.js.map +1 -0
- package/build/server/platform/transports/work-delivery/outbox.d.ts +84 -0
- package/build/server/platform/transports/work-delivery/outbox.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/outbox.js +270 -0
- package/build/server/platform/transports/work-delivery/outbox.js.map +1 -0
- package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts +57 -0
- package/build/server/platform/transports/work-delivery/permissionEnvelope.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/permissionEnvelope.js +74 -0
- package/build/server/platform/transports/work-delivery/permissionEnvelope.js.map +1 -0
- package/build/server/platform/transports/work-delivery/port.d.ts +60 -0
- package/build/server/platform/transports/work-delivery/port.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/port.js +15 -0
- package/build/server/platform/transports/work-delivery/port.js.map +1 -0
- package/build/server/platform/transports/work-delivery/proposal.d.ts +31 -0
- package/build/server/platform/transports/work-delivery/proposal.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/proposal.js +59 -0
- package/build/server/platform/transports/work-delivery/proposal.js.map +1 -0
- package/build/server/platform/transports/work-delivery/readiness.d.ts +61 -0
- package/build/server/platform/transports/work-delivery/readiness.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/readiness.js +123 -0
- package/build/server/platform/transports/work-delivery/readiness.js.map +1 -0
- package/build/server/platform/transports/work-delivery/stageProjection.d.ts +43 -0
- package/build/server/platform/transports/work-delivery/stageProjection.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/stageProjection.js +132 -0
- package/build/server/platform/transports/work-delivery/stageProjection.js.map +1 -0
- package/build/server/platform/transports/work-delivery/stateStore.d.ts +21 -0
- package/build/server/platform/transports/work-delivery/stateStore.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/stateStore.js +193 -0
- package/build/server/platform/transports/work-delivery/stateStore.js.map +1 -0
- package/build/server/platform/transports/work-delivery/telemetry.d.ts +49 -0
- package/build/server/platform/transports/work-delivery/telemetry.d.ts.map +1 -0
- package/build/server/platform/transports/work-delivery/telemetry.js +80 -0
- package/build/server/platform/transports/work-delivery/telemetry.js.map +1 -0
- package/build/server/products/chat/api/contracts.d.ts +10 -0
- package/build/server/products/chat/api/contracts.d.ts.map +1 -1
- package/build/server/products/chat/api/resources/channelRoutes.d.ts.map +1 -1
- package/build/server/products/chat/api/resources/channelRoutes.js +27 -1
- package/build/server/products/chat/api/resources/channelRoutes.js.map +1 -1
- package/build/server/products/chat/api/routeSupport.d.ts +8 -0
- package/build/server/products/chat/api/routeSupport.d.ts.map +1 -1
- package/build/server/products/chat/api/routeSupport.js +5 -4
- package/build/server/products/chat/api/routeSupport.js.map +1 -1
- package/build/server/products/chat/api/setupRoutes.d.ts.map +1 -1
- package/build/server/products/chat/api/setupRoutes.js +23 -8
- package/build/server/products/chat/api/setupRoutes.js.map +1 -1
- package/build/server/products/chat/state/model/channelExecutionTargets.d.ts +15 -0
- package/build/server/products/chat/state/model/channelExecutionTargets.d.ts.map +1 -1
- package/build/server/products/chat/state/model/channelExecutionTargets.js +25 -0
- package/build/server/products/chat/state/model/channelExecutionTargets.js.map +1 -1
- package/build/server/products/chat/state/model/index.d.ts +1 -1
- package/build/server/products/chat/state/model/index.d.ts.map +1 -1
- package/build/server/products/chat/state/model/index.js +1 -1
- package/build/server/products/chat/state/model/index.js.map +1 -1
- package/build/server/products/code/api/relayRoutes.d.ts.map +1 -1
- package/build/server/products/code/api/relayRoutes.js +36 -7
- package/build/server/products/code/api/relayRoutes.js.map +1 -1
- package/build/server/products/code/state/relayState.d.ts +2 -1
- package/build/server/products/code/state/relayState.d.ts.map +1 -1
- package/build/server/products/code/state/relayState.js +7 -7
- package/build/server/products/code/state/relayState.js.map +1 -1
- package/build/server/products/shared/api/workspaceContracts.d.ts +5 -0
- package/build/server/products/shared/api/workspaceContracts.d.ts.map +1 -1
- package/build/server/products/work/api/goldenPathProjection.d.ts +156 -0
- package/build/server/products/work/api/goldenPathProjection.d.ts.map +1 -0
- package/build/server/products/work/api/goldenPathProjection.js +252 -0
- package/build/server/products/work/api/goldenPathProjection.js.map +1 -0
- package/build/server/products/work/api/index.d.ts +54 -1
- package/build/server/products/work/api/index.d.ts.map +1 -1
- package/build/server/products/work/api/index.js +182 -4
- package/build/server/products/work/api/index.js.map +1 -1
- package/build/server/products/work/api/projection.d.ts +7 -1
- package/build/server/products/work/api/projection.d.ts.map +1 -1
- package/build/server/products/work/api/projection.js +7 -1
- package/build/server/products/work/api/projection.js.map +1 -1
- package/build/server/products/work/shared/apiPaths.d.ts +8 -0
- package/build/server/products/work/shared/apiPaths.d.ts.map +1 -1
- package/build/server/products/work/shared/apiPaths.js +8 -0
- package/build/server/products/work/shared/apiPaths.js.map +1 -1
- package/build/server/products/work/shared/workGoldenPathMessages.d.ts +83 -0
- package/build/server/products/work/shared/workGoldenPathMessages.d.ts.map +1 -0
- package/build/server/products/work/shared/workGoldenPathMessages.js +211 -0
- package/build/server/products/work/shared/workGoldenPathMessages.js.map +1 -0
- package/build/server/products/work/shared/workGoldenPathMetadata.d.ts +45 -0
- package/build/server/products/work/shared/workGoldenPathMetadata.d.ts.map +1 -0
- package/build/server/products/work/shared/workGoldenPathMetadata.js +122 -0
- package/build/server/products/work/shared/workGoldenPathMetadata.js.map +1 -0
- package/build/server/products/work/state/workCompletionEvidence.d.ts +42 -0
- package/build/server/products/work/state/workCompletionEvidence.d.ts.map +1 -0
- package/build/server/products/work/state/workCompletionEvidence.js +59 -0
- package/build/server/products/work/state/workCompletionEvidence.js.map +1 -0
- package/build/server/products/work/state/workExecutionTaskDelegate.d.ts +16 -0
- package/build/server/products/work/state/workExecutionTaskDelegate.d.ts.map +1 -1
- package/build/server/products/work/state/workExecutionTaskDelegate.js +91 -68
- package/build/server/products/work/state/workExecutionTaskDelegate.js.map +1 -1
- package/build/server/products/work/state/workGoldenPathAdmission.d.ts +78 -0
- package/build/server/products/work/state/workGoldenPathAdmission.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathAdmission.js +249 -0
- package/build/server/products/work/state/workGoldenPathAdmission.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts +33 -0
- package/build/server/products/work/state/workGoldenPathDeliveryEvidence.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js +161 -0
- package/build/server/products/work/state/workGoldenPathDeliveryEvidence.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathIntake.d.ts +34 -0
- package/build/server/products/work/state/workGoldenPathIntake.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathIntake.js +50 -0
- package/build/server/products/work/state/workGoldenPathIntake.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathLifecycle.d.ts +44 -0
- package/build/server/products/work/state/workGoldenPathLifecycle.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathLifecycle.js +146 -0
- package/build/server/products/work/state/workGoldenPathLifecycle.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathPublish.d.ts +84 -0
- package/build/server/products/work/state/workGoldenPathPublish.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathPublish.js +488 -0
- package/build/server/products/work/state/workGoldenPathPublish.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathRunner.d.ts +121 -0
- package/build/server/products/work/state/workGoldenPathRunner.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathRunner.js +447 -0
- package/build/server/products/work/state/workGoldenPathRunner.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts +74 -0
- package/build/server/products/work/state/workGoldenPathRuntimeExecutor.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js +239 -0
- package/build/server/products/work/state/workGoldenPathRuntimeExecutor.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathService.d.ts +152 -0
- package/build/server/products/work/state/workGoldenPathService.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathService.js +1057 -0
- package/build/server/products/work/state/workGoldenPathService.js.map +1 -0
- package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts +87 -0
- package/build/server/products/work/state/workGoldenPathTelegramPort.d.ts.map +1 -0
- package/build/server/products/work/state/workGoldenPathTelegramPort.js +220 -0
- package/build/server/products/work/state/workGoldenPathTelegramPort.js.map +1 -0
- package/build/server/runtime/client.d.ts +13 -0
- package/build/server/runtime/client.d.ts.map +1 -1
- package/build/server/runtime/client.js +39 -0
- package/build/server/runtime/client.js.map +1 -1
- package/build/server/runtime/setup.d.ts +10 -4
- package/build/server/runtime/setup.d.ts.map +1 -1
- package/build/server/runtime/setup.js +14 -18
- package/build/server/runtime/setup.js.map +1 -1
- package/build/server/runtime/usageSnapshot.d.ts +3 -0
- package/build/server/runtime/usageSnapshot.d.ts.map +1 -0
- package/build/server/runtime/usageSnapshot.js +69 -0
- package/build/server/runtime/usageSnapshot.js.map +1 -0
- package/build/server/server/routes/providerCapabilityBootstrap.d.ts +69 -0
- package/build/server/server/routes/providerCapabilityBootstrap.d.ts.map +1 -0
- package/build/server/server/routes/providerCapabilityBootstrap.js +165 -0
- package/build/server/server/routes/providerCapabilityBootstrap.js.map +1 -0
- package/build/server/server/routes/providers.d.ts.map +1 -1
- package/build/server/server/routes/providers.js +112 -81
- package/build/server/server/routes/providers.js.map +1 -1
- package/build/server/server/routes/telegram.d.ts +9 -0
- package/build/server/server/routes/telegram.d.ts.map +1 -1
- package/build/server/server/routes/telegram.js +79 -72
- package/build/server/server/routes/telegram.js.map +1 -1
- package/build/server/shared/catsAppManifest.d.ts +3 -1
- package/build/server/shared/catsAppManifest.d.ts.map +1 -1
- package/build/server/shared/catsAppManifest.js +2 -0
- package/build/server/shared/catsAppManifest.js.map +1 -1
- package/build/server/shared/executionLabel.d.ts.map +1 -1
- package/build/server/shared/executionLabel.js +8 -1
- package/build/server/shared/executionLabel.js.map +1 -1
- package/build/server/shared/i18n/catalogs/en.d.ts.map +1 -1
- package/build/server/shared/i18n/catalogs/en.js +198 -3
- package/build/server/shared/i18n/catalogs/en.js.map +1 -1
- package/build/server/shared/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/build/server/shared/i18n/catalogs/zh-TW.js +197 -3
- package/build/server/shared/i18n/catalogs/zh-TW.js.map +1 -1
- package/build/server/shared/i18n/index.d.ts +192 -0
- package/build/server/shared/i18n/index.d.ts.map +1 -1
- package/build/server/shared/i18n/messageKeys.d.ts +192 -0
- package/build/server/shared/i18n/messageKeys.d.ts.map +1 -1
- package/build/server/shared/i18n/messageKeys.js +192 -0
- package/build/server/shared/i18n/messageKeys.js.map +1 -1
- package/build/server/shared/platformSetupOperation.d.ts +2 -0
- package/build/server/shared/platformSetupOperation.d.ts.map +1 -0
- package/build/server/shared/platformSetupOperation.js +9 -0
- package/build/server/shared/platformSetupOperation.js.map +1 -0
- package/build/server/shared/providerCatalog.d.ts +5 -2
- package/build/server/shared/providerCatalog.d.ts.map +1 -1
- package/build/server/shared/providerCatalog.js +6 -0
- package/build/server/shared/providerCatalog.js.map +1 -1
- package/build/server/shared/providerCatalogData.d.ts +28 -22
- package/build/server/shared/providerCatalogData.d.ts.map +1 -1
- package/build/server/shared/providerCatalogData.js +22 -10
- package/build/server/shared/providerCatalogData.js.map +1 -1
- package/build/server/shared/providerCatalogInstances.d.ts.map +1 -1
- package/build/server/shared/providerCatalogInstances.js +3 -0
- package/build/server/shared/providerCatalogInstances.js.map +1 -1
- package/build/server/shared/providerModelLabelRegistry.d.ts +29 -0
- package/build/server/shared/providerModelLabelRegistry.d.ts.map +1 -0
- package/build/server/shared/providerModelLabelRegistry.js +49 -0
- package/build/server/shared/providerModelLabelRegistry.js.map +1 -0
- package/build/server/shared/providerSelection.d.ts.map +1 -1
- package/build/server/shared/providerSelection.js +10 -13
- package/build/server/shared/providerSelection.js.map +1 -1
- package/build/server/shared/runtimeSetup.d.ts +17 -4
- package/build/server/shared/runtimeSetup.d.ts.map +1 -1
- package/build/server/shared/runtimeStatusPresentation.js +1 -1
- package/build/server/shared/runtimeStatusPresentation.js.map +1 -1
- package/package.json +23 -1
- package/packages/app-sdk/browser.d.ts +51 -0
- package/packages/app-sdk/browser.js +48 -0
- package/packages/app-sdk/package.d.ts +14 -0
- package/packages/app-sdk/package.js +131 -0
- package/packages/app-sdk/package.json +7 -0
- package/scripts/README.md +43 -26
- package/scripts/build-desktop-installer.mjs +15 -0
- package/scripts/build-test-ui.mjs +1 -1
- package/scripts/bundle-server.mjs +23 -10
- package/scripts/linux/build-desktop-package.sh +6 -6
- package/scripts/linux/install-antigravity.sh +0 -0
- package/scripts/linux/install-devin.sh +0 -0
- package/scripts/linux/install-meta-cli.sh +8 -0
- package/scripts/linux/provider-cli-common.sh +103 -30
- package/scripts/linux/sync-agent-skills.sh +27 -5
- package/scripts/linux/test-linux-package-smoke.sh +4 -3
- package/scripts/linux/upgrade-cli-tools.sh +1 -1
- package/scripts/macos/build-desktop-package.sh +5 -5
- package/scripts/macos/install-antigravity.sh +0 -0
- package/scripts/macos/install-devin.sh +0 -0
- package/scripts/macos/install-meta-cli.sh +8 -0
- package/scripts/macos/provider-cli-common.sh +103 -30
- package/scripts/macos/sync-agent-skills.sh +27 -5
- package/scripts/macos/test-macos-package-smoke.sh +4 -3
- package/scripts/macos/upgrade-cli-tools.sh +1 -1
- package/scripts/package-desktop.mjs +15 -2
- package/scripts/testing/check-usage-app.mts +266 -0
- package/scripts/testing/usage-app-smoke-electron.cjs +10 -0
- package/scripts/testing/usage-app-smoke-renderer.tsx +8 -0
- package/scripts/verify-desktop-app-bundle.mjs +108 -0
- package/scripts/windows/Build-DesktopPackage.ps1 +9 -6
- package/scripts/windows/{Install-Aider.ps1 → Install-MetaCli.ps1} +180 -92
- package/scripts/windows/Install-Ollama.ps1 +4 -1
- package/scripts/windows/Sync-AgentSkills.ps1 +29 -6
- package/scripts/windows/Test-WindowsInstallerSmoke.ps1 +2 -1
- package/build/desktop/updateNotifications.d.ts +0 -64
- package/build/desktop/updateNotifications.d.ts.map +0 -1
- package/build/desktop/updateNotifications.js +0 -143
- package/build/desktop/updateNotifications.js.map +0 -1
- package/build/renderer/assets/App-BfOvMP2m.js +0 -1
- package/build/renderer/assets/App-DGwkRFZl.css +0 -1
- package/build/renderer/assets/App-DWd7I3Z2.js +0 -2
- package/build/renderer/assets/WorkspaceAppRoutes-CrbTA9En.js +0 -1
- package/build/renderer/assets/app-BTww3Hl4.js +0 -43
- package/build/renderer/assets/app-CPxWQwLA.css +0 -1
- package/build/renderer/assets/conversationSidebarRecentEntries-QuaOPxCb.js +0 -6
- package/scripts/linux/install-aider.sh +0 -8
- 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
|