@moretea-labs/forge 1.5.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/sprint-backlog.sh +6 -6
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +170 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-public-docs.sh +14 -1
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -1
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +106 -3
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +898 -430
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/github.ts +133 -0
- package/src/cli/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- package/src/cli/repositories/command-executor.ts +55 -521
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +121 -0
- package/src/cli/repositories/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation.ts +275 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +19 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +125 -8
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -2
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +702 -94
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/bin/forge-desktop-helper.mjs +0 -204
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
- package/docs/architecture/current/entity-model.md +0 -592
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -144
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -311
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -5,6 +5,7 @@ export * from './work-task-projection';
|
|
|
5
5
|
export * from './work-continuation';
|
|
6
6
|
export * from './controller-session-store';
|
|
7
7
|
export * from './plan-contract-store';
|
|
8
|
+
export * from './semantic-admission';
|
|
8
9
|
export * from './facade-result';
|
|
9
10
|
export * from './suggested-actions';
|
|
10
11
|
export * from './capability-registry';
|
|
@@ -3,12 +3,21 @@ import { join } from 'path';
|
|
|
3
3
|
import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
4
4
|
import { withControllerLock } from '../../../cli/repositories/locks';
|
|
5
5
|
import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../../shared/json-files';
|
|
6
|
+
import {
|
|
7
|
+
normalizePlanScopeKey,
|
|
8
|
+
resolvePlanAdmission,
|
|
9
|
+
withPlanAdmissionLock,
|
|
10
|
+
withPlanAdmissionLockAsync,
|
|
11
|
+
type PlanAdmissionRelation,
|
|
12
|
+
type PlanAdmissionResolution,
|
|
13
|
+
} from './semantic-admission';
|
|
6
14
|
import {
|
|
7
15
|
listControlPlaneRecords,
|
|
8
16
|
readControlPlaneRecordWithinTransaction,
|
|
9
17
|
withControlPlaneTransaction,
|
|
10
18
|
writeControlPlaneRecordWithinTransaction,
|
|
11
19
|
} from '../persistence/sqlite-store';
|
|
20
|
+
import { readRequirement } from '../persistence/requirement-store';
|
|
12
21
|
import {
|
|
13
22
|
isTerminalPlanContractStatus,
|
|
14
23
|
type EvidenceRef,
|
|
@@ -46,6 +55,13 @@ export interface CreatePlanContractInput {
|
|
|
46
55
|
evidenceRefs?: EvidenceRef[];
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
export interface AdmitPlanContractInput extends CreatePlanContractInput {
|
|
59
|
+
planRelation?: PlanAdmissionRelation;
|
|
60
|
+
relatedPlanId?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type AdmitPlanContractResult = PlanAdmissionResolution;
|
|
64
|
+
|
|
49
65
|
export interface PlanContractSummary {
|
|
50
66
|
planId: string;
|
|
51
67
|
repoId: string;
|
|
@@ -68,7 +84,7 @@ function bounded(values: readonly string[] | undefined, limit: number, maxLength
|
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
function normalizeScopeKey(value: string): string {
|
|
71
|
-
return
|
|
87
|
+
return normalizePlanScopeKey(value) || 'unknown';
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
function normalizeStep(step: CreatePlanContractInput['steps'][number]): PlanStep {
|
|
@@ -115,6 +131,14 @@ function updatePlanContract(
|
|
|
115
131
|
);
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
function assertRequirementReference(options: PlanContractStoreOptions, requirementId: string | undefined): void {
|
|
135
|
+
const normalized = requirementId?.trim();
|
|
136
|
+
if (!normalized || !options.controllerHome) return;
|
|
137
|
+
if (!readRequirement({ controllerHome: options.controllerHome, now: options.now }, normalized)) {
|
|
138
|
+
throw new Error(`PLAN_REQUIREMENT_NOT_FOUND: ${normalized}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
export function planContractRoot(location: PlanContractStoreLocation): string {
|
|
119
143
|
if (location.root) {
|
|
120
144
|
mkdirSync(location.root, { recursive: true });
|
|
@@ -205,11 +229,10 @@ function writePlanContractStore(options: PlanContractStoreOptions, store: PlanCo
|
|
|
205
229
|
return store;
|
|
206
230
|
}
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
const at = nowIso(options);
|
|
232
|
+
function buildPlanContract(input: CreatePlanContractInput, at: string): PlanContract {
|
|
210
233
|
const planId = sanitizeFileComponent(input.planId);
|
|
211
234
|
if (!String(input.planId ?? '').trim() || planId === 'unknown') throw new Error('plan_id is required');
|
|
212
|
-
|
|
235
|
+
return {
|
|
213
236
|
schemaVersion: 1,
|
|
214
237
|
planId,
|
|
215
238
|
repoId: input.repoId,
|
|
@@ -229,6 +252,12 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
|
|
|
229
252
|
createdAt: at,
|
|
230
253
|
updatedAt: at,
|
|
231
254
|
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function createPlanContractUnlocked(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
|
|
258
|
+
assertRequirementReference(options, input.requirementId);
|
|
259
|
+
const at = nowIso(options);
|
|
260
|
+
const plan = buildPlanContract(input, at);
|
|
232
261
|
const store = readPlanContractStore(options);
|
|
233
262
|
if (store.contracts.some((existing) => existing.planId === plan.planId)) {
|
|
234
263
|
throw new Error(`plan contract already exists: ${plan.planId}`);
|
|
@@ -237,6 +266,77 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
|
|
|
237
266
|
return plan;
|
|
238
267
|
}
|
|
239
268
|
|
|
269
|
+
function replacePlanContractUnlocked(
|
|
270
|
+
options: PlanContractStoreOptions,
|
|
271
|
+
predecessorId: string,
|
|
272
|
+
input: CreatePlanContractInput,
|
|
273
|
+
): PlanContract {
|
|
274
|
+
assertRequirementReference(options, input.requirementId);
|
|
275
|
+
const at = nowIso(options);
|
|
276
|
+
const store = readPlanContractStore(options);
|
|
277
|
+
const predecessorKey = sanitizeFileComponent(predecessorId);
|
|
278
|
+
const predecessorIndex = store.contracts.findIndex((contract) => contract.planId === predecessorKey);
|
|
279
|
+
if (predecessorIndex < 0) throw new Error(`plan contract not found: ${predecessorKey}`);
|
|
280
|
+
const predecessor = store.contracts[predecessorIndex]!;
|
|
281
|
+
if (isTerminalPlanContractStatus(predecessor.status)) throw new Error(`plan contract ${predecessor.planId} is terminal (${predecessor.status})`);
|
|
282
|
+
const successor = buildPlanContract(input, at);
|
|
283
|
+
if (successor.planId === predecessor.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
|
|
284
|
+
if (successor.repoId !== predecessor.repoId) throw new Error('PLAN_SUCCESSOR_REPOSITORY_MISMATCH');
|
|
285
|
+
if (store.contracts.some((existing) => existing.planId === successor.planId)) {
|
|
286
|
+
throw new Error(`plan contract already exists: ${successor.planId}`);
|
|
287
|
+
}
|
|
288
|
+
const conflictingScope = store.contracts.find((existing, index) => index !== predecessorIndex
|
|
289
|
+
&& !isTerminalPlanContractStatus(existing.status)
|
|
290
|
+
&& existing.scopeKey === successor.scopeKey);
|
|
291
|
+
if (conflictingScope) throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${successor.scopeKey}:${conflictingScope.planId}`);
|
|
292
|
+
const contracts = [...store.contracts];
|
|
293
|
+
contracts[predecessorIndex] = { ...predecessor, status: 'superseded', supersededBy: successor.planId, updatedAt: at };
|
|
294
|
+
writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts: [successor, ...contracts] });
|
|
295
|
+
return successor;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function createPlanContract(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
|
|
299
|
+
return withPlanAdmissionLock(options, () => {
|
|
300
|
+
const store = readPlanContractStore(options);
|
|
301
|
+
const planId = sanitizeFileComponent(input.planId);
|
|
302
|
+
if (store.contracts.some((existing) => existing.planId === planId)) {
|
|
303
|
+
throw new Error(`plan contract already exists: ${planId}`);
|
|
304
|
+
}
|
|
305
|
+
const scopeKey = normalizeScopeKey(input.scopeKey);
|
|
306
|
+
const existingScopeAuthority = scopeKey === 'unknown' ? undefined : store.contracts.find((existing) =>
|
|
307
|
+
!isTerminalPlanContractStatus(existing.status) && existing.scopeKey === scopeKey);
|
|
308
|
+
if (existingScopeAuthority) {
|
|
309
|
+
throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${scopeKey}:${existingScopeAuthority.planId}`);
|
|
310
|
+
}
|
|
311
|
+
return createPlanContractUnlocked(options, input);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function admitPlanContractUnlocked(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
|
|
316
|
+
assertRequirementReference(options, input.requirementId);
|
|
317
|
+
const activePlans = listPlanContracts({ ...options, status: 'active', limit: 100 });
|
|
318
|
+
const resolution = resolvePlanAdmission(activePlans, {
|
|
319
|
+
requirementId: input.requirementId,
|
|
320
|
+
scopeKey: input.scopeKey,
|
|
321
|
+
planRelation: input.planRelation,
|
|
322
|
+
relatedPlanId: input.relatedPlanId,
|
|
323
|
+
});
|
|
324
|
+
if (resolution.admissionDecision === 'extend_existing' && resolution.plan) {
|
|
325
|
+
const successor = replacePlanContractUnlocked(options, resolution.plan.planId, input);
|
|
326
|
+
return { ...resolution, admissionDecision: 'create_new', plan: successor };
|
|
327
|
+
}
|
|
328
|
+
if (resolution.admissionDecision !== 'create_new') return resolution;
|
|
329
|
+
return { ...resolution, plan: createPlanContractUnlocked(options, input) };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function admitPlanContract(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
|
|
333
|
+
return withPlanAdmissionLock(options, () => admitPlanContractUnlocked(options, input));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export async function admitPlanContractAsync(options: PlanContractStoreOptions, input: AdmitPlanContractInput): Promise<AdmitPlanContractResult> {
|
|
337
|
+
return await withPlanAdmissionLockAsync(options, () => admitPlanContractUnlocked(options, input));
|
|
338
|
+
}
|
|
339
|
+
|
|
240
340
|
export function getPlanContract(options: PlanContractStoreOptions, planId: string): PlanContract | undefined {
|
|
241
341
|
return readPlanContractStore(options).contracts.find((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
242
342
|
}
|
|
@@ -301,11 +401,12 @@ function approvalErrors(plan: PlanContract, allPlans: readonly PlanContract[]):
|
|
|
301
401
|
return [...new Set(errors)];
|
|
302
402
|
}
|
|
303
403
|
|
|
304
|
-
|
|
404
|
+
function approvePlanContractUnlocked(options: PlanContractStoreOptions, planId: string): PlanContract {
|
|
305
405
|
const store = readPlanContractStore(options);
|
|
306
406
|
const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
307
407
|
if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
|
|
308
408
|
const current = store.contracts[index];
|
|
409
|
+
assertRequirementReference(options, current.requirementId);
|
|
309
410
|
if (current.status !== 'draft' && current.status !== 'reviewing') throw new Error(`plan contract ${current.planId} cannot be approved from ${current.status}`);
|
|
310
411
|
const errors = approvalErrors(current, store.contracts);
|
|
311
412
|
if (errors.length > 0) throw new Error(`plan contract ${current.planId} cannot be approved: ${errors.join('; ')}`);
|
|
@@ -317,20 +418,33 @@ export function approvePlanContract(options: PlanContractStoreOptions, planId: s
|
|
|
317
418
|
return next;
|
|
318
419
|
}
|
|
319
420
|
|
|
421
|
+
export function approvePlanContract(options: PlanContractStoreOptions, planId: string): PlanContract {
|
|
422
|
+
return withPlanAdmissionLock(options, () => approvePlanContractUnlocked(options, planId));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export async function approvePlanContractAsync(options: PlanContractStoreOptions, planId: string): Promise<PlanContract> {
|
|
426
|
+
return await withPlanAdmissionLockAsync(options, () => approvePlanContractUnlocked(options, planId));
|
|
427
|
+
}
|
|
428
|
+
|
|
320
429
|
export function supersedePlanContract(options: PlanContractStoreOptions, planId: string, supersededBy: string): PlanContract {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
430
|
+
return withPlanAdmissionLock(options, () => {
|
|
431
|
+
const store = readPlanContractStore(options);
|
|
432
|
+
const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
433
|
+
if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
|
|
434
|
+
const current = store.contracts[index]!;
|
|
435
|
+
if (isTerminalPlanContractStatus(current.status)) throw new Error(`plan contract ${current.planId} is terminal (${current.status})`);
|
|
436
|
+
const replacement = sanitizeFileComponent(supersededBy);
|
|
437
|
+
if (!replacement || replacement === 'unknown') throw new Error('superseded_by is required');
|
|
438
|
+
if (replacement === current.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
|
|
439
|
+
const successor = store.contracts.find((contract) => contract.planId === replacement);
|
|
440
|
+
if (!successor) throw new Error(`PLAN_SUCCESSOR_NOT_FOUND: ${replacement}`);
|
|
441
|
+
const at = nowIso(options);
|
|
442
|
+
const next = { ...current, status: 'superseded' as const, supersededBy: successor.planId, updatedAt: at };
|
|
443
|
+
const contracts = [...store.contracts];
|
|
444
|
+
contracts[index] = next;
|
|
445
|
+
writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts });
|
|
446
|
+
return next;
|
|
447
|
+
});
|
|
334
448
|
}
|
|
335
449
|
|
|
336
450
|
/**
|
|
@@ -343,6 +457,7 @@ export function claimPlanStepForWork(
|
|
|
343
457
|
input: { planId: string; stepId: string; workId: string; sourceRevision: string },
|
|
344
458
|
): PlanContract {
|
|
345
459
|
return updatePlanContract(options, input.planId, (current) => {
|
|
460
|
+
assertRequirementReference(options, current.requirementId);
|
|
346
461
|
if (current.status !== 'approved' && current.status !== 'executing') {
|
|
347
462
|
throw new Error(`PLAN_NOT_EXECUTABLE: ${current.planId} is ${current.status}`);
|
|
348
463
|
}
|
|
@@ -363,6 +478,45 @@ export function claimPlanStepForWork(
|
|
|
363
478
|
});
|
|
364
479
|
}
|
|
365
480
|
|
|
481
|
+
export function repairDanglingPlanStepWorkBinding(
|
|
482
|
+
options: PlanContractStoreOptions,
|
|
483
|
+
input: { planId: string; stepId: string; expectedWorkId: string; reason: string },
|
|
484
|
+
): PlanContract {
|
|
485
|
+
const expectedWorkId = input.expectedWorkId.trim();
|
|
486
|
+
const reason = input.reason.trim();
|
|
487
|
+
if (!expectedWorkId) throw new Error('PLAN_STEP_REPAIR_EXPECTED_WORK_REQUIRED');
|
|
488
|
+
if (!reason) throw new Error('PLAN_STEP_REPAIR_REASON_REQUIRED');
|
|
489
|
+
return updatePlanContract(options, input.planId, (current) => {
|
|
490
|
+
if (isTerminalPlanContractStatus(current.status)) throw new Error(`PLAN_STEP_REPAIR_PLAN_TERMINAL: ${current.planId} is ${current.status}`);
|
|
491
|
+
const stepIndex = current.steps.findIndex((step) => step.id === sanitizeFileComponent(input.stepId));
|
|
492
|
+
if (stepIndex < 0) throw new Error(`PLAN_STEP_NOT_FOUND: ${input.stepId}`);
|
|
493
|
+
const step = current.steps[stepIndex];
|
|
494
|
+
if (step.workId !== expectedWorkId) {
|
|
495
|
+
throw new Error(`PLAN_STEP_REPAIR_BINDING_CHANGED: ${step.id} is bound to ${step.workId ?? 'no Work'}, expected ${expectedWorkId}`);
|
|
496
|
+
}
|
|
497
|
+
if (step.status !== 'executing') {
|
|
498
|
+
throw new Error(`PLAN_STEP_REPAIR_STATUS_INVALID: ${step.id} is ${step.status}`);
|
|
499
|
+
}
|
|
500
|
+
const at = nowIso(options);
|
|
501
|
+
const steps = [...current.steps];
|
|
502
|
+
steps[stepIndex] = {
|
|
503
|
+
...step,
|
|
504
|
+
status: 'ready',
|
|
505
|
+
workId: undefined,
|
|
506
|
+
evidenceRefs: [{
|
|
507
|
+
title: 'dangling Work binding repaired',
|
|
508
|
+
summary: `${expectedWorkId}: ${reason}`.slice(0, 2_000),
|
|
509
|
+
detailLevel: 'summary' as const,
|
|
510
|
+
}, ...step.evidenceRefs].slice(0, 20),
|
|
511
|
+
};
|
|
512
|
+
// This is admission-state repair, not semantic replanning: the Plan and its
|
|
513
|
+
// acceptance criteria remain authoritative. Return the exact step to ready
|
|
514
|
+
// so a later start can atomically admit one replacement for the missing
|
|
515
|
+
// record without forcing a second Plan authority.
|
|
516
|
+
return { ...current, status: 'executing', steps, updatedAt: at };
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
366
520
|
export function completePlanStepForWork(
|
|
367
521
|
options: PlanContractStoreOptions,
|
|
368
522
|
input: {
|
|
@@ -376,28 +530,59 @@ export function completePlanStepForWork(
|
|
|
376
530
|
if (stepIndex < 0) throw new Error(`PLAN_STEP_NOT_FOUND: ${input.stepId}`);
|
|
377
531
|
const step = current.steps[stepIndex];
|
|
378
532
|
if (step.workId !== input.work.workId) throw new Error(`PLAN_STEP_WORK_MISMATCH: ${input.stepId}`);
|
|
379
|
-
|
|
533
|
+
if (step.status === 'completed') return current;
|
|
534
|
+
const delivered = input.work.status === 'completed'
|
|
380
535
|
&& input.work.phase === 'cleanup'
|
|
381
536
|
&& input.work.evidenceState === 'valid'
|
|
382
537
|
&& Boolean(input.work.completionOutcome && input.work.completionOutcome !== 'superseded')
|
|
383
538
|
&& Boolean(input.work.completionReceipt);
|
|
384
539
|
const failed = input.work.status === 'failed' || input.work.status === 'cancelled';
|
|
385
|
-
if (!
|
|
540
|
+
if (!delivered && !failed) throw new Error(`PLAN_STEP_WORK_NOT_TERMINAL: ${input.work.workId}`);
|
|
386
541
|
const at = nowIso(options);
|
|
387
542
|
const steps = [...current.steps];
|
|
388
543
|
steps[stepIndex] = {
|
|
389
544
|
...step,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
545
|
+
// Work completion proves machine delivery only. The Plan step remains in
|
|
546
|
+
// semantic validation until an explicit Controller acceptance records it.
|
|
547
|
+
// Failed/cancelled Work returns the slice to ready for replanning.
|
|
548
|
+
status: delivered ? 'validating' : 'ready',
|
|
549
|
+
// Successful delivery keeps the exact Work identity through semantic
|
|
550
|
+
// validation. Failed/cancelled Work releases the step authority so the
|
|
551
|
+
// replanning path cannot retain a ghost binding to a terminal Work.
|
|
552
|
+
workId: delivered ? step.workId : undefined,
|
|
553
|
+
evidenceRefs: input.work.evidenceRefs.length > 0 ? input.work.evidenceRefs.slice(0, 20) : step.evidenceRefs,
|
|
554
|
+
};
|
|
555
|
+
return { ...current, status: delivered ? 'verifying' : 'replanning', steps, updatedAt: at };
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function acceptPlanStepEvidence(
|
|
560
|
+
options: PlanContractStoreOptions,
|
|
561
|
+
input: { planId: string; stepId: string; reviewer: string; rationale: string; acceptedSourceRevision?: string },
|
|
562
|
+
): PlanContract {
|
|
563
|
+
const reviewer = input.reviewer.trim();
|
|
564
|
+
const rationale = input.rationale.trim();
|
|
565
|
+
if (!reviewer || !rationale) throw new Error('PLAN_STEP_SEMANTIC_ACCEPTANCE_METADATA_REQUIRED');
|
|
566
|
+
return updatePlanContract(options, input.planId, (current) => {
|
|
567
|
+
const stepIndex = current.steps.findIndex((step) => step.id === sanitizeFileComponent(input.stepId));
|
|
568
|
+
if (stepIndex < 0) throw new Error(`PLAN_STEP_NOT_FOUND: ${input.stepId}`);
|
|
569
|
+
const step = current.steps[stepIndex];
|
|
570
|
+
if (step.status === 'completed') return current;
|
|
571
|
+
if (step.status !== 'validating') throw new Error(`PLAN_STEP_NOT_READY_FOR_SEMANTIC_ACCEPTANCE: ${step.id} is ${step.status}`);
|
|
572
|
+
const steps = [...current.steps];
|
|
573
|
+
steps[stepIndex] = {
|
|
574
|
+
...step,
|
|
575
|
+
status: 'completed',
|
|
576
|
+
evidenceRefs: [{ title: 'semantic acceptance', summary: `${reviewer}: ${rationale}`, detailLevel: 'summary' as const }, ...step.evidenceRefs].slice(0, 20),
|
|
394
577
|
};
|
|
395
578
|
const allCompleted = steps.every((candidate) => candidate.status === 'completed');
|
|
579
|
+
const acceptedSourceRevision = input.acceptedSourceRevision?.trim();
|
|
396
580
|
return {
|
|
397
581
|
...current,
|
|
398
|
-
|
|
582
|
+
...(acceptedSourceRevision ? { sourceRevision: acceptedSourceRevision } : {}),
|
|
583
|
+
status: allCompleted ? 'finalized' : 'executing',
|
|
399
584
|
steps,
|
|
400
|
-
updatedAt:
|
|
585
|
+
updatedAt: nowIso(options),
|
|
401
586
|
};
|
|
402
587
|
});
|
|
403
588
|
}
|
|
@@ -28,7 +28,9 @@ export interface RequirementBoardItem {
|
|
|
28
28
|
maintenanceSummary?: string;
|
|
29
29
|
blocker?: string;
|
|
30
30
|
requiredUserDecision?: string;
|
|
31
|
+
/** Compatibility projection for older UI clients; derived from activePlanIds. */
|
|
31
32
|
activePlanId?: string;
|
|
33
|
+
activePlanIds: string[];
|
|
32
34
|
latestDelivery?: {
|
|
33
35
|
workId: string;
|
|
34
36
|
receiptId: string;
|
|
@@ -55,10 +57,14 @@ function explicitUserDecision(requirement: Requirement): string | undefined {
|
|
|
55
57
|
return decision || undefined;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
function
|
|
60
|
+
function activePlansForRequirement(requirementId: string, plans: readonly PlanContract[]): PlanContract[] {
|
|
61
|
+
return plans.filter((plan) => plan.requirementId === requirementId);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function projectedRequirementState(requirement: Requirement, activePlans: readonly PlanContract[]): RequirementState {
|
|
59
65
|
if (requirement.state !== 'waiting_for_user') return requirement.state;
|
|
60
66
|
if (explicitUserDecision(requirement)) return 'waiting_for_user';
|
|
61
|
-
return
|
|
67
|
+
return activePlans.length > 0 ? 'active' : 'planned';
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
function loadScopedWork(options: RequirementBoardOptions): WorkContract[] {
|
|
@@ -92,8 +98,10 @@ function latestDelivery(requirementId: string, work: readonly WorkContract[]): R
|
|
|
92
98
|
};
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
function projectRequirement(requirement: Requirement, work: readonly WorkContract[]): RequirementBoardItem {
|
|
96
|
-
const
|
|
101
|
+
function projectRequirement(requirement: Requirement, work: readonly WorkContract[], plans: readonly PlanContract[] = []): RequirementBoardItem {
|
|
102
|
+
const activePlans = activePlansForRequirement(requirement.requirementId, plans);
|
|
103
|
+
const activePlanIds = activePlans.map((plan) => plan.planId);
|
|
104
|
+
const state = projectedRequirementState(requirement, activePlans);
|
|
97
105
|
const requiredUserDecision = explicitUserDecision(requirement);
|
|
98
106
|
const maintenanceSummary = requirement.needsAttention
|
|
99
107
|
? boundedText(requirement.attentionSummary, 1_000) || 'This Requirement has a maintenance finding.'
|
|
@@ -108,7 +116,8 @@ function projectRequirement(requirement: Requirement, work: readonly WorkContrac
|
|
|
108
116
|
maintenanceSummary,
|
|
109
117
|
blocker: requirement.needsAttention && state !== 'waiting_for_user' ? maintenanceSummary : undefined,
|
|
110
118
|
requiredUserDecision,
|
|
111
|
-
activePlanId:
|
|
119
|
+
activePlanId: activePlanIds[0],
|
|
120
|
+
activePlanIds,
|
|
112
121
|
latestDelivery: latestDelivery(requirement.requirementId, work),
|
|
113
122
|
updatedAt: requirement.updatedAt,
|
|
114
123
|
detailPointer: {
|
|
@@ -132,7 +141,10 @@ function requirementCounts(requirements: readonly RequirementBoardItem[]): Recor
|
|
|
132
141
|
|
|
133
142
|
export function buildRequirementBoard(options: RequirementBoardOptions): Record<string, unknown> {
|
|
134
143
|
const work = loadScopedWork(options);
|
|
135
|
-
const
|
|
144
|
+
const activePlans = options.repoId?.trim()
|
|
145
|
+
? listPlanContracts({ controllerHome: options.controllerHome, repoId: options.repoId, status: 'active', limit: 100 })
|
|
146
|
+
: [];
|
|
147
|
+
const allRequirements = loadRequirements(options).map((requirement) => projectRequirement(requirement, work, activePlans));
|
|
136
148
|
const requirements = allRequirements.slice(0, DEFAULT_REQUIREMENT_LIMIT);
|
|
137
149
|
const counts = requirementCounts(allRequirements);
|
|
138
150
|
return {
|
|
@@ -229,9 +241,13 @@ export function buildExecutionDiagnostics(
|
|
|
229
241
|
: [];
|
|
230
242
|
const scopedPlans = options.requirementId
|
|
231
243
|
? allPlans.filter((plan) => plan.requirementId === options.requirementId
|
|
232
|
-
|
|
244
|
+
// Legacy migration compatibility only: current Plans own the forward
|
|
245
|
+
// requirementId relation, so the persisted Requirement pointer is not an
|
|
246
|
+
// execution/lifecycle authority.
|
|
247
|
+
|| (!plan.requirementId && selectedRequirements.some((requirement) => requirement.activePlanId === plan.planId)))
|
|
233
248
|
: allPlans;
|
|
234
|
-
const
|
|
249
|
+
const activePlans = allPlans.filter((plan) => !['finalized', 'superseded', 'cancelled', 'invalidated_by_drift'].includes(plan.status));
|
|
250
|
+
const requirements = selectedRequirements.map((requirement) => projectRequirement(requirement, allWork, activePlans));
|
|
235
251
|
const works = scopedWork.slice(0, limit).map(summarizeDiagnosticWork);
|
|
236
252
|
const plans = scopedPlans.slice(0, limit).map((plan) => summarizeDiagnosticPlan(plan, options.detailLevel));
|
|
237
253
|
const maintenanceFindings = requirements
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { withControllerLock, withControllerLockAsync } from '../../../cli/repositories/locks';
|
|
2
|
+
import { sanitizeFileComponent } from '../../shared/json-files';
|
|
3
|
+
import { isTerminalPlanContractStatus, type PlanContract } from './types';
|
|
4
|
+
|
|
5
|
+
export const PLAN_ADMISSION_RELATIONS = ['extend', 'parallel'] as const;
|
|
6
|
+
export type PlanAdmissionRelation = (typeof PLAN_ADMISSION_RELATIONS)[number];
|
|
7
|
+
|
|
8
|
+
export type PlanAdmissionReason =
|
|
9
|
+
| 'exact_scope_authority'
|
|
10
|
+
| 'requirement_relation_required'
|
|
11
|
+
| 'extension_target_required'
|
|
12
|
+
| 'extend_existing'
|
|
13
|
+
| 'create_new';
|
|
14
|
+
|
|
15
|
+
export interface PlanAdmissionInput {
|
|
16
|
+
requirementId?: string;
|
|
17
|
+
scopeKey: string;
|
|
18
|
+
planRelation?: PlanAdmissionRelation;
|
|
19
|
+
relatedPlanId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PlanAdmissionResolution {
|
|
23
|
+
admissionDecision: 'reuse_existing' | 'resolution_required' | 'extend_existing' | 'create_new';
|
|
24
|
+
resolutionRequired: boolean;
|
|
25
|
+
reason: PlanAdmissionReason;
|
|
26
|
+
normalizedScopeKey: string;
|
|
27
|
+
plan?: PlanContract;
|
|
28
|
+
candidates: PlanContract[];
|
|
29
|
+
allowedPlanRelations?: readonly PlanAdmissionRelation[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizePlanScopeKey(value: string): string {
|
|
33
|
+
const raw = String(value ?? '').trim();
|
|
34
|
+
if (!raw) return '';
|
|
35
|
+
const normalized = sanitizeFileComponent(raw).toLowerCase().slice(0, 160);
|
|
36
|
+
return normalized === 'unknown' ? '' : normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolvePlanAdmission(
|
|
40
|
+
plans: readonly PlanContract[],
|
|
41
|
+
input: PlanAdmissionInput,
|
|
42
|
+
): PlanAdmissionResolution {
|
|
43
|
+
const activePlans = plans.filter((plan) => !isTerminalPlanContractStatus(plan.status));
|
|
44
|
+
const normalizedScopeKey = normalizePlanScopeKey(input.scopeKey);
|
|
45
|
+
const requirementId = input.requirementId?.trim() || undefined;
|
|
46
|
+
const requirementPlans = requirementId
|
|
47
|
+
? activePlans.filter((candidate) => candidate.requirementId === requirementId)
|
|
48
|
+
: [];
|
|
49
|
+
const relatedPlanId = input.relatedPlanId?.trim() || undefined;
|
|
50
|
+
const explicitlyRelatedPlan = relatedPlanId
|
|
51
|
+
? activePlans.find((candidate) => candidate.planId === relatedPlanId)
|
|
52
|
+
: undefined;
|
|
53
|
+
const relatedPlan = explicitlyRelatedPlan && (!requirementId || explicitlyRelatedPlan.requirementId === requirementId)
|
|
54
|
+
? explicitlyRelatedPlan
|
|
55
|
+
: !relatedPlanId && requirementPlans.length === 1 ? requirementPlans[0] : undefined;
|
|
56
|
+
const exactScopeAuthority = normalizedScopeKey
|
|
57
|
+
? activePlans.find((candidate) => candidate.scopeKey === normalizedScopeKey)
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
// An explicit extension is a serial replan, not a second active Plan. Resolve
|
|
61
|
+
// it before exact-scope reuse so a successor may retain the predecessor scope.
|
|
62
|
+
// If another Plan owns the requested successor scope, preserve that authority.
|
|
63
|
+
if (input.planRelation === 'extend') {
|
|
64
|
+
if (!relatedPlan) {
|
|
65
|
+
return {
|
|
66
|
+
admissionDecision: 'resolution_required',
|
|
67
|
+
resolutionRequired: true,
|
|
68
|
+
reason: 'extension_target_required',
|
|
69
|
+
normalizedScopeKey,
|
|
70
|
+
candidates: (requirementPlans.length > 0 ? requirementPlans : activePlans).slice(0, 8),
|
|
71
|
+
allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (exactScopeAuthority && exactScopeAuthority.planId !== relatedPlan.planId) {
|
|
75
|
+
return {
|
|
76
|
+
admissionDecision: 'reuse_existing',
|
|
77
|
+
resolutionRequired: false,
|
|
78
|
+
reason: 'exact_scope_authority',
|
|
79
|
+
normalizedScopeKey,
|
|
80
|
+
plan: exactScopeAuthority,
|
|
81
|
+
candidates: [exactScopeAuthority],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
admissionDecision: 'extend_existing',
|
|
86
|
+
resolutionRequired: false,
|
|
87
|
+
reason: 'extend_existing',
|
|
88
|
+
normalizedScopeKey,
|
|
89
|
+
plan: relatedPlan,
|
|
90
|
+
candidates: requirementPlans.length > 0 ? requirementPlans.slice(0, 8) : [relatedPlan],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (exactScopeAuthority) {
|
|
95
|
+
return {
|
|
96
|
+
admissionDecision: 'reuse_existing',
|
|
97
|
+
resolutionRequired: false,
|
|
98
|
+
reason: 'exact_scope_authority',
|
|
99
|
+
normalizedScopeKey,
|
|
100
|
+
plan: exactScopeAuthority,
|
|
101
|
+
candidates: [exactScopeAuthority],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (requirementPlans.length > 0 && !input.planRelation) {
|
|
106
|
+
return {
|
|
107
|
+
admissionDecision: 'resolution_required',
|
|
108
|
+
resolutionRequired: true,
|
|
109
|
+
reason: 'requirement_relation_required',
|
|
110
|
+
normalizedScopeKey,
|
|
111
|
+
candidates: requirementPlans.slice(0, 8),
|
|
112
|
+
allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
admissionDecision: 'create_new',
|
|
118
|
+
resolutionRequired: false,
|
|
119
|
+
reason: 'create_new',
|
|
120
|
+
normalizedScopeKey,
|
|
121
|
+
candidates: requirementPlans.slice(0, 8),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Short per-repository semantic admission critical section. The fixed task key
|
|
127
|
+
* keeps admission independent from repository/worktree execution locks while
|
|
128
|
+
* still coordinating competing controller processes for the same repository.
|
|
129
|
+
*/
|
|
130
|
+
const planAdmissionLockKey = (repoId: string) => ({
|
|
131
|
+
scope: 'task' as const,
|
|
132
|
+
repoId,
|
|
133
|
+
taskId: 'semantic-plan-admission',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
export function withPlanAdmissionLock<T>(
|
|
137
|
+
options: { controllerHome?: string; repoId?: string },
|
|
138
|
+
operation: () => T,
|
|
139
|
+
): T {
|
|
140
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
|
|
141
|
+
return withControllerLock(
|
|
142
|
+
options.controllerHome,
|
|
143
|
+
planAdmissionLockKey(options.repoId),
|
|
144
|
+
'semantic-plan-admission',
|
|
145
|
+
operation,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Gateway-facing admission waits briefly for a competing controller to finish
|
|
150
|
+
* its tiny create/approve decision, then re-reads canonical state under lock. */
|
|
151
|
+
export async function withPlanAdmissionLockAsync<T>(
|
|
152
|
+
options: { controllerHome?: string; repoId?: string },
|
|
153
|
+
operation: () => T | Promise<T>,
|
|
154
|
+
): Promise<T> {
|
|
155
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
|
|
156
|
+
return await withControllerLockAsync(
|
|
157
|
+
options.controllerHome,
|
|
158
|
+
planAdmissionLockKey(options.repoId),
|
|
159
|
+
'semantic-plan-admission',
|
|
160
|
+
async () => await operation(),
|
|
161
|
+
undefined,
|
|
162
|
+
500,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const primaryWorkAdmissionLockKey = (repoId: string) => ({
|
|
167
|
+
scope: 'task' as const,
|
|
168
|
+
repoId,
|
|
169
|
+
taskId: 'semantic-primary-work-admission',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Primary Work admission protects only canonical ownership resolution and
|
|
174
|
+
* persistence. It must never span workspace materialization, dependency prep,
|
|
175
|
+
* checks, worker launch, or the Work execution lifetime.
|
|
176
|
+
*/
|
|
177
|
+
export function withPrimaryWorkAdmissionLock<T>(
|
|
178
|
+
options: { controllerHome?: string; repoId?: string },
|
|
179
|
+
operation: () => T,
|
|
180
|
+
): T {
|
|
181
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
|
|
182
|
+
return withControllerLock(
|
|
183
|
+
options.controllerHome,
|
|
184
|
+
primaryWorkAdmissionLockKey(options.repoId),
|
|
185
|
+
'semantic-primary-work-admission',
|
|
186
|
+
operation,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Gateway callers wait briefly for a competing admission, then re-read the
|
|
191
|
+
* canonical Plan/Work state while holding the same short critical section. */
|
|
192
|
+
export async function withPrimaryWorkAdmissionLockAsync<T>(
|
|
193
|
+
options: { controllerHome?: string; repoId?: string },
|
|
194
|
+
operation: () => T | Promise<T>,
|
|
195
|
+
): Promise<T> {
|
|
196
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
|
|
197
|
+
return await withControllerLockAsync(
|
|
198
|
+
options.controllerHome,
|
|
199
|
+
primaryWorkAdmissionLockKey(options.repoId),
|
|
200
|
+
'semantic-primary-work-admission',
|
|
201
|
+
async () => await operation(),
|
|
202
|
+
undefined,
|
|
203
|
+
500,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
@@ -11,10 +11,13 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
|
|
|
11
11
|
case 'delegate':
|
|
12
12
|
case 'controller_claim':
|
|
13
13
|
case 'controller_release':
|
|
14
|
+
case 'controller_disposition':
|
|
14
15
|
case 'launcher_start':
|
|
16
|
+
case 'requirement_create':
|
|
15
17
|
case 'schedule_create':
|
|
16
18
|
case 'schedule_pause':
|
|
17
19
|
case 'schedule_resume':
|
|
20
|
+
case 'schedule_delete':
|
|
18
21
|
case 'schedule_trigger':
|
|
19
22
|
return 'workspace_write';
|
|
20
23
|
case 'controller_get_owner':
|
|
@@ -26,6 +29,7 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
|
|
|
26
29
|
case 'plan_list':
|
|
27
30
|
return 'readonly';
|
|
28
31
|
case 'plan_approve':
|
|
32
|
+
case 'plan_accept_step':
|
|
29
33
|
case 'plan_supersede':
|
|
30
34
|
return 'workspace_write';
|
|
31
35
|
case 'finalize':
|
|
@@ -40,7 +44,7 @@ const ALLOWED_FACADE_OPERATIONS: Record<FacadeTool, readonly string[]> = {
|
|
|
40
44
|
rh_status: ['list', 'get', 'repair'],
|
|
41
45
|
rh_inbox: ['list', 'get', 'ack', 'accept', 'resolve', 'dismiss', 'create'],
|
|
42
46
|
rh_context: ['list', 'get', 'search'],
|
|
43
|
-
rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_get_owner', 'launcher_start', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_trigger'],
|
|
47
|
+
rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_disposition', 'controller_get_owner', 'launcher_start', 'requirement_create', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_accept_step', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_delete', 'schedule_trigger'],
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
export interface SuggestedActionValidationOptions {
|