vgxness 1.19.2 → 1.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/canonical-agent-manifest.js +5 -4
- package/dist/mcp/control-plane.js +3 -0
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/resume-context-gate-service.js +176 -0
- package/dist/mcp/schema.js +13 -0
- package/dist/mcp/stdio-server.js +2 -0
- package/dist/mcp/validation.js +53 -0
- package/dist/memory/active-work-topics.js +14 -0
- package/package.json +1 -1
|
@@ -132,6 +132,7 @@ export function createCanonicalOpenCodeSddMcpToolPermissions() {
|
|
|
132
132
|
vgxness_sdd_list_artifacts: 'allow',
|
|
133
133
|
vgxness_sdd_cockpit: 'allow',
|
|
134
134
|
vgxness_context_cockpit: 'allow',
|
|
135
|
+
vgxness_resume_context: 'allow',
|
|
135
136
|
vgxness_agent_resolve: 'allow',
|
|
136
137
|
vgxness_sdd_save_artifact: 'allow',
|
|
137
138
|
vgxness_memory_search: 'allow',
|
|
@@ -177,8 +178,8 @@ Use lightest safe path: T0-2 direct, T3 preflight, T4 SDD for governance/permiss
|
|
|
177
178
|
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
179
|
|
|
179
180
|
## MCP playbook
|
|
180
|
-
- For starting, resuming, or recovering context:
|
|
181
|
-
- Bounded resume/start hard stop: for generic "continue"/"sigamos"/"continuemos"/"resume development" without an explicit user change,
|
|
181
|
+
- For starting, resuming, or recovering context: use \`vgxness_resume_context\` first for generic resume/continue prompts; pass \`explicitChange\` only when the user gave an exact valid change. \`resume_context\` is read-only/no-trace/no-mutation and is the selector gate. \`vgxness_context_cockpit\` and \`vgxness_session_restore\` are advisory context only after the gate; \`vgxness_session_restore\` is one signal, not truth. Do not infer a change from \`context_cockpit.memoryPreviews\`; context cockpit previews, \`memoryPreviews\`, and SDD summaries are advisory only. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with id and actor \`manager\`; if no id, do not invent one; summarize.
|
|
182
|
+
- Bounded resume/start hard stop: for generic "continue"/"sigamos"/"continuemos"/"resume development" without an explicit user change, call \`vgxness_resume_context\` before any SDD continuation/status. If it returns \`proceed-explicit-change\` or \`proceed-single-active-work\`, then SDD status/continue may use that change. If it returns \`inspect-interrupted-run\`, call \`vgxness_run_resume_inspect\`. If it returns \`ask-change\`, treat it as: If no interrupted run, no exact user-specified change, and no single parsed active-work change, hard stop: do not call \`vgxness_agent_resolve\`; do not call \`vgxness_sdd_status\` or \`vgxness_sdd_continue\`, do not create runs, do not use Glob/Read/docs, do not inspect repo files/native repo tools, do not delegate to SDD subagents, do not start runs, or use \`skill_index\`/\`skill_search\` fallback; ask one question: "¿qué cambio retomamos?".
|
|
182
183
|
- Proposal clarity: if product/business clarity is missing, run a proposal question round.
|
|
183
184
|
- 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.
|
|
184
185
|
- 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.
|
|
@@ -210,8 +211,8 @@ Be concise.`;
|
|
|
210
211
|
const registryManagerInstructionsV11 = [
|
|
211
212
|
'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.',
|
|
212
213
|
'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.',
|
|
213
|
-
'Use VGXNESS MCP as the durable control plane:
|
|
214
|
-
'For generic continue/sigamos/continuemos/resume development without an explicit user change,
|
|
214
|
+
'Use VGXNESS MCP as the durable control plane: for generic resume/continue prompts call vgxness_resume_context first, pass explicitChange only when the user gave an exact valid change, and do not infer a change from context_cockpit.memoryPreviews; vgxness_context_cockpit and vgxness_session_restore are advisory context only after the gate, and context cockpit previews, memoryPreviews, and SDD summaries are advisory only. Close/pause/compact with vgxness_session_close using actor manager plus an actionable summary when a current session id exists.',
|
|
215
|
+
'For generic continue/sigamos/continuemos/resume development without an explicit user change, call vgxness_resume_context before any SDD status/continuation. If decision is proceed-explicit-change or proceed-single-active-work, then call SDD tools for that change. If inspect-interrupted-run, call vgxness_run_resume_inspect. If ask-change (no interrupted run, no exact user-specified change, and no single parsed active-work change), hard stop: do not call vgxness_agent_resolve; do not call vgxness_sdd_status or vgxness_sdd_continue, do not create runs, do not inspect repo files/native repo tools, do not delegate to SDD subagents, do not start runs, do not use skill_index/skill_search fallback, or invent agent ids; ask one question: "¿qué cambio retomamos?".',
|
|
215
216
|
'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.',
|
|
216
217
|
'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.',
|
|
217
218
|
'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.',
|
|
@@ -23,6 +23,7 @@ import { ProviderStatusService } from './provider-status.js';
|
|
|
23
23
|
import { errorEnvelope, successEnvelope, } from './schema.js';
|
|
24
24
|
import { validateVgxMcpToolCall } from './validation.js';
|
|
25
25
|
import { OpenCodeHandoffPreviewService } from './opencode-handoff-preview.js';
|
|
26
|
+
import { ResumeContextGateService } from './resume-context-gate-service.js';
|
|
26
27
|
export function callVgxTool(call, services) {
|
|
27
28
|
const validated = validateVgxMcpToolCall(call);
|
|
28
29
|
if (!validated.ok)
|
|
@@ -75,6 +76,8 @@ export function callVgxTool(call, services) {
|
|
|
75
76
|
return toEnvelope(validated.tool, services.memory.restoreSession(validated.input));
|
|
76
77
|
case 'vgxness_context_cockpit':
|
|
77
78
|
return toEnvelope(validated.tool, new ContextCockpitSnapshotService({ memory: services.memory, sdd: services.sdd }).build(validated.input));
|
|
79
|
+
case 'vgxness_resume_context':
|
|
80
|
+
return toEnvelope(validated.tool, new ResumeContextGateService({ memory: services.memory, runs: services.runs }).build(validated.input));
|
|
78
81
|
case 'vgxness_agent_resolve':
|
|
79
82
|
return toEnvelope(validated.tool, services.agents.resolveAgents(validated.input));
|
|
80
83
|
case 'vgxness_agent_activate':
|
package/dist/mcp/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export * from './provider-canonical-agent-manifest.js';
|
|
|
22
22
|
export * from './provider-doctor.js';
|
|
23
23
|
export * from './provider-health-types.js';
|
|
24
24
|
export * from './provider-status.js';
|
|
25
|
+
export * from './resume-context-gate-service.js';
|
|
25
26
|
export * from './schema.js';
|
|
26
27
|
export * from './stdio-server.js';
|
|
27
28
|
export * from './validation.js';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { parseActiveWorkSelectorTopicKey } from '../memory/active-work-topics.js';
|
|
2
|
+
export class ResumeContextGateService {
|
|
3
|
+
dependencies;
|
|
4
|
+
constructor(dependencies) {
|
|
5
|
+
this.dependencies = dependencies;
|
|
6
|
+
}
|
|
7
|
+
build(input) {
|
|
8
|
+
const limit = input.limit ?? 5;
|
|
9
|
+
const context = this.dependencies.memory.getContextCockpit({ project: input.project, ...(input.directory === undefined ? {} : { directory: input.directory }), limit });
|
|
10
|
+
if (!context.ok)
|
|
11
|
+
return context;
|
|
12
|
+
const interrupted = this.dependencies.runs.listRecentInterruptedRuns({ project: input.project, limit });
|
|
13
|
+
if (!interrupted.ok)
|
|
14
|
+
return interrupted;
|
|
15
|
+
const activeWork = this.readActiveWork(input.project, limit);
|
|
16
|
+
if (!activeWork.ok)
|
|
17
|
+
return activeWork;
|
|
18
|
+
const explicitChange = input.explicitChange;
|
|
19
|
+
const activeWorkHits = activeWork.value.slice(0, limit).map((preview) => {
|
|
20
|
+
const parsed = parseActiveWorkSelectorTopicKey(preview.topicKey);
|
|
21
|
+
return {
|
|
22
|
+
id: preview.id,
|
|
23
|
+
topicKey: preview.topicKey ?? '',
|
|
24
|
+
...(parsed.parseStatus === 'parsed' ? { change: parsed.change, ...(parsed.segment === undefined ? {} : { segment: parsed.segment }) } : {}),
|
|
25
|
+
title: preview.title,
|
|
26
|
+
updatedAt: preview.updatedAt,
|
|
27
|
+
preview: preview.preview,
|
|
28
|
+
parseStatus: parsed.parseStatus,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
const distinctActiveWorkChanges = [...new Set(activeWorkHits.map((hit) => hit.change).filter(isString))];
|
|
32
|
+
const decision = decide(explicitChange, interrupted.value, distinctActiveWorkChanges);
|
|
33
|
+
const selectedChange = decision === 'proceed-explicit-change' ? explicitChange : decision === 'proceed-single-active-work' ? distinctActiveWorkChanges[0] : undefined;
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
value: {
|
|
37
|
+
kind: 'resume-context-gate',
|
|
38
|
+
version: 1,
|
|
39
|
+
project: input.project,
|
|
40
|
+
payloadMode: input.payloadMode ?? 'compact',
|
|
41
|
+
limit,
|
|
42
|
+
decision,
|
|
43
|
+
...(selectedChange === undefined ? {} : { selectedChange }),
|
|
44
|
+
...(decision === 'proceed-explicit-change' ? { selectedBy: 'explicit-change' } : {}),
|
|
45
|
+
...(decision === 'proceed-single-active-work' ? { selectedBy: 'single-active-work' } : {}),
|
|
46
|
+
...(decision === 'ask-change' ? { question: '¿qué cambio retomamos?' } : {}),
|
|
47
|
+
nextAction: nextAction(decision),
|
|
48
|
+
allowedNextTools: allowedNextTools(decision),
|
|
49
|
+
prohibitedNextTools: prohibitedNextTools(decision),
|
|
50
|
+
evidence: {
|
|
51
|
+
contextCockpitChecked: true,
|
|
52
|
+
sessionRestoreChecked: true,
|
|
53
|
+
runResumeCandidatesChecked: true,
|
|
54
|
+
activeWorkSearchChecked: true,
|
|
55
|
+
skillPayloadChecked: false,
|
|
56
|
+
explicitChange: explicitChange === undefined ? { supplied: false } : { supplied: true, value: explicitChange, valid: true },
|
|
57
|
+
interruptedRunCandidates: interrupted.value.slice(0, limit).map((candidate) => ({
|
|
58
|
+
runId: candidate.runId,
|
|
59
|
+
project: input.project,
|
|
60
|
+
workflow: candidate.workflow,
|
|
61
|
+
phase: candidate.phase,
|
|
62
|
+
status: candidate.status,
|
|
63
|
+
...(candidate.userIntent === undefined ? {} : { summary: candidate.userIntent }),
|
|
64
|
+
})),
|
|
65
|
+
activeWork: {
|
|
66
|
+
distinctChanges: distinctActiveWorkChanges.slice(0, limit),
|
|
67
|
+
unambiguous: distinctActiveWorkChanges.length === 1,
|
|
68
|
+
hits: activeWorkHits,
|
|
69
|
+
},
|
|
70
|
+
...(context.value.latestRestorableSession === undefined
|
|
71
|
+
? {}
|
|
72
|
+
: {
|
|
73
|
+
latestRestorableSession: {
|
|
74
|
+
sessionId: context.value.latestRestorableSession.id,
|
|
75
|
+
project: context.value.latestRestorableSession.project,
|
|
76
|
+
...(context.value.latestRestorableSession.directory === undefined ? {} : { directory: context.value.latestRestorableSession.directory }),
|
|
77
|
+
...(context.value.latestRestorableSession.endedAt === undefined ? {} : { updatedAt: context.value.latestRestorableSession.endedAt }),
|
|
78
|
+
...(context.value.latestRestorableSession.summary === undefined ? {} : { summary: context.value.latestRestorableSession.summary }),
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
contextPreviews: {
|
|
82
|
+
included: context.value.memoryPreviews.length > 0,
|
|
83
|
+
selectorAuthoritative: false,
|
|
84
|
+
note: 'Context and SDD previews are evidence only and must not be used to select a change.',
|
|
85
|
+
},
|
|
86
|
+
...(input.workspaceRoot === undefined ? {} : { workspaceRoot: { value: input.workspaceRoot, selectorAuthoritative: false } }),
|
|
87
|
+
...(input.userPrompt === undefined ? {} : { userPrompt: { supplied: true, selectorAuthoritative: false } }),
|
|
88
|
+
},
|
|
89
|
+
safety: {
|
|
90
|
+
readOnly: true,
|
|
91
|
+
recordsTraces: false,
|
|
92
|
+
mutatesSessions: false,
|
|
93
|
+
mutatesMemories: false,
|
|
94
|
+
mutatesRuns: false,
|
|
95
|
+
mutatesArtifacts: false,
|
|
96
|
+
writesProviderConfig: false,
|
|
97
|
+
executesProvider: false,
|
|
98
|
+
createsRun: false,
|
|
99
|
+
createsCheckpoint: false,
|
|
100
|
+
recordsSkillUsage: false,
|
|
101
|
+
inspectsRepository: false,
|
|
102
|
+
accessesNetwork: false,
|
|
103
|
+
accessesSecrets: false,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
readActiveWork(project, limit) {
|
|
109
|
+
if (this.dependencies.memory.searchObservationPreviewsNoTrace === undefined) {
|
|
110
|
+
return { ok: false, error: { code: 'validation_failed', message: 'No no-trace active-work preview search is available' } };
|
|
111
|
+
}
|
|
112
|
+
return this.dependencies.memory.searchObservationPreviewsNoTrace({ project, topicKey: 'active-work', limit });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function decide(explicitChange, interruptedRuns, distinctActiveWorkChanges) {
|
|
116
|
+
if (explicitChange !== undefined)
|
|
117
|
+
return 'proceed-explicit-change';
|
|
118
|
+
const interrupted = interruptedRuns[0];
|
|
119
|
+
if (interrupted !== undefined)
|
|
120
|
+
return 'inspect-interrupted-run';
|
|
121
|
+
if (distinctActiveWorkChanges.length === 1)
|
|
122
|
+
return 'proceed-single-active-work';
|
|
123
|
+
return 'ask-change';
|
|
124
|
+
}
|
|
125
|
+
function allowedNextTools(decision) {
|
|
126
|
+
if (decision === 'inspect-interrupted-run')
|
|
127
|
+
return ['vgxness_run_resume_inspect', 'vgxness_run_resume_gate'];
|
|
128
|
+
if (decision === 'ask-change')
|
|
129
|
+
return [];
|
|
130
|
+
return ['vgxness_sdd_status', 'vgxness_sdd_next', 'vgxness_sdd_cockpit', 'vgxness_sdd_continue', 'vgxness_sdd_get_artifact', 'vgxness_sdd_list_artifacts', 'vgxness_agent_resolve'];
|
|
131
|
+
}
|
|
132
|
+
function prohibitedNextTools(decision) {
|
|
133
|
+
const base = ['repository-inspection', 'provider-execution', 'provider-config-write', 'shell', 'git', 'install', 'network', 'secrets'];
|
|
134
|
+
if (decision === 'ask-change')
|
|
135
|
+
return [
|
|
136
|
+
...base,
|
|
137
|
+
'vgxness_sdd_status',
|
|
138
|
+
'vgxness_sdd_next',
|
|
139
|
+
'vgxness_sdd_cockpit',
|
|
140
|
+
'vgxness_sdd_continue',
|
|
141
|
+
'vgxness_sdd_get_artifact',
|
|
142
|
+
'vgxness_sdd_list_artifacts',
|
|
143
|
+
'vgxness_agent_resolve',
|
|
144
|
+
'vgxness_run_start',
|
|
145
|
+
'delegation',
|
|
146
|
+
];
|
|
147
|
+
if (decision === 'inspect-interrupted-run')
|
|
148
|
+
return [
|
|
149
|
+
...base,
|
|
150
|
+
'vgxness_sdd_status',
|
|
151
|
+
'vgxness_sdd_next',
|
|
152
|
+
'vgxness_sdd_cockpit',
|
|
153
|
+
'vgxness_sdd_continue',
|
|
154
|
+
'vgxness_sdd_get_artifact',
|
|
155
|
+
'vgxness_sdd_list_artifacts',
|
|
156
|
+
'vgxness_agent_resolve',
|
|
157
|
+
'vgxness_run_start',
|
|
158
|
+
'delegation',
|
|
159
|
+
];
|
|
160
|
+
return [...base, 'mutating-artifact-tools', 'vgxness_run_start'];
|
|
161
|
+
}
|
|
162
|
+
function nextAction(decision) {
|
|
163
|
+
switch (decision) {
|
|
164
|
+
case 'proceed-explicit-change':
|
|
165
|
+
return 'Proceed with SDD read/progression tools for the explicit selectedChange only.';
|
|
166
|
+
case 'proceed-single-active-work':
|
|
167
|
+
return 'Proceed with SDD read/progression tools for the single active-work selectedChange only.';
|
|
168
|
+
case 'inspect-interrupted-run':
|
|
169
|
+
return 'Inspect the interrupted run candidate before any SDD progression.';
|
|
170
|
+
case 'ask-change':
|
|
171
|
+
return 'Ask the user which change to resume and stop.';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function isString(value) {
|
|
175
|
+
return value !== undefined;
|
|
176
|
+
}
|
package/dist/mcp/schema.js
CHANGED
|
@@ -25,6 +25,7 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
|
|
|
25
25
|
'vgxness_session_close',
|
|
26
26
|
'vgxness_session_restore',
|
|
27
27
|
'vgxness_context_cockpit',
|
|
28
|
+
'vgxness_resume_context',
|
|
28
29
|
'vgxness_agent_resolve',
|
|
29
30
|
'vgxness_agent_activate',
|
|
30
31
|
'vgxness_manager_profile_get',
|
|
@@ -82,6 +83,7 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
|
|
|
82
83
|
'session_close',
|
|
83
84
|
'session_restore',
|
|
84
85
|
'context_cockpit',
|
|
86
|
+
'resume_context',
|
|
85
87
|
'agent_resolve',
|
|
86
88
|
'agent_activate',
|
|
87
89
|
'manager_profile_get',
|
|
@@ -483,6 +485,17 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
483
485
|
level: z.enum(contextCockpitLevels).optional(),
|
|
484
486
|
})
|
|
485
487
|
.passthrough(),
|
|
488
|
+
vgxness_resume_context: z
|
|
489
|
+
.object({
|
|
490
|
+
project: z.string().min(1),
|
|
491
|
+
directory: z.string().min(1).optional(),
|
|
492
|
+
workspaceRoot: z.string().min(1).optional(),
|
|
493
|
+
userPrompt: z.string().optional(),
|
|
494
|
+
explicitChange: z.preprocess((value) => (typeof value === 'string' && value.trim().length === 0 ? undefined : value), z.string().min(1).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/).optional()),
|
|
495
|
+
limit: z.number().int().min(1).max(25).optional(),
|
|
496
|
+
payloadMode: z.enum(payloadModes).optional(),
|
|
497
|
+
})
|
|
498
|
+
.passthrough(),
|
|
486
499
|
vgxness_agent_resolve: z
|
|
487
500
|
.object({
|
|
488
501
|
project: z.string().min(1).optional(),
|
package/dist/mcp/stdio-server.js
CHANGED
|
@@ -67,6 +67,8 @@ function descriptionForTool(publicToolName) {
|
|
|
67
67
|
return 'Read-only run resume gate advisory; plan-only and does not execute resume logic, invoke providers, write provider config, admit retries, abandon or mutate attempts, or reconstruct sandboxes, worktrees, sessions, or transcripts.';
|
|
68
68
|
if (publicToolName === 'context_cockpit')
|
|
69
69
|
return 'Read-only context cockpit for start/resume/recovery; change is optional and should be omitted when unknown; returns latest restorable session plus bounded memory previews without traces, provider config writes, repository writes, runs, artifacts, or session mutations.';
|
|
70
|
+
if (publicToolName === 'resume_context')
|
|
71
|
+
return 'Read-only no-trace/no-mutation resume gate required before generic resume prompts; decides explicit change, interrupted run inspect, single active-work, or ask-change without provider execution, repository inspection, traces, runs, artifacts, sessions, memory writes, or provider config writes.';
|
|
70
72
|
if (publicToolName === 'skill_index')
|
|
71
73
|
return 'Read-only skill registry index for a project and scope; returns SkillIndexOutput without provider config writes, native skill file writes, URL fetches, usage records, startup refresh, or plugin refresh.';
|
|
72
74
|
if (publicToolName === 'skill_create_draft')
|
package/dist/mcp/validation.js
CHANGED
|
@@ -82,6 +82,8 @@ export function validateVgxMcpToolCall(call) {
|
|
|
82
82
|
return validationSuccess(tool.value, validateSessionRestoreInput(input, tool.value));
|
|
83
83
|
case 'vgxness_context_cockpit':
|
|
84
84
|
return validationSuccess(tool.value, validateContextCockpitInput(input, tool.value));
|
|
85
|
+
case 'vgxness_resume_context':
|
|
86
|
+
return validationSuccess(tool.value, validateResumeContextInput(input, tool.value));
|
|
85
87
|
case 'vgxness_agent_resolve':
|
|
86
88
|
return validationSuccess(tool.value, validateAgentResolveInput(input, tool.value));
|
|
87
89
|
case 'vgxness_agent_activate':
|
|
@@ -333,6 +335,45 @@ function validateContextCockpitInput(input, tool) {
|
|
|
333
335
|
result.level = level.value;
|
|
334
336
|
return { ok: true, value: result };
|
|
335
337
|
}
|
|
338
|
+
function validateResumeContextInput(input, tool) {
|
|
339
|
+
const record = inputRecord(input, tool, ['project', 'directory', 'workspaceRoot', 'userPrompt', 'explicitChange', 'limit', 'payloadMode']);
|
|
340
|
+
if (!record.ok)
|
|
341
|
+
return record;
|
|
342
|
+
const project = readNonEmptyString(record.value, 'project', tool);
|
|
343
|
+
if (!project.ok)
|
|
344
|
+
return project;
|
|
345
|
+
const result = { project: project.value };
|
|
346
|
+
const directory = readOptionalNonEmptyString(record.value, 'directory', tool);
|
|
347
|
+
if (!directory.ok)
|
|
348
|
+
return directory;
|
|
349
|
+
if (directory.value !== undefined)
|
|
350
|
+
result.directory = directory.value;
|
|
351
|
+
const workspaceRoot = readOptionalNonEmptyString(record.value, 'workspaceRoot', tool);
|
|
352
|
+
if (!workspaceRoot.ok)
|
|
353
|
+
return workspaceRoot;
|
|
354
|
+
if (workspaceRoot.value !== undefined)
|
|
355
|
+
result.workspaceRoot = workspaceRoot.value;
|
|
356
|
+
if (record.value.userPrompt !== undefined) {
|
|
357
|
+
if (typeof record.value.userPrompt !== 'string')
|
|
358
|
+
return validationFailure('userPrompt must be a string', tool);
|
|
359
|
+
result.userPrompt = record.value.userPrompt;
|
|
360
|
+
}
|
|
361
|
+
const explicitChange = readOptionalExplicitChange(record.value, tool);
|
|
362
|
+
if (!explicitChange.ok)
|
|
363
|
+
return explicitChange;
|
|
364
|
+
if (explicitChange.value !== undefined)
|
|
365
|
+
result.explicitChange = explicitChange.value;
|
|
366
|
+
if (record.value.limit !== undefined) {
|
|
367
|
+
if (typeof record.value.limit !== 'number' || !Number.isSafeInteger(record.value.limit) || record.value.limit < 1 || record.value.limit > 25)
|
|
368
|
+
return validationFailure('limit must be an integer between 1 and 25', tool);
|
|
369
|
+
result.limit = record.value.limit;
|
|
370
|
+
}
|
|
371
|
+
const payloadMode = readOptionalOneOf(record.value, 'payloadMode', payloadModes, tool);
|
|
372
|
+
if (!payloadMode.ok)
|
|
373
|
+
return payloadMode;
|
|
374
|
+
result.payloadMode = payloadMode.value ?? 'compact';
|
|
375
|
+
return { ok: true, value: result };
|
|
376
|
+
}
|
|
336
377
|
function readProjectAndChange(record, tool) {
|
|
337
378
|
const project = readNonEmptyString(record, 'project', tool);
|
|
338
379
|
if (!project.ok)
|
|
@@ -1719,6 +1760,18 @@ function readOptionalContextCockpitChange(record, tool) {
|
|
|
1719
1760
|
return { ok: true, value: undefined };
|
|
1720
1761
|
return readOptionalChange(record, tool);
|
|
1721
1762
|
}
|
|
1763
|
+
function readOptionalExplicitChange(record, tool) {
|
|
1764
|
+
if (record.explicitChange === undefined)
|
|
1765
|
+
return { ok: true, value: undefined };
|
|
1766
|
+
if (typeof record.explicitChange !== 'string')
|
|
1767
|
+
return validationFailure('explicitChange must be a string', tool);
|
|
1768
|
+
const trimmed = record.explicitChange.trim();
|
|
1769
|
+
if (trimmed.length === 0)
|
|
1770
|
+
return { ok: true, value: undefined };
|
|
1771
|
+
if (!validChangePattern.test(trimmed))
|
|
1772
|
+
return validationFailure(`Invalid explicit change id: ${trimmed}`, tool);
|
|
1773
|
+
return { ok: true, value: trimmed };
|
|
1774
|
+
}
|
|
1722
1775
|
function readPhase(record, tool) {
|
|
1723
1776
|
const phase = readNonEmptyString(record, 'phase', tool);
|
|
1724
1777
|
if (!phase.ok)
|
|
@@ -33,6 +33,20 @@ export function parseActiveWorkTopicKey(topicKey) {
|
|
|
33
33
|
return { change: parts[1], segment, slug: parts.slice(3).join('/') };
|
|
34
34
|
return undefined;
|
|
35
35
|
}
|
|
36
|
+
export function parseActiveWorkChangeSelectorTopicKey(topicKey) {
|
|
37
|
+
const parsed = parseActiveWorkSelectorTopicKey(topicKey);
|
|
38
|
+
return parsed.parseStatus === 'parsed' ? { change: parsed.change } : undefined;
|
|
39
|
+
}
|
|
40
|
+
export function parseActiveWorkSelectorTopicKey(topicKey) {
|
|
41
|
+
const parts = topicKey?.split('/') ?? [];
|
|
42
|
+
if (parts[0] !== activeWorkTopicRoot)
|
|
43
|
+
return { parseStatus: 'ignored-non-active-work' };
|
|
44
|
+
const change = parts[1];
|
|
45
|
+
if (change === undefined || change.length === 0 || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(change))
|
|
46
|
+
return { parseStatus: 'malformed' };
|
|
47
|
+
const segment = parts[2];
|
|
48
|
+
return segment === undefined || segment.length === 0 ? { parseStatus: 'parsed', change } : { parseStatus: 'parsed', change, segment };
|
|
49
|
+
}
|
|
36
50
|
function isActiveWorkSegment(value) {
|
|
37
51
|
return isActiveWorkCurrentSegment(value) || isActiveWorkDetailSegment(value);
|
|
38
52
|
}
|