agentic-orchestrator 0.1.26 → 0.1.28

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 (172) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/README.md +47 -14
  4. package/agentic/orchestrator/agents.yaml +13 -0
  5. package/agentic/orchestrator/policy.yaml +3 -0
  6. package/agentic/orchestrator/schemas/agents.schema.json +76 -0
  7. package/agentic/orchestrator/schemas/policy.schema.json +16 -0
  8. package/agentic/orchestrator/schemas/policy.user.schema.json +16 -0
  9. package/agentic/orchestrator/schemas/state.schema.json +53 -0
  10. package/apps/control-plane/src/application/configuration-service.ts +181 -0
  11. package/apps/control-plane/src/application/kernel-tool-wiring.ts +292 -0
  12. package/apps/control-plane/src/application/services/checkpoint-service.ts +523 -0
  13. package/apps/control-plane/src/application/services/feature-send-message-service.ts +132 -0
  14. package/apps/control-plane/src/application/services/patch-service.ts +29 -5
  15. package/apps/control-plane/src/application/services/repo-operations-service.ts +276 -0
  16. package/apps/control-plane/src/application/services/worktree-watchdog-service.ts +156 -0
  17. package/apps/control-plane/src/cli/cli-argument-parser.ts +12 -0
  18. package/apps/control-plane/src/cli/help-command-handler.ts +17 -0
  19. package/apps/control-plane/src/cli/init-command-handler.ts +31 -0
  20. package/apps/control-plane/src/cli/resume-command-handler.ts +31 -4
  21. package/apps/control-plane/src/cli/rollback-command-handler.ts +217 -0
  22. package/apps/control-plane/src/cli/run-command-handler.ts +8 -0
  23. package/apps/control-plane/src/cli/types.ts +3 -0
  24. package/apps/control-plane/src/core/kernel-types.ts +55 -0
  25. package/apps/control-plane/src/core/kernel.ts +61 -878
  26. package/apps/control-plane/src/core/tool-caller.ts +10 -0
  27. package/apps/control-plane/src/core/utils/field-readers.ts +38 -0
  28. package/apps/control-plane/src/core/utils/index-normalizer.ts +119 -0
  29. package/apps/control-plane/src/core/utils/path-normalizers.ts +22 -0
  30. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +15 -0
  31. package/apps/control-plane/src/providers/api-worker-provider.ts +14 -12
  32. package/apps/control-plane/src/providers/cli-worker-provider.ts +82 -12
  33. package/apps/control-plane/src/providers/providers.ts +45 -24
  34. package/apps/control-plane/src/providers/worker-provider-factory.ts +36 -1
  35. package/apps/control-plane/src/supervisor/run-coordinator.ts +91 -36
  36. package/apps/control-plane/src/supervisor/runtime.ts +107 -1
  37. package/apps/control-plane/src/supervisor/types.ts +9 -0
  38. package/apps/control-plane/src/supervisor/worker-decision-loop.ts +253 -14
  39. package/apps/control-plane/test/checkpoint-service.spec.ts +537 -0
  40. package/apps/control-plane/test/cli-helpers.spec.ts +28 -0
  41. package/apps/control-plane/test/cli.unit.spec.ts +52 -0
  42. package/apps/control-plane/test/configuration-service.spec.ts +466 -0
  43. package/apps/control-plane/test/dashboard-api.integration.spec.ts +537 -0
  44. package/apps/control-plane/test/dashboard-client.spec.ts +233 -0
  45. package/apps/control-plane/test/feature-send-message-service.spec.ts +314 -0
  46. package/apps/control-plane/test/init-wizard.spec.ts +35 -0
  47. package/apps/control-plane/test/path-normalizers.spec.ts +41 -0
  48. package/apps/control-plane/test/repo-operations-service.spec.ts +339 -0
  49. package/apps/control-plane/test/resume-command.spec.ts +33 -0
  50. package/apps/control-plane/test/review-workspace-logic.spec.ts +130 -0
  51. package/apps/control-plane/test/rollback-command.spec.ts +208 -0
  52. package/apps/control-plane/test/run-coordinator.spec.ts +119 -0
  53. package/apps/control-plane/test/worker-decision-loop.spec.ts +209 -0
  54. package/apps/control-plane/test/worker-provider-adapters.spec.ts +102 -0
  55. package/apps/control-plane/test/worker-provider-factory.spec.ts +14 -0
  56. package/apps/control-plane/test/worktree-watchdog-service.spec.ts +147 -0
  57. package/config/agentic/orchestrator/agents.yaml +13 -0
  58. package/dist/apps/control-plane/application/configuration-service.d.ts +19 -0
  59. package/dist/apps/control-plane/application/configuration-service.js +123 -0
  60. package/dist/apps/control-plane/application/configuration-service.js.map +1 -0
  61. package/dist/apps/control-plane/application/kernel-tool-wiring.d.ts +39 -0
  62. package/dist/apps/control-plane/application/kernel-tool-wiring.js +38 -0
  63. package/dist/apps/control-plane/application/kernel-tool-wiring.js.map +1 -0
  64. package/dist/apps/control-plane/application/services/checkpoint-service.d.ts +84 -0
  65. package/dist/apps/control-plane/application/services/checkpoint-service.js +367 -0
  66. package/dist/apps/control-plane/application/services/checkpoint-service.js.map +1 -0
  67. package/dist/apps/control-plane/application/services/feature-send-message-service.d.ts +25 -0
  68. package/dist/apps/control-plane/application/services/feature-send-message-service.js +105 -0
  69. package/dist/apps/control-plane/application/services/feature-send-message-service.js.map +1 -0
  70. package/dist/apps/control-plane/application/services/patch-service.d.ts +6 -0
  71. package/dist/apps/control-plane/application/services/patch-service.js +11 -2
  72. package/dist/apps/control-plane/application/services/patch-service.js.map +1 -1
  73. package/dist/apps/control-plane/application/services/repo-operations-service.d.ts +70 -0
  74. package/dist/apps/control-plane/application/services/repo-operations-service.js +213 -0
  75. package/dist/apps/control-plane/application/services/repo-operations-service.js.map +1 -0
  76. package/dist/apps/control-plane/application/services/worktree-watchdog-service.d.ts +23 -0
  77. package/dist/apps/control-plane/application/services/worktree-watchdog-service.js +119 -0
  78. package/dist/apps/control-plane/application/services/worktree-watchdog-service.js.map +1 -0
  79. package/dist/apps/control-plane/cli/cli-argument-parser.js +12 -0
  80. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
  81. package/dist/apps/control-plane/cli/help-command-handler.js +17 -0
  82. package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
  83. package/dist/apps/control-plane/cli/init-command-handler.js +23 -0
  84. package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
  85. package/dist/apps/control-plane/cli/resume-command-handler.js +25 -5
  86. package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
  87. package/dist/apps/control-plane/cli/rollback-command-handler.d.ts +6 -0
  88. package/dist/apps/control-plane/cli/rollback-command-handler.js +177 -0
  89. package/dist/apps/control-plane/cli/rollback-command-handler.js.map +1 -0
  90. package/dist/apps/control-plane/cli/run-command-handler.js +7 -1
  91. package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
  92. package/dist/apps/control-plane/cli/types.d.ts +3 -0
  93. package/dist/apps/control-plane/cli/types.js +1 -0
  94. package/dist/apps/control-plane/cli/types.js.map +1 -1
  95. package/dist/apps/control-plane/core/configuration-service.d.ts +25 -0
  96. package/dist/apps/control-plane/core/configuration-service.js +130 -0
  97. package/dist/apps/control-plane/core/configuration-service.js.map +1 -0
  98. package/dist/apps/control-plane/core/kernel-tool-wiring.d.ts +50 -0
  99. package/dist/apps/control-plane/core/kernel-tool-wiring.js +44 -0
  100. package/dist/apps/control-plane/core/kernel-tool-wiring.js.map +1 -0
  101. package/dist/apps/control-plane/core/kernel-types.d.ts +48 -0
  102. package/dist/apps/control-plane/core/kernel-types.js +2 -0
  103. package/dist/apps/control-plane/core/kernel-types.js.map +1 -0
  104. package/dist/apps/control-plane/core/kernel.d.ts +17 -48
  105. package/dist/apps/control-plane/core/kernel.js +44 -539
  106. package/dist/apps/control-plane/core/kernel.js.map +1 -1
  107. package/dist/apps/control-plane/core/tool-caller.d.ts +10 -0
  108. package/dist/apps/control-plane/core/utils/error-normalizer.d.ts +2 -0
  109. package/dist/apps/control-plane/core/utils/error-normalizer.js +51 -0
  110. package/dist/apps/control-plane/core/utils/error-normalizer.js.map +1 -0
  111. package/dist/apps/control-plane/core/utils/field-readers.d.ts +9 -0
  112. package/dist/apps/control-plane/core/utils/field-readers.js +30 -0
  113. package/dist/apps/control-plane/core/utils/field-readers.js.map +1 -0
  114. package/dist/apps/control-plane/core/utils/index-normalizer.d.ts +7 -0
  115. package/dist/apps/control-plane/core/utils/index-normalizer.js +92 -0
  116. package/dist/apps/control-plane/core/utils/index-normalizer.js.map +1 -0
  117. package/dist/apps/control-plane/core/utils/path-normalizers.d.ts +2 -0
  118. package/dist/apps/control-plane/core/utils/path-normalizers.js +17 -0
  119. package/dist/apps/control-plane/core/utils/path-normalizers.js.map +1 -0
  120. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +13 -1
  121. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
  122. package/dist/apps/control-plane/providers/api-worker-provider.d.ts +4 -13
  123. package/dist/apps/control-plane/providers/api-worker-provider.js +10 -0
  124. package/dist/apps/control-plane/providers/api-worker-provider.js.map +1 -1
  125. package/dist/apps/control-plane/providers/cli-worker-provider.d.ts +11 -13
  126. package/dist/apps/control-plane/providers/cli-worker-provider.js +64 -0
  127. package/dist/apps/control-plane/providers/cli-worker-provider.js.map +1 -1
  128. package/dist/apps/control-plane/providers/providers.d.ts +31 -24
  129. package/dist/apps/control-plane/providers/providers.js +10 -0
  130. package/dist/apps/control-plane/providers/providers.js.map +1 -1
  131. package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +11 -0
  132. package/dist/apps/control-plane/providers/worker-provider-factory.js +20 -1
  133. package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -1
  134. package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +3 -0
  135. package/dist/apps/control-plane/supervisor/run-coordinator.js +81 -33
  136. package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
  137. package/dist/apps/control-plane/supervisor/runtime.d.ts +8 -1
  138. package/dist/apps/control-plane/supervisor/runtime.js +90 -0
  139. package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
  140. package/dist/apps/control-plane/supervisor/types.d.ts +11 -0
  141. package/dist/apps/control-plane/supervisor/types.js.map +1 -1
  142. package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +21 -1
  143. package/dist/apps/control-plane/supervisor/worker-decision-loop.js +207 -13
  144. package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
  145. package/package.json +1 -1
  146. package/packages/web-dashboard/package.json +2 -0
  147. package/packages/web-dashboard/src/app/analytics/page.tsx +83 -2
  148. package/packages/web-dashboard/src/app/api/actions/route.ts +92 -1
  149. package/packages/web-dashboard/src/app/api/analytics/route.ts +5 -2
  150. package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/[checkpointId]/diff/route.ts +43 -0
  151. package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/compare/route.ts +45 -0
  152. package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/stream/route.ts +170 -0
  153. package/packages/web-dashboard/src/app/api/features/[id]/file-diff/route.ts +144 -0
  154. package/packages/web-dashboard/src/app/api/features/[id]/log-stream/route.ts +167 -0
  155. package/packages/web-dashboard/src/app/api/features/[id]/raw-logs/[filename]/route.ts +65 -0
  156. package/packages/web-dashboard/src/app/api/features/[id]/raw-logs/route.ts +63 -0
  157. package/packages/web-dashboard/src/app/api/features/[id]/timeline/route.ts +60 -0
  158. package/packages/web-dashboard/src/app/feature/[id]/page.tsx +32 -11
  159. package/packages/web-dashboard/src/app/globals.css +2 -0
  160. package/packages/web-dashboard/src/components/detail-panel.tsx +483 -0
  161. package/packages/web-dashboard/src/components/review-workspace.tsx +1162 -0
  162. package/packages/web-dashboard/src/lib/aop-client.ts +725 -0
  163. package/packages/web-dashboard/src/lib/review-contracts.ts +182 -0
  164. package/packages/web-dashboard/src/lib/review-workspace-logic.ts +64 -0
  165. package/packages/web-dashboard/src/lib/types.ts +131 -0
  166. package/packages/web-dashboard/src/styles/dashboard.module.css +333 -0
  167. package/spec-files/completed/agentic_orchestrator_execution_mode_spec.md +1905 -0
  168. package/spec-files/outstanding/agentic_orchestrator_runtime_inspection_spec.md +940 -0
  169. package/spec-files/outstanding/execution_mode_critical_review.md +355 -0
  170. package/spec-files/outstanding/shadow_workspace_implementation_spec.md +1271 -0
  171. package/spec-files/outstanding/shadow_workspace_spec_summary.md +222 -0
  172. package/spec-files/progress.md +269 -1
