vgxness 1.9.1 → 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.
Files changed (54) hide show
  1. package/README.md +15 -5
  2. package/dist/agents/agent-activation-service.js +13 -4
  3. package/dist/agents/agent-registry-service.js +8 -2
  4. package/dist/agents/agent-resolver.js +33 -3
  5. package/dist/agents/agent-seed-upgrade-service.js +231 -0
  6. package/dist/agents/boot-upgrade.js +59 -0
  7. package/dist/agents/canonical-agent-manifest.js +39 -18
  8. package/dist/agents/canonical-agent-projection.js +38 -4
  9. package/dist/agents/manager-profile-overlay-service.js +14 -0
  10. package/dist/agents/repositories/agent-seed-history.js +128 -0
  11. package/dist/cli/cli-help.js +14 -3
  12. package/dist/cli/commands/index.js +1 -0
  13. package/dist/cli/commands/interactive-entrypoint-dispatcher.js +8 -0
  14. package/dist/cli/commands/mcp-dispatcher.js +7 -0
  15. package/dist/cli/commands/memory-sdd-dispatcher.js +71 -5
  16. package/dist/cli/commands/status-dispatcher.js +130 -0
  17. package/dist/cli/commands/workflow-dispatcher.js +11 -5
  18. package/dist/cli/dispatcher.js +11 -1
  19. package/dist/cli/product-resume-renderer.js +32 -0
  20. package/dist/cli/product-status-renderer.js +74 -0
  21. package/dist/cli/sdd-renderer.js +80 -3
  22. package/dist/code/cli/code-command.js +7 -4
  23. package/dist/code/reporting/summary.js +4 -1
  24. package/dist/code/runtime/code-runtime.js +27 -4
  25. package/dist/code/runtime/sdd-context.js +18 -2
  26. package/dist/governance/governance-report-builder.js +18 -7
  27. package/dist/mcp/claude-code-agent-config.js +10 -4
  28. package/dist/mcp/client-install-claude-code-contract.js +19 -4
  29. package/dist/mcp/client-install-claude-code.js +2 -2
  30. package/dist/mcp/control-plane.js +78 -5
  31. package/dist/mcp/provider-status.js +89 -88
  32. package/dist/mcp/schema.js +42 -8
  33. package/dist/mcp/stdio-server.js +6 -0
  34. package/dist/mcp/validation.js +77 -5
  35. package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
  36. package/dist/resume/product-resume.js +166 -0
  37. package/dist/runs/repositories/runs.js +12 -1
  38. package/dist/runs/run-service.js +62 -5
  39. package/dist/runs/schema.js +4 -0
  40. package/dist/sdd/schema.js +20 -0
  41. package/dist/sdd/sdd-continuation-plan.js +81 -0
  42. package/dist/sdd/sdd-workflow-service.js +146 -18
  43. package/dist/skills/skill-resolver.js +21 -4
  44. package/dist/status/product-status.js +117 -0
  45. package/docs/architecture.md +9 -1
  46. package/docs/cli.md +38 -13
  47. package/docs/code-runtime.md +3 -0
  48. package/docs/contributing.md +1 -1
  49. package/docs/glossary.md +2 -2
  50. package/docs/mcp.md +20 -6
  51. package/docs/project-health-audit-v1.9.1.md +126 -0
  52. package/docs/providers.md +4 -4
  53. package/docs/safety.md +1 -1
  54. package/package.json +1 -1
