agentic-orchestrator 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/AGENTS.md +139 -0
  2. package/CLAUDE.md +12 -0
  3. package/agentic/orchestrator/agents.yaml +3 -0
  4. package/agentic/orchestrator/defaults/policy.defaults.yaml +3 -0
  5. package/agentic/orchestrator/policy.yaml +3 -0
  6. package/agentic/orchestrator/schemas/agents.schema.json +15 -0
  7. package/agentic/orchestrator/schemas/policy.schema.json +14 -0
  8. package/apps/control-plane/src/cli/cli-argument-parser.ts +7 -0
  9. package/apps/control-plane/src/cli/help-command-handler.ts +8 -0
  10. package/apps/control-plane/src/cli/init-command-handler.ts +6 -0
  11. package/apps/control-plane/src/cli/resume-command-handler.ts +31 -2
  12. package/apps/control-plane/src/cli/run-command-handler.ts +31 -3
  13. package/apps/control-plane/src/cli/types.ts +1 -0
  14. package/apps/control-plane/src/core/error-codes.ts +4 -0
  15. package/apps/control-plane/src/core/kernel.ts +3 -0
  16. package/apps/control-plane/src/index.ts +14 -0
  17. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +25 -3
  18. package/apps/control-plane/src/providers/api-worker-provider.ts +115 -0
  19. package/apps/control-plane/src/providers/cli-worker-provider.ts +385 -0
  20. package/apps/control-plane/src/providers/output-parsers/generic-output-parser.ts +100 -0
  21. package/apps/control-plane/src/providers/output-parsers/index.ts +11 -0
  22. package/apps/control-plane/src/providers/output-parsers/types.ts +23 -0
  23. package/apps/control-plane/src/providers/providers.ts +19 -0
  24. package/apps/control-plane/src/providers/worker-provider-factory.ts +198 -0
  25. package/apps/control-plane/src/supervisor/build-wave-executor.ts +140 -3
  26. package/apps/control-plane/src/supervisor/planning-wave-executor.ts +125 -5
  27. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +144 -2
  28. package/apps/control-plane/src/supervisor/runtime.ts +24 -0
  29. package/apps/control-plane/src/supervisor/worker-decision-loop.ts +134 -12
  30. package/apps/control-plane/test/cli.unit.spec.ts +36 -0
  31. package/apps/control-plane/test/dashboard-api.integration.spec.ts +2 -2
  32. package/apps/control-plane/test/resume-command.spec.ts +31 -1
  33. package/apps/control-plane/test/worker-decision-loop.spec.ts +3 -0
  34. package/apps/control-plane/test/worker-execution-policy.spec.ts +284 -0
  35. package/apps/control-plane/test/worker-provider-adapters.spec.ts +440 -0
  36. package/apps/control-plane/test/worker-provider-factory.spec.ts +151 -0
  37. package/config/agentic/orchestrator/agents.yaml +3 -0
  38. package/dist/apps/control-plane/cli/cli-argument-parser.js +7 -0
  39. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
  40. package/dist/apps/control-plane/cli/help-command-handler.js +8 -0
  41. package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
  42. package/dist/apps/control-plane/cli/init-command-handler.js +6 -0
  43. package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
  44. package/dist/apps/control-plane/cli/resume-command-handler.d.ts +3 -0
  45. package/dist/apps/control-plane/cli/resume-command-handler.js +18 -2
  46. package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
  47. package/dist/apps/control-plane/cli/run-command-handler.d.ts +3 -1
  48. package/dist/apps/control-plane/cli/run-command-handler.js +17 -3
  49. package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
  50. package/dist/apps/control-plane/cli/types.d.ts +1 -0
  51. package/dist/apps/control-plane/core/error-codes.d.ts +4 -0
  52. package/dist/apps/control-plane/core/error-codes.js +4 -0
  53. package/dist/apps/control-plane/core/error-codes.js.map +1 -1
  54. package/dist/apps/control-plane/core/kernel.d.ts +3 -0
  55. package/dist/apps/control-plane/core/kernel.js.map +1 -1
  56. package/dist/apps/control-plane/index.d.ts +2 -0
  57. package/dist/apps/control-plane/index.js +1 -0
  58. package/dist/apps/control-plane/index.js.map +1 -1
  59. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +14 -2
  60. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
  61. package/dist/apps/control-plane/providers/api-worker-provider.d.ts +31 -0
  62. package/dist/apps/control-plane/providers/api-worker-provider.js +73 -0
  63. package/dist/apps/control-plane/providers/api-worker-provider.js.map +1 -0
  64. package/dist/apps/control-plane/providers/cli-worker-provider.d.ts +46 -0
  65. package/dist/apps/control-plane/providers/cli-worker-provider.js +274 -0
  66. package/dist/apps/control-plane/providers/cli-worker-provider.js.map +1 -0
  67. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.d.ts +10 -0
  68. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.js +79 -0
  69. package/dist/apps/control-plane/providers/output-parsers/generic-output-parser.js.map +1 -0
  70. package/dist/apps/control-plane/providers/output-parsers/index.d.ts +2 -0
  71. package/dist/apps/control-plane/providers/output-parsers/index.js +2 -0
  72. package/dist/apps/control-plane/providers/output-parsers/index.js.map +1 -0
  73. package/dist/apps/control-plane/providers/output-parsers/types.d.ts +21 -0
  74. package/dist/apps/control-plane/providers/output-parsers/types.js +2 -0
  75. package/dist/apps/control-plane/providers/output-parsers/types.js.map +1 -0
  76. package/dist/apps/control-plane/providers/providers.d.ts +4 -0
  77. package/dist/apps/control-plane/providers/providers.js +15 -0
  78. package/dist/apps/control-plane/providers/providers.js.map +1 -1
  79. package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +41 -0
  80. package/dist/apps/control-plane/providers/worker-provider-factory.js +104 -0
  81. package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -0
  82. package/dist/apps/control-plane/supervisor/build-wave-executor.d.ts +13 -0
  83. package/dist/apps/control-plane/supervisor/build-wave-executor.js +92 -3
  84. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
  85. package/dist/apps/control-plane/supervisor/planning-wave-executor.d.ts +12 -0
  86. package/dist/apps/control-plane/supervisor/planning-wave-executor.js +83 -5
  87. package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
  88. package/dist/apps/control-plane/supervisor/qa-wave-executor.d.ts +13 -0
  89. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +91 -2
  90. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
  91. package/dist/apps/control-plane/supervisor/runtime.js +19 -0
  92. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
  93. package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +10 -0
  94. package/dist/apps/control-plane/supervisor/worker-decision-loop.js +113 -12
  95. package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
  96. package/package.json +2 -2
  97. package/packages/web-dashboard/next-env.d.ts +2 -1
  98. package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +4 -3
  99. package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +6 -2
  100. package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +6 -5
  101. package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +5 -4
  102. package/packages/web-dashboard/src/app/api/features/[id]/route.ts +7 -3
  103. package/packages/web-dashboard/src/lib/aop-client.ts +2 -2
  104. package/packages/web-dashboard/src/lib/orchestrator-tools.ts +1 -1
  105. package/packages/web-dashboard/tsconfig.json +1 -0
  106. package/spec-files/outstanding/agentic_orchestrator_human_input_interaction_protocol_spec.md +590 -0
  107. package/spec-files/outstanding/agentic_orchestrator_real_worker_provider_execution_spec.md +616 -0
  108. package/spec-files/progress.md +91 -0