@@ -63,6 +63,7 @@ export class RunCoordinator {
63
63
  private readonly prMonitor: PrMonitorService | undefined;
64
64
  private readonly issueTracker: IssueTracker | undefined;
65
65
  private readonly statusCache: Map<string, string>;
66
+ private static readonly BACKGROUND_LEASE_RENEW_INTERVAL_MS = 30_000;
66
67
  private static readonly TERMINAL_STATUSES = new Set<string>([
67
68
  STATUS.MERGED,
68
69
  STATUS.FAILED,
@@ -105,56 +106,110 @@ export class RunCoordinator {
105
106
  });
106
107
 
107
108
  this.state.runMetadata.took_over_stale = Boolean(leaseResult.data.took_over_stale);
109
+ const backgroundLeaseRenewal = this.startBackgroundLeaseRenewal();
108
110
 
109
- await this.sessionOrchestrator.ensureGlobalOrchestratorSession();
111
+ try {
112
+ await this.sessionOrchestrator.ensureGlobalOrchestratorSession();
110
113
 
111
- const sorted = [...features].sort((a, b) => a.feature_id.localeCompare(b.feature_id));
112
- const scopedFeatureIds = sorted.map((item) => item.feature_id);
113
- this.state.queue = sorted;
114
- let activeFeatureIds: string[] = [];
114
+ const sorted = [...features].sort((a, b) => a.feature_id.localeCompare(b.feature_id));
115
+ const scopedFeatureIds = sorted.map((item) => item.feature_id);
116
+ this.state.queue = sorted;
117
+ let activeFeatureIds: string[] = [];
115
118
 
116
- await this.sessionOrchestrator.cleanupOrphanWorkerSessions(scopedFeatureIds);
117
- await this.kernel.pruneFeatureSessionAssignments({
118
- run_id: this.state.runId,
119
- owner_instance_id: this.state.ownerInstanceId,
120
- active_feature_ids: scopedFeatureIds,
121
- });
119
+ await this.sessionOrchestrator.cleanupOrphanWorkerSessions(scopedFeatureIds);
120
+ await this.kernel.pruneFeatureSessionAssignments({
121
+ run_id: this.state.runId,
122
+ owner_instance_id: this.state.ownerInstanceId,
123
+ active_feature_ids: scopedFeatureIds,
124
+ });
122
125
 
123
- activeFeatureIds = await this.rebalanceActiveFeatures(activeFeatureIds);
126
+ activeFeatureIds = await this.rebalanceActiveFeatures(activeFeatureIds);
124
127
 
125
- for (let iteration = 0; iteration < this.maxIterationsPerPhase; iteration += 1) {
126
- if (activeFeatureIds.length === 0 && this.state.queue.length === 0) {
127
- break;
128
+ for (let iteration = 0; iteration < this.maxIterationsPerPhase; iteration += 1) {
129
+ this.throwIfBackgroundLeaseRenewalFailed(backgroundLeaseRenewal.error());
130
+ if (activeFeatureIds.length === 0 && this.state.queue.length === 0) {
131
+ break;
132
+ }
133
+ await this.leaseHeartbeatService.renew();
134
+ this.throwIfBackgroundLeaseRenewalFailed(backgroundLeaseRenewal.error());
135
+ activeFeatureIds = await this.applyOrchestratorPrioritization(
136
+ activeFeatureIds,
137
+ iteration + 1,
138
+ );
139
+ // N3: Check budget before waves; pause over-budget features
140
+ activeFeatureIds = await this.pauseOverBudgetFeatures(activeFeatureIds);
141
+ await this.planningWaveExecutor.run(activeFeatureIds);
142
+ await this.buildWaveExecutor.run(activeFeatureIds, this.maxParallelGateRuns);
143
+ await this.qaWaveExecutor.run(activeFeatureIds, this.maxParallelGateRuns);
144
+ await this.planningWaveExecutor.runPostQaReconciliation(activeFeatureIds, iteration + 1);
145
+ await this.notifyStatusTransitions(activeFeatureIds);
146
+ activeFeatureIds = await this.rebalanceActiveFeatures(activeFeatureIds);
128
147
  }
129
- await this.leaseHeartbeatService.renew();
130
- activeFeatureIds = await this.applyOrchestratorPrioritization(
131
- activeFeatureIds,
132
- iteration + 1,
133
- );
134
- // N3: Check budget before waves; pause over-budget features
135
- activeFeatureIds = await this.pauseOverBudgetFeatures(activeFeatureIds);
136
- await this.planningWaveExecutor.run(activeFeatureIds);
137
- await this.buildWaveExecutor.run(activeFeatureIds, this.maxParallelGateRuns);
138
- await this.qaWaveExecutor.run(activeFeatureIds, this.maxParallelGateRuns);
139
- await this.planningWaveExecutor.runPostQaReconciliation(activeFeatureIds, iteration + 1);
140
- await this.notifyStatusTransitions(activeFeatureIds);
141
- activeFeatureIds = await this.rebalanceActiveFeatures(activeFeatureIds);
148
+
149
+ const dashboard = await this.toolCaller.callTool('orchestrator', TOOLS.REPORT_DASHBOARD, {});
150
+ const runtimeSessions = await this.kernel.getRuntimeSessions();
151
+ this.throwIfBackgroundLeaseRenewalFailed(backgroundLeaseRenewal.error());
152
+
153
+ return {
154
+ status: 'running',
155
+ dashboard: dashboard.data,
156
+ queue_depth: this.state.queue.length,
157
+ run_metadata: {
158
+ ...this.state.runMetadata,
159
+ runtime_sessions: runtimeSessions,
160
+ },
161
+ };
162
+ } finally {
163
+ await backgroundLeaseRenewal.stop();
142
164
  }
165
+ }
166
+
167
+ private startBackgroundLeaseRenewal(): {
168
+ stop: () => Promise<void>;
169
+ error: () => unknown;
170
+ } {
171
+ let inFlight: Promise<void> | null = null;
172
+ let failedRenewal: unknown = null;
173
+ let stopped = false;
174
+
175
+ const renew = (): void => {
176
+ if (stopped || inFlight !== null || failedRenewal) {
177
+ return;
178
+ }
179
+ inFlight = this.leaseHeartbeatService
180
+ .renew()
181
+ .catch((error: unknown) => {
182
+ failedRenewal = error;
183
+ })
184
+ .finally(() => {
185
+ inFlight = null;
186
+ });
187
+ };
143
188
 
144
- const dashboard = await this.toolCaller.callTool('orchestrator', TOOLS.REPORT_DASHBOARD, {});
145
- const runtimeSessions = await this.kernel.getRuntimeSessions();
189
+ const timer = setInterval(renew, RunCoordinator.BACKGROUND_LEASE_RENEW_INTERVAL_MS);
190
+ if (typeof timer.unref === 'function') {
191
+ timer.unref();
192
+ }
146
193
 
147
194
  return {
148
- status: 'running',
149
- dashboard: dashboard.data,
150
- queue_depth: this.state.queue.length,
151
- run_metadata: {
152
- ...this.state.runMetadata,
153
- runtime_sessions: runtimeSessions,
195
+ stop: async () => {
196
+ stopped = true;
197
+ clearInterval(timer);
198
+ if (inFlight !== null) {
199
+ await inFlight;
200
+ }
154
201
  },
202
+ error: () => failedRenewal,
155
203
  };
156
204
  }
157
205
 
206
+ private throwIfBackgroundLeaseRenewalFailed(error: unknown): void {
207
+ if (!error) {
208
+ return;
209
+ }
210
+ throw error;
211
+ }
212
+
158
213
  private async pauseOverBudgetFeatures(activeFeatureIds: string[]): Promise<string[]> {
159
214
  const remaining: string[] = [];
160
215
  for (const featureId of activeFeatureIds) {
@@ -1,6 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
2
  import { stableHash } from '../core/fs.js';
3
- import type { WorkerProvider } from '../providers/providers.js';
3
+ import type { ExecutionMode, WorkerProvider } from '../providers/providers.js';
4
4
  import { createOperationId, type ToolClient } from '../mcp/tool-client.js';
5
5
  import { withOperationIdIfRequired } from '../application/tools/tool-metadata.js';
6
6
  import { PromptBundleLoader } from './prompt-bundle-loader.js';
@@ -16,6 +16,11 @@ import { ReactionsService } from '../application/services/reactions-service.js';
16
16
  import { ActivityMonitorService } from '../application/services/activity-monitor-service.js';
17
17
  import { PrMonitorService, createGhRunner } from '../application/services/pr-monitor-service.js';
18
18
  import { createIssueTracker } from '../application/services/issue-tracker-service.js';
19
+ import {
20
+ CheckpointService,
21
+ type InteractiveExecutionConfig,
22
+ } from '../application/services/checkpoint-service.js';
23
+ import { WorktreeWatchdogService } from '../application/services/worktree-watchdog-service.js';
19
24
  import {
20
25
  resolveMalformedWorkerOutputAction,
21
26
  resolveNoProgressAction,
@@ -54,6 +59,34 @@ import type {
54
59
 
55
60
  type NamedContract = { name: string };
56
61
 
62
+ function asPositiveInteger(value: unknown, fallback: number): number {
63
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 1) {
64
+ return fallback;
65
+ }
66
+ return Math.floor(value);
67
+ }
68
+
69
+ function asBoolean(value: unknown, fallback: boolean): boolean {
70
+ if (typeof value !== 'boolean') {
71
+ return fallback;
72
+ }
73
+ return value;
74
+ }
75
+
76
+ function asSeverity(value: unknown, fallback: InteractiveExecutionConfig['violationSeverity']) {
77
+ if (value === 'info' || value === 'warning' || value === 'error' || value === 'critical') {
78
+ return value;
79
+ }
80
+ return fallback;
81
+ }
82
+
83
+ function asExecutionMode(value: unknown): ExecutionMode | null {
84
+ if (value === 'deterministic' || value === 'interactive') {
85
+ return value;
86
+ }
87
+ return null;
88
+ }
89
+
57
90
  function resolveAdapterName(
58
91
  slot: AdapterSlot<NamedContract>,
59
92
  configuredValue: unknown,
@@ -103,6 +136,10 @@ export class SupervisorRuntime
103
136
  private readonly workerDecisionLoop: WorkerDecisionLoop;
104
137
  private readonly leaseHeartbeatService: LeaseHeartbeatService;
105
138
  private readonly runCoordinator: RunCoordinator;
139
+ private readonly cliExecutionMode: ExecutionMode | null;
140
+ private readonly interactiveConfig: InteractiveExecutionConfig;
141
+ private readonly worktreeWatchdogService: WorktreeWatchdogService;
142
+ private readonly checkpointService: CheckpointService;
106
143
  private static readonly DEFAULT_MAX_ITERATIONS_PER_PHASE = 6;
107
144
 
108
145
  constructor(
@@ -120,6 +157,22 @@ export class SupervisorRuntime
120
157
  options.max_iterations_per_phase,
121
158
  );
122
159
  this.takeoverStaleRun = Boolean(options.takeover_stale_run);
160
+ this.cliExecutionMode = asExecutionMode(options.execution_mode);
161
+ this.interactiveConfig = this.resolveInteractiveExecutionConfig();
162
+ this.worktreeWatchdogService = new WorktreeWatchdogService();
163
+ this.checkpointService = new CheckpointService({
164
+ repoRoot: this.kernel.getRepoRoot(),
165
+ featurePathResolver: (featureId: string) => this.kernel.featurePath(featureId),
166
+ worktreePathResolver: (featureId: string) => this.kernel.worktreePath(featureId),
167
+ validateDiff: async (featureId: string, parsedDiff: unknown) =>
168
+ await this.kernel.validatePatchDiff(featureId, parsedDiff),
169
+ watchdog: this.worktreeWatchdogService,
170
+ provider: this.provider,
171
+ readState: (featureId: string) => this.kernel.readState(featureId),
172
+ updateState: async (featureId, expectedVersion, updater) =>
173
+ await this.kernel.updateState(featureId, expectedVersion, updater),
174
+ config: this.interactiveConfig,
175
+ });
123
176
  const runId = options.run_id ?? `run:${Date.now()}`;
124
177
  const ownerInstanceId =
125
178
  options.owner_instance_id ?? `supervisor:${process.pid}:${crypto.randomUUID()}`;
@@ -228,6 +281,13 @@ export class SupervisorRuntime
228
281
  activityMonitor,
229
282
  repoRoot: this.kernel.getRepoRoot(),
230
283
  runId: () => this.state.runId,
284
+ resolveExecutionMode: async (featureId: string) => await this.resolveExecutionMode(featureId),
285
+ resolveInteractiveConfig: () => this.interactiveConfig,
286
+ watchdog: this.worktreeWatchdogService,
287
+ checkpointService: this.checkpointService,
288
+ resolveWorktreePath: (featureId: string) => this.kernel.worktreePath(featureId),
289
+ resolveRoleSessionId: (role, featureId) =>
290
+ this.resolveRoleSessionIdByFeature(role, featureId),
231
291
  });
232
292
 
233
293
  this.planningWaveExecutor = new PlanningWaveExecutor({
@@ -548,6 +608,52 @@ export class SupervisorRuntime
548
608
  }
549
609
  }
550
610
 
611
+ private resolveRoleSessionIdByFeature(role: RuntimeRole, featureId: string): string | null {
612
+ return this.resolveRoleSessionId(role, { feature_id: featureId });
613
+ }
614
+
615
+ private resolveInteractiveExecutionConfig(): InteractiveExecutionConfig {
616
+ const runtime = this.kernel.getAgentsConfig().runtime;
617
+ const interactive =
618
+ runtime &&
619
+ typeof runtime === 'object' &&
620
+ runtime.interactive &&
621
+ typeof runtime.interactive === 'object'
622
+ ? (runtime.interactive as Record<string, unknown>)
623
+ : {};
624
+ return {
625
+ checkpointIntervalMs: asPositiveInteger(interactive['checkpoint_interval_ms'], 30_000),
626
+ watchdogPollIntervalMs: asPositiveInteger(interactive['watchdog_poll_interval_ms'], 2_000),
627
+ maxUncommittedChanges: asPositiveInteger(interactive['max_uncommitted_changes'], 50),
628
+ validationOnCheckpoint: asBoolean(interactive['validation_on_checkpoint'], true),
629
+ revertOnViolation: asBoolean(interactive['revert_on_violation'], false),
630
+ violationSeverity: asSeverity(interactive['violation_severity'], 'warning'),
631
+ };
632
+ }
633
+
634
+ async resolveExecutionMode(featureId: string): Promise<ExecutionMode> {
635
+ if (this.cliExecutionMode) {
636
+ return this.cliExecutionMode;
637
+ }
638
+
639
+ try {
640
+ const state = await this.kernel.readState(featureId);
641
+ const fromState = asExecutionMode(state.frontMatter['execution_mode']);
642
+ if (fromState) {
643
+ return fromState;
644
+ }
645
+ } catch {
646
+ // Ignore state read failures and continue to runtime defaults.
647
+ }
648
+
649
+ const fromRuntime = asExecutionMode(this.kernel.getAgentsConfig().runtime?.execution_mode);
650
+ if (fromRuntime) {
651
+ return fromRuntime;
652
+ }
653
+
654
+ return 'deterministic';
655
+ }
656
+
551
657
  async callTool<TData = Record<string, unknown>, TToolName extends string = string>(
552
658
  role: RuntimeRole,
553
659
  toolName: TToolName,
@@ -1,6 +1,8 @@
1
1
  import type { AgentsConfigSnapshot } from '../core/kernel.js';
2
2
  import type { FeatureStatePayload } from '../core/tool-caller.js';
3
3
  import type { BudgetCheckResult } from '../application/services/cost-tracking-service.js';
4
+ import type { ExecutionMode } from '../providers/providers.js';
5
+ import type { PatchValidationResult } from '../application/services/patch-service.js';
4
6
  export type {
5
7
  FeatureStateFrontMatter,
6
8
  FeatureStatePayload,
@@ -21,6 +23,7 @@ export interface SupervisorOptions {
21
23
  run_id?: string;
22
24
  owner_instance_id?: string;
23
25
  takeover_stale_run?: boolean;
26
+ execution_mode?: ExecutionMode;
24
27
  }
25
28
 
26
29
  export interface FeatureInput {
@@ -119,6 +122,12 @@ export interface FeatureOrchestrationPort {
119
122
  ownerInstanceId: string,
120
123
  ): Promise<{ data: { lease_expires_at: string } }>;
121
124
  repoDiff(featureId: string, options?: string[]): Promise<{ data: Record<string, unknown> }>;
125
+ worktreePath(featureId: string): string;
126
+ featurePath(featureId: string): string;
127
+ readState(
128
+ featureId: string,
129
+ ): Promise<{ frontMatter: Record<string, unknown>; body: string; raw?: string }>;
130
+ validatePatchDiff(featureId: string, parsedDiff: unknown): Promise<PatchValidationResult>;
122
131
  updateState(
123
132
  featureId: string,
124
133
  expectedVersion: number | null,