nexus-prime 7.9.6 → 7.9.8
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/dist/agents/adapters/mcp/types.d.ts +6 -0
- package/dist/agents/adapters/mcp/types.js +41 -3
- package/dist/agents/adapters/mcp.d.ts +1 -0
- package/dist/agents/adapters/mcp.js +49 -3
- package/dist/cli.js +7 -3
- package/dist/engines/orchestrator/scoring.js +3 -0
- package/dist/engines/orchestrator.js +29 -4
- package/dist/engines/runtime-assets.js +110 -32
- package/dist/engines/token-supremacy.d.ts +1 -1
- package/dist/engines/token-supremacy.js +11 -2
- package/dist/phantom/runtime/worktree.d.ts +6 -1
- package/dist/phantom/runtime/worktree.js +29 -3
- package/dist/phantom/runtime.d.ts +4 -0
- package/dist/phantom/runtime.js +77 -18
- package/package.json +1 -1
|
@@ -35,6 +35,8 @@ export declare class SessionTelemetry {
|
|
|
35
35
|
private fileIntentPaths;
|
|
36
36
|
private tokenAutoApplied;
|
|
37
37
|
private stickyTokenOptimization;
|
|
38
|
+
private tokenOptimizationPending;
|
|
39
|
+
private orchestrationPending;
|
|
38
40
|
bootstrapped: boolean;
|
|
39
41
|
bootstrapCallCount: number;
|
|
40
42
|
recordCall(): void;
|
|
@@ -54,12 +56,16 @@ export declare class SessionTelemetry {
|
|
|
54
56
|
fileReadIntentCount: number;
|
|
55
57
|
callsSinceOrchestrate: number;
|
|
56
58
|
tokenAutoApplied: boolean;
|
|
59
|
+
tokenOptimizationPending: boolean;
|
|
60
|
+
orchestrationPending: boolean;
|
|
57
61
|
};
|
|
58
62
|
elapsedMs(): number;
|
|
59
63
|
advancePhase(nextPhase: LifecyclePhase): void;
|
|
60
64
|
observeSuccessfulToolCall(toolName: string, args: Record<string, unknown>): void;
|
|
65
|
+
observeQueuedToolCall(toolName: string, args: Record<string, unknown>): void;
|
|
61
66
|
needsOptimizeTokens(currentToolName?: string): boolean;
|
|
62
67
|
markTokenAutoApplied(): void;
|
|
68
|
+
needsOrchestration(currentToolName?: string): boolean;
|
|
63
69
|
needsStoreMemory(currentToolName?: string): boolean;
|
|
64
70
|
needsSessionDna(currentToolName?: string): boolean;
|
|
65
71
|
notifyStore(priority: number, tags: string[], memStats: {
|
|
@@ -24,6 +24,8 @@ export class SessionTelemetry {
|
|
|
24
24
|
fileIntentPaths = new Set();
|
|
25
25
|
tokenAutoApplied = false;
|
|
26
26
|
stickyTokenOptimization = false;
|
|
27
|
+
tokenOptimizationPending = false;
|
|
28
|
+
orchestrationPending = false;
|
|
27
29
|
bootstrapped = false;
|
|
28
30
|
bootstrapCallCount = 0;
|
|
29
31
|
recordCall() {
|
|
@@ -49,6 +51,8 @@ export class SessionTelemetry {
|
|
|
49
51
|
fileReadIntentCount: this.fileReadIntentCount,
|
|
50
52
|
callsSinceOrchestrate: this.callsSinceOrchestrate,
|
|
51
53
|
tokenAutoApplied: this.tokenAutoApplied,
|
|
54
|
+
tokenOptimizationPending: this.tokenOptimizationPending,
|
|
55
|
+
orchestrationPending: this.orchestrationPending,
|
|
52
56
|
};
|
|
53
57
|
}
|
|
54
58
|
elapsedMs() {
|
|
@@ -69,8 +73,10 @@ export class SessionTelemetry {
|
|
|
69
73
|
this.optimizeTokensCalled = false;
|
|
70
74
|
this.tokenAutoApplied = false;
|
|
71
75
|
}
|
|
76
|
+
this.tokenOptimizationPending = false;
|
|
72
77
|
}
|
|
73
78
|
if (nextPhase === 'orchestrated') {
|
|
79
|
+
this.orchestrationPending = false;
|
|
74
80
|
this.mindkitCheckCalled = false;
|
|
75
81
|
this.storeMemoryCalledPostOrchestrate = false;
|
|
76
82
|
this.sessionDnaCalled = false;
|
|
@@ -85,12 +91,17 @@ export class SessionTelemetry {
|
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
87
93
|
if (toolName === 'nexus_orchestrate') {
|
|
94
|
+
this.stickyTokenOptimization = true;
|
|
88
95
|
this.advancePhase('orchestrated');
|
|
96
|
+
this.optimizeTokensCalled = true;
|
|
97
|
+
this.tokenAutoApplied = true;
|
|
98
|
+
this.tokenOptimizationPending = false;
|
|
89
99
|
this.noteFileIntent(args.files);
|
|
90
100
|
return;
|
|
91
101
|
}
|
|
92
102
|
if (toolName === 'nexus_optimize_tokens') {
|
|
93
103
|
this.optimizeTokensCalled = true;
|
|
104
|
+
this.tokenOptimizationPending = false;
|
|
94
105
|
if (this.lifecyclePhase === 'orchestrated')
|
|
95
106
|
this.advancePhase('working');
|
|
96
107
|
this.noteFileIntent(args.files);
|
|
@@ -126,6 +137,20 @@ export class SessionTelemetry {
|
|
|
126
137
|
this.advancePhase('working');
|
|
127
138
|
}
|
|
128
139
|
}
|
|
140
|
+
observeQueuedToolCall(toolName, args) {
|
|
141
|
+
if (toolName === 'nexus_orchestrate') {
|
|
142
|
+
this.orchestrationPending = true;
|
|
143
|
+
this.tokenOptimizationPending = true;
|
|
144
|
+
this.optimizeTokensCalled = true;
|
|
145
|
+
this.noteFileIntent(args.files);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (toolName === 'nexus_optimize_tokens') {
|
|
149
|
+
this.tokenOptimizationPending = true;
|
|
150
|
+
this.optimizeTokensCalled = true;
|
|
151
|
+
this.noteFileIntent(args.files);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
129
154
|
needsOptimizeTokens(currentToolName) {
|
|
130
155
|
if (currentToolName === 'nexus_optimize_tokens')
|
|
131
156
|
return false;
|
|
@@ -133,12 +158,26 @@ export class SessionTelemetry {
|
|
|
133
158
|
return false;
|
|
134
159
|
if (this.tokenAutoApplied)
|
|
135
160
|
return false;
|
|
161
|
+
if (this.tokenOptimizationPending)
|
|
162
|
+
return false;
|
|
136
163
|
return this.fileReadIntentCount >= 1 && !this.optimizeTokensCalled;
|
|
137
164
|
}
|
|
138
165
|
markTokenAutoApplied() {
|
|
139
166
|
this.tokenAutoApplied = true;
|
|
140
167
|
this.optimizeTokensCalled = true;
|
|
141
168
|
this.stickyTokenOptimization = true;
|
|
169
|
+
this.tokenOptimizationPending = false;
|
|
170
|
+
}
|
|
171
|
+
needsOrchestration(currentToolName) {
|
|
172
|
+
if (currentToolName === 'nexus_orchestrate')
|
|
173
|
+
return false;
|
|
174
|
+
if (this.lifecyclePhase === 'pre-bootstrap')
|
|
175
|
+
return false;
|
|
176
|
+
if (this.lifecyclePhase === 'orchestrated' || this.lifecyclePhase === 'working' || this.lifecyclePhase === 'closing')
|
|
177
|
+
return false;
|
|
178
|
+
if (this.orchestrationPending)
|
|
179
|
+
return false;
|
|
180
|
+
return this.bootstrapped && this.lifecyclePhase === 'bootstrapped';
|
|
142
181
|
}
|
|
143
182
|
needsStoreMemory(currentToolName) {
|
|
144
183
|
if (currentToolName === 'nexus_store_memory')
|
|
@@ -184,9 +223,8 @@ export class SessionTelemetry {
|
|
|
184
223
|
}
|
|
185
224
|
break;
|
|
186
225
|
case 'store':
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
226
|
+
// Storing memory completes the lifecycle step; do not create a
|
|
227
|
+
// self-referential "store again" loop.
|
|
190
228
|
break;
|
|
191
229
|
case 'mindkit_fail':
|
|
192
230
|
nudges.push('Guardrail FAILED. Do NOT proceed. Re-scope the task or call nexus_ghost_pass for a safer approach.');
|
|
@@ -41,6 +41,7 @@ export declare class MCPAdapter implements Adapter {
|
|
|
41
41
|
private formatRemainingProtocolSteps;
|
|
42
42
|
private prependTextToResponse;
|
|
43
43
|
private decorateLifecycleResponse;
|
|
44
|
+
private buildPreOrchestrationBlock;
|
|
44
45
|
private extractGoalLikeText;
|
|
45
46
|
private isMemoryTool;
|
|
46
47
|
private injectMemoryContext;
|
|
@@ -124,6 +124,23 @@ const PRE_BOOTSTRAP_ALLOWED_TOOLS = new Set([
|
|
|
124
124
|
'nexus_list_specialists',
|
|
125
125
|
'nexus_list_crews',
|
|
126
126
|
]);
|
|
127
|
+
const PRE_ORCHESTRATE_ALLOWED_TOOLS = new Set([
|
|
128
|
+
...PRE_BOOTSTRAP_ALLOWED_TOOLS,
|
|
129
|
+
'nexus_orchestrate',
|
|
130
|
+
'nexus_plan_execution',
|
|
131
|
+
'nexus_recall_memory',
|
|
132
|
+
'nexus_temporal_query',
|
|
133
|
+
'nexus_store_memory',
|
|
134
|
+
'nexus_search',
|
|
135
|
+
'nexus_optimize_tokens',
|
|
136
|
+
'nexus_mindkit_check',
|
|
137
|
+
'nexus_ghost_pass',
|
|
138
|
+
'nexus_token_report',
|
|
139
|
+
'nexus_session_dna',
|
|
140
|
+
'nexus_run_status',
|
|
141
|
+
'nexus_selection_ledger',
|
|
142
|
+
'nexus_license_usage',
|
|
143
|
+
]);
|
|
127
144
|
export class MCPAdapter {
|
|
128
145
|
name;
|
|
129
146
|
type = 'mcp';
|
|
@@ -513,6 +530,24 @@ export class MCPAdapter {
|
|
|
513
530
|
],
|
|
514
531
|
};
|
|
515
532
|
}
|
|
533
|
+
buildPreOrchestrationBlock(toolName) {
|
|
534
|
+
if (!this.telemetry.needsOrchestration(toolName))
|
|
535
|
+
return null;
|
|
536
|
+
if (PRE_ORCHESTRATE_ALLOWED_TOOLS.has(toolName) || isReadOnlyMcpTool(toolName))
|
|
537
|
+
return null;
|
|
538
|
+
return {
|
|
539
|
+
content: [{
|
|
540
|
+
type: 'text',
|
|
541
|
+
text: JSON.stringify({
|
|
542
|
+
status: 'blocked',
|
|
543
|
+
code: 'orchestration-required',
|
|
544
|
+
reason: 'nexus_orchestrate has not been called for this session',
|
|
545
|
+
action: 'Call nexus_orchestrate(prompt="<your task>") before invoking mutation, worker, kernel, hook, automation, or low-level execution tools.',
|
|
546
|
+
hint: 'Nexus Prime must own planning, token budgeting, memory recall, hooks, and review gates before work starts.',
|
|
547
|
+
}, null, 2),
|
|
548
|
+
}],
|
|
549
|
+
};
|
|
550
|
+
}
|
|
516
551
|
extractGoalLikeText(args) {
|
|
517
552
|
const candidates = [args.goal, args.task, args.prompt, args.query, args.action, args.content]
|
|
518
553
|
.map((value) => String(value ?? '').trim())
|
|
@@ -790,6 +825,9 @@ export class MCPAdapter {
|
|
|
790
825
|
}],
|
|
791
826
|
};
|
|
792
827
|
}
|
|
828
|
+
const preOrchestrationBlock = this.buildPreOrchestrationBlock(toolName);
|
|
829
|
+
if (preOrchestrationBlock)
|
|
830
|
+
return preOrchestrationBlock;
|
|
793
831
|
const callId = `mcp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
794
832
|
const startTimeMs = Date.now();
|
|
795
833
|
nexusEventBus.emit('mcp.call.start', {
|
|
@@ -831,8 +869,13 @@ export class MCPAdapter {
|
|
|
831
869
|
? { result: resultPreview }
|
|
832
870
|
: { error: envelope.error.message }),
|
|
833
871
|
});
|
|
834
|
-
if (envelope.ok
|
|
835
|
-
|
|
872
|
+
if (envelope.ok) {
|
|
873
|
+
if (asyncReceipt?.queued) {
|
|
874
|
+
this.telemetry.observeQueuedToolCall(toolName, args);
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
this.telemetry.observeSuccessfulToolCall(toolName, args);
|
|
878
|
+
}
|
|
836
879
|
}
|
|
837
880
|
// Surface meaningful tool calls on the SSE feed; persist only high-signal outcomes.
|
|
838
881
|
if (this.nexusRef) {
|
|
@@ -1028,8 +1071,10 @@ export class MCPAdapter {
|
|
|
1028
1071
|
return;
|
|
1029
1072
|
// Skip if an explicit optimize/orchestrate already ran this session.
|
|
1030
1073
|
const usage = this.nexusRef?.getRuntime?.()?.getUsageSnapshot?.();
|
|
1031
|
-
if (usage?.tokenOptimizationApplied)
|
|
1074
|
+
if (usage?.tokenOptimizationApplied) {
|
|
1075
|
+
this.telemetry.markTokenAutoApplied?.();
|
|
1032
1076
|
return;
|
|
1077
|
+
}
|
|
1033
1078
|
const fileRefs = this.extractFileRefsFromArgs(args);
|
|
1034
1079
|
if (fileRefs.length < 5)
|
|
1035
1080
|
return;
|
|
@@ -1043,6 +1088,7 @@ export class MCPAdapter {
|
|
|
1043
1088
|
const compressionRatio = grossInput > 0 ? totalEstimated / grossInput : 0;
|
|
1044
1089
|
const pct = grossInput > 0 ? Math.round((savings / grossInput) * 100) : 0;
|
|
1045
1090
|
// Mark applied so we don't repeat this for every subsequent tool call.
|
|
1091
|
+
this.telemetry.markTokenAutoApplied?.();
|
|
1046
1092
|
try {
|
|
1047
1093
|
this.nexusRef?.getRuntime?.()?.recordClientToolCall?.(toolName, {
|
|
1048
1094
|
tokenOptimizationApplied: true,
|
package/dist/cli.js
CHANGED
|
@@ -1329,12 +1329,14 @@ program
|
|
|
1329
1329
|
.alias('claude')
|
|
1330
1330
|
.description('Integrate with Claude Code')
|
|
1331
1331
|
.option('--dry-run', 'Preview changes')
|
|
1332
|
-
.option('--hooks', '
|
|
1332
|
+
.option('--hooks', 'Install Claude Code hooks into ~/.claude/settings.json for auto-bootstrap, auto-memory, and governance (default)')
|
|
1333
|
+
.option('--no-hooks', 'Skip Claude Code hooks')
|
|
1333
1334
|
.action((options) => {
|
|
1334
1335
|
const definition = getSetupDefinition('claude-code');
|
|
1336
|
+
const shouldInstallHooks = options.hooks !== false;
|
|
1335
1337
|
if (options.dryRun) {
|
|
1336
1338
|
printSetupPreview(definition);
|
|
1337
|
-
if (
|
|
1339
|
+
if (shouldInstallHooks) {
|
|
1338
1340
|
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
1339
1341
|
writeClaudeCodeHooks(settingsPath, true);
|
|
1340
1342
|
}
|
|
@@ -1344,7 +1346,7 @@ program
|
|
|
1344
1346
|
console.log(`✅ Nexus Prime installed for Claude Code`);
|
|
1345
1347
|
console.log(` MCP: ${definition.configPath}`);
|
|
1346
1348
|
definition.instructionFiles.forEach((file) => console.log(` Instruction: ${file.path}`));
|
|
1347
|
-
if (
|
|
1349
|
+
if (shouldInstallHooks) {
|
|
1348
1350
|
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
1349
1351
|
writeClaudeCodeHooks(settingsPath, false);
|
|
1350
1352
|
console.log(`✅ Claude Code hooks installed (auto-bootstrap, auto-memory, governance)`);
|
|
@@ -1428,9 +1430,11 @@ program
|
|
|
1428
1430
|
.map((clientId) => getSetupDefinition(clientId));
|
|
1429
1431
|
if (options.dryRun) {
|
|
1430
1432
|
definitions.forEach((definition) => printSetupPreview(definition));
|
|
1433
|
+
writeClaudeCodeHooks(join(homedir(), '.claude', 'settings.json'), true);
|
|
1431
1434
|
return;
|
|
1432
1435
|
}
|
|
1433
1436
|
definitions.forEach((definition) => installSetup(definition));
|
|
1437
|
+
writeClaudeCodeHooks(join(homedir(), '.claude', 'settings.json'), false);
|
|
1434
1438
|
console.log('✅ Nexus Prime installed for all supported clients');
|
|
1435
1439
|
definitions.forEach((definition) => {
|
|
1436
1440
|
console.log(` ${definition.label}`);
|
|
@@ -293,6 +293,9 @@ export function decideWorkers(requestedWorkers, plannedWorkers, phaseCount, inte
|
|
|
293
293
|
if (typeof requestedWorkers === 'number' && requestedWorkers > 0) {
|
|
294
294
|
return Math.max(1, Math.min(7, requestedWorkers));
|
|
295
295
|
}
|
|
296
|
+
if (intent.taskType === 'release') {
|
|
297
|
+
return 1;
|
|
298
|
+
}
|
|
296
299
|
if (intent.riskClass !== 'high'
|
|
297
300
|
&& intent.complexity <= 3
|
|
298
301
|
&& phaseCount <= 2
|
|
@@ -2316,6 +2316,23 @@ export class OrchestratorEngine {
|
|
|
2316
2316
|
limit: 4,
|
|
2317
2317
|
selector: 'name',
|
|
2318
2318
|
});
|
|
2319
|
+
const selectedWorkflowValues = intent.taskType === 'release'
|
|
2320
|
+
? dedupeStrings(['release-pipeline', ...workflowSelection.selectedValues])
|
|
2321
|
+
: workflowSelection.selectedValues;
|
|
2322
|
+
const workflowSelectedEntries = [
|
|
2323
|
+
...workflowSelection.selectedEntries,
|
|
2324
|
+
...selectedWorkflowValues
|
|
2325
|
+
.filter((value) => !workflowSelection.selectedEntries.some((entry) => entry.name === value || entry.id === value))
|
|
2326
|
+
.map((value) => this.toArtifactAuditEntry('workflow', value, workflowItems, {
|
|
2327
|
+
score: 1,
|
|
2328
|
+
source: 'planner',
|
|
2329
|
+
confidence: 'high',
|
|
2330
|
+
reason: 'Release intent requires the release-pipeline workflow so package, audit, and smoke evidence is collected.',
|
|
2331
|
+
selector: 'name',
|
|
2332
|
+
selectionPolicy: 'global-first',
|
|
2333
|
+
authorityTier: this.getAuthorityTier('workflow'),
|
|
2334
|
+
})),
|
|
2335
|
+
];
|
|
2319
2336
|
const hookSelection = this.resolveCatalogVotes('hook', task, intent, hookItems, {
|
|
2320
2337
|
explicit: options.hookSelectors,
|
|
2321
2338
|
planner: [],
|
|
@@ -2356,7 +2373,7 @@ export class OrchestratorEngine {
|
|
|
2356
2373
|
selectionSummary: {
|
|
2357
2374
|
specialists: specialistSelection.selectedValues.length,
|
|
2358
2375
|
skills: skillSelection.selectedValues.length,
|
|
2359
|
-
workflows:
|
|
2376
|
+
workflows: selectedWorkflowValues.length,
|
|
2360
2377
|
hooks: hookSelection.selectedValues.length,
|
|
2361
2378
|
automations: automationSelection.selectedValues.length,
|
|
2362
2379
|
crews: crewSelection.selectedValues.length,
|
|
@@ -2367,7 +2384,7 @@ export class OrchestratorEngine {
|
|
|
2367
2384
|
...crewSelection.selectedEntries,
|
|
2368
2385
|
...specialistSelection.selectedEntries,
|
|
2369
2386
|
...skillSelection.selectedEntries,
|
|
2370
|
-
...
|
|
2387
|
+
...workflowSelectedEntries,
|
|
2371
2388
|
...hookSelection.selectedEntries,
|
|
2372
2389
|
...automationSelection.selectedEntries,
|
|
2373
2390
|
];
|
|
@@ -2388,7 +2405,7 @@ export class OrchestratorEngine {
|
|
|
2388
2405
|
selectionSummary: {
|
|
2389
2406
|
specialists: specialistSelection.selectedValues.length,
|
|
2390
2407
|
skills: skillSelection.selectedValues.length,
|
|
2391
|
-
workflows:
|
|
2408
|
+
workflows: selectedWorkflowValues.length,
|
|
2392
2409
|
hooks: hookSelection.selectedValues.length,
|
|
2393
2410
|
automations: automationSelection.selectedValues.length,
|
|
2394
2411
|
crews: crewSelection.selectedValues.length,
|
|
@@ -2400,7 +2417,7 @@ export class OrchestratorEngine {
|
|
|
2400
2417
|
crew: crewSelection.selectedValues[0],
|
|
2401
2418
|
specialists: specialistSelection.selectedValues,
|
|
2402
2419
|
skills: skillSelection.selectedValues,
|
|
2403
|
-
workflows:
|
|
2420
|
+
workflows: selectedWorkflowValues,
|
|
2404
2421
|
hooks: hookSelection.selectedValues,
|
|
2405
2422
|
automations: automationSelection.selectedValues,
|
|
2406
2423
|
audit: {
|
|
@@ -2618,6 +2635,8 @@ export class OrchestratorEngine {
|
|
|
2618
2635
|
isCatalogVoteSelected(entry) {
|
|
2619
2636
|
if (entry.eligible === false)
|
|
2620
2637
|
return false;
|
|
2638
|
+
if (!entry.item && entry.source !== 'explicit')
|
|
2639
|
+
return false;
|
|
2621
2640
|
if (entry.source === 'explicit' || entry.source === 'planner')
|
|
2622
2641
|
return true;
|
|
2623
2642
|
if (entry.source === 'runtime-resolver')
|
|
@@ -2657,6 +2676,12 @@ export class OrchestratorEngine {
|
|
|
2657
2676
|
if (entry.source === 'explicit') {
|
|
2658
2677
|
return { eligible: true };
|
|
2659
2678
|
}
|
|
2679
|
+
if (!entry.item) {
|
|
2680
|
+
return {
|
|
2681
|
+
eligible: false,
|
|
2682
|
+
blockedReason: 'Recommended artifact is not present in the runtime catalog.',
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2660
2685
|
if (entry.source === 'scorer' && entry.confidence === 'low') {
|
|
2661
2686
|
return {
|
|
2662
2687
|
eligible: false,
|
|
@@ -568,30 +568,39 @@ export const BUILTIN_SKILL_PACKS = BASE_DOMAIN_SKILLS.flatMap((seed) => ([
|
|
|
568
568
|
promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
|
|
569
569
|
},
|
|
570
570
|
]));
|
|
571
|
-
|
|
571
|
+
const SPECIALIZED_WORKFLOW_PACKS = [
|
|
572
572
|
{
|
|
573
|
-
key:
|
|
574
|
-
name:
|
|
575
|
-
domain:
|
|
576
|
-
description:
|
|
577
|
-
triggerConditions: [
|
|
578
|
-
expectedOutputs:
|
|
579
|
-
guardrails:
|
|
580
|
-
|
|
581
|
-
|
|
573
|
+
key: 'release-pipeline',
|
|
574
|
+
name: 'release-pipeline',
|
|
575
|
+
domain: 'pdlc',
|
|
576
|
+
description: 'Release workflow with packaging, audit, smoke, and publish-readiness checkpoints.',
|
|
577
|
+
triggerConditions: ['release requested', 'publish or deploy task detected', 'version or tag work selected'],
|
|
578
|
+
expectedOutputs: ['release checklist', 'verification evidence', 'publish decision'],
|
|
579
|
+
guardrails: [
|
|
580
|
+
'Do not treat a draft release as published.',
|
|
581
|
+
'Do not publish without explicit release-gate evidence.',
|
|
582
|
+
],
|
|
583
|
+
verifierHooks: [
|
|
584
|
+
'Attach package, audit, and smoke evidence before promotion.',
|
|
585
|
+
'Record publish-readiness outcome in the run ledger.',
|
|
586
|
+
],
|
|
587
|
+
roleAffinity: ['planner', 'verifier', 'devops'],
|
|
582
588
|
steps: [
|
|
583
589
|
{
|
|
584
|
-
title:
|
|
590
|
+
title: 'Frame release criteria and affected package surface',
|
|
585
591
|
checkpoint: 'before-read',
|
|
586
592
|
role: 'planner',
|
|
587
593
|
},
|
|
588
594
|
{
|
|
589
|
-
title:
|
|
590
|
-
checkpoint: 'before-
|
|
591
|
-
role: '
|
|
595
|
+
title: 'Collect package, audit, and smoke evidence',
|
|
596
|
+
checkpoint: 'before-verify',
|
|
597
|
+
role: 'verifier',
|
|
598
|
+
bindings: [
|
|
599
|
+
{ type: 'run_command', command: 'npm run qa:release' },
|
|
600
|
+
],
|
|
592
601
|
},
|
|
593
602
|
{
|
|
594
|
-
title:
|
|
603
|
+
title: 'Record publish readiness and remaining blockers',
|
|
595
604
|
checkpoint: 'before-verify',
|
|
596
605
|
role: 'verifier',
|
|
597
606
|
},
|
|
@@ -599,35 +608,104 @@ export const BUILTIN_WORKFLOW_PACKS = BASE_DOMAIN_SKILLS.flatMap((seed) => ([
|
|
|
599
608
|
promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
|
|
600
609
|
},
|
|
601
610
|
{
|
|
602
|
-
key:
|
|
603
|
-
name:
|
|
604
|
-
domain:
|
|
605
|
-
description:
|
|
606
|
-
triggerConditions: [
|
|
607
|
-
expectedOutputs: ['
|
|
608
|
-
guardrails: [
|
|
609
|
-
|
|
610
|
-
|
|
611
|
+
key: 'research-and-implement',
|
|
612
|
+
name: 'research-and-implement',
|
|
613
|
+
domain: 'deep-tech',
|
|
614
|
+
description: 'Research-backed implementation workflow tying retrieved context to bounded code changes and verification.',
|
|
615
|
+
triggerConditions: ['rag context attached', 'research-backed implementation requested', 'unknown code path requires grounding'],
|
|
616
|
+
expectedOutputs: ['source-grounded plan', 'bounded implementation notes', 'verification evidence'],
|
|
617
|
+
guardrails: [
|
|
618
|
+
'Do not promote research claims without source-grounded evidence.',
|
|
619
|
+
'Keep implementation scope tied to retrieved context.',
|
|
620
|
+
],
|
|
621
|
+
verifierHooks: [
|
|
622
|
+
'Attach source-grounding notes before verification.',
|
|
623
|
+
'Record how research context changed the implementation decision.',
|
|
624
|
+
],
|
|
625
|
+
roleAffinity: ['planner', 'coder', 'verifier', 'research-shadow'],
|
|
611
626
|
steps: [
|
|
612
627
|
{
|
|
613
|
-
title:
|
|
628
|
+
title: 'Extract source-grounded implementation constraints',
|
|
614
629
|
checkpoint: 'before-read',
|
|
615
630
|
role: 'planner',
|
|
616
631
|
},
|
|
617
632
|
{
|
|
618
|
-
title:
|
|
619
|
-
checkpoint: 'before-
|
|
620
|
-
role: '
|
|
633
|
+
title: 'Apply the smallest implementation compatible with the research context',
|
|
634
|
+
checkpoint: 'before-mutate',
|
|
635
|
+
role: 'coder',
|
|
621
636
|
},
|
|
622
637
|
{
|
|
623
|
-
title:
|
|
624
|
-
checkpoint: '
|
|
625
|
-
role: '
|
|
638
|
+
title: 'Verify behavior and cite grounding evidence',
|
|
639
|
+
checkpoint: 'before-verify',
|
|
640
|
+
role: 'verifier',
|
|
626
641
|
},
|
|
627
642
|
],
|
|
628
643
|
promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
|
|
629
644
|
},
|
|
630
|
-
]
|
|
645
|
+
];
|
|
646
|
+
export const BUILTIN_WORKFLOW_PACKS = [
|
|
647
|
+
...BASE_DOMAIN_SKILLS.flatMap((seed) => ([
|
|
648
|
+
{
|
|
649
|
+
key: `${seed.domain}-execution-loop`,
|
|
650
|
+
name: `${seed.domain}-execution-loop`,
|
|
651
|
+
domain: seed.domain,
|
|
652
|
+
description: `Bundled ${seed.domain} workflow with planning, execution, and verification checkpoints.`,
|
|
653
|
+
triggerConditions: [`goal mentions ${seed.domain}`, `${seed.domain} pack selected`],
|
|
654
|
+
expectedOutputs: seed.outputs,
|
|
655
|
+
guardrails: seed.guardrails,
|
|
656
|
+
verifierHooks: ['Collect verifier evidence before workflow promotion.', ...seed.verifierHooks],
|
|
657
|
+
roleAffinity: ['planner', 'coder', 'verifier', 'skill-maker'],
|
|
658
|
+
steps: [
|
|
659
|
+
{
|
|
660
|
+
title: `Plan ${seed.domain} outcome and artifacts`,
|
|
661
|
+
checkpoint: 'before-read',
|
|
662
|
+
role: 'planner',
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
title: `Execute ${seed.domain} changes with bounded scope`,
|
|
666
|
+
checkpoint: 'before-mutate',
|
|
667
|
+
role: 'coder',
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
title: `Review ${seed.domain} outputs and attach evidence`,
|
|
671
|
+
checkpoint: 'before-verify',
|
|
672
|
+
role: 'verifier',
|
|
673
|
+
},
|
|
674
|
+
],
|
|
675
|
+
promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
key: `${seed.domain}-approval-loop`,
|
|
679
|
+
name: `${seed.domain}-approval-loop`,
|
|
680
|
+
domain: seed.domain,
|
|
681
|
+
description: `Bundled ${seed.domain} approval loop with review, verification, and promotion control.`,
|
|
682
|
+
triggerConditions: [`${seed.domain} approval requested`, `${seed.domain} promotion under review`],
|
|
683
|
+
expectedOutputs: ['approval note', 'review evidence', 'promotion decision'],
|
|
684
|
+
guardrails: ['Do not approve unverified work.', ...seed.guardrails],
|
|
685
|
+
verifierHooks: ['Require approval evidence before promotion.', ...seed.verifierHooks],
|
|
686
|
+
roleAffinity: ['planner', 'verifier', 'research-shadow'],
|
|
687
|
+
steps: [
|
|
688
|
+
{
|
|
689
|
+
title: `Frame ${seed.domain} approval criteria`,
|
|
690
|
+
checkpoint: 'before-read',
|
|
691
|
+
role: 'planner',
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
title: `Review ${seed.domain} evidence and risks`,
|
|
695
|
+
checkpoint: 'before-verify',
|
|
696
|
+
role: 'verifier',
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
title: `Publish ${seed.domain} approval outcome`,
|
|
700
|
+
checkpoint: 'retry',
|
|
701
|
+
role: 'research-shadow',
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
promotionThresholds: { minSuccesses: 2, maxFailures: 1 },
|
|
705
|
+
},
|
|
706
|
+
])),
|
|
707
|
+
...SPECIALIZED_WORKFLOW_PACKS,
|
|
708
|
+
];
|
|
631
709
|
export function slugify(value) {
|
|
632
710
|
return value
|
|
633
711
|
.toLowerCase()
|
|
@@ -204,6 +204,10 @@ export class TokenSupremacyEngine {
|
|
|
204
204
|
// Saves ~60-70% tokens on follow-up sessions
|
|
205
205
|
// ───────────────────────────────────────────────────────────────────────────
|
|
206
206
|
differential(prev, curr) {
|
|
207
|
+
const previousFileSHAs = prev.fileSHAs && typeof prev.fileSHAs === 'object'
|
|
208
|
+
? prev.fileSHAs
|
|
209
|
+
: {};
|
|
210
|
+
const hasComparableFingerprints = Object.keys(previousFileSHAs).length > 0;
|
|
207
211
|
const delta = {
|
|
208
212
|
added: [],
|
|
209
213
|
changed: [],
|
|
@@ -214,10 +218,15 @@ export class TokenSupremacyEngine {
|
|
|
214
218
|
: undefined,
|
|
215
219
|
};
|
|
216
220
|
for (const file of curr) {
|
|
217
|
-
const prevSHA =
|
|
221
|
+
const prevSHA = previousFileSHAs[file.path];
|
|
218
222
|
const currSHA = this.fingerprintFile(file);
|
|
219
223
|
if (!prevSHA) {
|
|
220
|
-
|
|
224
|
+
if (hasComparableFingerprints) {
|
|
225
|
+
delta.added.push(file);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
delta.changed.push(file);
|
|
229
|
+
}
|
|
221
230
|
}
|
|
222
231
|
else if (prevSHA !== currSHA) {
|
|
223
232
|
delta.changed.push(file);
|
|
@@ -8,6 +8,10 @@ import { type WorktreeHealthSnapshot } from '../../engines/worktree-health.js';
|
|
|
8
8
|
import type { MergeDecision } from '../index.js';
|
|
9
9
|
import type { RuntimeWorkerResult, WorkerRole } from '../runtime.js';
|
|
10
10
|
import { type CommandRecord } from './worker.js';
|
|
11
|
+
export interface BindingApplicationResult {
|
|
12
|
+
modifiedFiles: string[];
|
|
13
|
+
commandRecords: CommandRecord[];
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Lightweight per-run artifact writer. Maintains a flat index of paths
|
|
13
17
|
* written so callers can recover the full run artifact layout.
|
|
@@ -36,11 +40,12 @@ export declare class WorktreeSession {
|
|
|
36
40
|
constructor(repoRoot: string, workerId: string, role: WorkerRole, recorder: ArtifactRecorder, reportHealth?: (snapshot: WorktreeHealthSnapshot) => void);
|
|
37
41
|
create(): Promise<void>;
|
|
38
42
|
run(command: string, allowFailure?: boolean, timeoutMs?: number): Promise<CommandRecord>;
|
|
39
|
-
applyBindings(bindings: SkillBinding[], timeoutMs?: number): Promise<
|
|
43
|
+
applyBindings(bindings: SkillBinding[], timeoutMs?: number): Promise<BindingApplicationResult>;
|
|
40
44
|
captureDiff(): Promise<string>;
|
|
41
45
|
applyPatchContent(diff: string): Promise<void>;
|
|
42
46
|
cleanup(): Promise<void>;
|
|
43
47
|
private resolve;
|
|
48
|
+
private linkWorkspaceDependencies;
|
|
44
49
|
}
|
|
45
50
|
/** Thin adapter that wraps a MemoryBackend for use inside MergeOracle. */
|
|
46
51
|
export declare class MergeMemoryAdapter {
|
|
@@ -33,14 +33,14 @@ export class ArtifactRecorder {
|
|
|
33
33
|
writeJson(relativePath, value) {
|
|
34
34
|
const target = path.join(this.runDir, relativePath);
|
|
35
35
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
36
|
-
|
|
36
|
+
fs.writeFileSync(target, JSON.stringify(value, null, 2), 'utf-8');
|
|
37
37
|
this.index[relativePath] = target;
|
|
38
38
|
return target;
|
|
39
39
|
}
|
|
40
40
|
writeText(relativePath, value) {
|
|
41
41
|
const target = path.join(this.runDir, relativePath);
|
|
42
42
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
43
|
-
|
|
43
|
+
fs.writeFileSync(target, value, 'utf-8');
|
|
44
44
|
this.index[relativePath] = target;
|
|
45
45
|
return target;
|
|
46
46
|
}
|
|
@@ -75,6 +75,7 @@ export class WorktreeSession {
|
|
|
75
75
|
cwd: this.repoRoot,
|
|
76
76
|
maxBuffer: 1024 * 1024 * 20,
|
|
77
77
|
});
|
|
78
|
+
await this.linkWorkspaceDependencies();
|
|
78
79
|
}
|
|
79
80
|
catch (error) {
|
|
80
81
|
const retryHealth = await doctorGitWorktrees(this.repoRoot);
|
|
@@ -87,6 +88,7 @@ export class WorktreeSession {
|
|
|
87
88
|
}
|
|
88
89
|
async applyBindings(bindings, timeoutMs) {
|
|
89
90
|
const modified = new Set();
|
|
91
|
+
const commandRecords = [];
|
|
90
92
|
for (const binding of bindings) {
|
|
91
93
|
switch (binding.type) {
|
|
92
94
|
case 'write_file': {
|
|
@@ -114,12 +116,13 @@ export class WorktreeSession {
|
|
|
114
116
|
}
|
|
115
117
|
case 'run_command': {
|
|
116
118
|
const record = await this.run(binding.command || '', false, timeoutMs);
|
|
119
|
+
commandRecords.push(record);
|
|
117
120
|
this.recorder.writeJson(path.join('workers', this.workerId, `${sanitizeFileName(record.command)}.json`), record);
|
|
118
121
|
break;
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
|
-
return [...modified];
|
|
125
|
+
return { modifiedFiles: [...modified], commandRecords };
|
|
123
126
|
}
|
|
124
127
|
async captureDiff() {
|
|
125
128
|
try {
|
|
@@ -130,6 +133,12 @@ export class WorktreeSession {
|
|
|
130
133
|
catch {
|
|
131
134
|
// Runtime skill overlays stay outside repo patches.
|
|
132
135
|
}
|
|
136
|
+
try {
|
|
137
|
+
await exec('git reset HEAD -- node_modules', { cwd: this.worktreeDir, maxBuffer: 1024 * 1024 * 20 });
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Shared dependency symlink stays outside repo patches.
|
|
141
|
+
}
|
|
133
142
|
const { stdout } = await exec('git diff --binary --cached HEAD', {
|
|
134
143
|
cwd: this.worktreeDir,
|
|
135
144
|
maxBuffer: 1024 * 1024 * 20,
|
|
@@ -167,6 +176,23 @@ export class WorktreeSession {
|
|
|
167
176
|
return target;
|
|
168
177
|
return path.join(this.worktreeDir, target);
|
|
169
178
|
}
|
|
179
|
+
async linkWorkspaceDependencies() {
|
|
180
|
+
const source = path.join(this.repoRoot, 'node_modules');
|
|
181
|
+
const target = path.join(this.worktreeDir, 'node_modules');
|
|
182
|
+
try {
|
|
183
|
+
const stat = await fs.promises.lstat(source);
|
|
184
|
+
if (!stat.isDirectory() && !stat.isSymbolicLink())
|
|
185
|
+
return;
|
|
186
|
+
await fs.promises.lstat(target).then(() => undefined).catch(async (error) => {
|
|
187
|
+
if (error?.code !== 'ENOENT')
|
|
188
|
+
throw error;
|
|
189
|
+
await fs.promises.symlink(source, target, 'dir');
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Worktrees remain valid for repos that do not use node_modules or have not installed deps.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
170
196
|
}
|
|
171
197
|
// ─── MergeMemoryAdapter ───────────────────────────────────────────────────────
|
|
172
198
|
/** Thin adapter that wraps a MemoryBackend for use inside MergeOracle. */
|
|
@@ -187,6 +187,8 @@ export interface WorkerVerification {
|
|
|
187
187
|
workerId: string;
|
|
188
188
|
verifierId: string;
|
|
189
189
|
passed: boolean;
|
|
190
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
191
|
+
skippedReason?: string;
|
|
190
192
|
commands: CommandRecord[];
|
|
191
193
|
summary: string;
|
|
192
194
|
artifactsPath: string;
|
|
@@ -197,6 +199,7 @@ export interface RuntimeWorkerResult extends WorkerResult {
|
|
|
197
199
|
verification?: WorkerVerification;
|
|
198
200
|
artifactsPath: string;
|
|
199
201
|
modifiedFiles: string[];
|
|
202
|
+
commandRecords?: CommandRecord[];
|
|
200
203
|
}
|
|
201
204
|
export interface PlannerResult {
|
|
202
205
|
summary: string;
|
|
@@ -531,6 +534,7 @@ export declare class SubAgentRuntime {
|
|
|
531
534
|
private applyDecision;
|
|
532
535
|
private evaluatePromotions;
|
|
533
536
|
private resolveCandidateDiff;
|
|
537
|
+
private isCommandOnlyOperationalSuccess;
|
|
534
538
|
private resolveFileRefs;
|
|
535
539
|
private discoverTargetFiles;
|
|
536
540
|
private collectLibraryCounts;
|
package/dist/phantom/runtime.js
CHANGED
|
@@ -619,6 +619,7 @@ export class SubAgentRuntime {
|
|
|
619
619
|
workerId: manifest.targetWorkerId ?? manifest.workerId,
|
|
620
620
|
verifierId: manifest.workerId,
|
|
621
621
|
passed: false,
|
|
622
|
+
status: 'failed',
|
|
622
623
|
commands: [],
|
|
623
624
|
summary: target?.budgetExceeded
|
|
624
625
|
? 'Skipped verification for over-budget worker.'
|
|
@@ -637,12 +638,14 @@ export class SubAgentRuntime {
|
|
|
637
638
|
}
|
|
638
639
|
const verification = verificationResults.find((entry) => entry.workerId === result.workerId);
|
|
639
640
|
if (verification) {
|
|
641
|
+
const verificationPassed = verification.status === 'passed' || (verification.passed && verification.status !== 'skipped');
|
|
640
642
|
result.verification = verification;
|
|
641
|
-
result.verified =
|
|
643
|
+
result.verified = verificationPassed;
|
|
642
644
|
result.testsPassing = verification.commands.filter((command) => command.exitCode === 0).length;
|
|
643
|
-
result.outcome =
|
|
645
|
+
result.outcome = verificationPassed ? 'success' : (result.diff.trim() ? 'partial' : 'failed');
|
|
644
646
|
}
|
|
645
647
|
}
|
|
648
|
+
recorder.writeJson('worker-results.json', run.workerResults);
|
|
646
649
|
const decision = await consensusPolicy.merge(mergeableCoderResults);
|
|
647
650
|
run.finalDecision = decision;
|
|
648
651
|
run.backendEvidence.consensus = consensusPolicy.shadowStats();
|
|
@@ -685,14 +688,15 @@ export class SubAgentRuntime {
|
|
|
685
688
|
: enforcedBlockingGate
|
|
686
689
|
? { applied: false, rolledBack: false, summary: `Review gate ${enforcedBlockingGate.gate} remains ${enforcedBlockingGate.status}.` }
|
|
687
690
|
: await this.applyDecision(recorder, { ...task, verifyCommands: beforeVerifyResolved.verifyCommands }, decision, consensusPolicy);
|
|
688
|
-
|
|
689
|
-
//
|
|
690
|
-
// a review gate enforced, or a
|
|
691
|
+
const commandOnlyOperationalSuccess = this.isCommandOnlyOperationalSuccess(task, decision);
|
|
692
|
+
// Read-only and command-only operational runs don't produce a diff to apply.
|
|
693
|
+
// Only mark 'failed' when a shield blocked, a review gate enforced, or a
|
|
694
|
+
// mutate run produced neither a patch nor successful command evidence.
|
|
691
695
|
const readOnlyIntent = task.intent === 'inspect' || task.intent === 'plan';
|
|
692
696
|
const failureExpected = preApplyShield.blocked || Boolean(enforcedBlockingGate);
|
|
693
697
|
run.state = applied.applied
|
|
694
698
|
? (applied.rolledBack ? 'rolled_back' : 'merged')
|
|
695
|
-
: (readOnlyIntent && !failureExpected ? 'inspected' : 'failed');
|
|
699
|
+
: ((readOnlyIntent || commandOnlyOperationalSuccess) && !failureExpected ? 'inspected' : 'failed');
|
|
696
700
|
// Surface completion on the SSE feed so the dashboard can move the
|
|
697
701
|
// run from "Running" to "Done" without a poll round-trip.
|
|
698
702
|
try {
|
|
@@ -2045,13 +2049,20 @@ export class SubAgentRuntime {
|
|
|
2045
2049
|
verified: false,
|
|
2046
2050
|
artifactsPath: workerDir,
|
|
2047
2051
|
modifiedFiles: [],
|
|
2052
|
+
commandRecords: [],
|
|
2048
2053
|
};
|
|
2049
2054
|
}
|
|
2050
|
-
const
|
|
2055
|
+
const bindingResult = await session.applyBindings(allowedActions, timeoutMs);
|
|
2056
|
+
const modifiedFiles = bindingResult.modifiedFiles;
|
|
2057
|
+
const commandRecords = bindingResult.commandRecords;
|
|
2051
2058
|
modifiedFiles.forEach(file => {
|
|
2052
2059
|
this.sessionDNA?.recordFileModified(file);
|
|
2053
2060
|
learnings.push(`Modified ${file}`);
|
|
2054
2061
|
});
|
|
2062
|
+
if (commandRecords.length > 0) {
|
|
2063
|
+
const passedCommands = commandRecords.filter((record) => record.exitCode === 0).length;
|
|
2064
|
+
learnings.push(`Ran ${commandRecords.length} command binding(s), ${passedCommands} passed.`);
|
|
2065
|
+
}
|
|
2055
2066
|
const diff = await session.captureDiff();
|
|
2056
2067
|
recorder.writeText(path.join('workers', manifest.workerId, 'diff.patch'), diff);
|
|
2057
2068
|
podNetwork.publish(manifest.workerId, `Produced ${modifiedFiles.length} modified files`, 0.8, ['#runtime-worker']);
|
|
@@ -2060,23 +2071,30 @@ export class SubAgentRuntime {
|
|
|
2060
2071
|
if (budgetExceeded) {
|
|
2061
2072
|
learnings.push(`Token budget exceeded: used ${estimatedTokens}, budget ${manifest.tokenBudget}`);
|
|
2062
2073
|
}
|
|
2063
|
-
|
|
2074
|
+
const commandOnlySuccess = !diff.trim()
|
|
2075
|
+
&& commandRecords.length > 0
|
|
2076
|
+
&& commandRecords.every((record) => record.exitCode === 0);
|
|
2077
|
+
nexusEventBus.emit('phantom.worker.complete', {
|
|
2078
|
+
workerId: manifest.workerId,
|
|
2079
|
+
confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.7 : (commandOnlySuccess ? 0.75 : 0.2)),
|
|
2080
|
+
});
|
|
2064
2081
|
return {
|
|
2065
2082
|
workerId: manifest.workerId,
|
|
2066
2083
|
role: manifest.role,
|
|
2067
2084
|
taskId: runId,
|
|
2068
2085
|
approach: manifest.strategy,
|
|
2069
2086
|
diff,
|
|
2070
|
-
outcome: budgetExceeded ? 'budgetExceeded' : (diff.trim() ? 'partial' : 'failed'),
|
|
2071
|
-
confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.68 : 0.18),
|
|
2087
|
+
outcome: budgetExceeded ? 'budgetExceeded' : (diff.trim() ? 'partial' : (commandOnlySuccess ? 'success' : 'failed')),
|
|
2088
|
+
confidence: budgetExceeded ? 0.1 : (diff.trim() ? 0.68 : (commandOnlySuccess ? 0.82 : 0.18)),
|
|
2072
2089
|
tokensUsed: estimatedTokens,
|
|
2073
2090
|
tokenEstimateSource: 'runtime-estimate',
|
|
2074
2091
|
budgetExceeded,
|
|
2075
2092
|
learnings,
|
|
2076
|
-
testsPassing: 0,
|
|
2093
|
+
testsPassing: commandRecords.filter((record) => record.exitCode === 0).length,
|
|
2077
2094
|
verified: false,
|
|
2078
2095
|
artifactsPath: workerDir,
|
|
2079
2096
|
modifiedFiles,
|
|
2097
|
+
commandRecords,
|
|
2080
2098
|
};
|
|
2081
2099
|
}
|
|
2082
2100
|
catch (error) {
|
|
@@ -2099,6 +2117,7 @@ export class SubAgentRuntime {
|
|
|
2099
2117
|
verified: false,
|
|
2100
2118
|
artifactsPath: workerDir,
|
|
2101
2119
|
modifiedFiles: [],
|
|
2120
|
+
commandRecords: [],
|
|
2102
2121
|
};
|
|
2103
2122
|
}
|
|
2104
2123
|
finally {
|
|
@@ -2130,15 +2149,31 @@ export class SubAgentRuntime {
|
|
|
2130
2149
|
const verifier = new WorktreeSession(this.repoRoot, `${runId}-${manifest.workerId}`, 'verifier', recorder, (snapshot) => this.recordWorktreeHealth(snapshot));
|
|
2131
2150
|
const records = [];
|
|
2132
2151
|
const artifactsPath = recorder.workerDir(manifest.workerId);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2152
|
+
const commandRecords = target?.commandRecords ?? [];
|
|
2153
|
+
const commandOnlySuccess = !target?.diff?.trim()
|
|
2154
|
+
&& commandRecords.length > 0
|
|
2155
|
+
&& commandRecords.every((record) => record.exitCode === 0);
|
|
2156
|
+
// Skip worktree creation if there is no diff. Command-only operational
|
|
2157
|
+
// workers are verified from their command records; pure no-op workers
|
|
2158
|
+
// remain skipped rather than counted as implementation evidence.
|
|
2137
2159
|
if (!target?.diff?.trim()) {
|
|
2160
|
+
if (commandOnlySuccess) {
|
|
2161
|
+
return {
|
|
2162
|
+
workerId: manifest.targetWorkerId ?? 'unknown',
|
|
2163
|
+
verifierId: manifest.workerId,
|
|
2164
|
+
passed: true,
|
|
2165
|
+
status: 'passed',
|
|
2166
|
+
commands: commandRecords,
|
|
2167
|
+
summary: `Verifier accepted command-only evidence (${commandRecords.length} command binding(s) passed).`,
|
|
2168
|
+
artifactsPath,
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2138
2171
|
return {
|
|
2139
2172
|
workerId: manifest.targetWorkerId ?? 'unknown',
|
|
2140
2173
|
verifierId: manifest.workerId,
|
|
2141
|
-
passed:
|
|
2174
|
+
passed: false,
|
|
2175
|
+
status: 'skipped',
|
|
2176
|
+
skippedReason: 'no_candidate_diff',
|
|
2142
2177
|
commands: [],
|
|
2143
2178
|
summary: 'Verifier skipped — no candidate diff to verify (read-only or no-op run).',
|
|
2144
2179
|
artifactsPath,
|
|
@@ -2175,6 +2210,7 @@ export class SubAgentRuntime {
|
|
|
2175
2210
|
workerId: manifest.targetWorkerId ?? 'unknown',
|
|
2176
2211
|
verifierId: manifest.workerId,
|
|
2177
2212
|
passed,
|
|
2213
|
+
status: passed ? 'passed' : 'failed',
|
|
2178
2214
|
commands: records,
|
|
2179
2215
|
summary: passed
|
|
2180
2216
|
? `Verifier passed ${records.length} command(s).`
|
|
@@ -2190,6 +2226,7 @@ export class SubAgentRuntime {
|
|
|
2190
2226
|
workerId: manifest.targetWorkerId ?? 'unknown',
|
|
2191
2227
|
verifierId: manifest.workerId,
|
|
2192
2228
|
passed: false,
|
|
2229
|
+
status: 'failed',
|
|
2193
2230
|
commands: [],
|
|
2194
2231
|
summary,
|
|
2195
2232
|
artifactsPath,
|
|
@@ -2202,6 +2239,14 @@ export class SubAgentRuntime {
|
|
|
2202
2239
|
async applyDecision(recorder, task, decision, consensusPolicy) {
|
|
2203
2240
|
const candidateDiff = this.resolveCandidateDiff(decision);
|
|
2204
2241
|
if (!candidateDiff.trim()) {
|
|
2242
|
+
if (this.isCommandOnlyOperationalSuccess(task, decision)) {
|
|
2243
|
+
const records = decision.winner?.commandRecords ?? [];
|
|
2244
|
+
return {
|
|
2245
|
+
applied: false,
|
|
2246
|
+
rolledBack: false,
|
|
2247
|
+
summary: `Operational workflow completed with command evidence only (${records.length} command binding(s) passed); no repository patch was required.`,
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2205
2250
|
return {
|
|
2206
2251
|
applied: false,
|
|
2207
2252
|
rolledBack: false,
|
|
@@ -2402,6 +2447,20 @@ export class SubAgentRuntime {
|
|
|
2402
2447
|
}
|
|
2403
2448
|
return decision.winner?.diff ?? '';
|
|
2404
2449
|
}
|
|
2450
|
+
isCommandOnlyOperationalSuccess(task, decision) {
|
|
2451
|
+
if (decision.action === 'reject')
|
|
2452
|
+
return false;
|
|
2453
|
+
if (this.resolveCandidateDiff(decision).trim())
|
|
2454
|
+
return false;
|
|
2455
|
+
const winner = decision.winner;
|
|
2456
|
+
const records = winner?.commandRecords ?? [];
|
|
2457
|
+
if (records.length === 0 || !records.every((record) => record.exitCode === 0)) {
|
|
2458
|
+
return false;
|
|
2459
|
+
}
|
|
2460
|
+
return task.releasePolicy.mode === 'ship-ready'
|
|
2461
|
+
|| task.workflowSelectors.some((selector) => /release|deploy|publish|ci|smoke|audit/i.test(selector))
|
|
2462
|
+
|| /release|publish|deploy|npm|github action|ci\/cd|smoke|audit/i.test(task.goal);
|
|
2463
|
+
}
|
|
2405
2464
|
async resolveFileRefs(files) {
|
|
2406
2465
|
return Promise.all(files.map(async (file) => {
|
|
2407
2466
|
const resolved = path.isAbsolute(file) ? file : path.join(this.repoRoot, file);
|
|
@@ -2945,8 +3004,8 @@ export class SubAgentRuntime {
|
|
|
2945
3004
|
};
|
|
2946
3005
|
}
|
|
2947
3006
|
evaluateReviewGates(run) {
|
|
2948
|
-
const verificationPassed = run.verificationResults.filter((result) => result.passed).length;
|
|
2949
|
-
const verificationFailed = run.verificationResults.filter((result) => !result.passed).length;
|
|
3007
|
+
const verificationPassed = run.verificationResults.filter((result) => result.status === 'passed' || (result.passed && result.status !== 'skipped')).length;
|
|
3008
|
+
const verificationFailed = run.verificationResults.filter((result) => result.status === 'failed' || (!result.passed && result.status !== 'skipped')).length;
|
|
2950
3009
|
const hasPlannerEvidence = Boolean(run.plannerState?.ledger?.length || run.plannerResult?.summary || run.executionLedger?.steps?.some((step) => step.id === 'planner-selection' && step.status === 'completed'));
|
|
2951
3010
|
const hasImplementation = run.workerResults.some((result) => result.role === 'coder');
|
|
2952
3011
|
const hasVerifiedImplementation = run.workerResults.some((result) => result.role === 'coder' && result.verified);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.8",
|
|
4
4
|
"description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|