vgxness 1.15.0 → 1.17.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/README.md +2 -2
- package/dist/agents/canonical-agent-manifest.js +34 -25
- package/dist/cli/cli-help.js +1 -0
- package/dist/cli/home-tui-app.js +115 -15
- package/dist/cli/home-tui-controller.js +100 -3
- package/dist/cli/sdd-renderer.js +7 -0
- package/dist/mcp/control-plane.js +10 -1
- package/dist/mcp/schema.js +17 -0
- package/dist/mcp/stdio-server.js +1 -1
- package/dist/runs/execution-planning.js +67 -0
- package/dist/runs/run-service.js +244 -7
- package/dist/runs/sandbox-process-execution.js +7 -1
- package/dist/sdd/cockpit-read-model.js +6 -5
- package/dist/sdd/sdd-continuation-plan.js +46 -0
- package/dist/workflows/command-allowlist-adapter.js +100 -0
- package/dist/workflows/workflow-executor.js +7 -1
- package/dist/workspace-strategy/diagnostics.js +34 -0
- package/dist/workspace-strategy/index.js +3 -0
- package/dist/workspace-strategy/recommendation-policy.js +196 -0
- package/dist/workspace-strategy/schema.js +1 -0
- package/docs/architecture.md +14 -3
- package/docs/cli.md +21 -1
- package/docs/glossary.md +6 -2
- package/docs/mcp.md +3 -1
- package/docs/project-health-audit-v1.10.x.md +3 -3
- package/docs/project-health-audit-v1.14.x.md +2 -0
- package/docs/project-health-audit-v1.9.1.md +1 -1
- package/docs/providers.md +1 -1
- package/docs/roadmap.md +3 -3
- package/package.json +1 -1
- package/seeds/skills/skill-seed-v1.json +4 -4
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ This package is proprietary software. The npm package ships inspectable JavaScri
|
|
|
8
8
|
|
|
9
9
|
OpenCode is the primary supported provider. Claude setup support is secondary. VGX-managed OpenCode and Claude provider configuration is user-global only; provider config writes require explicit CLI confirmation.
|
|
10
10
|
|
|
11
|
-
VGXNESS
|
|
11
|
+
VGXNESS is currently versioned from `package.json` (`1.16.0`). The latest full project health audit is the historical [v1.14.x snapshot](./docs/project-health-audit-v1.14.x.md), which documents the implemented CLI/MCP/SDD control plane, OpenCode-first workflow, release-readiness checks, and the remaining execution/recovery gaps at that point in time. [v1.10.x](./docs/project-health-audit-v1.10.x.md) and [v1.9.1](./docs/project-health-audit-v1.9.1.md) remain historical validation evidence for those releases.
|
|
12
12
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
@@ -256,7 +256,7 @@ Remove any user-global OpenCode/Claude config entries and local/global VGXNESS d
|
|
|
256
256
|
## More docs
|
|
257
257
|
|
|
258
258
|
- [CLI reference](./docs/cli.md)
|
|
259
|
-
- [Project health audit v1.14.x](./docs/project-health-audit-v1.14.x.md)
|
|
259
|
+
- [Project health audit v1.14.x](./docs/project-health-audit-v1.14.x.md) — historical snapshot
|
|
260
260
|
- [Project health audit v1.10.x](./docs/project-health-audit-v1.10.x.md)
|
|
261
261
|
- [Project health audit v1.9.1](./docs/project-health-audit-v1.9.1.md)
|
|
262
262
|
- [Architecture](./docs/architecture.md)
|
|
@@ -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 = 12;
|
|
6
6
|
export const canonicalSddSubagentNames = [
|
|
7
7
|
'vgxness-sdd-explore',
|
|
8
8
|
'vgxness-sdd-propose',
|
|
@@ -131,8 +131,15 @@ export function createCanonicalOpenCodeSddMcpToolPermissions() {
|
|
|
131
131
|
vgxness_sdd_get_artifact: 'allow',
|
|
132
132
|
vgxness_sdd_list_artifacts: 'allow',
|
|
133
133
|
vgxness_sdd_cockpit: 'allow',
|
|
134
|
+
vgxness_context_cockpit: 'allow',
|
|
134
135
|
vgxness_agent_resolve: 'allow',
|
|
135
136
|
vgxness_sdd_save_artifact: 'allow',
|
|
137
|
+
vgxness_memory_search: 'allow',
|
|
138
|
+
vgxness_memory_get: 'allow',
|
|
139
|
+
vgxness_memory_save: 'allow',
|
|
140
|
+
vgxness_memory_update: 'allow',
|
|
141
|
+
vgxness_skill_payload: 'allow',
|
|
142
|
+
vgxness_run_preflight: 'allow',
|
|
136
143
|
};
|
|
137
144
|
}
|
|
138
145
|
export function createCanonicalOpenCodeSddSubagentPrompt(name) {
|
|
@@ -146,50 +153,52 @@ export function createCanonicalOpenCodeSddSubagentPrompt(name) {
|
|
|
146
153
|
* This feeds the OpenCode default config and Claude generated agent files; it is
|
|
147
154
|
* intentionally separate from registry/seed instructions below.
|
|
148
155
|
*/
|
|
149
|
-
export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager
|
|
156
|
+
export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager
|
|
150
157
|
|
|
151
|
-
Bind
|
|
158
|
+
Bind to \`vgxness-manager\` only. Executors use their own prompts.
|
|
152
159
|
|
|
153
160
|
## Role
|
|
154
|
-
Coordinate SDD; keep chat thin, use VGXNESS MCP
|
|
161
|
+
Coordinate SDD; keep chat thin, use VGXNESS 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.
|
|
155
162
|
|
|
156
163
|
## Non-negotiable governance
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
- Before
|
|
160
|
-
-
|
|
164
|
+
- VGXNESS uses SQLite artifacts/control-plane; OpenCode as primary provider; artifacts are not openspec files. Do not write to \`openspec/\`.
|
|
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
|
+
- Before phase advancement, call \`vgxness_sdd_status\`/\`vgxness_sdd_next\` plus \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
|
|
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 of an exact SDD artifact via \`vgxness_sdd_accept_artifact\` is not a generic SDD write for manager routing: do not call \`vgxness_run_preflight\` solely for that acceptance; require the user explicitly accepted the exact project/change/phase artifact, \`acceptedBy.type\` is \`"human"\`, \`acceptedBy.id\` is non-empty, and status/readiness confirms the artifact is eligible. This shortcut applies only to \`vgxness_sdd_accept_artifact\` and the trusted draft autorun chain described below; excludes edits, shell/tests, git, provider config, memory writes, external paths, secrets, destructive/privileged/ambiguous operations.
|
|
161
169
|
- OpenCode native/provider tools are governance-v1 audit-only/non-hard-blocking. Report warnings; do not say native OpenCode tools are hard-blocked by config.
|
|
162
|
-
- Do not mutate provider/global OpenCode config. Do not
|
|
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.
|
|
163
171
|
- Do not change provider model/reasoning config unless explicitly requested.
|
|
164
172
|
|
|
165
173
|
## Flexible governance routing
|
|
166
|
-
Use
|
|
174
|
+
Use lightest safe path: T0-2 direct, T3 preflight, T4 formal SDD for governance, permissions, acceptance, architecture/security, or cross-surface behavior. Provider status/doctor/preview/handoff read-only/audit-only; writes gated.
|
|
167
175
|
|
|
168
176
|
## Provider-native daily flow
|
|
169
|
-
SDD happens in OpenCode via conversation, VGXNESS MCP, and SDD subagents. No terminal SDD phase commands
|
|
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.
|
|
170
178
|
|
|
171
179
|
## MCP playbook
|
|
172
|
-
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\`; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
+
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\`; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with session id and actor \`manager\`; if no id, do not invent one and summarize.
|
|
181
|
+
- Proposal clarity: if product/business clarity is missing, run a proposal question round.
|
|
182
|
+
- 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 exactly \`spec -> design -> tasks\` without extra human confirmation via exact subagents; save drafts with \`vgxness_sdd_save_artifact\`. This is draft-only planning, not acceptance or completion. Not for explore/proposal/apply-progress/verify/archive, rejected/superseded artifacts, accepted overwrites, or risky side effects (provider config, edits, shell/tests, git, secrets, external/destructive/privileged/ambiguous). Re-check status/readiness before/after; generated spec/design drafts can feed design/tasks but remain unaccepted.
|
|
184
|
+
- Acceptance/readiness: check SDD status/readiness for the exact project/change/phase; confirm explicit human acceptance; call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId); re-check status/readiness before reporting state. Do not add \`vgxness_run_preflight\` solely for that exact direct acceptance call. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, audit warnings.
|
|
185
|
+
- Memory: \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work/unclear context; \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
|
|
186
|
+
- Agents/skills: Skill registry index-first; resolve skills by registry, exact path, and task context; read exact paths; do not invent summaries. Resolve phase with \`vgxness_agent_resolve\`. Preview mode does not execute a provider or write provider config for \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, or \`vgxness_skill_payload\`. \`vgxness_manager_profile_get\` before changes; \`vgxness_manager_profile_set\` needs explicit human authorization.
|
|
187
|
+
- 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
|
+
- Provider diagnostics: \`vgxness_provider_status\`/\`vgxness_provider_doctor\` read-only; report \`providerEvidence.evidenceLevel\` and \`hostToolPresenceVerified\` limits.
|
|
180
189
|
|
|
181
190
|
## Minimum flows
|
|
182
|
-
- Simple answer: inline;
|
|
183
|
-
- Proposal/spec/design/tasks: status/next -> readiness ->
|
|
184
|
-
- Apply/verify:
|
|
185
|
-
- Config/provider/prompt change: inspect manager/profile
|
|
191
|
+
- Simple answer: inline; no run by default.
|
|
192
|
+
- 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.
|
|
193
|
+
- 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.
|
|
194
|
+
- Config/provider/prompt change: inspect manager/profile/payload first; persistence needs explicit human authorization.
|
|
186
195
|
- 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.
|
|
187
196
|
|
|
188
197
|
## Delegation thresholds
|
|
189
|
-
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. Never delegate to unknown agents; use exact
|
|
198
|
+
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. Never delegate to unknown agents; use exact phase mapping.
|
|
190
199
|
|
|
191
200
|
## Output
|
|
192
|
-
Be concise
|
|
201
|
+
Be concise.`;
|
|
193
202
|
/**
|
|
194
203
|
* Registry/seed instructions for the manager agent definition.
|
|
195
204
|
* These feed the canonical manifest, checked-in seed, boot upgrade, registry
|
package/dist/cli/cli-help.js
CHANGED
|
@@ -144,6 +144,7 @@ Areas:
|
|
|
144
144
|
runs finish --run-id <id> --status completed|failed|blocked|cancelled --outcome <outcome> [--reason <text>]
|
|
145
145
|
runs permission-check --run-id <id> --category <category> --operation <name> [--path <path>] [--agent-id <id>] [--destructive] [--external] [--privileged] [--ambiguous]
|
|
146
146
|
runs preflight --run-id <id> --category <category> --operation <name> [--workspace <path>] [--path <path>] [--provider-tool <name>] [--agent-id <id>] [--destructive] [--external] [--privileged] [--ambiguous]
|
|
147
|
+
runs preflight output includes workspaceStrategy recommendation JSON and does not create branches, worktrees, or PRs.
|
|
147
148
|
runs approvals --run <run-id>
|
|
148
149
|
runs approve|reject|cancel-approval --approval <id> --actor <name> [--reason <text>]
|
|
149
150
|
runs retry-check --approval <id> [--policy <json>]
|
package/dist/cli/home-tui-app.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box } from 'ink';
|
|
3
|
+
import { renderSddAdviceLines } from './sdd-renderer.js';
|
|
3
4
|
import { TuiCard, TuiFooter, TuiHeader, TuiSummaryBar, TuiTabs } from './tui/ink/components.js';
|
|
4
5
|
import { inkTuiTheme, normalizeInkTuiWidth } from './tui/ink/theme.js';
|
|
5
6
|
export const homeTuiTabs = ['setup', 'status', 'runs', 'sdd', 'skills', 'provider'];
|
|
6
7
|
const homeTuiTabItems = homeTuiTabs.map((tab) => ({ key: tab, label: labelForHomeTab(tab) }));
|
|
7
|
-
export function HomeTuiApp({ plan, width, selectedTab, view = 'overview', runsReadModel = { state: 'not-loaded' }, skillsReadModel = { state: 'not-loaded' }, sddInput = { change: '' } }) {
|
|
8
|
+
export function HomeTuiApp({ plan, width, selectedTab, view = 'overview', runsReadModel = { state: 'not-loaded' }, skillsReadModel = { state: 'not-loaded' }, sddInput = { change: '' }, sddReadModel = { state: 'not-loaded' } }) {
|
|
8
9
|
const cardWidth = normalizeInkTuiWidth(width);
|
|
9
10
|
if (view === 'status-focus')
|
|
10
11
|
return _jsx(FocusedStatusView, { plan: plan, width: cardWidth });
|
|
11
12
|
if (view === 'runs-focus')
|
|
12
13
|
return _jsx(FocusedRunsView, { plan: plan, width: cardWidth, runs: runsReadModel });
|
|
13
14
|
if (view === 'sdd-focus')
|
|
14
|
-
return _jsx(FocusedSddView, { plan: plan, width: cardWidth, sddInput: sddInput });
|
|
15
|
+
return _jsx(FocusedSddView, { plan: plan, width: cardWidth, sddInput: sddInput, sdd: sddReadModel });
|
|
15
16
|
if (view === 'skills-focus')
|
|
16
17
|
return _jsx(FocusedSkillsView, { plan: plan, width: cardWidth, skills: skillsReadModel });
|
|
17
18
|
if (view === 'provider-focus')
|
|
@@ -26,10 +27,13 @@ function FocusedSkillsView({ plan, width, skills }) {
|
|
|
26
27
|
function FocusedRunsView({ plan, width, runs }) {
|
|
27
28
|
const badge = runs.state === 'ready' ? 'read-only' : runs.state === 'unavailable' ? 'unavailable' : 'loading';
|
|
28
29
|
const tone = runs.state === 'unavailable' ? 'warning' : 'info';
|
|
29
|
-
|
|
30
|
+
const title = runs.state === 'ready' && runs.selected !== undefined ? 'Run detail' : 'Runs';
|
|
31
|
+
return (_jsxs(Box, { flexDirection: "column", width: width, paddingX: 1, paddingY: 1, children: [_jsx(TuiHeader, { title: "VGXNESS Runs", subtitle: "Run recovery cockpit \u00B7 read-only", project: plan.project }), _jsx(TuiSummaryBar, { items: summaryItems(plan) }), _jsx(Box, { marginTop: 1, children: _jsx(TuiCard, { title: title, badge: badge, tone: tone, lines: focusedRunsLines(plan, runs) }) }), _jsx(TuiFooter, { text: "Keys: Enter inspects first run detail \u00B7 Esc/b back to Home \u00B7 q exits \u00B7 read-only \u00B7 no writes \u00B7 no retry/approval/execution" })] }));
|
|
30
32
|
}
|
|
31
|
-
function FocusedSddView({ plan, width, sddInput }) {
|
|
32
|
-
|
|
33
|
+
function FocusedSddView({ plan, width, sddInput, sdd }) {
|
|
34
|
+
const badge = sddInput.change.length === 0 ? 'change required' : sdd.state === 'ready' ? sdd.next.status : sdd.state === 'unavailable' ? 'unavailable' : 'change selected';
|
|
35
|
+
const tone = sdd.state === 'unavailable' ? 'warning' : 'info';
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", width: width, paddingX: 1, paddingY: 1, children: [_jsx(TuiHeader, { title: "VGXNESS SDD", subtitle: "Spec-driven development cockpit \u00B7 change required \u00B7 read-only", project: plan.project }), _jsx(TuiSummaryBar, { items: summaryItems(plan) }), _jsx(Box, { marginTop: 1, children: _jsx(TuiCard, { title: "SDD change gate", badge: badge, tone: tone, lines: focusedSddLines(plan, sddInput, sdd) }) }), _jsx(TuiFooter, { text: `Keys: type change id · Backspace edits · Esc back · Ctrl-C exits · ${sdd.state === 'ready' ? 'SDD state read-only' : 'local SDD state not opened'}` })] }));
|
|
33
37
|
}
|
|
34
38
|
function FocusedStatusView({ plan, width }) {
|
|
35
39
|
return (_jsxs(Box, { flexDirection: "column", width: width, paddingX: 1, paddingY: 1, children: [_jsx(TuiHeader, { title: "VGXNESS Status", subtitle: "Focused setup readiness \u00B7 read-only", project: plan.project }), _jsx(TuiSummaryBar, { items: summaryItems(plan) }), _jsx(Box, { marginTop: 1, children: _jsx(TuiCard, { title: "Current setup status", badge: plan.status === 'ready' ? 'ready' : statusTitle(plan.status), tone: plan.status === 'ready' ? 'success' : plan.status === 'conflict' ? 'danger' : 'warning', lines: focusedStatusLines(plan) }) }), _jsx(TuiFooter, { text: "Keys: Esc/b back to Home \u00B7 q exits \u00B7 read-only \u00B7 no provider config writes" })] }));
|
|
@@ -103,11 +107,14 @@ function focusedRunsLines(plan, runs) {
|
|
|
103
107
|
'',
|
|
104
108
|
runs.message,
|
|
105
109
|
'',
|
|
106
|
-
'
|
|
110
|
+
'Interrupted runs can be inspected with `vgxness runs list --project <name> --status needs-human` from a Bun-backed runtime.',
|
|
107
111
|
'Use `vgxness runs list --project <name> --status needs-human` for actionable blockers.',
|
|
112
|
+
'Safety: read-only/no writes/no retry/approval/execution.',
|
|
108
113
|
];
|
|
109
114
|
}
|
|
110
115
|
if (runs.state === 'ready') {
|
|
116
|
+
if (runs.selected !== undefined)
|
|
117
|
+
return focusedRunDetailLines(plan, runs.databasePath, runs.selected);
|
|
111
118
|
return [
|
|
112
119
|
field('Project', plan.project),
|
|
113
120
|
field('Store', 'opened read-only'),
|
|
@@ -115,11 +122,13 @@ function focusedRunsLines(plan, runs) {
|
|
|
115
122
|
field('Recent runs', String(runs.recent.length)),
|
|
116
123
|
field('Interrupted', String(runs.interrupted.length)),
|
|
117
124
|
'',
|
|
118
|
-
...runSummarySection('Interrupted runs', runs.interrupted),
|
|
125
|
+
...runSummarySection('Interrupted runs', runs.interrupted, runs.details),
|
|
119
126
|
'',
|
|
120
|
-
...runSummarySection('Recent runs', runs.recent),
|
|
127
|
+
...runSummarySection('Recent runs', runs.recent, runs.details),
|
|
121
128
|
'',
|
|
122
|
-
'
|
|
129
|
+
runs.details.length === 0 ? 'No run is available for detail inspection.' : 'Press Enter to inspect the first interrupted/recent run here.',
|
|
130
|
+
'CLI fallback: `vgxness runs get --id <run-id>`.',
|
|
131
|
+
'Safety: read-only/no writes/no retry/approval/execution.',
|
|
123
132
|
];
|
|
124
133
|
}
|
|
125
134
|
return [
|
|
@@ -128,22 +137,108 @@ function focusedRunsLines(plan, runs) {
|
|
|
128
137
|
field('Safety', 'read-only placeholder'),
|
|
129
138
|
'',
|
|
130
139
|
'This panel does not open the local SQLite store yet.',
|
|
131
|
-
'Use `vgxness
|
|
140
|
+
'Use `vgxness runs list --project <name> --status needs-human` to inspect interrupted runs.',
|
|
132
141
|
'Use `vgxness runs list --project <name> --status needs-human` for actionable blockers.',
|
|
133
142
|
'',
|
|
134
|
-
'
|
|
143
|
+
'This panel is read-only/no writes/no retry/approval/execution.',
|
|
135
144
|
];
|
|
136
145
|
}
|
|
137
|
-
function
|
|
146
|
+
export function renderHomeRunsReadOnlyLines(plan, runs) {
|
|
147
|
+
return focusedRunsLines(plan, runs);
|
|
148
|
+
}
|
|
149
|
+
function focusedRunDetailLines(plan, databasePath, run) {
|
|
150
|
+
return [
|
|
151
|
+
field('Project', plan.project),
|
|
152
|
+
field('Store', 'opened read-only'),
|
|
153
|
+
field('Database', databasePath),
|
|
154
|
+
field('Run', shortRunId(run.id)),
|
|
155
|
+
field('Status', run.status),
|
|
156
|
+
field('Workflow/phase', `${run.workflow}/${run.phase}`),
|
|
157
|
+
field('Events', String(run.events)),
|
|
158
|
+
field('Checkpoints', String(run.checkpoints)),
|
|
159
|
+
field('Approvals', `${run.approvals} (${formatApprovalStatusCounts(run.approvalStatusCounts)})`),
|
|
160
|
+
field('Attempts', `${run.attempts} (${formatAttemptStatusCounts(run.attemptStatusCounts)})`),
|
|
161
|
+
...(run.latestCheckpointLabel === undefined ? [] : [field('Latest checkpoint', run.latestCheckpointLabel)]),
|
|
162
|
+
...(run.latestEventTitle === undefined ? [] : [field('Latest event', run.latestEventTitle)]),
|
|
163
|
+
...(run.outcome === undefined ? [] : [field('Outcome', run.outcome)]),
|
|
164
|
+
...(run.outcomeReason === undefined ? [] : [field('Outcome reason', run.outcomeReason)]),
|
|
165
|
+
...(run.userIntent === undefined ? [] : ['', `Intent: ${run.userIntent}`]),
|
|
166
|
+
'',
|
|
167
|
+
`Inspect JSON: vgxness runs get --id ${run.id}`,
|
|
168
|
+
'Safety: read-only/no writes/no retry/approval/execution from this TUI detail.',
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
function runSummarySection(title, runs, details = []) {
|
|
138
172
|
if (runs.length === 0)
|
|
139
173
|
return [title, '- none'];
|
|
140
|
-
return [title, ...runs.map((run) =>
|
|
174
|
+
return [title, ...runs.map((run) => runSummaryLine(run, details.find((detail) => detail.id === run.id)))];
|
|
175
|
+
}
|
|
176
|
+
function runSummaryLine(run, detail) {
|
|
177
|
+
const base = `- ${shortRunId(run.id)} ${run.status} ${run.workflow}/${run.phase}${run.userIntent === undefined ? '' : ` — ${run.userIntent}`}`;
|
|
178
|
+
if (detail === undefined)
|
|
179
|
+
return base;
|
|
180
|
+
return [
|
|
181
|
+
base,
|
|
182
|
+
`approvals ${detail.approvals} (${formatApprovalStatusCounts(detail.approvalStatusCounts)})`,
|
|
183
|
+
`attempts ${detail.attempts} (${formatAttemptStatusCounts(detail.attemptStatusCounts)})`,
|
|
184
|
+
`latest checkpoint ${detail.latestCheckpointLabel ?? 'none'}`,
|
|
185
|
+
`latest event ${detail.latestEventTitle ?? 'none'}`,
|
|
186
|
+
].join(' · ');
|
|
187
|
+
}
|
|
188
|
+
function formatApprovalStatusCounts(counts) {
|
|
189
|
+
return [`pending=${counts.pending}`, `approved=${counts.approved}`, `rejected=${counts.rejected}`, `cancelled=${counts.cancelled}`].join(' ');
|
|
190
|
+
}
|
|
191
|
+
function formatAttemptStatusCounts(counts) {
|
|
192
|
+
return [`reserved=${counts.reserved}`, `succeeded=${counts.succeeded}`, `failed=${counts.failed}`, `abandoned=${counts.abandoned}`].join(' ');
|
|
141
193
|
}
|
|
142
194
|
function shortRunId(id) {
|
|
143
195
|
return id.length <= 8 ? id : id.slice(0, 8);
|
|
144
196
|
}
|
|
145
|
-
function focusedSddLines(plan, input) {
|
|
197
|
+
function focusedSddLines(plan, input, sdd) {
|
|
146
198
|
const change = input.change.length === 0 ? '<id>' : input.change;
|
|
199
|
+
if (input.change.length > 0 && sdd.state === 'unavailable') {
|
|
200
|
+
return [
|
|
201
|
+
field('Project', plan.project),
|
|
202
|
+
field('Change', input.change),
|
|
203
|
+
field('Input', `${input.change}_`),
|
|
204
|
+
field('Store', 'unavailable'),
|
|
205
|
+
field('Database', sdd.databasePath),
|
|
206
|
+
field('Acceptance', 'human-only'),
|
|
207
|
+
field('Safety', 'read-only'),
|
|
208
|
+
'',
|
|
209
|
+
sdd.message,
|
|
210
|
+
'',
|
|
211
|
+
`Continue: vgxness sdd continue --project ${plan.project} --change ${change}`,
|
|
212
|
+
`Status: vgxness sdd status --project ${plan.project} --change ${change}`,
|
|
213
|
+
'Human acceptance remains explicit; artifact presence is not acceptance.',
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
if (input.change.length > 0 && sdd.state === 'ready') {
|
|
217
|
+
return [
|
|
218
|
+
field('Project', plan.project),
|
|
219
|
+
field('Change', input.change),
|
|
220
|
+
field('Input', `${input.change}_`),
|
|
221
|
+
field('Store', 'opened read-only'),
|
|
222
|
+
field('Database', sdd.databasePath),
|
|
223
|
+
field('Status', sdd.next.status),
|
|
224
|
+
field('Next phase', sdd.next.nextPhase ?? 'none'),
|
|
225
|
+
field('Accepted', `${sdd.cockpit.acceptedCount}/${sdd.cockpit.phases.length}`),
|
|
226
|
+
field('Artifacts', String(sdd.cockpit.artifacts.length)),
|
|
227
|
+
field('Blockers', String(sdd.cockpit.aggregateBlockers.length)),
|
|
228
|
+
field('Recommended', sdd.cockpit.recommendedAction),
|
|
229
|
+
field('Acceptance', sdd.cockpit.gates?.requiresHumanAcceptance === true ? 'human review needed' : 'human-only'),
|
|
230
|
+
field('Safety', 'read-only'),
|
|
231
|
+
'',
|
|
232
|
+
sdd.next.reason,
|
|
233
|
+
'',
|
|
234
|
+
...sddBlockerLines(sdd.cockpit.aggregateBlockers),
|
|
235
|
+
...(sdd.continuation.advice.length === 0 ? [] : ['', ...renderSddAdviceLines(sdd.continuation.advice)]),
|
|
236
|
+
'',
|
|
237
|
+
`Inspect: ${sdd.continuation.inspectCommand}`,
|
|
238
|
+
`Continue: vgxness sdd continue --project ${plan.project} --change ${change}`,
|
|
239
|
+
'Human acceptance remains explicit; artifact presence is not acceptance.',
|
|
240
|
+
];
|
|
241
|
+
}
|
|
147
242
|
return [
|
|
148
243
|
field('Project', plan.project),
|
|
149
244
|
field('Change', input.change.length === 0 ? 'not selected' : input.change),
|
|
@@ -163,6 +258,11 @@ function focusedSddLines(plan, input) {
|
|
|
163
258
|
'Next implementation slice can ask for/select a change id before loading SDD state.',
|
|
164
259
|
];
|
|
165
260
|
}
|
|
261
|
+
function sddBlockerLines(blockers) {
|
|
262
|
+
if (blockers.length === 0)
|
|
263
|
+
return ['Blockers', '- none'];
|
|
264
|
+
return ['Blockers', ...blockers.slice(0, 4).map((blocker) => `- ${blocker.phase}: ${blocker.reason}`), ...(blockers.length > 4 ? [`- +${blockers.length - 4} more`] : [])];
|
|
265
|
+
}
|
|
166
266
|
function focusedSkillsLines(plan, skills) {
|
|
167
267
|
if (skills.state === 'unavailable') {
|
|
168
268
|
return [
|
|
@@ -210,7 +310,7 @@ function skillSummarySection(skills) {
|
|
|
210
310
|
return ['Indexed skills', ...skills.slice(0, 8).map((skill) => `- ${skill.name} ${skill.status}${skill.activeVersion === null ? '' : ` @ ${skill.activeVersion}`}`), ...(skills.length > 8 ? [`- +${skills.length - 8} more`] : [])];
|
|
211
311
|
}
|
|
212
312
|
function runsLines() {
|
|
213
|
-
return ['Run dashboard is not loaded in this
|
|
313
|
+
return ['Run dashboard is not loaded in this Home TUI overview.', '', 'Use `vgxness status` or `vgxness runs list --project <name>` for current CLI diagnostics.', 'Focused Runs is read-only/no writes/no retry/approval/execution.'];
|
|
214
314
|
}
|
|
215
315
|
function sddLines() {
|
|
216
316
|
return [
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { render as renderInk } from 'ink';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { MemoryService } from '../memory/memory-service.js';
|
|
4
|
+
import { openMemoryDatabase } from '../memory/sqlite/database.js';
|
|
3
5
|
import { RunService } from '../runs/run-service.js';
|
|
6
|
+
import { SddWorkflowService } from '../sdd/sdd-workflow-service.js';
|
|
7
|
+
import { sddContinuationPlanFrom } from '../sdd/sdd-continuation-plan.js';
|
|
4
8
|
import { SkillRegistryService } from '../skills/skill-registry-service.js';
|
|
5
9
|
import { SkillIndexService } from '../skills/skill-index-service.js';
|
|
6
10
|
import { okText } from './cli-help.js';
|
|
@@ -9,7 +13,6 @@ import { renderSetupPlan } from './setup-plan-renderer.js';
|
|
|
9
13
|
import { runSetupTuiController } from './setup-tui-controller.js';
|
|
10
14
|
import { navigationIntentFromInput } from './tui/keymap.js';
|
|
11
15
|
import { nextItem } from './tui/navigation.js';
|
|
12
|
-
import { openMemoryDatabase } from '../memory/sqlite/database.js';
|
|
13
16
|
const enter = '\r';
|
|
14
17
|
const escape = '\u001B';
|
|
15
18
|
const ctrlC = '\u0003';
|
|
@@ -26,7 +29,8 @@ export async function runHomeTuiController(input) {
|
|
|
26
29
|
let runsReadModel = { state: 'not-loaded' };
|
|
27
30
|
let skillsReadModel = { state: 'not-loaded' };
|
|
28
31
|
let sddInput = { change: '' };
|
|
29
|
-
|
|
32
|
+
let sddReadModel = { state: 'not-loaded' };
|
|
33
|
+
const app = renderInk(React.createElement(HomeTuiApp, { plan: input.plan, width, selectedTab, view, runsReadModel, skillsReadModel, sddInput, sddReadModel }), {
|
|
30
34
|
stdin: stdin,
|
|
31
35
|
stdout: stdout,
|
|
32
36
|
interactive: true,
|
|
@@ -34,7 +38,7 @@ export async function runHomeTuiController(input) {
|
|
|
34
38
|
exitOnCtrlC: false,
|
|
35
39
|
});
|
|
36
40
|
const rerender = () => {
|
|
37
|
-
app.rerender(React.createElement(HomeTuiApp, { plan: input.plan, width, selectedTab, view, runsReadModel, skillsReadModel, sddInput }));
|
|
41
|
+
app.rerender(React.createElement(HomeTuiApp, { plan: input.plan, width, selectedTab, view, runsReadModel, skillsReadModel, sddInput, sddReadModel }));
|
|
38
42
|
};
|
|
39
43
|
stdin.setRawMode?.(true);
|
|
40
44
|
stdin.resume?.();
|
|
@@ -54,12 +58,14 @@ export async function runHomeTuiController(input) {
|
|
|
54
58
|
}
|
|
55
59
|
if (value === backspace || value === ctrlH) {
|
|
56
60
|
sddInput = { change: sddInput.change.slice(0, -1) };
|
|
61
|
+
sddReadModel = readHomeSdd(input.plan, sddInput.change);
|
|
57
62
|
rerender();
|
|
58
63
|
return;
|
|
59
64
|
}
|
|
60
65
|
const nextSddChange = appendSddChangeInput(sddInput.change, value);
|
|
61
66
|
if (nextSddChange !== sddInput.change) {
|
|
62
67
|
sddInput = { change: nextSddChange };
|
|
68
|
+
sddReadModel = readHomeSdd(input.plan, nextSddChange);
|
|
63
69
|
rerender();
|
|
64
70
|
return;
|
|
65
71
|
}
|
|
@@ -77,6 +83,11 @@ export async function runHomeTuiController(input) {
|
|
|
77
83
|
rerender();
|
|
78
84
|
return;
|
|
79
85
|
}
|
|
86
|
+
if (view === 'runs-focus' && (value === enter || value === '\n' || intent === 'select')) {
|
|
87
|
+
runsReadModel = selectFirstHomeRunDetail(runsReadModel);
|
|
88
|
+
rerender();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
80
91
|
if (intent === 'cancel') {
|
|
81
92
|
cleanup();
|
|
82
93
|
resolve('quit');
|
|
@@ -99,6 +110,7 @@ export async function runHomeTuiController(input) {
|
|
|
99
110
|
return;
|
|
100
111
|
}
|
|
101
112
|
if ((value === enter || value === '\n' || intent === 'select') && selectedTab === 'sdd') {
|
|
113
|
+
sddReadModel = readHomeSdd(input.plan, sddInput.change);
|
|
102
114
|
view = 'sdd-focus';
|
|
103
115
|
rerender();
|
|
104
116
|
return;
|
|
@@ -179,12 +191,73 @@ function readHomeRuns(plan) {
|
|
|
179
191
|
phase: run.phase,
|
|
180
192
|
...(run.userIntent === undefined ? {} : { userIntent: run.userIntent }),
|
|
181
193
|
})),
|
|
194
|
+
details: collectRunDetails(service, [...interrupted.value.map((run) => run.runId), ...recent.value.map((run) => run.id)]),
|
|
182
195
|
};
|
|
183
196
|
}
|
|
184
197
|
finally {
|
|
185
198
|
opened.value.close();
|
|
186
199
|
}
|
|
187
200
|
}
|
|
201
|
+
function collectRunDetails(service, runIds) {
|
|
202
|
+
const uniqueRunIds = [...new Set(runIds)];
|
|
203
|
+
const details = [];
|
|
204
|
+
for (const runId of uniqueRunIds) {
|
|
205
|
+
const run = service.getRun(runId);
|
|
206
|
+
if (!run.ok)
|
|
207
|
+
continue;
|
|
208
|
+
details.push(runDetailSummary(run.value));
|
|
209
|
+
}
|
|
210
|
+
return details;
|
|
211
|
+
}
|
|
212
|
+
function runDetailSummary(run) {
|
|
213
|
+
const userIntent = run.userIntent.trim();
|
|
214
|
+
const latestCheckpoint = run.checkpoints.at(-1);
|
|
215
|
+
const latestEvent = run.events.at(-1);
|
|
216
|
+
return {
|
|
217
|
+
id: run.id,
|
|
218
|
+
status: run.status,
|
|
219
|
+
workflow: run.workflow,
|
|
220
|
+
phase: run.phase,
|
|
221
|
+
...(userIntent.length === 0 ? {} : { userIntent }),
|
|
222
|
+
...(run.outcome === undefined ? {} : { outcome: run.outcome }),
|
|
223
|
+
...(run.outcomeReason === undefined ? {} : { outcomeReason: run.outcomeReason }),
|
|
224
|
+
events: run.events.length,
|
|
225
|
+
checkpoints: run.checkpoints.length,
|
|
226
|
+
approvals: run.approvals.length,
|
|
227
|
+
approvalStatusCounts: countApprovalStatuses(run.approvals.map((approval) => approval.status)),
|
|
228
|
+
attempts: run.operationAttempts.length,
|
|
229
|
+
attemptStatusCounts: countAttemptStatuses(run.operationAttempts.map((attempt) => attempt.status)),
|
|
230
|
+
...(latestCheckpoint === undefined ? {} : { latestCheckpointLabel: latestCheckpoint.label }),
|
|
231
|
+
...(latestEvent === undefined ? {} : { latestEventTitle: latestEvent.title }),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function countApprovalStatuses(statuses) {
|
|
235
|
+
return {
|
|
236
|
+
pending: statuses.filter((status) => status === 'pending').length,
|
|
237
|
+
approved: statuses.filter((status) => status === 'approved').length,
|
|
238
|
+
rejected: statuses.filter((status) => status === 'rejected').length,
|
|
239
|
+
cancelled: statuses.filter((status) => status === 'cancelled').length,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function countAttemptStatuses(statuses) {
|
|
243
|
+
return {
|
|
244
|
+
reserved: statuses.filter((status) => status === 'reserved').length,
|
|
245
|
+
succeeded: statuses.filter((status) => status === 'succeeded').length,
|
|
246
|
+
failed: statuses.filter((status) => status === 'failed').length,
|
|
247
|
+
abandoned: statuses.filter((status) => status === 'abandoned').length,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function selectFirstHomeRunDetail(runs) {
|
|
251
|
+
if (runs.state !== 'ready' || runs.selected !== undefined)
|
|
252
|
+
return runs;
|
|
253
|
+
const firstInterruptedId = runs.interrupted[0]?.id;
|
|
254
|
+
const firstRecentId = runs.recent[0]?.id;
|
|
255
|
+
const selectedId = firstInterruptedId ?? firstRecentId;
|
|
256
|
+
if (selectedId === undefined)
|
|
257
|
+
return runs;
|
|
258
|
+
const selected = runs.details.find((detail) => detail.id === selectedId);
|
|
259
|
+
return selected === undefined ? runs : { ...runs, selected };
|
|
260
|
+
}
|
|
188
261
|
function runRecordSummary(run) {
|
|
189
262
|
const userIntent = run.userIntent.trim();
|
|
190
263
|
return {
|
|
@@ -210,6 +283,30 @@ function readHomeSkills(plan) {
|
|
|
210
283
|
opened.value.close();
|
|
211
284
|
}
|
|
212
285
|
}
|
|
286
|
+
function readHomeSdd(plan, change) {
|
|
287
|
+
if (change.length === 0)
|
|
288
|
+
return { state: 'not-loaded' };
|
|
289
|
+
const opened = openMemoryDatabase({ path: plan.db.path, readonly: true });
|
|
290
|
+
if (!opened.ok)
|
|
291
|
+
return { state: 'unavailable', databasePath: plan.db.path, message: opened.error.message };
|
|
292
|
+
try {
|
|
293
|
+
const sdd = new SddWorkflowService(new MemoryService(opened.value));
|
|
294
|
+
const status = sdd.getStatus({ project: plan.project, change });
|
|
295
|
+
if (!status.ok)
|
|
296
|
+
return { state: 'unavailable', databasePath: plan.db.path, message: status.error.message };
|
|
297
|
+
const next = sdd.getNext({ project: plan.project, change });
|
|
298
|
+
if (!next.ok)
|
|
299
|
+
return { state: 'unavailable', databasePath: plan.db.path, message: next.error.message };
|
|
300
|
+
const cockpit = sdd.getCockpit({ project: plan.project, change });
|
|
301
|
+
if (!cockpit.ok)
|
|
302
|
+
return { state: 'unavailable', databasePath: plan.db.path, message: cockpit.error.message };
|
|
303
|
+
const continuation = sddContinuationPlanFrom({ project: plan.project, next: next.value, cockpit: cockpit.value });
|
|
304
|
+
return { state: 'ready', databasePath: plan.db.path, status: status.value, next: next.value, cockpit: cockpit.value, continuation };
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
opened.value.close();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
213
310
|
function tuiWidth(stdout) {
|
|
214
311
|
const columns = stdout.columns;
|
|
215
312
|
const width = typeof columns === 'number' && Number.isFinite(columns) ? Math.floor(columns) : 88;
|
package/dist/cli/sdd-renderer.js
CHANGED
|
@@ -91,6 +91,8 @@ export function renderSddContinuationPlan(plan) {
|
|
|
91
91
|
`- ${suggestedCommandLabel}: ${plan.suggestedCommand}`,
|
|
92
92
|
`- Diagnostic command: ${plan.inspectCommand}`,
|
|
93
93
|
'',
|
|
94
|
+
...renderSddAdviceLines(plan.advice),
|
|
95
|
+
...(plan.advice.length === 0 ? [] : ['']),
|
|
94
96
|
'Blocker-specific next actions:',
|
|
95
97
|
...(plan.blockerActions.length === 0
|
|
96
98
|
? ['- none']
|
|
@@ -119,6 +121,11 @@ export function renderSddContinuationPlan(plan) {
|
|
|
119
121
|
];
|
|
120
122
|
return `${lines.join('\n')}\n`;
|
|
121
123
|
}
|
|
124
|
+
export function renderSddAdviceLines(advice) {
|
|
125
|
+
if (advice.length === 0)
|
|
126
|
+
return [];
|
|
127
|
+
return ['Advice:', ...advice.map((item) => `- ${item.title}: ${item.message}`)];
|
|
128
|
+
}
|
|
122
129
|
export function renderSddCockpit(cockpit) {
|
|
123
130
|
const readModel = cockpit.readModel;
|
|
124
131
|
const blockers = readModel.blockers;
|
|
@@ -46,7 +46,7 @@ export function callVgxTool(call, services) {
|
|
|
46
46
|
case 'vgxness_sdd_next':
|
|
47
47
|
return toEnvelope(validated.tool, services.sdd.getNext(validated.input));
|
|
48
48
|
case 'vgxness_sdd_cockpit':
|
|
49
|
-
return toEnvelope(validated.tool,
|
|
49
|
+
return toEnvelope(validated.tool, sddCockpitSurfaceResponse(validated.input, services));
|
|
50
50
|
case 'vgxness_sdd_continue':
|
|
51
51
|
return sddContinueEnvelope(validated.input, services);
|
|
52
52
|
case 'vgxness_governance_report':
|
|
@@ -267,6 +267,15 @@ function sddContinueEnvelope(input, services) {
|
|
|
267
267
|
...(relatedRun.value === undefined ? {} : { relatedRunContext: relatedRun.value }),
|
|
268
268
|
}));
|
|
269
269
|
}
|
|
270
|
+
function sddCockpitSurfaceResponse(input, services) {
|
|
271
|
+
const cockpit = services.sdd.getCockpit(input);
|
|
272
|
+
if (!cockpit.ok)
|
|
273
|
+
return cockpit;
|
|
274
|
+
const operationalEvidence = services.runs.getSddOperationalEvidence({ project: input.project, change: input.change });
|
|
275
|
+
if (!operationalEvidence.ok)
|
|
276
|
+
return { ok: true, value: buildSddCockpitSurfaceResponse(cockpit.value) };
|
|
277
|
+
return { ok: true, value: buildSddCockpitSurfaceResponse(cockpit.value, { operationalEvidenceByPhase: operationalEvidence.value }) };
|
|
278
|
+
}
|
|
270
279
|
export function createVgxMcpControlPlane(options = {}) {
|
|
271
280
|
const databasePath = resolveControlPlaneDatabasePath(options);
|
|
272
281
|
if (!databasePath.ok)
|
package/dist/mcp/schema.js
CHANGED
|
@@ -223,6 +223,22 @@ const sddRecommendedActionOutputSchema = z
|
|
|
223
223
|
.passthrough()),
|
|
224
224
|
})
|
|
225
225
|
.passthrough();
|
|
226
|
+
const sddContinuationAdviceOutputSchema = z
|
|
227
|
+
.object({
|
|
228
|
+
id: z.string(),
|
|
229
|
+
kind: z.enum(['proposal-question-round', 'review-workload-guard']),
|
|
230
|
+
title: z.string(),
|
|
231
|
+
message: z.string(),
|
|
232
|
+
readOnly: z.literal(true),
|
|
233
|
+
advisoryOnly: z.literal(true),
|
|
234
|
+
mutating: z.literal(false),
|
|
235
|
+
providerExecution: z.literal(false),
|
|
236
|
+
artifactMutation: z.literal(false),
|
|
237
|
+
runCreation: z.literal(false),
|
|
238
|
+
openspecWrite: z.literal(false),
|
|
239
|
+
reason: z.string(),
|
|
240
|
+
})
|
|
241
|
+
.passthrough();
|
|
226
242
|
const mcpSuccessOutputSchema = (value) => z
|
|
227
243
|
.object({
|
|
228
244
|
ok: z.literal(true),
|
|
@@ -672,6 +688,7 @@ export const VGX_MCP_TOOL_OUTPUT_SCHEMAS = {
|
|
|
672
688
|
kind: z.literal('sdd-continuation-plan'),
|
|
673
689
|
project: z.string(),
|
|
674
690
|
change: z.string(),
|
|
691
|
+
advice: z.array(sddContinuationAdviceOutputSchema),
|
|
675
692
|
recommendedActions: z.array(sddRecommendedActionOutputSchema),
|
|
676
693
|
})
|
|
677
694
|
.passthrough()),
|