package/AGENTS.md ADDED
@@ -0,0 +1,139 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to coding agents operating in this repository.
4
+
5
+ ## Build & Development Commands
6
+
7
+ ```bash
8
+ npm run build # Lint + TypeScript compile
9
+ npm run typecheck # tsc --noEmit (type-check only)
10
+ npm run lint # ESLint with --max-warnings 0
11
+ npm run lint:fix # ESLint with auto-fix
12
+ npm run test # Vitest with coverage (via Nx)
13
+ npm run run # Run CLI: tsx apps/control-plane/src/cli/aop.ts
14
+
15
+ # Run a single test file
16
+ npx vitest run apps/control-plane/test/<file>.spec.ts
17
+
18
+ # Run tests matching a pattern
19
+ npx vitest run -t "pattern"
20
+
21
+ # Validation scripts (also run in CI)
22
+ npm run validate:mcp-contracts
23
+ npm run validate:architecture
24
+ npm run validate:docker-mcp
25
+ ```
26
+
27
+ ## Architecture
28
+
29
+ This is an MCP-first, multi-agent orchestrator control plane. The codebase lives in an Nx monorepo with a single application at `apps/control-plane/`.
30
+
31
+ ### Layer Overview
32
+
33
+ **CLI** (`apps/control-plane/src/cli/`) -> **Supervisor** (`supervisor/`) -> **Kernel** (`core/kernel.ts`) -> **Services** (`application/services/`)
34
+
35
+ - **CLI**: Entry point at `cli/aop.ts` -> delegates to `interfaces/cli/bootstrap.ts`. Commands: `run`, `status`, `resume`, `stop`, `delete`, `cleanup`, `init`, `dashboard`, `retry`, `send`, `attach`, `help`. All responses are structured JSON with `{ ok, data/error }`. Run `aop --help` or `aop help <command>` for usage details.
36
+ - **Kernel** (`core/kernel.ts`, ~900 lines): Deterministic orchestration engine. Manages schema validation, state, locks, plans, patches, gates, merges, and reporting. Delegates to 12+ domain services via port interfaces.
37
+ - **Supervisor** (`supervisor/runtime.ts`): Multi-agent runtime. Resolves providers (codex, claude, gemini, etc.), loads role-specific prompts, orchestrates planning/build/QA waves, and manages lease heartbeats.
38
+ - **MCP Tool Runtime** (`mcp/tool-runtime.ts`): Tool execution pipeline with registry-based dispatch, input/output schema validation, role-based authorization, and idempotency tracking via `operation_id`.
39
+ - **Application Services** (`application/services/`): 12+ focused services (plan, patch, gates, QA, locks, merge, deletion, etc.) implementing port interfaces consumed by the kernel.
40
+
41
+ ### Configuration Layer (`config/agentic/orchestrator/`)
42
+
43
+ - `policy.yaml` - Commit/patch/merge policies, lock TTL, RBAC, supervisor iteration limits
44
+ - `gates.yaml` - Gate profiles (fast/full/merge) with commands and coverage thresholds
45
+ - `agents.yaml` - Agent roles (planner/builder/qa), provider defaults, prompt paths
46
+ - `prompts/` - Role-specific system prompts
47
+ - `tools/` - MCP tool contracts: `catalog.json` (33 tools), per-tool `schemas/input/` and `schemas/output/`
48
+ - `schemas/` - JSON schemas for policy, state, plan, index, gates, agents, qa_test_index
49
+
50
+ ### Runtime Artifacts (`.aop/`)
51
+
52
+ - `.aop/features/index.json` - Global feature index (active/blocked/merged features, locks, sessions)
53
+ - `.aop/features/<id>/state.md` - Per-feature state (YAML frontmatter + markdown)
54
+ - `.aop/features/<id>/plan.json` - Versioned plan with optimistic concurrency
55
+ - `.aop/features/<id>/evidence/` - Gate evidence artifacts
56
+
57
+ ## Key Patterns
58
+
59
+ - **Port-based DI**: Services define interface ports; the kernel implements them. This decouples domain logic from infrastructure.
60
+ - **Schema-driven validation**: All configs and artifacts validated via AJV against JSON schemas. Validation failures return structured errors with retry hints.
61
+ - **Atomic state management**: File locks, version guards, and optimistic concurrency for all state mutations.
62
+ - **Structured error codes**: 45+ error codes with `{ code, message, details: { retryable, requires_human, suggestions } }`.
63
+ - **Tool registry dispatch**: `catalog.json` metadata drives tool routing, replacing switch-based dispatch. Mutating tools require `operation_id` for idempotency.
64
+
65
+ ## Design Principles
66
+
67
+ - **KISS (Keep It Simple, Stupid)**: Prefer the simplest design that satisfies requirements and preserves deterministic behavior.
68
+ - **SRP (Single Responsibility Principle)**: Each class/module should have one clear reason to change.
69
+ - **SOLID (pragmatic application)**:
70
+ - Open/Closed: Extend by adding adapters/services instead of modifying unrelated orchestrator flow.
71
+ - Interface Segregation: Keep interfaces focused (avoid broad "do everything" contracts).
72
+ - Dependency Inversion: Depend on ports/interfaces, not concrete infrastructure.
73
+ - **Hexagonal Architecture**: Domain/application layers must not depend directly on provider CLI/SDK details; keep those in adapter boundaries.
74
+ - **Composition over Inheritance**: Build behavior through composed collaborators (factory + adapter + parser + service) rather than deep class hierarchies.
75
+ - **High Cohesion, Low Coupling**: Keep CLI parsing, provider execution, orchestration logic, and persistence concerns in separate modules.
76
+
77
+ ## Lint Rules to Know
78
+
79
+ - `no-explicit-any` is **enforced** in source, relaxed in tests
80
+ - `no-floating-promises` and `no-misused-promises` are errors
81
+ - `require-await` is enforced in source, relaxed in tests
82
+ - `consistent-type-imports` is required (use `import type` for type-only imports)
83
+ - `no-console` is an error in source (except `warn`/`error`); allowed in scripts and CLI entry point
84
+ - Unused variables must be prefixed with `_`
85
+ - Zero warnings allowed (`--max-warnings 0`)
86
+
87
+ ## Adding a New CLI Command or Flag
88
+
89
+ When adding a new command or flag to the CLI, update **all** of the following in the same change:
90
+
91
+ 1. **`apps/control-plane/src/cli/types.ts`** - add the command to the `CliCommand` enum.
92
+ 2. **`apps/control-plane/src/cli/cli-argument-parser.ts`** - parse the new flag/command token.
93
+ 3. **`apps/control-plane/src/interfaces/cli/bootstrap.ts`** - dispatch the new command.
94
+ 4. **`apps/control-plane/src/cli/help-command-handler.ts`** - add or update the entry in `COMMAND_HELP` (both the command summary and its flags list). This is the source of truth for `aop --help` output.
95
+ 5. **`CLAUDE.md` and `AGENTS.md`** - update command lists/documentation to keep both assistant guides aligned.
96
+
97
+ ## Coverage Thresholds
98
+
99
+ Lines: 90% | Branches: 90% | Functions: 90% | Statements: 90%
100
+
101
+ ## Updating progress.md
102
+
103
+ `spec-files/progress.md` is the versioned work log for this repository. **After completing any meaningful code change** (new feature, bug fix, refactor, test coverage improvement, validator change, etc.), append a new numbered entry to the **Completed Tasks** section of `progress.md`.
104
+
105
+ ### Entry format
106
+
107
+ Each entry must include:
108
+
109
+ - A sequential number (increment from the last entry)
110
+ - A short title describing what was done
111
+ - **Goal** - one sentence on why the change was made
112
+ - **Changes made** - bullet list of files modified and what changed; be specific (function names, test counts, metric deltas)
113
+ - **Result** - outcome verification (e.g. `npm test [PASS]`, `npm run lint [PASS]`, validator output)
114
+
115
+ ```text
116
+ Entry N - Short Title
117
+ - **Goal:** One-sentence description of the problem or objective.
118
+ - **Changes made:**
119
+ - `path/to/file.ts` - description of what changed and why.
120
+ - `apps/control-plane/test/foo.spec.ts` - X tests added covering Y branches.
121
+ - **Result:** `npm test` [PASS] (N test files / M tests passing). `npm run lint` [PASS].
122
+ ```
123
+
124
+ ### Rules
125
+
126
+ - **One entry per logical unit of work.** Don't bundle unrelated changes into one entry.
127
+ - **Do not edit or renumber past entries.** The log is append-only.
128
+ - Entries go in the **Completed Tasks** section (before the `## Next Tasks:` heading).
129
+ - The `## Next Tasks:` section lists only work that has **not yet been done**. Move items out of it once completed.
130
+ - After completing work from a spec file, update the **Spec Gap Tracker** table (if present) to mark the item as complete.
131
+
132
+ ## Executing a Spec
133
+
134
+ When beginning work from a spec file (for example `spec-files/agentic_orchestrator_*.md`):
135
+
136
+ 1. **Before writing any code**, read the spec and add all planned tasks to the `## Next Tasks:` section of `progress.md`. Use the spec's section numbers as identifiers (for example `Section 3.1 - Add mutating cross-check`).
137
+ 2. **As each task completes**, move it out of `## Next Tasks:` and into a new numbered `Entry N - ...` in the Completed Tasks section.
138
+ 3. **Never leave a completed task in `## Next Tasks:`**. The section must only contain work that hasn't started or isn't finished.
139
+ 4. If a task is blocked or deferred, leave it in `## Next Tasks:` with a `[BLOCKED]` or `[DEFERRED]` annotation and a one-line reason.
package/CLAUDE.md CHANGED
@@ -62,6 +62,18 @@ This is an MCP-first, multi-agent orchestrator control plane. The codebase lives
62
62
  - **Structured error codes**: 45+ error codes with `{ code, message, details: { retryable, requires_human, suggestions } }`.
