vgxness 1.9.2 → 1.9.3
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/README.md +9 -4
- package/dist/agents/agent-resolver.js +33 -3
- package/dist/agents/canonical-agent-manifest.js +39 -18
- package/dist/agents/canonical-agent-projection.js +31 -4
- package/dist/cli/cli-help.js +14 -3
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/interactive-entrypoint-dispatcher.js +8 -0
- package/dist/cli/commands/memory-sdd-dispatcher.js +71 -5
- package/dist/cli/commands/status-dispatcher.js +130 -0
- package/dist/cli/commands/workflow-dispatcher.js +11 -5
- package/dist/cli/dispatcher.js +9 -1
- package/dist/cli/product-resume-renderer.js +32 -0
- package/dist/cli/product-status-renderer.js +74 -0
- package/dist/cli/sdd-renderer.js +80 -3
- package/dist/code/cli/code-command.js +7 -4
- package/dist/code/reporting/summary.js +4 -1
- package/dist/code/runtime/code-runtime.js +27 -4
- package/dist/code/runtime/sdd-context.js +18 -2
- package/dist/governance/governance-report-builder.js +18 -7
- package/dist/mcp/claude-code-agent-config.js +10 -4
- package/dist/mcp/control-plane.js +56 -0
- package/dist/mcp/provider-status.js +86 -81
- package/dist/mcp/schema.js +25 -7
- package/dist/mcp/stdio-server.js +4 -0
- package/dist/mcp/validation.js +39 -3
- package/dist/resume/product-resume.js +166 -0
- package/dist/runs/repositories/runs.js +12 -1
- package/dist/runs/run-service.js +62 -5
- package/dist/sdd/schema.js +8 -0
- package/dist/sdd/sdd-continuation-plan.js +81 -0
- package/dist/sdd/sdd-workflow-service.js +103 -16
- package/dist/skills/skill-resolver.js +21 -4
- package/dist/status/product-status.js +117 -0
- package/docs/architecture.md +1 -1
- package/docs/cli.md +33 -8
- package/docs/code-runtime.md +3 -0
- package/docs/glossary.md +1 -1
- package/docs/mcp.md +18 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSddPhasePermissionMatrixForPhase, permissionCategories, sddPhasePermissionMatrixVersion } from '../permissions/policy-evaluator.js';
|
|
2
|
-
import { isSddPhase, normalizeSddArtifact } from '../sdd/schema.js';
|
|
2
|
+
import { isSddPhase, normalizeSddArtifact, normalizeSddPhaseInput } from '../sdd/schema.js';
|
|
3
3
|
import { fingerprintManagerProfileOverlay } from './overlay-fingerprint.js';
|
|
4
4
|
export class GovernanceReportBuilder {
|
|
5
5
|
services;
|
|
@@ -27,11 +27,13 @@ export class GovernanceReportBuilder {
|
|
|
27
27
|
warnings.push('run-service-unavailable');
|
|
28
28
|
}
|
|
29
29
|
const workflow = input.workflow ?? run?.workflow;
|
|
30
|
-
const
|
|
30
|
+
const rawPhase = input.phase ?? run?.phase;
|
|
31
|
+
const phase = canonicalReportPhase(workflow, rawPhase);
|
|
31
32
|
const agent = buildAgentRef(input, run);
|
|
32
33
|
const change = input.change;
|
|
33
34
|
const payloadMode = input.payloadMode ?? 'compact';
|
|
34
35
|
const resolvedRunId = input.runId ?? run?.id;
|
|
36
|
+
const permissionsPhase = phase === undefined ? undefined : (normalizeSddPhaseInput(phase) ?? phase);
|
|
35
37
|
const report = {
|
|
36
38
|
version: 1,
|
|
37
39
|
kind: 'governance-report',
|
|
@@ -44,7 +46,7 @@ export class GovernanceReportBuilder {
|
|
|
44
46
|
...(agent === undefined ? {} : { agent }),
|
|
45
47
|
sdd: {},
|
|
46
48
|
permissions: {
|
|
47
|
-
...buildPermissionsMode(
|
|
49
|
+
...buildPermissionsMode(permissionsPhase),
|
|
48
50
|
},
|
|
49
51
|
openCode: {
|
|
50
52
|
mode: 'audit-only',
|
|
@@ -72,7 +74,8 @@ export class GovernanceReportBuilder {
|
|
|
72
74
|
tryBuildOptimizedSddSnapshot(report, project, change, phase, payloadMode, warnings) {
|
|
73
75
|
if (this.services.sdd.getGovernanceSnapshot === undefined)
|
|
74
76
|
return false;
|
|
75
|
-
|
|
77
|
+
const canonicalPhase = phase === undefined ? undefined : normalizeSddPhaseInput(phase);
|
|
78
|
+
if (phase !== undefined && canonicalPhase === undefined) {
|
|
76
79
|
warnings.push('sdd-readiness-skipped-non-sdd-phase');
|
|
77
80
|
return false;
|
|
78
81
|
}
|
|
@@ -80,7 +83,7 @@ export class GovernanceReportBuilder {
|
|
|
80
83
|
project,
|
|
81
84
|
change,
|
|
82
85
|
payloadMode,
|
|
83
|
-
...(
|
|
86
|
+
...(canonicalPhase === undefined ? {} : { phase: canonicalPhase }),
|
|
84
87
|
});
|
|
85
88
|
if (!snapshot.ok) {
|
|
86
89
|
warnings.push(`sdd-snapshot-unavailable:${snapshot.error.code}`);
|
|
@@ -125,8 +128,9 @@ export class GovernanceReportBuilder {
|
|
|
125
128
|
else {
|
|
126
129
|
warnings.push(`sdd-artifacts-unavailable:${listed.error.code}`);
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
const canonicalPhase = phase === undefined ? undefined : normalizeSddPhaseInput(phase);
|
|
132
|
+
if (canonicalPhase !== undefined) {
|
|
133
|
+
const readiness = this.services.sdd.getReady({ project, change, phase: canonicalPhase });
|
|
130
134
|
if (readiness.ok) {
|
|
131
135
|
report.sdd.readiness = readiness.value;
|
|
132
136
|
for (const blocker of readiness.value.blockedPrerequisites ?? [])
|
|
@@ -280,6 +284,13 @@ function buildAgentRef(input, run) {
|
|
|
280
284
|
function dedupe(values) {
|
|
281
285
|
return [...new Set(values)];
|
|
282
286
|
}
|
|
287
|
+
function canonicalReportPhase(workflow, phase) {
|
|
288
|
+
if (phase === undefined)
|
|
289
|
+
return undefined;
|
|
290
|
+
if (workflow !== undefined && workflow.trim().toLowerCase() !== 'sdd')
|
|
291
|
+
return phase;
|
|
292
|
+
return normalizeSddPhaseInput(phase) ?? phase;
|
|
293
|
+
}
|
|
283
294
|
function validationFailure(message) {
|
|
284
295
|
return { ok: false, error: { code: 'validation_failed', message } };
|
|
285
296
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join, resolve } from 'node:path';
|
|
3
|
+
import { join, parse, resolve } from 'node:path';
|
|
4
4
|
import { projectCanonicalAgentManifestToClaudeCode } from '../agents/canonical-agent-projection.js';
|
|
5
5
|
import { assertInsideWorkspace } from './claude-code-config.js';
|
|
6
6
|
export const claudeCodeGeneratedMarker = 'VGXNESS-GENERATED';
|
|
@@ -29,10 +29,16 @@ export function resolveClaudeAgentTarget(input) {
|
|
|
29
29
|
return { workspaceRoot: input.workspaceRoot, scope, directoryPath: join(home, '.claude', 'agents'), ...(input.env === undefined ? {} : { env: input.env }) };
|
|
30
30
|
}
|
|
31
31
|
export function safeHomeDirectory(env = process.env) {
|
|
32
|
-
const
|
|
33
|
-
|
|
32
|
+
const homeDrive = env.HOMEDRIVE?.trim();
|
|
33
|
+
const homePath = env.HOMEPATH?.trim();
|
|
34
|
+
const homeDrivePath = homeDrive && homePath ? `${homeDrive}${homePath}` : '';
|
|
35
|
+
const candidate = env.HOME?.trim() || env.USERPROFILE?.trim() || homeDrivePath || homedir();
|
|
36
|
+
if (!candidate || candidate.startsWith('~') || candidate.includes('\0'))
|
|
34
37
|
throw new Error('Unable to resolve a safe home directory for Claude user agents.');
|
|
35
|
-
|
|
38
|
+
const expanded = resolve(candidate);
|
|
39
|
+
if (parse(expanded).root === expanded)
|
|
40
|
+
throw new Error('Unable to resolve a safe home directory for Claude user agents.');
|
|
41
|
+
return expanded;
|
|
36
42
|
}
|
|
37
43
|
export function isVgxnessOwnedClaudeAgentMarkdown(contents) {
|
|
38
44
|
return contents.includes(claudeCodeGeneratedMarker) && contents.includes('provider=claude') && contents.includes('artifact=claude-code-subagent');
|
|
@@ -8,6 +8,7 @@ import { openMemoryDatabase } from '../memory/sqlite/database.js';
|
|
|
8
8
|
import { prepareMemoryDatabasePath, resolveMemoryDatabasePath } from '../memory/storage-paths.js';
|
|
9
9
|
import { OpenCodeManagerPayloadService } from '../providers/opencode/manager-payload.js';
|
|
10
10
|
import { RunService } from '../runs/run-service.js';
|
|
11
|
+
import { sddContinuationPlanFrom } from '../sdd/sdd-continuation-plan.js';
|
|
11
12
|
import { SddWorkflowService } from '../sdd/sdd-workflow-service.js';
|
|
12
13
|
import { SkillRegistryService } from '../skills/skill-registry-service.js';
|
|
13
14
|
import { VerificationPlanService } from '../verification/index.js';
|
|
@@ -43,6 +44,8 @@ export function callVgxTool(call, services) {
|
|
|
43
44
|
return toEnvelope(validated.tool, services.sdd.getNext(validated.input));
|
|
44
45
|
case 'vgxness_sdd_cockpit':
|
|
45
46
|
return toEnvelope(validated.tool, services.sdd.getCockpit(validated.input));
|
|
47
|
+
case 'vgxness_sdd_continue':
|
|
48
|
+
return sddContinueEnvelope(validated.input, services);
|
|
46
49
|
case 'vgxness_governance_report':
|
|
47
50
|
return auditedEnvelope(validated.tool, new GovernanceReportBuilder({
|
|
48
51
|
sdd: governanceSddServices(services.sdd),
|
|
@@ -96,6 +99,8 @@ export function callVgxTool(call, services) {
|
|
|
96
99
|
return toEnvelope(validated.tool, services.runs.appendCheckpoint(validated.input));
|
|
97
100
|
case 'vgxness_run_finalize':
|
|
98
101
|
return toEnvelope(validated.tool, services.runs.updateFinalStatus(validated.input));
|
|
102
|
+
case 'vgxness_run_resume_candidates':
|
|
103
|
+
return runResumeCandidatesEnvelope(validated.input, services);
|
|
99
104
|
case 'vgxness_run_resume_inspect':
|
|
100
105
|
return toEnvelope(validated.tool, services.runs.getRunOperatorResumePlan(validated.input.runId));
|
|
101
106
|
case 'vgxness_run_resume_gate':
|
|
@@ -238,6 +243,23 @@ function governanceSddServices(services) {
|
|
|
238
243
|
},
|
|
239
244
|
};
|
|
240
245
|
}
|
|
246
|
+
function sddContinueEnvelope(input, services) {
|
|
247
|
+
const next = services.sdd.getNext({ project: input.project, change: input.change });
|
|
248
|
+
if (!next.ok)
|
|
249
|
+
return errorEnvelope(next.error.code, next.error.message, 'vgxness_sdd_continue');
|
|
250
|
+
const cockpit = services.sdd.getCockpit({ project: input.project, change: input.change });
|
|
251
|
+
if (!cockpit.ok)
|
|
252
|
+
return errorEnvelope(cockpit.error.code, cockpit.error.message, 'vgxness_sdd_continue');
|
|
253
|
+
const relatedRun = services.runs.findRelatedInterruptedSddRun({ project: input.project, change: input.change });
|
|
254
|
+
if (!relatedRun.ok)
|
|
255
|
+
return errorEnvelope(relatedRun.error.code, relatedRun.error.message, 'vgxness_sdd_continue');
|
|
256
|
+
return successEnvelope('vgxness_sdd_continue', sddContinuationPlanFrom({
|
|
257
|
+
project: input.project,
|
|
258
|
+
next: next.value,
|
|
259
|
+
cockpit: cockpit.value,
|
|
260
|
+
...(relatedRun.value === undefined ? {} : { relatedRunContext: relatedRun.value }),
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
241
263
|
export function createVgxMcpControlPlane(options = {}) {
|
|
242
264
|
const databasePath = resolveControlPlaneDatabasePath(options);
|
|
243
265
|
if (!databasePath.ok)
|
|
@@ -346,6 +368,40 @@ function listRunsEnvelope(input, services) {
|
|
|
346
368
|
? successEnvelope('vgxness_run_list', result.value.slice(0, limit))
|
|
347
369
|
: errorEnvelope(result.error.code, result.error.message, 'vgxness_run_list');
|
|
348
370
|
}
|
|
371
|
+
function runResumeCandidatesEnvelope(input, services) {
|
|
372
|
+
const limit = input.limit ?? 5;
|
|
373
|
+
const result = services.runs.listRecentInterruptedRuns({ project: input.project, limit });
|
|
374
|
+
if (!result.ok)
|
|
375
|
+
return errorEnvelope(result.error.code, result.error.message, 'vgxness_run_resume_candidates');
|
|
376
|
+
return successEnvelope('vgxness_run_resume_candidates', {
|
|
377
|
+
kind: 'run-resume-candidates',
|
|
378
|
+
version: 1,
|
|
379
|
+
project: input.project,
|
|
380
|
+
statuses: ['failed', 'blocked', 'needs-human'],
|
|
381
|
+
limit,
|
|
382
|
+
candidates: result.value.map((candidate) => ({
|
|
383
|
+
...candidate,
|
|
384
|
+
recommendation: 'Inspect this interrupted run before deciding whether to continue manually.',
|
|
385
|
+
inspectTool: 'run_resume_inspect',
|
|
386
|
+
inspectInput: { runId: candidate.runId },
|
|
387
|
+
resumeCommand: `vgxness resume --project ${input.project} --run-id ${candidate.runId}`,
|
|
388
|
+
})),
|
|
389
|
+
nextStep: result.value.length === 0
|
|
390
|
+
? 'No interrupted run candidates were found for this project. Start from the current project status or a known runId.'
|
|
391
|
+
: 'Call run_resume_inspect with a candidate runId, then use run_resume_gate only when inspect returns a relevant approvalId.',
|
|
392
|
+
safety: {
|
|
393
|
+
readOnly: true,
|
|
394
|
+
runMutation: false,
|
|
395
|
+
retryAdmitted: false,
|
|
396
|
+
providerInvoked: false,
|
|
397
|
+
writesProviderConfig: false,
|
|
398
|
+
writesArtifacts: false,
|
|
399
|
+
writesOpenSpec: false,
|
|
400
|
+
createsSandbox: false,
|
|
401
|
+
createsWorktree: false,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
349
405
|
function toEnvelope(tool, result) {
|
|
350
406
|
return result.ok ? successEnvelope(tool, result.value) : errorEnvelope(result.error.code, result.error.message, tool);
|
|
351
407
|
}
|
|
@@ -52,35 +52,36 @@ export class ProviderStatusService {
|
|
|
52
52
|
const compactBytes = Buffer.byteLength(JSON.stringify(compactShape), 'utf8');
|
|
53
53
|
const issueCount = [canonicalAgentManifest.status, configStatus, ...tools.map((tool) => (tool.present ? 'pass' : 'fail'))].filter((item) => item === 'fail' || item === 'not-configured').length;
|
|
54
54
|
const warningCount = [configStatus, mcpEntry.status, ...paths.map((path) => path.status)].filter((item) => item === 'warn').length;
|
|
55
|
+
const reportBase = {
|
|
56
|
+
version: 1,
|
|
57
|
+
kind: 'provider-status',
|
|
58
|
+
project: normalized.project,
|
|
59
|
+
providerAdapter: normalized.providerAdapter,
|
|
60
|
+
scope: normalized.scope,
|
|
61
|
+
workspaceRoot: normalized.workspaceRoot,
|
|
62
|
+
status,
|
|
63
|
+
overallStatus: status,
|
|
64
|
+
inspectedPaths: checkedPaths,
|
|
65
|
+
issueCount,
|
|
66
|
+
warningCount,
|
|
67
|
+
summary: summarizeStatus(status, mcpEntry),
|
|
68
|
+
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
69
|
+
checkedPaths,
|
|
70
|
+
canonicalAgentManifest,
|
|
71
|
+
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
72
|
+
mcpRequiredTools: tools,
|
|
73
|
+
originalBytes,
|
|
74
|
+
compactBytes,
|
|
75
|
+
verboseAvailable: normalized.payloadMode === 'compact',
|
|
76
|
+
fullContentRef: `provider-status:${normalized.providerAdapter}:${normalized.workspaceRoot}`,
|
|
77
|
+
generatedAt,
|
|
78
|
+
safety: PROVIDER_HEALTH_SAFETY,
|
|
79
|
+
};
|
|
55
80
|
return {
|
|
56
81
|
ok: true,
|
|
57
|
-
value:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
project: normalized.project,
|
|
61
|
-
providerAdapter: normalized.providerAdapter,
|
|
62
|
-
scope: normalized.scope,
|
|
63
|
-
workspaceRoot: normalized.workspaceRoot,
|
|
64
|
-
status,
|
|
65
|
-
payloadMode: normalized.payloadMode,
|
|
66
|
-
overallStatus: status,
|
|
67
|
-
inspectedPaths: checkedPaths,
|
|
68
|
-
issueCount,
|
|
69
|
-
warningCount,
|
|
70
|
-
summary: summarizeStatus(status, mcpEntry),
|
|
71
|
-
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
72
|
-
checkedPaths,
|
|
73
|
-
canonicalAgentManifest,
|
|
74
|
-
config: { status: configStatus, paths: compactPaths(paths, normalized.payloadMode), mcpEntry: compactMcpEntry(mcpEntry, normalized.payloadMode) },
|
|
75
|
-
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
76
|
-
mcpRequiredTools: tools,
|
|
77
|
-
originalBytes,
|
|
78
|
-
compactBytes,
|
|
79
|
-
verboseAvailable: normalized.payloadMode === 'compact',
|
|
80
|
-
fullContentRef: `provider-status:${normalized.providerAdapter}:${normalized.workspaceRoot}`,
|
|
81
|
-
generatedAt,
|
|
82
|
-
safety: PROVIDER_HEALTH_SAFETY,
|
|
83
|
-
},
|
|
82
|
+
value: normalized.payloadMode === 'verbose'
|
|
83
|
+
? { ...reportBase, payloadMode: 'verbose', config: { status: configStatus, paths: compactPaths(paths, 'verbose'), mcpEntry: compactMcpEntry(mcpEntry, 'verbose') } }
|
|
84
|
+
: { ...reportBase, payloadMode: 'compact', config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') } },
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
readSdd(project, change) {
|
|
@@ -115,35 +116,36 @@ export class ProviderStatusService {
|
|
|
115
116
|
const compactBytes = Buffer.byteLength(JSON.stringify(compactShape), 'utf8');
|
|
116
117
|
const issueCount = [canonicalAgentManifest.status, configStatus, ...agentStatuses].filter((item) => item === 'fail' || item === 'not-configured').length;
|
|
117
118
|
const warningCount = advisory.length + resolvedScope.value.warnings.length + paths.filter((path) => path.status === 'warn').length;
|
|
119
|
+
const reportBase = {
|
|
120
|
+
version: 1,
|
|
121
|
+
kind: 'provider-status',
|
|
122
|
+
project: normalized.project,
|
|
123
|
+
providerAdapter: 'claude',
|
|
124
|
+
scope: normalized.scope,
|
|
125
|
+
workspaceRoot: normalized.workspaceRoot,
|
|
126
|
+
status,
|
|
127
|
+
overallStatus: status,
|
|
128
|
+
inspectedPaths: checkedPaths,
|
|
129
|
+
issueCount,
|
|
130
|
+
warningCount,
|
|
131
|
+
summary: summarizeClaudeStatus(status, mcpEntry),
|
|
132
|
+
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
133
|
+
checkedPaths,
|
|
134
|
+
canonicalAgentManifest,
|
|
135
|
+
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
136
|
+
mcpRequiredTools: tools,
|
|
137
|
+
originalBytes,
|
|
138
|
+
compactBytes,
|
|
139
|
+
verboseAvailable: normalized.payloadMode === 'compact',
|
|
140
|
+
fullContentRef: `provider-status:claude:${normalized.workspaceRoot}`,
|
|
141
|
+
generatedAt: 'read-only-snapshot',
|
|
142
|
+
safety: PROVIDER_HEALTH_SAFETY,
|
|
143
|
+
};
|
|
118
144
|
return {
|
|
119
145
|
ok: true,
|
|
120
|
-
value:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
project: normalized.project,
|
|
124
|
-
providerAdapter: 'claude',
|
|
125
|
-
scope: normalized.scope,
|
|
126
|
-
workspaceRoot: normalized.workspaceRoot,
|
|
127
|
-
status,
|
|
128
|
-
payloadMode: normalized.payloadMode,
|
|
129
|
-
overallStatus: status,
|
|
130
|
-
inspectedPaths: checkedPaths,
|
|
131
|
-
issueCount,
|
|
132
|
-
warningCount,
|
|
133
|
-
summary: summarizeClaudeStatus(status, mcpEntry),
|
|
134
|
-
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
135
|
-
checkedPaths,
|
|
136
|
-
canonicalAgentManifest,
|
|
137
|
-
config: { status: configStatus, paths: compactPaths(paths, normalized.payloadMode), mcpEntry: compactMcpEntry(mcpEntry, normalized.payloadMode) },
|
|
138
|
-
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
139
|
-
mcpRequiredTools: tools,
|
|
140
|
-
originalBytes,
|
|
141
|
-
compactBytes,
|
|
142
|
-
verboseAvailable: normalized.payloadMode === 'compact',
|
|
143
|
-
fullContentRef: `provider-status:claude:${normalized.workspaceRoot}`,
|
|
144
|
-
generatedAt: 'read-only-snapshot',
|
|
145
|
-
safety: PROVIDER_HEALTH_SAFETY,
|
|
146
|
-
},
|
|
146
|
+
value: normalized.payloadMode === 'verbose'
|
|
147
|
+
? { ...reportBase, payloadMode: 'verbose', config: { status: configStatus, paths: compactPaths(paths, 'verbose'), mcpEntry: compactMcpEntry(mcpEntry, 'verbose') } }
|
|
148
|
+
: { ...reportBase, payloadMode: 'compact', config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') } },
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
getClaudeUserGlobalStatus(normalized, canonicalScope = 'user', scopeWarnings = []) {
|
|
@@ -162,35 +164,38 @@ export class ProviderStatusService {
|
|
|
162
164
|
const checkedPaths = normalized.payloadMode === 'verbose' ? [mcpState.path, agents.directoryPath, ...agents.agents.map((agent) => agent.path), userMemory.path] : [mcpState.path, userMemory.path, ...agents.agents.filter((agent) => agent.exists || agent.status !== 'missing').map((agent) => agent.path)];
|
|
163
165
|
const verboseShape = { config: { status: configStatus, paths, mcpEntry }, canonicalAgentManifest, agents, userMemory: { status: userMemory.status, action: userMemory.action }, scopeCapabilities: CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES, scopeWarnings, sdd, mcpRequiredTools: tools };
|
|
164
166
|
const compactShape = { config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') }, canonicalAgentManifest, agentSummary: summarizeClaudeAgents(agents), userMemory: { status: userMemory.status, action: userMemory.action }, scopeCapabilities: CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES, sdd: sdd === undefined ? undefined : compactSdd(sdd, 'compact'), mcpRequiredTools: tools };
|
|
167
|
+
const originalBytes = Buffer.byteLength(JSON.stringify(verboseShape), 'utf8');
|
|
168
|
+
const compactBytes = Buffer.byteLength(JSON.stringify(compactShape), 'utf8');
|
|
169
|
+
const reportBase = {
|
|
170
|
+
version: 1,
|
|
171
|
+
kind: 'provider-status',
|
|
172
|
+
project: normalized.project,
|
|
173
|
+
providerAdapter: 'claude',
|
|
174
|
+
scope: normalized.scope,
|
|
175
|
+
workspaceRoot: normalized.workspaceRoot,
|
|
176
|
+
status,
|
|
177
|
+
overallStatus: status,
|
|
178
|
+
inspectedPaths: checkedPaths,
|
|
179
|
+
issueCount: [canonicalAgentManifest.status, configStatus, ...agentStatuses].filter((item) => item === 'fail' || item === 'not-configured').length,
|
|
180
|
+
warningCount: scopeWarnings.length + paths.filter((path) => path.status === 'warn').length + 1,
|
|
181
|
+
summary: summarizeClaudeStatus(status, mcpEntry),
|
|
182
|
+
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
183
|
+
checkedPaths,
|
|
184
|
+
canonicalAgentManifest,
|
|
185
|
+
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
186
|
+
mcpRequiredTools: tools,
|
|
187
|
+
originalBytes,
|
|
188
|
+
compactBytes,
|
|
189
|
+
verboseAvailable: normalized.payloadMode === 'compact',
|
|
190
|
+
fullContentRef: `provider-status:claude:user-global:${normalized.workspaceRoot}`,
|
|
191
|
+
generatedAt: 'read-only-snapshot',
|
|
192
|
+
safety: { ...PROVIDER_HEALTH_SAFETY, scopeCapabilities: CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES },
|
|
193
|
+
};
|
|
165
194
|
return {
|
|
166
195
|
ok: true,
|
|
167
|
-
value:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
project: normalized.project,
|
|
171
|
-
providerAdapter: 'claude',
|
|
172
|
-
scope: normalized.scope,
|
|
173
|
-
workspaceRoot: normalized.workspaceRoot,
|
|
174
|
-
status,
|
|
175
|
-
payloadMode: normalized.payloadMode,
|
|
176
|
-
overallStatus: status,
|
|
177
|
-
inspectedPaths: checkedPaths,
|
|
178
|
-
issueCount: [canonicalAgentManifest.status, configStatus, ...agentStatuses].filter((item) => item === 'fail' || item === 'not-configured').length,
|
|
179
|
-
warningCount: scopeWarnings.length + paths.filter((path) => path.status === 'warn').length + 1,
|
|
180
|
-
summary: summarizeClaudeStatus(status, mcpEntry),
|
|
181
|
-
nextAction: nextActionFor(status, mcpEntry, sdd?.next),
|
|
182
|
-
checkedPaths,
|
|
183
|
-
canonicalAgentManifest,
|
|
184
|
-
config: { status: configStatus, paths: compactPaths(paths, normalized.payloadMode), mcpEntry: compactMcpEntry(mcpEntry, normalized.payloadMode) },
|
|
185
|
-
...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
|
|
186
|
-
mcpRequiredTools: tools,
|
|
187
|
-
originalBytes: Buffer.byteLength(JSON.stringify(verboseShape), 'utf8'),
|
|
188
|
-
compactBytes: Buffer.byteLength(JSON.stringify(compactShape), 'utf8'),
|
|
189
|
-
verboseAvailable: normalized.payloadMode === 'compact',
|
|
190
|
-
fullContentRef: `provider-status:claude:user-global:${normalized.workspaceRoot}`,
|
|
191
|
-
generatedAt: 'read-only-snapshot',
|
|
192
|
-
safety: { ...PROVIDER_HEALTH_SAFETY, scopeCapabilities: CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES },
|
|
193
|
-
},
|
|
196
|
+
value: normalized.payloadMode === 'verbose'
|
|
197
|
+
? { ...reportBase, payloadMode: 'verbose', config: { status: configStatus, paths: compactPaths(paths, 'verbose'), mcpEntry: compactMcpEntry(mcpEntry, 'verbose') } }
|
|
198
|
+
: { ...reportBase, payloadMode: 'compact', config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') } },
|
|
194
199
|
};
|
|
195
200
|
}
|
|
196
201
|
}
|
package/dist/mcp/schema.js
CHANGED
|
@@ -14,6 +14,7 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
|
|
|
14
14
|
'vgxness_sdd_list_artifacts',
|
|
15
15
|
'vgxness_sdd_next',
|
|
16
16
|
'vgxness_sdd_cockpit',
|
|
17
|
+
'vgxness_sdd_continue',
|
|
17
18
|
'vgxness_governance_report',
|
|
18
19
|
'vgxness_memory_save',
|
|
19
20
|
'vgxness_memory_search',
|
|
@@ -37,6 +38,7 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
|
|
|
37
38
|
'vgxness_run_start',
|
|
38
39
|
'vgxness_run_checkpoint',
|
|
39
40
|
'vgxness_run_finalize',
|
|
41
|
+
'vgxness_run_resume_candidates',
|
|
40
42
|
'vgxness_run_resume_inspect',
|
|
41
43
|
'vgxness_run_resume_gate',
|
|
42
44
|
'vgxness_provider_status',
|
|
@@ -55,6 +57,7 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
|
|
|
55
57
|
'sdd_list_artifacts',
|
|
56
58
|
'sdd_next',
|
|
57
59
|
'sdd_cockpit',
|
|
60
|
+
'sdd_continue',
|
|
58
61
|
'governance_report',
|
|
59
62
|
'memory_save',
|
|
60
63
|
'memory_search',
|
|
@@ -78,6 +81,7 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
|
|
|
78
81
|
'run_start',
|
|
79
82
|
'run_checkpoint',
|
|
80
83
|
'run_finalize',
|
|
84
|
+
'run_resume_candidates',
|
|
81
85
|
'run_resume_inspect',
|
|
82
86
|
'run_resume_gate',
|
|
83
87
|
'provider_status',
|
|
@@ -104,6 +108,7 @@ const payloadModes = ['compact', 'verbose'];
|
|
|
104
108
|
const contextCockpitLevels = ['compact', 'expanded', 'verbose'];
|
|
105
109
|
const providerChangePlanProviders = ['opencode', 'claude', 'antigravity', 'custom'];
|
|
106
110
|
const providerChangePlanTypes = ['opencode-mcp-install', 'claude-mcp-install', 'setup', 'install', 'config-preparation'];
|
|
111
|
+
const sddPhaseInputSchema = z.union([z.enum(sddPhases), z.literal('apply')]);
|
|
107
112
|
const jsonValueSchema = z.lazy(() => z.union([z.string(), z.number().finite(), z.boolean(), z.null(), z.array(jsonValueSchema), z.record(z.string(), jsonValueSchema)]));
|
|
108
113
|
export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
109
114
|
vgxness_sdd_status: z
|
|
@@ -116,7 +121,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
116
121
|
.object({
|
|
117
122
|
project: z.string().min(1),
|
|
118
123
|
change: z.string().min(1),
|
|
119
|
-
phase:
|
|
124
|
+
phase: sddPhaseInputSchema,
|
|
120
125
|
runId: z.string().min(1).optional(),
|
|
121
126
|
agentId: z.string().min(1).optional(),
|
|
122
127
|
})
|
|
@@ -125,7 +130,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
125
130
|
.object({
|
|
126
131
|
project: z.string().min(1),
|
|
127
132
|
change: z.string().min(1),
|
|
128
|
-
phase:
|
|
133
|
+
phase: sddPhaseInputSchema,
|
|
129
134
|
runId: z.string().min(1).optional(),
|
|
130
135
|
agentId: z.string().min(1).optional(),
|
|
131
136
|
})
|
|
@@ -134,7 +139,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
134
139
|
.object({
|
|
135
140
|
project: z.string().min(1),
|
|
136
141
|
change: z.string().min(1),
|
|
137
|
-
phase:
|
|
142
|
+
phase: sddPhaseInputSchema,
|
|
138
143
|
content: z.string().min(1),
|
|
139
144
|
})
|
|
140
145
|
.passthrough(),
|
|
@@ -142,7 +147,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
142
147
|
.object({
|
|
143
148
|
project: z.string().min(1),
|
|
144
149
|
change: z.string().min(1),
|
|
145
|
-
phase:
|
|
150
|
+
phase: sddPhaseInputSchema,
|
|
146
151
|
acceptedBy: z.object({
|
|
147
152
|
type: z.literal('human'),
|
|
148
153
|
id: z.string().min(1),
|
|
@@ -158,7 +163,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
158
163
|
.object({
|
|
159
164
|
project: z.string().min(1),
|
|
160
165
|
change: z.string().min(1),
|
|
161
|
-
phase:
|
|
166
|
+
phase: sddPhaseInputSchema,
|
|
162
167
|
reopenedBy: z.object({
|
|
163
168
|
type: z.literal('human'),
|
|
164
169
|
id: z.string().min(1),
|
|
@@ -174,7 +179,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
174
179
|
.object({
|
|
175
180
|
project: z.string().min(1),
|
|
176
181
|
change: z.string().min(1),
|
|
177
|
-
phase:
|
|
182
|
+
phase: sddPhaseInputSchema,
|
|
178
183
|
payloadMode: z.enum(payloadModes).optional(),
|
|
179
184
|
})
|
|
180
185
|
.passthrough(),
|
|
@@ -197,6 +202,13 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
197
202
|
change: z.string().min(1),
|
|
198
203
|
})
|
|
199
204
|
.passthrough(),
|
|
205
|
+
vgxness_sdd_continue: z
|
|
206
|
+
.object({
|
|
207
|
+
project: z.string().min(1),
|
|
208
|
+
change: z.string().min(1),
|
|
209
|
+
payloadMode: z.enum(payloadModes).optional(),
|
|
210
|
+
})
|
|
211
|
+
.passthrough(),
|
|
200
212
|
vgxness_governance_report: z
|
|
201
213
|
.object({
|
|
202
214
|
project: z.string().min(1),
|
|
@@ -364,7 +376,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
364
376
|
workspaceRoot: z.string().min(1).optional(),
|
|
365
377
|
maxSourceBytes: z.number().int().positive().optional(),
|
|
366
378
|
change: z.string().min(1).optional(),
|
|
367
|
-
phase:
|
|
379
|
+
phase: sddPhaseInputSchema.optional(),
|
|
368
380
|
})
|
|
369
381
|
.passthrough(),
|
|
370
382
|
vgxness_run_list: z
|
|
@@ -431,6 +443,12 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
431
443
|
outcomeReason: z.string().min(1).optional(),
|
|
432
444
|
})
|
|
433
445
|
.passthrough(),
|
|
446
|
+
vgxness_run_resume_candidates: z
|
|
447
|
+
.object({
|
|
448
|
+
project: z.string().min(1),
|
|
449
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
450
|
+
})
|
|
451
|
+
.passthrough(),
|
|
434
452
|
vgxness_run_resume_inspect: z
|
|
435
453
|
.object({
|
|
436
454
|
runId: z.string().min(1),
|
package/dist/mcp/stdio-server.js
CHANGED
|
@@ -57,6 +57,8 @@ function descriptionForTool(publicToolName) {
|
|
|
57
57
|
return 'Read-only OpenCode handoff preview; returns context for manual continuation only, does not execute/control OpenCode, write .opencode/provider config, or create runs, checkpoints, events, sessions, or skill-usage records.';
|
|
58
58
|
if (publicToolName === 'verification_plan')
|
|
59
59
|
return 'Read-only verification plan recommendations only; does not execute commands, write provider config, persist results, create checkpoints, or infer SDD acceptance.';
|
|
60
|
+
if (publicToolName === 'run_resume_candidates')
|
|
61
|
+
return 'Read-only interrupted run resume candidate discovery by project; lists bounded failed, blocked, and needs-human runs without mutation, retry admission, provider execution, artifact/config/openspec writes, sandboxes, worktrees, or sessions.';
|
|
60
62
|
if (publicToolName === 'run_resume_inspect')
|
|
61
63
|
return 'Read-only run resume advisory inspect; plan-only and does not execute resume logic, invoke providers, write provider config, mutate retry/abandon/attempt state, or reconstruct sandboxes, worktrees, sessions, or transcripts.';
|
|
62
64
|
if (publicToolName === 'run_resume_gate')
|
|
@@ -65,6 +67,8 @@ function descriptionForTool(publicToolName) {
|
|
|
65
67
|
return 'Read-only context cockpit for start/resume/recovery; returns latest restorable session plus bounded memory previews without traces, provider config writes, repository writes, runs, artifacts, or session mutations.';
|
|
66
68
|
if (publicToolName === 'sdd_cockpit')
|
|
67
69
|
return 'Read-only SDD cockpit summary with next decision, explicit acceptance state, metadata-only artifact summaries, and aggregate blockers.';
|
|
70
|
+
if (publicToolName === 'sdd_continue')
|
|
71
|
+
return 'Read-only SDD continuation planner; returns blocker actions, safe suggested commands, related interrupted run context, and safety notes without provider execution, run creation, artifact mutation, provider config writes, or openspec writes.';
|
|
68
72
|
const toolName = toInternalVgxMcpToolName(publicToolName);
|
|
69
73
|
return `VGX control-plane tool ${toolName}`;
|
|
70
74
|
}
|
package/dist/mcp/validation.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isRiskyPermissionCategory, permissionCategories } from '../permissions/schema.js';
|
|
2
2
|
import { parseOperationRetryPolicy } from '../runs/operation-retry.js';
|
|
3
|
-
import {
|
|
3
|
+
import { normalizeSddPhaseInput, sddPhases } from '../sdd/schema.js';
|
|
4
4
|
import { workflowIds } from '../workflows/schema.js';
|
|
5
5
|
import { supportedTypeMessage, verificationChangeTypes } from '../verification/index.js';
|
|
6
6
|
import { errorEnvelope, isVgxMcpToolName, } from './schema.js';
|
|
@@ -52,6 +52,8 @@ export function validateVgxMcpToolCall(call) {
|
|
|
52
52
|
return validationSuccess(tool.value, validateSddNextInput(input, tool.value));
|
|
53
53
|
case 'vgxness_sdd_cockpit':
|
|
54
54
|
return validationSuccess(tool.value, validateSddCockpitInput(input, tool.value));
|
|
55
|
+
case 'vgxness_sdd_continue':
|
|
56
|
+
return validationSuccess(tool.value, validateSddContinueInput(input, tool.value));
|
|
55
57
|
case 'vgxness_governance_report':
|
|
56
58
|
return validationSuccess(tool.value, validateGovernanceReportInput(input, tool.value));
|
|
57
59
|
case 'vgxness_memory_save':
|
|
@@ -98,6 +100,8 @@ export function validateVgxMcpToolCall(call) {
|
|
|
98
100
|
return validationSuccess(tool.value, validateRunCheckpointInput(input, tool.value));
|
|
99
101
|
case 'vgxness_run_finalize':
|
|
100
102
|
return validationSuccess(tool.value, validateRunFinalizeInput(input, tool.value));
|
|
103
|
+
case 'vgxness_run_resume_candidates':
|
|
104
|
+
return validationSuccess(tool.value, validateRunResumeCandidatesInput(input, tool.value));
|
|
101
105
|
case 'vgxness_run_resume_inspect':
|
|
102
106
|
return validationSuccess(tool.value, validateRunResumeInspectInput(input, tool.value));
|
|
103
107
|
case 'vgxness_run_resume_gate':
|
|
@@ -240,6 +244,21 @@ function validateSddCockpitInput(input, tool) {
|
|
|
240
244
|
return record;
|
|
241
245
|
return readProjectAndChange(record.value, tool);
|
|
242
246
|
}
|
|
247
|
+
function validateSddContinueInput(input, tool) {
|
|
248
|
+
const record = inputRecord(input, tool, ['project', 'change', 'payloadMode']);
|
|
249
|
+
if (!record.ok)
|
|
250
|
+
return record;
|
|
251
|
+
const base = readProjectAndChange(record.value, tool);
|
|
252
|
+
if (!base.ok)
|
|
253
|
+
return base;
|
|
254
|
+
const payloadMode = readOptionalOneOf(record.value, 'payloadMode', payloadModes, tool);
|
|
255
|
+
if (!payloadMode.ok)
|
|
256
|
+
return payloadMode;
|
|
257
|
+
const result = { ...base.value };
|
|
258
|
+
if (payloadMode.value !== undefined)
|
|
259
|
+
result.payloadMode = payloadMode.value;
|
|
260
|
+
return { ok: true, value: result };
|
|
261
|
+
}
|
|
243
262
|
function validateContextCockpitInput(input, tool) {
|
|
244
263
|
const record = inputRecord(input, tool, ['project', 'change', 'directory', 'limit', 'level']);
|
|
245
264
|
if (!record.ok)
|
|
@@ -971,6 +990,22 @@ function validateRunFinalizeInput(input, tool) {
|
|
|
971
990
|
return copied;
|
|
972
991
|
return { ok: true, value: result };
|
|
973
992
|
}
|
|
993
|
+
function validateRunResumeCandidatesInput(input, tool) {
|
|
994
|
+
const record = inputRecord(input, tool, ['project', 'limit']);
|
|
995
|
+
if (!record.ok)
|
|
996
|
+
return record;
|
|
997
|
+
const project = readNonEmptyString(record.value, 'project', tool);
|
|
998
|
+
if (!project.ok)
|
|
999
|
+
return project;
|
|
1000
|
+
const result = { project: project.value };
|
|
1001
|
+
if (record.value.limit !== undefined) {
|
|
1002
|
+
const limit = readBoundedLimit(record.value, tool);
|
|
1003
|
+
if (!limit.ok)
|
|
1004
|
+
return limit;
|
|
1005
|
+
result.limit = limit.value;
|
|
1006
|
+
}
|
|
1007
|
+
return { ok: true, value: result };
|
|
1008
|
+
}
|
|
974
1009
|
function validateRunResumeInspectInput(input, tool) {
|
|
975
1010
|
const record = inputRecord(input, tool, ['runId']);
|
|
976
1011
|
if (!record.ok)
|
|
@@ -1053,9 +1088,10 @@ function readPhase(record, tool) {
|
|
|
1053
1088
|
const phase = readNonEmptyString(record, 'phase', tool);
|
|
1054
1089
|
if (!phase.ok)
|
|
1055
1090
|
return phase;
|
|
1056
|
-
|
|
1091
|
+
const normalized = normalizeSddPhaseInput(phase.value);
|
|
1092
|
+
if (normalized === undefined)
|
|
1057
1093
|
return validationFailure(`phase must be one of: ${sddPhases.join(', ')}`, tool);
|
|
1058
|
-
return { ok: true, value:
|
|
1094
|
+
return { ok: true, value: normalized };
|
|
1059
1095
|
}
|
|
1060
1096
|
function copyOptionalStrings(target, record, tool, fields) {
|
|
1061
1097
|
for (const field of fields) {
|