vgxness 1.20.2 → 1.20.4
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 +16 -15
- package/dist/agents/canonical-agent-projection.js +3 -2
- package/dist/agents/manager-profile-overlay-service.js +13 -5
- package/dist/cli/cli-flags.js +47 -1
- package/dist/cli/commands/run-permission-dispatcher.js +152 -1
- package/dist/cli/dispatcher.js +6 -1
- package/dist/mcp/control-plane.js +34 -0
- package/dist/mcp/schema.js +84 -0
- package/dist/mcp/stdio-server.js +6 -0
- package/dist/mcp/validation.js +214 -1
- package/dist/memory/sqlite/migrations/019_task_scoped_command_grants.sql +37 -0
- package/dist/permissions/path-boundaries.js +87 -0
- package/dist/permissions/task-scoped-command-grant-matcher.js +117 -0
- package/dist/permissions/task-scoped-command-grant-validation.js +135 -0
- package/dist/runs/repositories/task-scoped-command-grants.js +462 -0
- package/dist/runs/run-service.js +60 -1
- package/dist/runs/sandbox-process-execution.js +41 -30
- package/dist/runs/task-scoped-command-grant-service.js +216 -0
- package/dist/sdd/sdd-continuation-plan.js +1 -1
- package/dist/workflows/command-allowlist-adapter.js +50 -0
- package/docs/safety.md +23 -0
- package/package.json +1 -1
|
@@ -157,10 +157,10 @@ export function createCanonicalOpenCodeSddSubagentPrompt(name) {
|
|
|
157
157
|
*/
|
|
158
158
|
export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager
|
|
159
159
|
|
|
160
|
-
Bind to \`vgxness-manager\` only.
|
|
160
|
+
Bind to \`vgxness-manager\` only.
|
|
161
161
|
|
|
162
162
|
## Role
|
|
163
|
-
Coordinate SDD; keep chat thin, use
|
|
163
|
+
Coordinate SDD; keep chat thin, use MCP state, delegate to exact SDD subagent. Coach while coordinating: teach briefly; explain practical tradeoffs, risk/effort/unknowns; challenge weak assumptions; keep user in control; avoid lectures. Always verify before agreeing with technical claims. When blocked, ask one focused question and stop.
|
|
164
164
|
|
|
165
165
|
## Non-negotiable governance
|
|
166
166
|
- VGXNESS uses SQLite artifacts/control-plane; OpenCode as primary provider; artifacts are not openspec files. Do not write to \`openspec/\`.
|
|
@@ -173,28 +173,28 @@ Coordinate SDD; keep chat thin, use VGXNESS MCP state, delegate to exact SDD sub
|
|
|
173
173
|
- Do not change provider model/reasoning config unless explicitly requested.
|
|
174
174
|
|
|
175
175
|
## Routing
|
|
176
|
-
Use lightest safe path: T0-2 direct, T3 preflight, T4 SDD
|
|
176
|
+
Use lightest safe path: T0-2 direct, T3 preflight, T4 SDD.
|
|
177
|
+
Do not invent or invoke generic reviewer subagents; use only exact canonical SDD subagents or MCP guidance.
|
|
177
178
|
|
|
178
179
|
## Provider-native daily flow
|
|
179
|
-
SDD happens in OpenCode
|
|
180
|
+
SDD happens in OpenCode conversation + VGXNESS MCP/subagents. No terminal SDD phase commands. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
|
|
180
181
|
|
|
181
182
|
## MCP playbook
|
|
182
|
-
-
|
|
183
|
-
- Bounded resume/start hard stop: for generic "continue"/"sigamos"/"continuemos"/"resume development" without an explicit
|
|
183
|
+
- Start/resume/recover: call \`vgxness_resume_context\` first for generic resume/continue; pass \`explicitChange\` only for an exact user change. Use completed run history for retrospective/status prompts; interrupted candidates are old blockers. \`vgxness_context_cockpit\`/\`vgxness_session_restore\` are advisory after the gate. Do not infer a change from \`context_cockpit.memoryPreviews\`. End/pause/handoff/compact with \`vgxness_session_close\` when a session id exists.
|
|
184
|
+
- Bounded resume/start hard stop: for generic "continue"/"sigamos"/"continuemos"/"resume development" without an explicit change, call \`vgxness_resume_context\` before any SDD continuation/status. \`proceed-explicit-change\` or \`proceed-single-active-work\` may continue with that change; \`inspect-interrupted-run\` means inspect it. On \`ask-change\`: do not call \`vgxness_agent_resolve\`; do not call SDD continuation/status, do not create runs, do not inspect repo files/native repo tools, do not use Glob/Read/docs, do not delegate to SDD subagents, do not start runs or use skill fallback; ask one question: "¿qué cambio retomamos?".
|
|
184
185
|
- Proposal clarity: if product/business clarity is missing, run a proposal question round.
|
|
185
|
-
- SDD artifacts: guide with \`vgxness_sdd_next\`/\`vgxness_sdd_cockpit\`; list
|
|
186
|
-
-
|
|
186
|
+
- SDD artifacts: guide with \`vgxness_sdd_next\`/\`vgxness_sdd_cockpit\`; read/list compact (payloadMode: "compact") via \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`. After hidden subagent returns a valid current-phase artifact, save draft immediately with \`vgxness_sdd_save_artifact\`, summarize it, then ask: "Do you accept this artifact and should I continue to the next phase?" Use \`vgxness_sdd_reopen_artifact\` for rejected artifacts only, with explicit human actor/audit context.
|
|
187
|
+
- delegated phase subagent outputs: inspect delegated result-envelope completeness before SDD artifact persistence or phase advancement (status, executive_summary, artifacts_examined, skill_resolution, risks, next_recommended); this is prompt guidance only, not runtime parsing/rejection.
|
|
187
188
|
- 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.
|
|
188
|
-
- Acceptance/readiness: check exact status/readiness;
|
|
189
|
-
- Memory:
|
|
189
|
+
- Acceptance/readiness: check exact status/readiness; call \`vgxness_sdd_accept_artifact\` directly with audit context; re-check before reporting. Do not preflight solely for exact acceptance. Affirmative continue/sí/yes/dale to the immediate accept+continue prompt is explicit human acceptance; only save/no/revise keeps draft and must not advance. Never silently auto-accept. Use \`vgxness_governance_report\` for audit.
|
|
190
|
+
- Memory: search/get for prior work and duplicate avoidance. Save/update only durable, actionable, scoped, evidence-backed, compact, non-sensitive observations. Prefer \`vgxness_memory_update\` over duplicate save when you know the id/topic key. Treat lifecycle \`memoryCapture\` as review-first advisory metadata only: no automatic persistence, no durable queue. Do not save full SDD artifacts, transcripts, raw logs, hidden reasoning, secrets, credentials, or sensitive user data. Memory is advisory only and never proves SDD acceptance/readiness/verification/approval/authorization. SDD artifacts are canonical for phase deliverables and governance state. OpenCode previews/handoff are read-only and capture-free. Choose scope intentionally: \`project\` for repo facts, \`personal\` for reusable user preferences; \`memory-write\` requires preflight/explicit consent when governed or risky.
|
|
190
191
|
- 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, approval, or authorization.
|
|
191
192
|
- 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.
|
|
192
193
|
- 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[]\`.
|
|
193
|
-
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` read-only; report \`providerEvidence.evidenceLevel\` and \`hostToolPresenceVerified\` limits.
|
|
194
|
+
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` read-only; report \`providerEvidence.evidenceLevel\` and \`hostToolPresenceVerified\` limits; use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health.
|
|
194
195
|
|
|
195
196
|
## Minimum flows
|
|
196
|
-
-
|
|
197
|
-
- Proposal/spec/design/tasks: status/next -> readiness -> prereqs -> exact subagent -> delegate -> persist; if proposal is accepted, spec/design/tasks may use trusted draft autorun without extra confirmation; drafts remain unaccepted.
|
|
197
|
+
- Proposal/spec/design/tasks: status/next -> readiness -> prereqs -> subagent -> save draft -> summarize -> ask accept+continue; trusted autorun may draft spec/design/tasks after accepted proposal; drafts stay unaccepted until human acceptance.
|
|
198
198
|
- Apply/verify: prereqs -> artifacts -> exact subagent -> start/recover run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save -> finalize. Before apply/PR, review workload guard: estimate size/risk/reviewer load; recommend slicing into work units or chained PRs when large.
|
|
199
199
|
- Config/provider/prompt change: inspect manager/profile/payload first; persistence needs explicit human authorization.
|
|
200
200
|
- Recovery: MCP first. Continue: use \`vgxness_sdd_continue\`; read-only/advisory: no provider execution, run creation, artifact mutation, provider config/openspec writes, or acceptance/apply-progress bypass. CLI \`vgxness sdd continue\` is human fallback only. \`vgxness resume --project\` is for candidate runs. The removed experimental \`vgxness code\` runtime is not an SDD fallback.
|
|
@@ -211,10 +211,11 @@ Be concise.`;
|
|
|
211
211
|
* intentionally distinct from the provider runtime prompt above.
|
|
212
212
|
*/
|
|
213
213
|
const registryManagerInstructionsV11 = [
|
|
214
|
-
'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.',
|
|
215
|
-
'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.',
|
|
214
|
+
'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. Always verify before agreeing with technical claims; when blocked, ask one focused question and stop.',
|
|
215
|
+
'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. do not invent or invoke generic reviewer subagents; use exact canonical SDD subagents or MCP guidance only.',
|
|
216
216
|
'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; for retrospective/status prompts such as "what did we do?" or "qué habíamos hecho", inspect recent completed run history first with vgxness_run_list completed and mention interrupted candidates only as old blockers, not as latest truth; 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.',
|
|
217
217
|
'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?".',
|
|
218
|
+
'Manager save-then-accept behavior: after a valid hidden phase subagent artifact, save it immediately as a draft with sdd_save_artifact, summarize the saved draft, then ask whether the human accepts it and wants to continue to the next phase. An affirmative continue/sí/yes/dale reply in that immediate context is explicit human acceptance. A reply of only save/no/revise keeps draft and must not advance. Never silently auto-accept.',
|
|
218
219
|
'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; inspect delegated result-envelope completeness before SDD artifact persistence or phase advancement (status, executive_summary, artifacts_examined, skill_resolution, risks, next_recommended) as prompt guidance only, not runtime parsing/rejection; when proposal is accepted, spec/design/tasks may run sequentially as draft-only autorun without extra confirmation, while acceptance remains human-only. Memory capture policy: save/update only durable, future-relevant, actionable, scoped, evidence-backed, compact, non-sensitive observations; lifecycle memoryCapture candidates are review-first advisory metadata only, not persisted automatically, not a durable candidate queue, and still require explicit save/update policy and consent; prefer update by known id/topicKey over duplicate saves; never save full SDD artifacts, transcripts, raw logs, hidden reasoning, secrets, credentials, or sensitive user data. Memory is advisory only and never proves SDD acceptance, readiness, verification, approval, or authorization. SDD artifacts are canonical for phase deliverables/governance state. OpenCode previews/handoff are read-only and capture-free. Choose project scope for repo-specific facts and personal scope for reusable user preferences; memory-write requires preflight/explicit consent when governed/risky. 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.',
|
|
219
220
|
'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.',
|
|
220
221
|
'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.',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { canonicalAgentManifest, canonicalDefaultAgentName, canonicalOpenCodeManagerReasoningEffort, canonicalOpenCodeManagerPrompt, canonicalPromptContractVersion, canonicalSddSubagentNames, createCanonicalOpenCodeSddSubagentPrompt, createCanonicalOpenCodeSddTaskPermissions, validateCanonicalAgentManifest, } from './canonical-agent-manifest.js';
|
|
2
|
+
import { composeManagerInstructions } from './manager-profile-overlay-service.js';
|
|
2
3
|
export function canonicalManifestValidationErrors(manifest = canonicalAgentManifest) {
|
|
3
4
|
const validation = validateCanonicalAgentManifest(manifest);
|
|
4
5
|
return validation.ok ? [] : validation.errors;
|
|
@@ -55,7 +56,7 @@ export function withEffectiveOpenCodeManagerInstructions(projection, instruction
|
|
|
55
56
|
defaultAgent: projection.defaultAgent,
|
|
56
57
|
agents: {
|
|
57
58
|
...projection.agents,
|
|
58
|
-
[projection.defaultAgent]: { ...manager, prompt: trimmed },
|
|
59
|
+
[projection.defaultAgent]: { ...manager, prompt: composeManagerInstructions(manager.prompt, trimmed) },
|
|
59
60
|
},
|
|
60
61
|
};
|
|
61
62
|
}
|
|
@@ -93,7 +94,7 @@ export function withEffectiveManagerInstructions(projection, instructions) {
|
|
|
93
94
|
const trimmed = instructions?.trim() ?? '';
|
|
94
95
|
if (trimmed === '')
|
|
95
96
|
return projection;
|
|
96
|
-
const agents = projection.agents.map((agent) => agent.canonicalName === projection.defaultAgent ? { ...agent, instructions: trimmed } : agent);
|
|
97
|
+
const agents = projection.agents.map((agent) => agent.canonicalName === projection.defaultAgent ? { ...agent, instructions: composeManagerInstructions(agent.instructions, trimmed) } : agent);
|
|
97
98
|
return { defaultAgent: projection.defaultAgent, agents };
|
|
98
99
|
}
|
|
99
100
|
export function projectCanonicalAgentManifestToClaudeProjectMemory(manifest = canonicalAgentManifest) {
|
|
@@ -26,12 +26,23 @@ export class ManagerProfileOverlayService {
|
|
|
26
26
|
return ok({
|
|
27
27
|
manager: {
|
|
28
28
|
...manager.value,
|
|
29
|
-
instructions: { kind: 'inline', value: overlay.value.instructions },
|
|
29
|
+
instructions: { kind: 'inline', value: composeManagerInstructions(manager.value.instructions.value, overlay.value.instructions) },
|
|
30
30
|
},
|
|
31
31
|
overlay: overlay.value,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
export function composeManagerInstructions(baseline, overlay) {
|
|
36
|
+
const baselineInstructions = baseline.trim();
|
|
37
|
+
const overlayInstructions = overlay.trim();
|
|
38
|
+
if (baselineInstructions === '')
|
|
39
|
+
return overlayInstructions;
|
|
40
|
+
if (overlayInstructions === '')
|
|
41
|
+
return baselineInstructions;
|
|
42
|
+
if (overlayInstructions.includes(baselineInstructions))
|
|
43
|
+
return overlayInstructions;
|
|
44
|
+
return `${baselineInstructions}\n\n## Manager profile overlay\n${overlayInstructions}`;
|
|
45
|
+
}
|
|
35
46
|
function ok(value) {
|
|
36
47
|
return { ok: true, value };
|
|
37
48
|
}
|
|
@@ -45,8 +56,5 @@ export function computeEffectiveManagerInstructions(database, project = 'vgxness
|
|
|
45
56
|
const resolved = service.resolveEffectiveManager({ project, scope, managerName });
|
|
46
57
|
if (!resolved.ok || resolved.value.overlay === undefined)
|
|
47
58
|
return undefined;
|
|
48
|
-
|
|
49
|
-
if (overlayInstructions === '')
|
|
50
|
-
return undefined;
|
|
51
|
-
return overlayInstructions;
|
|
59
|
+
return resolved.value.manager.instructions.value.trim() || undefined;
|
|
52
60
|
}
|
package/dist/cli/cli-flags.js
CHANGED
|
@@ -241,6 +241,52 @@ function optionalNumberFlag(flags, name) {
|
|
|
241
241
|
const parsed = Number(value);
|
|
242
242
|
return Number.isInteger(parsed) && parsed > 0 ? { ok: true, value: parsed } : validationFailure(`--${name} must be a positive integer`);
|
|
243
243
|
}
|
|
244
|
+
function requiredPositiveIntegerFlag(flags, name) {
|
|
245
|
+
const value = requiredFlag(flags, name);
|
|
246
|
+
if (!value.ok)
|
|
247
|
+
return value;
|
|
248
|
+
const parsed = Number(value.value);
|
|
249
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? { ok: true, value: parsed } : validationFailure(`--${name} must be a positive integer`);
|
|
250
|
+
}
|
|
251
|
+
function taskGrantCategoryFlag(flags, name) {
|
|
252
|
+
const value = requiredFlag(flags, name);
|
|
253
|
+
if (!value.ok)
|
|
254
|
+
return value;
|
|
255
|
+
return value.value === 'shell' || value.value === 'test-run' || value.value === 'git'
|
|
256
|
+
? { ok: true, value: value.value }
|
|
257
|
+
: validationFailure(`--${name} must be shell, test-run, or git`);
|
|
258
|
+
}
|
|
259
|
+
function optionalTaskGrantStatusFlag(flags, name) {
|
|
260
|
+
const value = optionalStringFlag(flags, name);
|
|
261
|
+
if (value === undefined)
|
|
262
|
+
return { ok: true, value: undefined };
|
|
263
|
+
return value === 'active' || value === 'revoked' || value === 'expired' || value === 'exhausted'
|
|
264
|
+
? { ok: true, value }
|
|
265
|
+
: validationFailure(`--${name} must be active, revoked, expired, or exhausted`);
|
|
266
|
+
}
|
|
267
|
+
function taskGrantExpirationFlag(flags) {
|
|
268
|
+
const expiresAt = optionalStringFlag(flags, 'expires-at');
|
|
269
|
+
const ttl = optionalStringFlag(flags, 'ttl');
|
|
270
|
+
if ((expiresAt === undefined) === (ttl === undefined))
|
|
271
|
+
return validationFailure('Pass exactly one of --expires-at or --ttl');
|
|
272
|
+
if (expiresAt !== undefined) {
|
|
273
|
+
const timestamp = Date.parse(expiresAt);
|
|
274
|
+
if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== expiresAt)
|
|
275
|
+
return validationFailure('--expires-at must be a valid ISO date-time');
|
|
276
|
+
return timestamp > Date.now() ? { ok: true, value: expiresAt } : validationFailure('--expires-at must be in the future');
|
|
277
|
+
}
|
|
278
|
+
if (ttl === undefined)
|
|
279
|
+
return validationFailure('Pass exactly one of --expires-at or --ttl');
|
|
280
|
+
const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl);
|
|
281
|
+
if (match === null)
|
|
282
|
+
return validationFailure('--ttl must be a positive duration like 30s, 15m, 2h, or 1d');
|
|
283
|
+
const amount = Number(match[1]);
|
|
284
|
+
if (!Number.isSafeInteger(amount) || amount <= 0)
|
|
285
|
+
return validationFailure('--ttl must be a positive duration like 30s, 15m, 2h, or 1d');
|
|
286
|
+
const unit = match[2];
|
|
287
|
+
const multiplier = unit === 'ms' ? 1 : unit === 's' ? 1_000 : unit === 'm' ? 60_000 : unit === 'h' ? 3_600_000 : 86_400_000;
|
|
288
|
+
return { ok: true, value: new Date(Date.now() + amount * multiplier).toISOString() };
|
|
289
|
+
}
|
|
244
290
|
function optionalRunStatusFlag(flags, name) {
|
|
245
291
|
const value = optionalStringFlag(flags, name);
|
|
246
292
|
if (value === undefined)
|
|
@@ -338,4 +384,4 @@ function setupInstallModeFlag(flags) {
|
|
|
338
384
|
return { ok: true, value: 'mcp-only' };
|
|
339
385
|
return { ok: true, value: vgxnessSetupDefaults.defaultInstallMode };
|
|
340
386
|
}
|
|
341
|
-
export { acceptedAtFlag, codeApprovalPolicyFlag, codeMemoryPolicyFlag, codeTranscriptModeFlag, codeVerificationModeFlag, csvFlag, databasePathFor, databasePathSelectionFor, finalRunStatusFlag, instructionKindFlag, isRunStatus, isSkillEvaluationResultStatus, isSkillTargetType, jsonFlag, providerUserGlobalOnlyMigrationMessage, opencodeDiagnosticScopeFlag, opencodeInstallScopeFlag, optionalJsonFlag, optionalModeFlag, optionalNumberFlag, optionalRunStatusFlag, optionalScopeFlag, optionalSkillEvaluationResultStatusFlag, optionalSkillImprovementProposalStatusFlag, optionalSkillResolutionUsageFlag, optionalSkillTargetTypeFlag, optionalSkillVersionStatusFlag, optionalStringFlag, optionalTrimmedFlag, parseArgs, permissionCategoryFlag, requiredFlag, requiredTrimmedFlag, retryPolicyFlag, runOutcomeFlag, scopeFlag, setupDatabaseFlags, setupInstallModeFlag, setupProviderFlag, skillEvaluationResultStatusFlag, skillSourceFromFlags, skillSourceKindFlag, skillTargetTypeFlag, skillUsageOutcomeFlag, };
|
|
387
|
+
export { acceptedAtFlag, codeApprovalPolicyFlag, codeMemoryPolicyFlag, codeTranscriptModeFlag, codeVerificationModeFlag, csvFlag, databasePathFor, databasePathSelectionFor, finalRunStatusFlag, instructionKindFlag, isRunStatus, isSkillEvaluationResultStatus, isSkillTargetType, jsonFlag, providerUserGlobalOnlyMigrationMessage, opencodeDiagnosticScopeFlag, opencodeInstallScopeFlag, optionalJsonFlag, optionalModeFlag, optionalNumberFlag, optionalRunStatusFlag, optionalScopeFlag, optionalSkillEvaluationResultStatusFlag, optionalSkillImprovementProposalStatusFlag, optionalSkillResolutionUsageFlag, optionalSkillTargetTypeFlag, optionalSkillVersionStatusFlag, optionalTaskGrantStatusFlag, optionalStringFlag, optionalTrimmedFlag, parseArgs, permissionCategoryFlag, requiredPositiveIntegerFlag, requiredFlag, requiredTrimmedFlag, retryPolicyFlag, runOutcomeFlag, scopeFlag, setupDatabaseFlags, setupInstallModeFlag, setupProviderFlag, skillEvaluationResultStatusFlag, skillSourceFromFlags, skillSourceKindFlag, skillTargetTypeFlag, skillUsageOutcomeFlag, taskGrantCategoryFlag, taskGrantExpirationFlag, };
|
|
@@ -2,7 +2,8 @@ import { AgentRegistryService } from '../../agents/agent-registry-service.js';
|
|
|
2
2
|
import { evaluatePermission } from '../../permissions/policy-evaluator.js';
|
|
3
3
|
import { RunService } from '../../runs/run-service.js';
|
|
4
4
|
import { RunSnapshotExportService } from '../../runs/run-snapshot-export-service.js';
|
|
5
|
-
import {
|
|
5
|
+
import { grantEvaluationPayload } from '../../runs/task-scoped-command-grant-service.js';
|
|
6
|
+
import { csvFlag, finalRunStatusFlag, jsonFlag, optionalRunStatusFlag, optionalStringFlag, optionalTaskGrantStatusFlag, permissionCategoryFlag, requiredPositiveIntegerFlag, requiredFlag, retryPolicyFlag, runOutcomeFlag, taskGrantCategoryFlag, taskGrantExpirationFlag, } from '../cli-flags.js';
|
|
6
7
|
import { usageFailure } from '../cli-help.js';
|
|
7
8
|
import { isJsonObject, jsonResult, resultFailure, writeJsonFile } from '../cli-helpers.js';
|
|
8
9
|
function listCliApprovals(service, runId) {
|
|
@@ -94,8 +95,158 @@ function permissionRequestFromFlags(parsed, database, environment) {
|
|
|
94
95
|
}
|
|
95
96
|
return { ok: true, value: request };
|
|
96
97
|
}
|
|
98
|
+
function rejectExecutableGrantFlags(parsed) {
|
|
99
|
+
if (parsed.flags.executable !== undefined || parsed.flags.argv !== undefined)
|
|
100
|
+
return { ok: false, error: { code: 'validation_failed', message: 'Task grants accept --command-id only; arbitrary executable/argv inputs are not supported' } };
|
|
101
|
+
return { ok: true, value: undefined };
|
|
102
|
+
}
|
|
103
|
+
function taskGrantExternalRoots(parsed) {
|
|
104
|
+
const roots = [...csvFlag(parsed.flags, 'external-roots')];
|
|
105
|
+
const root = optionalStringFlag(parsed.flags, 'external-root');
|
|
106
|
+
if (root !== undefined)
|
|
107
|
+
roots.unshift(root);
|
|
108
|
+
return roots.length > 0 ? { ok: true, value: roots } : { ok: false, error: { code: 'validation_failed', message: 'Missing required --external-root or --external-roots' } };
|
|
109
|
+
}
|
|
110
|
+
function runTaskGrantCommand(command, parsed, service) {
|
|
111
|
+
const grantService = service.taskScopedCommandGrantService();
|
|
112
|
+
const executableFlags = rejectExecutableGrantFlags(parsed);
|
|
113
|
+
if (!executableFlags.ok)
|
|
114
|
+
return resultFailure(executableFlags);
|
|
115
|
+
if (command === 'task-grant-create') {
|
|
116
|
+
const runId = requiredFlag(parsed.flags, 'run-id');
|
|
117
|
+
const agentId = requiredFlag(parsed.flags, 'agent-id');
|
|
118
|
+
const taskFingerprint = requiredFlag(parsed.flags, 'task-fingerprint');
|
|
119
|
+
const category = taskGrantCategoryFlag(parsed.flags, 'category');
|
|
120
|
+
const commandId = requiredFlag(parsed.flags, 'command-id');
|
|
121
|
+
const maxUses = requiredPositiveIntegerFlag(parsed.flags, 'max-uses');
|
|
122
|
+
const expiresAt = taskGrantExpirationFlag(parsed.flags);
|
|
123
|
+
const createdById = requiredFlag(parsed.flags, 'created-by-id');
|
|
124
|
+
const creationReason = requiredFlag(parsed.flags, 'creation-reason');
|
|
125
|
+
const approvedScopeSummary = requiredFlag(parsed.flags, 'approved-scope-summary');
|
|
126
|
+
const externalRoots = taskGrantExternalRoots(parsed);
|
|
127
|
+
if (!runId.ok)
|
|
128
|
+
return resultFailure(runId);
|
|
129
|
+
if (!agentId.ok)
|
|
130
|
+
return resultFailure(agentId);
|
|
131
|
+
if (!taskFingerprint.ok)
|
|
132
|
+
return resultFailure(taskFingerprint);
|
|
133
|
+
if (!category.ok)
|
|
134
|
+
return resultFailure(category);
|
|
135
|
+
if (!commandId.ok)
|
|
136
|
+
return resultFailure(commandId);
|
|
137
|
+
if (!maxUses.ok)
|
|
138
|
+
return resultFailure(maxUses);
|
|
139
|
+
if (!expiresAt.ok)
|
|
140
|
+
return resultFailure(expiresAt);
|
|
141
|
+
if (!createdById.ok)
|
|
142
|
+
return resultFailure(createdById);
|
|
143
|
+
if (!creationReason.ok)
|
|
144
|
+
return resultFailure(creationReason);
|
|
145
|
+
if (!approvedScopeSummary.ok)
|
|
146
|
+
return resultFailure(approvedScopeSummary);
|
|
147
|
+
if (!externalRoots.ok)
|
|
148
|
+
return resultFailure(externalRoots);
|
|
149
|
+
const createdByName = optionalStringFlag(parsed.flags, 'created-by-name');
|
|
150
|
+
const cwdPatterns = csvFlag(parsed.flags, 'cwd-patterns');
|
|
151
|
+
return jsonResult(grantService.create({
|
|
152
|
+
runId: runId.value,
|
|
153
|
+
agentId: agentId.value,
|
|
154
|
+
taskFingerprint: taskFingerprint.value,
|
|
155
|
+
categories: [category.value],
|
|
156
|
+
externalRoots: externalRoots.value,
|
|
157
|
+
...(cwdPatterns.length === 0 ? {} : { cwdPatterns }),
|
|
158
|
+
commandPolicy: { kind: 'allowlisted-command-ids', ids: [commandId.value] },
|
|
159
|
+
expiresAt: expiresAt.value,
|
|
160
|
+
maxUses: maxUses.value,
|
|
161
|
+
createdBy: { type: 'human', id: createdById.value, ...(createdByName === undefined ? {} : { displayName: createdByName }) },
|
|
162
|
+
audit: { creationReason: creationReason.value, approvedScopeSummary: approvedScopeSummary.value },
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
if (command === 'task-grant-list') {
|
|
166
|
+
const runId = requiredFlag(parsed.flags, 'run-id');
|
|
167
|
+
const status = optionalTaskGrantStatusFlag(parsed.flags, 'status');
|
|
168
|
+
const limit = parsed.flags.limit === undefined ? { ok: true, value: undefined } : requiredPositiveIntegerFlag(parsed.flags, 'limit');
|
|
169
|
+
if (!runId.ok)
|
|
170
|
+
return resultFailure(runId);
|
|
171
|
+
if (!status.ok)
|
|
172
|
+
return resultFailure(status);
|
|
173
|
+
if (!limit.ok)
|
|
174
|
+
return resultFailure(limit);
|
|
175
|
+
const agentId = optionalStringFlag(parsed.flags, 'agent-id');
|
|
176
|
+
return jsonResult(grantService.list({
|
|
177
|
+
runId: runId.value,
|
|
178
|
+
...(agentId === undefined ? {} : { agentId }),
|
|
179
|
+
...(status.value === undefined ? {} : { status: status.value }),
|
|
180
|
+
...(limit.value === undefined ? {} : { limit: limit.value }),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
if (command === 'task-grant-get') {
|
|
184
|
+
const grantId = requiredFlag(parsed.flags, 'grant-id');
|
|
185
|
+
return grantId.ok ? jsonResult(grantService.get(grantId.value)) : resultFailure(grantId);
|
|
186
|
+
}
|
|
187
|
+
if (command === 'task-grant-revoke') {
|
|
188
|
+
const grantId = requiredFlag(parsed.flags, 'grant-id');
|
|
189
|
+
const actorId = requiredFlag(parsed.flags, 'actor-id');
|
|
190
|
+
const reason = requiredFlag(parsed.flags, 'reason');
|
|
191
|
+
const actorType = optionalStringFlag(parsed.flags, 'actor-type') ?? 'human';
|
|
192
|
+
if (!grantId.ok)
|
|
193
|
+
return resultFailure(grantId);
|
|
194
|
+
if (!actorId.ok)
|
|
195
|
+
return resultFailure(actorId);
|
|
196
|
+
if (!reason.ok)
|
|
197
|
+
return resultFailure(reason);
|
|
198
|
+
if (actorType !== 'human' && actorType !== 'system')
|
|
199
|
+
return resultFailure({ ok: false, error: { code: 'validation_failed', message: '--actor-type must be human or system' } });
|
|
200
|
+
const actorName = optionalStringFlag(parsed.flags, 'actor-name');
|
|
201
|
+
return jsonResult(grantService.revoke({
|
|
202
|
+
grantId: grantId.value,
|
|
203
|
+
actor: { type: actorType, id: actorId.value, ...(actorName === undefined ? {} : { displayName: actorName }) },
|
|
204
|
+
reason: reason.value,
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
if (command === 'task-grant-preview') {
|
|
208
|
+
const runId = requiredFlag(parsed.flags, 'run-id');
|
|
209
|
+
const agentId = requiredFlag(parsed.flags, 'agent-id');
|
|
210
|
+
const taskFingerprint = requiredFlag(parsed.flags, 'task-fingerprint');
|
|
211
|
+
const category = taskGrantCategoryFlag(parsed.flags, 'category');
|
|
212
|
+
const commandId = requiredFlag(parsed.flags, 'command-id');
|
|
213
|
+
const workspaceRoot = requiredFlag(parsed.flags, 'workspace-root');
|
|
214
|
+
const cwd = requiredFlag(parsed.flags, 'cwd');
|
|
215
|
+
if (!runId.ok)
|
|
216
|
+
return resultFailure(runId);
|
|
217
|
+
if (!agentId.ok)
|
|
218
|
+
return resultFailure(agentId);
|
|
219
|
+
if (!taskFingerprint.ok)
|
|
220
|
+
return resultFailure(taskFingerprint);
|
|
221
|
+
if (!category.ok)
|
|
222
|
+
return resultFailure(category);
|
|
223
|
+
if (!commandId.ok)
|
|
224
|
+
return resultFailure(commandId);
|
|
225
|
+
if (!workspaceRoot.ok)
|
|
226
|
+
return resultFailure(workspaceRoot);
|
|
227
|
+
if (!cwd.ok)
|
|
228
|
+
return resultFailure(cwd);
|
|
229
|
+
const operation = optionalStringFlag(parsed.flags, 'operation') ?? `command-allowlist:${commandId.value}`;
|
|
230
|
+
const targetPaths = csvFlag(parsed.flags, 'target-paths');
|
|
231
|
+
const preview = grantService.previewMatch({
|
|
232
|
+
runId: runId.value,
|
|
233
|
+
agentId: agentId.value,
|
|
234
|
+
taskFingerprint: taskFingerprint.value,
|
|
235
|
+
category: category.value,
|
|
236
|
+
commandId: commandId.value,
|
|
237
|
+
operation,
|
|
238
|
+
workspaceRoot: workspaceRoot.value,
|
|
239
|
+
cwd: cwd.value,
|
|
240
|
+
...(targetPaths.length === 0 ? {} : { targetPaths }),
|
|
241
|
+
});
|
|
242
|
+
return preview.ok ? jsonResult({ ok: true, value: grantEvaluationPayload(preview.value) }) : resultFailure(preview);
|
|
243
|
+
}
|
|
244
|
+
return usageFailure(`Unknown runs command: ${command}`);
|
|
245
|
+
}
|
|
97
246
|
export function runRunsCommand(command, parsed, database, environment) {
|
|
98
247
|
const service = new RunService(database);
|
|
248
|
+
if (command.startsWith('task-grant-'))
|
|
249
|
+
return runTaskGrantCommand(command, parsed, service);
|
|
99
250
|
if (command === 'timeline' || command === 'debug' || command === 'resume-plan' || command === 'resume-inspect')
|
|
100
251
|
return runRunInsightCommand(command, parsed, service);
|
|
101
252
|
if (command === 'create') {
|
package/dist/cli/dispatcher.js
CHANGED
|
@@ -164,7 +164,12 @@ function validateCommand(area, command) {
|
|
|
164
164
|
command === 'resume-gate' ||
|
|
165
165
|
command === 'approve' ||
|
|
166
166
|
command === 'reject' ||
|
|
167
|
-
command === 'cancel-approval'
|
|
167
|
+
command === 'cancel-approval' ||
|
|
168
|
+
command === 'task-grant-create' ||
|
|
169
|
+
command === 'task-grant-list' ||
|
|
170
|
+
command === 'task-grant-get' ||
|
|
171
|
+
command === 'task-grant-revoke' ||
|
|
172
|
+
command === 'task-grant-preview'
|
|
168
173
|
? { ok: true }
|
|
169
174
|
: { ok: false, message: `Unknown runs command: ${command}` };
|
|
170
175
|
}
|
|
@@ -157,6 +157,22 @@ export function callVgxTool(call, services) {
|
|
|
157
157
|
return toEnvelope(validated.tool, services.runs.getRun(validated.input.id));
|
|
158
158
|
case 'vgxness_run_preflight':
|
|
159
159
|
return toEnvelope(validated.tool, services.runs.preflightOperation(validated.input));
|
|
160
|
+
case 'vgxness_run_task_grant_create':
|
|
161
|
+
return createTaskGrantEnvelope(validated.input, services);
|
|
162
|
+
case 'vgxness_run_task_grant_list':
|
|
163
|
+
return listTaskGrantsEnvelope(validated.input, services);
|
|
164
|
+
case 'vgxness_run_task_grant_get':
|
|
165
|
+
if (services.runs.taskScopedCommandGrantService === undefined)
|
|
166
|
+
return errorEnvelope('validation_failed', 'Task-scoped command grant service is not available', validated.tool);
|
|
167
|
+
return toEnvelope(validated.tool, services.runs.taskScopedCommandGrantService().get(validated.input.grantId));
|
|
168
|
+
case 'vgxness_run_task_grant_revoke':
|
|
169
|
+
if (services.runs.taskScopedCommandGrantService === undefined)
|
|
170
|
+
return errorEnvelope('validation_failed', 'Task-scoped command grant service is not available', validated.tool);
|
|
171
|
+
return toEnvelope(validated.tool, services.runs.taskScopedCommandGrantService().revoke(validated.input));
|
|
172
|
+
case 'vgxness_run_task_grant_preview_match':
|
|
173
|
+
if (services.runs.taskScopedCommandGrantService === undefined)
|
|
174
|
+
return errorEnvelope('validation_failed', 'Task-scoped command grant service is not available', validated.tool);
|
|
175
|
+
return toEnvelope(validated.tool, services.runs.taskScopedCommandGrantService().previewMatch(validated.input));
|
|
160
176
|
case 'vgxness_run_start':
|
|
161
177
|
return toEnvelope(validated.tool, services.runs.createRun(validated.input));
|
|
162
178
|
case 'vgxness_run_checkpoint':
|
|
@@ -564,6 +580,24 @@ function listRunsEnvelope(input, services) {
|
|
|
564
580
|
? successEnvelope('vgxness_run_list', result.value.slice(0, limit))
|
|
565
581
|
: errorEnvelope(result.error.code, result.error.message, 'vgxness_run_list');
|
|
566
582
|
}
|
|
583
|
+
function createTaskGrantEnvelope(input, services) {
|
|
584
|
+
if (services.runs.taskScopedCommandGrantService === undefined)
|
|
585
|
+
return errorEnvelope('validation_failed', 'Task-scoped command grant service is not available', 'vgxness_run_task_grant_create');
|
|
586
|
+
const { creationReason, approvedScopeSummary, ...grantInput } = input;
|
|
587
|
+
return toEnvelope('vgxness_run_task_grant_create', services.runs.taskScopedCommandGrantService().create({
|
|
588
|
+
...grantInput,
|
|
589
|
+
audit: { creationReason, approvedScopeSummary },
|
|
590
|
+
}));
|
|
591
|
+
}
|
|
592
|
+
function listTaskGrantsEnvelope(input, services) {
|
|
593
|
+
if (services.runs.taskScopedCommandGrantService === undefined)
|
|
594
|
+
return errorEnvelope('validation_failed', 'Task-scoped command grant service is not available', 'vgxness_run_task_grant_list');
|
|
595
|
+
const { limit, ...filters } = input;
|
|
596
|
+
const result = services.runs.taskScopedCommandGrantService().list(filters);
|
|
597
|
+
return result.ok
|
|
598
|
+
? successEnvelope('vgxness_run_task_grant_list', limit === undefined ? result.value : result.value.slice(0, limit))
|
|
599
|
+
: errorEnvelope(result.error.code, result.error.message, 'vgxness_run_task_grant_list');
|
|
600
|
+
}
|
|
567
601
|
function runResumeCandidatesEnvelope(input, services) {
|
|
568
602
|
const limit = input.limit ?? 5;
|
|
569
603
|
const result = services.runs.listRecentInterruptedRuns({ project: input.project, limit });
|
package/dist/mcp/schema.js
CHANGED
|
@@ -50,6 +50,11 @@ export const SUPPORTED_VGX_MCP_TOOL_NAMES = [
|
|
|
50
50
|
'vgxness_run_list',
|
|
51
51
|
'vgxness_run_get',
|
|
52
52
|
'vgxness_run_preflight',
|
|
53
|
+
'vgxness_run_task_grant_create',
|
|
54
|
+
'vgxness_run_task_grant_list',
|
|
55
|
+
'vgxness_run_task_grant_get',
|
|
56
|
+
'vgxness_run_task_grant_revoke',
|
|
57
|
+
'vgxness_run_task_grant_preview_match',
|
|
53
58
|
'vgxness_run_start',
|
|
54
59
|
'vgxness_run_checkpoint',
|
|
55
60
|
'vgxness_run_finalize',
|
|
@@ -108,6 +113,11 @@ export const EXPOSED_VGX_MCP_TOOL_NAMES = [
|
|
|
108
113
|
'run_list',
|
|
109
114
|
'run_get',
|
|
110
115
|
'run_preflight',
|
|
116
|
+
'run_task_grant_create',
|
|
117
|
+
'run_task_grant_list',
|
|
118
|
+
'run_task_grant_get',
|
|
119
|
+
'run_task_grant_revoke',
|
|
120
|
+
'run_task_grant_preview_match',
|
|
111
121
|
'run_start',
|
|
112
122
|
'run_checkpoint',
|
|
113
123
|
'run_finalize',
|
|
@@ -132,6 +142,8 @@ const agentModes = ['agent', 'subagent'];
|
|
|
132
142
|
const memoryTypes = ['architecture', 'decision', 'bugfix', 'pattern', 'config', 'discovery', 'learning', 'preference', 'manual'];
|
|
133
143
|
const activityKinds = ['prompt', 'tool_call', 'artifact', 'summary', 'error'];
|
|
134
144
|
const runStatuses = ['created', 'planned', 'running', 'needs-human', 'completed', 'failed', 'blocked', 'cancelled'];
|
|
145
|
+
const taskGrantStatuses = ['active', 'revoked', 'expired', 'exhausted'];
|
|
146
|
+
const taskGrantCategories = ['shell', 'test-run', 'git'];
|
|
135
147
|
const finalRunStatuses = ['completed', 'failed', 'blocked', 'cancelled'];
|
|
136
148
|
const runOutcomes = ['success', 'partial', 'failure', 'blocked', 'cancelled'];
|
|
137
149
|
const payloadModes = ['compact', 'verbose'];
|
|
@@ -163,6 +175,26 @@ const providerChangePlanTypes = ['opencode-mcp-install', 'claude-mcp-install', '
|
|
|
163
175
|
const sddPhaseInputSchema = z.union([z.enum(sddPhases), z.literal('apply')]);
|
|
164
176
|
const sddPhaseSchema = z.enum(sddPhases);
|
|
165
177
|
const jsonValueSchema = z.lazy(() => z.union([z.string(), z.number().finite(), z.boolean(), z.null(), z.array(jsonValueSchema), z.record(z.string(), jsonValueSchema)]));
|
|
178
|
+
const taskGrantHumanActorInputSchema = z
|
|
179
|
+
.object({
|
|
180
|
+
type: z.literal('human'),
|
|
181
|
+
id: z.string().min(1),
|
|
182
|
+
displayName: z.string().min(1).optional(),
|
|
183
|
+
})
|
|
184
|
+
.strict();
|
|
185
|
+
const taskGrantRevocationActorInputSchema = z
|
|
186
|
+
.object({
|
|
187
|
+
type: z.enum(['human', 'system']),
|
|
188
|
+
id: z.string().min(1),
|
|
189
|
+
displayName: z.string().min(1).optional(),
|
|
190
|
+
})
|
|
191
|
+
.strict();
|
|
192
|
+
const taskGrantCommandPolicyInputSchema = z
|
|
193
|
+
.object({
|
|
194
|
+
kind: z.literal('allowlisted-command-ids'),
|
|
195
|
+
ids: z.array(z.string().min(1)).min(1),
|
|
196
|
+
})
|
|
197
|
+
.strict();
|
|
166
198
|
const providerEvidenceOutputSchema = z
|
|
167
199
|
.object({
|
|
168
200
|
staticManifestKnown: z.boolean(),
|
|
@@ -810,6 +842,10 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
810
842
|
agentId: z.string().min(1).optional(),
|
|
811
843
|
workspaceRoot: z.string().min(1).optional(),
|
|
812
844
|
targetPath: z.string().min(1).optional(),
|
|
845
|
+
taskFingerprint: z.string().min(1).optional(),
|
|
846
|
+
commandId: z.string().min(1).optional(),
|
|
847
|
+
cwd: z.string().min(1).optional(),
|
|
848
|
+
targetPaths: z.array(z.string().min(1)).optional(),
|
|
813
849
|
providerToolName: z.string().min(1).optional(),
|
|
814
850
|
agent: z
|
|
815
851
|
.object({
|
|
@@ -826,6 +862,54 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
|
|
|
826
862
|
ambiguous: z.boolean().optional(),
|
|
827
863
|
})
|
|
828
864
|
.passthrough(),
|
|
865
|
+
vgxness_run_task_grant_create: z
|
|
866
|
+
.object({
|
|
867
|
+
runId: z.string().min(1),
|
|
868
|
+
agentId: z.string().min(1),
|
|
869
|
+
taskFingerprint: z.string().min(1),
|
|
870
|
+
categories: z.array(z.enum(taskGrantCategories)).min(1),
|
|
871
|
+
externalRoots: z.array(z.string().min(1)).min(1),
|
|
872
|
+
cwdPatterns: z.array(z.string().min(1)).optional(),
|
|
873
|
+
commandPolicy: taskGrantCommandPolicyInputSchema,
|
|
874
|
+
expiresAt: z.string().min(1),
|
|
875
|
+
maxUses: z.number().int().positive(),
|
|
876
|
+
createdBy: taskGrantHumanActorInputSchema,
|
|
877
|
+
creationReason: z.string().min(1),
|
|
878
|
+
approvedScopeSummary: z.string().min(1),
|
|
879
|
+
})
|
|
880
|
+
.strict(),
|
|
881
|
+
vgxness_run_task_grant_list: z
|
|
882
|
+
.object({
|
|
883
|
+
runId: z.string().min(1).optional(),
|
|
884
|
+
agentId: z.string().min(1).optional(),
|
|
885
|
+
status: z.enum(taskGrantStatuses).optional(),
|
|
886
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
887
|
+
})
|
|
888
|
+
.strict(),
|
|
889
|
+
vgxness_run_task_grant_get: z
|
|
890
|
+
.object({
|
|
891
|
+
grantId: z.string().min(1),
|
|
892
|
+
})
|
|
893
|
+
.strict(),
|
|
894
|
+
vgxness_run_task_grant_revoke: z
|
|
895
|
+
.object({
|
|
896
|
+
grantId: z.string().min(1),
|
|
897
|
+
actor: taskGrantRevocationActorInputSchema,
|
|
898
|
+
reason: z.string().min(1),
|
|
899
|
+
})
|
|
900
|
+
.strict(),
|
|
901
|
+
vgxness_run_task_grant_preview_match: z
|
|
902
|
+
.object({
|
|
903
|
+
runId: z.string().min(1),
|
|
904
|
+
agentId: z.string().min(1).optional(),
|
|
905
|
+
taskFingerprint: z.string().min(1),
|
|
906
|
+
category: z.enum(permissionCategories),
|
|
907
|
+
operation: z.string().min(1),
|
|
908
|
+
workspaceRoot: z.string().min(1).optional(),
|
|
909
|
+
cwd: z.string().min(1),
|
|
910
|
+
targetPaths: z.array(z.string().min(1)).optional(),
|
|
911
|
+
})
|
|
912
|
+
.strict(),
|
|
829
913
|
vgxness_run_start: z
|
|
830
914
|
.object({
|
|
831
915
|
project: z.string().min(1),
|
package/dist/mcp/stdio-server.js
CHANGED
|
@@ -93,6 +93,12 @@ function descriptionForTool(publicToolName) {
|
|
|
93
93
|
return 'Human-only mutating SDD reopen gate; records an explicit human decision to reopen an artifact and must not be called by agents on their own behalf. Use only after human instruction, then revise or re-review the phase.';
|
|
94
94
|
if (publicToolName === 'run_preflight')
|
|
95
95
|
return 'Agent-callable advisory/planning-only run preflight; evaluates permissions, records a plan, and returns a workspace strategy recommendation, but does not execute the checked shell/git/install/network/provider/secrets operation and does not create branches, worktrees, or pull requests. Use it to decide whether human approval or a separate executor step is required.';
|
|
96
|
+
if (publicToolName === 'run_task_grant_create')
|
|
97
|
+
return 'Human-approved lifecycle tool that creates a task-scoped command grant for bounded allowlisted external command operations.';
|
|
98
|
+
if (publicToolName === 'run_task_grant_list' || publicToolName === 'run_task_grant_get' || publicToolName === 'run_task_grant_preview_match')
|
|
99
|
+
return 'Read-only task-scoped command grant lifecycle tool; does not execute commands, write provider config, or consume grant usage.';
|
|
100
|
+
if (publicToolName === 'run_task_grant_revoke')
|
|
101
|
+
return 'Revokes a task-scoped command grant with an explicit human or system actor and reason.';
|
|
96
102
|
const toolName = toInternalVgxMcpToolName(publicToolName);
|
|
97
103
|
return `VGX control-plane tool ${toolName}`;
|
|
98
104
|
}
|