63
63
  - **Tool registry dispatch**: `catalog.json` metadata drives tool routing, replacing switch-based dispatch. Mutating tools require `operation_id` for idempotency.
64
64
 
65
+ ## Design Principles
66
+
67
+ - **KISS (Keep It Simple, Stupid)**: Prefer the simplest design that satisfies requirements and preserves deterministic behavior.
68
+ - **SRP (Single Responsibility Principle)**: Each class/module should have one clear reason to change.
69
+ - **SOLID (pragmatic application)**:
70
+ - Open/Closed: Extend by adding adapters/services instead of modifying unrelated orchestrator flow.
71
+ - Interface Segregation: Keep interfaces focused (avoid broad "do everything" contracts).
72
+ - Dependency Inversion: Depend on ports/interfaces, not concrete infrastructure.
73
+ - **Hexagonal Architecture**: Domain/application layers must not depend directly on provider CLI/SDK details; keep those in adapter boundaries.
74
+ - **Composition over Inheritance**: Build behavior through composed collaborators (factory + adapter + parser + service) rather than deep class hierarchies.
75
+ - **High Cohesion, Low Coupling**: Keep CLI parsing, provider execution, orchestration logic, and persistence concerns in separate modules.
76
+
65
77
  ## Lint Rules to Know
66
78
 