@@ -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';
@@ -32,7 +33,9 @@ export function callVgxTool(call, services) {
32
33
  case 'vgxness_sdd_save_artifact':
33
34
  return auditedEnvelope(validated.tool, services.sdd.saveArtifact(validated.input), services, sddSaveAuditPayload(validated.input));
34
35
  case 'vgxness_sdd_accept_artifact':
35
- return auditedEnvelope(validated.tool, services.sdd.acceptArtifact(toAcceptArtifactServiceInput(validated.input)), services, sddAcceptanceAuditPayload(validated.input));
36
+ return auditedEnvelope(validated.tool, services.sdd.acceptArtifact(validated.input), services, sddAcceptanceAuditPayload(validated.input));
37
+ case 'vgxness_sdd_reopen_artifact':
38
+ return auditedEnvelope(validated.tool, services.sdd.reopenArtifact(validated.input), services, sddReopenAuditPayload(validated.input));
36
39
  case 'vgxness_sdd_get_artifact':
37
40
  return toEnvelope(validated.tool, services.sdd.getArtifact(validated.input));
38
41
  case 'vgxness_sdd_list_artifacts':
@@ -41,6 +44,8 @@ export function callVgxTool(call, services) {
41
44
  return toEnvelope(validated.tool, services.sdd.getNext(validated.input));
42
45
  case 'vgxness_sdd_cockpit':
43
46
  return toEnvelope(validated.tool, services.sdd.getCockpit(validated.input));
47
+ case 'vgxness_sdd_continue':
48
+ return sddContinueEnvelope(validated.input, services);
44
49
  case 'vgxness_governance_report':
45
50
  return auditedEnvelope(validated.tool, new GovernanceReportBuilder({
46
51
  sdd: governanceSddServices(services.sdd),
@@ -94,6 +99,8 @@ export function callVgxTool(call, services) {
94
99
  return toEnvelope(validated.tool, services.runs.appendCheckpoint(validated.input));
95
100
  case 'vgxness_run_finalize':
96
101
  return toEnvelope(validated.tool, services.runs.updateFinalStatus(validated.input));
102
+ case 'vgxness_run_resume_candidates':
103
+ return runResumeCandidatesEnvelope(validated.input, services);
97
104
  case 'vgxness_run_resume_inspect':
98
105
  return toEnvelope(validated.tool, services.runs.getRunOperatorResumePlan(validated.input.runId));
99
106
  case 'vgxness_run_resume_gate':
@@ -117,10 +124,6 @@ export function callVgxTool(call, services) {
117
124
  }
118
125
  return errorEnvelope('validation_failed', 'Tool dispatch is not implemented');
119
126
  }
120
- function toAcceptArtifactServiceInput(input) {
121
- const note = input.note ?? input.notes ?? input.rationale;
122
- return note === undefined ? input : { ...input, note };
123
- }
124
127
  function auditedEnvelope(tool, result, services, audit) {
125
128
  if (result.ok && audit.runId !== undefined) {
126
129
  services.runs.appendEvent({
@@ -171,6 +174,24 @@ function sddAcceptanceAuditPayload(input) {
171
174
  }),
172
175
  };
173
176
  }
177
+ function sddReopenAuditPayload(input) {
178
+ return {
179
+ runId: input.runId,
180
+ title: 'Audit: sdd-artifact-reopened',
181
+ relatedType: 'sdd-artifact',
182
+ relatedId: `${input.change}:${input.phase}`,
183
+ payload: (value) => ({
184
+ eventType: 'sdd-artifact-reopened',
185
+ project: input.project,
186
+ change: input.change,
187
+ phase: input.phase,
188
+ topicKey: value.topicKey,
189
+ artifactId: value.id,
190
+ reopenedBy: { type: 'human', id: input.reopenedBy.id },
191
+ ...(input.agentId === undefined ? {} : { agentId: input.agentId }),
192
+ }),
193
+ };
194
+ }
174
195
  function sddSaveAuditPayload(input) {
175
196
  return {
176
197
  runId: input.runId,
@@ -222,6 +243,23 @@ function governanceSddServices(services) {
222
243
  },
223
244
  };
224
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
+ }
225
263
  export function createVgxMcpControlPlane(options = {}) {
226
264
  const databasePath = resolveControlPlaneDatabasePath(options);
227
265
  if (!databasePath.ok)
@@ -237,6 +275,7 @@ export function createVgxMcpControlPlane(options = {}) {
237
275
  let closed = false;
238
276
  return {
239
277
  callVgxTool: (tool, input) => callVgxTool({ tool, input }, services),
278
+ database,
240
279
  close: () => {
241
280
  if (closed)
242
281
  return;
@@ -329,6 +368,40 @@ function listRunsEnvelope(input, services) {
329
368
  ? successEnvelope('vgxness_run_list', result.value.slice(0, limit))
330
369
  : errorEnvelope(result.error.code, result.error.message, 'vgxness_run_list');
331
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
+ }
332
405
  function toEnvelope(tool, result) {
333
406
  return result.ok ? successEnvelope(tool, result.value) : errorEnvelope(result.error.code, result.error.message, tool);
334
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
- version: 1,
59
- kind: 'provider-status',
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
- version: 1,
122
- kind: 'provider-status',
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
- version: 1,
169
- kind: 'provider-status',
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
  }
@@ -221,16 +226,12 @@ function claudeProjectMemoryPathStatus(state) {
221
226
  function compactPaths(paths, mode) {
222
227
  if (mode === 'verbose')
223
228
  return paths;
224
- return paths.map((path) => path.status === 'pass'
225
- ? { ...path, detail: `${path.label}: pass` }
226
- : path.status === 'not-configured'
227
- ? { ...path, detail: `${path.label}: not configured` }
228
- : path);
229
+ return paths.map((path) => ({ label: path.label, path: path.path, status: path.status }));
229
230
  }
230
231
  function compactMcpEntry(entry, mode) {
231
- if (mode === 'verbose' || entry.status !== 'pass')
232
+ if (mode === 'verbose')
232
233
  return entry;
233
- return { ...entry, detail: 'MCP server entry found.' };
234
+ return { configured: entry.configured, status: entry.status, serverName: entry.serverName };
234
235
  }
235
236
  function compactSdd(sdd, mode) {
236
237
  if (mode === 'verbose')
@@ -9,10 +9,12 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
9
9
  'vgxness_sdd_get_readiness',
10
10
  'vgxness_sdd_save_artifact',
11
11
  'vgxness_sdd_accept_artifact',
12
+ 'vgxness_sdd_reopen_artifact',
12
13
  'vgxness_sdd_get_artifact',
13
14
  'vgxness_sdd_list_artifacts',
14
15
  'vgxness_sdd_next',
15
16
  'vgxness_sdd_cockpit',
17
+ 'vgxness_sdd_continue',
16
18
  'vgxness_governance_report',
17
19
  'vgxness_memory_save',
18
20
  'vgxness_memory_search',
@@ -36,6 +38,7 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
36
38
  'vgxness_run_start',
37
39
  'vgxness_run_checkpoint',
38
40
  'vgxness_run_finalize',
41
+ 'vgxness_run_resume_candidates',
39
42
  'vgxness_run_resume_inspect',
40
43
  'vgxness_run_resume_gate',
41
44
  'vgxness_provider_status',
@@ -49,10 +52,12 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
49
52
  'sdd_get_readiness',
50
53
  'sdd_save_artifact',
51
54
  'sdd_accept_artifact',
55
+ 'sdd_reopen_artifact',
52
56
  'sdd_get_artifact',
53
57
  'sdd_list_artifacts',
54
58
  'sdd_next',
55
59
  'sdd_cockpit',
60
+ 'sdd_continue',
56
61
  'governance_report',
57
62
  'memory_save',
58
63
  'memory_search',
@@ -76,6 +81,7 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
76
81
  'run_start',
77
82
  'run_checkpoint',
78
83
  'run_finalize',
84
+ 'run_resume_candidates',
79
85
  'run_resume_inspect',
80
86
  'run_resume_gate',
81
87
  'provider_status',
@@ -102,6 +108,7 @@ const payloadModes = ['compact', 'verbose'];
102
108
  const contextCockpitLevels = ['compact', 'expanded', 'verbose'];
103
109
  const providerChangePlanProviders = ['opencode', 'claude', 'antigravity', 'custom'];
104
110
  const providerChangePlanTypes = ['opencode-mcp-install', 'claude-mcp-install', 'setup', 'install', 'config-preparation'];
111
+ const sddPhaseInputSchema = z.union([z.enum(sddPhases), z.literal('apply')]);
105
112
  const jsonValueSchema = z.lazy(() => z.union([z.string(), z.number().finite(), z.boolean(), z.null(), z.array(jsonValueSchema), z.record(z.string(), jsonValueSchema)]));
106
113
  export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
107
114
  vgxness_sdd_status: z
@@ -114,7 +121,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
114
121
  .object({
115
122
  project: z.string().min(1),
116
123
  change: z.string().min(1),
117
- phase: z.enum(sddPhases),
124
+ phase: sddPhaseInputSchema,
118
125
  runId: z.string().min(1).optional(),
119
126
  agentId: z.string().min(1).optional(),
120
127
  })
@@ -123,7 +130,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
123
130
  .object({
124
131
  project: z.string().min(1),
125
132
  change: z.string().min(1),
126
- phase: z.enum(sddPhases),
133
+ phase: sddPhaseInputSchema,
127
134
  runId: z.string().min(1).optional(),
128
135
  agentId: z.string().min(1).optional(),
129
136
  })
@@ -132,7 +139,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
132
139
  .object({
133
140
  project: z.string().min(1),
134
141
  change: z.string().min(1),
135
- phase: z.enum(sddPhases),
142
+ phase: sddPhaseInputSchema,
136
143
  content: z.string().min(1),
137
144
  })
138
145
  .passthrough(),
@@ -140,7 +147,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
140
147
  .object({
141
148
  project: z.string().min(1),
142
149
  change: z.string().min(1),
143
- phase: z.enum(sddPhases),
150
+ phase: sddPhaseInputSchema,
144
151
  acceptedBy: z.object({
145
152
  type: z.literal('human'),
146
153
  id: z.string().min(1),
@@ -148,8 +155,22 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
148
155
  }),
149
156
  acceptedAt: z.string().min(1).optional(),
150
157
  note: z.string().min(1).optional(),
151
- notes: z.string().min(1).optional(),
152
- rationale: z.string().min(1).optional(),
158
+ runId: z.string().min(1).optional(),
159
+ agentId: z.string().min(1).optional(),
160
+ })
161
+ .passthrough(),
162
+ vgxness_sdd_reopen_artifact: z
163
+ .object({
164
+ project: z.string().min(1),
165
+ change: z.string().min(1),
166
+ phase: sddPhaseInputSchema,
167
+ reopenedBy: z.object({
168
+ type: z.literal('human'),
169
+ id: z.string().min(1),
170
+ displayName: z.string().min(1).optional(),
171
+ }),
172
+ reopenedAt: z.string().min(1).optional(),
173
+ note: z.string().min(1).optional(),
153
174
  runId: z.string().min(1).optional(),
154
175
  agentId: z.string().min(1).optional(),
155
176
  })
@@ -158,7 +179,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
158
179
  .object({
159
180
  project: z.string().min(1),
160
181
  change: z.string().min(1),
161
- phase: z.enum(sddPhases),
182
+ phase: sddPhaseInputSchema,
162
183
  payloadMode: z.enum(payloadModes).optional(),
163
184
  })
164
185
  .passthrough(),
@@ -181,6 +202,13 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
181
202
  change: z.string().min(1),
182
203
  })
183
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(),
184
212
  vgxness_governance_report: z
185
213
  .object({
186
214
  project: z.string().min(1),
@@ -348,7 +376,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
348
376
  workspaceRoot: z.string().min(1).optional(),
349
377
  maxSourceBytes: z.number().int().positive().optional(),
350
378
  change: z.string().min(1).optional(),
351
- phase: z.enum(sddPhases).optional(),
379
+ phase: sddPhaseInputSchema.optional(),
352
380
  })
353
381
  .passthrough(),
354
382
  vgxness_run_list: z
@@ -415,6 +443,12 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
415
443
  outcomeReason: z.string().min(1).optional(),
416
444
  })
417
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(),
418
452
  vgxness_run_resume_inspect: z
419
453
  .object({
420
454
  runId: z.string().min(1),
@@ -1,9 +1,11 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { runBootAgentSeedUpgrade } from '../agents/boot-upgrade.js';
3
4
  import { createVgxMcpControlPlane } from './control-plane.js';
4
5
  import { EXPOSED_VGX_MCP_TOOL_NAMES, toInternalVgxMcpToolName, VGX_MCP_TOOL_INPUT_SCHEMAS, } from './schema.js';
5
6
  export async function startVgxMcpStdioServer(options = {}) {
6
7
  const controlPlane = createVgxMcpControlPlane(options.databasePath === undefined ? {} : { databasePath: options.databasePath });
8
+ runBootAgentSeedUpgrade(controlPlane.database);
7
9
  const mcpServer = new McpServer({ name: 'vgxness', version: '0.1.0' });
8
10
  const transport = new StdioServerTransport();
9
11
  let closed = false;
@@ -55,6 +57,8 @@ function descriptionForTool(publicToolName) {
55
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.';
56
58
  if (publicToolName === 'verification_plan')
57
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.';
58
62
  if (publicToolName === 'run_resume_inspect')
59
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.';
60
64
  if (publicToolName === 'run_resume_gate')
@@ -63,6 +67,8 @@ function descriptionForTool(publicToolName) {
63
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.';
64
68
  if (publicToolName === 'sdd_cockpit')
65
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.';
66
72
  const toolName = toInternalVgxMcpToolName(publicToolName);
67
73
  return `VGX control-plane tool ${toolName}`;
68
74
  }