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
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ This package is proprietary software. The npm package ships inspectable JavaScri
|
|
|
8
8
|
|
|
9
9
|
OpenCode is the primary supported provider. Other providers remain preview/manual only. Provider config writes require explicit CLI confirmation.
|
|
10
10
|
|
|
11
|
+
VGXNESS v1.9.1 has a canonical project health audit with validated evidence: 38 MCP tools, 106 test files, and the official Bun validation path passing, including package evidence with `releaseReadiness: pass`. See [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md) for the versioned matrix and safety taxonomy.
|
|
12
|
+
|
|
11
13
|
## Requirements
|
|
12
14
|
|
|
13
15
|
- Bun >= 1.3.14 for the installed `vgxness`/`vgx` CLI/MCP runtime and repository verification
|
|
@@ -25,6 +27,7 @@ bun install --frozen-lockfile
|
|
|
25
27
|
bun run check:bun-lock
|
|
26
28
|
bun run verify:typecheck
|
|
27
29
|
bun run verify:test
|
|
30
|
+
bun run verify:test:bun-storage
|
|
28
31
|
bun run verify:bun-sqlite
|
|
29
32
|
bun run verify:package
|
|
30
33
|
bun run package:bun:evidence -- --require-pass
|
|
@@ -86,7 +89,7 @@ Release-candidate checklist, still non-publishing:
|
|
|
86
89
|
|
|
87
90
|
1. `bun install --frozen-lockfile` completes on a clean checkout.
|
|
88
91
|
2. `bun run check:bun-lock` passes.
|
|
89
|
-
3. `bun run verify:typecheck`, `bun run verify:test`, and `bun run verify:bun-sqlite` pass.
|
|
92
|
+
3. `bun run verify:typecheck`, `bun run verify:test`, `bun run verify:test:bun-storage`, and `bun run verify:bun-sqlite` pass.
|
|
90
93
|
4. `bun run package:bun:evidence -- --require-pass` passes on supported CI OSes.
|
|
91
94
|
5. JSON evidence shows `status: pass`, artifact/install smoke pass, no retired bridge scripts, no `package-lock.json`, `publicationAttempted: false`, and `releaseReadiness.status: pass`.
|
|
92
95
|
6. Version, release notes, proprietary license boundary, and rollback criteria are reviewed.
|
|
@@ -156,7 +159,7 @@ Apply only after reviewing the plan:
|
|
|
156
159
|
vgxness setup apply --yes
|
|
157
160
|
```
|
|
158
161
|
|
|
159
|
-
`vgxness setup plan` and `vgxness setup status` are human-readable and
|
|
162
|
+
`vgxness setup plan` and `vgxness setup status` are human-readable and do not write provider config by default; local VGXNESS store initialization may occur when a command needs the selected SQLite store. `vgxness doctor`, `vgxness sdd status`, `vgxness sdd next`, and `vgxness sdd accept-artifact` are also human-readable by default. Pass `--json` when you need parseable automation output. `vgxness setup apply --yes` is the explicit provider-config write path.
|
|
160
163
|
|
|
161
164
|
## Code runtime (`vgxness code`)
|
|
162
165
|
|
|
@@ -173,7 +176,7 @@ Edits, shell, network, git mutations, SDD persistence, and memory saves route th
|
|
|
173
176
|
|
|
174
177
|
## Safety model
|
|
175
178
|
|
|
176
|
-
- Preview, status, and plan commands
|
|
179
|
+
- Preview, status, and plan commands do not write provider config; local VGXNESS store initialization may occur where the command needs SQLite-backed state.
|
|
177
180
|
- Provider config writes require explicit `--yes` confirmation.
|
|
178
181
|
- Setup/status TUI surfaces are preview-oriented; run copied commands explicitly when you choose to act.
|
|
179
182
|
- SDD artifacts are SQLite-backed through VGXNESS services. Do not create or write `openspec/`.
|
|
@@ -236,6 +239,7 @@ bun install --frozen-lockfile
|
|
|
236
239
|
bun run check:bun-lock
|
|
237
240
|
bun run verify:typecheck
|
|
238
241
|
bun run verify:test
|
|
242
|
+
bun run verify:test:bun-storage
|
|
239
243
|
bun run verify:bun-sqlite
|
|
240
244
|
bun run package:bun:evidence -- --require-pass
|
|
241
245
|
```
|
|
@@ -255,6 +259,7 @@ Remove any OpenCode config entries and local/global VGXNESS data manually if you
|
|
|
255
259
|
## More docs
|
|
256
260
|
|
|
257
261
|
- [CLI reference](./docs/cli.md)
|
|
262
|
+
- [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md)
|
|
258
263
|
- [Architecture](./docs/architecture.md)
|
|
259
264
|
- [Code runtime](./docs/code-runtime.md)
|
|
260
265
|
- [MCP tools](./docs/mcp.md)
|
|
@@ -98,14 +98,23 @@ export class AgentActivationService {
|
|
|
98
98
|
return this.dependencies.agents.getAgentByName(input.project, input.scope, defaultAgentName);
|
|
99
99
|
}
|
|
100
100
|
failAfterRun(runId, code, message) {
|
|
101
|
-
|
|
101
|
+
const nestedFailures = [];
|
|
102
|
+
const eventResult = this.dependencies.runs.appendEvent({
|
|
102
103
|
runId,
|
|
103
104
|
kind: 'timeline',
|
|
104
105
|
title: 'Agent activation failed',
|
|
105
|
-
payload: { error: { code: String(code), message }, nextSafeActionReturned: false },
|
|
106
|
+
payload: toJson({ error: { code: String(code), message }, nextSafeActionReturned: false }),
|
|
106
107
|
});
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
if (!eventResult.ok) {
|
|
109
|
+
nestedFailures.push({ step: 'append-event', code: String(eventResult.error.code), message: eventResult.error.message });
|
|
110
|
+
}
|
|
111
|
+
const finalStatusResult = this.dependencies.runs.updateFinalStatus({ runId, status: 'failed', outcome: 'failure', outcomeReason: message });
|
|
112
|
+
if (!finalStatusResult.ok) {
|
|
113
|
+
nestedFailures.push({ step: 'update-final-status', code: String(finalStatusResult.error.code), message: finalStatusResult.error.message });
|
|
114
|
+
}
|
|
115
|
+
return nestedFailures.length === 0
|
|
116
|
+
? { ok: false, error: { code, message } }
|
|
117
|
+
: { ok: false, error: { code, message, cause: { nestedFailures } } };
|
|
109
118
|
}
|
|
110
119
|
}
|
|
111
120
|
function validateActivationInput(input) {
|
|
@@ -32,8 +32,14 @@ export class AgentRegistryService {
|
|
|
32
32
|
this.database.close();
|
|
33
33
|
}
|
|
34
34
|
listAgentDefinitions(input) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const filters = {};
|
|
36
|
+
if (input.project !== undefined)
|
|
37
|
+
filters.project = input.project;
|
|
38
|
+
if (input.scope !== undefined)
|
|
39
|
+
filters.scope = input.scope;
|
|
40
|
+
if (input.mode !== undefined)
|
|
41
|
+
filters.mode = input.mode;
|
|
42
|
+
const summaries = this.agents.list(filters);
|
|
37
43
|
if (!summaries.ok)
|
|
38
44
|
return summaries;
|
|
39
45
|
const definitions = [];
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { canonicalAgentManifest, canonicalPromptContractVersion, canonicalSddSubagentNames } from './canonical-agent-manifest.js';
|
|
2
|
+
import { projectCanonicalAgentManifestToSeed } from './canonical-agent-projection.js';
|
|
3
|
+
export class AgentSeedUpgradeService {
|
|
4
|
+
agents;
|
|
5
|
+
history;
|
|
6
|
+
database;
|
|
7
|
+
constructor(dependencies) {
|
|
8
|
+
this.agents = dependencies.agents;
|
|
9
|
+
this.history = dependencies.history;
|
|
10
|
+
this.database = dependencies.database;
|
|
11
|
+
}
|
|
12
|
+
upgrade(input) {
|
|
13
|
+
if (!input.project.trim())
|
|
14
|
+
return validationFailure('Agent seed upgrade project is required');
|
|
15
|
+
if (input.scope !== 'project' && input.scope !== 'personal')
|
|
16
|
+
return validationFailure('Agent seed upgrade scope is invalid');
|
|
17
|
+
const seed = projectCanonicalAgentManifestToSeed();
|
|
18
|
+
const canonicalByName = new Map();
|
|
19
|
+
for (const agent of seed.agents)
|
|
20
|
+
canonicalByName.set(agent.name, agent);
|
|
21
|
+
for (const subagent of seed.subagents)
|
|
22
|
+
canonicalByName.set(subagent.name, subagent);
|
|
23
|
+
const source = input.source ?? 'boot-upgrade';
|
|
24
|
+
const summary = {
|
|
25
|
+
created: [],
|
|
26
|
+
upgraded: [],
|
|
27
|
+
noop: [],
|
|
28
|
+
overwritten: [],
|
|
29
|
+
errors: [],
|
|
30
|
+
skipped: false,
|
|
31
|
+
};
|
|
32
|
+
const expected = canonicalPromptContractVersion;
|
|
33
|
+
const transaction = this.database.transaction(() => {
|
|
34
|
+
for (const [name, definition] of canonicalByName) {
|
|
35
|
+
const outcome = this.upgradeOne(input.project, input.scope, name, definition, source, expected);
|
|
36
|
+
if (outcome.ok) {
|
|
37
|
+
switch (outcome.value.outcome) {
|
|
38
|
+
case 'created':
|
|
39
|
+
summary.created.push(name);
|
|
40
|
+
break;
|
|
41
|
+
case 'upgraded':
|
|
42
|
+
summary.upgraded.push(name);
|
|
43
|
+
break;
|
|
44
|
+
case 'overwrote-custom-instructions':
|
|
45
|
+
summary.overwritten.push(name);
|
|
46
|
+
break;
|
|
47
|
+
case 'noop':
|
|
48
|
+
summary.noop.push(name);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const failureOutcome = outcome.error.code === 'validation_failed' ? 'validation_failed' : 'db_error';
|
|
54
|
+
summary.errors.push({ agentName: name, outcome: failureOutcome, reason: outcome.error.message });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return summary;
|
|
58
|
+
});
|
|
59
|
+
if (!transaction.ok) {
|
|
60
|
+
return { ok: false, error: transaction.error };
|
|
61
|
+
}
|
|
62
|
+
return transaction;
|
|
63
|
+
}
|
|
64
|
+
hasDrift(input) {
|
|
65
|
+
const expected = input.expectedVersion ?? canonicalPromptContractVersion;
|
|
66
|
+
try {
|
|
67
|
+
const canonicalNames = [canonicalAgentManifest.defaultAgentName, ...canonicalSddSubagentNames];
|
|
68
|
+
const placeholders = canonicalNames.map(() => '?').join(', ');
|
|
69
|
+
const row = this.database.connection
|
|
70
|
+
.prepare(`
|
|
71
|
+
SELECT COUNT(*) AS stale_count
|
|
72
|
+
FROM agents
|
|
73
|
+
WHERE project = ? AND scope = ? AND name IN (${placeholders})
|
|
74
|
+
AND json_extract(adapters_json, '$.opencode.config.options.vgxnessPromptContractVersion') <> ?
|
|
75
|
+
`)
|
|
76
|
+
.get(input.project, input.scope, ...canonicalNames, expected);
|
|
77
|
+
return ok((row?.stale_count ?? 0) > 0);
|
|
78
|
+
}
|
|
79
|
+
catch (cause) {
|
|
80
|
+
return fail('Failed to detect agent seed drift', cause);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
upgradeOne(project, scope, name, definition, source, expected) {
|
|
84
|
+
const existing = this.agents.getByName(project, scope, name);
|
|
85
|
+
const fromVersion = existing.ok ? readPromptContractVersion(existing.value) : null;
|
|
86
|
+
if (existing.ok && fromVersion === expected && isByteEqualToCanonical(existing.value, definition)) {
|
|
87
|
+
return ok({ outcome: 'noop', fromVersion: expected });
|
|
88
|
+
}
|
|
89
|
+
const parentAgentId = 'parentAgentName' in definition ? this.resolveParentId(project, scope, definition.parentAgentName) : undefined;
|
|
90
|
+
if ('parentAgentName' in definition && parentAgentId === undefined) {
|
|
91
|
+
return validationFailure(`Unresolved parentAgentName: ${definition.parentAgentName}`);
|
|
92
|
+
}
|
|
93
|
+
const registerInput = toRegisterInput(project, scope, definition, parentAgentId);
|
|
94
|
+
const registered = this.agents.register(registerInput);
|
|
95
|
+
if (!registered.ok) {
|
|
96
|
+
this.appendHistory({
|
|
97
|
+
project,
|
|
98
|
+
scope,
|
|
99
|
+
agentName: name,
|
|
100
|
+
fromVersion,
|
|
101
|
+
toVersion: expected,
|
|
102
|
+
outcome: registered.error.code === 'validation_failed' ? 'validation_failed' : 'db_error',
|
|
103
|
+
reason: registered.error.message,
|
|
104
|
+
source,
|
|
105
|
+
});
|
|
106
|
+
return registered;
|
|
107
|
+
}
|
|
108
|
+
let outcome;
|
|
109
|
+
if (!existing.ok) {
|
|
110
|
+
outcome = 'created';
|
|
111
|
+
}
|
|
112
|
+
else if (fromVersion !== null && fromVersion !== expected) {
|
|
113
|
+
outcome = 'upgraded';
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
outcome = 'overwrote-custom-instructions';
|
|
117
|
+
}
|
|
118
|
+
this.appendHistory({
|
|
119
|
+
project,
|
|
120
|
+
scope,
|
|
121
|
+
agentName: name,
|
|
122
|
+
fromVersion,
|
|
123
|
+
toVersion: expected,
|
|
124
|
+
outcome,
|
|
125
|
+
reason: outcomeReason(outcome, fromVersion, expected, existing.ok),
|
|
126
|
+
source,
|
|
127
|
+
});
|
|
128
|
+
return ok({ outcome, fromVersion });
|
|
129
|
+
}
|
|
130
|
+
resolveParentId(project, scope, parentAgentName) {
|
|
131
|
+
const parent = this.agents.getByName(project, scope, parentAgentName);
|
|
132
|
+
if (!parent.ok || parent.value.mode !== 'agent')
|
|
133
|
+
return undefined;
|
|
134
|
+
return parent.value.id;
|
|
135
|
+
}
|
|
136
|
+
appendHistory(input) {
|
|
137
|
+
this.history.append({
|
|
138
|
+
project: input.project,
|
|
139
|
+
scope: input.scope,
|
|
140
|
+
agentName: input.agentName,
|
|
141
|
+
fromVersion: input.fromVersion,
|
|
142
|
+
toVersion: input.toVersion,
|
|
143
|
+
outcome: input.outcome,
|
|
144
|
+
reason: input.reason,
|
|
145
|
+
source: input.source,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function toRegisterInput(project, scope, definition, parentAgentId) {
|
|
150
|
+
const input = {
|
|
151
|
+
project,
|
|
152
|
+
scope,
|
|
153
|
+
mode: 'parentAgentName' in definition ? 'subagent' : 'agent',
|
|
154
|
+
name: definition.name,
|
|
155
|
+
description: definition.description,
|
|
156
|
+
instructions: definition.instructions,
|
|
157
|
+
};
|
|
158
|
+
if (definition.capabilities !== undefined)
|
|
159
|
+
input.capabilities = definition.capabilities;
|
|
160
|
+
if (definition.permissions !== undefined)
|
|
161
|
+
input.permissions = definition.permissions;
|
|
162
|
+
if (definition.memory !== undefined)
|
|
163
|
+
input.memory = definition.memory;
|
|
164
|
+
if (definition.workflows !== undefined)
|
|
165
|
+
input.workflows = definition.workflows;
|
|
166
|
+
if (definition.skills !== undefined)
|
|
167
|
+
input.skills = definition.skills;
|
|
168
|
+
if (definition.adapters !== undefined)
|
|
169
|
+
input.adapters = definition.adapters;
|
|
170
|
+
if (parentAgentId !== undefined)
|
|
171
|
+
input.parentAgentId = parentAgentId;
|
|
172
|
+
return input;
|
|
173
|
+
}
|
|
174
|
+
function isByteEqualToCanonical(existing, definition) {
|
|
175
|
+
if (existing.description !== definition.description)
|
|
176
|
+
return false;
|
|
177
|
+
if (existing.instructions.value !== definition.instructions.value)
|
|
178
|
+
return false;
|
|
179
|
+
if (!adaptersEqual(existing.adapters, definition.adapters ?? {}))
|
|
180
|
+
return false;
|
|
181
|
+
if (!arraysEqual(existing.capabilities, definition.capabilities ?? []))
|
|
182
|
+
return false;
|
|
183
|
+
if (!arraysEqual(existing.workflows, definition.workflows ?? []))
|
|
184
|
+
return false;
|
|
185
|
+
if (!arraysEqual(existing.skills, definition.skills ?? []))
|
|
186
|
+
return false;
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
function adaptersEqual(a, b) {
|
|
190
|
+
return JSON.stringify(a ?? {}) === JSON.stringify(b ?? {});
|
|
191
|
+
}
|
|
192
|
+
function arraysEqual(a, b) {
|
|
193
|
+
if (a.length !== b.length)
|
|
194
|
+
return false;
|
|
195
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
196
|
+
if (a[i] !== b[i])
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
function readPromptContractVersion(agent) {
|
|
202
|
+
const opencode = agent.adapters?.opencode;
|
|
203
|
+
if (opencode === undefined)
|
|
204
|
+
return null;
|
|
205
|
+
const options = opencode.config?.options;
|
|
206
|
+
const value = options?.vgxnessPromptContractVersion;
|
|
207
|
+
if (typeof value !== 'number' || !Number.isInteger(value))
|
|
208
|
+
return null;
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
function outcomeReason(outcome, fromVersion, toVersion, existed) {
|
|
212
|
+
if (outcome === 'created')
|
|
213
|
+
return `created canonical row at v${toVersion}`;
|
|
214
|
+
if (outcome === 'upgraded')
|
|
215
|
+
return `upgraded v${fromVersion ?? 'unknown'} -> v${toVersion}`;
|
|
216
|
+
if (outcome === 'overwrote-custom-instructions')
|
|
217
|
+
return `overwrote customized row (was v${fromVersion ?? 'unknown'}) with v${toVersion} canonical`;
|
|
218
|
+
return existed ? `noop at v${toVersion}` : `noop at v${toVersion}`;
|
|
219
|
+
}
|
|
220
|
+
function ok(value) {
|
|
221
|
+
return { ok: true, value };
|
|
222
|
+
}
|
|
223
|
+
function validationFailure(message) {
|
|
224
|
+
return { ok: false, error: { code: 'validation_failed', message } };
|
|
225
|
+
}
|
|
226
|
+
function fail(message, cause) {
|
|
227
|
+
const error = { code: 'validation_failed', message };
|
|
228
|
+
if (cause !== undefined)
|
|
229
|
+
error.cause = cause;
|
|
230
|
+
return { ok: false, error };
|
|
231
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AgentSeedUpgradeService } from './agent-seed-upgrade-service.js';
|
|
2
|
+
import { AgentRepository } from './repositories/agents.js';
|
|
3
|
+
import { AgentSeedHistoryRepository } from './repositories/agent-seed-history.js';
|
|
4
|
+
import { canonicalAgentManifest } from './canonical-agent-manifest.js';
|
|
5
|
+
const skipEnvVar = 'VGXNESS_SKIP_AGENT_SEED_AUTO_UPGRADE';
|
|
6
|
+
export function runBootAgentSeedUpgrade(database, env = process.env, project = canonicalAgentManifest.project, scope = canonicalAgentManifest.scope) {
|
|
7
|
+
if (isOptOut(env)) {
|
|
8
|
+
return { ok: true, skipped: true, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 0 };
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const service = new AgentSeedUpgradeService({
|
|
12
|
+
agents: new AgentRepository(database),
|
|
13
|
+
history: new AgentSeedHistoryRepository(database),
|
|
14
|
+
database,
|
|
15
|
+
});
|
|
16
|
+
const result = service.upgrade({ project, scope, source: 'boot-upgrade' });
|
|
17
|
+
if (!result.ok) {
|
|
18
|
+
const message = `boot-upgrade failed: ${result.error.message} (code=${result.error.code})`;
|
|
19
|
+
process.stderr.write(`[vgxness] ${message}\n`);
|
|
20
|
+
return { ok: false, skipped: false, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 1, message };
|
|
21
|
+
}
|
|
22
|
+
const value = result.value;
|
|
23
|
+
const touched = value.created.length + value.upgraded.length + value.overwritten.length + value.errors.length;
|
|
24
|
+
if (touched > 0) {
|
|
25
|
+
const parts = [];
|
|
26
|
+
if (value.created.length > 0)
|
|
27
|
+
parts.push(`created ${value.created.length}`);
|
|
28
|
+
if (value.upgraded.length > 0)
|
|
29
|
+
parts.push(`upgraded ${value.upgraded.length}`);
|
|
30
|
+
if (value.overwritten.length > 0)
|
|
31
|
+
parts.push(`overwrote ${value.overwritten.length}`);
|
|
32
|
+
if (value.errors.length > 0)
|
|
33
|
+
parts.push(`errors ${value.errors.length}`);
|
|
34
|
+
process.stderr.write(`[vgxness] boot-upgrade: ${parts.join(', ')}\n`);
|
|
35
|
+
for (const error of value.errors) {
|
|
36
|
+
process.stderr.write(`[vgxness] boot-upgrade: ${error.agentName} ${error.outcome}: ${error.reason}\n`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
ok: true,
|
|
41
|
+
skipped: false,
|
|
42
|
+
created: value.created.length,
|
|
43
|
+
upgraded: value.upgraded.length,
|
|
44
|
+
overwritten: value.overwritten.length,
|
|
45
|
+
noop: value.noop.length,
|
|
46
|
+
errors: value.errors.length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (cause) {
|
|
50
|
+
const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
|
|
51
|
+
const message = `boot-upgrade threw: ${detail}`;
|
|
52
|
+
process.stderr.write(`[vgxness] ${message}\n`);
|
|
53
|
+
return { ok: false, skipped: false, created: 0, upgraded: 0, overwritten: 0, noop: 0, errors: 1, message };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isOptOut(env) {
|
|
57
|
+
const value = env[skipEnvVar];
|
|
58
|
+
return value === '1' || value === 'true';
|
|
59
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { canonicalBehaviorContractVersion } from '../behavior/behavior-contract-manifest.js';
|
|
1
2
|
export const canonicalDefaultAgentName = 'vgxness-manager';
|
|
2
|
-
export const canonicalPromptContractVersion =
|
|
3
|
+
export const canonicalPromptContractVersion = 7;
|
|
3
4
|
export const canonicalSddSubagentNames = [
|
|
4
5
|
'vgxness-sdd-explore',
|
|
5
6
|
'vgxness-sdd-propose',
|
|
@@ -81,7 +82,7 @@ function managerDefinition() {
|
|
|
81
82
|
memory: { scopes: ['project'] },
|
|
82
83
|
workflows: ['explore', 'quickfix', 'plan', 'build', 'debug', 'sdd', 'agent-seeding', 'opencode-install'],
|
|
83
84
|
skills: ['vgxness-sdd-manager'],
|
|
84
|
-
adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
|
|
85
|
+
adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
|
|
85
86
|
providerSupport: commonSupport(),
|
|
86
87
|
};
|
|
87
88
|
}
|
|
@@ -100,7 +101,7 @@ function subagentDefinition(name) {
|
|
|
100
101
|
memory: { scopes: ['project'] },
|
|
101
102
|
workflows: data.workflows,
|
|
102
103
|
skills: data.skills,
|
|
103
|
-
adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion } } } },
|
|
104
|
+
adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion } } } },
|
|
104
105
|
providerSupport: commonSupport(),
|
|
105
106
|
};
|
|
106
107
|
}
|
|
@@ -118,45 +119,44 @@ export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager - compact SDD o
|
|
|
118
119
|
Bind only to the primary \`vgxness-manager\` agent. Executor agents (for example \`vgxness-sdd-apply\`) use their own prompts.
|
|
119
120
|
|
|
120
121
|
## Role
|
|
121
|
-
Coordinate SDD;
|
|
122
|
-
|
|
123
|
-
Coach while coordinating: teach briefly when helpful, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
|
|
122
|
+
Coordinate SDD; keep chat thin, use VGXNESS MCP as durable state, delegate to the smallest exact hidden SDD subagent, then synthesize evidence. Coach while coordinating: teach briefly, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
|
|
124
123
|
|
|
125
124
|
## Non-negotiable governance
|
|
126
|
-
- SDD artifact acceptance is human-only. Never infer acceptance from generated output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/unaccepted artifacts as not accepted until a human acceptance record exists.
|
|
125
|
+
- SDD artifact acceptance is human-only. Never infer or fabricate acceptance from generated output, subagent/model output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/stale/unaccepted artifacts as not accepted until a human acceptance record exists.
|
|
127
126
|
- Before phase advancement, call readiness/status tools: \`vgxness_sdd_status\`/\`vgxness_sdd_next\` and \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
|
|
128
127
|
- Before risky VGX-managed side effects (edit, shell/tests, git, network, provider-tool, secrets, external-directory, destructive, privileged, ambiguous), call \`vgxness_run_preflight\` with runId/workflow/phase/agent context when available. If approval/block is required, stop; do not invent approval.
|
|
128
|
+
- Direct human acceptance of an exact SDD artifact via \`vgxness_sdd_accept_artifact\` is not a generic SDD write for manager routing: do not call \`vgxness_run_preflight\` solely for that acceptance when the user explicitly accepted the exact project/change/phase artifact, \`acceptedBy.type\` is \`"human"\`, \`acceptedBy.id\` is non-empty, and status/readiness confirms the artifact is eligible. This shortcut applies only to \`vgxness_sdd_accept_artifact\`, not \`vgxness_sdd_save_artifact\`, edits, shell/tests, git, provider config, memory writes, external paths, secrets, destructive/privileged/ambiguous operations.
|
|
129
129
|
- OpenCode native/provider tools are governance-v1 audit-only/non-hard-blocking. Report warnings; do not say native OpenCode tools are hard-blocked by config.
|
|
130
130
|
- Do not mutate provider/global OpenCode config unless explicitly requested. Do not write to \`openspec/\`. Never revert/overwrite unrelated user work. Preserve \`permission.task\` deny-by-default with only exact known SDD subagents allowed.
|
|
131
131
|
- Do not publish packages unless explicitly requested.
|
|
132
132
|
- Do not change model or reasoning effort.
|
|
133
133
|
|
|
134
134
|
## Flexible governance routing
|
|
135
|
-
Use the lightest safe path: Tier 0-2 direct
|
|
135
|
+
Use the lightest safe path: Tier 0-2 direct; Tier 3 preflight/explicit validation; Tier 4 formal SDD for governance, permission model, SDD acceptance, architecture/security, or cross-surface behavior. Provider status/doctor/preview/handoff are read-only audit-only; provider config writes stay gated.
|
|
136
136
|
|
|
137
137
|
## Provider-native daily flow
|
|
138
|
-
|
|
138
|
+
Daily SDD happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for normal flow. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
|
|
139
139
|
|
|
140
140
|
## MCP playbook
|
|
141
|
-
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal
|
|
142
|
-
- SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default
|
|
143
|
-
- Acceptance/readiness: confirm explicit human acceptance,
|
|
144
|
-
- Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\`
|
|
145
|
-
- Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\`
|
|
141
|
+
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one and summarize.
|
|
142
|
+
- SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default so the primary context stays clean. Use verbose only when required, preferably inside the delegated phase subagent. Save with \`vgxness_sdd_save_artifact\` only after the appropriate flow. SDD artifacts are not generic memory.
|
|
143
|
+
- Acceptance/readiness: check SDD status/readiness for the exact project/change/phase, confirm explicit human acceptance of that exact artifact, call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId, note/rationale when useful), then re-check status/readiness before reporting state. Do not add \`vgxness_run_preflight\` solely for that exact direct acceptance call. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings before risky/ambiguous transitions and in apply/verify summaries.
|
|
144
|
+
- Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work or unclear context; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
|
|
145
|
+
- Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` are preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
|
|
146
146
|
- Runs: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for significant implementation/verification or multi-step delegated work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`.
|
|
147
147
|
- Provider diagnostics: \`vgxness_provider_status\` and \`vgxness_provider_doctor\` are read-only reports.
|
|
148
148
|
|
|
149
149
|
## Minimum flows
|
|
150
|
-
- Simple answer:
|
|
151
|
-
- Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist
|
|
152
|
-
- Apply/verify: require
|
|
150
|
+
- Simple answer: inline; memory only for prior context; no run by default.
|
|
151
|
+
- Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist by governance.
|
|
152
|
+
- Apply/verify: require prerequisites -> read artifacts -> resolve exact subagent -> start/recover run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save output -> finalize when clear.
|
|
153
153
|
- Config/provider/prompt change: inspect manager/profile or payload first; persistent changes require explicit human authorization.
|
|
154
154
|
|
|
155
155
|
## Delegation thresholds
|
|
156
|
-
Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration
|
|
156
|
+
Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration, substantial implementation, writes with analysis/new logic, and execution-heavy verification. Never delegate to unknown agents; use exact SDD phase mapping.
|
|
157
157
|
|
|
158
158
|
## Output
|
|
159
|
-
Be concise:
|
|
159
|
+
Be concise: delegated work, results, files/decisions, evidence/tests, risks, next step.`;
|
|
160
160
|
const registryManagerInstructions = 'You are the VGXNESS SDD coordinator, not a monolithic executor. Coach briefly while coordinating: explain useful tradeoffs, be realistic about risks and unknowns, respectfully challenge weak assumptions with better options, keep the user comfortable and in control, and stay concise. Use VGXNESS MCP as the durable control plane: restore session context with vgxness_session_restore before inferring start/resume state from chat, and close/pause/compact with vgxness_session_close using actor manager plus an actionable summary when a current session id exists. Check SDD status/next/ready, read prerequisites with sdd_get_artifact or sdd_list_artifacts, save accepted phase output with sdd_save_artifact, search/get/save/update memory only for reusable knowledge, resolve exact SDD subagents before substantial phase work, use runs/checkpoints/preflight/finalize for significant implementation or verification, and use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health. Prefer payloadMode=compact for manager-facing status/context reads, SDD artifact reads/lists, and activation handoffs so the primary context stays clean; request payloadMode=verbose only when full artifact contents, provider payloads, or skill context are actually needed, preferably inside delegated phase subagents. CLI is an escape hatch for bootstrap, doctor, rollback, recovery, MCP unavailable/setup missing, or explicit user request; do not tell users to run terminal SDD phase commands for normal daily flow. Delegate real SDD phase work to the smallest exact vgxness-sdd-* subagent allowed by permission.task, synthesize results, and persist artifacts/checkpoints. Do not perform substantial multi-file implementation inline. Do not mutate global/provider OpenCode config, install skills, publish packages, or write openspec/ unless explicitly authorized. Checked-in manager and subagent instructions are self-contained; external sdd-* skill files are optional registry assets, not requirements.';
|
|
161
161
|
const subagentData = {
|
|
162
162
|
'vgxness-sdd-explore': { seedDescription: 'Investigates codebase context and identifies options before proposals.', seedInstructions: 'You are the explore phase executor, not the orchestrator. Do not delegate. Explore repository evidence for the requested SDD change. Do not implement code changes. Return concise findings, risks, and recommended next artifacts.', capabilities: ['sdd-exploration', 'codebase-research', 'discovery'], permissions: { read: 'allow', edit: 'deny', shell: 'ask', git: 'deny', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' }, workflows: ['sdd:explore'], skills: ['vgxness-sdd-explore'], phaseContract: 'Investigate codebase context, constraints, options, and risks. Do not implement code changes. Return findings and recommended next artifacts.' },
|
|
@@ -73,6 +73,13 @@ export function projectCanonicalAgentManifestToClaudeCode(manifest = canonicalAg
|
|
|
73
73
|
}
|
|
74
74
|
return { defaultAgent: canonicalDefaultAgentName, agents };
|
|
75
75
|
}
|
|
76
|
+
export function withEffectiveManagerInstructions(projection, instructions) {
|
|
77
|
+
const trimmed = instructions?.trim() ?? '';
|
|
78
|
+
if (trimmed === '')
|
|
79
|
+
return projection;
|
|
80
|
+
const agents = projection.agents.map((agent) => agent.canonicalName === projection.defaultAgent ? { ...agent, instructions: trimmed } : agent);
|
|
81
|
+
return { defaultAgent: projection.defaultAgent, agents };
|
|
82
|
+
}
|
|
76
83
|
export function projectCanonicalAgentManifestToClaudeProjectMemory(manifest = canonicalAgentManifest) {
|
|
77
84
|
assertValidCanonicalManifest(manifest);
|
|
78
85
|
return {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AgentRegistryService } from './agent-registry-service.js';
|
|
2
|
+
import { ManagerProfileOverlayRepository } from './repositories/manager-profile-overlays.js';
|
|
1
3
|
export class ManagerProfileOverlayService {
|
|
2
4
|
dependencies;
|
|
3
5
|
constructor(dependencies) {
|
|
@@ -36,3 +38,15 @@ function ok(value) {
|
|
|
36
38
|
function validationFailure(message) {
|
|
37
39
|
return { ok: false, error: { code: 'validation_failed', message } };
|
|
38
40
|
}
|
|
41
|
+
export function computeEffectiveManagerInstructions(database, project = 'vgxness', scope = 'project', managerName = 'vgxness-manager') {
|
|
42
|
+
const registry = new AgentRegistryService(database);
|
|
43
|
+
const overlays = new ManagerProfileOverlayRepository(database);
|
|
44
|
+
const service = new ManagerProfileOverlayService({ agents: registry, overlays });
|
|
45
|
+
const resolved = service.resolveEffectiveManager({ project, scope, managerName });
|
|
46
|
+
if (!resolved.ok || resolved.value.overlay === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
const overlayInstructions = resolved.value.overlay.instructions.trim();
|
|
49
|
+
if (overlayInstructions === '')
|
|
50
|
+
return undefined;
|
|
51
|
+
return overlayInstructions;
|
|
52
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalPromptContractVersion } from '../canonical-agent-manifest.js';
|
|
2
|
+
import { canonicalBehaviorContractVersion } from '../../behavior/behavior-contract-manifest.js';
|
|
1
3
|
import { ok, validationFailure } from './provider-adapter.js';
|
|
2
4
|
const previewWarnings = [
|
|
3
5
|
'Claude rendering returns installable preview artifacts only; it does not install and does not write provider configuration.',
|
|
@@ -66,7 +68,7 @@ export class ClaudeAgentRenderer {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
function renderClaudeAgentMarkdown(agent, key) {
|
|
69
|
-
return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion
|
|
71
|
+
return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion=${canonicalPromptContractVersion} behaviorContractVersion=${canonicalBehaviorContractVersion} safe-update=true -->\n\n${agent.instructions.value.trim()}\n`;
|
|
70
72
|
}
|
|
71
73
|
function claudeAgentKey(value) {
|
|
72
74
|
return pathSegment(value);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { behaviorContractProjectionMetadata } from '../../behavior/behavior-contract-manifest.js';
|
|
1
2
|
import { ok, validationFailure } from './provider-adapter.js';
|
|
2
3
|
const openCodeSchema = 'https://opencode.ai/config.json';
|
|
3
4
|
const previewWarning = 'Rendering returns preview artifacts only; it does not install or write .opencode/, .claude/, or provider configuration.';
|
|
@@ -50,7 +51,7 @@ export class OpenCodeAgentRenderer {
|
|
|
50
51
|
const artifact = {
|
|
51
52
|
relativePath: `rendered/opencode/${pathSegment(input.agent.project)}/${input.agent.scope}/${pathSegment(input.agent.name)}/opencode.json`,
|
|
52
53
|
contentType: 'application/json',
|
|
53
|
-
contents: `${JSON.stringify({ $schema: openCodeSchema, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
|
|
54
|
+
contents: `${JSON.stringify({ $schema: openCodeSchema, behaviorContract: behaviorContractProjectionMetadata, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
|
|
54
55
|
};
|
|
55
56
|
return ok({
|
|
56
57
|
provider: this.provider,
|