67
79
  - `no-explicit-any` is **enforced** in source, relaxed in tests
@@ -10,6 +10,9 @@ missing_prompt_behavior: ignore
10
10
  runtime:
11
11
  default_provider: custom
12
12
  default_model: local-default
13
+ worker_provider_mode: live
14
+ worker_response_timeout_ms: 120000
15
+ max_consecutive_no_progress_iterations: 2
13
16
  provider_config_env: AOP_PROVIDER_CONFIG_ENV
14
17
  provider_configs:
15
18
  kiro-cli:
@@ -48,6 +48,9 @@ path_rules:
48
48
  allow_symlink_traversal: false
49
49
  execution:
50
50
  default_step_timeout_seconds: 600
51
+ require_live_provider_for_run: true
52
+ malformed_worker_output_action: block_feature
53
+ no_progress_action: block_feature
51
54
  retry_policy:
52
55
  transient_max_retries: 1
53
56
  transient_error_codes:
@@ -48,6 +48,9 @@ path_rules:
48
48
  allow_symlink_traversal: false
49
49
  execution:
50
50
  default_step_timeout_seconds: 600
51
+ require_live_provider_for_run: true
52
+ malformed_worker_output_action: block_feature
53
+ no_progress_action: block_feature
51
54
  retry_policy:
52
55
  transient_max_retries: 1
53
56
  transient_error_codes:
@@ -48,6 +48,21 @@
48
48
  "type": "string",
