@moretea-labs/forge 1.5.0-rc.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -11
- package/CHANGELOG.md +27 -1
- package/CLAUDE.md +8 -10
- package/DESIGN.md +278 -0
- package/README.md +70 -90
- package/README.zh-CN.md +52 -55
- package/SKILL.md +8 -11
- package/THIRD_PARTY_NOTICES.md +9 -3
- package/assets/branding/README.md +4 -2
- package/assets/hooks/lib/workflow-state.sh +19 -18
- package/assets/hooks/post-edit-guard.sh +6 -75
- package/assets/hooks/prompt-guard.sh +24 -12
- package/assets/hooks/session-start-context.sh +17 -17
- package/assets/hooks/stop-orchestrator.sh +2 -2
- package/assets/partials/04-project-structure.partial.md +6 -6
- package/assets/partials/05-workflow.partial.md +6 -6
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials/08-orchestration.partial.md +2 -2
- package/assets/partials-agents/02-operating-mode.partial.md +5 -5
- package/assets/partials-agents/03-orchestration.partial.md +3 -3
- package/assets/partials-agents/04-task-protocol.partial.md +3 -3
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/reference-configs/agentic-development-flow.md +49 -86
- package/assets/reference-configs/external-tooling.md +21 -61
- package/assets/reference-configs/handoff-protocol.md +25 -20
- package/assets/reference-configs/harness-overview.md +8 -8
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/archive-workflow.sh +0 -1
- package/assets/templates/helpers/capture-plan.sh +3 -3
- package/assets/templates/helpers/check-task-workflow.sh +2 -2
- package/assets/templates/helpers/codex-handoff-resume.sh +5 -5
- package/assets/templates/helpers/ensure-task-workflow.sh +37 -35
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +2 -2
- package/assets/templates/helpers/prepare-codex-handoff.sh +2 -2
- package/assets/templates/helpers/prepare-handoff.sh +9 -9
- package/assets/templates/helpers/refresh-current-status.sh +2 -2
- package/assets/templates/helpers/ship-worktrees.sh +1 -1
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/verify-sprint.sh +7 -7
- package/assets/templates/helpers/workstream-sync.sh +1 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/workflow-contract.v1.json +6 -3
- package/docs/architecture/current/README.md +10 -9
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +2 -2
- package/docs/architecture/current/architecture-invariants.md +2 -2
- package/docs/architecture/current/automation-and-schedule-engine.md +15 -15
- package/docs/architecture/current/codegraph-context-provider.md +2 -2
- package/docs/architecture/current/control-plane-authority-inventory.md +12 -0
- package/docs/architecture/current/dispatch-and-agent-strategy.md +11 -11
- package/docs/architecture/current/entity-model.md +0 -27
- package/docs/architecture/current/failure-recovery.md +0 -24
- package/docs/architecture/current/governance.md +1 -1
- package/docs/architecture/current/implementation-status.md +1 -1
- package/docs/architecture/current/legacy-governance.md +58 -0
- package/docs/architecture/current/multi-repository-execution.md +1 -1
- package/docs/architecture/current/peer-supercontroller-runtime.md +1 -1
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +4 -5
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -10
- package/docs/architecture/current/target-requirements-traceability.md +2 -2
- package/docs/architecture/current/thin-harness-governance-policy.md +1 -1
- package/docs/architecture/current/thin-harness-v1.md +9 -11
- package/docs/architecture/domains/runtime-harness.md +1 -1
- package/docs/forge-autonomous-goal-loop.md +3 -1
- package/docs/forge-chatgpt-mcp-setup.md +67 -176
- package/docs/images/forge-banner-cn.svg +26 -1
- package/docs/images/forge-banner.svg +26 -1
- package/docs/operations/features.md +1 -1
- package/docs/operations/homebrew.md +3 -3
- package/docs/operations/mcp-tool-exposure.md +5 -5
- package/docs/operations/platform-support.md +45 -37
- package/docs/operations/platform-support.zh-CN.md +40 -38
- package/docs/operations/releasing.md +3 -3
- package/docs/operations/releasing.zh-CN.md +3 -3
- package/docs/operations/troubleshooting.md +11 -6
- package/docs/operations/troubleshooting.zh-CN.md +11 -6
- package/docs/public-usage-guide.md +28 -21
- package/docs/public-usage-guide.zh-CN.md +28 -21
- package/docs/tutorials/01-install-and-start.md +58 -35
- package/docs/tutorials/01-install-and-start.zh-CN.md +58 -35
- package/docs/tutorials/02-connect-chatgpt.md +118 -46
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +119 -45
- package/docs/wiki/Architecture.md +1 -1
- package/docs/wiki/Installation.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/install.ps1 +4 -5
- package/install.sh +4 -6
- package/package.json +15 -5
- package/scripts/archive-workflow.sh +0 -1
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +27 -14
- package/scripts/build-controller-ui.ts +28 -0
- package/scripts/capture-plan.sh +4 -4
- package/scripts/check-agent-tooling.sh +12 -1
- package/scripts/check-platform-support.mjs +2 -2
- package/scripts/check-public-docs.sh +16 -5
- package/scripts/check-release-readiness.sh +5 -11
- package/scripts/check-runtime-architecture.mjs +14 -22
- package/scripts/check-tarball-install-smoke.sh +3 -2
- package/scripts/check-task-workflow.sh +2 -2
- package/scripts/codex-handoff-resume.sh +5 -5
- package/scripts/create-project-dirs.sh +3 -3
- package/scripts/ensure-task-workflow.sh +45 -35
- package/scripts/init-project.sh +4 -4
- package/scripts/install-desktop-operator-registration.ts +2 -2
- package/scripts/lib/project-init-lib.sh +46 -44
- package/scripts/migrate-project-template.sh +7 -4
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +2 -2
- package/scripts/prepare-codex-handoff.sh +2 -2
- package/scripts/prepare-handoff.sh +9 -9
- package/scripts/refresh-current-status.sh +2 -2
- package/scripts/setup-plugins.sh +3 -3
- package/scripts/ship-worktrees.sh +1 -1
- package/scripts/sprint-backlog.sh +5 -5
- package/scripts/sync-codex-installed-copies.sh +2 -3
- package/scripts/verify-sprint.sh +7 -7
- package/scripts/workstream-sync.sh +1 -1
- package/src/cli/chatgpt-browser/bridge-extension.ts +1 -1
- package/src/cli/chatgpt-browser/bridge-provider.ts +29 -0
- package/src/cli/chatgpt-browser/file-policy.ts +3 -1
- package/src/cli/commands/chatgpt.ts +51 -0
- package/src/cli/commands/doctor.ts +1 -1
- package/src/cli/commands/init-hook.ts +213 -70
- package/src/cli/commands/init.ts +5 -6
- package/src/cli/commands/mcp.ts +31 -5
- package/src/cli/commands/runtime.ts +61 -8
- package/src/cli/commands/setup-profile.ts +472 -0
- package/src/cli/controller/check-runner.ts +20 -3
- package/src/cli/controller/codex-continuation.ts +4 -7
- package/src/cli/controller/context-pack.ts +118 -14
- package/src/cli/controller/operational-plan.ts +4 -4
- package/src/cli/controller/task-ledger.ts +10 -10
- package/src/cli/controller/work-mode.ts +6 -13
- package/src/cli/index.ts +14 -35
- package/src/cli/local-bridge/connector-freshness.ts +1 -1
- package/src/cli/local-bridge/console-assets.ts +17 -0
- package/src/cli/local-bridge/console-automations.ts +160 -0
- package/src/cli/local-bridge/dashboard.ts +8 -1303
- package/src/cli/local-bridge/facade-api.ts +16 -7
- package/src/cli/local-bridge/frontend/src/App.tsx +10 -0
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +11 -0
- package/src/cli/local-bridge/frontend/src/design/tokens.css +8 -0
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +4 -0
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/lib/api.ts +23 -0
- package/src/cli/local-bridge/frontend/src/lib/format.ts +4 -0
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +7 -0
- package/src/cli/local-bridge/frontend/src/main.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +1 -0
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +2 -0
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +3 -0
- package/src/cli/local-bridge/frontend/src/styles.css +12 -0
- package/src/cli/local-bridge/frontend/src/types.ts +14 -0
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +1 -0
- package/src/cli/local-bridge/frontend/vite.config.ts +27 -0
- package/src/cli/local-bridge/server.ts +45 -0
- package/src/cli/local-bridge/ui-dist/app.css +1 -0
- package/src/cli/local-bridge/ui-dist/app.js +11094 -0
- package/src/cli/mcp/auth.ts +2 -0
- package/src/cli/mcp/instructions.ts +3 -3
- package/src/cli/mcp/legacy-tool-service.ts +27 -23
- package/src/cli/mcp/multi-repository.ts +2 -0
- package/src/cli/mcp/policy.ts +7 -5
- package/src/cli/mcp/repository-tools.ts +53 -14
- package/src/cli/mcp/server.ts +5 -10
- package/src/cli/mcp/setup.ts +220 -276
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-names.ts +1 -1
- package/src/cli/mcp/toolset.ts +0 -1
- package/src/cli/mcp/transports/http.ts +51 -4
- package/src/cli/repositories/branch-name-policy.ts +0 -7
- package/src/cli/repositories/controller-home.ts +110 -3
- package/src/cli/repositories/safe-patch.ts +1 -1
- package/src/cli/repositories/selected-path-actions.ts +26 -24
- package/src/core/adoption/gitignore-plan.ts +2 -3
- package/src/core/adoption/plan.ts +4 -1
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +556 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +128 -1
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +277 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +169 -0
- package/src/runtime/control-plane/facade/capability-registry.ts +0 -10
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +44 -25
- package/src/runtime/control-plane/facade/suggested-actions.ts +7 -1
- package/src/runtime/control-plane/facade/types.ts +0 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +30 -8
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +114 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +175 -0
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +161 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +64 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +117 -24
- package/src/runtime/control-plane/routing/route-policy.ts +14 -14
- package/src/runtime/control-plane/runtime-cleanup.ts +0 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/{workflow/campaigns/workspace.ts → execution/managed-workspace.ts} +41 -43
- package/src/runtime/execution/process-runtime/check-facade.ts +6 -0
- package/src/runtime/execution/process-runtime/check-result.ts +45 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -16
- package/src/runtime/execution/process-runtime/command-facade.ts +4 -2
- package/src/runtime/execution/process-runtime/gc.ts +1 -1
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +14 -5
- package/src/runtime/execution/process-runtime/persisted-check.ts +285 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +30 -0
- package/src/runtime/execution/process-runtime/runtime.ts +18 -1
- package/src/runtime/execution/process-runtime/store.ts +1 -19
- package/src/runtime/execution/process-runtime/types.ts +4 -0
- package/src/runtime/execution/thin-harness/execution-router.ts +0 -1
- package/src/runtime/execution/thin-harness/fast-executor.ts +1 -1
- package/src/runtime/execution/thin-harness/lightweight-lanes.ts +1 -2
- package/src/runtime/execution/thin-harness/types.ts +0 -1
- package/src/runtime/execution/workers/executor.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +1 -1
- package/src/runtime/gateway/mcp/persisted-check-process.ts +3 -255
- package/src/runtime/gateway/mcp/process-tools.ts +16 -3
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -9
- package/src/runtime/gateway/mcp/router.ts +117 -182
- package/src/runtime/gateway/mcp/runtime-tools.ts +331 -409
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +1 -219
- package/src/runtime/plugins/browser-adapter.ts +5 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +1 -1
- package/src/runtime/plugins/store.ts +14 -6
- package/src/runtime/plugins/types.ts +1 -1
- package/src/runtime/projections/materialized-view.ts +1 -23
- package/src/runtime/recovery/maintenance-executor.ts +9 -0
- package/src/runtime/resources/ownership.ts +0 -1
- package/src/runtime/root/gateway-adapter.ts +2 -0
- package/src/runtime/root/mcp-transport.ts +10 -2
- package/src/runtime/root/package-connector-service.ts +136 -0
- package/src/runtime/root/package-runtime-release.ts +161 -0
- package/src/runtime/root/package-runtime-service.ts +185 -0
- package/src/runtime/root/release-materialize.ts +19 -0
- package/src/runtime/root/runtime.ts +2 -1
- package/src/runtime/root/scheduler.ts +5 -0
- package/src/runtime/root/service.ts +18 -5
- package/src/runtime/standalone-recovery/core.ts +65 -16
- package/src/runtime/workflow/findings/store.ts +145 -106
- package/src/runtime/workflow/findings/types.ts +22 -0
- package/src/runtime/workflow/schedules/engine.ts +132 -2
- package/src/runtime/workflow/schedules/pending-activation.ts +139 -0
- package/src/runtime/workflow/schedules/work-continuation.ts +218 -0
- package/docs/CHANGELOG.md +0 -978
- package/docs/README.zh-CN.md +0 -58
- package/scripts/smoke-chatgpt-supervised-automation.ts +0 -208
- package/src/cli/commands/controller.ts +0 -680
- package/src/runtime/workflow/campaigns/cleanup.ts +0 -263
- package/src/runtime/workflow/campaigns/engine.ts +0 -112
- package/src/runtime/workflow/campaigns/normalize.ts +0 -100
- package/src/runtime/workflow/campaigns/review.ts +0 -315
- package/src/runtime/workflow/campaigns/store.ts +0 -526
- package/src/runtime/workflow/campaigns/supervisor.ts +0 -112
- package/src/runtime/workflow/campaigns/types.ts +0 -290
- /package/src/runtime/{workflow/campaigns → execution}/workspace-agent.ts +0 -0
|
@@ -14,9 +14,10 @@ import { repositoryControllerRoot } from '../../../cli/repositories/controller-h
|
|
|
14
14
|
import { cancelExecutionJob, findExecutionJob, getExecutionJob, getExecutionJobByRequestId, listExecutionJobs } from '../../execution/jobs/store';
|
|
15
15
|
import { waitForExecutionJob } from '../../execution/jobs/wait';
|
|
16
16
|
import type { ExecutionJob } from '../../execution/jobs/types';
|
|
17
|
-
import { getProcessHandle, processRuntimeResourceDiagnostics, waitForProcess } from '../../execution/process-runtime';
|
|
17
|
+
import { getProcessHandle, getProcessRecord, processCheckCompletionReceipt, processRuntimeResourceDiagnostics, readPersistedCheckResultReceipt, runPersistedCheckViaProcessRuntime, waitForProcess } from '../../execution/process-runtime';
|
|
18
18
|
import { buildJobOperationDigest } from '../../control-plane/facade/operation-digest';
|
|
19
19
|
import { readWorkHandle, type WorkHandleState } from '../../control-plane/execution/work-handle-store';
|
|
20
|
+
import { executionIdentityForRepository } from '../../control-plane/execution/execution-identity';
|
|
20
21
|
import { reconcileFinalizedDirectEditWorksAfterCommit } from '../../control-plane/execution/direct-edit-work-completion';
|
|
21
22
|
import { readJobEvents } from '../../evidence/event-ledger';
|
|
22
23
|
import { readExecutionArtifact } from '../../evidence/artifact-store';
|
|
@@ -41,6 +42,15 @@ import {
|
|
|
41
42
|
} from '../../health';
|
|
42
43
|
import { applyScheduleDedupe, buildScheduleDedupeReport, createSchedule, getSchedule, getScheduleDecision, listOccurrences, listSchedules, saveSchedule } from '../../workflow/schedules/store';
|
|
43
44
|
import { evaluateSchedule } from '../../workflow/schedules/engine';
|
|
45
|
+
import {
|
|
46
|
+
createWorkContinuationSchedule,
|
|
47
|
+
getWorkContinuationSchedule,
|
|
48
|
+
listWorkContinuationSchedules,
|
|
49
|
+
pauseWorkContinuationSchedule,
|
|
50
|
+
resumeWorkContinuationSchedule,
|
|
51
|
+
triggerWorkContinuationSchedule,
|
|
52
|
+
type ContinuationControllerType,
|
|
53
|
+
} from '../../workflow/schedules/work-continuation';
|
|
44
54
|
import { createPortfolioWorkflow, getPortfolioWorkflow, listPortfolioWorkflows } from '../../workflow/portfolio/store';
|
|
45
55
|
import { claimsForMcpOperation } from './resource-policy';
|
|
46
56
|
import {
|
|
@@ -55,16 +65,6 @@ import {
|
|
|
55
65
|
} from './router';
|
|
56
66
|
import { assertAutomatedOperationAllowed } from '../../control-plane/governance/external-effects';
|
|
57
67
|
import { getCandidateFinding, listCandidateFindings, recordCandidateFinding } from '../../workflow/findings/store';
|
|
58
|
-
import { addCampaignTask, createCampaign, getCampaign, listCampaigns, setCampaignStatus, validateCreateCampaignTasks } from '../../workflow/campaigns/store';
|
|
59
|
-
import { reconcileCampaign } from '../../workflow/campaigns/engine';
|
|
60
|
-
import { submitCampaignReview } from '../../workflow/campaigns/review';
|
|
61
|
-
import type { CampaignReviewPolicy, CampaignSupervisorAction, CreateCampaignTaskInput } from '../../workflow/campaigns/types';
|
|
62
|
-
import { currentCampaignWorkspace, ensureCampaignWorkspace } from '../../workflow/campaigns/workspace';
|
|
63
|
-
import { cancelCampaign, completeCampaignWorkspace } from '../../workflow/campaigns/cleanup';
|
|
64
|
-
import {
|
|
65
|
-
assertCampaignOperationSupported,
|
|
66
|
-
normalizeCampaignDependencyReferences,
|
|
67
|
-
} from '../../workflow/campaigns/normalize';
|
|
68
68
|
import { ensureRepositoryRuntimeStorage } from '../../../cli/repositories/runtime-storage';
|
|
69
69
|
import { assessWorkMode, parseExplicitTaskMode } from '../../../cli/controller/work-mode';
|
|
70
70
|
import { projectBoard } from '../../../cli/controller/issue-store';
|
|
@@ -72,16 +72,16 @@ import {
|
|
|
72
72
|
buildControllerTaskLedgerProjection,
|
|
73
73
|
writeControllerTaskLedgerArtifacts,
|
|
74
74
|
} from '../../../cli/controller/task-ledger';
|
|
75
|
-
import { buildControllerContextPack } from '../../../cli/controller/context-pack';
|
|
75
|
+
import { buildControllerContextPack, CONTROLLER_CONTEXT_IMPACT_DOMAINS, type ControllerContextImpactDomain } from '../../../cli/controller/context-pack';
|
|
76
76
|
import { legacyIssueAuthorityRetired } from '../../../cli/controller/legacy-issue-cutover';
|
|
77
77
|
import { buildControllerOperationalPlan } from '../../../cli/controller/operational-plan';
|
|
78
|
-
import { listControllerChecks, readLatestControllerCheckEvidence
|
|
78
|
+
import { listControllerChecks, readLatestControllerCheckEvidence } from '../../../cli/controller/check-runner';
|
|
79
79
|
import { repositoryChangeVerify } from '../../../cli/controller/composite-operations';
|
|
80
80
|
import { listActiveAgentJobSnapshots } from '../../../cli/agent-jobs/job-manager';
|
|
81
81
|
import { readAgentExecutableReadinessSnapshot } from '../../../cli/agent-jobs/executable-resolver';
|
|
82
82
|
import {
|
|
83
83
|
commitSelectedPaths,
|
|
84
|
-
|
|
84
|
+
prepareTransferArtifacts,
|
|
85
85
|
selectedPathDiff,
|
|
86
86
|
stageSelectedPaths,
|
|
87
87
|
} from '../../../cli/repositories/selected-path-actions';
|
|
@@ -284,7 +284,9 @@ export const runtimeToolDefinitions: McpToolDefinition[] = [
|
|
|
284
284
|
known_paths: { type: 'array', items: { type: 'string' }, description: 'Optional exact paths or globs that should receive highest retrieval priority.' },
|
|
285
285
|
include_globs: { type: 'array', items: { type: 'string' } },
|
|
286
286
|
exclude_globs: { type: 'array', items: { type: 'string' } },
|
|
287
|
-
|
|
287
|
+
retrieval_mode: { type: 'string', enum: ['implementation', 'plan', 'debug', 'review'], description: 'Defaults to implementation. Implementation returns current raw impact evidence for ChatGPT to judge; plan/debug/review intentionally widen evidence.' },
|
|
288
|
+
impact_domains: { type: 'array', items: { type: 'string', enum: [...CONTROLLER_CONTEXT_IMPACT_DOMAINS] }, description: 'Optional GPT-selected cross-cutting evidence dimensions. Forge expands these mechanically in the same retrieval call; it never treats them as proof of semantic completeness.' },
|
|
289
|
+
structural_context: { type: 'string', enum: ['off', 'auto', 'required'], description: 'Optional override. Defaults to auto for implementation/review and required for plan/debug.' },
|
|
288
290
|
max_files: { type: 'number' },
|
|
289
291
|
max_snippets: { type: 'number' },
|
|
290
292
|
requested_check_ids: { type: 'array', items: { type: 'string' } },
|
|
@@ -294,16 +296,41 @@ export const runtimeToolDefinitions: McpToolDefinition[] = [
|
|
|
294
296
|
}),
|
|
295
297
|
definition('rh_work', 'Preferred ChatGPT facade: bounded planning, direct control, controller ownership, and external SuperController launch.', {
|
|
296
298
|
repo_id: repoId,
|
|
297
|
-
operation: { type: 'string', enum: ['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'], description: 'Defaults to start. Complex starts require plan_id and plan_step_id.' },
|
|
299
|
+
operation: { type: 'string', enum: ['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'], description: 'Defaults to start. Complex starts require plan_id and plan_step_id. schedule_* manages bounded external-controller continuation for existing Work.' },
|
|
298
300
|
objective: { type: 'string' },
|
|
299
301
|
work_id: { type: 'string' },
|
|
300
302
|
handoff_id: { type: 'string', description: 'Optional existing HandoffItem used to resume controller context.' },
|
|
301
303
|
controller_id: { type: 'string', description: 'Stable external SuperController identity for controller_claim/release.' },
|
|
302
304
|
controller_type: { type: 'string', enum: ['chatgpt', 'codex', 'grok', 'claude', 'human'], description: 'Controller kind for claims and launcher_start.' },
|
|
303
305
|
session_id: { type: 'string', description: 'Controller-owned session identity used for an exclusive Work lease.' },
|
|
304
|
-
lease_ms: { type: 'number', description: 'Controller lease duration, bounded to one hour.' },
|
|
306
|
+
lease_ms: { type: 'number', description: 'Authenticated Controller ownership lease duration, bounded to one hour. Legacy launcher callers may still use this as a launch-reservation TTL.' },
|
|
307
|
+
launch_reservation_ms: { type: 'number', description: 'Short duplicate-spawn reservation TTL for launcher_start or schedule_create; does not grant Work ownership.' },
|
|
305
308
|
executable: { type: 'string', description: 'External controller executable for launcher_start. This is owned by the Launcher, not the Kernel.' },
|
|
306
309
|
launch_args: { type: 'array', items: { type: 'string' }, description: 'Provider-specific arguments for launcher_start.' },
|
|
310
|
+
browser_session_id: { type: 'string', description: 'Saved Forge ChatGPT browser session to continue when controller_type=chatgpt.' },
|
|
311
|
+
conversation_url: { type: 'string', description: 'Explicit https://chatgpt.com conversation URL used when no saved Forge browser session exists.' },
|
|
312
|
+
continuation_prompt: { type: 'string', description: 'Additional bounded continuation instruction appended to the Work/Handoff prompt.' },
|
|
313
|
+
schedule_id: { type: 'string', description: 'Schedule identity for schedule_get/pause/resume/trigger.' },
|
|
314
|
+
schedule_name: { type: 'string', description: 'Human-readable schedule name; defaults to Continue Work <work_id>.' },
|
|
315
|
+
schedule_request_id: { type: 'string', description: 'Stable idempotency key for schedule_create.' },
|
|
316
|
+
trigger_type: { type: 'string', enum: ['interval', 'cron', 'calendar', 'condition', 'repository-event', 'dependency-checkpoint', 'manual'] },
|
|
317
|
+
every_minutes: { type: 'number', description: 'Interval trigger cadence in minutes.' },
|
|
318
|
+
cron_expression: { type: 'string' },
|
|
319
|
+
schedule_timezone: { type: 'string' },
|
|
320
|
+
catch_up_minutes: { type: 'number' },
|
|
321
|
+
calendar_at: { type: 'string' },
|
|
322
|
+
condition: { type: 'object' },
|
|
323
|
+
event_name: { type: 'string' },
|
|
324
|
+
event_id: { type: 'string' },
|
|
325
|
+
event_data: { type: 'object' },
|
|
326
|
+
dependency_job_ids: { type: 'array', items: { type: 'string' } },
|
|
327
|
+
max_failures: { type: 'number' },
|
|
328
|
+
cooldown_minutes: { type: 'number' },
|
|
329
|
+
daily_budget_minutes: { type: 'number' },
|
|
330
|
+
shadow_mode: { type: 'boolean', description: 'Defaults to true. Set false only after the continuation path has been validated.' },
|
|
331
|
+
backoff_base_minutes: { type: 'number' },
|
|
332
|
+
backoff_max_minutes: { type: 'number' },
|
|
333
|
+
include_occurrences: { type: 'boolean' },
|
|
307
334
|
plan_id: { type: 'string' },
|
|
308
335
|
plan_step_id: { type: 'string', description: 'Approved PlanContract step to bind to a complex Goal Workloop start.' },
|
|
309
336
|
scope_key: { type: 'string', description: 'Stable normalized scope identity used to prevent overlapping active plans.' },
|
|
@@ -405,7 +432,7 @@ export const runtimeToolDefinitions: McpToolDefinition[] = [
|
|
|
405
432
|
paths: { type: 'array', items: { type: 'string' } },
|
|
406
433
|
message: { type: 'string' },
|
|
407
434
|
}, ['paths', 'message'], false),
|
|
408
|
-
definition('
|
|
435
|
+
definition('prepare_transfer_artifacts', 'Refresh rebuildable session continuation plus a selected-path patch transfer artifact when recovery context is needed.', {
|
|
409
436
|
repo_id: repoId,
|
|
410
437
|
reason: { type: 'string' },
|
|
411
438
|
}, [], false),
|
|
@@ -887,62 +914,6 @@ export const runtimeToolDefinitions: McpToolDefinition[] = [
|
|
|
887
914
|
max_candidates: { type: 'number' },
|
|
888
915
|
confirm_cleanup: { type: 'boolean' },
|
|
889
916
|
}, [], false),
|
|
890
|
-
definition('create_campaign', 'Create a durable ChatGPT-supervised repository campaign.', {
|
|
891
|
-
repo_id: repoId,
|
|
892
|
-
checkout_id: { type: 'string', description: 'Optional source checkout used to create the Campaign workspace.' },
|
|
893
|
-
request_id: { type: 'string' },
|
|
894
|
-
semantic_key: { type: 'string' },
|
|
895
|
-
title: { type: 'string' },
|
|
896
|
-
goal: { type: 'string' },
|
|
897
|
-
acceptance_criteria: { type: 'array', items: { type: 'string' } },
|
|
898
|
-
non_goals: { type: 'array', items: { type: 'string' } },
|
|
899
|
-
review_policy: { type: 'string', enum: ['every_task', 'failures_and_final', 'final_only'] },
|
|
900
|
-
tasks: { type: 'array', items: { type: 'object' } },
|
|
901
|
-
budget: { type: 'object' },
|
|
902
|
-
supervisor: { type: 'object' },
|
|
903
|
-
workspace: { type: 'object', description: 'Workspace policy. Defaults to an isolated long-lived Campaign worktree. Set mode=current to opt out.' },
|
|
904
|
-
}, ['request_id', 'title', 'goal', 'tasks'], false),
|
|
905
|
-
definition('list_campaigns', 'List durable campaigns for one repository.', { repo_id: repoId, limit: { type: 'number' } }),
|
|
906
|
-
definition('get_campaign', 'Read one durable campaign.', { repo_id: repoId, campaign_id: { type: 'string' } }, ['campaign_id']),
|
|
907
|
-
definition('add_campaign_task', 'Add one task to a non-terminal campaign.', {
|
|
908
|
-
repo_id: repoId,
|
|
909
|
-
campaign_id: { type: 'string' },
|
|
910
|
-
request_id: { type: 'string' },
|
|
911
|
-
expected_revision: { type: 'number' },
|
|
912
|
-
task: { type: 'object' },
|
|
913
|
-
}, ['campaign_id', 'request_id', 'task'], false),
|
|
914
|
-
definition('pause_campaign', 'Pause a campaign without holding execution resources.', {
|
|
915
|
-
repo_id: repoId, campaign_id: { type: 'string' }, request_id: { type: 'string' }, expected_revision: { type: 'number' }, reason: { type: 'string' },
|
|
916
|
-
}, ['campaign_id', 'request_id'], false),
|
|
917
|
-
definition('resume_campaign', 'Resume a paused or supervisor-waiting campaign.', {
|
|
918
|
-
repo_id: repoId, campaign_id: { type: 'string' }, request_id: { type: 'string' }, expected_revision: { type: 'number' },
|
|
919
|
-
}, ['campaign_id', 'request_id'], false),
|
|
920
|
-
definition('cancel_campaign', 'Cancel a campaign and reconcile child Jobs and managed workspace resources before reaching a terminal state.', {
|
|
921
|
-
repo_id: repoId, campaign_id: { type: 'string' }, request_id: { type: 'string' }, expected_revision: { type: 'number' }, reason: { type: 'string' },
|
|
922
|
-
}, ['campaign_id', 'request_id'], false),
|
|
923
|
-
definition('get_campaign_review_packet', 'Read one bounded open review packet for ChatGPT or another supervisor.', {
|
|
924
|
-
repo_id: repoId, campaign_id: { type: 'string' }, checkpoint_id: { type: 'string' },
|
|
925
|
-
}, ['campaign_id']),
|
|
926
|
-
definition('submit_campaign_review', 'Submit one nonce- and goal-revision-bound supervisor decision.', {
|
|
927
|
-
repo_id: repoId,
|
|
928
|
-
campaign_id: { type: 'string' },
|
|
929
|
-
checkpoint_id: { type: 'string' },
|
|
930
|
-
checkpoint_nonce: { type: 'string' },
|
|
931
|
-
goal_revision: { type: 'number' },
|
|
932
|
-
expected_campaign_revision: { type: 'number' },
|
|
933
|
-
request_id: { type: 'string' },
|
|
934
|
-
action: { type: 'string', enum: ['accept', 'request_changes', 'retry', 'skip', 'pause', 'resume', 'approve_final', 'revise_goal', 'escalate'] },
|
|
935
|
-
summary: { type: 'string' },
|
|
936
|
-
instructions: { type: 'string' },
|
|
937
|
-
revised_goal: { type: 'object' },
|
|
938
|
-
submitted_by: { type: 'string' },
|
|
939
|
-
}, ['campaign_id', 'checkpoint_id', 'checkpoint_nonce', 'goal_revision', 'request_id', 'action', 'summary'], false),
|
|
940
|
-
definition('accept_campaign', 'Record human acceptance after final supervisor approval.', {
|
|
941
|
-
repo_id: repoId, campaign_id: { type: 'string' }, request_id: { type: 'string' }, expected_revision: { type: 'number' },
|
|
942
|
-
}, ['campaign_id', 'request_id'], false),
|
|
943
|
-
definition('reconcile_campaign', 'Run one bounded campaign reconciliation pass immediately.', {
|
|
944
|
-
repo_id: repoId, campaign_id: { type: 'string' },
|
|
945
|
-
}, ['campaign_id'], false),
|
|
946
917
|
definition('create_schedule', 'Create a bounded repository Schedule. Shadow mode defaults to true.', {
|
|
947
918
|
repo_id: repoId,
|
|
948
919
|
request_id: { type: 'string' },
|
|
@@ -1542,7 +1513,7 @@ function compactControllerContextSummaryPayload(payload: Record<string, unknown>
|
|
|
1542
1513
|
function authenticatedFacadeControllerIdentity(
|
|
1543
1514
|
ctx: MultiRepositoryMcpToolContext,
|
|
1544
1515
|
args: Record<string, unknown>,
|
|
1545
|
-
): { controllerId: string; principalId: string; sessionId: string; controllerInstanceId: string } {
|
|
1516
|
+
): { controllerId: string; principalId: string; sessionId: string; controllerInstanceId: string; controllerType: 'chatgpt' | 'codex' | 'claude' | 'grok' | 'human' } {
|
|
1546
1517
|
const principalId = ctx.principalId?.trim();
|
|
1547
1518
|
const sessionId = ctx.sessionId?.trim();
|
|
1548
1519
|
if (!principalId || !sessionId) {
|
|
@@ -1556,10 +1527,18 @@ function authenticatedFacadeControllerIdentity(
|
|
|
1556
1527
|
if (requestedSessionId && requestedSessionId !== sessionId) {
|
|
1557
1528
|
throw new Error('CONTROLLER_SESSION_CONTEXT_MISMATCH: session_id must match the authenticated MCP session');
|
|
1558
1529
|
}
|
|
1530
|
+
const requestedControllerType = typeof args.controller_type === 'string' && ['chatgpt', 'codex', 'claude', 'grok', 'human'].includes(args.controller_type)
|
|
1531
|
+
? args.controller_type as 'chatgpt' | 'codex' | 'claude' | 'grok' | 'human'
|
|
1532
|
+
: undefined;
|
|
1533
|
+
const transportControllerType = ctx.controllerType;
|
|
1534
|
+
if (transportControllerType && requestedControllerType && requestedControllerType !== transportControllerType) {
|
|
1535
|
+
throw new Error('CONTROLLER_TYPE_CONTEXT_MISMATCH: controller_type must match the authenticated transport provider');
|
|
1536
|
+
}
|
|
1559
1537
|
return {
|
|
1560
1538
|
controllerId: principalId,
|
|
1561
1539
|
principalId,
|
|
1562
1540
|
sessionId,
|
|
1541
|
+
controllerType: transportControllerType ?? requestedControllerType ?? 'chatgpt',
|
|
1563
1542
|
controllerInstanceId: ctx.controllerInstanceId?.trim() || currentControllerInstanceId(),
|
|
1564
1543
|
};
|
|
1565
1544
|
}
|
|
@@ -1709,66 +1688,6 @@ function pluginRepository(
|
|
|
1709
1688
|
: selected(ctx, args);
|
|
1710
1689
|
}
|
|
1711
1690
|
|
|
1712
|
-
const CAMPAIGN_AGENT_OPERATIONS = new Set(['dispatch_task', 'launch_issue', 'dispatch_ready_tasks', 'retry_task_run', 'quick_agent_session']);
|
|
1713
|
-
const CAMPAIGN_CONTROL_OPERATIONS = new Set(['create_campaign', 'add_campaign_task', 'pause_campaign', 'resume_campaign', 'cancel_campaign', 'submit_campaign_review', 'accept_campaign', 'reconcile_campaign']);
|
|
1714
|
-
|
|
1715
|
-
function campaignAutomationDeprecated(): boolean {
|
|
1716
|
-
return true;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
function campaignTaskInput(
|
|
1720
|
-
raw: unknown,
|
|
1721
|
-
repoIdValue: string,
|
|
1722
|
-
checkoutId?: string,
|
|
1723
|
-
): CreateCampaignTaskInput {
|
|
1724
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('CAMPAIGN_TASK_INVALID');
|
|
1725
|
-
const value = raw as Record<string, unknown>;
|
|
1726
|
-
const taskId = String(value.task_id ?? value.taskId ?? '').trim();
|
|
1727
|
-
const title = String(value.title ?? '').trim();
|
|
1728
|
-
const rawOperation = String(value.operation ?? '').trim();
|
|
1729
|
-
const operation = rawOperation ? assertCampaignOperationSupported(rawOperation) : '';
|
|
1730
|
-
if (!taskId || !title || !operation) throw new Error('CAMPAIGN_TASK_INVALID: task_id, title, and operation are required');
|
|
1731
|
-
if (CAMPAIGN_CONTROL_OPERATIONS.has(operation)) throw new Error(`CAMPAIGN_RECURSIVE_OPERATION_DENIED: ${operation}`);
|
|
1732
|
-
const argumentsValue = value.arguments && typeof value.arguments === 'object' && !Array.isArray(value.arguments)
|
|
1733
|
-
? { ...(value.arguments as Record<string, unknown>) }
|
|
1734
|
-
: {};
|
|
1735
|
-
if (CAMPAIGN_AGENT_OPERATIONS.has(operation)) argumentsValue.isolate = true;
|
|
1736
|
-
assertAutomatedOperationAllowed(operation, argumentsValue);
|
|
1737
|
-
const requestedClaims = Array.isArray(value.resource_claims)
|
|
1738
|
-
? value.resource_claims.flatMap((rawClaim) => {
|
|
1739
|
-
if (!rawClaim || typeof rawClaim !== 'object') return [];
|
|
1740
|
-
const claim = rawClaim as Record<string, unknown>;
|
|
1741
|
-
const resourceKey = String(claim.resource_key ?? claim.resourceKey ?? '').trim();
|
|
1742
|
-
const mode = String(claim.mode ?? 'write');
|
|
1743
|
-
if (!resourceKey || !['read', 'write', 'exclusive'].includes(mode)) return [];
|
|
1744
|
-
return [{ resourceKey, mode: mode as 'read' | 'write' | 'exclusive' }];
|
|
1745
|
-
})
|
|
1746
|
-
: [];
|
|
1747
|
-
const executor = value.executor && typeof value.executor === 'object' && !Array.isArray(value.executor)
|
|
1748
|
-
? value.executor as Record<string, unknown>
|
|
1749
|
-
: undefined;
|
|
1750
|
-
return {
|
|
1751
|
-
taskId,
|
|
1752
|
-
title,
|
|
1753
|
-
objective: typeof value.objective === 'string' ? value.objective : undefined,
|
|
1754
|
-
operation,
|
|
1755
|
-
arguments: argumentsValue,
|
|
1756
|
-
dependsOn: normalizeCampaignDependencyReferences(Array.isArray(value.depends_on ?? value.dependsOn) ? (value.depends_on ?? value.dependsOn) as unknown[] : []),
|
|
1757
|
-
priority: ['P0', 'P1', 'P2', 'P3', 'P4'].includes(String(value.priority)) ? String(value.priority) as 'P0' | 'P1' | 'P2' | 'P3' | 'P4' : 'P1',
|
|
1758
|
-
resourceClaims: requestedClaims.length > 0 ? requestedClaims : claimsForMcpOperation(operation, argumentsValue, repoIdValue, checkoutId),
|
|
1759
|
-
reviewRequired: value.review_required === undefined ? true : value.review_required === true,
|
|
1760
|
-
requiresChanges: value.requires_changes === true || value.requiresChanges === true,
|
|
1761
|
-
maxAttempts: typeof value.max_attempts === 'number' ? value.max_attempts : undefined,
|
|
1762
|
-
executor: executor ? {
|
|
1763
|
-
enableDevRunner: executor.enable_dev_runner === undefined ? undefined : executor.enable_dev_runner === true,
|
|
1764
|
-
enableChatgptBrowser: executor.enable_chatgpt_browser === true,
|
|
1765
|
-
allowedAgents: Array.isArray(executor.allowed_agents) ? executor.allowed_agents.map(String) : undefined,
|
|
1766
|
-
runnerTimeoutMs: typeof executor.runner_timeout_ms === 'number' ? executor.runner_timeout_ms : undefined,
|
|
1767
|
-
runnerMaxTimeoutMs: typeof executor.runner_max_timeout_ms === 'number' ? executor.runner_max_timeout_ms : undefined,
|
|
1768
|
-
} : undefined,
|
|
1769
|
-
};
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
1691
|
function expectedRevision(args: Record<string, unknown>, key = 'expected_revision'): number | undefined {
|
|
1773
1692
|
return typeof args[key] === 'number' ? Math.trunc(args[key] as number) : undefined;
|
|
1774
1693
|
}
|
|
@@ -2574,11 +2493,11 @@ async function runFacadeRepair(
|
|
|
2574
2493
|
return result(facade as unknown as Record<string, unknown>, facade.status === 'blocked' || facade.status === 'approval_required' || facade.status === 'failed');
|
|
2575
2494
|
}
|
|
2576
2495
|
|
|
2577
|
-
function runFacadeVerify(
|
|
2496
|
+
async function runFacadeVerify(
|
|
2578
2497
|
ctx: MultiRepositoryMcpToolContext,
|
|
2579
2498
|
repository: ReturnType<typeof selected>,
|
|
2580
2499
|
args: Record<string, unknown>,
|
|
2581
|
-
): CallToolResult {
|
|
2500
|
+
): Promise<CallToolResult> {
|
|
2582
2501
|
const store = { controllerHome: ctx.controllerHome, repoId: repository.repoId };
|
|
2583
2502
|
const checks = listControllerChecks(repository.canonicalRoot);
|
|
2584
2503
|
const workloopCtx = {
|
|
@@ -2643,16 +2562,166 @@ function runFacadeVerify(
|
|
|
2643
2562
|
return result(facade as unknown as Record<string, unknown>, facade.status === 'failed');
|
|
2644
2563
|
}
|
|
2645
2564
|
|
|
2646
|
-
// Real
|
|
2565
|
+
// Real checks share the same persisted Process Runtime path as run_check and
|
|
2566
|
+
// work_validate. Never synchronously block the MCP facade on long native tests.
|
|
2647
2567
|
try {
|
|
2648
|
-
const
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2651
|
-
|
|
2568
|
+
const normalizedCheckId = classified.normalizedCheckId!;
|
|
2569
|
+
const requestId = typeof args.request_id === 'string' && args.request_id.trim()
|
|
2570
|
+
? args.request_id.trim()
|
|
2571
|
+
: undefined;
|
|
2572
|
+
const observedGitHead = repositoryGitStatus(repository).head;
|
|
2573
|
+
const workContract = workId ? getWorkContract(store, workId) : undefined;
|
|
2574
|
+
if (workId && !workContract) throw new Error(`WORK_NOT_FOUND: ${workId}`);
|
|
2575
|
+
const executed = await runPersistedCheckViaProcessRuntime({
|
|
2576
|
+
controllerHome: ctx.controllerHome,
|
|
2577
|
+
repoId: repository.repoId,
|
|
2578
|
+
checkoutId: repository.activeCheckoutId,
|
|
2579
|
+
repoRoot: repository.canonicalRoot,
|
|
2580
|
+
executionIdentity: executionIdentityForRepository(repository, workId ? { workId } : {}),
|
|
2581
|
+
checkId: normalizedCheckId,
|
|
2582
|
+
timeoutMs: typeof args.timeout_ms === 'number' ? args.timeout_ms : undefined,
|
|
2583
|
+
// rh_work.verify is a control-plane continuation primitive: callers should
|
|
2584
|
+
// be able to keep working and later reattach to the exact same Process.
|
|
2585
|
+
interactiveWaitMs: 0,
|
|
2586
|
+
requestId,
|
|
2587
|
+
workId: workId || undefined,
|
|
2588
|
+
commandId: requestId,
|
|
2589
|
+
verificationSnapshot: workContract ? {
|
|
2590
|
+
workId: workContract.workId,
|
|
2591
|
+
allowedPaths: workContract.allowedPaths,
|
|
2592
|
+
forbiddenPaths: workContract.forbiddenPaths,
|
|
2593
|
+
} : undefined,
|
|
2594
|
+
});
|
|
2595
|
+
|
|
2596
|
+
if (executed.mode === 'durable') {
|
|
2597
|
+
return result(buildFacadeResult({
|
|
2598
|
+
status: 'blocked',
|
|
2599
|
+
summary: `Check ${normalizedCheckId} requires an explicit durable workflow; no acceptance result was recorded.`,
|
|
2600
|
+
data: {
|
|
2601
|
+
verification: {
|
|
2602
|
+
checkId: normalizedCheckId,
|
|
2603
|
+
outcome: 'deferred',
|
|
2604
|
+
isAcceptanceFailure: false,
|
|
2605
|
+
isInfrastructureIssue: false,
|
|
2606
|
+
durable: executed.durable,
|
|
2607
|
+
observedGitHead,
|
|
2608
|
+
},
|
|
2609
|
+
},
|
|
2610
|
+
suggestedNextActions: [{
|
|
2611
|
+
label: 'Continue Work with the durable check requirement',
|
|
2612
|
+
tool: 'rh_work',
|
|
2613
|
+
operation: 'continue',
|
|
2614
|
+
payload: { work_id: workId || undefined },
|
|
2615
|
+
risk: 'workspace_write',
|
|
2616
|
+
confidence: 'high',
|
|
2617
|
+
}],
|
|
2618
|
+
}) as unknown as Record<string, unknown>, true);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
const handle = executed.process;
|
|
2622
|
+
if (!handle) throw new Error(`PROCESS_CHECK_HANDLE_MISSING: ${normalizedCheckId}`);
|
|
2623
|
+
const record = getProcessRecord(ctx.controllerHome, repository.repoId, handle.processId);
|
|
2624
|
+
const checkContentRevision = record?.checkExecution?.revision;
|
|
2625
|
+
|
|
2626
|
+
if (!handle.completed) {
|
|
2627
|
+
return result(buildFacadeResult({
|
|
2628
|
+
status: 'ok',
|
|
2629
|
+
summary: `Check ${normalizedCheckId} is running through Process Runtime; continue other work and reattach to ${handle.processId}.`,
|
|
2630
|
+
data: {
|
|
2631
|
+
verification: {
|
|
2632
|
+
checkId: normalizedCheckId,
|
|
2633
|
+
outcome: 'running',
|
|
2634
|
+
isAcceptanceFailure: false,
|
|
2635
|
+
isInfrastructureIssue: false,
|
|
2636
|
+
executed: true,
|
|
2637
|
+
completed: false,
|
|
2638
|
+
processId: handle.processId,
|
|
2639
|
+
processStatus: handle.status,
|
|
2640
|
+
deduplicated: handle.deduplicated === true,
|
|
2641
|
+
semanticDeduplicated: handle.semanticDeduplicated === true,
|
|
2642
|
+
checkContentRevision,
|
|
2643
|
+
observedGitHead,
|
|
2644
|
+
verificationIsolation: workContract ? 'work_snapshot' : 'shared_checkout',
|
|
2645
|
+
revisionSemantics: 'checkContentRevision is a content-bound Check identity; observedGitHead is Git HEAD and is not interchangeable.',
|
|
2646
|
+
},
|
|
2647
|
+
},
|
|
2648
|
+
rawAvailable: false,
|
|
2649
|
+
}) as unknown as Record<string, unknown>);
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
if (!record) throw new Error(`PROCESS_CHECK_RECORD_MISSING: ${handle.processId}`);
|
|
2653
|
+
const receipt = processCheckCompletionReceipt(record, {
|
|
2654
|
+
repoId: repository.repoId,
|
|
2655
|
+
checkId: normalizedCheckId,
|
|
2656
|
+
processId: handle.processId,
|
|
2657
|
+
...(record.checkExecution ? {
|
|
2658
|
+
checkoutId: repository.activeCheckoutId,
|
|
2659
|
+
workId: workId || undefined,
|
|
2660
|
+
requestId,
|
|
2661
|
+
checkExecution: {
|
|
2662
|
+
cacheKey: record.checkExecution.cacheKey,
|
|
2663
|
+
revision: record.checkExecution.revision,
|
|
2664
|
+
definitionDigest: record.checkExecution.definitionDigest,
|
|
2665
|
+
environmentFingerprint: record.checkExecution.environmentFingerprint,
|
|
2666
|
+
timeoutMs: record.checkExecution.timeoutMs,
|
|
2667
|
+
scopeKey: record.checkExecution.scopeKey,
|
|
2668
|
+
},
|
|
2669
|
+
} : {}),
|
|
2670
|
+
});
|
|
2671
|
+
const structuredCheckResult = readPersistedCheckResultReceipt(record.origin?.checkResultReceiptPath);
|
|
2672
|
+
const structuredResultMatchesProcess = Boolean(
|
|
2673
|
+
structuredCheckResult
|
|
2674
|
+
&& record.checkExecution?.cacheKey
|
|
2675
|
+
&& structuredCheckResult.checkId === normalizedCheckId
|
|
2676
|
+
&& structuredCheckResult.cacheKey === record.checkExecution.cacheKey,
|
|
2677
|
+
);
|
|
2678
|
+
const legacyEvidence = record.origin?.checkResultReceiptPath
|
|
2679
|
+
? undefined
|
|
2680
|
+
: readLatestControllerCheckEvidence(repository.canonicalRoot, normalizedCheckId);
|
|
2681
|
+
const legacyEvidenceMatchesProcess = Boolean(
|
|
2682
|
+
legacyEvidence?.cacheKey
|
|
2683
|
+
&& record.checkExecution?.cacheKey
|
|
2684
|
+
&& legacyEvidence.cacheKey === record.checkExecution.cacheKey,
|
|
2685
|
+
);
|
|
2686
|
+
const resultMatchesProcess = structuredResultMatchesProcess || legacyEvidenceMatchesProcess;
|
|
2687
|
+
const failureClass = structuredResultMatchesProcess
|
|
2688
|
+
? structuredCheckResult?.failureClass
|
|
2689
|
+
: legacyEvidenceMatchesProcess ? legacyEvidence?.failureClass : undefined;
|
|
2690
|
+
const infrastructureFailed = receipt.timedOut
|
|
2691
|
+
|| receipt.cancelled
|
|
2692
|
+
|| !resultMatchesProcess
|
|
2693
|
+
|| (!receipt.ok && failureClass !== 'acceptance_failure');
|
|
2694
|
+
const checkFailed = !receipt.ok && !infrastructureFailed;
|
|
2695
|
+
const boundedStatus = receipt.ok ? 'pass' : infrastructureFailed ? 'infrastructure_failure' : 'fail';
|
|
2696
|
+
const commonVerification = {
|
|
2697
|
+
checkId: normalizedCheckId,
|
|
2698
|
+
outcome: infrastructureFailed ? 'infrastructure_failure' : receipt.ok ? 'valid_pass' : 'valid_fail',
|
|
2699
|
+
isAcceptanceFailure: checkFailed,
|
|
2700
|
+
isInfrastructureIssue: infrastructureFailed,
|
|
2701
|
+
executed: true,
|
|
2702
|
+
completed: true,
|
|
2703
|
+
processId: receipt.processId,
|
|
2704
|
+
processStatus: receipt.runtimeStatus,
|
|
2705
|
+
ok: receipt.ok,
|
|
2706
|
+
timedOut: receipt.timedOut,
|
|
2707
|
+
cancelled: receipt.cancelled,
|
|
2708
|
+
failureClass: infrastructureFailed ? 'infrastructure_failure' : failureClass,
|
|
2709
|
+
deduplicated: handle.deduplicated === true,
|
|
2710
|
+
semanticDeduplicated: handle.semanticDeduplicated === true,
|
|
2711
|
+
checkContentRevision: receipt.checkRevision,
|
|
2712
|
+
observedGitHead,
|
|
2713
|
+
revisionSemantics: 'checkContentRevision is a content-bound Check identity; observedGitHead is Git HEAD and is not interchangeable.',
|
|
2714
|
+
evidenceArtifactPath: record.origin?.workVerificationSnapshot ? undefined : receipt.artifactPath,
|
|
2715
|
+
evidenceReceiptId: receipt.receiptId,
|
|
2716
|
+
checkResultReceiptId: structuredCheckResult?.receiptId,
|
|
2717
|
+
verificationIsolation: record.origin?.workVerificationSnapshot ? 'work_snapshot' : 'shared_checkout',
|
|
2718
|
+
boundedStatus,
|
|
2719
|
+
};
|
|
2720
|
+
|
|
2652
2721
|
if (workId) {
|
|
2653
2722
|
const facade = verifyGoalWorkloop(workloopCtx, {
|
|
2654
2723
|
workId,
|
|
2655
|
-
checkId:
|
|
2724
|
+
checkId: normalizedCheckId,
|
|
2656
2725
|
infrastructureFailed,
|
|
2657
2726
|
checkFailed,
|
|
2658
2727
|
});
|
|
@@ -2663,46 +2732,26 @@ function runFacadeVerify(
|
|
|
2663
2732
|
...data,
|
|
2664
2733
|
verification: {
|
|
2665
2734
|
...(typeof data.verification === 'object' && data.verification ? data.verification as Record<string, unknown> : {}),
|
|
2666
|
-
|
|
2667
|
-
registeredCheckId: classified.normalizedCheckId,
|
|
2668
|
-
ok: executed.ok,
|
|
2669
|
-
timedOut: executed.timedOut,
|
|
2670
|
-
failureClass: executed.failureClass,
|
|
2671
|
-
cacheHit: executed.cacheHit,
|
|
2672
|
-
validatedRevision: executed.validatedRevision,
|
|
2673
|
-
originalExecutedAt: executed.originalExecutedAt,
|
|
2674
|
-
// Never return raw stdout/stderr to ChatGPT by default.
|
|
2675
|
-
evidenceArtifactPath: executed.artifactPath,
|
|
2676
|
-
boundedStatus: executed.ok ? 'pass' : infrastructureFailed ? 'infrastructure_failure' : 'fail',
|
|
2735
|
+
...commonVerification,
|
|
2677
2736
|
},
|
|
2678
2737
|
},
|
|
2738
|
+
warnings: infrastructureFailed
|
|
2739
|
+
? [...facade.warnings, resultMatchesProcess ? 'infrastructure_failure is distinct from acceptance failure' : 'check result receipt did not match the terminal Process semantic identity']
|
|
2740
|
+
: facade.warnings,
|
|
2679
2741
|
} as unknown as Record<string, unknown>, facade.status === 'failed');
|
|
2680
2742
|
}
|
|
2743
|
+
|
|
2681
2744
|
return result(buildFacadeResult({
|
|
2682
2745
|
status: checkFailed ? 'failed' : 'ok',
|
|
2683
2746
|
summary: infrastructureFailed
|
|
2684
|
-
? `Infrastructure failure while running ${
|
|
2685
|
-
:
|
|
2686
|
-
? `Check ${
|
|
2687
|
-
: `Check ${
|
|
2688
|
-
data: {
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
isAcceptanceFailure: checkFailed,
|
|
2693
|
-
isInfrastructureIssue: infrastructureFailed,
|
|
2694
|
-
executed: true,
|
|
2695
|
-
evidenceArtifactPath: executed.artifactPath,
|
|
2696
|
-
cacheHit: executed.cacheHit,
|
|
2697
|
-
validatedRevision: executed.validatedRevision,
|
|
2698
|
-
originalExecutedAt: executed.originalExecutedAt,
|
|
2699
|
-
failureClass: executed.failureClass,
|
|
2700
|
-
},
|
|
2701
|
-
},
|
|
2702
|
-
warnings: infrastructureFailed ? ['infrastructure_failure is distinct from acceptance failure'] : [],
|
|
2703
|
-
suggestedNextActions: executed.ok
|
|
2704
|
-
? [{ label: 'Continue work', tool: 'rh_work', operation: 'continue', payload: { work_id: workId || undefined }, risk: 'readonly' }]
|
|
2705
|
-
: [{ label: 'Diagnose if infrastructure', tool: 'rh_work', operation: 'repair', payload: { repair_operation: 'diagnose', dry_run: true }, risk: 'readonly' }],
|
|
2747
|
+
? `Infrastructure failure while running ${normalizedCheckId}; not an acceptance failure.`
|
|
2748
|
+
: receipt.ok
|
|
2749
|
+
? `Check ${normalizedCheckId} passed with persisted Process evidence.`
|
|
2750
|
+
: `Check ${normalizedCheckId} failed acceptance.`,
|
|
2751
|
+
data: { verification: commonVerification },
|
|
2752
|
+
warnings: infrastructureFailed
|
|
2753
|
+
? [resultMatchesProcess ? 'infrastructure_failure is distinct from acceptance failure' : 'check result receipt did not match the terminal Process semantic identity']
|
|
2754
|
+
: [],
|
|
2706
2755
|
rawAvailable: false,
|
|
2707
2756
|
}) as unknown as Record<string, unknown>, checkFailed);
|
|
2708
2757
|
} catch (error) {
|
|
@@ -2724,7 +2773,7 @@ function runFacadeVerify(
|
|
|
2724
2773
|
}
|
|
2725
2774
|
return result(buildFacadeResult({
|
|
2726
2775
|
status: 'ok',
|
|
2727
|
-
summary: `Infrastructure failure invoking
|
|
2776
|
+
summary: `Infrastructure failure invoking Process Runtime for ${classified.normalizedCheckId}; not acceptance failure.`,
|
|
2728
2777
|
data: {
|
|
2729
2778
|
verification: {
|
|
2730
2779
|
checkId: classified.normalizedCheckId,
|
|
@@ -2993,7 +3042,15 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
2993
3042
|
recommendedContinuationPrompt: typeof args.recommended_continuation_prompt === 'string' ? args.recommended_continuation_prompt : undefined,
|
|
2994
3043
|
suggestedNextActions: [],
|
|
2995
3044
|
});
|
|
2996
|
-
|
|
3045
|
+
let ownershipReleased = false;
|
|
3046
|
+
if (item.workId && ctx.principalId?.trim() && ctx.sessionId?.trim()) {
|
|
3047
|
+
const owner = getControllerSession(store, item.workId);
|
|
3048
|
+
if (owner && owner.controllerId === ctx.principalId.trim() && owner.sessionId === ctx.sessionId.trim()) {
|
|
3049
|
+
releaseControllerSession(store, item.workId, owner.controllerId);
|
|
3050
|
+
ownershipReleased = true;
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
return result(buildFacadeResult({ summary: `Created handoff ${item.id}.`, data: { item: summarizeHandoffItem(item), ownershipReleased } }) as unknown as Record<string, unknown>);
|
|
2997
3054
|
}
|
|
2998
3055
|
// Default list: pending summary only.
|
|
2999
3056
|
const items = listHandoffItems({ ...store, status: 'pending', limit: typeof args.limit === 'number' ? args.limit : 50 });
|
|
@@ -3023,9 +3080,16 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3023
3080
|
const list = (value: unknown): string[] => Array.isArray(value)
|
|
3024
3081
|
? value.map(String).map((entry) => entry.trim()).filter(Boolean)
|
|
3025
3082
|
: [];
|
|
3026
|
-
const
|
|
3083
|
+
const retrievalMode = args.retrieval_mode === 'plan' || args.retrieval_mode === 'debug' || args.retrieval_mode === 'review'
|
|
3084
|
+
? args.retrieval_mode
|
|
3085
|
+
: 'implementation';
|
|
3086
|
+
const impactDomains = list(args.impact_domains)
|
|
3087
|
+
.filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain));
|
|
3088
|
+
const structuralContext = args.structural_context === 'off' || args.structural_context === 'auto' || args.structural_context === 'required'
|
|
3027
3089
|
? args.structural_context
|
|
3028
|
-
: '
|
|
3090
|
+
: retrievalMode === 'plan' || retrievalMode === 'debug'
|
|
3091
|
+
? 'required'
|
|
3092
|
+
: 'auto';
|
|
3029
3093
|
const pack = buildControllerContextPack(repository.canonicalRoot, ctx.policy, {
|
|
3030
3094
|
description: query,
|
|
3031
3095
|
searchTerms: [query],
|
|
@@ -3035,6 +3099,8 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3035
3099
|
maxFiles: typeof args.max_files === 'number' ? args.max_files : undefined,
|
|
3036
3100
|
maxSnippets: typeof args.max_snippets === 'number' ? args.max_snippets : undefined,
|
|
3037
3101
|
structuralContext,
|
|
3102
|
+
retrievalMode,
|
|
3103
|
+
impactDomains,
|
|
3038
3104
|
});
|
|
3039
3105
|
const warnings = structuralContext === 'required' && !pack.structuralContext.requiredSatisfied
|
|
3040
3106
|
? [pack.structuralContext.fallbackReason ?? 'Required structural context is not ready; lexical retrieval results are returned as degraded evidence.']
|
|
@@ -3308,6 +3374,78 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3308
3374
|
if (!allowedFacadeOperations('rh_work').includes(operation)) {
|
|
3309
3375
|
return invalidFacadeOperation('rh_work', operation);
|
|
3310
3376
|
}
|
|
3377
|
+
if (operation.startsWith('schedule_')) {
|
|
3378
|
+
try {
|
|
3379
|
+
const workId = String(args.work_id ?? '').trim();
|
|
3380
|
+
const scheduleId = String(args.schedule_id ?? '').trim();
|
|
3381
|
+
if (operation === 'schedule_create') {
|
|
3382
|
+
const controllerType = String(args.controller_type ?? 'chatgpt').trim();
|
|
3383
|
+
if (!['chatgpt', 'codex', 'claude', 'grok'].includes(controllerType)) throw new Error('CONTROLLER_TYPE_INVALID');
|
|
3384
|
+
const triggerTypeRaw = String(args.trigger_type ?? '').trim();
|
|
3385
|
+
const triggerType = ['interval', 'cron', 'calendar', 'condition', 'repository-event', 'dependency-checkpoint', 'manual'].includes(triggerTypeRaw)
|
|
3386
|
+
? triggerTypeRaw as 'interval' | 'cron' | 'calendar' | 'condition' | 'repository-event' | 'dependency-checkpoint' | 'manual'
|
|
3387
|
+
: undefined;
|
|
3388
|
+
const created = createWorkContinuationSchedule(ctx.controllerHome, repository.repoId, {
|
|
3389
|
+
workId,
|
|
3390
|
+
controllerType: controllerType as ContinuationControllerType,
|
|
3391
|
+
executable: typeof args.executable === 'string' ? args.executable : undefined,
|
|
3392
|
+
launchArgs: Array.isArray(args.launch_args) ? args.launch_args.map(String) : undefined,
|
|
3393
|
+
launchReservationMs: typeof args.launch_reservation_ms === 'number' ? args.launch_reservation_ms : typeof args.lease_ms === 'number' ? args.lease_ms : undefined,
|
|
3394
|
+
handoffId: typeof args.handoff_id === 'string' ? args.handoff_id : undefined,
|
|
3395
|
+
browserSessionId: typeof args.browser_session_id === 'string' ? args.browser_session_id : undefined,
|
|
3396
|
+
conversationUrl: typeof args.conversation_url === 'string' ? args.conversation_url : undefined,
|
|
3397
|
+
continuationPrompt: typeof args.continuation_prompt === 'string' ? args.continuation_prompt : undefined,
|
|
3398
|
+
scheduleName: typeof args.schedule_name === 'string' ? args.schedule_name : undefined,
|
|
3399
|
+
requestId: typeof args.schedule_request_id === 'string' ? args.schedule_request_id : undefined,
|
|
3400
|
+
triggerType,
|
|
3401
|
+
everyMinutes: typeof args.every_minutes === 'number' ? args.every_minutes : undefined,
|
|
3402
|
+
cronExpression: typeof args.cron_expression === 'string' ? args.cron_expression : undefined,
|
|
3403
|
+
timezone: typeof args.schedule_timezone === 'string' ? args.schedule_timezone : undefined,
|
|
3404
|
+
catchUpMinutes: typeof args.catch_up_minutes === 'number' ? args.catch_up_minutes : undefined,
|
|
3405
|
+
calendarAt: typeof args.calendar_at === 'string' ? args.calendar_at : undefined,
|
|
3406
|
+
condition: args.condition && typeof args.condition === 'object' && !Array.isArray(args.condition) ? args.condition as never : undefined,
|
|
3407
|
+
eventName: typeof args.event_name === 'string' ? args.event_name : undefined,
|
|
3408
|
+
dependencyJobIds: Array.isArray(args.dependency_job_ids) ? args.dependency_job_ids.map(String) : undefined,
|
|
3409
|
+
maxFailures: typeof args.max_failures === 'number' ? args.max_failures : undefined,
|
|
3410
|
+
cooldownMinutes: typeof args.cooldown_minutes === 'number' ? args.cooldown_minutes : undefined,
|
|
3411
|
+
dailyBudgetMinutes: typeof args.daily_budget_minutes === 'number' ? args.daily_budget_minutes : undefined,
|
|
3412
|
+
shadowMode: typeof args.shadow_mode === 'boolean' ? args.shadow_mode : undefined,
|
|
3413
|
+
backoffBaseMinutes: typeof args.backoff_base_minutes === 'number' ? args.backoff_base_minutes : undefined,
|
|
3414
|
+
backoffMaxMinutes: typeof args.backoff_max_minutes === 'number' ? args.backoff_max_minutes : undefined,
|
|
3415
|
+
stopConditions: Array.isArray(args.stop_conditions) ? args.stop_conditions.map(String) : undefined,
|
|
3416
|
+
});
|
|
3417
|
+
return result(buildFacadeResult({ summary: `Continuation schedule ${created.schedule.scheduleId} is configured for Work ${workId}.`, data: { schedule: created.schedule, work: buildWorkContinuationSnapshot(created.work) } }) as unknown as Record<string, unknown>);
|
|
3418
|
+
}
|
|
3419
|
+
if (operation === 'schedule_list') {
|
|
3420
|
+
const listed = listWorkContinuationSchedules(ctx.controllerHome, repository.repoId, { workId: workId || undefined, includeOccurrences: args.include_occurrences === true });
|
|
3421
|
+
return result(buildFacadeResult({ summary: `Found ${listed.schedules.length} Work continuation schedule(s).`, data: listed }) as unknown as Record<string, unknown>);
|
|
3422
|
+
}
|
|
3423
|
+
if (!scheduleId) throw new Error('SCHEDULE_ID_REQUIRED');
|
|
3424
|
+
if (operation === 'schedule_get') {
|
|
3425
|
+
const fetched = getWorkContinuationSchedule(ctx.controllerHome, repository.repoId, scheduleId, args.include_occurrences === true);
|
|
3426
|
+
return result(buildFacadeResult({ summary: `Continuation schedule ${scheduleId}.`, data: fetched }) as unknown as Record<string, unknown>);
|
|
3427
|
+
}
|
|
3428
|
+
if (operation === 'schedule_pause') {
|
|
3429
|
+
const schedule = pauseWorkContinuationSchedule(ctx.controllerHome, repository.repoId, scheduleId, typeof args.reason === 'string' ? args.reason : undefined);
|
|
3430
|
+
return result(buildFacadeResult({ summary: `Continuation schedule ${scheduleId} is paused.`, data: { schedule } }) as unknown as Record<string, unknown>);
|
|
3431
|
+
}
|
|
3432
|
+
if (operation === 'schedule_resume') {
|
|
3433
|
+
const schedule = resumeWorkContinuationSchedule(ctx.controllerHome, repository.repoId, scheduleId);
|
|
3434
|
+
return result(buildFacadeResult({ summary: `Continuation schedule ${scheduleId} is resumed.`, data: { schedule } }) as unknown as Record<string, unknown>);
|
|
3435
|
+
}
|
|
3436
|
+
if (operation === 'schedule_trigger') {
|
|
3437
|
+
const occurrence = await triggerWorkContinuationSchedule(ctx.controllerHome, repository.repoId, scheduleId, {
|
|
3438
|
+
source: typeof args.event_name === 'string' && args.event_name.trim() ? 'repository-event' : 'manual',
|
|
3439
|
+
eventName: typeof args.event_name === 'string' ? args.event_name : undefined,
|
|
3440
|
+
eventId: typeof args.event_id === 'string' ? args.event_id : undefined,
|
|
3441
|
+
data: args.event_data && typeof args.event_data === 'object' && !Array.isArray(args.event_data) ? args.event_data as Record<string, unknown> : undefined,
|
|
3442
|
+
});
|
|
3443
|
+
return result(buildFacadeResult({ summary: occurrence ? `Continuation schedule ${scheduleId} produced ${occurrence.decision}.` : `Continuation schedule ${scheduleId} produced no occurrence.`, data: { occurrence } }) as unknown as Record<string, unknown>);
|
|
3444
|
+
}
|
|
3445
|
+
} catch (error) {
|
|
3446
|
+
return result(buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'Schedule operation failed.', data: {} }) as unknown as Record<string, unknown>, true);
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3311
3449
|
if (operation === 'controller_get_owner') {
|
|
3312
3450
|
const owner = getControllerSession(store, String(args.work_id ?? '').trim());
|
|
3313
3451
|
return result(buildFacadeResult({ summary: owner ? `Work is claimed by ${owner.controllerId}.` : 'Work has no active controller owner.', data: { owner } }) as unknown as Record<string, unknown>);
|
|
@@ -3354,13 +3492,14 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3354
3492
|
executable: typeof args.executable === 'string' && args.executable.trim() ? args.executable.trim() : undefined,
|
|
3355
3493
|
args: Array.isArray(args.launch_args) ? args.launch_args.map(String) : [],
|
|
3356
3494
|
workId: String(args.work_id ?? '').trim(),
|
|
3357
|
-
|
|
3358
|
-
sessionId: String(args.session_id ?? '').trim(),
|
|
3359
|
-
leaseMs: typeof args.lease_ms === 'number' ? args.lease_ms : undefined,
|
|
3495
|
+
launchReservationMs: typeof args.launch_reservation_ms === 'number' ? args.launch_reservation_ms : typeof args.lease_ms === 'number' ? args.lease_ms : undefined,
|
|
3360
3496
|
handoffId: typeof args.handoff_id === 'string' ? args.handoff_id : undefined,
|
|
3497
|
+
browserSessionId: typeof args.browser_session_id === 'string' ? args.browser_session_id : undefined,
|
|
3498
|
+
conversationUrl: typeof args.conversation_url === 'string' ? args.conversation_url : undefined,
|
|
3499
|
+
continuationPrompt: typeof args.continuation_prompt === 'string' ? args.continuation_prompt : undefined,
|
|
3361
3500
|
cwd: repository.canonicalRoot,
|
|
3362
3501
|
});
|
|
3363
|
-
return result(buildFacadeResult({ summary: `Thin Launcher started ${launched.controllerType}.`, data: { pid: launched.pid, executable: launched.executable, workId: String(args.work_id ?? ''),
|
|
3502
|
+
return result(buildFacadeResult({ summary: `Thin Launcher started ${launched.controllerType}.`, data: { pid: launched.pid, executable: launched.executable, workId: String(args.work_id ?? ''), reservationId: launched.reservationId } }) as unknown as Record<string, unknown>);
|
|
3364
3503
|
} catch (error) {
|
|
3365
3504
|
return result(buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'Launcher failed.', data: {} }) as unknown as Record<string, unknown>, true);
|
|
3366
3505
|
}
|
|
@@ -3449,7 +3588,7 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3449
3588
|
}
|
|
3450
3589
|
|
|
3451
3590
|
if (operation === 'verify') {
|
|
3452
|
-
return runFacadeVerify(ctx, repository, args);
|
|
3591
|
+
return await runFacadeVerify(ctx, repository, args);
|
|
3453
3592
|
}
|
|
3454
3593
|
|
|
3455
3594
|
if (operation === 'delegate') {
|
|
@@ -3555,7 +3694,7 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3555
3694
|
resumedControllerSession = resumeControllerSession(store, {
|
|
3556
3695
|
workId,
|
|
3557
3696
|
controllerId: identity.controllerId,
|
|
3558
|
-
controllerType:
|
|
3697
|
+
controllerType: identity.controllerType,
|
|
3559
3698
|
sessionId: identity.sessionId,
|
|
3560
3699
|
principalId: identity.principalId,
|
|
3561
3700
|
controllerInstanceId: identity.controllerInstanceId,
|
|
@@ -3896,17 +4035,17 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
3896
4035
|
...(directEditWorkCompletion ? { directEditWorkCompletion } : {}),
|
|
3897
4036
|
}, Boolean(committed.error));
|
|
3898
4037
|
}
|
|
3899
|
-
case '
|
|
4038
|
+
case 'prepare_transfer_artifacts': {
|
|
3900
4039
|
const repository = selected(ctx, args);
|
|
3901
|
-
const
|
|
4040
|
+
const transfer = prepareTransferArtifacts(repository, { reason: args.reason });
|
|
3902
4041
|
const taskLedger = writeControllerTaskLedgerArtifacts(repository.canonicalRoot, { reason: args.reason });
|
|
3903
4042
|
return result({
|
|
3904
4043
|
repoId: repository.repoId,
|
|
3905
4044
|
checkoutId: repository.activeCheckoutId,
|
|
3906
|
-
...
|
|
4045
|
+
...transfer,
|
|
3907
4046
|
taskLedger: taskLedger.projection,
|
|
3908
4047
|
artifacts: [
|
|
3909
|
-
...
|
|
4048
|
+
...transfer.artifacts,
|
|
3910
4049
|
...taskLedger.artifacts,
|
|
3911
4050
|
],
|
|
3912
4051
|
});
|
|
@@ -5109,8 +5248,8 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
5109
5248
|
break;
|
|
5110
5249
|
}
|
|
5111
5250
|
case 'recovery.create_patch_handoff':
|
|
5112
|
-
payload =
|
|
5113
|
-
affectedPaths = ['.ai/
|
|
5251
|
+
payload = prepareTransferArtifacts(repository, { reason }) as unknown as Record<string, unknown>;
|
|
5252
|
+
affectedPaths = ['.ai/harness/transfers', '.ai/harness/session'];
|
|
5114
5253
|
break;
|
|
5115
5254
|
case 'recovery.workspace_auth_login_prepare':
|
|
5116
5255
|
payload = { skipped: true, nextTool: 'workspace_auth_login_prepare', reason: 'Auth login is a non-secret handoff and should be prepared through the dedicated typed tool.' };
|
|
@@ -5601,223 +5740,6 @@ export async function callRuntimeTool(ctx: MultiRepositoryMcpToolContext, name:
|
|
|
5601
5740
|
model: typeof args.model === 'string' ? args.model : undefined,
|
|
5602
5741
|
}) });
|
|
5603
5742
|
}
|
|
5604
|
-
case 'create_campaign': {
|
|
5605
|
-
const repository = selected(ctx, args);
|
|
5606
|
-
if (campaignAutomationDeprecated()) {
|
|
5607
|
-
return result({
|
|
5608
|
-
error: {
|
|
5609
|
-
code: 'CAMPAIGN_DEPRECATED',
|
|
5610
|
-
message: 'Campaign creation is retired. Create a PlanContract and WorkContracts, then launch an external Controller.',
|
|
5611
|
-
},
|
|
5612
|
-
deprecated: true,
|
|
5613
|
-
migration: ['rh_work.plan_create', 'rh_work.plan_approve', 'rh_work.controller_claim', 'rh_work.launcher_start'],
|
|
5614
|
-
repoId: repository.repoId,
|
|
5615
|
-
}, true);
|
|
5616
|
-
}
|
|
5617
|
-
const rawTasks = Array.isArray(args.tasks) ? args.tasks : [];
|
|
5618
|
-
const requestId = String(args.request_id ?? '').trim();
|
|
5619
|
-
const title = String(args.title ?? '').trim();
|
|
5620
|
-
const goal = String(args.goal ?? '').trim();
|
|
5621
|
-
if (!requestId) throw new Error('CAMPAIGN_REQUEST_ID_REQUIRED');
|
|
5622
|
-
if (!title) throw new Error('CAMPAIGN_TITLE_REQUIRED');
|
|
5623
|
-
if (!goal) throw new Error('CAMPAIGN_GOAL_REQUIRED');
|
|
5624
|
-
const budget = args.budget && typeof args.budget === 'object' && !Array.isArray(args.budget)
|
|
5625
|
-
? args.budget as Record<string, unknown>
|
|
5626
|
-
: {};
|
|
5627
|
-
const budgetInput = {
|
|
5628
|
-
maxParallelTasks: typeof budget.max_parallel_tasks === 'number' ? budget.max_parallel_tasks : undefined,
|
|
5629
|
-
maxExecutionJobs: typeof budget.max_execution_jobs === 'number' ? budget.max_execution_jobs : undefined,
|
|
5630
|
-
maxSupervisorReviews: typeof budget.max_supervisor_reviews === 'number' ? budget.max_supervisor_reviews : undefined,
|
|
5631
|
-
defaultTaskMaxAttempts: typeof budget.default_task_max_attempts === 'number' ? budget.default_task_max_attempts : undefined,
|
|
5632
|
-
taskTimeoutMs: typeof budget.task_timeout_ms === 'number' ? budget.task_timeout_ms : undefined,
|
|
5633
|
-
retryBaseDelayMs: typeof budget.retry_base_delay_ms === 'number' ? budget.retry_base_delay_ms : undefined,
|
|
5634
|
-
retryMaxDelayMs: typeof budget.retry_max_delay_ms === 'number' ? budget.retry_max_delay_ms : undefined,
|
|
5635
|
-
reviewPacketMaxBytes: typeof budget.review_packet_max_bytes === 'number' ? budget.review_packet_max_bytes : undefined,
|
|
5636
|
-
};
|
|
5637
|
-
// Validate task shape, DAG, and governance before creating any Git refs or worktrees.
|
|
5638
|
-
const sourceTasks = rawTasks.map((task) => campaignTaskInput(task, repository.repoId, repository.activeCheckoutId));
|
|
5639
|
-
validateCreateCampaignTasks(sourceTasks, budgetInput);
|
|
5640
|
-
// Validate supervisor policy before allocating a Git worktree. Invalid
|
|
5641
|
-
// recursive or external-effect operations must leave no workspace behind.
|
|
5642
|
-
const supervisor = args.supervisor && typeof args.supervisor === 'object' && !Array.isArray(args.supervisor)
|
|
5643
|
-
? args.supervisor as Record<string, unknown>
|
|
5644
|
-
: {};
|
|
5645
|
-
const supervisorMode = supervisor.mode === 'operation'
|
|
5646
|
-
? 'operation'
|
|
5647
|
-
: supervisor.mode === 'workspace_agent' ? 'workspace_agent' : 'pull';
|
|
5648
|
-
const supervisorOperation = typeof supervisor.operation === 'string' && supervisor.operation.trim()
|
|
5649
|
-
? assertCampaignOperationSupported(supervisor.operation)
|
|
5650
|
-
: undefined;
|
|
5651
|
-
const workspaceAgentId = typeof supervisor.workspace_agent_id === 'string'
|
|
5652
|
-
? supervisor.workspace_agent_id.trim()
|
|
5653
|
-
: typeof supervisor.workspaceAgentId === 'string' ? supervisor.workspaceAgentId.trim() : undefined;
|
|
5654
|
-
const conversationKey = typeof supervisor.conversation_key === 'string'
|
|
5655
|
-
? supervisor.conversation_key.trim()
|
|
5656
|
-
: typeof supervisor.conversationKey === 'string' ? supervisor.conversationKey.trim() : undefined;
|
|
5657
|
-
const supervisorArguments = supervisor.arguments && typeof supervisor.arguments === 'object' && !Array.isArray(supervisor.arguments)
|
|
5658
|
-
? supervisor.arguments as Record<string, unknown>
|
|
5659
|
-
: undefined;
|
|
5660
|
-
if (supervisorOperation) {
|
|
5661
|
-
if (CAMPAIGN_CONTROL_OPERATIONS.has(supervisorOperation)) throw new Error(`CAMPAIGN_RECURSIVE_OPERATION_DENIED: ${supervisorOperation}`);
|
|
5662
|
-
assertAutomatedOperationAllowed(supervisorOperation, supervisorArguments ?? {});
|
|
5663
|
-
}
|
|
5664
|
-
if (supervisorMode === 'operation' && !supervisorOperation) throw new Error('CAMPAIGN_SUPERVISOR_OPERATION_REQUIRED');
|
|
5665
|
-
if (supervisorMode === 'workspace_agent' && !workspaceAgentId) throw new Error('CAMPAIGN_WORKSPACE_AGENT_ID_REQUIRED');
|
|
5666
|
-
|
|
5667
|
-
const workspaceInput = args.workspace && typeof args.workspace === 'object' && !Array.isArray(args.workspace)
|
|
5668
|
-
? args.workspace as Record<string, unknown>
|
|
5669
|
-
: {};
|
|
5670
|
-
const workspaceMode = workspaceInput.mode === 'current' ? 'current' : 'isolated';
|
|
5671
|
-
const workspace = workspaceMode === 'current'
|
|
5672
|
-
? currentCampaignWorkspace(repository)
|
|
5673
|
-
: ensureCampaignWorkspace(ctx.controllerHome, repository, {
|
|
5674
|
-
requestId,
|
|
5675
|
-
title,
|
|
5676
|
-
baseRef: typeof workspaceInput.base_ref === 'string'
|
|
5677
|
-
? workspaceInput.base_ref
|
|
5678
|
-
: typeof workspaceInput.baseRef === 'string' ? workspaceInput.baseRef : undefined,
|
|
5679
|
-
branchName: typeof workspaceInput.branch_name === 'string'
|
|
5680
|
-
? workspaceInput.branch_name
|
|
5681
|
-
: typeof workspaceInput.branchName === 'string' ? workspaceInput.branchName : undefined,
|
|
5682
|
-
});
|
|
5683
|
-
const campaignCheckoutId = workspace.checkoutId ?? repository.activeCheckoutId;
|
|
5684
|
-
const campaign = createCampaign(ctx.controllerHome, {
|
|
5685
|
-
repoId: repository.repoId,
|
|
5686
|
-
checkoutId: campaignCheckoutId,
|
|
5687
|
-
workspace,
|
|
5688
|
-
requestId,
|
|
5689
|
-
semanticKey: typeof args.semantic_key === 'string' && args.semantic_key.trim()
|
|
5690
|
-
? args.semantic_key.trim()
|
|
5691
|
-
: `campaign:${repository.repoId}:${createHash('sha256').update(`${String(args.title ?? '')}:${String(args.goal ?? '')}`).digest('hex').slice(0, 20)}`,
|
|
5692
|
-
title,
|
|
5693
|
-
goal,
|
|
5694
|
-
acceptanceCriteria: stringList(args.acceptance_criteria),
|
|
5695
|
-
nonGoals: stringList(args.non_goals),
|
|
5696
|
-
reviewPolicy: ['every_task', 'failures_and_final', 'final_only'].includes(String(args.review_policy))
|
|
5697
|
-
? String(args.review_policy) as CampaignReviewPolicy
|
|
5698
|
-
: 'every_task',
|
|
5699
|
-
tasks: rawTasks.map((task) => campaignTaskInput(task, repository.repoId, campaignCheckoutId)),
|
|
5700
|
-
budget: budgetInput,
|
|
5701
|
-
supervisor: {
|
|
5702
|
-
mode: supervisorMode,
|
|
5703
|
-
operation: supervisorMode === 'operation' ? supervisorOperation : undefined,
|
|
5704
|
-
workspaceAgentId: supervisorMode === 'workspace_agent' ? workspaceAgentId : undefined,
|
|
5705
|
-
conversationKey: supervisorMode === 'workspace_agent' ? conversationKey : undefined,
|
|
5706
|
-
arguments: supervisorMode === 'operation' ? supervisorArguments : undefined,
|
|
5707
|
-
priority: ['P0', 'P1', 'P2', 'P3', 'P4'].includes(String(supervisor.priority)) ? String(supervisor.priority) as 'P0' | 'P1' | 'P2' | 'P3' | 'P4' : undefined,
|
|
5708
|
-
resourceClaims: supervisorMode === 'operation' && supervisorOperation
|
|
5709
|
-
? claimsForMcpOperation(supervisorOperation, supervisorArguments ?? {}, repository.repoId, campaignCheckoutId)
|
|
5710
|
-
: undefined,
|
|
5711
|
-
triggerCooldownMs: typeof supervisor.trigger_cooldown_ms === 'number' ? supervisor.trigger_cooldown_ms : undefined,
|
|
5712
|
-
maxTriggerAttempts: typeof supervisor.max_trigger_attempts === 'number' ? supervisor.max_trigger_attempts : undefined,
|
|
5713
|
-
decisionTimeoutMs: typeof supervisor.decision_timeout_ms === 'number' ? supervisor.decision_timeout_ms : undefined,
|
|
5714
|
-
},
|
|
5715
|
-
createdBy: 'chatgpt',
|
|
5716
|
-
});
|
|
5717
|
-
return result({ ...campaign, next: 'Use get_campaign_review_packet when the campaign opens a checkpoint.' });
|
|
5718
|
-
}
|
|
5719
|
-
case 'list_campaigns': {
|
|
5720
|
-
const repository = selected(ctx, args);
|
|
5721
|
-
return result({ campaigns: listCampaigns(ctx.controllerHome, repository.repoId, typeof args.limit === 'number' ? args.limit : 100) });
|
|
5722
|
-
}
|
|
5723
|
-
case 'get_campaign': {
|
|
5724
|
-
const repository = selected(ctx, args);
|
|
5725
|
-
return result({ campaign: getCampaign(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? '')) });
|
|
5726
|
-
}
|
|
5727
|
-
case 'add_campaign_task': {
|
|
5728
|
-
const repository = selected(ctx, args);
|
|
5729
|
-
if (campaignAutomationDeprecated()) {
|
|
5730
|
-
return result({
|
|
5731
|
-
error: { code: 'CAMPAIGN_DEPRECATED', message: 'Campaign task creation is retired. Add a WorkContract to an approved PlanContract instead.' },
|
|
5732
|
-
deprecated: true,
|
|
5733
|
-
repoId: repository.repoId,
|
|
5734
|
-
}, true);
|
|
5735
|
-
}
|
|
5736
|
-
const campaignId = String(args.campaign_id ?? '');
|
|
5737
|
-
const existingCampaign = getCampaign(ctx.controllerHome, repository.repoId, campaignId);
|
|
5738
|
-
const campaign = addCampaignTask(
|
|
5739
|
-
ctx.controllerHome,
|
|
5740
|
-
repository.repoId,
|
|
5741
|
-
campaignId,
|
|
5742
|
-
String(args.request_id ?? ''),
|
|
5743
|
-
campaignTaskInput(args.task, repository.repoId, existingCampaign.checkoutId),
|
|
5744
|
-
expectedRevision(args),
|
|
5745
|
-
);
|
|
5746
|
-
return result({ campaign });
|
|
5747
|
-
}
|
|
5748
|
-
case 'pause_campaign': {
|
|
5749
|
-
const repository = selected(ctx, args);
|
|
5750
|
-
return result({ campaign: setCampaignStatus(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''), String(args.request_id ?? ''), 'paused', typeof args.reason === 'string' ? args.reason : undefined, expectedRevision(args)) });
|
|
5751
|
-
}
|
|
5752
|
-
case 'resume_campaign': {
|
|
5753
|
-
const repository = selected(ctx, args);
|
|
5754
|
-
if (campaignAutomationDeprecated()) {
|
|
5755
|
-
return result({
|
|
5756
|
-
error: { code: 'CAMPAIGN_DEPRECATED', message: 'Campaign automation cannot be resumed. Use reconcile_campaign once to migrate outstanding tasks to WorkContracts.' },
|
|
5757
|
-
deprecated: true,
|
|
5758
|
-
repoId: repository.repoId,
|
|
5759
|
-
}, true);
|
|
5760
|
-
}
|
|
5761
|
-
const campaign = setCampaignStatus(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''), String(args.request_id ?? ''), 'active', undefined, expectedRevision(args));
|
|
5762
|
-
return result({ campaign, warning: 'Campaign reconcile is frozen; use reconcile_campaign once to migrate pending tasks to WorkContracts.' });
|
|
5763
|
-
}
|
|
5764
|
-
case 'cancel_campaign': {
|
|
5765
|
-
const repository = selected(ctx, args);
|
|
5766
|
-
return result({ campaign: await cancelCampaign(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''), String(args.request_id ?? ''), typeof args.reason === 'string' ? args.reason : undefined, expectedRevision(args)) });
|
|
5767
|
-
}
|
|
5768
|
-
case 'get_campaign_review_packet': {
|
|
5769
|
-
const repository = selected(ctx, args);
|
|
5770
|
-
const campaign = getCampaign(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''));
|
|
5771
|
-
const checkpointId = typeof args.checkpoint_id === 'string' && args.checkpoint_id.trim()
|
|
5772
|
-
? args.checkpoint_id.trim()
|
|
5773
|
-
: campaign.checkpoints.filter((checkpoint) => checkpoint.status === 'open').at(-1)?.checkpointId;
|
|
5774
|
-
if (!checkpointId) throw new Error('CAMPAIGN_OPEN_CHECKPOINT_NOT_FOUND');
|
|
5775
|
-
const checkpoint = campaign.checkpoints.find((entry) => entry.checkpointId === checkpointId);
|
|
5776
|
-
if (!checkpoint || checkpoint.status !== 'open') throw new Error(`CAMPAIGN_OPEN_CHECKPOINT_NOT_FOUND: ${checkpointId}`);
|
|
5777
|
-
return result({ campaignId: campaign.campaignId, campaignRevision: campaign.revision, checkpoint: { ...checkpoint, packet: checkpoint.packet } });
|
|
5778
|
-
}
|
|
5779
|
-
case 'submit_campaign_review': {
|
|
5780
|
-
const repository = selected(ctx, args);
|
|
5781
|
-
const revised = args.revised_goal && typeof args.revised_goal === 'object' && !Array.isArray(args.revised_goal)
|
|
5782
|
-
? args.revised_goal as Record<string, unknown>
|
|
5783
|
-
: undefined;
|
|
5784
|
-
const campaign = submitCampaignReview(ctx.controllerHome, {
|
|
5785
|
-
repoId: repository.repoId,
|
|
5786
|
-
campaignId: String(args.campaign_id ?? ''),
|
|
5787
|
-
checkpointId: String(args.checkpoint_id ?? ''),
|
|
5788
|
-
nonce: String(args.checkpoint_nonce ?? ''),
|
|
5789
|
-
goalRevision: Number(args.goal_revision),
|
|
5790
|
-
expectedCampaignRevision: expectedRevision(args, 'expected_campaign_revision'),
|
|
5791
|
-
requestId: String(args.request_id ?? ''),
|
|
5792
|
-
decision: {
|
|
5793
|
-
action: String(args.action ?? '') as CampaignSupervisorAction,
|
|
5794
|
-
summary: String(args.summary ?? ''),
|
|
5795
|
-
instructions: typeof args.instructions === 'string' ? args.instructions : undefined,
|
|
5796
|
-
revisedGoal: revised ? {
|
|
5797
|
-
statement: String(revised.statement ?? ''),
|
|
5798
|
-
acceptanceCriteria: stringList(revised.acceptance_criteria),
|
|
5799
|
-
nonGoals: stringList(revised.non_goals),
|
|
5800
|
-
reason: typeof revised.reason === 'string' ? revised.reason : undefined,
|
|
5801
|
-
} : undefined,
|
|
5802
|
-
submittedBy: typeof args.submitted_by === 'string' ? args.submitted_by : 'chatgpt',
|
|
5803
|
-
},
|
|
5804
|
-
});
|
|
5805
|
-
return result({ campaign, warning: 'Campaign reconcile is frozen; review state is retained for migration.' });
|
|
5806
|
-
}
|
|
5807
|
-
case 'accept_campaign': {
|
|
5808
|
-
const repository = selected(ctx, args);
|
|
5809
|
-
const current = getCampaign(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''));
|
|
5810
|
-
if (current.status !== 'ready_for_human_acceptance') throw new Error(`CAMPAIGN_NOT_READY_FOR_ACCEPTANCE: ${current.status}`);
|
|
5811
|
-
const requestId = String(args.request_id ?? '');
|
|
5812
|
-
setCampaignStatus(ctx.controllerHome, repository.repoId, current.campaignId, requestId, 'completed', 'Accepted by human.', expectedRevision(args));
|
|
5813
|
-
const campaign = completeCampaignWorkspace(ctx.controllerHome, repository.repoId, current.campaignId, requestId);
|
|
5814
|
-
return result({ campaign });
|
|
5815
|
-
}
|
|
5816
|
-
case 'reconcile_campaign': {
|
|
5817
|
-
const repository = selected(ctx, args);
|
|
5818
|
-
const reconciliation = reconcileCampaign(ctx.controllerHome, repository.repoId, String(args.campaign_id ?? ''));
|
|
5819
|
-
return result({ reconciliation, campaign: getCampaign(ctx.controllerHome, repository.repoId, reconciliation.campaignId) });
|
|
5820
|
-
}
|
|
5821
5743
|
case 'create_schedule': {
|
|
5822
5744
|
const repository = selected(ctx, args);
|
|
5823
5745
|
const operation = String(args.operation ?? '').trim();
|