agentic-orchestrator 0.1.2 → 0.1.4
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/.claude/settings.local.json +15 -0
- package/CLAUDE.md +126 -0
- package/README.md +166 -25
- package/agentic/orchestrator/adapters.yaml +3 -0
- package/agentic/orchestrator/gates.yaml +47 -0
- package/agentic/orchestrator/policy.yaml +89 -0
- package/agentic/orchestrator/schemas/adapters.schema.json +12 -0
- package/agentic/orchestrator/schemas/gates.schema.json +6 -1
- package/agentic/orchestrator/schemas/index.schema.json +14 -0
- package/agentic/orchestrator/schemas/multi-project.schema.json +41 -0
- package/agentic/orchestrator/schemas/policy.schema.json +449 -52
- package/agentic/orchestrator/schemas/state.schema.json +16 -0
- package/agentic/orchestrator/tools/catalog.json +68 -0
- package/agentic/orchestrator/tools/schemas/input/cost.get.input.schema.json +10 -0
- package/agentic/orchestrator/tools/schemas/input/cost.record.input.schema.json +13 -0
- package/agentic/orchestrator/tools/schemas/input/feature.send_message.input.schema.json +11 -0
- package/agentic/orchestrator/tools/schemas/input/performance.get_analytics.input.schema.json +10 -0
- package/agentic/orchestrator/tools/schemas/input/performance.record_outcome.input.schema.json +18 -0
- package/agentic/orchestrator/tools/schemas/output/cost.get.output.schema.json +13 -0
- package/agentic/orchestrator/tools/schemas/output/cost.record.output.schema.json +13 -0
- package/agentic/orchestrator/tools/schemas/output/feature.ready_to_merge.output.schema.json +7 -0
- package/agentic/orchestrator/tools/schemas/output/feature.send_message.output.schema.json +23 -0
- package/agentic/orchestrator/tools/schemas/output/performance.get_analytics.output.schema.json +46 -0
- package/agentic/orchestrator/tools/schemas/output/performance.record_outcome.output.schema.json +10 -0
- package/agentic/orchestrator/tools.md +5 -0
- package/apps/control-plane/scripts/validate-architecture-rules.mjs +28 -2
- package/apps/control-plane/scripts/validate-docker-mcp-contract.mjs +12 -0
- package/apps/control-plane/scripts/validate-mcp-contracts.ts +92 -0
- package/apps/control-plane/src/application/adapters/adapter-registry.ts +169 -0
- package/apps/control-plane/src/application/multi-project-loader.ts +119 -0
- package/apps/control-plane/src/application/services/activity-monitor-service.ts +199 -0
- package/apps/control-plane/src/application/services/cost-tracking-service.ts +82 -0
- package/apps/control-plane/src/application/services/dependency-scheduler-service.ts +86 -0
- package/apps/control-plane/src/application/services/feature-deletion-service.ts +8 -7
- package/apps/control-plane/src/application/services/gate-interpolation-service.ts +15 -0
- package/apps/control-plane/src/application/services/gate-service.ts +38 -2
- package/apps/control-plane/src/application/services/instance-isolation-service.ts +18 -0
- package/apps/control-plane/src/application/services/issue-tracker-service.ts +469 -0
- package/apps/control-plane/src/application/services/merge-service.ts +67 -3
- package/apps/control-plane/src/application/services/notifier-service.ts +295 -0
- package/apps/control-plane/src/application/services/performance-analytics-service.ts +122 -0
- package/apps/control-plane/src/application/services/plan-service.ts +51 -0
- package/apps/control-plane/src/application/services/pr-monitor-service.ts +262 -0
- package/apps/control-plane/src/application/services/reactions-service.ts +175 -0
- package/apps/control-plane/src/application/services/reporting-service.ts +17 -2
- package/apps/control-plane/src/application/services/run-lease-service.ts +16 -38
- package/apps/control-plane/src/application/tools/tool-metadata.ts +4 -1
- package/apps/control-plane/src/cli/aop.ts +1 -1
- package/apps/control-plane/src/cli/attach-command-handler.ts +120 -0
- package/apps/control-plane/src/cli/cleanup-command-handler.ts +190 -0
- package/apps/control-plane/src/cli/cli-argument-parser.ts +69 -3
- package/apps/control-plane/src/cli/dashboard-command-handler.ts +57 -0
- package/apps/control-plane/src/cli/help-command-handler.ts +163 -0
- package/apps/control-plane/src/cli/init-command-handler.ts +609 -0
- package/apps/control-plane/src/cli/resume-command-handler.ts +1 -0
- package/apps/control-plane/src/cli/retry-command-handler.ts +138 -0
- package/apps/control-plane/src/cli/run-command-handler.ts +115 -3
- package/apps/control-plane/src/cli/send-command-handler.ts +65 -0
- package/apps/control-plane/src/cli/status-command-handler.ts +102 -2
- package/apps/control-plane/src/cli/types.ts +26 -1
- package/apps/control-plane/src/core/constants.ts +8 -2
- package/apps/control-plane/src/core/error-codes.ts +3 -1
- package/apps/control-plane/src/core/gates.ts +170 -50
- package/apps/control-plane/src/core/kernel.ts +280 -5
- package/apps/control-plane/src/core/path-layout.ts +12 -0
- package/apps/control-plane/src/core/tool-caller.ts +36 -0
- package/apps/control-plane/src/core/workspace-hooks.ts +87 -0
- package/apps/control-plane/src/interfaces/cli/bootstrap.ts +258 -9
- package/apps/control-plane/src/providers/providers.ts +235 -14
- package/apps/control-plane/src/supervisor/build-wave-executor.ts +129 -8
- package/apps/control-plane/src/supervisor/qa-wave-executor.ts +123 -5
- package/apps/control-plane/src/supervisor/run-coordinator.ts +143 -6
- package/apps/control-plane/src/supervisor/runtime.ts +135 -6
- package/apps/control-plane/src/supervisor/types.ts +12 -21
- package/apps/control-plane/src/supervisor/worker-decision-loop.ts +8 -0
- package/apps/control-plane/test/activity-monitor.spec.ts +294 -0
- package/apps/control-plane/test/adapter-registry.spec.ts +132 -0
- package/apps/control-plane/test/batch-operations.spec.ts +112 -0
- package/apps/control-plane/test/bootstrap-attach.spec.ts +102 -0
- package/apps/control-plane/test/bootstrap-edge-cases.spec.ts +252 -0
- package/apps/control-plane/test/bootstrap.spec.ts +560 -0
- package/apps/control-plane/test/cleanup-command.spec.ts +301 -0
- package/apps/control-plane/test/cli-helpers.spec.ts +404 -1
- package/apps/control-plane/test/cli.unit.spec.ts +182 -1
- package/apps/control-plane/test/collision-queue.spec.ts +104 -1
- package/apps/control-plane/test/core-utils.spec.ts +175 -2
- package/apps/control-plane/test/cost-tracking.spec.ts +143 -0
- package/apps/control-plane/test/dashboard-api.integration.spec.ts +247 -0
- package/apps/control-plane/test/dashboard-client.spec.ts +116 -0
- package/apps/control-plane/test/dashboard-command.spec.ts +103 -0
- package/apps/control-plane/test/dependency-scheduler.spec.ts +189 -0
- package/apps/control-plane/test/epoch-tracking.spec.ts +4 -4
- package/apps/control-plane/test/feature-deletion-service.spec.ts +422 -0
- package/apps/control-plane/test/feature-lifecycle.spec.ts +202 -0
- package/apps/control-plane/test/git-spawn-error.spec.ts +24 -0
- package/apps/control-plane/test/incremental-gates.spec.ts +137 -0
- package/apps/control-plane/test/init-wizard.spec.ts +506 -0
- package/apps/control-plane/test/instance-isolation.spec.ts +83 -0
- package/apps/control-plane/test/issue-tracker.spec.ts +890 -0
- package/apps/control-plane/test/kernel.coverage.spec.ts +3 -5
- package/apps/control-plane/test/kernel.coverage2.spec.ts +871 -0
- package/apps/control-plane/test/kernel.spec.ts +13 -11
- package/apps/control-plane/test/lock-service.spec.ts +508 -0
- package/apps/control-plane/test/mcp-helpers.spec.ts +176 -0
- package/apps/control-plane/test/mcp.spec.ts +50 -15
- package/apps/control-plane/test/merge-service.spec.ts +67 -4
- package/apps/control-plane/test/multi-project.spec.ts +372 -0
- package/apps/control-plane/test/notifier-service.spec.ts +388 -0
- package/apps/control-plane/test/parallel-gates.spec.ts +312 -0
- package/apps/control-plane/test/patch-service.spec.ts +253 -0
- package/apps/control-plane/test/performance-analytics.spec.ts +338 -0
- package/apps/control-plane/test/planning-wave-executor.spec.ts +168 -0
- package/apps/control-plane/test/pr-monitor.spec.ts +385 -0
- package/apps/control-plane/test/providers.spec.ts +344 -1
- package/apps/control-plane/test/reactions.spec.ts +392 -0
- package/apps/control-plane/test/resume-command.spec.ts +390 -0
- package/apps/control-plane/test/run-coordinator.spec.ts +481 -2
- package/apps/control-plane/test/schema-date-time.spec.ts +46 -0
- package/apps/control-plane/test/service-retry-paths.spec.ts +30 -0
- package/apps/control-plane/test/services.spec.ts +95 -2
- package/apps/control-plane/test/session-management.spec.ts +450 -0
- package/apps/control-plane/test/spec-ingestion.spec.ts +190 -0
- package/apps/control-plane/test/supervisor-collaborators.spec.ts +699 -2
- package/apps/control-plane/test/supervisor.spec.ts +36 -30
- package/apps/control-plane/test/supervisor.unit.spec.ts +405 -0
- package/apps/control-plane/test/worker-decision-loop.spec.ts +57 -0
- package/apps/control-plane/test/workspace-hooks.spec.ts +177 -0
- package/apps/control-plane/vitest.config.ts +21 -5
- package/dist/apps/control-plane/application/adapters/adapter-registry.d.ts +44 -0
- package/dist/apps/control-plane/application/adapters/adapter-registry.js +76 -0
- package/dist/apps/control-plane/application/adapters/adapter-registry.js.map +1 -0
- package/dist/apps/control-plane/application/multi-project-loader.d.ts +31 -0
- package/dist/apps/control-plane/application/multi-project-loader.js +82 -0
- package/dist/apps/control-plane/application/multi-project-loader.js.map +1 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.d.ts +43 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js +132 -0
- package/dist/apps/control-plane/application/services/activity-monitor-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.d.ts +28 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js +48 -0
- package/dist/apps/control-plane/application/services/cost-tracking-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/dependency-scheduler-service.d.ts +26 -0
- package/dist/apps/control-plane/application/services/dependency-scheduler-service.js +75 -0
- package/dist/apps/control-plane/application/services/dependency-scheduler-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.d.ts +2 -0
- package/dist/apps/control-plane/application/services/feature-deletion-service.js +6 -7
- package/dist/apps/control-plane/application/services/feature-deletion-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/gate-interpolation-service.d.ts +7 -0
- package/dist/apps/control-plane/application/services/gate-interpolation-service.js +7 -0
- package/dist/apps/control-plane/application/services/gate-interpolation-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/gate-service.js +32 -2
- package/dist/apps/control-plane/application/services/gate-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/instance-isolation-service.d.ts +11 -0
- package/dist/apps/control-plane/application/services/instance-isolation-service.js +17 -0
- package/dist/apps/control-plane/application/services/instance-isolation-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.d.ts +65 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js +358 -0
- package/dist/apps/control-plane/application/services/issue-tracker-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/merge-service.d.ts +4 -0
- package/dist/apps/control-plane/application/services/merge-service.js +44 -2
- package/dist/apps/control-plane/application/services/merge-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/notifier-service.d.ts +74 -0
- package/dist/apps/control-plane/application/services/notifier-service.js +212 -0
- package/dist/apps/control-plane/application/services/notifier-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.d.ts +39 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js +75 -0
- package/dist/apps/control-plane/application/services/performance-analytics-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/plan-service.d.ts +1 -0
- package/dist/apps/control-plane/application/services/plan-service.js +53 -0
- package/dist/apps/control-plane/application/services/plan-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/pr-monitor-service.d.ts +44 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js +192 -0
- package/dist/apps/control-plane/application/services/pr-monitor-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/reactions-service.d.ts +67 -0
- package/dist/apps/control-plane/application/services/reactions-service.js +114 -0
- package/dist/apps/control-plane/application/services/reactions-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/reporting-service.d.ts +1 -0
- package/dist/apps/control-plane/application/services/reporting-service.js +13 -2
- package/dist/apps/control-plane/application/services/reporting-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/run-lease-service.d.ts +2 -0
- package/dist/apps/control-plane/application/services/run-lease-service.js +14 -38
- package/dist/apps/control-plane/application/services/run-lease-service.js.map +1 -1
- package/dist/apps/control-plane/application/tools/tool-metadata.js +3 -1
- package/dist/apps/control-plane/application/tools/tool-metadata.js.map +1 -1
- package/dist/apps/control-plane/cli/aop.d.ts +1 -1
- package/dist/apps/control-plane/cli/aop.js +1 -1
- package/dist/apps/control-plane/cli/attach-command-handler.d.ts +12 -0
- package/dist/apps/control-plane/cli/attach-command-handler.js +98 -0
- package/dist/apps/control-plane/cli/attach-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/cleanup-command-handler.d.ts +12 -0
- package/dist/apps/control-plane/cli/cleanup-command-handler.js +162 -0
- package/dist/apps/control-plane/cli/cleanup-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/cli-argument-parser.js +73 -3
- package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
- package/dist/apps/control-plane/cli/dashboard-command-handler.d.ts +7 -0
- package/dist/apps/control-plane/cli/dashboard-command-handler.js +45 -0
- package/dist/apps/control-plane/cli/dashboard-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/help-command-handler.d.ts +8 -0
- package/dist/apps/control-plane/cli/help-command-handler.js +146 -0
- package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/init-command-handler.d.ts +26 -0
- package/dist/apps/control-plane/cli/init-command-handler.js +517 -0
- package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/resume-command-handler.js +1 -1
- package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/retry-command-handler.d.ts +8 -0
- package/dist/apps/control-plane/cli/retry-command-handler.js +111 -0
- package/dist/apps/control-plane/cli/retry-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/run-command-handler.d.ts +5 -0
- package/dist/apps/control-plane/cli/run-command-handler.js +82 -3
- package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/send-command-handler.d.ts +8 -0
- package/dist/apps/control-plane/cli/send-command-handler.js +55 -0
- package/dist/apps/control-plane/cli/send-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/status-command-handler.d.ts +12 -1
- package/dist/apps/control-plane/cli/status-command-handler.js +55 -2
- package/dist/apps/control-plane/cli/status-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/types.d.ts +26 -1
- package/dist/apps/control-plane/cli/types.js +15 -1
- package/dist/apps/control-plane/cli/types.js.map +1 -1
- package/dist/apps/control-plane/core/constants.d.ts +6 -0
- package/dist/apps/control-plane/core/constants.js +8 -2
- package/dist/apps/control-plane/core/constants.js.map +1 -1
- package/dist/apps/control-plane/core/error-codes.d.ts +2 -0
- package/dist/apps/control-plane/core/error-codes.js +3 -1
- package/dist/apps/control-plane/core/error-codes.js.map +1 -1
- package/dist/apps/control-plane/core/gates.d.ts +4 -0
- package/dist/apps/control-plane/core/gates.js +140 -43
- package/dist/apps/control-plane/core/gates.js.map +1 -1
- package/dist/apps/control-plane/core/kernel.d.ts +50 -1
- package/dist/apps/control-plane/core/kernel.js +220 -7
- package/dist/apps/control-plane/core/kernel.js.map +1 -1
- package/dist/apps/control-plane/core/path-layout.d.ts +3 -0
- package/dist/apps/control-plane/core/path-layout.js +9 -0
- package/dist/apps/control-plane/core/path-layout.js.map +1 -1
- package/dist/apps/control-plane/core/tool-caller.d.ts +32 -0
- package/dist/apps/control-plane/core/tool-caller.js +2 -0
- package/dist/apps/control-plane/core/tool-caller.js.map +1 -0
- package/dist/apps/control-plane/core/workspace-hooks.d.ts +20 -0
- package/dist/apps/control-plane/core/workspace-hooks.js +69 -0
- package/dist/apps/control-plane/core/workspace-hooks.js.map +1 -0
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js +245 -9
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
- package/dist/apps/control-plane/providers/providers.d.ts +42 -3
- package/dist/apps/control-plane/providers/providers.js +216 -5
- package/dist/apps/control-plane/providers/providers.js.map +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js +115 -6
- package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js +109 -5
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +15 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js +132 -6
- package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
- package/dist/apps/control-plane/supervisor/runtime.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/runtime.js +110 -6
- package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
- package/dist/apps/control-plane/supervisor/types.d.ts +9 -16
- package/dist/apps/control-plane/supervisor/types.js.map +1 -1
- package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/worker-decision-loop.js +5 -0
- package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
- package/eslint.config.mjs +2 -1
- package/package.json +12 -2
- package/packages/web-dashboard/next-env.d.ts +5 -0
- package/packages/web-dashboard/next.config.js +7 -0
- package/packages/web-dashboard/package.json +26 -0
- package/packages/web-dashboard/src/app/api/actions/route.ts +64 -0
- package/packages/web-dashboard/src/app/api/events/route.ts +51 -0
- package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +256 -0
- package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +10 -0
- package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +25 -0
- package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +63 -0
- package/packages/web-dashboard/src/app/api/features/[id]/route.ts +16 -0
- package/packages/web-dashboard/src/app/api/projects/route.ts +31 -0
- package/packages/web-dashboard/src/app/api/status/route.ts +15 -0
- package/packages/web-dashboard/src/app/globals.css +2 -0
- package/packages/web-dashboard/src/app/layout.tsx +15 -0
- package/packages/web-dashboard/src/app/page.tsx +393 -0
- package/packages/web-dashboard/src/lib/aop-client.ts +244 -0
- package/packages/web-dashboard/src/lib/multi-project-config.ts +116 -0
- package/packages/web-dashboard/src/lib/orchestrator-tools.ts +284 -0
- package/packages/web-dashboard/src/lib/types.ts +58 -0
- package/packages/web-dashboard/tsconfig.json +40 -0
- package/packages/web-dashboard/vitest.config.ts +6 -0
- package/spec-files/completed/agentic_orchestrator_feature_gaps_closure_spec.md +1764 -0
- package/spec-files/outstanding/agentic_orchestrator_enterprise_governance_dashboard_spec.md +348 -0
- package/spec-files/outstanding/agentic_orchestrator_knowledge_canary_spec.md +344 -0
- package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +374 -0
- package/spec-files/outstanding/agentic_orchestrator_performance_improvements_spec.md +1059 -0
- package/spec-files/outstanding/agentic_orchestrator_planning_review_quality_spec.md +466 -0
- package/spec-files/outstanding/agentic_orchestrator_quality_adoption_execution_spec.md +198 -0
- package/spec-files/outstanding/agentic_orchestrator_validator_hardening_spec.md +365 -0
- package/spec-files/progress.md +481 -52
- /package/spec-files/{agentic_orchestrator_cli_delete_command_spec.md → completed/agentic_orchestrator_cli_delete_command_spec.md} +0 -0
- /package/spec-files/{agentic_orchestrator_dot_aop_generated_artifacts_spec.md → completed/agentic_orchestrator_dot_aop_generated_artifacts_spec.md} +0 -0
- /package/spec-files/{agentic_orchestrator_mcp_formalization_spec.md → completed/agentic_orchestrator_mcp_formalization_spec.md} +0 -0
- /package/spec-files/{agentic_orchestrator_oop_refactor_spec.md → completed/agentic_orchestrator_oop_refactor_spec.md} +0 -0
- /package/spec-files/{agentic_orchestrator_single_global_orchestrator_spec.md → completed/agentic_orchestrator_single_global_orchestrator_spec.md} +0 -0
- /package/spec-files/{agentic_orchestrator_spec.md → completed/agentic_orchestrator_spec.md} +0 -0
|
@@ -27,6 +27,8 @@ export interface GateStep {
|
|
|
27
27
|
cwd?: string;
|
|
28
28
|
env?: Record<string, string>;
|
|
29
29
|
timeout_seconds?: number;
|
|
30
|
+
parallel_group?: string;
|
|
31
|
+
depends_on?: string[];
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export interface GateProfile {
|
|
@@ -172,6 +174,37 @@ export async function parseCoverage(parserConfig: ParserConfig | undefined, work
|
|
|
172
174
|
return defaultCoverageParserRegistry.parse(parserConfig, content);
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
export function buildExecutionWaves(steps: GateStep[]): GateStep[][] {
|
|
178
|
+
const stepMap = new Map(steps.map((s) => [s.name, s]));
|
|
179
|
+
const placed = new Set<string>();
|
|
180
|
+
const waves: GateStep[][] = [];
|
|
181
|
+
|
|
182
|
+
let remaining = [...steps];
|
|
183
|
+
while (remaining.length > 0) {
|
|
184
|
+
const wave: GateStep[] = [];
|
|
185
|
+
for (const step of remaining) {
|
|
186
|
+
const deps = step.depends_on ?? [];
|
|
187
|
+
if (deps.every((dep) => placed.has(dep) || !stepMap.has(dep))) {
|
|
188
|
+
wave.push(step);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (wave.length === 0) {
|
|
193
|
+
// Circular dependency — force remaining into final wave
|
|
194
|
+
waves.push(remaining);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
waves.push(wave);
|
|
199
|
+
for (const step of wave) {
|
|
200
|
+
placed.add(step.name);
|
|
201
|
+
}
|
|
202
|
+
remaining = remaining.filter((s) => !placed.has(s.name));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return waves;
|
|
206
|
+
}
|
|
207
|
+
|
|
175
208
|
export class GateExecutionService {
|
|
176
209
|
private readonly thresholdPolicy: CoverageThresholdPolicy;
|
|
177
210
|
|
|
@@ -179,6 +212,70 @@ export class GateExecutionService {
|
|
|
179
212
|
this.thresholdPolicy = thresholdPolicy;
|
|
180
213
|
}
|
|
181
214
|
|
|
215
|
+
private async executeStep(
|
|
216
|
+
step: GateStep,
|
|
217
|
+
ctx: {
|
|
218
|
+
featureId: string;
|
|
219
|
+
mode: string;
|
|
220
|
+
policy: PolicyShape;
|
|
221
|
+
env: NodeJS.ProcessEnv;
|
|
222
|
+
worktreePath: string;
|
|
223
|
+
logDirectory: string;
|
|
224
|
+
}
|
|
225
|
+
): Promise<Record<string, unknown>> {
|
|
226
|
+
const timeoutSeconds = Number(step.timeout_seconds ?? ctx.policy.execution.default_step_timeout_seconds);
|
|
227
|
+
const stepLogPath = path.join(
|
|
228
|
+
ctx.logDirectory,
|
|
229
|
+
`${Date.now()}-${ctx.mode}-${step.name.replaceAll(/[^a-zA-Z0-9_-]/g, '_')}.log`
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const retryPolicy = ctx.policy.execution.retry_policy;
|
|
233
|
+
let attempts = 0;
|
|
234
|
+
let lastResult: CommandResult = { code: 1, signal: null, stdout: '', stderr: '', timeout: false };
|
|
235
|
+
const maxAttempts = retryPolicy.transient_max_retries + 1;
|
|
236
|
+
|
|
237
|
+
while (attempts < maxAttempts) {
|
|
238
|
+
attempts += 1;
|
|
239
|
+
const result = await runCommand(step.cmd[0], step.cmd.slice(1), {
|
|
240
|
+
cwd: step.cwd ? path.join(ctx.worktreePath, step.cwd) : ctx.worktreePath,
|
|
241
|
+
env: { ...ctx.env, ...step.env },
|
|
242
|
+
timeoutMs: timeoutSeconds * 1000
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
lastResult = result;
|
|
246
|
+
const logText = [
|
|
247
|
+
`# feature=${ctx.featureId} mode=${ctx.mode} step=${step.name} attempt=${attempts}`,
|
|
248
|
+
`# cmd=${JSON.stringify(step.cmd)}`,
|
|
249
|
+
`# exit=${result.code} timeout=${result.timeout}`,
|
|
250
|
+
'',
|
|
251
|
+
'## stdout',
|
|
252
|
+
result.stdout,
|
|
253
|
+
'',
|
|
254
|
+
'## stderr',
|
|
255
|
+
result.stderr
|
|
256
|
+
].join('\n');
|
|
257
|
+
await fs.writeFile(stepLogPath, `${logText}\n`, 'utf8');
|
|
258
|
+
|
|
259
|
+
if (result.timeout || result.code === 0) {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const isTransient = retryPolicy.transient_error_codes.includes(result.code);
|
|
264
|
+
if (!isTransient) {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
name: step.name,
|
|
271
|
+
cmd: step.cmd,
|
|
272
|
+
attempts,
|
|
273
|
+
exit_code: lastResult.code,
|
|
274
|
+
timeout: lastResult.timeout,
|
|
275
|
+
log_path: stepLogPath
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
182
279
|
async runMode({
|
|
183
280
|
featureId,
|
|
184
281
|
mode,
|
|
@@ -206,68 +303,91 @@ export class GateExecutionService {
|
|
|
206
303
|
await ensureDir(logDirectory);
|
|
207
304
|
await ensureDir(evidenceDirectory);
|
|
208
305
|
|
|
209
|
-
const retryPolicy = policy.execution.retry_policy;
|
|
210
306
|
const env = pickAllowedEnv(policy.execution.env_allowlist);
|
|
211
307
|
const stepResults: Array<Record<string, unknown>> = [];
|
|
212
308
|
let overall = 'pass';
|
|
213
309
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const result = await runCommand(step.cmd[0], step.cmd.slice(1), {
|
|
228
|
-
cwd: step.cwd ? path.join(worktreePath, step.cwd) : worktreePath,
|
|
229
|
-
env: { ...env, ...step.env },
|
|
230
|
-
timeoutMs: timeoutSeconds * 1000
|
|
310
|
+
const hasParallelConfig = modeSteps.some((s) => s.parallel_group || s.depends_on?.length);
|
|
311
|
+
|
|
312
|
+
if (hasParallelConfig) {
|
|
313
|
+
const waves = buildExecutionWaves(modeSteps);
|
|
314
|
+
const completedSteps = new Set<string>();
|
|
315
|
+
const failedSteps = new Set<string>();
|
|
316
|
+
|
|
317
|
+
for (const wave of waves) {
|
|
318
|
+
const runnableSteps = wave.filter((step) => {
|
|
319
|
+
if (!step.depends_on?.length) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return step.depends_on.every((dep) => completedSteps.has(dep) && !failedSteps.has(dep));
|
|
231
323
|
});
|
|
232
324
|
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
break;
|
|
325
|
+
const skippedSteps = wave.filter((step) => !runnableSteps.includes(step));
|
|
326
|
+
for (const step of skippedSteps) {
|
|
327
|
+
failedSteps.add(step.name);
|
|
328
|
+
stepResults.push({
|
|
329
|
+
name: step.name,
|
|
330
|
+
cmd: step.cmd,
|
|
331
|
+
attempts: 0,
|
|
332
|
+
exit_code: -1,
|
|
333
|
+
timeout: false,
|
|
334
|
+
skipped: true,
|
|
335
|
+
skip_reason: 'dependency_failed',
|
|
336
|
+
parallel_group: step.parallel_group,
|
|
337
|
+
started_at: nowIso(),
|
|
338
|
+
finished_at: nowIso()
|
|
339
|
+
});
|
|
249
340
|
}
|
|
250
341
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
342
|
+
const results = await Promise.allSettled(
|
|
343
|
+
runnableSteps.map(async (step) => {
|
|
344
|
+
const startedAt = nowIso();
|
|
345
|
+
const stepResult = await this.executeStep(step, {
|
|
346
|
+
featureId,
|
|
347
|
+
mode,
|
|
348
|
+
policy,
|
|
349
|
+
env,
|
|
350
|
+
worktreePath,
|
|
351
|
+
logDirectory
|
|
352
|
+
});
|
|
353
|
+
stepResult['parallel_group'] = step.parallel_group;
|
|
354
|
+
stepResult['started_at'] = startedAt;
|
|
355
|
+
stepResult['finished_at'] = nowIso();
|
|
356
|
+
return stepResult;
|
|
357
|
+
})
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
for (const result of results) {
|
|
361
|
+
if (result.status === 'fulfilled') {
|
|
362
|
+
const val = result.value;
|
|
363
|
+
stepResults.push(val);
|
|
364
|
+
if (val['exit_code'] === 0 && !val['timeout']) {
|
|
365
|
+
completedSteps.add(val['name'] as string);
|
|
366
|
+
} else {
|
|
367
|
+
overall = 'fail';
|
|
368
|
+
failedSteps.add(val['name'] as string);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
254
371
|
}
|
|
255
372
|
}
|
|
373
|
+
} else {
|
|
374
|
+
for (const step of modeSteps) {
|
|
375
|
+
const startedAt = nowIso();
|
|
376
|
+
const result = await this.executeStep(step, {
|
|
377
|
+
featureId,
|
|
378
|
+
mode,
|
|
379
|
+
policy,
|
|
380
|
+
env,
|
|
381
|
+
worktreePath,
|
|
382
|
+
logDirectory
|
|
383
|
+
});
|
|
256
384
|
|
|
257
|
-
|
|
258
|
-
name: step.name,
|
|
259
|
-
cmd: step.cmd,
|
|
260
|
-
attempts,
|
|
261
|
-
exit_code: lastResult.code,
|
|
262
|
-
timeout: lastResult.timeout,
|
|
263
|
-
log_path: stepLogPath
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
stepResults.push(stepState);
|
|
385
|
+
stepResults.push({ ...result, started_at: startedAt, finished_at: nowIso() });
|
|
267
386
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
387
|
+
if (result.timeout || result.exit_code !== 0) {
|
|
388
|
+
overall = 'fail';
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
271
391
|
}
|
|
272
392
|
}
|
|
273
393
|
|
|
@@ -9,6 +9,12 @@ import { ERROR_CODES } from './error-codes.js';
|
|
|
9
9
|
import { ok, fail, withSuggestedActions, type ToolResponse } from './response.js';
|
|
10
10
|
import { ALLOWED_ACTORS, DEFAULT_CLUSTER, DEFAULT_ROLE_STATUS, GATE_RESULT, STATUS, TOOLS } from './constants.js';
|
|
11
11
|
import { AopPathLayout, ensureAopRuntimeLayout } from './path-layout.js';
|
|
12
|
+
import {
|
|
13
|
+
applyWorktreeSymlinks,
|
|
14
|
+
formatWorkspaceHookWarning,
|
|
15
|
+
runWorktreePostCreate,
|
|
16
|
+
type WorkspaceHookWarning
|
|
17
|
+
} from './workspace-hooks.js';
|
|
12
18
|
import type { RuntimeSessionsSnapshot } from './runtime-sessions.js';
|
|
13
19
|
import { ToolRegistryLoader } from '../mcp/tool-registry-loader.js';
|
|
14
20
|
import { ToolHandlerRegistry, ToolRouter, type ToolHandlerContext } from '../application/tools/tool-router.js';
|
|
@@ -24,6 +30,15 @@ import { QaIndexService } from '../application/services/qa-index-service.js';
|
|
|
24
30
|
import { MergeService } from '../application/services/merge-service.js';
|
|
25
31
|
import { CollisionQueueService } from '../application/services/collision-queue-service.js';
|
|
26
32
|
import { FeatureDeletionService, type FeatureDeleteResult } from '../application/services/feature-deletion-service.js';
|
|
33
|
+
import { CostTrackingService } from '../application/services/cost-tracking-service.js';
|
|
34
|
+
import { PerformanceAnalyticsService, type FeatureOutcome } from '../application/services/performance-analytics-service.js';
|
|
35
|
+
import {
|
|
36
|
+
ACTIVITY_DETECTOR_SLOT,
|
|
37
|
+
NOTIFICATION_CHANNEL_SLOT,
|
|
38
|
+
SCM_PROVIDER_SLOT,
|
|
39
|
+
globalAdapterRegistry
|
|
40
|
+
} from '../application/adapters/adapter-registry.js';
|
|
41
|
+
import type { WorkerProvider } from '../providers/providers.js';
|
|
27
42
|
|
|
28
43
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
44
|
type AnyRecord = Record<string, any>;
|
|
@@ -33,6 +48,13 @@ interface KernelContext {
|
|
|
33
48
|
actor_id?: string;
|
|
34
49
|
}
|
|
35
50
|
|
|
51
|
+
interface KernelConfigOverrides {
|
|
52
|
+
policyPath?: string;
|
|
53
|
+
gatesPath?: string;
|
|
54
|
+
agentsPath?: string;
|
|
55
|
+
adaptersPath?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
36
58
|
export interface AgentsRoleConfig {
|
|
37
59
|
system_prompt_path?: string;
|
|
38
60
|
}
|
|
@@ -91,6 +113,7 @@ export class AopKernel {
|
|
|
91
113
|
gatesConfig: AnyRecord;
|
|
92
114
|
policy: PolicyConfigSnapshot;
|
|
93
115
|
agentsConfig: AgentsConfigSnapshot;
|
|
116
|
+
adaptersConfig: Record<string, unknown>;
|
|
94
117
|
toolRegistry: AnyRecord | null;
|
|
95
118
|
toolHandlers: ToolHandlerRegistry;
|
|
96
119
|
toolRouter: ToolRouter;
|
|
@@ -106,16 +129,24 @@ export class AopKernel {
|
|
|
106
129
|
private readonly qaIndexService: QaIndexService;
|
|
107
130
|
private readonly mergeService: MergeService;
|
|
108
131
|
private readonly featureDeletionService: FeatureDeletionService;
|
|
132
|
+
private readonly costTrackingService: CostTrackingService;
|
|
133
|
+
private readonly performanceAnalyticsService: PerformanceAnalyticsService;
|
|
109
134
|
private readonly pathLayout: AopPathLayout;
|
|
135
|
+
readonly instanceId: string;
|
|
136
|
+
private provider: WorkerProvider | null = null;
|
|
137
|
+
private readonly configOverrides: KernelConfigOverrides;
|
|
110
138
|
|
|
111
|
-
constructor(repoRoot: string) {
|
|
139
|
+
constructor(repoRoot: string, instanceId = 'default', configOverrides: KernelConfigOverrides = {}) {
|
|
112
140
|
this.repoRoot = repoRoot;
|
|
141
|
+
this.instanceId = instanceId;
|
|
142
|
+
this.configOverrides = configOverrides;
|
|
113
143
|
this.pathLayout = new AopPathLayout(repoRoot);
|
|
114
144
|
this.schemaRegistry = new SchemaRegistry(repoRoot);
|
|
115
145
|
this.loaded = false;
|
|
116
146
|
this.gatesConfig = {};
|
|
117
147
|
this.policy = {};
|
|
118
148
|
this.agentsConfig = {};
|
|
149
|
+
this.adaptersConfig = {};
|
|
119
150
|
this.toolRegistry = null;
|
|
120
151
|
this.toolHandlers = new ToolHandlerRegistry();
|
|
121
152
|
this.registerToolHandlers();
|
|
@@ -131,6 +162,8 @@ export class AopKernel {
|
|
|
131
162
|
this.qaIndexService = new QaIndexService(this);
|
|
132
163
|
this.mergeService = new MergeService(this);
|
|
133
164
|
this.featureDeletionService = new FeatureDeletionService(this);
|
|
165
|
+
this.costTrackingService = new CostTrackingService(this);
|
|
166
|
+
this.performanceAnalyticsService = new PerformanceAnalyticsService(this);
|
|
134
167
|
this.toolRouter = new ToolRouter(this.toolHandlers, (toolName) =>
|
|
135
168
|
Promise.resolve(
|
|
136
169
|
fail(ERROR_CODES.INVALID_ARGUMENT, `Unknown tool ${toolName}`, {
|
|
@@ -145,6 +178,10 @@ export class AopKernel {
|
|
|
145
178
|
return this.repoRoot;
|
|
146
179
|
}
|
|
147
180
|
|
|
181
|
+
setProvider(provider: WorkerProvider): void {
|
|
182
|
+
this.provider = provider;
|
|
183
|
+
}
|
|
184
|
+
|
|
148
185
|
getAgentsConfig(): AgentsConfigSnapshot {
|
|
149
186
|
return this.agentsConfig;
|
|
150
187
|
}
|
|
@@ -153,6 +190,10 @@ export class AopKernel {
|
|
|
153
190
|
return this.policy.rbac ?? {};
|
|
154
191
|
}
|
|
155
192
|
|
|
193
|
+
getAdaptersConfig(): Record<string, unknown> {
|
|
194
|
+
return this.adaptersConfig;
|
|
195
|
+
}
|
|
196
|
+
|
|
156
197
|
getPolicySnapshot(): AnyRecord {
|
|
157
198
|
return this.policy;
|
|
158
199
|
}
|
|
@@ -276,6 +317,7 @@ export class AopKernel {
|
|
|
276
317
|
locks: source.locks && typeof source.locks === 'object' ? source.locks : {},
|
|
277
318
|
lock_leases: source.lock_leases && typeof source.lock_leases === 'object' ? source.lock_leases : {},
|
|
278
319
|
blocked_queue: asArray(source.blocked_queue).filter((item) => item && typeof item === 'object'),
|
|
320
|
+
dep_blocked: asArray(source.dep_blocked).filter((item) => item && typeof item === 'object'),
|
|
279
321
|
updated_at: typeof source.updated_at === 'string' && source.updated_at ? source.updated_at : now,
|
|
280
322
|
runtime_sessions: this.normalizeRuntimeSessions(source.runtime_sessions, now)
|
|
281
323
|
};
|
|
@@ -292,9 +334,10 @@ export class AopKernel {
|
|
|
292
334
|
async load() {
|
|
293
335
|
await ensureAopRuntimeLayout(this.pathLayout);
|
|
294
336
|
|
|
295
|
-
const gatesPath = path.join(this.orchestratorDir, 'gates.yaml');
|
|
296
|
-
const policyPath = path.join(this.orchestratorDir, 'policy.yaml');
|
|
297
|
-
const agentsPath = path.join(this.orchestratorDir, 'agents.yaml');
|
|
337
|
+
const gatesPath = this.configOverrides.gatesPath ?? path.join(this.orchestratorDir, 'gates.yaml');
|
|
338
|
+
const policyPath = this.configOverrides.policyPath ?? path.join(this.orchestratorDir, 'policy.yaml');
|
|
339
|
+
const agentsPath = this.configOverrides.agentsPath ?? path.join(this.orchestratorDir, 'agents.yaml');
|
|
340
|
+
const adaptersPath = this.configOverrides.adaptersPath ?? path.join(this.orchestratorDir, 'adapters.yaml');
|
|
298
341
|
|
|
299
342
|
const gates = await loadAndValidateYaml(this.schemaRegistry, 'gates.schema.json', gatesPath);
|
|
300
343
|
if (!gates.validation.valid) {
|
|
@@ -326,9 +369,44 @@ export class AopKernel {
|
|
|
326
369
|
}
|
|
327
370
|
}
|
|
328
371
|
|
|
372
|
+
const adaptersExists = await pathExists(adaptersPath);
|
|
373
|
+
let adapters = { parsed: {}, validation: { valid: true, errors: [] as unknown[] } };
|
|
374
|
+
if (adaptersExists) {
|
|
375
|
+
adapters = await loadAndValidateYaml(this.schemaRegistry, 'adapters.schema.json', adaptersPath);
|
|
376
|
+
if (!adapters.validation.valid) {
|
|
377
|
+
throw new Error(`invalid_adapters_yaml:${JSON.stringify(adapters.validation.errors)}`);
|
|
378
|
+
}
|
|
379
|
+
const parsedAdapters = readObjectField(adapters, 'parsed');
|
|
380
|
+
const notificationChannel = readStringField(parsedAdapters, NOTIFICATION_CHANNEL_SLOT.name);
|
|
381
|
+
if (notificationChannel) {
|
|
382
|
+
try {
|
|
383
|
+
globalAdapterRegistry.resolve(NOTIFICATION_CHANNEL_SLOT, notificationChannel, {});
|
|
384
|
+
} catch {
|
|
385
|
+
throw new Error(`adapter_not_found:${NOTIFICATION_CHANNEL_SLOT.name}:${notificationChannel}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const activityDetector = readStringField(parsedAdapters, ACTIVITY_DETECTOR_SLOT.name);
|
|
389
|
+
if (activityDetector) {
|
|
390
|
+
try {
|
|
391
|
+
globalAdapterRegistry.resolve(ACTIVITY_DETECTOR_SLOT, activityDetector, {});
|
|
392
|
+
} catch {
|
|
393
|
+
throw new Error(`adapter_not_found:${ACTIVITY_DETECTOR_SLOT.name}:${activityDetector}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const scmProvider = readStringField(parsedAdapters, SCM_PROVIDER_SLOT.name);
|
|
397
|
+
if (scmProvider) {
|
|
398
|
+
try {
|
|
399
|
+
globalAdapterRegistry.resolve(SCM_PROVIDER_SLOT, scmProvider, {});
|
|
400
|
+
} catch {
|
|
401
|
+
throw new Error(`adapter_not_found:${SCM_PROVIDER_SLOT.name}:${scmProvider}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
329
406
|
this.gatesConfig = gates.parsed;
|
|
330
407
|
this.policy = parsedPolicy;
|
|
331
408
|
this.agentsConfig = agents.parsed as AgentsConfigSnapshot;
|
|
409
|
+
this.adaptersConfig = readObjectField(adapters, 'parsed');
|
|
332
410
|
const registryLoader = new ToolRegistryLoader(this.repoRoot);
|
|
333
411
|
this.toolRegistry = await registryLoader.load();
|
|
334
412
|
this.loaded = true;
|
|
@@ -530,6 +608,25 @@ export class AopKernel {
|
|
|
530
608
|
this.toolHandlers.register(TOOLS.REPORT_FEATURE_SUMMARY, async (args) =>
|
|
531
609
|
await this.reportFeatureSummary(readStringField(args, 'feature_id'))
|
|
532
610
|
);
|
|
611
|
+
this.toolHandlers.register(TOOLS.FEATURE_SEND_MESSAGE, async (args) =>
|
|
612
|
+
await this.featureSendMessage(readStringField(args, 'feature_id'), readStringField(args, 'message'))
|
|
613
|
+
);
|
|
614
|
+
this.toolHandlers.register(TOOLS.COST_RECORD, async (args) =>
|
|
615
|
+
await this.costRecord(
|
|
616
|
+
readStringField(args, 'feature_id'),
|
|
617
|
+
typeof args.tokens_used_delta === 'number' ? args.tokens_used_delta : 0,
|
|
618
|
+
typeof args.estimated_cost_usd_delta === 'number' ? args.estimated_cost_usd_delta : 0
|
|
619
|
+
)
|
|
620
|
+
);
|
|
621
|
+
this.toolHandlers.register(TOOLS.COST_GET, async (args) =>
|
|
622
|
+
await this.costGet(readStringField(args, 'feature_id'))
|
|
623
|
+
);
|
|
624
|
+
this.toolHandlers.register(TOOLS.PERFORMANCE_RECORD_OUTCOME, async (args) =>
|
|
625
|
+
await this.performanceRecordOutcome(args)
|
|
626
|
+
);
|
|
627
|
+
this.toolHandlers.register(TOOLS.PERFORMANCE_GET_ANALYTICS, async (args) =>
|
|
628
|
+
await this.performanceGetAnalytics(readStringField(args, 'provider'), readStringField(args, 'model'))
|
|
629
|
+
);
|
|
533
630
|
}
|
|
534
631
|
|
|
535
632
|
featurePath(featureId) {
|
|
@@ -548,6 +645,14 @@ export class AopKernel {
|
|
|
548
645
|
return this.pathLayout.planPath(featureId);
|
|
549
646
|
}
|
|
550
647
|
|
|
648
|
+
featureCostPath(featureId: string): string {
|
|
649
|
+
return this.pathLayout.featureCostPath(featureId);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
analyticsPath(): string {
|
|
653
|
+
return path.join(this.pathLayout.runtimeRoot, 'analytics', 'performance.json');
|
|
654
|
+
}
|
|
655
|
+
|
|
551
656
|
qaIndexPath(featureId) {
|
|
552
657
|
return this.pathLayout.qaIndexPath(featureId);
|
|
553
658
|
}
|
|
@@ -681,8 +786,27 @@ export class AopKernel {
|
|
|
681
786
|
}
|
|
682
787
|
|
|
683
788
|
async getRuntimeSessions(): Promise<RuntimeSessionsSnapshot> {
|
|
789
|
+
return this.normalizeRuntimeSessions(await this.readRunLease());
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async readRunLease(): Promise<RuntimeSessionsSnapshot> {
|
|
793
|
+
const runLeasePath = this.pathLayout.runLeaseFilePath(this.instanceId);
|
|
794
|
+
const existing = await readJson(runLeasePath, null);
|
|
795
|
+
if (existing !== null) {
|
|
796
|
+
return this.normalizeRuntimeSessions(existing);
|
|
797
|
+
}
|
|
798
|
+
// Migration: fall back to index.json runtime_sessions for existing repos
|
|
684
799
|
const index = await this.readIndex();
|
|
685
|
-
|
|
800
|
+
if (index.runtime_sessions && index.runtime_sessions.run_id !== 'none') {
|
|
801
|
+
return this.normalizeRuntimeSessions(index.runtime_sessions);
|
|
802
|
+
}
|
|
803
|
+
return this.emptyRuntimeSessions();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
async writeRunLease(data: RuntimeSessionsSnapshot): Promise<void> {
|
|
807
|
+
const runLeasePath = this.pathLayout.runLeaseFilePath(this.instanceId);
|
|
808
|
+
await ensureDir(path.dirname(runLeasePath));
|
|
809
|
+
await atomicWriteJson(runLeasePath, data);
|
|
686
810
|
}
|
|
687
811
|
|
|
688
812
|
async acquireRunLease(input: AcquireRunLeaseInput): Promise<{ data: { runtime_sessions: RuntimeSessionsSnapshot; took_over_stale: boolean; reused_existing_owner: boolean } }> {
|
|
@@ -846,6 +970,29 @@ export class AopKernel {
|
|
|
846
970
|
};
|
|
847
971
|
}
|
|
848
972
|
|
|
973
|
+
const worktreeConfig = this.policy.worktree as {
|
|
974
|
+
base_branch: string;
|
|
975
|
+
symlinks?: string[];
|
|
976
|
+
post_create?: string[];
|
|
977
|
+
} | undefined;
|
|
978
|
+
const hookWarnings: WorkspaceHookWarning[] = [];
|
|
979
|
+
const collectHookWarning = (warning: WorkspaceHookWarning) => {
|
|
980
|
+
hookWarnings.push(warning);
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
if (worktreeConfig?.symlinks?.length) {
|
|
984
|
+
await applyWorktreeSymlinks(this.repoRoot, worktree, worktreeConfig.symlinks, collectHookWarning);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (worktreeConfig?.post_create?.length) {
|
|
988
|
+
await runWorktreePostCreate(worktree, worktreeConfig.post_create, collectHookWarning);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
for (const warning of hookWarnings) {
|
|
992
|
+
// Preserve non-fatal behavior while making hook failures observable.
|
|
993
|
+
console.warn(`[aop] workspace hook warning: ${formatWorkspaceHookWarning(warning)}`);
|
|
994
|
+
}
|
|
995
|
+
|
|
849
996
|
return {
|
|
850
997
|
data: {
|
|
851
998
|
feature_id: featureId,
|
|
@@ -1059,6 +1206,134 @@ export class AopKernel {
|
|
|
1059
1206
|
async recoverFromState() {
|
|
1060
1207
|
return await this.lockService.recoverFromState();
|
|
1061
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
private async waitForSessionToBecomeActive(sessionId: string): Promise<void> {
|
|
1211
|
+
if (!this.provider?.getSessionInfo) {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const timeoutMs = 5000;
|
|
1216
|
+
const pollIntervalMs = 250;
|
|
1217
|
+
const deadline = Date.now() + timeoutMs;
|
|
1218
|
+
|
|
1219
|
+
while (Date.now() <= deadline) {
|
|
1220
|
+
const sessionInfo = await this.provider.getSessionInfo(sessionId).catch(() => null);
|
|
1221
|
+
if (sessionInfo?.active) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
await new Promise<void>((resolve) => {
|
|
1225
|
+
setTimeout(resolve, pollIntervalMs);
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
async featureSendMessage(
|
|
1231
|
+
featureId: string | null,
|
|
1232
|
+
message: string | null
|
|
1233
|
+
): Promise<unknown> {
|
|
1234
|
+
if (!featureId) {
|
|
1235
|
+
throw { normalizedResponse: fail(ERROR_CODES.INVALID_ARGUMENT, 'feature_id is required', { retryable: false, requires_human: false }) };
|
|
1236
|
+
}
|
|
1237
|
+
if (!message) {
|
|
1238
|
+
throw { normalizedResponse: fail(ERROR_CODES.INVALID_ARGUMENT, 'message is required and must not be empty', { retryable: false, requires_human: false }) };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
const runtimeSessions = await this.getRuntimeSessions();
|
|
1242
|
+
const featureSession = runtimeSessions.feature_sessions?.[featureId];
|
|
1243
|
+
if (!featureSession) {
|
|
1244
|
+
throw { normalizedResponse: { ok: false, error: { code: 'session_not_found', message: 'No active session cluster for feature' } } };
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
const state = await this.readState(featureId);
|
|
1248
|
+
const status = typeof state.frontMatter.status === 'string' ? state.frontMatter.status : '';
|
|
1249
|
+
const gates = readObjectField(state.frontMatter, 'gates');
|
|
1250
|
+
|
|
1251
|
+
let targetRole = 'orchestrator';
|
|
1252
|
+
let targetSessionId = runtimeSessions.orchestrator_session_id;
|
|
1253
|
+
|
|
1254
|
+
if (status === STATUS.PLANNING) {
|
|
1255
|
+
targetRole = 'planner';
|
|
1256
|
+
targetSessionId = featureSession.planner_session_id;
|
|
1257
|
+
} else if (status === STATUS.BUILDING) {
|
|
1258
|
+
targetRole = 'builder';
|
|
1259
|
+
targetSessionId = featureSession.builder_session_id;
|
|
1260
|
+
} else if (status === STATUS.QA || status === STATUS.READY_TO_MERGE) {
|
|
1261
|
+
targetRole = 'qa';
|
|
1262
|
+
targetSessionId = featureSession.qa_session_id;
|
|
1263
|
+
} else if (status === STATUS.BLOCKED) {
|
|
1264
|
+
const fastGate = readStringField(gates, 'fast');
|
|
1265
|
+
const fullGate = readStringField(gates, 'full');
|
|
1266
|
+
if (fastGate === GATE_RESULT.FAIL && fullGate !== GATE_RESULT.FAIL) {
|
|
1267
|
+
targetRole = 'builder';
|
|
1268
|
+
targetSessionId = featureSession.builder_session_id;
|
|
1269
|
+
} else {
|
|
1270
|
+
targetRole = 'qa';
|
|
1271
|
+
targetSessionId = featureSession.qa_session_id;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
if (!targetSessionId || targetSessionId === 'unassigned' || targetSessionId === 'unknown') {
|
|
1276
|
+
targetRole = 'orchestrator';
|
|
1277
|
+
targetSessionId = runtimeSessions.orchestrator_session_id;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
if (!this.provider?.sendMessage) {
|
|
1281
|
+
throw { normalizedResponse: { ok: false, error: { code: 'provider_unsupported', message: 'Provider does not support sendMessage' } } };
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
await this.waitForSessionToBecomeActive(targetSessionId);
|
|
1285
|
+
await this.provider.sendMessage(targetSessionId, message);
|
|
1286
|
+
return {
|
|
1287
|
+
feature_id: featureId,
|
|
1288
|
+
session_id: targetSessionId,
|
|
1289
|
+
target_role: targetRole,
|
|
1290
|
+
status,
|
|
1291
|
+
delivered: true
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
async costRecord(featureId: string, tokensDelta: number, costUsdDelta: number) {
|
|
1296
|
+
const updated = await this.costTrackingService.recordCost(featureId, tokensDelta, costUsdDelta);
|
|
1297
|
+
return { ok: true as const, data: updated };
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
async costGet(featureId: string) {
|
|
1301
|
+
const cost = await this.costTrackingService.getFeatureCost(featureId);
|
|
1302
|
+
return { ok: true as const, data: cost };
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
async checkBudget(featureId: string) {
|
|
1306
|
+
return await this.costTrackingService.checkBudget(featureId);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
async performanceRecordOutcome(args: AnyRecord) {
|
|
1310
|
+
const outcome: FeatureOutcome = {
|
|
1311
|
+
feature_id: typeof args.feature_id === 'string' ? args.feature_id : '',
|
|
1312
|
+
provider: typeof args.provider === 'string' ? args.provider : '',
|
|
1313
|
+
model: typeof args.model === 'string' ? args.model : '',
|
|
1314
|
+
status: typeof args.status === 'string' ? args.status : '',
|
|
1315
|
+
gate_pass: typeof args.gate_pass === 'boolean' ? args.gate_pass : false,
|
|
1316
|
+
retry_count: typeof args.retry_count === 'number' ? args.retry_count : 0,
|
|
1317
|
+
duration_ms: typeof args.duration_ms === 'number' ? args.duration_ms : 0,
|
|
1318
|
+
cost_usd: typeof args.cost_usd === 'number' ? args.cost_usd : 0,
|
|
1319
|
+
recorded_at: nowIso()
|
|
1320
|
+
};
|
|
1321
|
+
const snapshot = await this.performanceAnalyticsService.recordOutcome(outcome);
|
|
1322
|
+
return { ok: true as const, data: { total_outcomes: snapshot.outcomes.length } };
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
async performanceGetAnalytics(provider: string | null, model: string | null) {
|
|
1326
|
+
if (provider || model) {
|
|
1327
|
+
const stats = await this.performanceAnalyticsService.getProviderStats(
|
|
1328
|
+
provider ?? undefined,
|
|
1329
|
+
model ?? undefined
|
|
1330
|
+
);
|
|
1331
|
+
const snapshot = await this.performanceAnalyticsService.getAnalytics();
|
|
1332
|
+
return { ok: true as const, data: { outcomes: snapshot.outcomes, aggregates: stats, generated_at: snapshot.generated_at } };
|
|
1333
|
+
}
|
|
1334
|
+
const snapshot = await this.performanceAnalyticsService.getAnalytics();
|
|
1335
|
+
return { ok: true as const, data: snapshot };
|
|
1336
|
+
}
|
|
1062
1337
|
}
|
|
1063
1338
|
|
|
1064
1339
|
function normalizeRepoPathForState(repoRoot: string, absolutePath: string) {
|
|
@@ -29,6 +29,14 @@ export class AopPathLayout {
|
|
|
29
29
|
return path.join(this.runtimeDataRoot, 'operation-ledger');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
runLeaseFilePath(instanceId: string): string {
|
|
33
|
+
return path.join(this.runtimeDataRoot, instanceId, 'run-lease.json');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
runLeaseLockPath(instanceId: string): string {
|
|
37
|
+
return path.join(this.runtimeDataRoot, instanceId, '.run-lease.lock');
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
get legacyFeaturesRoot(): string {
|
|
33
41
|
return path.join(this.repoRoot, 'agentic', 'features');
|
|
34
42
|
}
|
|
@@ -61,6 +69,10 @@ export class AopPathLayout {
|
|
|
61
69
|
return path.join(this.featureRoot(featureId), 'plan.json');
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
featureCostPath(featureId: string): string {
|
|
73
|
+
return path.join(this.featureRoot(featureId), 'cost.json');
|
|
74
|
+
}
|
|
75
|
+
|
|
64
76
|
qaIndexPath(featureId: string): string {
|
|
65
77
|
return path.join(this.featureRoot(featureId), 'qa_test_index.json');
|
|
66
78
|
}
|