49
49
  "description": "Default model string passed to the provider SDK. Overridden by --agent-model CLI flag or AOP_AGENT_MODEL env var."
50
50
  },
51
+ "worker_provider_mode": {
52
+ "type": "string",
53
+ "description": "Execution mode for worker providers. 'live' uses provider-backed execution, 'stub' uses NullWorkerProvider.",
54
+ "enum": ["live", "stub"]
55
+ },
56
+ "worker_response_timeout_ms": {
57
+ "type": "number",
58
+ "description": "Timeout in milliseconds for a single worker execution request.",
59
+ "minimum": 1
60
+ },
61
+ "max_consecutive_no_progress_iterations": {
62
+ "type": "number",
63
+ "description": "Consecutive no-progress worker iterations allowed before no-progress policy action is triggered.",
64
+ "minimum": 1
65
+ },
51
66
  "default_agent_config": {
52
67
  "type": "object",
53
68
  "description": "Default agent configuration object forwarded to the provider SDK when no provider-specific entry exists in provider_configs."
@@ -206,6 +206,20 @@
206
206
  "minimum": 1,
207
207
  "description": "Default execution timeout applied to each gate step that does not declare its own timeout_seconds."
208
208
  },
209
+ "require_live_provider_for_run": {
210
+ "type": "boolean",
211
+ "description": "When true, run/resume reject stub worker providers and require live provider execution."
212
+ },
213
+ "malformed_worker_output_action": {
214
+ "type": "string",
215
+ "description": "Action taken when worker output cannot be parsed or contains unsupported output types.",
216
+ "enum": ["block_feature", "fail_run"]
217
+ },
218
+ "no_progress_action": {
219
+ "type": "string",
220
+ "description": "Action taken when a live worker repeatedly produces no actionable outputs.",
221
+ "enum": ["block_feature", "fail_run"]
222
+ },
209
223
  "retry_policy": {
210
224
  "type": "object",
211
225
  "description": "Governs automatic retries for transient gate execution failures.",
@@ -99,6 +99,13 @@ export class CliArgumentParser {
99
99
  index += 1;
100
100
  continue;
101
101
  }
102
+ if (token === '--worker-provider-mode') {
103
+ if (next === 'live' || next === 'stub') {
104
+ options.worker_provider_mode = next;
105
+ index += 1;
106
+ }
107
+ continue;
108
+ }
102
109
  if (token === '--transport') {
103
110
  options.transport = next;
104
111
  index += 1;
@@ -27,6 +27,10 @@ const COMMAND_HELP: Record<CliCommand, CommandHelp> = {
27
27
  flag: '--provider-config-env <var>',
28
28
  description: 'Env var name used for API-backed provider auth/config',
29
29
  },
30
+ {
31
+ flag: '--worker-provider-mode <live|stub>',
32
+ description: 'Worker execution mode override (default: live for run/resume)',
33
+ },
30
34
  { flag: '--transport <inprocess|mcp>', description: 'Tool transport layer (default: mcp)' },
31
35
  { flag: '--takeover-stale-run', description: 'Take over a stale run lease' },
32
36
  { flag: '--project <name>', description: 'Select project from multi-project.yaml' },
@@ -47,6 +51,10 @@ const COMMAND_HELP: Record<CliCommand, CommandHelp> = {
47
51
  flags: [
48
52
  { flag: '--feature-id <id>', description: 'Resume a specific feature' },
49
53
  { flag: '--force', description: 'Force-resume even if the feature is not blocked' },
54
+ {
55
+ flag: '--worker-provider-mode <live|stub>',
56
+ description: 'Worker execution mode override (default: live)',
57
+ },
50
58
  { flag: '--transport <inprocess|mcp>', description: 'Tool transport layer (default: mcp)' },
51
59
  ],
52
60
  },
@@ -259,6 +259,9 @@ path_rules:
259
259
  allow_symlink_traversal: false
260
260
  execution:
261
261
  default_step_timeout_seconds: 600
262
+ require_live_provider_for_run: true
263
+ malformed_worker_output_action: block_feature
264
+ no_progress_action: block_feature
262
265
  retry_policy:
263
266
  transient_max_retries: 1
264
267
  transient_error_codes:
@@ -420,6 +423,9 @@ missing_prompt_behavior: ignore
420
423
  runtime:
421
424
  default_provider: ${wizard.defaultProvider}
422
425
  default_model: ${wizard.defaultModel}
426
+ worker_provider_mode: live
427
+ worker_response_timeout_ms: 120000
428
+ max_consecutive_no_progress_iterations: 2
423
429
  ${providerConfigEnvLine} role_provider_overrides: {}
424
430
  `;
425
431
  }
@@ -1,5 +1,12 @@
1
1
  import { STATUS } from '../core/constants.js';
2
- import { resolveProviderSelection, NullWorkerProvider } from '../providers/providers.js';
2
+ import { resolveProviderSelection } from '../providers/providers.js';
3
+ import {
4
+ DefaultWorkerProviderFactory,
5
+ resolveWorkerProviderMode,
6
+ resolveWorkerProviderPolicy,
7
+ resolveWorkerProviderRuntime,
8
+ type WorkerProviderFactory,
9
+ } from '../providers/worker-provider-factory.js';
3
10
  import { SupervisorRuntime } from '../supervisor/runtime.js';
4
11
  import type { AopKernel } from '../core/kernel.js';
5
12
  import type { ToolClient } from '../mcp/tool-client.js';
@@ -84,6 +91,12 @@ interface ResumePlan {
84
91
  }
85
92
 
86
93
  export class ResumeCommandHandler {
94
+ private readonly workerProviderFactory: WorkerProviderFactory;
95
+
96
+ constructor(workerProviderFactory: WorkerProviderFactory = new DefaultWorkerProviderFactory()) {
97
+ this.workerProviderFactory = workerProviderFactory;
98
+ }
99
+
87
100
  async execute(context: ResumeCommandContext): Promise<unknown> {
88
101
  const { env, runId, transport, options, kernel, toolClient } = context;
89
102
  const recovery = await kernel.recoverFromState();
@@ -108,7 +121,23 @@ export class ResumeCommandHandler {
108
121
  agentsConfig: kernel.getAgentsConfig(),
109
122
  });
110
123
 
111
- const provider = new NullWorkerProvider(selection);
124
+ const runtimeConfig = resolveWorkerProviderRuntime(kernel.getAgentsConfig().runtime);
125
+ const policySnapshot = kernel.getPolicySnapshot();
126
+ const executionPolicy =
127
+ policySnapshot.execution && typeof policySnapshot.execution === 'object'
128
+ ? (policySnapshot.execution as Record<string, unknown>)
129
+ : null;
130
+ const provider = this.workerProviderFactory.create({
131
+ selection,
132
+ mode: resolveWorkerProviderMode(
133
+ options.worker_provider_mode,
134
+ kernel.getAgentsConfig().runtime?.worker_provider_mode,
135
+ 'live',
136
+ ),
137
+ context: 'resume',
138
+ policy: resolveWorkerProviderPolicy(executionPolicy),
139
+ runtime: runtimeConfig,
140
+ });
112
141
  const supervisor = new SupervisorRuntime(kernel, provider, toolClient, {
113
142
  max_active_features: 5,
114
143
  max_parallel_gate_runs: 3,
@@ -1,6 +1,13 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { resolveProviderSelection, NullWorkerProvider } from '../providers/providers.js';
3
+ import { resolveProviderSelection } from '../providers/providers.js';
4
+ import {
5
+ DefaultWorkerProviderFactory,
6
+ resolveWorkerProviderMode,
7
+ resolveWorkerProviderPolicy,
8
+ resolveWorkerProviderRuntime,
9
+ type WorkerProviderFactory,
10
+ } from '../providers/worker-provider-factory.js';
4
11
  import { SupervisorRuntime } from '../supervisor/runtime.js';
5
12
  import { TOOLS } from '../core/constants.js';
6
13
  import type { AopKernel } from '../core/kernel.js';
@@ -36,9 +43,14 @@ interface FeatureIndex {
36
43
 
37
44
  export class RunCommandHandler {
38
45
  private readonly spawnDelayMs: number;
46
+ private readonly workerProviderFactory: WorkerProviderFactory;
39
47
 
40
- constructor(spawnDelayMs = 500) {
48
+ constructor(
49
+ spawnDelayMs = 500,
50
+ workerProviderFactory: WorkerProviderFactory = new DefaultWorkerProviderFactory(),
51
+ ) {
41
52
  this.spawnDelayMs = spawnDelayMs;
53
+ this.workerProviderFactory = workerProviderFactory;
42
54
  }
43
55
 
44
56
  async execute(context: RunCommandContext): Promise<unknown> {
@@ -70,7 +82,23 @@ export class RunCommandHandler {
70
82
  agentsConfig: kernel.getAgentsConfig(),
71
83
  });
72
84
 
73
- const provider = new NullWorkerProvider(selection);
85
+ const runtimeConfig = resolveWorkerProviderRuntime(kernel.getAgentsConfig().runtime);
86
+ const policySnapshot = kernel.getPolicySnapshot();
87
+ const executionPolicy =
88
+ policySnapshot.execution && typeof policySnapshot.execution === 'object'
89
+ ? (policySnapshot.execution as Record<string, unknown>)
90
+ : null;
91
+ const provider = this.workerProviderFactory.create({
92
+ selection,
93
+ mode: resolveWorkerProviderMode(
94
+ options.worker_provider_mode,
95
+ kernel.getAgentsConfig().runtime?.worker_provider_mode,
96
+ 'live',
97
+ ),
98
+ context: 'run',
99
+ policy: resolveWorkerProviderPolicy(executionPolicy),
100
+ runtime: runtimeConfig,
101
+ });
74
102
  const supervisor = new SupervisorRuntime(kernel, provider, toolClient, {
75
103
  max_active_features: maxActiveFeatures ?? 5,
76
104
  max_parallel_gate_runs: maxParallelGateRuns ?? 3,
@@ -27,6 +27,7 @@ export interface CliOptions {
27
27
  agent_model?: string;
28
28
  agent_config?: string;
29
29
  provider_config_env?: string;
30
+ worker_provider_mode?: 'live' | 'stub';
30
31
  transport?: string;
31
32
  takeover_stale_run?: boolean;
32
33
  batch?: boolean;
@@ -31,6 +31,10 @@ export const ERROR_CODES = {
31
31
  AGENT_PROVIDER_NOT_CONFIGURED: 'agent_provider_not_configured',
32
32
  UNSUPPORTED_AGENT_PROVIDER: 'unsupported_agent_provider',
33
33
  PROVIDER_AUTH_MISSING: 'provider_auth_missing',
34
+ PROVIDER_STUB_DISALLOWED: 'provider_stub_disallowed',
35
+ PROVIDER_RUNTIME_UNAVAILABLE: 'provider_runtime_unavailable',
36
+ PROVIDER_OUTPUT_INVALID: 'provider_output_invalid',
37
+ PROVIDER_NO_PROGRESS: 'provider_no_progress',
34
38
  UNAUTHENTICATED: 'unauthenticated',
35
39
  INVALID_ACTOR_CLAIM: 'invalid_actor_claim',
36
40
  OPERATION_ID_REQUIRED: 'operation_id_required',
@@ -95,6 +95,9 @@ export interface AgentsRuntimeConfig {
95
95
  provider_config_env?: string;
96
96
  default_agent_config?: Record<string, unknown>;
97
97
  provider_configs?: Record<string, Record<string, unknown>>;
98
+ worker_provider_mode?: 'live' | 'stub' | string;
99
+ worker_response_timeout_ms?: number;
100
+ max_consecutive_no_progress_iterations?: number;
98
101
  }
99
102
 
100
103
  export interface AgentsConfigSnapshot {
@@ -3,6 +3,20 @@ export type { AgentsConfigSnapshot } from './core/kernel.js';
3
3
  export { SupervisorRuntime } from './supervisor/runtime.js';
4
4
  export { resolveProviderSelection, NullWorkerProvider } from './providers/providers.js';
5
5
  export type { WorkerProvider, ProviderSelectionResolver } from './providers/providers.js';
6
+ export {
7
+ DefaultWorkerProviderFactory,
8
+ resolveWorkerProviderMode,
9
+ resolveWorkerProviderRuntime,
10
+ resolveWorkerProviderPolicy,
11
+ resolveMalformedWorkerOutputAction,
12
+ resolveNoProgressAction,
13
+ } from './providers/worker-provider-factory.js';
14
+ export type {
15
+ WorkerProviderMode,
16
+ ProviderCommandContext,
17
+ WorkerProviderFactory,
18
+ CreateWorkerProviderInput,
19
+ } from './providers/worker-provider-factory.js';
6
20
  export { createToolingRuntime, resolveToolClient } from './mcp/runtime-factory.js';
7
21
  export type { ToolingKernelPort, CreateToolingRuntimeOptions } from './mcp/runtime-factory.js';
8
22
  export { InProcessToolClient, McpToolClient, createOperationId } from './mcp/tool-client.js';
@@ -26,7 +26,13 @@ import { AttachCommandHandler } from '../../cli/attach-command-handler.js';
26
26
  import { HelpCommandHandler } from '../../cli/help-command-handler.js';
27
27
  import { readEnvFileValues } from '../../cli/env-file.js';
28
28
  import { MultiProjectLoader } from '../../application/multi-project-loader.js';
29
- import { NullWorkerProvider, resolveProviderSelection } from '../../providers/providers.js';
29
+ import { resolveProviderSelection } from '../../providers/providers.js';
30
+ import {
31
+ DefaultWorkerProviderFactory,
32
+ resolveWorkerProviderMode,
33
+ resolveWorkerProviderPolicy,
34
+ resolveWorkerProviderRuntime,
35
+ } from '../../providers/worker-provider-factory.js';
30
36
  import type { RuntimeContext } from '../../cli/types.js';
31
37
  import type { ProjectConfig } from '../../application/multi-project-loader.js';
32
38
 
@@ -315,7 +321,7 @@ export async function runCli(
315
321
  const tooling = await createToolingRuntime(repoRoot, kernel);
316
322
  const toolClient = resolveToolClient(transport, tooling);
317
323
 
318
- let commandProvider: NullWorkerProvider | null = null;
324
+ let commandProvider = null;
319
325
  if (options.command === CliCommand.Send || options.command === CliCommand.Attach) {
320
326
  let selection;
321
327
  try {
@@ -332,7 +338,23 @@ export async function runCli(
332
338
  provider_config_ref: null,
333
339
  };
334
340
  }
335
- commandProvider = new NullWorkerProvider(selection);
341
+ const workerProviderFactory = new DefaultWorkerProviderFactory();
342
+ const policySnapshot = kernel.getPolicySnapshot();
343
+ const executionPolicy =
344
+ policySnapshot.execution && typeof policySnapshot.execution === 'object'
345
+ ? (policySnapshot.execution as Record<string, unknown>)
346
+ : null;
347
+ commandProvider = workerProviderFactory.create({
348
+ selection,
349
+ mode: resolveWorkerProviderMode(
350
+ options.worker_provider_mode,
351
+ kernel.getAgentsConfig().runtime?.worker_provider_mode,
352
+ 'stub',
353
+ ),
354
+ context: options.command === CliCommand.Send ? 'send' : 'attach',
355
+ policy: resolveWorkerProviderPolicy(executionPolicy),
356
+ runtime: resolveWorkerProviderRuntime(kernel.getAgentsConfig().runtime),
357
+ });
336
358
  kernel.setProvider(commandProvider);
337
359
  }
338
360
 
@@ -0,0 +1,115 @@
1
+ import crypto from 'node:crypto';
2
+ import { ERROR_CODES } from '../core/error-codes.js';
3
+ import type { AppError, ProviderSelection, WorkerProvider, WorkerSession } from './providers.js';
4
+ import type { ProviderOutputParser } from './output-parsers/types.js';
5
+
6
+ interface ApiWorkerProviderOptions {
7
+ outputParser: ProviderOutputParser;
8
+ workerResponseTimeoutMs: number;
9
+ }
10
+
11
+ function providerError(code: string, message: string, details: Record<string, unknown>): AppError {
12
+ const error = new Error(message) as AppError;
13
+ error.code = code;
14
+ error.details = details;
15
+ return error;
16
+ }
17
+
18
+ function isRecord(value: unknown): value is Record<string, unknown> {
19
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
20
+ }
21
+
22
+ export class ApiWorkerProvider implements WorkerProvider {
23
+ readonly mode = 'live' as const;
24
+ readonly selection: ProviderSelection;
25
+
26
+ private readonly outputParser: ProviderOutputParser;
27
+ private readonly workerResponseTimeoutMs: number;
28
+
29
+ constructor(selection: ProviderSelection, options: ApiWorkerProviderOptions) {
30
+ this.selection = selection;
31
+ this.outputParser = options.outputParser;
32
+ this.workerResponseTimeoutMs = options.workerResponseTimeoutMs;
33
+ }
34
+
35
+ createSession(
36
+ role: string,
37
+ featureId: string,
38
+ systemPrompt: string | null,
39
+ ): Promise<WorkerSession> {
40
+ return Promise.resolve({
41
+ session_id: `${role}-${featureId}-${crypto.randomUUID()}`,
42
+ role,
43
+ feature_id: featureId,
44
+ system_prompt_loaded: Boolean(systemPrompt),
45
+ });
46
+ }
47
+
48
+ reattachSession(sessionId: string): Promise<WorkerSession | null> {
49
+ if (!sessionId || sessionId === 'unknown') {
50
+ return Promise.resolve(null);
51
+ }
52
+ return Promise.resolve({
53
+ session_id: sessionId,
54
+ role: 'orchestrator',
55
+ feature_id: 'global',
56
+ system_prompt_loaded: false,
57
+ });
58
+ }
59
+
60
+ closeSession(_sessionId: string): Promise<{ closed: true }> {
61
+ return Promise.resolve({ closed: true });
62
+ }
63
+
64
+ runWorker(input: {
65
+ role: string;
66
+ feature_id: string;
67
+ context_bundle?: Record<string, unknown>;
68
+ instructions?: string;
69
+ last_tool_results?: Array<Record<string, unknown>>;
70
+ runtime_selection?: {
71
+ provider: string;
72
+ model: string;
73
+ provider_config_ref: string | null;
74
+ };
75
+ }): Promise<Record<string, unknown>> {
76
+ // API-backed providers (gemini) require explicit adapter wiring. Until that is configured,
77
+ // fail deterministically instead of silently producing NOTE-only progress.
78
+ if (!this.selection.provider_config_ref) {
79
+ throw providerError(
80
+ ERROR_CODES.PROVIDER_RUNTIME_UNAVAILABLE,
81
+ 'API-backed provider credentials are not available',
82
+ {
83
+ provider: this.selection.provider,
84
+ reason: 'missing_provider_config_ref',
85
+ },
86
+ );
87
+ }
88
+
89
+ const mockResponse = isRecord(this.selection.agent_config)
90
+ ? this.selection.agent_config.mock_response
91
+ : null;
92
+ if (!isRecord(mockResponse)) {
93
+ throw providerError(
94
+ ERROR_CODES.PROVIDER_RUNTIME_UNAVAILABLE,
95
+ 'API-backed worker adapter is not configured for this provider',
96
+ {
97
+ provider: this.selection.provider,
98
+ reason: 'missing_api_adapter_configuration',
99
+ timeout_ms: this.workerResponseTimeoutMs,
100
+ },
101
+ );
102
+ }
103
+
104
+ const sessionId = `${input.role}-${input.feature_id}-api`;
105
+ return Promise.resolve(
106
+ this.outputParser.parse(JSON.stringify(mockResponse), {
107
+ sessionId,
108
+ role: input.role,
109
+ featureId: input.feature_id,
110
+ provider: this.selection.provider,
111
+ model: this.selection.model,
112
+ }),
113
+ );
114
+ }
115
+ }