vgxness 1.17.2 → 1.19.0
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/dist/agents/agent-lookup-contexts.js +28 -0
- package/dist/agents/agent-registry-service.js +29 -2
- package/dist/agents/agent-resolver.js +24 -5
- package/dist/agents/agent-selector-resolver.js +12 -7
- package/dist/agents/canonical-agent-manifest.js +14 -11
- package/dist/agents/canonical-agent-projection.js +24 -2
- package/dist/cli/cli-help.js +1 -1
- package/dist/cli/home-tui-app.js +1 -1
- package/dist/mcp/client-install-opencode-contract.js +2 -2
- package/dist/mcp/client-install-opencode.js +2 -2
- package/dist/mcp/control-plane-snapshot-service.js +11 -0
- package/dist/mcp/control-plane.js +55 -0
- package/dist/mcp/schema.js +253 -0
- package/dist/mcp/stdio-server.js +6 -0
- package/dist/mcp/validation.js +662 -0
- package/dist/memory/active-work-preview.js +75 -0
- package/dist/memory/active-work-topics.js +50 -0
- package/dist/memory/sqlite/migrations/018_skill_system_v2.sql +112 -0
- package/dist/setup/providers/opencode-setup-adapter.js +4 -4
- package/dist/setup/setup-plan.js +1 -1
- package/dist/skills/repositories/skill-evaluation-requests.js +190 -0
- package/dist/skills/repositories/skill-improvement-proposals.js +55 -4
- package/dist/skills/repositories/skills.js +630 -3
- package/dist/skills/skill-payload.js +9 -2
- package/dist/skills/skill-registry-service.js +554 -4
- package/dist/skills/skill-resolver.js +53 -3
- package/dist/skills/skill-status-service.js +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const GLOBAL_PERSONAL_AGENTS_PROJECT = '__personal__';
|
|
2
|
+
export const GLOBAL_VGXNESS_AGENTS_PROJECT = '__vgxness__';
|
|
3
|
+
export const LEGACY_VGXNESS_AGENTS_PROJECT = 'vgxness';
|
|
4
|
+
export const agentLookupContexts = (project, scope) => {
|
|
5
|
+
const contexts = [{ project, scope }];
|
|
6
|
+
if (scope !== 'personal')
|
|
7
|
+
contexts.push({ project, scope: 'personal' });
|
|
8
|
+
if (project !== GLOBAL_PERSONAL_AGENTS_PROJECT)
|
|
9
|
+
contexts.push({ project: GLOBAL_PERSONAL_AGENTS_PROJECT, scope: 'personal' });
|
|
10
|
+
if (project !== GLOBAL_VGXNESS_AGENTS_PROJECT)
|
|
11
|
+
contexts.push({ project: GLOBAL_VGXNESS_AGENTS_PROJECT, scope: 'project' });
|
|
12
|
+
if (project !== LEGACY_VGXNESS_AGENTS_PROJECT)
|
|
13
|
+
contexts.push({ project: LEGACY_VGXNESS_AGENTS_PROJECT, scope: 'project' });
|
|
14
|
+
return dedupeContexts(contexts);
|
|
15
|
+
};
|
|
16
|
+
export const dedupeAgentLookupContexts = (contexts) => {
|
|
17
|
+
return dedupeContexts(contexts);
|
|
18
|
+
};
|
|
19
|
+
function dedupeContexts(contexts) {
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
return contexts.filter((context) => {
|
|
22
|
+
const key = `${context.project}:${context.scope}`;
|
|
23
|
+
if (seen.has(key))
|
|
24
|
+
return false;
|
|
25
|
+
seen.add(key);
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentLookupContexts } from './agent-lookup-contexts.js';
|
|
1
2
|
import { AgentResolver } from './agent-resolver.js';
|
|
2
3
|
import { AgentRepository } from './repositories/agents.js';
|
|
3
4
|
export class AgentRegistryService {
|
|
@@ -32,9 +33,9 @@ export class AgentRegistryService {
|
|
|
32
33
|
this.database.close();
|
|
33
34
|
}
|
|
34
35
|
listAgentDefinitions(input) {
|
|
35
|
-
const filters = {};
|
|
36
36
|
if (input.project !== undefined)
|
|
37
|
-
|
|
37
|
+
return this.listAgentDefinitionsForContexts(input);
|
|
38
|
+
const filters = {};
|
|
38
39
|
if (input.scope !== undefined)
|
|
39
40
|
filters.scope = input.scope;
|
|
40
41
|
if (input.mode !== undefined)
|
|
@@ -51,4 +52,30 @@ export class AgentRegistryService {
|
|
|
51
52
|
}
|
|
52
53
|
return { ok: true, value: definitions };
|
|
53
54
|
}
|
|
55
|
+
listAgentDefinitionsForContexts(input) {
|
|
56
|
+
const project = input.project;
|
|
57
|
+
if (project === undefined)
|
|
58
|
+
return { ok: true, value: [] };
|
|
59
|
+
const summariesByName = new Map();
|
|
60
|
+
for (const context of agentLookupContexts(project, input.scope ?? 'project')) {
|
|
61
|
+
const filters = { project: context.project, scope: context.scope };
|
|
62
|
+
if (input.mode !== undefined)
|
|
63
|
+
filters.mode = input.mode;
|
|
64
|
+
const summaries = this.agents.list(filters);
|
|
65
|
+
if (!summaries.ok)
|
|
66
|
+
return summaries;
|
|
67
|
+
for (const summary of summaries.value) {
|
|
68
|
+
if (!summariesByName.has(summary.name))
|
|
69
|
+
summariesByName.set(summary.name, summary);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const definitions = [];
|
|
73
|
+
for (const summary of summariesByName.values()) {
|
|
74
|
+
const agent = this.agents.getById(summary.id);
|
|
75
|
+
if (!agent.ok)
|
|
76
|
+
return agent;
|
|
77
|
+
definitions.push(agent.value);
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, value: definitions };
|
|
80
|
+
}
|
|
54
81
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeSddPhaseInput } from '../sdd/schema.js';
|
|
2
|
+
import { agentLookupContexts } from './agent-lookup-contexts.js';
|
|
2
3
|
export class AgentResolver {
|
|
3
4
|
loadAgents;
|
|
4
5
|
constructor(loadAgents) {
|
|
@@ -59,9 +60,13 @@ function normalizeInput(input) {
|
|
|
59
60
|
const desiredCapabilities = normalizeList(input.desiredCapabilities ?? []);
|
|
60
61
|
const taskDescription = normalizeText(input.taskDescription);
|
|
61
62
|
const intent = normalizeText(input.intent);
|
|
62
|
-
const
|
|
63
|
+
const metaWorkflow = isMetaWorkflow(input.workflow);
|
|
64
|
+
const workflow = normalizeMetaWorkflow(input.workflow);
|
|
65
|
+
const phase = metaWorkflow || input.phase === undefined ? undefined : (isSddWorkflow(workflow) ? (normalizeSddPhaseInput(input.phase) ?? input.phase) : input.phase);
|
|
66
|
+
const { workflow: _workflow, phase: _phase, ...rest } = input;
|
|
63
67
|
return {
|
|
64
|
-
...
|
|
68
|
+
...rest,
|
|
69
|
+
...(workflow !== undefined ? { workflow } : {}),
|
|
65
70
|
...(phase !== undefined ? { phase } : {}),
|
|
66
71
|
...(taskDescription !== undefined ? { taskDescription } : {}),
|
|
67
72
|
...(intent !== undefined ? { intent } : {}),
|
|
@@ -69,11 +74,19 @@ function normalizeInput(input) {
|
|
|
69
74
|
terms: tokenize([taskDescription, intent].filter((value) => value !== undefined).join(' ')),
|
|
70
75
|
};
|
|
71
76
|
}
|
|
77
|
+
function normalizeMetaWorkflow(workflow) {
|
|
78
|
+
if (isMetaWorkflow(workflow))
|
|
79
|
+
return undefined;
|
|
80
|
+
return workflow;
|
|
81
|
+
}
|
|
82
|
+
function isMetaWorkflow(workflow) {
|
|
83
|
+
return workflow?.trim().toLowerCase() === 'workflow-selection';
|
|
84
|
+
}
|
|
72
85
|
function hardSkipReasons(agent, input) {
|
|
73
86
|
const reasons = [];
|
|
74
|
-
if (input.project !== undefined && agent
|
|
75
|
-
reasons.push(`
|
|
76
|
-
if (input.scope !== undefined && agent.scope !== input.scope)
|
|
87
|
+
if (input.project !== undefined && !agentMatchesRequestedContext(agent, input))
|
|
88
|
+
reasons.push(`context mismatch: ${agent.project}/${agent.scope}`);
|
|
89
|
+
else if (input.project === undefined && input.scope !== undefined && agent.scope !== input.scope)
|
|
77
90
|
reasons.push(`scope mismatch: ${agent.scope}`);
|
|
78
91
|
if (input.mode !== undefined && agent.mode !== input.mode)
|
|
79
92
|
reasons.push(`mode mismatch: ${agent.mode}`);
|
|
@@ -85,6 +98,12 @@ function hardSkipReasons(agent, input) {
|
|
|
85
98
|
reasons.push('capability mismatch');
|
|
86
99
|
return reasons;
|
|
87
100
|
}
|
|
101
|
+
function agentMatchesRequestedContext(agent, input) {
|
|
102
|
+
if (input.project === undefined)
|
|
103
|
+
return true;
|
|
104
|
+
const contexts = agentLookupContexts(input.project, input.scope ?? 'project');
|
|
105
|
+
return contexts.some((context) => agent.project === context.project && agent.scope === context.scope);
|
|
106
|
+
}
|
|
88
107
|
function scoreAgent(agent, input) {
|
|
89
108
|
const reasons = [];
|
|
90
109
|
let score = 0;
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
+
import { agentLookupContexts } from './agent-lookup-contexts.js';
|
|
1
2
|
export const agentSelectorNameFallbackCode = 'AGENT_SELECTOR_NAME_FALLBACK';
|
|
2
3
|
export function resolveAgentSelector(input, lookup) {
|
|
3
4
|
const byId = lookup.getById(input.selector);
|
|
4
5
|
if (byId.ok)
|
|
5
6
|
return ok({ agent: byId.value });
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
for (const context of agentLookupContexts(input.project, input.scope)) {
|
|
8
|
+
const byName = lookup.getByName(context.project, context.scope, input.selector);
|
|
9
|
+
if (byName.ok) {
|
|
10
|
+
return ok({
|
|
11
|
+
agent: byName.value,
|
|
12
|
+
warning: `${agentSelectorNameFallbackCode}: selector "${input.selector}" matched agent name in ${context.project}/${context.scope}; using canonical agent id "${byName.value.id}" name "${byName.value.name}".`,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (byName.error.code !== 'not_found')
|
|
16
|
+
return byName;
|
|
12
17
|
}
|
|
13
18
|
return {
|
|
14
19
|
ok: false,
|
|
15
20
|
error: {
|
|
16
21
|
code: 'not_found',
|
|
17
|
-
message: `Agent selector not found: "${input.selector}". Looked up registry id first, then
|
|
22
|
+
message: `Agent selector not found: "${input.selector}". Looked up registry id first, then agent name across project, personal, and VGXNESS global contexts for project "${input.project}" and scope "${input.scope}". Accepted forms: canonical agent id or visible agent name.`,
|
|
18
23
|
},
|
|
19
24
|
};
|
|
20
25
|
}
|
|
@@ -2,7 +2,7 @@ import { canonicalBehaviorContractVersion } from '../behavior/behavior-contract-
|
|
|
2
2
|
export const canonicalDefaultAgentName = 'vgxness-manager';
|
|
3
3
|
export const canonicalOpenCodeDefaultModel = 'openai/gpt-5.5';
|
|
4
4
|
export const canonicalOpenCodeManagerReasoningEffort = 'high';
|
|
5
|
-
export const canonicalPromptContractVersion =
|
|
5
|
+
export const canonicalPromptContractVersion = 15;
|
|
6
6
|
export const canonicalSddSubagentNames = [
|
|
7
7
|
'vgxness-sdd-explore',
|
|
8
8
|
'vgxness-sdd-propose',
|
|
@@ -79,7 +79,7 @@ function managerDefinition() {
|
|
|
79
79
|
name: canonicalDefaultAgentName,
|
|
80
80
|
description: 'Coordinates VGXNESS MCP state and SDD sub-agents while routing Tier 0-2 lightweight work, Tier 3 preflight validation, and Tier 4 formal SDD.',
|
|
81
81
|
instructions: { kind: 'inline', value: registryManagerInstructionsV11 },
|
|
82
|
-
capabilities: ['sdd-orchestration', 'agent-routing', 'mcp-coordination', 'project-local-automation'],
|
|
82
|
+
capabilities: ['sdd-orchestration', 'agent-routing', 'mcp-coordination', 'project-local-automation', 'coordination', 'workflow-selection', 'resume'],
|
|
83
83
|
permissions: { read: 'allow', edit: 'ask', shell: 'ask', git: 'ask', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' },
|
|
84
84
|
memory: { scopes: ['project'] },
|
|
85
85
|
workflows: ['explore', 'quickfix', 'plan', 'build', 'debug', 'sdd', 'agent-seeding', 'opencode-install'],
|
|
@@ -165,25 +165,27 @@ Coordinate SDD; keep chat thin, use VGXNESS MCP state, delegate to exact SDD sub
|
|
|
165
165
|
- SDD artifact acceptance is human-only. Artifact presence is not acceptance. Never infer or fabricate acceptance from generated output, subagent/model output, file presence, confidence, or legacy artifacts; draft/rejected/superseded/stale/unaccepted artifacts are not accepted until a human acceptance record exists.
|
|
166
166
|
- Before phase advancement, call \`vgxness_sdd_status\`/\`vgxness_sdd_next\` plus \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
|
|
167
167
|
- 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 when available. Stop on approval/block; never invent approval.
|
|
168
|
-
- Direct human acceptance
|
|
169
|
-
-
|
|
168
|
+
- Direct human acceptance via \`vgxness_sdd_accept_artifact\` is not a generic SDD write: do not preflight solely for it; require exact project/change/phase, \`acceptedBy.type\` \`"human"\`, non-empty \`acceptedBy.id\`, and eligible status/readiness. Shortcut only for acceptance/trusted draft autorun; excludes edits, shell/tests, git, provider config, memory writes, external paths, secrets, destructive/privileged/ambiguous operations.
|
|
169
|
+
- manager native repo tools are disabled in config (read/glob/edit/write/bash); report config-level enforcement, not proof of host runtime behavior. Subagents keep phase tools.
|
|
170
170
|
- Do not mutate provider/global OpenCode config. Do not publish packages unless explicitly requested. Never revert/overwrite unrelated user work. Preserve \`permission.task\` deny-by-default with only exact known SDD subagents.
|
|
171
171
|
- Do not change provider model/reasoning config unless explicitly requested.
|
|
172
172
|
|
|
173
|
-
##
|
|
174
|
-
Use lightest safe path: T0-2 direct, T3 preflight, T4
|
|
173
|
+
## Routing
|
|
174
|
+
Use lightest safe path: T0-2 direct, T3 preflight, T4 SDD for governance/permissions/acceptance/architecture/security/cross-surface behavior. Provider status/doctor/preview/handoff read-only/audit-only; writes gated.
|
|
175
175
|
|
|
176
176
|
## Provider-native daily flow
|
|
177
177
|
SDD happens in OpenCode via conversation, VGXNESS MCP, and SDD subagents. No terminal SDD phase commands. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
|
|
178
178
|
|
|
179
179
|
## MCP playbook
|
|
180
|
-
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\`;
|
|
180
|
+
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\`; \`vgxness_session_restore\` is one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with id and actor \`manager\`; if no id, do not invent one; summarize.
|
|
181
|
+
- Bounded resume/start hard stop: for "continue"/"sigamos"/"resume development", call compact \`vgxness_skill_payload\`, \`vgxness_context_cockpit\`, \`vgxness_run_resume_candidates\`, then \`vgxness_agent_resolve\` only if an executor is needed. If no interrupted run or exact SDD change/next action exists, stop: do not use Glob/Read/docs, do not inspect repo files, do not delegate to SDD subagents, do not start runs; return status and ask one question: "¿qué cambio retomamos?". \`skill_index\`/\`skill_search\` are not resume fallback.
|
|
181
182
|
- Proposal clarity: if product/business clarity is missing, run a proposal question round.
|
|
182
183
|
- SDD artifacts: guide with \`vgxness_sdd_next\`/\`vgxness_sdd_cockpit\`; list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; prefer \`payloadMode: "compact"\` so primary context stays clean; use verbose only when required, preferably inside the delegated phase subagent. Save with \`vgxness_sdd_save_artifact\` after the right flow. Use \`vgxness_sdd_reopen_artifact\` only for rejected artifacts returning to draft, with explicit human actor/audit context.
|
|
183
|
-
- Trusted draft autorun: when the exact \`proposal\` artifact is already accepted, run
|
|
184
|
-
- Acceptance/readiness: check
|
|
185
|
-
- Memory: \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work/unclear context; \`vgxness_memory_save\` for durable discoveries
|
|
186
|
-
-
|
|
184
|
+
- Trusted draft autorun: when the exact \`proposal\` artifact is already accepted, run \`spec -> design -> tasks\` without extra human confirmation via subagents; save drafts with \`vgxness_sdd_save_artifact\`. This is draft-only planning, not acceptance or completion. Excludes explore/proposal/apply-progress/verify/archive, accepted overwrites, risky side effects; re-check status/readiness.
|
|
185
|
+
- Acceptance/readiness: check exact status/readiness; confirm explicit human acceptance; call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId); re-check before reporting. Do not preflight solely for exact acceptance. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, audit warnings.
|
|
186
|
+
- Memory: \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work/unclear context; \`vgxness_memory_save\` for durable discoveries/decisions/bug fixes/config/patterns/preferences; \`vgxness_memory_update\` only for a known id. Do not duplicate full SDD artifacts as memory.
|
|
187
|
+
- Active-work memory uses existing memory APIs only: \`active-work/{change}/summary\` (+others). No secrets or hidden reasoning. Active-work memory is advisory only; never proves SDD acceptance/readiness, verification, or authorization.
|
|
188
|
+
- Agents/skills: Use \`vgxness_skill_payload\` first for contextual skill guidance; preview/context-only, no provider execution or config writes. Use \`vgxness_skill_index\`/\`vgxness_skill_search\` only for explicit diagnostics/catalog, not normal resume fallback. Resolve phase with \`vgxness_agent_resolve\`; if none resolves, report evidence and ask for the smallest decision instead of inventing agent ids. \`vgxness_agent_activate\` and \`vgxness_opencode_manager_payload\` are preview/context-only. \`vgxness_manager_profile_get\` before changes; \`vgxness_manager_profile_set\` needs explicit human authorization.
|
|
187
189
|
- Runs/recovery: \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\`; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, close with \`vgxness_run_finalize\`. Unknown runId: \`vgxness_run_resume_candidates\`. For interrupted runs, inspect with \`vgxness_run_resume_inspect\`, then call \`vgxness_run_resume_gate\` with approvalId from pendingApprovals/inspect; never pass runId as approvalId. Follow safe \`recommendedActions[]\`.
|
|
188
190
|
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` read-only; report \`providerEvidence.evidenceLevel\` and \`hostToolPresenceVerified\` limits.
|
|
189
191
|
|
|
@@ -209,6 +211,7 @@ const registryManagerInstructionsV11 = [
|
|
|
209
211
|
'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.',
|
|
210
212
|
'Default to delegation for SDD phase-shaped work, repository exploration beyond one narrow lookup, implementation, verification, incident recovery, or multi-step analysis. Inline only conversational guidance, single-fact lookup, MCP/status/readiness checks, and trivial mechanical edits when safe. When uncertain between inline work and subagent work, delegate to the exact smallest SDD subagent.',
|
|
211
213
|
'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.',
|
|
214
|
+
'For continue/sigamos/resume development, run only bounded resume/start: compact skill_payload, context_cockpit, run_resume_candidates, then agent_resolve only if needed. If no interrupted run or exact SDD change/next action exists, hard stop: do not use Glob/Read/docs, inspect repo files, delegate to SDD subagents, start runs, use skill_index/skill_search fallback, or invent agent ids; summarize status and ask one question: "¿qué cambio retomamos?".',
|
|
212
215
|
'Check SDD status/next/ready/cockpit, read prerequisites with sdd_get_artifact or sdd_list_artifacts, use public sdd_continue/internal vgxness_sdd_continue first for advisory read-only continuation plans, use sdd_reopen_artifact only for rejected artifacts returning to draft with explicit human actor/audit context, save phase output with sdd_save_artifact only by governance; when proposal is accepted, spec/design/tasks may run sequentially as draft-only autorun without extra confirmation, while acceptance remains human-only. 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, use run_resume_candidates for unknown runId, inspect interrupted runs by runId with run_resume_inspect, then call run_resume_gate with approvalId from pendingApprovals/inspect; never pass runId as approvalId; use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health.',
|
|
213
216
|
'When sdd_continue/vgxness_sdd_continue returns recommendedActions, prefer the first safe action with agentCallable true and humanOnly false; use targetTool plus suggestedArgs as the starting point, and stop for requiresHumanConfirmation, requiresProviderWriteConsent, requiresPreflight, ambiguous, destructive, privileged, external, or outside-approval actions. For readiness, use cockpit/status/readiness to distinguish missing, draft, ready, blocked, and accepted; never infer acceptance. For provider evidence, report providerEvidence.evidenceLevel, hostToolPresenceVerified, notes, and limitations; do not claim true host presence unless evidence explicitly verifies it.',
|
|
214
217
|
'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.',
|
|
@@ -20,10 +20,10 @@ export function projectCanonicalAgentManifestToOpenCode(manifest = canonicalAgen
|
|
|
20
20
|
mode: 'primary',
|
|
21
21
|
...(manager.adapters?.opencode?.model !== undefined ? { model: manager.adapters.opencode.model } : {}),
|
|
22
22
|
options: { reasoningEffort: canonicalOpenCodeManagerReasoningEffort, vgxnessPromptContractVersion: canonicalPromptContractVersion },
|
|
23
|
-
permission:
|
|
23
|
+
permission: openCodeManagerPermissionsFor(manager),
|
|
24
24
|
prompt: canonicalOpenCodeManagerPrompt,
|
|
25
25
|
reasoningEffort: canonicalOpenCodeManagerReasoningEffort,
|
|
26
|
-
tools:
|
|
26
|
+
tools: openCodeManagerTools(),
|
|
27
27
|
variant: '',
|
|
28
28
|
},
|
|
29
29
|
};
|
|
@@ -132,6 +132,28 @@ function openCodePermissionsFor(agent, additional = {}) {
|
|
|
132
132
|
const adapterPermission = asRecord(agent.adapters?.opencode?.config?.permission);
|
|
133
133
|
return { ...adapterPermission, ...additional, ...mapCanonicalPermissionsToOpenCode(agent.permissions) };
|
|
134
134
|
}
|
|
135
|
+
function openCodeManagerPermissionsFor(agent) {
|
|
136
|
+
return {
|
|
137
|
+
...openCodePermissionsFor(agent, { task: createCanonicalOpenCodeSddTaskPermissions() }),
|
|
138
|
+
bash: 'deny',
|
|
139
|
+
edit: 'deny',
|
|
140
|
+
read: 'deny',
|
|
141
|
+
write: 'deny',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function openCodeManagerTools() {
|
|
145
|
+
return {
|
|
146
|
+
bash: false,
|
|
147
|
+
delegate: true,
|
|
148
|
+
delegation_list: true,
|
|
149
|
+
delegation_read: true,
|
|
150
|
+
edit: false,
|
|
151
|
+
glob: false,
|
|
152
|
+
grep: false,
|
|
153
|
+
read: false,
|
|
154
|
+
write: false,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
135
157
|
function mapCanonicalPermissionsToOpenCode(permissions) {
|
|
136
158
|
const mapped = {};
|
|
137
159
|
if (permissions === undefined)
|
package/dist/cli/cli-help.js
CHANGED
|
@@ -69,7 +69,7 @@ Areas:
|
|
|
69
69
|
mcp doctor [--db <path>] [--project <name>] [--change <id>] [--timeout-ms <ms>]
|
|
70
70
|
MCP setup preview is read-only; it does not install or write .opencode/, .claude/, or provider config.
|
|
71
71
|
Without --db, MCP install and setup commands use the vgxness global default database; pass --db .vgx/memory.sqlite for project-local compatibility.
|
|
72
|
-
OpenCode install defaults to user-global scope and installs mcp.vgxness plus top-level permission.bash=ask, vgxness-manager with bash=
|
|
72
|
+
OpenCode install defaults to user-global scope and installs mcp.vgxness plus top-level permission.bash=ask, vgxness-manager with bash=deny and native repo tools disabled, and hidden vgxness-sdd-* agents with explicit permissions; use --mcp-only for legacy MCP-only config.
|
|
73
73
|
Use --overwrite-vgxness (alias --reinstall) to reinstall only VGXNESS-managed OpenCode entries while preserving unrelated config; --yes is still required to write.
|
|
74
74
|
It writes only after --yes. VGX-managed provider configuration is user-global only for OpenCode and Claude; the OpenCode target is $HOME/.config/opencode/opencode.json.
|
|
75
75
|
Project/local provider files are external/manual diagnostics and will not be written by VGXNESS. Plans are read-only; applies refuse unsafe existing user-global config and create backups before merge.
|
package/dist/cli/home-tui-app.js
CHANGED
|
@@ -365,7 +365,7 @@ function focusedProviderLines(plan) {
|
|
|
365
365
|
];
|
|
366
366
|
}
|
|
367
367
|
function bashPolicySummary(policy) {
|
|
368
|
-
return policy.manager === '
|
|
368
|
+
return policy.manager === 'deny' ? `${policy.topLevel} globally, deny for VGXNESS manager` : policy.topLevel;
|
|
369
369
|
}
|
|
370
370
|
function summaryItems(plan) {
|
|
371
371
|
const ready = plan.status === 'ready';
|
|
@@ -197,7 +197,7 @@ function warningsForScope(scope, overwriteVgxness, agentPlan, bashPermissionPoli
|
|
|
197
197
|
`Reinstall/overwrite is enabled: existing VGXNESS-managed OpenCode entries will be replaced (${agentPlan.installsAgents ? 'mcp.vgxness, default_agent, instructions AGENTS.md, and known VGXNESS agents' : 'mcp.vgxness only'}); unrelated OpenCode config is preserved.`,
|
|
198
198
|
]
|
|
199
199
|
: [];
|
|
200
|
-
const bashWarnings = bashPermissionPolicy.manager === '
|
|
200
|
+
const bashWarnings = bashPermissionPolicy.manager === 'deny' ? ['OpenCode top-level permission.bash is set to ask; the VGXNESS manager agent denies bash while SDD subagents keep explicit phase permissions.'] : ['OpenCode top-level permission.bash is set to ask.'];
|
|
201
201
|
return [
|
|
202
202
|
'Restart OpenCode after installation so it reloads the user MCP config.',
|
|
203
203
|
'OpenCode project config may override user config for a workspace; check project-level config if vgxness is not visible.',
|
|
@@ -206,7 +206,7 @@ function warningsForScope(scope, overwriteVgxness, agentPlan, bashPermissionPoli
|
|
|
206
206
|
];
|
|
207
207
|
}
|
|
208
208
|
function bashPermissionPolicyFor(agentPlan) {
|
|
209
|
-
return agentPlan.installsAgents ? { topLevel: 'ask', manager: '
|
|
209
|
+
return agentPlan.installsAgents ? { topLevel: 'ask', manager: 'deny' } : { topLevel: 'ask' };
|
|
210
210
|
}
|
|
211
211
|
function createdConfigKeys(agentPlan) {
|
|
212
212
|
const keys = agentPlan.installsAgents ? ['$schema', 'instructions', 'default_agent', 'agent', 'mcp'] : ['$schema', 'mcp'];
|
|
@@ -214,7 +214,7 @@ function confirmationRequiredMessage(scope) {
|
|
|
214
214
|
return `\`mcp install opencode\` requires explicit --yes before any ${scope} config write.`;
|
|
215
215
|
}
|
|
216
216
|
function warnings() {
|
|
217
|
-
return ['Restart OpenCode after installation so it reloads the project MCP config.', 'OpenCode top-level permission.bash is set to ask; the VGXNESS manager agent
|
|
217
|
+
return ['Restart OpenCode after installation so it reloads the project MCP config.', 'OpenCode top-level permission.bash is set to ask; the VGXNESS manager agent denies bash while SDD subagents keep explicit phase permissions.'];
|
|
218
218
|
}
|
|
219
219
|
function manualTest(databasePath, source) {
|
|
220
220
|
return {
|
|
@@ -231,5 +231,5 @@ function defaultVerificationHints(databasePath, source) {
|
|
|
231
231
|
];
|
|
232
232
|
}
|
|
233
233
|
function bashPermissionPolicyFor(agentPlan) {
|
|
234
|
-
return agentPlan.installsAgents ? { topLevel: 'ask', manager: '
|
|
234
|
+
return agentPlan.installsAgents ? { topLevel: 'ask', manager: 'deny' } : { topLevel: 'ask' };
|
|
235
235
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActiveWorkPreviewService } from '../memory/active-work-preview.js';
|
|
1
2
|
import { ContextBudgetService } from '../payload/context-budget-service.js';
|
|
2
3
|
export const contextCockpitSnapshotLevels = ['compact', 'expanded', 'verbose'];
|
|
3
4
|
export class ContextCockpitSnapshotService {
|
|
@@ -22,11 +23,21 @@ export class ContextCockpitSnapshotService {
|
|
|
22
23
|
return sddResult;
|
|
23
24
|
const sdd = sddResult?.value;
|
|
24
25
|
const integratesSdd = sdd !== undefined;
|
|
26
|
+
const searchObservationPreviewsNoTrace = this.dependencies.memory.searchObservationPreviewsNoTrace === undefined
|
|
27
|
+
? (() => ({ ok: true, value: [] }))
|
|
28
|
+
: (filters) => this.dependencies.memory.searchObservationPreviewsNoTrace(filters);
|
|
29
|
+
const activeWorkResult = new ActiveWorkPreviewService({
|
|
30
|
+
searchObservationPreviewsNoTrace,
|
|
31
|
+
}).build({ project: input.project, ...(input.change === undefined ? {} : { change: input.change }) });
|
|
32
|
+
if (!activeWorkResult.ok)
|
|
33
|
+
return activeWorkResult;
|
|
34
|
+
const activeWork = activeWorkResult.value;
|
|
25
35
|
const optionalSectionsOmitted = omittedSections(legacy.value.optionalSectionsOmitted, integratesSdd ? ['provider'] : ['sdd', 'provider'], integratesSdd ? ['sdd'] : []);
|
|
26
36
|
const snapshotWithoutBudget = {
|
|
27
37
|
...legacy.value,
|
|
28
38
|
optionalSectionsOmitted,
|
|
29
39
|
...(sdd === undefined ? {} : { sdd }),
|
|
40
|
+
...(activeWork.items.length === 0 && activeWork.change === undefined ? {} : { activeWork }),
|
|
30
41
|
snapshotVersion: 2,
|
|
31
42
|
level,
|
|
32
43
|
references: [
|
|
@@ -14,6 +14,7 @@ import { SddWorkflowService } from '../sdd/sdd-workflow-service.js';
|
|
|
14
14
|
import { runBootSkillSeed } from '../skills/boot-seed.js';
|
|
15
15
|
import { SkillIndexService } from '../skills/skill-index-service.js';
|
|
16
16
|
import { SkillRegistryService } from '../skills/skill-registry-service.js';
|
|
17
|
+
import { SkillStatusService } from '../skills/skill-status-service.js';
|
|
17
18
|
import { VerificationPlanService } from '../verification/index.js';
|
|
18
19
|
import { ProviderChangePlanService } from './provider-change-plan.js';
|
|
19
20
|
import { ContextCockpitSnapshotService } from './control-plane-snapshot-service.js';
|
|
@@ -86,8 +87,60 @@ export function callVgxTool(call, services) {
|
|
|
86
87
|
if (services.skillIndex === undefined)
|
|
87
88
|
return errorEnvelope('validation_failed', 'Skill index service is not available', validated.tool);
|
|
88
89
|
return toEnvelope(validated.tool, services.skillIndex.getIndex(validated.input));
|
|
90
|
+
case 'vgxness_skill_search':
|
|
91
|
+
if (services.skills.searchSkills === undefined)
|
|
92
|
+
return errorEnvelope('validation_failed', 'Skill search service is not available', validated.tool);
|
|
93
|
+
return toEnvelope(validated.tool, services.skills.searchSkills(validated.input));
|
|
94
|
+
case 'vgxness_skill_get':
|
|
95
|
+
if (services.skills.getSkill === undefined)
|
|
96
|
+
return errorEnvelope('validation_failed', 'Skill get service is not available', validated.tool);
|
|
97
|
+
return toEnvelope(validated.tool, services.skills.getSkill(validated.input));
|
|
98
|
+
case 'vgxness_skill_status':
|
|
99
|
+
if (services.skillStatus === undefined)
|
|
100
|
+
return errorEnvelope('validation_failed', 'Skill status service is not available', validated.tool);
|
|
101
|
+
return toEnvelope(validated.tool, services.skillStatus.getStatus(validated.input));
|
|
89
102
|
case 'vgxness_skill_payload':
|
|
90
103
|
return buildSkillPayloadEnvelope(validated.input, services);
|
|
104
|
+
case 'vgxness_skill_activate':
|
|
105
|
+
if (services.skills.activateSkill === undefined)
|
|
106
|
+
return errorEnvelope('validation_failed', 'Skill activation service is not available', validated.tool);
|
|
107
|
+
return toEnvelope(validated.tool, services.skills.activateSkill(validated.input));
|
|
108
|
+
case 'vgxness_skill_record_usage':
|
|
109
|
+
if (services.skills.recordSkillUsage === undefined)
|
|
110
|
+
return errorEnvelope('validation_failed', 'Skill usage service is not available', validated.tool);
|
|
111
|
+
return toEnvelope(validated.tool, services.skills.recordSkillUsage(validated.input));
|
|
112
|
+
case 'vgxness_skill_propose_improvement':
|
|
113
|
+
if (services.skills.proposeSkillImprovement === undefined)
|
|
114
|
+
return errorEnvelope('validation_failed', 'Skill improvement proposal service is not available', validated.tool);
|
|
115
|
+
return toEnvelope(validated.tool, services.skills.proposeSkillImprovement(validated.input));
|
|
116
|
+
case 'vgxness_skill_list_improvement_proposals':
|
|
117
|
+
if (services.skills.listSkillImprovementProposals === undefined)
|
|
118
|
+
return errorEnvelope('validation_failed', 'Skill improvement proposal list service is not available', validated.tool);
|
|
119
|
+
return toEnvelope(validated.tool, services.skills.listSkillImprovementProposals(validated.input));
|
|
120
|
+
case 'vgxness_skill_request_evaluation':
|
|
121
|
+
if (services.skills.requestSkillEvaluation === undefined)
|
|
122
|
+
return errorEnvelope('validation_failed', 'Skill evaluation request service is not available', validated.tool);
|
|
123
|
+
return toEnvelope(validated.tool, services.skills.requestSkillEvaluation(validated.input));
|
|
124
|
+
case 'vgxness_skill_list_evaluations':
|
|
125
|
+
if (services.skills.listSkillEvaluations === undefined)
|
|
126
|
+
return errorEnvelope('validation_failed', 'Skill evaluation list service is not available', validated.tool);
|
|
127
|
+
return toEnvelope(validated.tool, services.skills.listSkillEvaluations(validated.input));
|
|
128
|
+
case 'vgxness_skill_get_evaluation':
|
|
129
|
+
if (services.skills.getSkillEvaluationRequest === undefined)
|
|
130
|
+
return errorEnvelope('validation_failed', 'Skill evaluation get service is not available', validated.tool);
|
|
131
|
+
return toEnvelope(validated.tool, services.skills.getSkillEvaluationRequest(validated.input));
|
|
132
|
+
case 'vgxness_skill_create_draft':
|
|
133
|
+
if (services.skills.createSkillDraft === undefined)
|
|
134
|
+
return errorEnvelope('validation_failed', 'Skill draft creation service is not available', validated.tool);
|
|
135
|
+
return toEnvelope(validated.tool, services.skills.createSkillDraft(validated.input));
|
|
136
|
+
case 'vgxness_skill_update_draft':
|
|
137
|
+
if (services.skills.updateSkillDraft === undefined)
|
|
138
|
+
return errorEnvelope('validation_failed', 'Skill draft update service is not available', validated.tool);
|
|
139
|
+
return toEnvelope(validated.tool, services.skills.updateSkillDraft(validated.input));
|
|
140
|
+
case 'vgxness_skill_publish_version':
|
|
141
|
+
if (services.skills.publishSkillVersion === undefined)
|
|
142
|
+
return errorEnvelope('validation_failed', 'Skill version publication service is not available', validated.tool);
|
|
143
|
+
return toEnvelope(validated.tool, services.skills.publishSkillVersion(validated.input));
|
|
91
144
|
case 'vgxness_opencode_manager_payload':
|
|
92
145
|
return toEnvelope(validated.tool, services.opencodeManagerPayload.build(validated.input));
|
|
93
146
|
case 'vgxness_opencode_handoff_preview':
|
|
@@ -316,6 +369,7 @@ function createServices(database) {
|
|
|
316
369
|
const agents = new AgentRegistryService(database);
|
|
317
370
|
const skills = new SkillRegistryService(database);
|
|
318
371
|
const skillIndex = new SkillIndexService(skills);
|
|
372
|
+
const skillStatus = new SkillStatusService(skills, agents);
|
|
319
373
|
const runs = new RunService(database);
|
|
320
374
|
const managerProfiles = new ManagerProfileOverlayService({ agents, overlays: new ManagerProfileOverlayRepository(database) });
|
|
321
375
|
const opencodeManagerPayload = new OpenCodeManagerPayloadService({ agents, managerProfiles, skills });
|
|
@@ -329,6 +383,7 @@ function createServices(database) {
|
|
|
329
383
|
managerProfiles,
|
|
330
384
|
skills,
|
|
331
385
|
skillIndex,
|
|
386
|
+
skillStatus,
|
|
332
387
|
seedBuiltInSkills: () => runBootSkillSeed(database),
|
|
333
388
|
opencodeManagerPayload,
|
|
334
389
|
opencodeHandoffPreview: new OpenCodeHandoffPreviewService({ managerPayload: opencodeManagerPayload, sdd, providerStatus }),
|