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.
- package/AGENTS.md +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +47 -14
- package/agentic/orchestrator/agents.yaml +13 -0
- package/agentic/orchestrator/policy.yaml +3 -0
- package/agentic/orchestrator/schemas/agents.schema.json +76 -0
- package/agentic/orchestrator/schemas/policy.schema.json +16 -0
- package/agentic/orchestrator/schemas/policy.user.schema.json +16 -0
- package/agentic/orchestrator/schemas/state.schema.json +53 -0
- package/apps/control-plane/src/application/configuration-service.ts +181 -0
- package/apps/control-plane/src/application/kernel-tool-wiring.ts +292 -0
- package/apps/control-plane/src/application/services/checkpoint-service.ts +523 -0
- package/apps/control-plane/src/application/services/feature-send-message-service.ts +132 -0
- package/apps/control-plane/src/application/services/patch-service.ts +29 -5
- package/apps/control-plane/src/application/services/repo-operations-service.ts +276 -0
- package/apps/control-plane/src/application/services/worktree-watchdog-service.ts +156 -0
- package/apps/control-plane/src/cli/cli-argument-parser.ts +12 -0
- package/apps/control-plane/src/cli/help-command-handler.ts +17 -0
- package/apps/control-plane/src/cli/init-command-handler.ts +31 -0
- package/apps/control-plane/src/cli/resume-command-handler.ts +31 -4
- package/apps/control-plane/src/cli/rollback-command-handler.ts +217 -0
- package/apps/control-plane/src/cli/run-command-handler.ts +8 -0
- package/apps/control-plane/src/cli/types.ts +3 -0
- package/apps/control-plane/src/core/kernel-types.ts +55 -0
- package/apps/control-plane/src/core/kernel.ts +61 -878
- package/apps/control-plane/src/core/tool-caller.ts +10 -0
- package/apps/control-plane/src/core/utils/field-readers.ts +38 -0
- package/apps/control-plane/src/core/utils/index-normalizer.ts +119 -0
- package/apps/control-plane/src/core/utils/path-normalizers.ts +22 -0
- package/apps/control-plane/src/interfaces/cli/bootstrap.ts +15 -0
- package/apps/control-plane/src/providers/api-worker-provider.ts +14 -12
- package/apps/control-plane/src/providers/cli-worker-provider.ts +82 -12
- package/apps/control-plane/src/providers/providers.ts +45 -24
- package/apps/control-plane/src/providers/worker-provider-factory.ts +36 -1
- package/apps/control-plane/src/supervisor/run-coordinator.ts +91 -36
- package/apps/control-plane/src/supervisor/runtime.ts +107 -1
- package/apps/control-plane/src/supervisor/types.ts +9 -0
- package/apps/control-plane/src/supervisor/worker-decision-loop.ts +253 -14
- package/apps/control-plane/test/checkpoint-service.spec.ts +537 -0
- package/apps/control-plane/test/cli-helpers.spec.ts +28 -0
- package/apps/control-plane/test/cli.unit.spec.ts +52 -0
- package/apps/control-plane/test/configuration-service.spec.ts +466 -0
- package/apps/control-plane/test/dashboard-api.integration.spec.ts +537 -0
- package/apps/control-plane/test/dashboard-client.spec.ts +233 -0
- package/apps/control-plane/test/feature-send-message-service.spec.ts +314 -0
- package/apps/control-plane/test/init-wizard.spec.ts +35 -0
- package/apps/control-plane/test/path-normalizers.spec.ts +41 -0
- package/apps/control-plane/test/repo-operations-service.spec.ts +339 -0
- package/apps/control-plane/test/resume-command.spec.ts +33 -0
- package/apps/control-plane/test/review-workspace-logic.spec.ts +130 -0
- package/apps/control-plane/test/rollback-command.spec.ts +208 -0
- package/apps/control-plane/test/run-coordinator.spec.ts +119 -0
- package/apps/control-plane/test/worker-decision-loop.spec.ts +209 -0
- package/apps/control-plane/test/worker-provider-adapters.spec.ts +102 -0
- package/apps/control-plane/test/worker-provider-factory.spec.ts +14 -0
- package/apps/control-plane/test/worktree-watchdog-service.spec.ts +147 -0
- package/config/agentic/orchestrator/agents.yaml +13 -0
- package/dist/apps/control-plane/application/configuration-service.d.ts +19 -0
- package/dist/apps/control-plane/application/configuration-service.js +123 -0
- package/dist/apps/control-plane/application/configuration-service.js.map +1 -0
- package/dist/apps/control-plane/application/kernel-tool-wiring.d.ts +39 -0
- package/dist/apps/control-plane/application/kernel-tool-wiring.js +38 -0
- package/dist/apps/control-plane/application/kernel-tool-wiring.js.map +1 -0
- package/dist/apps/control-plane/application/services/checkpoint-service.d.ts +84 -0
- package/dist/apps/control-plane/application/services/checkpoint-service.js +367 -0
- package/dist/apps/control-plane/application/services/checkpoint-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/feature-send-message-service.d.ts +25 -0
- package/dist/apps/control-plane/application/services/feature-send-message-service.js +105 -0
- package/dist/apps/control-plane/application/services/feature-send-message-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/patch-service.d.ts +6 -0
- package/dist/apps/control-plane/application/services/patch-service.js +11 -2
- package/dist/apps/control-plane/application/services/patch-service.js.map +1 -1
- package/dist/apps/control-plane/application/services/repo-operations-service.d.ts +70 -0
- package/dist/apps/control-plane/application/services/repo-operations-service.js +213 -0
- package/dist/apps/control-plane/application/services/repo-operations-service.js.map +1 -0
- package/dist/apps/control-plane/application/services/worktree-watchdog-service.d.ts +23 -0
- package/dist/apps/control-plane/application/services/worktree-watchdog-service.js +119 -0
- package/dist/apps/control-plane/application/services/worktree-watchdog-service.js.map +1 -0
- package/dist/apps/control-plane/cli/cli-argument-parser.js +12 -0
- package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
- package/dist/apps/control-plane/cli/help-command-handler.js +17 -0
- package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/init-command-handler.js +23 -0
- package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/resume-command-handler.js +25 -5
- package/dist/apps/control-plane/cli/resume-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/rollback-command-handler.d.ts +6 -0
- package/dist/apps/control-plane/cli/rollback-command-handler.js +177 -0
- package/dist/apps/control-plane/cli/rollback-command-handler.js.map +1 -0
- package/dist/apps/control-plane/cli/run-command-handler.js +7 -1
- package/dist/apps/control-plane/cli/run-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/types.d.ts +3 -0
- package/dist/apps/control-plane/cli/types.js +1 -0
- package/dist/apps/control-plane/cli/types.js.map +1 -1
- package/dist/apps/control-plane/core/configuration-service.d.ts +25 -0
- package/dist/apps/control-plane/core/configuration-service.js +130 -0
- package/dist/apps/control-plane/core/configuration-service.js.map +1 -0
- package/dist/apps/control-plane/core/kernel-tool-wiring.d.ts +50 -0
- package/dist/apps/control-plane/core/kernel-tool-wiring.js +44 -0
- package/dist/apps/control-plane/core/kernel-tool-wiring.js.map +1 -0
- package/dist/apps/control-plane/core/kernel-types.d.ts +48 -0
- package/dist/apps/control-plane/core/kernel-types.js +2 -0
- package/dist/apps/control-plane/core/kernel-types.js.map +1 -0
- package/dist/apps/control-plane/core/kernel.d.ts +17 -48
- package/dist/apps/control-plane/core/kernel.js +44 -539
- package/dist/apps/control-plane/core/kernel.js.map +1 -1
- package/dist/apps/control-plane/core/tool-caller.d.ts +10 -0
- package/dist/apps/control-plane/core/utils/error-normalizer.d.ts +2 -0
- package/dist/apps/control-plane/core/utils/error-normalizer.js +51 -0
- package/dist/apps/control-plane/core/utils/error-normalizer.js.map +1 -0
- package/dist/apps/control-plane/core/utils/field-readers.d.ts +9 -0
- package/dist/apps/control-plane/core/utils/field-readers.js +30 -0
- package/dist/apps/control-plane/core/utils/field-readers.js.map +1 -0
- package/dist/apps/control-plane/core/utils/index-normalizer.d.ts +7 -0
- package/dist/apps/control-plane/core/utils/index-normalizer.js +92 -0
- package/dist/apps/control-plane/core/utils/index-normalizer.js.map +1 -0
- package/dist/apps/control-plane/core/utils/path-normalizers.d.ts +2 -0
- package/dist/apps/control-plane/core/utils/path-normalizers.js +17 -0
- package/dist/apps/control-plane/core/utils/path-normalizers.js.map +1 -0
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js +13 -1
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
- package/dist/apps/control-plane/providers/api-worker-provider.d.ts +4 -13
- package/dist/apps/control-plane/providers/api-worker-provider.js +10 -0
- package/dist/apps/control-plane/providers/api-worker-provider.js.map +1 -1
- package/dist/apps/control-plane/providers/cli-worker-provider.d.ts +11 -13
- package/dist/apps/control-plane/providers/cli-worker-provider.js +64 -0
- package/dist/apps/control-plane/providers/cli-worker-provider.js.map +1 -1
- package/dist/apps/control-plane/providers/providers.d.ts +31 -24
- package/dist/apps/control-plane/providers/providers.js +10 -0
- package/dist/apps/control-plane/providers/providers.js.map +1 -1
- package/dist/apps/control-plane/providers/worker-provider-factory.d.ts +11 -0
- package/dist/apps/control-plane/providers/worker-provider-factory.js +20 -1
- package/dist/apps/control-plane/providers/worker-provider-factory.js.map +1 -1
- package/dist/apps/control-plane/supervisor/run-coordinator.d.ts +3 -0
- package/dist/apps/control-plane/supervisor/run-coordinator.js +81 -33
- package/dist/apps/control-plane/supervisor/run-coordinator.js.map +1 -1
- package/dist/apps/control-plane/supervisor/runtime.d.ts +8 -1
- package/dist/apps/control-plane/supervisor/runtime.js +90 -0
- package/dist/apps/control-plane/supervisor/runtime.js.map +1 -1
- package/dist/apps/control-plane/supervisor/types.d.ts +11 -0
- package/dist/apps/control-plane/supervisor/types.js.map +1 -1
- package/dist/apps/control-plane/supervisor/worker-decision-loop.d.ts +21 -1
- package/dist/apps/control-plane/supervisor/worker-decision-loop.js +207 -13
- package/dist/apps/control-plane/supervisor/worker-decision-loop.js.map +1 -1
- package/package.json +1 -1
- package/packages/web-dashboard/package.json +2 -0
- package/packages/web-dashboard/src/app/analytics/page.tsx +83 -2
- package/packages/web-dashboard/src/app/api/actions/route.ts +92 -1
- package/packages/web-dashboard/src/app/api/analytics/route.ts +5 -2
- package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/[checkpointId]/diff/route.ts +43 -0
- package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/compare/route.ts +45 -0
- package/packages/web-dashboard/src/app/api/features/[id]/checkpoints/stream/route.ts +170 -0
- package/packages/web-dashboard/src/app/api/features/[id]/file-diff/route.ts +144 -0
- package/packages/web-dashboard/src/app/api/features/[id]/log-stream/route.ts +167 -0
- package/packages/web-dashboard/src/app/api/features/[id]/raw-logs/[filename]/route.ts +65 -0
- package/packages/web-dashboard/src/app/api/features/[id]/raw-logs/route.ts +63 -0
- package/packages/web-dashboard/src/app/api/features/[id]/timeline/route.ts +60 -0
- package/packages/web-dashboard/src/app/feature/[id]/page.tsx +32 -11
- package/packages/web-dashboard/src/app/globals.css +2 -0
- package/packages/web-dashboard/src/components/detail-panel.tsx +483 -0
- package/packages/web-dashboard/src/components/review-workspace.tsx +1162 -0
- package/packages/web-dashboard/src/lib/aop-client.ts +725 -0
- package/packages/web-dashboard/src/lib/review-contracts.ts +182 -0
- package/packages/web-dashboard/src/lib/review-workspace-logic.ts +64 -0
- package/packages/web-dashboard/src/lib/types.ts +131 -0
- package/packages/web-dashboard/src/styles/dashboard.module.css +333 -0
- package/spec-files/completed/agentic_orchestrator_execution_mode_spec.md +1905 -0
- package/spec-files/outstanding/agentic_orchestrator_runtime_inspection_spec.md +940 -0
- package/spec-files/outstanding/execution_mode_critical_review.md +355 -0
- package/spec-files/outstanding/shadow_workspace_implementation_spec.md +1271 -0
- package/spec-files/outstanding/shadow_workspace_spec_summary.md +222 -0
- package/spec-files/progress.md +269 -1
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { ensureDir, pathExists, readJson, atomicWriteJson, atomicWriteFile, withFileLock, nowIso,
|
|
4
|
-
import { SchemaRegistry
|
|
5
|
-
import { normalizeRepoPath } from './path-rules.js';
|
|
3
|
+
import { ensureDir, pathExists, readJson, atomicWriteJson, atomicWriteFile, withFileLock, nowIso, } from './fs.js';
|
|
4
|
+
import { SchemaRegistry } from './schemas.js';
|
|
6
5
|
import { parseFrontMatter, buildFrontMatter } from './frontmatter.js';
|
|
7
|
-
import { runGit, runCommand } from './git.js';
|
|
8
6
|
import { ERROR_CODES } from './error-codes.js';
|
|
9
7
|
import { ok, fail, withSuggestedActions } from './response.js';
|
|
10
|
-
import { ALLOWED_ACTORS, DEFAULT_CLUSTER, DEFAULT_ROLE_STATUS, GATE_RESULT, STATUS,
|
|
11
|
-
import { AopPathLayout
|
|
12
|
-
import { applyWorktreeSymlinks, formatWorkspaceHookWarning, runWorktreePostCreate, } from './workspace-hooks.js';
|
|
13
|
-
import { ToolRegistryLoader } from '../mcp/tool-registry-loader.js';
|
|
8
|
+
import { ALLOWED_ACTORS, DEFAULT_CLUSTER, DEFAULT_ROLE_STATUS, GATE_RESULT, STATUS, } from './constants.js';
|
|
9
|
+
import { AopPathLayout } from './path-layout.js';
|
|
14
10
|
import { ToolHandlerRegistry, ToolRouter, } from '../application/tools/tool-router.js';
|
|
15
11
|
import { RunLeaseService, } from '../application/services/run-lease-service.js';
|
|
16
12
|
import { LockService } from '../application/services/lock-service.js';
|
|
@@ -27,52 +23,13 @@ import { FeatureDeletionService, } from '../application/services/feature-deletio
|
|
|
27
23
|
import { CostTrackingService } from '../application/services/cost-tracking-service.js';
|
|
28
24
|
import { PerformanceAnalyticsService, } from '../application/services/performance-analytics-service.js';
|
|
29
25
|
import { GateSelectionService } from '../application/services/gate-selection-service.js';
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return typeof value === 'string' ? value : null;
|
|
38
|
-
}
|
|
39
|
-
function readNumberField(record, key) {
|
|
40
|
-
const value = record[key];
|
|
41
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
42
|
-
}
|
|
43
|
-
function readPositiveIntegerField(record, key) {
|
|
44
|
-
const value = record[key];
|
|
45
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value < 1) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
return Math.floor(value);
|
|
49
|
-
}
|
|
50
|
-
function readBooleanField(record, key) {
|
|
51
|
-
const value = record[key];
|
|
52
|
-
return typeof value === 'boolean' ? value : null;
|
|
53
|
-
}
|
|
54
|
-
function readObjectField(record, key) {
|
|
55
|
-
const value = record[key];
|
|
56
|
-
return value && typeof value === 'object' ? value : {};
|
|
57
|
-
}
|
|
58
|
-
function normalizeSet(array) {
|
|
59
|
-
return [...new Set(array)].sort((a, b) => a.localeCompare(b));
|
|
60
|
-
}
|
|
61
|
-
function validateAgentRuntimeTimeoutRelationships(agentsConfig) {
|
|
62
|
-
const runtime = readObjectField(agentsConfig, 'runtime');
|
|
63
|
-
if (Object.keys(runtime).length === 0) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const responseTimeoutMs = readPositiveIntegerField(runtime, 'worker_response_timeout_ms');
|
|
67
|
-
const spawnTimeoutMs = readPositiveIntegerField(runtime, 'worker_spawn_timeout_ms');
|
|
68
|
-
const idleTimeoutMs = readPositiveIntegerField(runtime, 'worker_idle_timeout_ms');
|
|
69
|
-
if (responseTimeoutMs != null && spawnTimeoutMs != null && spawnTimeoutMs >= responseTimeoutMs) {
|
|
70
|
-
throw new Error('invalid_agents_yaml:runtime.worker_spawn_timeout_ms must be less than runtime.worker_response_timeout_ms');
|
|
71
|
-
}
|
|
72
|
-
if (responseTimeoutMs != null && idleTimeoutMs != null && idleTimeoutMs > responseTimeoutMs) {
|
|
73
|
-
throw new Error('invalid_agents_yaml:runtime.worker_idle_timeout_ms must be less than or equal to runtime.worker_response_timeout_ms');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
26
|
+
import { readStringField } from './utils/field-readers.js';
|
|
27
|
+
import { emptyRuntimeSessions as utilEmptyRuntimeSessions, normalizeRuntimeSessions as utilNormalizeRuntimeSessions, normalizeIndexShape as utilNormalizeIndexShape, isRunLeaseFresh as utilIsRunLeaseFresh, } from './utils/index-normalizer.js';
|
|
28
|
+
import { normalizeRepoPathForState } from './utils/path-normalizers.js';
|
|
29
|
+
import { ConfigurationService } from '../application/configuration-service.js';
|
|
30
|
+
import { RepoOperationsService } from '../application/services/repo-operations-service.js';
|
|
31
|
+
import { FeatureSendMessageService } from '../application/services/feature-send-message-service.js';
|
|
32
|
+
import { registerKernelTools } from '../application/kernel-tool-wiring.js';
|
|
76
33
|
/**
|
|
77
34
|
* Deterministic orchestration kernel for multi-agent feature development.
|
|
78
35
|
*
|
|
@@ -134,6 +91,8 @@ export class AopKernel {
|
|
|
134
91
|
costTrackingService;
|
|
135
92
|
performanceAnalyticsService;
|
|
136
93
|
gateSelectionService;
|
|
94
|
+
repoOperationsService;
|
|
95
|
+
sendMessageService;
|
|
137
96
|
pathLayout;
|
|
138
97
|
instanceId;
|
|
139
98
|
provider = null;
|
|
@@ -158,7 +117,7 @@ export class AopKernel {
|
|
|
158
117
|
this.adaptersConfig = {};
|
|
159
118
|
this.toolRegistry = null;
|
|
160
119
|
this.toolHandlers = new ToolHandlerRegistry();
|
|
161
|
-
this.
|
|
120
|
+
registerKernelTools(this.toolHandlers, this);
|
|
162
121
|
this.runLeaseService = new RunLeaseService(this);
|
|
163
122
|
this.collisionQueueService = new CollisionQueueService(this);
|
|
164
123
|
this.lockService = new LockService(this);
|
|
@@ -174,6 +133,12 @@ export class AopKernel {
|
|
|
174
133
|
this.costTrackingService = new CostTrackingService(this);
|
|
175
134
|
this.performanceAnalyticsService = new PerformanceAnalyticsService(this);
|
|
176
135
|
this.gateSelectionService = new GateSelectionService(this);
|
|
136
|
+
this.repoOperationsService = new RepoOperationsService(this);
|
|
137
|
+
this.sendMessageService = new FeatureSendMessageService({
|
|
138
|
+
readState: (id) => this.readState(id),
|
|
139
|
+
getRuntimeSessions: () => this.getRuntimeSessions(),
|
|
140
|
+
getProvider: () => this.provider,
|
|
141
|
+
});
|
|
177
142
|
this.toolRouter = new ToolRouter(this.toolHandlers, (toolName) => Promise.resolve(fail(ERROR_CODES.INVALID_ARGUMENT, `Unknown tool ${toolName}`, {
|
|
178
143
|
retryable: false,
|
|
179
144
|
requires_human: true,
|
|
@@ -223,180 +188,25 @@ export class AopKernel {
|
|
|
223
188
|
return Math.floor(configured);
|
|
224
189
|
}
|
|
225
190
|
emptyRuntimeSessions(at = nowIso()) {
|
|
226
|
-
return
|
|
227
|
-
run_id: 'none',
|
|
228
|
-
orchestrator_session_id: 'unknown',
|
|
229
|
-
provider: 'unknown',
|
|
230
|
-
model: 'unknown',
|
|
231
|
-
provider_config_ref_hash: stableHash('none'),
|
|
232
|
-
owner_instance_id: 'none',
|
|
233
|
-
lease_id: 'none',
|
|
234
|
-
started_at: at,
|
|
235
|
-
last_heartbeat_at: at,
|
|
236
|
-
lease_expires_at: at,
|
|
237
|
-
orchestrator_epoch: 0,
|
|
238
|
-
feature_sessions: {},
|
|
239
|
-
};
|
|
191
|
+
return utilEmptyRuntimeSessions(at);
|
|
240
192
|
}
|
|
241
193
|
normalizeRuntimeSessions(value, at = nowIso()) {
|
|
242
|
-
|
|
243
|
-
const source = value && typeof value === 'object' ? value : {};
|
|
244
|
-
const featureSessionsInput = source.feature_sessions && typeof source.feature_sessions === 'object'
|
|
245
|
-
? source.feature_sessions
|
|
246
|
-
: {};
|
|
247
|
-
const featureSessions = {};
|
|
248
|
-
for (const [featureId, raw] of Object.entries(featureSessionsInput)) {
|
|
249
|
-
if (!featureId || typeof raw !== 'object' || !raw) {
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
const typed = raw;
|
|
253
|
-
featureSessions[featureId] = {
|
|
254
|
-
planner_session_id: typeof typed.planner_session_id === 'string' ? typed.planner_session_id : 'unassigned',
|
|
255
|
-
builder_session_id: typeof typed.builder_session_id === 'string' ? typed.builder_session_id : 'unassigned',
|
|
256
|
-
qa_session_id: typeof typed.qa_session_id === 'string' ? typed.qa_session_id : 'unassigned',
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
const epoch = typeof source.orchestrator_epoch === 'number' && Number.isFinite(source.orchestrator_epoch)
|
|
260
|
-
? Math.max(0, Math.floor(source.orchestrator_epoch))
|
|
261
|
-
: 0;
|
|
262
|
-
return {
|
|
263
|
-
run_id: typeof source.run_id === 'string' && source.run_id ? source.run_id : fallback.run_id,
|
|
264
|
-
orchestrator_session_id: typeof source.orchestrator_session_id === 'string' && source.orchestrator_session_id
|
|
265
|
-
? source.orchestrator_session_id
|
|
266
|
-
: fallback.orchestrator_session_id,
|
|
267
|
-
provider: typeof source.provider === 'string' && source.provider
|
|
268
|
-
? source.provider
|
|
269
|
-
: fallback.provider,
|
|
270
|
-
model: typeof source.model === 'string' && source.model ? source.model : fallback.model,
|
|
271
|
-
provider_config_ref_hash: typeof source.provider_config_ref_hash === 'string' && source.provider_config_ref_hash
|
|
272
|
-
? source.provider_config_ref_hash
|
|
273
|
-
: fallback.provider_config_ref_hash,
|
|
274
|
-
owner_instance_id: typeof source.owner_instance_id === 'string' && source.owner_instance_id
|
|
275
|
-
? source.owner_instance_id
|
|
276
|
-
: fallback.owner_instance_id,
|
|
277
|
-
lease_id: typeof source.lease_id === 'string' && source.lease_id
|
|
278
|
-
? source.lease_id
|
|
279
|
-
: fallback.lease_id,
|
|
280
|
-
started_at: typeof source.started_at === 'string' && source.started_at
|
|
281
|
-
? source.started_at
|
|
282
|
-
: fallback.started_at,
|
|
283
|
-
last_heartbeat_at: typeof source.last_heartbeat_at === 'string' && source.last_heartbeat_at
|
|
284
|
-
? source.last_heartbeat_at
|
|
285
|
-
: fallback.last_heartbeat_at,
|
|
286
|
-
lease_expires_at: typeof source.lease_expires_at === 'string' && source.lease_expires_at
|
|
287
|
-
? source.lease_expires_at
|
|
288
|
-
: fallback.lease_expires_at,
|
|
289
|
-
orchestrator_epoch: epoch,
|
|
290
|
-
feature_sessions: featureSessions,
|
|
291
|
-
};
|
|
194
|
+
return utilNormalizeRuntimeSessions(value, at);
|
|
292
195
|
}
|
|
293
196
|
normalizeIndexShape(value) {
|
|
294
|
-
|
|
295
|
-
const source = value && typeof value === 'object' ? value : {};
|
|
296
|
-
return {
|
|
297
|
-
version: typeof source.version === 'number' && Number.isFinite(source.version)
|
|
298
|
-
? Math.max(1, Math.floor(source.version))
|
|
299
|
-
: 1,
|
|
300
|
-
active: normalizeSet(asArray(source.active).filter((item) => typeof item === 'string')),
|
|
301
|
-
blocked: normalizeSet(asArray(source.blocked).filter((item) => typeof item === 'string')),
|
|
302
|
-
merged: normalizeSet(asArray(source.merged).filter((item) => typeof item === 'string')),
|
|
303
|
-
locks: source.locks && typeof source.locks === 'object' ? source.locks : {},
|
|
304
|
-
lock_leases: source.lock_leases && typeof source.lock_leases === 'object' ? source.lock_leases : {},
|
|
305
|
-
blocked_queue: asArray(source.blocked_queue).filter((item) => item && typeof item === 'object'),
|
|
306
|
-
dep_blocked: asArray(source.dep_blocked).filter((item) => item && typeof item === 'object'),
|
|
307
|
-
updated_at: typeof source.updated_at === 'string' && source.updated_at ? source.updated_at : now,
|
|
308
|
-
runtime_sessions: this.normalizeRuntimeSessions(source.runtime_sessions, now),
|
|
309
|
-
};
|
|
197
|
+
return utilNormalizeIndexShape(value);
|
|
310
198
|
}
|
|
311
199
|
isRunLeaseFresh(runtimeSessions) {
|
|
312
|
-
|
|
313
|
-
if (!Number.isFinite(expiry)) {
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
return expiry > Date.now();
|
|
317
|
-
}
|
|
318
|
-
async resolveDefaultConfigPath(fileName) {
|
|
319
|
-
const primary = path.join(this.pathLayout.orchestratorRoot, fileName);
|
|
320
|
-
if (await pathExists(primary)) {
|
|
321
|
-
return primary;
|
|
322
|
-
}
|
|
323
|
-
const legacy = path.join(this.pathLayout.legacyOrchestratorRoot, fileName);
|
|
324
|
-
if (await pathExists(legacy)) {
|
|
325
|
-
return legacy;
|
|
326
|
-
}
|
|
327
|
-
return primary;
|
|
200
|
+
return utilIsRunLeaseFresh(runtimeSessions);
|
|
328
201
|
}
|
|
329
202
|
async load() {
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
throw new Error(`invalid_gates_yaml:${JSON.stringify(gates.validation.errors)}`);
|
|
338
|
-
}
|
|
339
|
-
const { mergedPolicy } = await loadComposedPolicy(this.repoRoot, policyPath, this.schemaRegistry);
|
|
340
|
-
const parsedPolicy = mergedPolicy;
|
|
341
|
-
const implementation = readObjectField(parsedPolicy, 'implementation');
|
|
342
|
-
const testing = readObjectField(parsedPolicy, 'testing');
|
|
343
|
-
if (readStringField(implementation, 'workspace') !== 'nx') {
|
|
344
|
-
throw new Error(ERROR_CODES.INVALID_WORKSPACE_IMPLEMENTATION);
|
|
345
|
-
}
|
|
346
|
-
if (readStringField(testing, 'framework') !== 'vitest') {
|
|
347
|
-
throw new Error(ERROR_CODES.INVALID_WORKSPACE_IMPLEMENTATION);
|
|
348
|
-
}
|
|
349
|
-
const agentsExists = await pathExists(agentsPath);
|
|
350
|
-
let agents = { parsed: { version: 1, roles: {} }, validation: { valid: true, errors: [] } };
|
|
351
|
-
if (agentsExists) {
|
|
352
|
-
agents = await loadAndValidateYaml(this.schemaRegistry, 'agents.schema.json', agentsPath);
|
|
353
|
-
if (!agents.validation.valid) {
|
|
354
|
-
throw new Error(`invalid_agents_yaml:${JSON.stringify(agents.validation.errors)}`);
|
|
355
|
-
}
|
|
356
|
-
validateAgentRuntimeTimeoutRelationships(agents.parsed);
|
|
357
|
-
}
|
|
358
|
-
const adaptersExists = await pathExists(adaptersPath);
|
|
359
|
-
let adapters = { parsed: {}, validation: { valid: true, errors: [] } };
|
|
360
|
-
if (adaptersExists) {
|
|
361
|
-
adapters = await loadAndValidateYaml(this.schemaRegistry, 'adapters.schema.json', adaptersPath);
|
|
362
|
-
if (!adapters.validation.valid) {
|
|
363
|
-
throw new Error(`invalid_adapters_yaml:${JSON.stringify(adapters.validation.errors)}`);
|
|
364
|
-
}
|
|
365
|
-
const parsedAdapters = readObjectField(adapters, 'parsed');
|
|
366
|
-
const notificationChannel = readStringField(parsedAdapters, NOTIFICATION_CHANNEL_SLOT.name);
|
|
367
|
-
if (notificationChannel) {
|
|
368
|
-
try {
|
|
369
|
-
globalAdapterRegistry.resolve(NOTIFICATION_CHANNEL_SLOT, notificationChannel, {});
|
|
370
|
-
}
|
|
371
|
-
catch {
|
|
372
|
-
throw new Error(`adapter_not_found:${NOTIFICATION_CHANNEL_SLOT.name}:${notificationChannel}`);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
const activityDetector = readStringField(parsedAdapters, ACTIVITY_DETECTOR_SLOT.name);
|
|
376
|
-
if (activityDetector) {
|
|
377
|
-
try {
|
|
378
|
-
globalAdapterRegistry.resolve(ACTIVITY_DETECTOR_SLOT, activityDetector, {});
|
|
379
|
-
}
|
|
380
|
-
catch {
|
|
381
|
-
throw new Error(`adapter_not_found:${ACTIVITY_DETECTOR_SLOT.name}:${activityDetector}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
const scmProvider = readStringField(parsedAdapters, SCM_PROVIDER_SLOT.name);
|
|
385
|
-
if (scmProvider) {
|
|
386
|
-
try {
|
|
387
|
-
globalAdapterRegistry.resolve(SCM_PROVIDER_SLOT, scmProvider, {});
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
throw new Error(`adapter_not_found:${SCM_PROVIDER_SLOT.name}:${scmProvider}`);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
this.gatesConfig = gates.parsed;
|
|
395
|
-
this.policy = parsedPolicy;
|
|
396
|
-
this.agentsConfig = agents.parsed;
|
|
397
|
-
this.adaptersConfig = readObjectField(adapters, 'parsed');
|
|
398
|
-
const registryLoader = new ToolRegistryLoader(this.repoRoot);
|
|
399
|
-
this.toolRegistry = await registryLoader.load();
|
|
203
|
+
const configService = new ConfigurationService(this.repoRoot, this.pathLayout, this.schemaRegistry, this.configOverrides);
|
|
204
|
+
const config = await configService.loadAll();
|
|
205
|
+
this.gatesConfig = config.gatesConfig;
|
|
206
|
+
this.policy = config.policy;
|
|
207
|
+
this.agentsConfig = config.agentsConfig;
|
|
208
|
+
this.adaptersConfig = config.adaptersConfig;
|
|
209
|
+
this.toolRegistry = config.toolRegistry;
|
|
400
210
|
this.loaded = true;
|
|
401
211
|
}
|
|
402
212
|
async ensureLoaded() {
|
|
@@ -480,41 +290,6 @@ export class AopKernel {
|
|
|
480
290
|
async dispatchTool(toolName, args, context) {
|
|
481
291
|
return await this.toolRouter.route(toolName, args, context);
|
|
482
292
|
}
|
|
483
|
-
registerToolHandlers() {
|
|
484
|
-
this.toolHandlers.register(TOOLS.FEATURE_DISCOVER_SPECS, async () => await this.featureDiscoverSpecs());
|
|
485
|
-
this.toolHandlers.register(TOOLS.FEATURE_INIT, async (args) => await this.featureInit(readStringField(args, 'feature_id')));
|
|
486
|
-
this.toolHandlers.register(TOOLS.FEATURE_GET_CONTEXT, async (args) => await this.featureGetContext(readStringField(args, 'feature_id')));
|
|
487
|
-
this.toolHandlers.register(TOOLS.FEATURE_STATE_GET, async (args) => await this.featureStateGet(readStringField(args, 'feature_id')));
|
|
488
|
-
this.toolHandlers.register(TOOLS.FEATURE_STATE_PATCH, async (args) => await this.featureStatePatch(readStringField(args, 'feature_id'), readNumberField(args, 'expected_version'), args.patch));
|
|
489
|
-
this.toolHandlers.register(TOOLS.FEATURE_LOG_APPEND, async (args, context) => await this.featureLogAppend(readStringField(args, 'feature_id'), readStringField(args, 'note'), context));
|
|
490
|
-
this.toolHandlers.register(TOOLS.PLAN_SUBMIT, async (args) => await this.planSubmit(readStringField(args, 'feature_id'), args.plan_json, readNumberField(args, 'expected_version')));
|
|
491
|
-
this.toolHandlers.register(TOOLS.PLAN_GET, async (args) => await this.planGet(readStringField(args, 'feature_id')));
|
|
492
|
-
this.toolHandlers.register(TOOLS.PLAN_UPDATE, async (args) => await this.planUpdate(readStringField(args, 'feature_id'), readNumberField(args, 'expected_plan_version'), args.plan_json));
|
|
493
|
-
this.toolHandlers.register(TOOLS.REPO_ENSURE_WORKTREE, async (args) => await this.repoEnsureWorktree(readStringField(args, 'feature_id')));
|
|
494
|
-
this.toolHandlers.register(TOOLS.REPO_APPLY_PATCH, async (args) => await this.repoApplyPatch(readStringField(args, 'feature_id'), readStringField(args, 'unified_diff')));
|
|
495
|
-
this.toolHandlers.register(TOOLS.REPO_STATUS, async (args) => await this.repoStatus(readStringField(args, 'feature_id')));
|
|
496
|
-
this.toolHandlers.register(TOOLS.REPO_DIFF, async (args) => await this.repoDiff(readStringField(args, 'feature_id'), asArray(args.options)));
|
|
497
|
-
this.toolHandlers.register(TOOLS.REPO_READ_FILE, async (args) => await this.repoReadFile(readStringField(args, 'feature_id'), readStringField(args, 'path')));
|
|
498
|
-
this.toolHandlers.register(TOOLS.REPO_SEARCH, async (args) => await this.repoSearch(readStringField(args, 'feature_id'), readStringField(args, 'query')));
|
|
499
|
-
this.toolHandlers.register(TOOLS.REPO_DIFF_BUNDLE, async (args) => await this.repoDiffBundle(readStringField(args, 'feature_id')));
|
|
500
|
-
this.toolHandlers.register(TOOLS.FEATURE_READY_TO_MERGE, async (args) => await this.featureReadyToMerge(readStringField(args, 'feature_id'), readStringField(args, 'commit_message'), readStringField(args, 'merge_strategy'), readStringField(args, 'user_approval_token')));
|
|
501
|
-
this.toolHandlers.register(TOOLS.FEATURE_DELETE, async (args) => await this.featureDelete(readStringField(args, 'feature_id'), readBooleanField(args, 'dry_run'), readBooleanField(args, 'confirm'), readBooleanField(args, 'remove_worktree'), readStringField(args, 'remove_branch')));
|
|
502
|
-
this.toolHandlers.register(TOOLS.GATES_LIST, async (args) => await this.gatesList(readStringField(args, 'profile')));
|
|
503
|
-
this.toolHandlers.register(TOOLS.GATES_RUN, async (args) => await this.gatesRun(readStringField(args, 'feature_id'), readStringField(args, 'profile'), readStringField(args, 'mode')));
|
|
504
|
-
this.toolHandlers.register(TOOLS.EVIDENCE_LATEST, async (args) => await this.evidenceLatest(readStringField(args, 'feature_id')));
|
|
505
|
-
this.toolHandlers.register(TOOLS.QA_TEST_INDEX_GET, async (args) => await this.qaTestIndexGet(readStringField(args, 'feature_id')));
|
|
506
|
-
this.toolHandlers.register(TOOLS.QA_TEST_INDEX_UPDATE, async (args) => await this.qaTestIndexUpdate(readStringField(args, 'feature_id'), readNumberField(args, 'expected_version'), args.updates, asArray(args.evidence_refs)));
|
|
507
|
-
this.toolHandlers.register(TOOLS.LOCKS_ACQUIRE, async (args) => await this.locksAcquire(readStringField(args, 'resource'), readStringField(args, 'feature_id'), readNumberField(args, 'wait_timeout_seconds')));
|
|
508
|
-
this.toolHandlers.register(TOOLS.LOCKS_RELEASE, async (args) => await this.locksRelease(readStringField(args, 'resource'), readStringField(args, 'feature_id')));
|
|
509
|
-
this.toolHandlers.register(TOOLS.COLLISIONS_SCAN, async () => await this.collisionsScan());
|
|
510
|
-
this.toolHandlers.register(TOOLS.REPORT_DASHBOARD, async () => await this.reportDashboard());
|
|
511
|
-
this.toolHandlers.register(TOOLS.REPORT_FEATURE_SUMMARY, async (args) => await this.reportFeatureSummary(readStringField(args, 'feature_id')));
|
|
512
|
-
this.toolHandlers.register(TOOLS.FEATURE_SEND_MESSAGE, async (args) => await this.featureSendMessage(readStringField(args, 'feature_id'), readStringField(args, 'message')));
|
|
513
|
-
this.toolHandlers.register(TOOLS.COST_RECORD, async (args) => await this.costRecord(readStringField(args, 'feature_id'), typeof args.tokens_used_delta === 'number' ? args.tokens_used_delta : 0, typeof args.estimated_cost_usd_delta === 'number' ? args.estimated_cost_usd_delta : 0));
|
|
514
|
-
this.toolHandlers.register(TOOLS.COST_GET, async (args) => await this.costGet(readStringField(args, 'feature_id')));
|
|
515
|
-
this.toolHandlers.register(TOOLS.PERFORMANCE_RECORD_OUTCOME, async (args) => await this.performanceRecordOutcome(args));
|
|
516
|
-
this.toolHandlers.register(TOOLS.PERFORMANCE_GET_ANALYTICS, async (args) => await this.performanceGetAnalytics(readStringField(args, 'provider'), readStringField(args, 'model')));
|
|
517
|
-
}
|
|
518
293
|
featurePath(featureId) {
|
|
519
294
|
return this.pathLayout.featureRoot(featureId);
|
|
520
295
|
}
|
|
@@ -562,12 +337,14 @@ export class AopKernel {
|
|
|
562
337
|
}
|
|
563
338
|
}
|
|
564
339
|
makeDefaultState(featureId, branch, worktreePath) {
|
|
340
|
+
const configuredExecutionMode = this.agentsConfig.runtime?.execution_mode === 'interactive' ? 'interactive' : 'deterministic';
|
|
565
341
|
return {
|
|
566
342
|
feature_id: featureId,
|
|
567
343
|
version: 1,
|
|
568
344
|
branch,
|
|
569
345
|
worktree_path: normalizeRepoPathForState(this.repoRoot, worktreePath),
|
|
570
346
|
status: STATUS.PLANNING,
|
|
347
|
+
execution_mode: configuredExecutionMode,
|
|
571
348
|
gate_profile: 'default',
|
|
572
349
|
gates: {
|
|
573
350
|
plan: GATE_RESULT.NA,
|
|
@@ -585,6 +362,7 @@ export class AopKernel {
|
|
|
585
362
|
},
|
|
586
363
|
cluster: { ...DEFAULT_CLUSTER },
|
|
587
364
|
role_status: { ...DEFAULT_ROLE_STATUS },
|
|
365
|
+
checkpoints: [],
|
|
588
366
|
last_updated: nowIso(),
|
|
589
367
|
};
|
|
590
368
|
}
|
|
@@ -776,76 +554,7 @@ export class AopKernel {
|
|
|
776
554
|
return await this.planService.planUpdate(featureId, expectedPlanVersion, plan);
|
|
777
555
|
}
|
|
778
556
|
async repoEnsureWorktree(featureId) {
|
|
779
|
-
|
|
780
|
-
const branch = featureId;
|
|
781
|
-
await ensureDir(path.join(this.repoRoot, '.worktrees'));
|
|
782
|
-
if (await pathExists(worktree)) {
|
|
783
|
-
return {
|
|
784
|
-
data: {
|
|
785
|
-
feature_id: featureId,
|
|
786
|
-
branch,
|
|
787
|
-
worktree_path_abs: worktree,
|
|
788
|
-
existed: true,
|
|
789
|
-
},
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
const baseBranch = this.policy.worktree.base_branch;
|
|
793
|
-
const baseCheck = await runGit(this.repoRoot, ['rev-parse', '--verify', baseBranch]);
|
|
794
|
-
const baseRef = baseCheck.code === 0 ? baseBranch : 'HEAD';
|
|
795
|
-
const branchCheck = await runGit(this.repoRoot, ['rev-parse', '--verify', branch]);
|
|
796
|
-
if (branchCheck.code !== 0) {
|
|
797
|
-
const branchCreate = await runGit(this.repoRoot, ['branch', branch, baseRef]);
|
|
798
|
-
if (branchCreate.code !== 0) {
|
|
799
|
-
throw {
|
|
800
|
-
normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Unable to create feature branch', {
|
|
801
|
-
feature_id: featureId,
|
|
802
|
-
stderr: branchCreate.stderr,
|
|
803
|
-
retryable: false,
|
|
804
|
-
requires_human: true,
|
|
805
|
-
}, {
|
|
806
|
-
command: ['git', 'branch', branch, baseRef],
|
|
807
|
-
exit_code: branchCreate.code,
|
|
808
|
-
}),
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
const addWorktree = await runGit(this.repoRoot, ['worktree', 'add', worktree, branch]);
|
|
813
|
-
if (addWorktree.code !== 0) {
|
|
814
|
-
throw {
|
|
815
|
-
normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Unable to create git worktree', {
|
|
816
|
-
feature_id: featureId,
|
|
817
|
-
stderr: addWorktree.stderr,
|
|
818
|
-
retryable: false,
|
|
819
|
-
requires_human: true,
|
|
820
|
-
}, {
|
|
821
|
-
command: ['git', 'worktree', 'add', worktree, branch],
|
|
822
|
-
exit_code: addWorktree.code,
|
|
823
|
-
}),
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
const worktreeConfig = this.policy.worktree;
|
|
827
|
-
const hookWarnings = [];
|
|
828
|
-
const collectHookWarning = (warning) => {
|
|
829
|
-
hookWarnings.push(warning);
|
|
830
|
-
};
|
|
831
|
-
if (worktreeConfig?.symlinks?.length) {
|
|
832
|
-
await applyWorktreeSymlinks(this.repoRoot, worktree, worktreeConfig.symlinks, collectHookWarning);
|
|
833
|
-
}
|
|
834
|
-
if (worktreeConfig?.post_create?.length) {
|
|
835
|
-
await runWorktreePostCreate(worktree, worktreeConfig.post_create, collectHookWarning);
|
|
836
|
-
}
|
|
837
|
-
for (const warning of hookWarnings) {
|
|
838
|
-
// Preserve non-fatal behavior while making hook failures observable.
|
|
839
|
-
console.warn(`[aop] workspace hook warning: ${formatWorkspaceHookWarning(warning)}`);
|
|
840
|
-
}
|
|
841
|
-
return {
|
|
842
|
-
data: {
|
|
843
|
-
feature_id: featureId,
|
|
844
|
-
branch,
|
|
845
|
-
worktree_path_abs: worktree,
|
|
846
|
-
existed: false,
|
|
847
|
-
},
|
|
848
|
-
};
|
|
557
|
+
return this.repoOperationsService.repoEnsureWorktree(featureId);
|
|
849
558
|
}
|
|
850
559
|
async loadAcceptedPlan(featureId) {
|
|
851
560
|
return await this.patchService.loadAcceptedPlan(featureId);
|
|
@@ -856,123 +565,23 @@ export class AopKernel {
|
|
|
856
565
|
async repoApplyPatch(featureId, unifiedDiff) {
|
|
857
566
|
return await this.patchService.repoApplyPatch(featureId, unifiedDiff);
|
|
858
567
|
}
|
|
568
|
+
async validatePatchDiff(featureId, parsedDiff) {
|
|
569
|
+
return await this.patchService.validateDiff(featureId, parsedDiff);
|
|
570
|
+
}
|
|
859
571
|
async repoStatus(featureId) {
|
|
860
|
-
|
|
861
|
-
const status = await runGit(this.repoRoot, ['status', '--porcelain'], { cwd: worktree });
|
|
862
|
-
const branch = await runGit(this.repoRoot, ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
863
|
-
cwd: worktree,
|
|
864
|
-
});
|
|
865
|
-
return {
|
|
866
|
-
data: {
|
|
867
|
-
feature_id: featureId,
|
|
868
|
-
branch: branch.stdout.trim(),
|
|
869
|
-
status_porcelain: status.stdout.trim().split('\n').filter(Boolean),
|
|
870
|
-
},
|
|
871
|
-
};
|
|
572
|
+
return this.repoOperationsService.repoStatus(featureId);
|
|
872
573
|
}
|
|
873
574
|
async repoDiff(featureId, options = []) {
|
|
874
|
-
|
|
875
|
-
const diff = await runGit(this.repoRoot, ['diff', ...safeOptions], {
|
|
876
|
-
cwd: this.worktreePath(featureId),
|
|
877
|
-
});
|
|
878
|
-
return {
|
|
879
|
-
data: {
|
|
880
|
-
feature_id: featureId,
|
|
881
|
-
diff: diff.stdout,
|
|
882
|
-
},
|
|
883
|
-
};
|
|
575
|
+
return this.repoOperationsService.repoDiff(featureId, options);
|
|
884
576
|
}
|
|
885
577
|
async repoReadFile(featureId, filePath) {
|
|
886
|
-
|
|
887
|
-
const absolute = path.join(this.repoRoot, normalized);
|
|
888
|
-
const exists = await pathExists(absolute);
|
|
889
|
-
if (!exists) {
|
|
890
|
-
throw {
|
|
891
|
-
normalizedResponse: fail(ERROR_CODES.FILE_NOT_FOUND, 'File not found', {
|
|
892
|
-
path: normalized,
|
|
893
|
-
retryable: false,
|
|
894
|
-
requires_human: false,
|
|
895
|
-
}),
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
const content = await fs.readFile(absolute, 'utf8');
|
|
899
|
-
return {
|
|
900
|
-
data: {
|
|
901
|
-
feature_id: featureId,
|
|
902
|
-
path: normalized,
|
|
903
|
-
content,
|
|
904
|
-
},
|
|
905
|
-
};
|
|
578
|
+
return this.repoOperationsService.repoReadFile(featureId, filePath);
|
|
906
579
|
}
|
|
907
580
|
async repoSearch(featureId, query) {
|
|
908
|
-
|
|
909
|
-
const rgResult = await runCommand('rg', ['-n', '--no-heading', query, '.'], {
|
|
910
|
-
cwd: worktree,
|
|
911
|
-
timeoutMs: 30_000,
|
|
912
|
-
});
|
|
913
|
-
if (rgResult.code === 127) {
|
|
914
|
-
throw {
|
|
915
|
-
normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'ripgrep (rg) not found - required for search functionality', {
|
|
916
|
-
stderr: rgResult.stderr,
|
|
917
|
-
retryable: false,
|
|
918
|
-
requires_human: true,
|
|
919
|
-
}),
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
if (rgResult.code !== 0 && rgResult.code !== 1) {
|
|
923
|
-
throw {
|
|
924
|
-
normalizedResponse: fail(ERROR_CODES.GIT_FAILURE, 'Search failed', {
|
|
925
|
-
stderr: rgResult.stderr,
|
|
926
|
-
retryable: true,
|
|
927
|
-
requires_human: false,
|
|
928
|
-
}),
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
const matches = rgResult.stdout
|
|
932
|
-
.trim()
|
|
933
|
-
.split('\n')
|
|
934
|
-
.filter(Boolean)
|
|
935
|
-
.map((line) => {
|
|
936
|
-
const firstColon = line.indexOf(':');
|
|
937
|
-
const secondColon = line.indexOf(':', firstColon + 1);
|
|
938
|
-
if (firstColon === -1 || secondColon === -1) {
|
|
939
|
-
return { raw: line };
|
|
940
|
-
}
|
|
941
|
-
return {
|
|
942
|
-
path: line.slice(0, firstColon),
|
|
943
|
-
line: Number(line.slice(firstColon + 1, secondColon)),
|
|
944
|
-
snippet: line.slice(secondColon + 1),
|
|
945
|
-
};
|
|
946
|
-
});
|
|
947
|
-
return {
|
|
948
|
-
data: {
|
|
949
|
-
feature_id: featureId,
|
|
950
|
-
query,
|
|
951
|
-
matches,
|
|
952
|
-
},
|
|
953
|
-
};
|
|
581
|
+
return this.repoOperationsService.repoSearch(featureId, query);
|
|
954
582
|
}
|
|
955
583
|
async repoDiffBundle(featureId) {
|
|
956
|
-
|
|
957
|
-
cwd: this.worktreePath(featureId),
|
|
958
|
-
});
|
|
959
|
-
const full = await runGit(this.repoRoot, ['diff'], { cwd: this.worktreePath(featureId) });
|
|
960
|
-
const names = await runGit(this.repoRoot, ['diff', '--name-only'], {
|
|
961
|
-
cwd: this.worktreePath(featureId),
|
|
962
|
-
});
|
|
963
|
-
const latest = await this.evidenceLatest(featureId);
|
|
964
|
-
return {
|
|
965
|
-
data: {
|
|
966
|
-
feature_id: featureId,
|
|
967
|
-
diff_stat: stat.stdout,
|
|
968
|
-
diff: full.stdout,
|
|
969
|
-
touched_files: names.stdout
|
|
970
|
-
.split('\n')
|
|
971
|
-
.map((x) => x.trim())
|
|
972
|
-
.filter(Boolean),
|
|
973
|
-
last_gate_summary: latest.data?.latest ?? null,
|
|
974
|
-
},
|
|
975
|
-
};
|
|
584
|
+
return this.repoOperationsService.repoDiffBundle(featureId);
|
|
976
585
|
}
|
|
977
586
|
async gatesList(profileName = null) {
|
|
978
587
|
return await this.gateService.gatesList(profileName);
|
|
@@ -1024,97 +633,8 @@ export class AopKernel {
|
|
|
1024
633
|
async recoverFromState() {
|
|
1025
634
|
return await this.lockService.recoverFromState();
|
|
1026
635
|
}
|
|
1027
|
-
async waitForSessionToBecomeActive(sessionId) {
|
|
1028
|
-
if (!this.provider?.getSessionInfo) {
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
const timeoutMs = 5000;
|
|
1032
|
-
const pollIntervalMs = 250;
|
|
1033
|
-
const deadline = Date.now() + timeoutMs;
|
|
1034
|
-
while (Date.now() <= deadline) {
|
|
1035
|
-
const sessionInfo = await this.provider.getSessionInfo(sessionId).catch(() => null);
|
|
1036
|
-
if (sessionInfo?.active) {
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
await new Promise((resolve) => {
|
|
1040
|
-
setTimeout(resolve, pollIntervalMs);
|
|
1041
|
-
});
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
636
|
async featureSendMessage(featureId, message) {
|
|
1045
|
-
|
|
1046
|
-
throw {
|
|
1047
|
-
normalizedResponse: fail(ERROR_CODES.INVALID_ARGUMENT, 'feature_id is required', {
|
|
1048
|
-
retryable: false,
|
|
1049
|
-
requires_human: false,
|
|
1050
|
-
}),
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
if (!message) {
|
|
1054
|
-
throw {
|
|
1055
|
-
normalizedResponse: fail(ERROR_CODES.INVALID_ARGUMENT, 'message is required and must not be empty', { retryable: false, requires_human: false }),
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
const runtimeSessions = await this.getRuntimeSessions();
|
|
1059
|
-
const featureSession = runtimeSessions.feature_sessions?.[featureId];
|
|
1060
|
-
if (!featureSession) {
|
|
1061
|
-
throw {
|
|
1062
|
-
normalizedResponse: {
|
|
1063
|
-
ok: false,
|
|
1064
|
-
error: { code: 'session_not_found', message: 'No active session cluster for feature' },
|
|
1065
|
-
},
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
const state = await this.readState(featureId);
|
|
1069
|
-
const status = typeof state.frontMatter.status === 'string' ? state.frontMatter.status : '';
|
|
1070
|
-
const gates = readObjectField(state.frontMatter, 'gates');
|
|
1071
|
-
let targetRole = 'orchestrator';
|
|
1072
|
-
let targetSessionId = runtimeSessions.orchestrator_session_id;
|
|
1073
|
-
if (status === STATUS.PLANNING) {
|
|
1074
|
-
targetRole = 'planner';
|
|
1075
|
-
targetSessionId = featureSession.planner_session_id;
|
|
1076
|
-
}
|
|
1077
|
-
else if (status === STATUS.BUILDING) {
|
|
1078
|
-
targetRole = 'builder';
|
|
1079
|
-
targetSessionId = featureSession.builder_session_id;
|
|
1080
|
-
}
|
|
1081
|
-
else if (status === STATUS.QA || status === STATUS.READY_TO_MERGE) {
|
|
1082
|
-
targetRole = 'qa';
|
|
1083
|
-
targetSessionId = featureSession.qa_session_id;
|
|
1084
|
-
}
|
|
1085
|
-
else if (status === STATUS.BLOCKED) {
|
|
1086
|
-
const fastGate = readStringField(gates, 'fast');
|
|
1087
|
-
const fullGate = readStringField(gates, 'full');
|
|
1088
|
-
if (fastGate === GATE_RESULT.FAIL && fullGate !== GATE_RESULT.FAIL) {
|
|
1089
|
-
targetRole = 'builder';
|
|
1090
|
-
targetSessionId = featureSession.builder_session_id;
|
|
1091
|
-
}
|
|
1092
|
-
else {
|
|
1093
|
-
targetRole = 'qa';
|
|
1094
|
-
targetSessionId = featureSession.qa_session_id;
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
if (!targetSessionId || targetSessionId === 'unassigned' || targetSessionId === 'unknown') {
|
|
1098
|
-
targetRole = 'orchestrator';
|
|
1099
|
-
targetSessionId = runtimeSessions.orchestrator_session_id;
|
|
1100
|
-
}
|
|
1101
|
-
if (!this.provider?.sendMessage) {
|
|
1102
|
-
throw {
|
|
1103
|
-
normalizedResponse: {
|
|
1104
|
-
ok: false,
|
|
1105
|
-
error: { code: 'provider_unsupported', message: 'Provider does not support sendMessage' },
|
|
1106
|
-
},
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
await this.waitForSessionToBecomeActive(targetSessionId);
|
|
1110
|
-
await this.provider.sendMessage(targetSessionId, message);
|
|
1111
|
-
return {
|
|
1112
|
-
feature_id: featureId,
|
|
1113
|
-
session_id: targetSessionId,
|
|
1114
|
-
target_role: targetRole,
|
|
1115
|
-
status,
|
|
1116
|
-
delivered: true,
|
|
1117
|
-
};
|
|
637
|
+
return this.sendMessageService.featureSendMessage(featureId, message);
|
|
1118
638
|
}
|
|
1119
639
|
async costRecord(featureId, tokensDelta, costUsdDelta) {
|
|
1120
640
|
const updated = await this.costTrackingService.recordCost(featureId, tokensDelta, costUsdDelta);
|
|
@@ -1159,19 +679,4 @@ export class AopKernel {
|
|
|
1159
679
|
return { ok: true, data: snapshot };
|
|
1160
680
|
}
|
|
1161
681
|
}
|
|
1162
|
-
function normalizeRepoPathForState(repoRoot, absolutePath) {
|
|
1163
|
-
const relative = path.relative(repoRoot, absolutePath).replaceAll('\\\\', '/');
|
|
1164
|
-
if (!relative || relative === '.') {
|
|
1165
|
-
return '.';
|
|
1166
|
-
}
|
|
1167
|
-
return relative;
|
|
1168
|
-
}
|
|
1169
|
-
function normalizeFromWorktree(worktreePath, repoRoot, repoRelativeFromWorktree) {
|
|
1170
|
-
const absolute = path.resolve(repoRoot, repoRelativeFromWorktree);
|
|
1171
|
-
const maybeRelativeToWorktree = path.relative(worktreePath, absolute).replaceAll('\\\\', '/');
|
|
1172
|
-
if (!maybeRelativeToWorktree.startsWith('../')) {
|
|
1173
|
-
return maybeRelativeToWorktree;
|
|
1174
|
-
}
|
|
1175
|
-
return path.relative(repoRoot, absolute).replaceAll('\\\\', '/');
|
|
1176
|
-
}
|
|
1177
682
|
//# sourceMappingURL=kernel.js.map
|