vgxness 1.9.0 → 1.9.2

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 (40) hide show
  1. package/README.md +8 -3
  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-seed-upgrade-service.js +231 -0
  5. package/dist/agents/boot-upgrade.js +59 -0
  6. package/dist/agents/canonical-agent-manifest.js +19 -19
  7. package/dist/agents/canonical-agent-projection.js +7 -0
  8. package/dist/agents/manager-profile-overlay-service.js +14 -0
  9. package/dist/agents/renderers/claude-renderer.js +3 -1
  10. package/dist/agents/renderers/opencode-renderer.js +2 -1
  11. package/dist/agents/repositories/agent-seed-history.js +128 -0
  12. package/dist/behavior/behavior-contract-manifest.js +42 -0
  13. package/dist/behavior/behavior-contract-schema.js +1 -0
  14. package/dist/behavior/behavior-contract-validation.js +42 -0
  15. package/dist/cli/commands/mcp-dispatcher.js +7 -0
  16. package/dist/cli/dispatcher.js +2 -0
  17. package/dist/mcp/client-install-claude-code-contract.js +19 -4
  18. package/dist/mcp/client-install-claude-code.js +2 -2
  19. package/dist/mcp/control-plane-snapshot-service.js +272 -0
  20. package/dist/mcp/control-plane.js +24 -6
  21. package/dist/mcp/index.js +1 -0
  22. package/dist/mcp/provider-status.js +3 -7
  23. package/dist/mcp/schema.js +21 -2
  24. package/dist/mcp/stdio-server.js +2 -0
  25. package/dist/mcp/validation.js +50 -3
  26. package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
  27. package/dist/payload/context-budget-policy.js +17 -0
  28. package/dist/payload/context-budget-service.js +44 -0
  29. package/dist/runs/schema.js +4 -0
  30. package/dist/sdd/schema.js +12 -0
  31. package/dist/sdd/sdd-workflow-service.js +43 -2
  32. package/docs/architecture.md +8 -0
  33. package/docs/cli.md +5 -5
  34. package/docs/contributing.md +1 -1
  35. package/docs/glossary.md +1 -1
  36. package/docs/mcp.md +2 -2
  37. package/docs/project-health-audit-v1.9.1.md +126 -0
  38. package/docs/providers.md +4 -4
  39. package/docs/safety.md +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,128 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export class AgentSeedHistoryRepository {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ append(input) {
8
+ const validation = validate(input);
9
+ if (!validation.ok)
10
+ return validation;
11
+ try {
12
+ const id = randomUUID();
13
+ const appliedAt = new Date().toISOString();
14
+ this.db.connection
15
+ .prepare(`
16
+ INSERT INTO agent_seed_history(id, applied_at, from_version, to_version, agent_name, project, scope, outcome, reason, source)
17
+ VALUES (@id, @appliedAt, @fromVersion, @toVersion, @agentName, @project, @scope, @outcome, @reason, @source)
18
+ `)
19
+ .run({
20
+ id,
21
+ appliedAt,
22
+ fromVersion: input.fromVersion,
23
+ toVersion: input.toVersion,
24
+ agentName: input.agentName,
25
+ project: input.project,
26
+ scope: input.scope,
27
+ outcome: input.outcome,
28
+ reason: input.reason ?? null,
29
+ source: input.source,
30
+ });
31
+ const entry = {
32
+ id,
33
+ appliedAt,
34
+ fromVersion: input.fromVersion,
35
+ toVersion: input.toVersion,
36
+ agentName: input.agentName,
37
+ project: input.project,
38
+ scope: input.scope,
39
+ outcome: input.outcome,
40
+ reason: input.reason ?? null,
41
+ source: input.source,
42
+ };
43
+ return ok(entry);
44
+ }
45
+ catch (cause) {
46
+ return fail('Failed to append agent_seed_history entry', cause);
47
+ }
48
+ }
49
+ listRecent(filters = {}) {
50
+ try {
51
+ const where = [];
52
+ const parameters = {};
53
+ for (const [key, column] of [
54
+ ['project', 'project'],
55
+ ['scope', 'scope'],
56
+ ['agentName', 'agent_name'],
57
+ ]) {
58
+ const value = filters[key];
59
+ if (value !== undefined) {
60
+ where.push(`${column}=@${key}`);
61
+ parameters[key] = value;
62
+ }
63
+ }
64
+ const limit = Math.max(1, Math.min(filters.limit ?? 100, 1000));
65
+ const rows = this.db.connection
66
+ .prepare(`
67
+ SELECT * FROM agent_seed_history
68
+ ${where.length ? `WHERE ${where.join(' AND ')}` : ''}
69
+ ORDER BY applied_at DESC
70
+ LIMIT ${limit}
71
+ `)
72
+ .all(parameters);
73
+ return ok(rows.map(map));
74
+ }
75
+ catch (cause) {
76
+ return fail('Failed to list agent_seed_history entries', cause);
77
+ }
78
+ }
79
+ }
80
+ function validate(input) {
81
+ if (!input.project.trim())
82
+ return validationFailure('agent_seed_history project is required');
83
+ if (input.scope !== 'project' && input.scope !== 'personal')
84
+ return validationFailure('agent_seed_history scope is invalid');
85
+ if (!input.agentName.trim())
86
+ return validationFailure('agent_seed_history agentName is required');
87
+ if (!Number.isInteger(input.toVersion) || input.toVersion <= 0)
88
+ return validationFailure('agent_seed_history toVersion must be a positive integer');
89
+ if (input.fromVersion !== null && (!Number.isInteger(input.fromVersion) || input.fromVersion < 0))
90
+ return validationFailure('agent_seed_history fromVersion must be null or non-negative integer');
91
+ if (!input.source.trim())
92
+ return validationFailure('agent_seed_history source is required');
93
+ if (input.outcome !== 'created' &&
94
+ input.outcome !== 'upgraded' &&
95
+ input.outcome !== 'noop' &&
96
+ input.outcome !== 'overwrote-custom-instructions' &&
97
+ input.outcome !== 'validation_failed' &&
98
+ input.outcome !== 'db_error') {
99
+ return validationFailure(`agent_seed_history outcome is invalid: ${input.outcome}`);
100
+ }
101
+ return ok(undefined);
102
+ }
103
+ function map(row) {
104
+ return {
105
+ id: String(row.id),
106
+ appliedAt: String(row.applied_at),
107
+ fromVersion: row.from_version === null || row.from_version === undefined ? null : Number(row.from_version),
108
+ toVersion: Number(row.to_version),
109
+ agentName: String(row.agent_name),
110
+ project: String(row.project),
111
+ scope: row.scope,
112
+ outcome: String(row.outcome),
113
+ reason: row.reason === null || row.reason === undefined ? null : String(row.reason),
114
+ source: String(row.source),
115
+ };
116
+ }
117
+ function ok(value) {
118
+ return { ok: true, value };
119
+ }
120
+ function validationFailure(message) {
121
+ return { ok: false, error: { code: 'validation_failed', message } };
122
+ }
123
+ function fail(message, cause) {
124
+ const error = { code: 'validation_failed', message };
125
+ if (cause !== undefined)
126
+ error.cause = cause;
127
+ return { ok: false, error };
128
+ }
@@ -0,0 +1,42 @@
1
+ export const canonicalBehaviorContractName = 'vgxness-manager-behavior-contract';
2
+ export const canonicalBehaviorContractVersion = '1.0.0';
3
+ export const criticalBehaviorContractInvariantIds = [
4
+ 'sdd.acceptance.human-only',
5
+ 'sdd.draft-is-not-accepted',
6
+ 'sdd.readiness-before-advance',
7
+ 'provider.preview-status-doctor-readonly',
8
+ 'provider.config-writes-explicit-consent',
9
+ 'run.risky-effects-preflight',
10
+ 'delegation.deny-by-default',
11
+ 'delegation.no-wildcard',
12
+ 'context.manager-compact-default',
13
+ 'context.progressive-disclosure',
14
+ 'flow.provider-native-no-terminal-sdd-daily',
15
+ 'scope.exclude-vgxcode-runtime',
16
+ 'worktree.preserve-unrelated-user-work',
17
+ ];
18
+ export const behaviorContractManifest = {
19
+ name: canonicalBehaviorContractName,
20
+ contractVersion: canonicalBehaviorContractVersion,
21
+ excludedTargets: ['vgxcode', 'src/code/runtime/*', 'src/code/prompts/*'],
22
+ invariants: [
23
+ { id: 'sdd.acceptance.human-only', category: 'sdd', severity: 'critical', summary: 'SDD acceptance is recorded only from explicit human acceptance.' },
24
+ { id: 'sdd.draft-is-not-accepted', category: 'sdd', severity: 'critical', summary: 'Draft, rejected, superseded, legacy, or unaccepted artifacts are not accepted prerequisites.' },
25
+ { id: 'sdd.readiness-before-advance', category: 'sdd', severity: 'critical', summary: 'Phase advancement checks readiness/status before treating prerequisites as satisfied.' },
26
+ { id: 'provider.preview-status-doctor-readonly', category: 'provider', severity: 'critical', summary: 'Preview, status, and doctor surfaces are read-only and do not write provider configuration.' },
27
+ { id: 'provider.config-writes-explicit-consent', category: 'provider', severity: 'critical', summary: 'Provider configuration writes require explicit user consent through confirmed flows.' },
28
+ { id: 'run.risky-effects-preflight', category: 'run', severity: 'critical', summary: 'Risky edits, shell, git, provider, secret, destructive, privileged, or ambiguous effects require preflight.' },
29
+ { id: 'delegation.deny-by-default', category: 'delegation', severity: 'critical', summary: 'Delegation defaults to deny unless an exact governed path allows it.' },
30
+ { id: 'delegation.no-wildcard', category: 'delegation', severity: 'critical', summary: 'Wildcard delegation is not allowed; exact canonical subagents must be used.' },
31
+ { id: 'context.manager-compact-default', category: 'context', severity: 'critical', summary: 'Manager-facing context defaults to compact payloads.' },
32
+ { id: 'context.progressive-disclosure', category: 'context', severity: 'critical', summary: 'Expanded and verbose context are requested only when needed.' },
33
+ { id: 'flow.provider-native-no-terminal-sdd-daily', category: 'flow', severity: 'critical', summary: 'Daily SDD progression happens provider-natively through conversation and MCP, not mandatory terminal phase commands.' },
34
+ { id: 'scope.exclude-vgxcode-runtime', category: 'scope', severity: 'critical', summary: 'This behavior contract excludes vgxcode and src/code runtime/prompt implementation targets.' },
35
+ { id: 'worktree.preserve-unrelated-user-work', category: 'worktree', severity: 'critical', summary: 'Unrelated user work and dirty files are preserved.' },
36
+ ],
37
+ };
38
+ export const behaviorContractProjectionMetadata = {
39
+ behaviorContractName: canonicalBehaviorContractName,
40
+ behaviorContractVersion: canonicalBehaviorContractVersion,
41
+ criticalInvariantIds: criticalBehaviorContractInvariantIds,
42
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { behaviorContractManifest, criticalBehaviorContractInvariantIds } from './behavior-contract-manifest.js';
2
+ export function validateBehaviorContractManifest(manifest = behaviorContractManifest) {
3
+ const errors = [];
4
+ if (!manifest.name.trim())
5
+ errors.push('behavior contract name is required');
6
+ if (!manifest.contractVersion.trim())
7
+ errors.push('behavior contract version is required');
8
+ const seen = new Set();
9
+ for (const invariant of manifest.invariants) {
10
+ if (!invariant.id.trim())
11
+ errors.push('invariant id is required');
12
+ if (seen.has(invariant.id))
13
+ errors.push(`duplicate invariant id: ${invariant.id}`);
14
+ seen.add(invariant.id);
15
+ if (!invariant.summary.trim())
16
+ errors.push(`invariant ${invariant.id} summary is required`);
17
+ }
18
+ for (const id of criticalBehaviorContractInvariantIds) {
19
+ const invariant = manifest.invariants.find((candidate) => candidate.id === id);
20
+ if (invariant === undefined)
21
+ errors.push(`missing critical invariant: ${id}`);
22
+ else if (invariant.severity !== 'critical')
23
+ errors.push(`critical invariant ${id} must have severity critical`);
24
+ }
25
+ for (const target of ['vgxcode', 'src/code/runtime/*', 'src/code/prompts/*']) {
26
+ if (!manifest.excludedTargets.includes(target))
27
+ errors.push(`missing excluded target: ${target}`);
28
+ }
29
+ return { ok: errors.length === 0, errors };
30
+ }
31
+ export function validateBehaviorContractProjection(metadata, manifest = behaviorContractManifest) {
32
+ const errors = [];
33
+ if (metadata.behaviorContractName !== manifest.name)
34
+ errors.push(`behavior contract name mismatch: ${metadata.behaviorContractName}`);
35
+ if (metadata.behaviorContractVersion !== manifest.contractVersion)
36
+ errors.push(`behavior contract version mismatch: ${metadata.behaviorContractVersion}`);
37
+ for (const id of criticalBehaviorContractInvariantIds) {
38
+ if (!metadata.criticalInvariantIds.includes(id))
39
+ errors.push(`projection missing critical invariant reference: ${id}`);
40
+ }
41
+ return { ok: errors.length === 0, errors };
42
+ }
@@ -6,7 +6,9 @@ import { createMcpClientSetupPreview, isMcpClientSetupProvider } from '../../mcp
6
6
  import { createMcpDoctorReport } from '../../mcp/doctor.js';
7
7
  import { createOpenCodeMcpVisibilityReport } from '../../mcp/opencode-visibility.js';
8
8
  import { resolveClaudeCodeScope } from '../../mcp/claude-code-scope.js';
9
+ import { computeEffectiveManagerInstructions } from '../../agents/manager-profile-overlay-service.js';
9
10
  import { RunService } from '../../runs/run-service.js';
11
+ import { isTerminalRunStatus } from '../../runs/schema.js';
10
12
  import { databasePathFor, databasePathSelectionFor, opencodeInstallScopeFlag, optionalNumberFlag, optionalStringFlag } from '../cli-flags.js';
11
13
  import { usageFailure, validationFailure } from '../cli-help.js';
12
14
  import { jsonResult, openCliDatabase, resultFailure } from '../cli-helpers.js';
@@ -42,6 +44,7 @@ export function runMcpInstallCommand(parsed, environment) {
42
44
  return resultFailure(opened);
43
45
  try {
44
46
  const preflight = createClaudeCodeInstallPreflight(parsed, opened.value, environment);
47
+ const effectiveManagerInstructions = computeEffectiveManagerInstructions(opened.value);
45
48
  const result = await installClaudeCodeMcpClient({
46
49
  cwd: environment.cwd,
47
50
  databasePath: databasePath.value.path,
@@ -50,6 +53,7 @@ export function runMcpInstallCommand(parsed, environment) {
50
53
  overwriteVgxness,
51
54
  scope: claudeScope.value,
52
55
  preflight,
56
+ ...(effectiveManagerInstructions === undefined ? {} : { effectiveManagerInstructions }),
53
57
  });
54
58
  return result.status === 'installed' ? jsonResult({ ok: true, value: result }) : resultFailure({ ok: false, error: { code: 'validation_failed', message: `${result.reason}: ${result.message}` } });
55
59
  }
@@ -107,6 +111,9 @@ function createClaudeCodeInstallPreflight(parsed, database, environment) {
107
111
  const details = service.getRun(runId);
108
112
  if (!details.ok)
109
113
  return details;
114
+ if (isTerminalRunStatus(details.value.status)) {
115
+ return validationFailure(`VGXNESS run ${runId} is in terminal state '${details.value.status}'; Claude Code provider config writes require a live run (created/planned/running/needs-human). Start a new run with \`vgxness run start\` and pass its --run-id.`);
116
+ }
110
117
  const phase = optionalStringFlag(parsed.flags, 'phase') ?? details.value.phase;
111
118
  const agentId = optionalStringFlag(parsed.flags, 'agent-id') ?? details.value.selectedAgentId;
112
119
  const preflight = service.preflightOperation({
@@ -4,6 +4,7 @@ import { isWorkflowId } from '../workflows/schema.js';
4
4
  import { databasePathFor, parseArgs, requiredFlag } from './cli-flags.js';
5
5
  import { okText, usageFailure, visibleHelpText } from './cli-help.js';
6
6
  import { openCliDatabase, resultFailure } from './cli-helpers.js';
7
+ import { runBootAgentSeedUpgrade } from '../agents/boot-upgrade.js';
7
8
  import { runAgentCommand, runApprovalsCommand, runCodeCliCommand, runDefaultInteractiveEntrypoint, runDoctorAliasCommand, runInitCommand, runMcpDoctorCommand, runMcpInstallCommand, runMcpSetupCommand, runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runPermissionsCommand, runRunsCommand, runSddCommand, runSetupApplyCommand, runSetupLifecycleCommand, runSetupPlanCommand, runSetupRollbackCommand, runSkillCommand, runSubagentCommand, runVerificationPlanCommand, runVerificationReportCommand, runWorkflowExecuteCommand, runWorkflowPreviewCommand, runWorkflowRunCommand, } from './commands/index.js';
8
9
  const _promptBuffers = new WeakMap();
9
10
  const require = createRequire(import.meta.url);
@@ -48,6 +49,7 @@ export function dispatchCli(argv, environment) {
48
49
  const opened = openCliDatabase(databasePath);
49
50
  if (!opened.ok)
50
51
  return resultFailure(opened);
52
+ runBootAgentSeedUpgrade(opened.value, environment.env);
51
53
  try {
52
54
  if (isWorkflowId(area) && command === 'run')
53
55
  return runWorkflowRunCommand(area, parsed, opened.value);
@@ -1,3 +1,4 @@
1
+ import { withEffectiveManagerInstructions } from '../agents/canonical-agent-projection.js';
1
2
  import { expectedClaudeCodeAgentFiles, inspectClaudeCodeAgents, renderClaudeCodeAgentMarkdown } from './claude-code-agent-config.js';
2
3
  import { buildClaudeCodeMcpAddCommand } from './claude-code-cli.js';
3
4
  import { createClaudeCodeMcpDoctorCommand, createClaudeCodeMcpServerConfig, inspectClaudeCodeMcpConfig, resolveClaudeCodeMcpJsonPath } from './claude-code-config.js';
@@ -63,11 +64,25 @@ export function planClaudeCodeMcpInstall(input) {
63
64
  status: 'would_install',
64
65
  };
65
66
  }
66
- export function expectedClaudeCodeRenderedAgents(workspaceRoot) {
67
- return expectedClaudeCodeAgentFiles(workspaceRoot).map((agent) => ({ path: agent.path, agentName: agent.name, contents: renderClaudeCodeAgentMarkdown(agent) }));
67
+ export function expectedClaudeCodeRenderedAgents(workspaceRoot, options) {
68
+ const files = expectedClaudeCodeAgentFiles(workspaceRoot);
69
+ const projection = withEffectiveManagerInstructions({ defaultAgent: 'vgxness-manager', agents: files.map(({ path: _path, ...rest }) => rest) }, options?.effectiveManagerInstructions);
70
+ return projection.agents.map((agent) => {
71
+ const file = files.find((entry) => entry.name === agent.name);
72
+ if (file === undefined)
73
+ throw new Error(`Expected Claude agent file missing for ${agent.name}`);
74
+ return { path: file.path, agentName: agent.name, contents: renderClaudeCodeAgentMarkdown(agent) };
75
+ });
68
76
  }
69
- export function expectedClaudeCodeRenderedUserAgents(workspaceRoot, env) {
70
- return expectedClaudeCodeAgentFiles({ workspaceRoot, scope: 'user', ...(env === undefined ? {} : { env }) }).map((agent) => ({ path: agent.path, agentName: agent.name, contents: renderClaudeCodeAgentMarkdown(agent) }));
77
+ export function expectedClaudeCodeRenderedUserAgents(workspaceRoot, env, options) {
78
+ const files = expectedClaudeCodeAgentFiles({ workspaceRoot, scope: 'user', ...(env === undefined ? {} : { env }) });
79
+ const projection = withEffectiveManagerInstructions({ defaultAgent: 'vgxness-manager', agents: files.map(({ path: _path, ...rest }) => rest) }, options?.effectiveManagerInstructions);
80
+ return projection.agents.map((agent) => {
81
+ const file = files.find((entry) => entry.name === agent.name);
82
+ if (file === undefined)
83
+ throw new Error(`Expected Claude user agent file missing for ${agent.name}`);
84
+ return { path: file.path, agentName: agent.name, contents: renderClaudeCodeAgentMarkdown(agent) };
85
+ });
71
86
  }
72
87
  function planUserInstall(input, server, overwriteVgxness, cliCommand, scopeWarnings) {
73
88
  const mcpState = inspectClaudeCodeUserMcpConfig(input.env);
@@ -62,7 +62,7 @@ export async function installClaudeCodeMcpClient(input) {
62
62
  if (!mcpWrite.ok)
63
63
  return refusal('post_write_validation_failed', mcpWrite.error.message, plan, server, writtenPaths, backups);
64
64
  writtenPaths.push(mcpWrite.value);
65
- for (const agent of expectedClaudeCodeRenderedAgents(input.cwd)) {
65
+ for (const agent of expectedClaudeCodeRenderedAgents(input.cwd, { effectiveManagerInstructions: input.effectiveManagerInstructions })) {
66
66
  const target = plan.targets.find((item) => item.kind === 'agent-file' && item.path === agent.path);
67
67
  if (target?.action !== 'create' && target?.action !== 'update-vgxness')
68
68
  continue;
@@ -140,7 +140,7 @@ async function applyUserInstall(input, plan, server, writtenPaths, backups) {
140
140
  if (!mcpWrite.ok)
141
141
  return refusal('post_write_validation_failed', mcpWrite.error.message, plan, server, writtenPaths, backups);
142
142
  writtenPaths.push(mcpWrite.value);
143
- for (const agent of expectedClaudeCodeRenderedUserAgents(input.cwd, input.env)) {
143
+ for (const agent of expectedClaudeCodeRenderedUserAgents(input.cwd, input.env, { effectiveManagerInstructions: input.effectiveManagerInstructions })) {
144
144
  const target = plan.targets.find((item) => item.kind === 'agent-file' && item.path === agent.path);
145
145
  if (target?.action !== 'create' && target?.action !== 'update-vgxness')
146
146
  continue;
@@ -0,0 +1,272 @@
1
+ import { ContextBudgetService } from '../payload/context-budget-service.js';
2
+ export const contextCockpitSnapshotLevels = ['compact', 'expanded', 'verbose'];
3
+ export class ContextCockpitSnapshotService {
4
+ dependencies;
5
+ budget;
6
+ constructor(dependencies) {
7
+ this.dependencies = dependencies;
8
+ this.budget = dependencies.budget ?? new ContextBudgetService();
9
+ }
10
+ build(input) {
11
+ const level = input.level ?? 'compact';
12
+ const legacyInput = {
13
+ project: input.project,
14
+ ...(input.directory === undefined ? {} : { directory: input.directory }),
15
+ ...(input.limit === undefined ? {} : { limit: input.limit }),
16
+ };
17
+ const legacy = this.dependencies.memory.getContextCockpit(legacyInput);
18
+ if (!legacy.ok)
19
+ return legacy;
20
+ const sddResult = input.change === undefined ? undefined : this.buildSddSection({ project: input.project, change: input.change }, level);
21
+ if (sddResult !== undefined && !sddResult.ok)
22
+ return sddResult;
23
+ const sdd = sddResult?.value;
24
+ const integratesSdd = sdd !== undefined;
25
+ const optionalSectionsOmitted = omittedSections(legacy.value.optionalSectionsOmitted, integratesSdd ? ['provider'] : ['sdd', 'provider'], integratesSdd ? ['sdd'] : []);
26
+ const snapshotWithoutBudget = {
27
+ ...legacy.value,
28
+ optionalSectionsOmitted,
29
+ ...(sdd === undefined ? {} : { sdd }),
30
+ snapshotVersion: 2,
31
+ level,
32
+ references: [
33
+ {
34
+ id: 'legacy-context-cockpit',
35
+ kind: 'legacy-context-cockpit',
36
+ description: 'Wrapped output from the existing no-trace context cockpit path.',
37
+ },
38
+ ],
39
+ warnings: [
40
+ ...(integratesSdd ? [] : ['sdd-snapshot-omitted:no-change']),
41
+ 'provider-snapshot-omitted:no-workspace-root',
42
+ ],
43
+ safety: {
44
+ ...legacy.value.safety,
45
+ readOnly: true,
46
+ recordsTraces: false,
47
+ noTrace: true,
48
+ mutatesSessions: false,
49
+ noSessionMutation: true,
50
+ mutatesMemories: false,
51
+ noMemoryMutation: true,
52
+ mutatesArtifacts: false,
53
+ noArtifactMutation: true,
54
+ mutatesRuns: false,
55
+ noCheckpoints: true,
56
+ writesProviderConfig: false,
57
+ noProviderConfigWrites: true,
58
+ mutatesRepository: false,
59
+ executesProvider: false,
60
+ mutatesProviderConfig: false,
61
+ createsRun: false,
62
+ createsCheckpoint: false,
63
+ recordsTimelineEvent: false,
64
+ integratesSdd,
65
+ integratesProvider: false,
66
+ },
67
+ };
68
+ const report = this.budget.reportJson(budgetIdForLevel(level), snapshotWithoutBudget);
69
+ return {
70
+ ok: true,
71
+ value: {
72
+ ...snapshotWithoutBudget,
73
+ budget: {
74
+ level,
75
+ policy: budgetIdForLevel(level),
76
+ report,
77
+ memoryPreviewLimit: input.limit ?? legacy.value.memoryPreviews.length,
78
+ includesSdd: integratesSdd,
79
+ includesProvider: false,
80
+ omittedSections: [
81
+ ...(integratesSdd ? [] : ['sdd']),
82
+ 'provider',
83
+ 'runs',
84
+ 'memory-content',
85
+ 'session-transcripts',
86
+ 'provider-config-contents',
87
+ 'run-checkpoint-details',
88
+ ],
89
+ heavyContentPolicy: 'references-only',
90
+ notes: ['Budget measured with ContextBudgetService.reportJson before adding the final budget section.'],
91
+ },
92
+ },
93
+ };
94
+ }
95
+ buildSddSection(input, level) {
96
+ if (this.dependencies.sdd === undefined) {
97
+ return { ok: false, error: { code: 'validation_failed', message: 'SDD cockpit service is not available' } };
98
+ }
99
+ const cockpit = this.dependencies.sdd.getCockpit(input);
100
+ if (!cockpit.ok)
101
+ return cockpit;
102
+ return { ok: true, value: toSddSection(cockpit.value, level) };
103
+ }
104
+ }
105
+ function toSddSection(cockpit, level) {
106
+ switch (level) {
107
+ case 'compact':
108
+ return toCompactSddSection(cockpit);
109
+ case 'expanded':
110
+ return toExpandedSddSection(cockpit);
111
+ case 'verbose':
112
+ return toVerboseSddSection(cockpit);
113
+ }
114
+ }
115
+ function toCompactSddSection(cockpit) {
116
+ return {
117
+ projection: 'compact',
118
+ project: cockpit.project,
119
+ change: cockpit.change,
120
+ recommendedAction: cockpit.recommendedAction,
121
+ ...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
122
+ next: {
123
+ status: cockpit.next.status,
124
+ ...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
125
+ reason: cockpit.next.reason,
126
+ },
127
+ phases: cockpit.phases.map((phase) => ({
128
+ phase: phase.phase,
129
+ topicKey: phase.topicKey,
130
+ present: phase.present,
131
+ accepted: phase.accepted,
132
+ legacy: phase.legacy,
133
+ state: phase.state,
134
+ readinessReady: phase.readiness.ready,
135
+ blockerCount: phase.blockers.length,
136
+ })),
137
+ counts: {
138
+ phases: cockpit.phases.length,
139
+ artifacts: cockpit.artifacts.length,
140
+ accepted: cockpit.acceptedCount,
141
+ legacy: cockpit.legacyCount,
142
+ blockers: cockpit.aggregateBlockers.length,
143
+ },
144
+ };
145
+ }
146
+ function toExpandedSddSection(cockpit) {
147
+ return {
148
+ projection: 'expanded',
149
+ project: cockpit.project,
150
+ change: cockpit.change,
151
+ recommendedAction: cockpit.recommendedAction,
152
+ ...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
153
+ next: {
154
+ status: cockpit.next.status,
155
+ ...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
156
+ reason: cockpit.next.reason,
157
+ },
158
+ phases: cockpit.phases.map(toExpandedSddPhase),
159
+ counts: {
160
+ phases: cockpit.phases.length,
161
+ artifacts: cockpit.artifacts.length,
162
+ accepted: cockpit.acceptedCount,
163
+ legacy: cockpit.legacyCount,
164
+ blockers: cockpit.aggregateBlockers.length,
165
+ },
166
+ blockers: cockpit.aggregateBlockers.map(toSddBlocker),
167
+ };
168
+ }
169
+ function toVerboseSddSection(cockpit) {
170
+ return {
171
+ projection: 'verbose',
172
+ project: cockpit.project,
173
+ change: cockpit.change,
174
+ recommendedAction: cockpit.recommendedAction,
175
+ ...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
176
+ next: {
177
+ status: cockpit.next.status,
178
+ ...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
179
+ reason: cockpit.next.reason,
180
+ },
181
+ phases: cockpit.phases.map(toExpandedSddPhase),
182
+ counts: {
183
+ phases: cockpit.phases.length,
184
+ artifacts: cockpit.artifacts.length,
185
+ accepted: cockpit.acceptedCount,
186
+ legacy: cockpit.legacyCount,
187
+ blockers: cockpit.aggregateBlockers.length,
188
+ },
189
+ blockers: cockpit.aggregateBlockers.map(toSddBlocker),
190
+ artifacts: cockpit.artifacts.map(toArtifactMetadata),
191
+ };
192
+ }
193
+ function toExpandedSddPhase(phase) {
194
+ return {
195
+ phase: phase.phase,
196
+ topicKey: phase.topicKey,
197
+ present: phase.present,
198
+ accepted: phase.accepted,
199
+ legacy: phase.legacy,
200
+ state: phase.state,
201
+ readinessReady: phase.readiness.ready,
202
+ blockerCount: phase.blockers.length,
203
+ readiness: toReadinessMetadata(phase.readiness),
204
+ ...(phase.artifact === undefined ? {} : { artifact: toArtifactMetadata(phase.artifact) }),
205
+ blockers: phase.blockers.map(toSddBlocker),
206
+ };
207
+ }
208
+ function toReadinessMetadata(readiness) {
209
+ return {
210
+ change: readiness.change,
211
+ phase: readiness.phase,
212
+ ready: readiness.ready,
213
+ satisfiedPrerequisites: readiness.satisfiedPrerequisites,
214
+ missingArtifactTopicKeys: readiness.missingArtifactTopicKeys,
215
+ blockedPrerequisites: (readiness.blockedPrerequisites ?? []).map(toPrerequisiteBlocker),
216
+ };
217
+ }
218
+ function toPrerequisiteBlocker(blocker) {
219
+ return {
220
+ phase: blocker.phase,
221
+ topicKey: blocker.topicKey,
222
+ reason: blocker.reason,
223
+ ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
224
+ };
225
+ }
226
+ function toArtifactMetadata(artifact) {
227
+ return {
228
+ phase: artifact.phase,
229
+ topicKey: artifact.topicKey,
230
+ present: artifact.present,
231
+ accepted: artifact.accepted,
232
+ legacy: artifact.legacy,
233
+ state: artifact.state,
234
+ ...(artifact.artifactId === undefined ? {} : { artifactId: artifact.artifactId }),
235
+ ...(artifact.createdAt === undefined ? {} : { createdAt: artifact.createdAt }),
236
+ ...(artifact.updatedAt === undefined ? {} : { updatedAt: artifact.updatedAt }),
237
+ ...(artifact.acceptance === undefined ? {} : { acceptance: toAcceptanceMetadata(artifact.acceptance) }),
238
+ };
239
+ }
240
+ function toAcceptanceMetadata(acceptance) {
241
+ return {
242
+ actor: {
243
+ type: acceptance.actor.type,
244
+ id: acceptance.actor.id,
245
+ ...(acceptance.actor.displayName === undefined ? {} : { displayName: acceptance.actor.displayName }),
246
+ },
247
+ acceptedAt: acceptance.acceptedAt,
248
+ };
249
+ }
250
+ function toSddBlocker(blocker) {
251
+ return {
252
+ kind: blocker.kind,
253
+ phase: blocker.phase,
254
+ topicKey: blocker.topicKey,
255
+ reason: blocker.reason,
256
+ ...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
257
+ };
258
+ }
259
+ function budgetIdForLevel(level) {
260
+ switch (level) {
261
+ case 'compact':
262
+ return 'snapshotCompact';
263
+ case 'expanded':
264
+ return 'snapshotExpanded';
265
+ case 'verbose':
266
+ return 'subagentVerbosePayload';
267
+ }
268
+ }
269
+ function omittedSections(legacy, required, resolved = []) {
270
+ const resolvedSet = new Set(resolved);
271
+ return [...new Set([...legacy, ...required])].filter((section) => !resolvedSet.has(section));
272
+ }
@@ -12,6 +12,7 @@ import { SddWorkflowService } from '../sdd/sdd-workflow-service.js';
12
12
  import { SkillRegistryService } from '../skills/skill-registry-service.js';
13
13
  import { VerificationPlanService } from '../verification/index.js';
14
14
  import { ProviderChangePlanService } from './provider-change-plan.js';
15
+ import { ContextCockpitSnapshotService } from './control-plane-snapshot-service.js';
15
16
  import { ProviderDoctorService } from './provider-doctor.js';
16
17
  import { ProviderStatusService } from './provider-status.js';
17
18
  import { errorEnvelope, successEnvelope, } from './schema.js';
@@ -31,7 +32,9 @@ export function callVgxTool(call, services) {
31
32
  case 'vgxness_sdd_save_artifact':
32
33
  return auditedEnvelope(validated.tool, services.sdd.saveArtifact(validated.input), services, sddSaveAuditPayload(validated.input));
33
34
  case 'vgxness_sdd_accept_artifact':
34
- return auditedEnvelope(validated.tool, services.sdd.acceptArtifact(toAcceptArtifactServiceInput(validated.input)), services, sddAcceptanceAuditPayload(validated.input));
35
+ return auditedEnvelope(validated.tool, services.sdd.acceptArtifact(validated.input), services, sddAcceptanceAuditPayload(validated.input));
36
+ case 'vgxness_sdd_reopen_artifact':
37
+ return auditedEnvelope(validated.tool, services.sdd.reopenArtifact(validated.input), services, sddReopenAuditPayload(validated.input));
35
38
  case 'vgxness_sdd_get_artifact':
36
39
  return toEnvelope(validated.tool, services.sdd.getArtifact(validated.input));
37
40
  case 'vgxness_sdd_list_artifacts':
@@ -64,7 +67,7 @@ export function callVgxTool(call, services) {
64
67
  case 'vgxness_session_restore':
65
68
  return toEnvelope(validated.tool, services.memory.restoreSession(validated.input));
66
69
  case 'vgxness_context_cockpit':
67
- return toEnvelope(validated.tool, services.memory.getContextCockpit(validated.input));
70
+ return toEnvelope(validated.tool, new ContextCockpitSnapshotService({ memory: services.memory, sdd: services.sdd }).build(validated.input));
68
71
  case 'vgxness_agent_resolve':
69
72
  return toEnvelope(validated.tool, services.agents.resolveAgents(validated.input));
70
73
  case 'vgxness_agent_activate':
@@ -116,10 +119,6 @@ export function callVgxTool(call, services) {
116
119
  }
117
120
  return errorEnvelope('validation_failed', 'Tool dispatch is not implemented');
118
121
  }
119
- function toAcceptArtifactServiceInput(input) {
120
- const note = input.note ?? input.notes ?? input.rationale;
121
- return note === undefined ? input : { ...input, note };
122
- }
123
122
  function auditedEnvelope(tool, result, services, audit) {
124
123
  if (result.ok && audit.runId !== undefined) {
125
124
  services.runs.appendEvent({
@@ -170,6 +169,24 @@ function sddAcceptanceAuditPayload(input) {
170
169
  }),
171
170
  };
172
171
  }
172
+ function sddReopenAuditPayload(input) {
173
+ return {
174
+ runId: input.runId,
175
+ title: 'Audit: sdd-artifact-reopened',
176
+ relatedType: 'sdd-artifact',
177
+ relatedId: `${input.change}:${input.phase}`,
178
+ payload: (value) => ({
179
+ eventType: 'sdd-artifact-reopened',
180
+ project: input.project,
181
+ change: input.change,
182
+ phase: input.phase,
183
+ topicKey: value.topicKey,
184
+ artifactId: value.id,
185
+ reopenedBy: { type: 'human', id: input.reopenedBy.id },
186
+ ...(input.agentId === undefined ? {} : { agentId: input.agentId }),
187
+ }),
188
+ };
189
+ }
173
190
  function sddSaveAuditPayload(input) {
174
191
  return {
175
192
  runId: input.runId,
@@ -236,6 +253,7 @@ export function createVgxMcpControlPlane(options = {}) {
236
253
  let closed = false;
237
254
  return {
238
255
  callVgxTool: (tool, input) => callVgxTool({ tool, input }, services),
256
+ database,
239
257
  close: () => {
240
258
  if (closed)
241
259
  return;