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.
- package/README.md +8 -3
- package/dist/agents/agent-activation-service.js +13 -4
- package/dist/agents/agent-registry-service.js +8 -2
- package/dist/agents/agent-seed-upgrade-service.js +231 -0
- package/dist/agents/boot-upgrade.js +59 -0
- package/dist/agents/canonical-agent-manifest.js +19 -19
- package/dist/agents/canonical-agent-projection.js +7 -0
- package/dist/agents/manager-profile-overlay-service.js +14 -0
- package/dist/agents/renderers/claude-renderer.js +3 -1
- package/dist/agents/renderers/opencode-renderer.js +2 -1
- package/dist/agents/repositories/agent-seed-history.js +128 -0
- package/dist/behavior/behavior-contract-manifest.js +42 -0
- package/dist/behavior/behavior-contract-schema.js +1 -0
- package/dist/behavior/behavior-contract-validation.js +42 -0
- package/dist/cli/commands/mcp-dispatcher.js +7 -0
- package/dist/cli/dispatcher.js +2 -0
- package/dist/mcp/client-install-claude-code-contract.js +19 -4
- package/dist/mcp/client-install-claude-code.js +2 -2
- package/dist/mcp/control-plane-snapshot-service.js +272 -0
- package/dist/mcp/control-plane.js +24 -6
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/provider-status.js +3 -7
- package/dist/mcp/schema.js +21 -2
- package/dist/mcp/stdio-server.js +2 -0
- package/dist/mcp/validation.js +50 -3
- package/dist/memory/sqlite/migrations/016_agent_seed_history.sql +15 -0
- package/dist/payload/context-budget-policy.js +17 -0
- package/dist/payload/context-budget-service.js +44 -0
- package/dist/runs/schema.js +4 -0
- package/dist/sdd/schema.js +12 -0
- package/dist/sdd/sdd-workflow-service.js +43 -2
- package/docs/architecture.md +8 -0
- package/docs/cli.md +5 -5
- package/docs/contributing.md +1 -1
- package/docs/glossary.md +1 -1
- package/docs/mcp.md +2 -2
- package/docs/project-health-audit-v1.9.1.md +126 -0
- package/docs/providers.md +4 -4
- package/docs/safety.md +1 -1
- 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({
|
package/dist/cli/dispatcher.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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;
|