vgxness 1.8.0 → 1.9.1
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 +22 -19
- package/dist/agents/renderers/claude-renderer.js +3 -1
- package/dist/agents/renderers/opencode-renderer.js +2 -1
- package/dist/behavior/behavior-contract-manifest.js +42 -0
- package/dist/behavior/behavior-contract-schema.js +1 -0
- package/dist/behavior/behavior-contract-validation.js +42 -0
- package/dist/governance/index.js +1 -0
- package/dist/governance/risk-classifier.js +116 -0
- package/dist/mcp/control-plane-snapshot-service.js +272 -0
- package/dist/mcp/control-plane.js +2 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/schema.js +5 -0
- package/dist/mcp/validation.js +19 -1
- package/dist/orchestrator/natural-language-planner.js +34 -17
- package/dist/payload/context-budget-policy.js +17 -0
- package/dist/payload/context-budget-service.js +44 -0
- package/dist/permissions/policy-evaluator.js +88 -11
- package/dist/runs/execution-planning.js +1 -1
- package/dist/runs/run-service.js +129 -35
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { canonicalBehaviorContractVersion } from '../behavior/behavior-contract-manifest.js';
|
|
1
2
|
export const canonicalDefaultAgentName = 'vgxness-manager';
|
|
2
|
-
export const canonicalPromptContractVersion =
|
|
3
|
+
export const canonicalPromptContractVersion = 7;
|
|
3
4
|
export const canonicalSddSubagentNames = [
|
|
4
5
|
'vgxness-sdd-explore',
|
|
5
6
|
'vgxness-sdd-propose',
|
|
@@ -74,14 +75,14 @@ function managerDefinition() {
|
|
|
74
75
|
mode: 'agent',
|
|
75
76
|
builtIn: true,
|
|
76
77
|
name: canonicalDefaultAgentName,
|
|
77
|
-
description: 'Coordinates VGXNESS MCP state and SDD sub-agents while
|
|
78
|
+
description: 'Coordinates VGXNESS MCP state and SDD sub-agents while routing Tier 0-2 lightweight work, Tier 3 preflight validation, and Tier 4 formal SDD.',
|
|
78
79
|
instructions: { kind: 'inline', value: registryManagerInstructions },
|
|
79
80
|
capabilities: ['sdd-orchestration', 'agent-routing', 'mcp-coordination', 'project-local-automation'],
|
|
80
81
|
permissions: { read: 'allow', edit: 'ask', shell: 'ask', git: 'ask', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' },
|
|
81
82
|
memory: { scopes: ['project'] },
|
|
82
83
|
workflows: ['explore', 'quickfix', 'plan', 'build', 'debug', 'sdd', 'agent-seeding', 'opencode-install'],
|
|
83
84
|
skills: ['vgxness-sdd-manager'],
|
|
84
|
-
adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
|
|
85
|
+
adapters: { opencode: { model: 'openai/gpt-5.5', config: { options: { reasoningEffort: 'high', vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion }, permission: { task: createCanonicalOpenCodeSddTaskPermissions() } } } },
|
|
85
86
|
providerSupport: commonSupport(),
|
|
86
87
|
};
|
|
87
88
|
}
|
|
@@ -100,7 +101,7 @@ function subagentDefinition(name) {
|
|
|
100
101
|
memory: { scopes: ['project'] },
|
|
101
102
|
workflows: data.workflows,
|
|
102
103
|
skills: data.skills,
|
|
103
|
-
adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion } } } },
|
|
104
|
+
adapters: { opencode: { model: 'openai/gpt-5.5', config: { hidden: true, options: { vgxnessPromptContractVersion: canonicalPromptContractVersion, vgxnessBehaviorContractVersion: canonicalBehaviorContractVersion } } } },
|
|
104
105
|
providerSupport: commonSupport(),
|
|
105
106
|
};
|
|
106
107
|
}
|
|
@@ -118,42 +119,44 @@ export const canonicalOpenCodeManagerPrompt = `# VGXNESS Manager - compact SDD o
|
|
|
118
119
|
Bind only to the primary \`vgxness-manager\` agent. Executor agents (for example \`vgxness-sdd-apply\`) use their own prompts.
|
|
119
120
|
|
|
120
121
|
## Role
|
|
121
|
-
Coordinate SDD;
|
|
122
|
-
|
|
123
|
-
Coach while coordinating: teach briefly when helpful, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
|
|
122
|
+
Coordinate SDD; keep chat thin, use VGXNESS MCP as durable state, delegate to the smallest exact hidden SDD subagent, then synthesize evidence. Coach while coordinating: teach briefly, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
|
|
124
123
|
|
|
125
124
|
## Non-negotiable governance
|
|
126
|
-
- SDD artifact acceptance is human-only. Never infer acceptance from generated output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/unaccepted artifacts as not accepted until a human acceptance record exists.
|
|
125
|
+
- SDD artifact acceptance is human-only. Never infer or fabricate acceptance from generated output, subagent/model output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/stale/unaccepted artifacts as not accepted until a human acceptance record exists.
|
|
127
126
|
- Before phase advancement, call readiness/status tools: \`vgxness_sdd_status\`/\`vgxness_sdd_next\` and \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
|
|
128
127
|
- Before risky VGX-managed side effects (edit, shell/tests, git, network, provider-tool, secrets, external-directory, destructive, privileged, ambiguous), call \`vgxness_run_preflight\` with runId/workflow/phase/agent context when available. If approval/block is required, stop; do not invent approval.
|
|
128
|
+
- Direct human acceptance of an exact SDD artifact via \`vgxness_sdd_accept_artifact\` is not a generic SDD write for manager routing: do not call \`vgxness_run_preflight\` solely for that acceptance when the user explicitly accepted the exact project/change/phase artifact, \`acceptedBy.type\` is \`"human"\`, \`acceptedBy.id\` is non-empty, and status/readiness confirms the artifact is eligible. This shortcut applies only to \`vgxness_sdd_accept_artifact\`, not \`vgxness_sdd_save_artifact\`, edits, shell/tests, git, provider config, memory writes, external paths, secrets, destructive/privileged/ambiguous operations.
|
|
129
129
|
- OpenCode native/provider tools are governance-v1 audit-only/non-hard-blocking. Report warnings; do not say native OpenCode tools are hard-blocked by config.
|
|
130
130
|
- Do not mutate provider/global OpenCode config unless explicitly requested. Do not write to \`openspec/\`. Never revert/overwrite unrelated user work. Preserve \`permission.task\` deny-by-default with only exact known SDD subagents allowed.
|
|
131
131
|
- Do not publish packages unless explicitly requested.
|
|
132
132
|
- Do not change model or reasoning effort.
|
|
133
133
|
|
|
134
|
+
## Flexible governance routing
|
|
135
|
+
Use the lightest safe path: Tier 0-2 direct; Tier 3 preflight/explicit validation; Tier 4 formal SDD for governance, permission model, SDD acceptance, architecture/security, or cross-surface behavior. Provider status/doctor/preview/handoff are read-only audit-only; provider config writes stay gated.
|
|
136
|
+
|
|
134
137
|
## Provider-native daily flow
|
|
135
|
-
|
|
138
|
+
Daily SDD happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for normal flow. CLI is an escape hatch for bootstrap, doctor, recovery, setup gaps, or explicit request.
|
|
136
139
|
|
|
137
140
|
## MCP playbook
|
|
138
|
-
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal
|
|
139
|
-
- SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default
|
|
140
|
-
- Acceptance/readiness: confirm explicit human acceptance,
|
|
141
|
-
- Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\`
|
|
142
|
-
- Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\`
|
|
141
|
+
- For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal, not truth. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one and summarize.
|
|
142
|
+
- SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default so the primary context stays clean. Use verbose only when required, preferably inside the delegated phase subagent. Save with \`vgxness_sdd_save_artifact\` only after the appropriate flow. SDD artifacts are not generic memory.
|
|
143
|
+
- Acceptance/readiness: check SDD status/readiness for the exact project/change/phase, confirm explicit human acceptance of that exact artifact, call \`vgxness_sdd_accept_artifact\` directly with audit context (\`acceptedBy.type: "human"\`, non-empty \`acceptedBy.id\`, runId, agentId, note/rationale when useful), then re-check status/readiness before reporting state. Do not add \`vgxness_run_preflight\` solely for that exact direct acceptance call. Ambiguous replies count only when tied to an immediate exact acceptance prompt. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings before risky/ambiguous transitions and in apply/verify summaries.
|
|
144
|
+
- Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` for prior work or unclear context; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
|
|
145
|
+
- Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` are preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
|
|
143
146
|
- Runs: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for significant implementation/verification or multi-step delegated work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`.
|
|
144
147
|
- Provider diagnostics: \`vgxness_provider_status\` and \`vgxness_provider_doctor\` are read-only reports.
|
|
145
148
|
|
|
146
149
|
## Minimum flows
|
|
147
|
-
- Simple answer:
|
|
148
|
-
- Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist
|
|
149
|
-
- Apply/verify: require
|
|
150
|
+
- Simple answer: inline; memory only for prior context; no run by default.
|
|
151
|
+
- Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist by governance.
|
|
152
|
+
- Apply/verify: require prerequisites -> read artifacts -> resolve exact subagent -> start/recover run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save output -> finalize when clear.
|
|
150
153
|
- Config/provider/prompt change: inspect manager/profile or payload first; persistent changes require explicit human authorization.
|
|
151
154
|
|
|
152
155
|
## Delegation thresholds
|
|
153
|
-
Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration
|
|
156
|
+
Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration, substantial implementation, writes with analysis/new logic, and execution-heavy verification. Never delegate to unknown agents; use exact SDD phase mapping.
|
|
154
157
|
|
|
155
158
|
## Output
|
|
156
|
-
Be concise:
|
|
159
|
+
Be concise: delegated work, results, files/decisions, evidence/tests, risks, next step.`;
|
|
157
160
|
const registryManagerInstructions = 'You are the VGXNESS SDD coordinator, not a monolithic executor. Coach briefly while coordinating: explain useful tradeoffs, be realistic about risks and unknowns, respectfully challenge weak assumptions with better options, keep the user comfortable and in control, and stay concise. Use VGXNESS MCP as the durable control plane: restore session context with vgxness_session_restore before inferring start/resume state from chat, and close/pause/compact with vgxness_session_close using actor manager plus an actionable summary when a current session id exists. Check SDD status/next/ready, read prerequisites with sdd_get_artifact or sdd_list_artifacts, save accepted phase output with sdd_save_artifact, search/get/save/update memory only for reusable knowledge, resolve exact SDD subagents before substantial phase work, use runs/checkpoints/preflight/finalize for significant implementation or verification, and use vgxness_provider_status for configured/phase/next questions plus vgxness_provider_doctor for read-only OpenCode MCP/manager health. Prefer payloadMode=compact for manager-facing status/context reads, SDD artifact reads/lists, and activation handoffs so the primary context stays clean; request payloadMode=verbose only when full artifact contents, provider payloads, or skill context are actually needed, preferably inside delegated phase subagents. CLI is an escape hatch for bootstrap, doctor, rollback, recovery, MCP unavailable/setup missing, or explicit user request; do not tell users to run terminal SDD phase commands for normal daily flow. Delegate real SDD phase work to the smallest exact vgxness-sdd-* subagent allowed by permission.task, synthesize results, and persist artifacts/checkpoints. Do not perform substantial multi-file implementation inline. Do not mutate global/provider OpenCode config, install skills, publish packages, or write openspec/ unless explicitly authorized. Checked-in manager and subagent instructions are self-contained; external sdd-* skill files are optional registry assets, not requirements.';
|
|
158
161
|
const subagentData = {
|
|
159
162
|
'vgxness-sdd-explore': { seedDescription: 'Investigates codebase context and identifies options before proposals.', seedInstructions: 'You are the explore phase executor, not the orchestrator. Do not delegate. Explore repository evidence for the requested SDD change. Do not implement code changes. Return concise findings, risks, and recommended next artifacts.', capabilities: ['sdd-exploration', 'codebase-research', 'discovery'], permissions: { read: 'allow', edit: 'deny', shell: 'ask', git: 'deny', memory: 'allow', 'provider-tool': 'deny', secrets: 'deny' }, workflows: ['sdd:explore'], skills: ['vgxness-sdd-explore'], phaseContract: 'Investigate codebase context, constraints, options, and risks. Do not implement code changes. Return findings and recommended next artifacts.' },
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { canonicalPromptContractVersion } from '../canonical-agent-manifest.js';
|
|
2
|
+
import { canonicalBehaviorContractVersion } from '../../behavior/behavior-contract-manifest.js';
|
|
1
3
|
import { ok, validationFailure } from './provider-adapter.js';
|
|
2
4
|
const previewWarnings = [
|
|
3
5
|
'Claude rendering returns installable preview artifacts only; it does not install and does not write provider configuration.',
|
|
@@ -66,7 +68,7 @@ export class ClaudeAgentRenderer {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
function renderClaudeAgentMarkdown(agent, key) {
|
|
69
|
-
return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion
|
|
71
|
+
return `---\nname: ${JSON.stringify(key)}\ndescription: ${JSON.stringify(agent.description)}\n---\n\n<!-- VGXNESS-GENERATED claude-code-provider-support provider=claude artifact=claude-code-subagent promptContractVersion=${canonicalPromptContractVersion} behaviorContractVersion=${canonicalBehaviorContractVersion} safe-update=true -->\n\n${agent.instructions.value.trim()}\n`;
|
|
70
72
|
}
|
|
71
73
|
function claudeAgentKey(value) {
|
|
72
74
|
return pathSegment(value);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { behaviorContractProjectionMetadata } from '../../behavior/behavior-contract-manifest.js';
|
|
1
2
|
import { ok, validationFailure } from './provider-adapter.js';
|
|
2
3
|
const openCodeSchema = 'https://opencode.ai/config.json';
|
|
3
4
|
const previewWarning = 'Rendering returns preview artifacts only; it does not install or write .opencode/, .claude/, or provider configuration.';
|
|
@@ -50,7 +51,7 @@ export class OpenCodeAgentRenderer {
|
|
|
50
51
|
const artifact = {
|
|
51
52
|
relativePath: `rendered/opencode/${pathSegment(input.agent.project)}/${input.agent.scope}/${pathSegment(input.agent.name)}/opencode.json`,
|
|
52
53
|
contentType: 'application/json',
|
|
53
|
-
contents: `${JSON.stringify({ $schema: openCodeSchema, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
|
|
54
|
+
contents: `${JSON.stringify({ $schema: openCodeSchema, behaviorContract: behaviorContractProjectionMetadata, agent: renderedAgents, safety: previewSafety }, null, 2)}\n`,
|
|
54
55
|
};
|
|
55
56
|
return ok({
|
|
56
57
|
provider: this.provider,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const canonicalBehaviorContractName = 'vgxness-manager-behavior-contract';
|
|
2
|
+
export const canonicalBehaviorContractVersion = '1.0.0';
|
|
3
|
+
export const criticalBehaviorContractInvariantIds = [
|
|
4
|
+
'sdd.acceptance.human-only',
|
|
5
|
+
'sdd.draft-is-not-accepted',
|
|
6
|
+
'sdd.readiness-before-advance',
|
|
7
|
+
'provider.preview-status-doctor-readonly',
|
|
8
|
+
'provider.config-writes-explicit-consent',
|
|
9
|
+
'run.risky-effects-preflight',
|
|
10
|
+
'delegation.deny-by-default',
|
|
11
|
+
'delegation.no-wildcard',
|
|
12
|
+
'context.manager-compact-default',
|
|
13
|
+
'context.progressive-disclosure',
|
|
14
|
+
'flow.provider-native-no-terminal-sdd-daily',
|
|
15
|
+
'scope.exclude-vgxcode-runtime',
|
|
16
|
+
'worktree.preserve-unrelated-user-work',
|
|
17
|
+
];
|
|
18
|
+
export const behaviorContractManifest = {
|
|
19
|
+
name: canonicalBehaviorContractName,
|
|
20
|
+
contractVersion: canonicalBehaviorContractVersion,
|
|
21
|
+
excludedTargets: ['vgxcode', 'src/code/runtime/*', 'src/code/prompts/*'],
|
|
22
|
+
invariants: [
|
|
23
|
+
{ id: 'sdd.acceptance.human-only', category: 'sdd', severity: 'critical', summary: 'SDD acceptance is recorded only from explicit human acceptance.' },
|
|
24
|
+
{ id: 'sdd.draft-is-not-accepted', category: 'sdd', severity: 'critical', summary: 'Draft, rejected, superseded, legacy, or unaccepted artifacts are not accepted prerequisites.' },
|
|
25
|
+
{ id: 'sdd.readiness-before-advance', category: 'sdd', severity: 'critical', summary: 'Phase advancement checks readiness/status before treating prerequisites as satisfied.' },
|
|
26
|
+
{ id: 'provider.preview-status-doctor-readonly', category: 'provider', severity: 'critical', summary: 'Preview, status, and doctor surfaces are read-only and do not write provider configuration.' },
|
|
27
|
+
{ id: 'provider.config-writes-explicit-consent', category: 'provider', severity: 'critical', summary: 'Provider configuration writes require explicit user consent through confirmed flows.' },
|
|
28
|
+
{ id: 'run.risky-effects-preflight', category: 'run', severity: 'critical', summary: 'Risky edits, shell, git, provider, secret, destructive, privileged, or ambiguous effects require preflight.' },
|
|
29
|
+
{ id: 'delegation.deny-by-default', category: 'delegation', severity: 'critical', summary: 'Delegation defaults to deny unless an exact governed path allows it.' },
|
|
30
|
+
{ id: 'delegation.no-wildcard', category: 'delegation', severity: 'critical', summary: 'Wildcard delegation is not allowed; exact canonical subagents must be used.' },
|
|
31
|
+
{ id: 'context.manager-compact-default', category: 'context', severity: 'critical', summary: 'Manager-facing context defaults to compact payloads.' },
|
|
32
|
+
{ id: 'context.progressive-disclosure', category: 'context', severity: 'critical', summary: 'Expanded and verbose context are requested only when needed.' },
|
|
33
|
+
{ id: 'flow.provider-native-no-terminal-sdd-daily', category: 'flow', severity: 'critical', summary: 'Daily SDD progression happens provider-natively through conversation and MCP, not mandatory terminal phase commands.' },
|
|
34
|
+
{ id: 'scope.exclude-vgxcode-runtime', category: 'scope', severity: 'critical', summary: 'This behavior contract excludes vgxcode and src/code runtime/prompt implementation targets.' },
|
|
35
|
+
{ id: 'worktree.preserve-unrelated-user-work', category: 'worktree', severity: 'critical', summary: 'Unrelated user work and dirty files are preserved.' },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
export const behaviorContractProjectionMetadata = {
|
|
39
|
+
behaviorContractName: canonicalBehaviorContractName,
|
|
40
|
+
behaviorContractVersion: canonicalBehaviorContractVersion,
|
|
41
|
+
criticalInvariantIds: criticalBehaviorContractInvariantIds,
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { behaviorContractManifest, criticalBehaviorContractInvariantIds } from './behavior-contract-manifest.js';
|
|
2
|
+
export function validateBehaviorContractManifest(manifest = behaviorContractManifest) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
if (!manifest.name.trim())
|
|
5
|
+
errors.push('behavior contract name is required');
|
|
6
|
+
if (!manifest.contractVersion.trim())
|
|
7
|
+
errors.push('behavior contract version is required');
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
for (const invariant of manifest.invariants) {
|
|
10
|
+
if (!invariant.id.trim())
|
|
11
|
+
errors.push('invariant id is required');
|
|
12
|
+
if (seen.has(invariant.id))
|
|
13
|
+
errors.push(`duplicate invariant id: ${invariant.id}`);
|
|
14
|
+
seen.add(invariant.id);
|
|
15
|
+
if (!invariant.summary.trim())
|
|
16
|
+
errors.push(`invariant ${invariant.id} summary is required`);
|
|
17
|
+
}
|
|
18
|
+
for (const id of criticalBehaviorContractInvariantIds) {
|
|
19
|
+
const invariant = manifest.invariants.find((candidate) => candidate.id === id);
|
|
20
|
+
if (invariant === undefined)
|
|
21
|
+
errors.push(`missing critical invariant: ${id}`);
|
|
22
|
+
else if (invariant.severity !== 'critical')
|
|
23
|
+
errors.push(`critical invariant ${id} must have severity critical`);
|
|
24
|
+
}
|
|
25
|
+
for (const target of ['vgxcode', 'src/code/runtime/*', 'src/code/prompts/*']) {
|
|
26
|
+
if (!manifest.excludedTargets.includes(target))
|
|
27
|
+
errors.push(`missing excluded target: ${target}`);
|
|
28
|
+
}
|
|
29
|
+
return { ok: errors.length === 0, errors };
|
|
30
|
+
}
|
|
31
|
+
export function validateBehaviorContractProjection(metadata, manifest = behaviorContractManifest) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (metadata.behaviorContractName !== manifest.name)
|
|
34
|
+
errors.push(`behavior contract name mismatch: ${metadata.behaviorContractName}`);
|
|
35
|
+
if (metadata.behaviorContractVersion !== manifest.contractVersion)
|
|
36
|
+
errors.push(`behavior contract version mismatch: ${metadata.behaviorContractVersion}`);
|
|
37
|
+
for (const id of criticalBehaviorContractInvariantIds) {
|
|
38
|
+
if (!metadata.criticalInvariantIds.includes(id))
|
|
39
|
+
errors.push(`projection missing critical invariant reference: ${id}`);
|
|
40
|
+
}
|
|
41
|
+
return { ok: errors.length === 0, errors };
|
|
42
|
+
}
|
package/dist/governance/index.js
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isWorkflowId } from '../workflows/schema.js';
|
|
2
|
+
const readOnlyIntentTerms = [
|
|
3
|
+
/\b(explain|what|why|how|describe|read|inspect|search|status|doctor|preview|diagnos(?:e|tic|tics)|logs?)\b/,
|
|
4
|
+
/\b(explicar|diagnosticar)\b/,
|
|
5
|
+
];
|
|
6
|
+
const planningTerms = [/\b(plan|review|checklist|preview|dry[- ]run)\b/, /\btarea simple\b/];
|
|
7
|
+
const quickfixTerms = [/\b(fix|patch|typo|small|bounded|arreglar)\b/];
|
|
8
|
+
const buildTerms = [/\b(build|add|create|implement|feature|capability)\b/];
|
|
9
|
+
const tier3Terms = [
|
|
10
|
+
/\b(install|repair|write config|provider config|\.opencode|\.claude|global config)\b/,
|
|
11
|
+
/\b(commit|push|tag|amend|force[- ]push|publish|release|delete|remove|destroy|drop|reset|secret|token|sudo|external|network)\b/,
|
|
12
|
+
/\b(aprobaci[oó]n|permiso)\b/,
|
|
13
|
+
];
|
|
14
|
+
const tier4MutationTerms = [
|
|
15
|
+
/\b(build|add|create|implement)\b.*\bnew\b.*\bworkflow\b/,
|
|
16
|
+
/\b(change|update|modify|refactor|implement|build|add|create|fix|arreglar)\b.*\b(governance|permission model|permissions?|sdd acceptance|artifact acceptance|workflow routing|workflow capability|routing|security semantics|architecture|cross[- ]surface)\b/,
|
|
17
|
+
/\b(governance|permission model|permissions?|sdd acceptance|artifact acceptance|workflow routing|workflow capability|routing|security semantics|architecture|cross[- ]surface)\b.*\b(change|update|modify|refactor|implement|build|add|create|fix|arreglar)\b/,
|
|
18
|
+
];
|
|
19
|
+
const keywordOnlyProtectedTerms = /\b(governance|permissions?|workflow|provider|sdd|security|architecture)\b/;
|
|
20
|
+
export function classifyIntentRisk(input) {
|
|
21
|
+
const intent = normalize(input.intent);
|
|
22
|
+
const evidence = [`intent:${intent}`];
|
|
23
|
+
if (tier4MutationTerms.some((term) => term.test(intent))) {
|
|
24
|
+
const reasonCodes = [];
|
|
25
|
+
if (/\bpermissions?|permission model|permiso\b/.test(intent))
|
|
26
|
+
reasonCodes.push('permission_model_change');
|
|
27
|
+
if (/\bsdd acceptance|artifact acceptance|sdd\b/.test(intent))
|
|
28
|
+
reasonCodes.push('sdd_governance_change');
|
|
29
|
+
if (/\bgovernance\b/.test(intent))
|
|
30
|
+
reasonCodes.push('governance_change');
|
|
31
|
+
if (/\barchitecture\b/.test(intent))
|
|
32
|
+
reasonCodes.push('architecture_change');
|
|
33
|
+
if (/\bcross[- ]surface|workflow routing|workflow capability|routing\b/.test(intent))
|
|
34
|
+
reasonCodes.push('cross_surface_change');
|
|
35
|
+
return classification(4, unique(reasonCodes.length === 0 ? ['governance_change'] : reasonCodes), 'sdd', 'ask', evidence);
|
|
36
|
+
}
|
|
37
|
+
if (readOnlyIntentTerms.some((term) => term.test(intent)) || (/\b(no uses sdd)\b/.test(intent) && keywordOnlyProtectedTerms.test(intent))) {
|
|
38
|
+
const workflow = /\bdiagnos(?:e|tic|tics)|diagnosticar|doctor|status|logs?\b/.test(intent) ? 'debug' : 'direct';
|
|
39
|
+
return classification(0, ['read_only'], workflow, 'audit', evidence);
|
|
40
|
+
}
|
|
41
|
+
if (tier3Terms.some((term) => term.test(intent)))
|
|
42
|
+
return classification(3, ['unknown_conservative'], workflowFromHint(input.workflowHint, 'plan'), 'ask', evidence);
|
|
43
|
+
if (buildTerms.some((term) => term.test(intent)))
|
|
44
|
+
return classification(2, ['moderate_project_mutation'], 'build', 'ask', evidence);
|
|
45
|
+
if (planningTerms.some((term) => term.test(intent)))
|
|
46
|
+
return classification(1, ['read_only'], 'plan', 'audit', evidence);
|
|
47
|
+
if (quickfixTerms.some((term) => term.test(intent)))
|
|
48
|
+
return classification(1, ['small_local_change'], 'quickfix', 'allow', evidence);
|
|
49
|
+
return classification(0, ['read_only'], 'explore', 'audit', evidence);
|
|
50
|
+
}
|
|
51
|
+
export function classifyOperationRisk(input) {
|
|
52
|
+
const op = normalize([input.operation, input.providerToolName].filter(Boolean).join(' '));
|
|
53
|
+
const evidence = [`category:${input.category}`, `operation:${input.operation}`];
|
|
54
|
+
if (input.providerToolName !== undefined)
|
|
55
|
+
evidence.push(`providerTool:${input.providerToolName}`);
|
|
56
|
+
if (input.destructive === true)
|
|
57
|
+
return classification(3, ['destructive_operation'], 'plan', 'ask', evidence);
|
|
58
|
+
if (input.external === true || input.category === 'network' || input.category === 'external-directory')
|
|
59
|
+
return classification(3, ['external_operation'], 'plan', 'ask', evidence);
|
|
60
|
+
if (input.privileged === true)
|
|
61
|
+
return classification(3, ['privileged_operation'], 'plan', 'ask', evidence);
|
|
62
|
+
if (input.ambiguous === true)
|
|
63
|
+
return classification(3, ['ambiguous_mutation'], 'plan', 'ask', evidence);
|
|
64
|
+
if (input.category === 'secrets')
|
|
65
|
+
return classification(3, ['secret_access'], 'plan', 'deny', evidence);
|
|
66
|
+
if (input.category === 'provider-tool')
|
|
67
|
+
return classifyProviderTool(op, evidence);
|
|
68
|
+
if (input.category === 'git-write' || (input.category === 'git' && /\b(commit|amend|tag|push|delete[- ]branch|force[- ]push|merge|rebase)\b/.test(op)))
|
|
69
|
+
return classification(3, ['git_write'], 'plan', 'ask', evidence);
|
|
70
|
+
if (input.category === 'git')
|
|
71
|
+
return classification(0, ['read_only'], 'direct', 'audit', evidence);
|
|
72
|
+
if (/\b(publish|release|npm publish|package distribution)\b/.test(op))
|
|
73
|
+
return classification(3, ['publish_release'], 'plan', 'ask', evidence);
|
|
74
|
+
if (input.category === 'shell' && /^command-allowlist[ :-][a-z0-9._/-]+$/.test(op))
|
|
75
|
+
return classification(0, ['allowlisted_verification'], 'debug', 'allow', evidence);
|
|
76
|
+
if (input.category === 'shell')
|
|
77
|
+
return classification(3, ['unknown_conservative'], 'plan', 'ask', evidence);
|
|
78
|
+
if (input.category === 'test-run')
|
|
79
|
+
return classification(0, ['allowlisted_verification'], 'debug', 'audit', evidence);
|
|
80
|
+
if (input.category === 'read')
|
|
81
|
+
return classification(0, [input.workspaceRoot !== undefined ? 'workspace_read' : 'read_only'], 'direct', 'audit', evidence);
|
|
82
|
+
if (input.category === 'edit' || input.category === 'implementation-edit' || input.category === 'spec-write' || input.category === 'design-write' || input.category === 'task-write')
|
|
83
|
+
return classification(input.category === 'implementation-edit' ? 2 : 1, [input.category === 'implementation-edit' ? 'moderate_project_mutation' : 'small_local_change'], workflowFromHint(input.workflow, 'quickfix'), 'ask', evidence);
|
|
84
|
+
if (input.category === 'install')
|
|
85
|
+
return classification(3, ['external_operation'], 'plan', 'ask', evidence);
|
|
86
|
+
if (input.category === 'memory' || input.category === 'memory-write')
|
|
87
|
+
return classification(1, ['small_local_change'], 'explore', 'audit', evidence);
|
|
88
|
+
return classification(3, ['unknown_conservative'], 'plan', 'ask', evidence);
|
|
89
|
+
}
|
|
90
|
+
function classifyProviderTool(operation, evidence) {
|
|
91
|
+
if (/\b(status|doctor|preview|handoff|change[- ]plan|health)\b/.test(operation))
|
|
92
|
+
return classification(0, ['provider_readonly_audit'], 'direct', 'audit', evidence);
|
|
93
|
+
if (/\b(install|setup|repair|write|apply|config|mcp[- ]install)\b/.test(operation))
|
|
94
|
+
return classification(3, ['provider_config_write'], 'plan', 'ask', evidence);
|
|
95
|
+
return classification(3, ['unknown_conservative'], 'plan', 'ask', evidence);
|
|
96
|
+
}
|
|
97
|
+
function classification(tier, reasonCodes, recommendedWorkflow, defaultDecision, evidence) {
|
|
98
|
+
return {
|
|
99
|
+
tier,
|
|
100
|
+
reasonCodes,
|
|
101
|
+
recommendedWorkflow,
|
|
102
|
+
defaultDecision,
|
|
103
|
+
requiresSdd: tier === 4,
|
|
104
|
+
requiresExplicitValidation: tier >= 3,
|
|
105
|
+
evidence,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function workflowFromHint(value, fallback) {
|
|
109
|
+
return value !== undefined && isWorkflowId(value) ? value : fallback;
|
|
110
|
+
}
|
|
111
|
+
function normalize(value) {
|
|
112
|
+
return value.toLowerCase().replace(/[^a-z0-9._/ áéíóúüñ-]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
113
|
+
}
|
|
114
|
+
function unique(values) {
|
|
115
|
+
return [...new Set(values)];
|
|
116
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { ContextBudgetService } from '../payload/context-budget-service.js';
|
|
2
|
+
export const contextCockpitSnapshotLevels = ['compact', 'expanded', 'verbose'];
|
|
3
|
+
export class ContextCockpitSnapshotService {
|
|
4
|
+
dependencies;
|
|
5
|
+
budget;
|
|
6
|
+
constructor(dependencies) {
|
|
7
|
+
this.dependencies = dependencies;
|
|
8
|
+
this.budget = dependencies.budget ?? new ContextBudgetService();
|
|
9
|
+
}
|
|
10
|
+
build(input) {
|
|
11
|
+
const level = input.level ?? 'compact';
|
|
12
|
+
const legacyInput = {
|
|
13
|
+
project: input.project,
|
|
14
|
+
...(input.directory === undefined ? {} : { directory: input.directory }),
|
|
15
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
16
|
+
};
|
|
17
|
+
const legacy = this.dependencies.memory.getContextCockpit(legacyInput);
|
|
18
|
+
if (!legacy.ok)
|
|
19
|
+
return legacy;
|
|
20
|
+
const sddResult = input.change === undefined ? undefined : this.buildSddSection({ project: input.project, change: input.change }, level);
|
|
21
|
+
if (sddResult !== undefined && !sddResult.ok)
|
|
22
|
+
return sddResult;
|
|
23
|
+
const sdd = sddResult?.value;
|
|
24
|
+
const integratesSdd = sdd !== undefined;
|
|
25
|
+
const optionalSectionsOmitted = omittedSections(legacy.value.optionalSectionsOmitted, integratesSdd ? ['provider'] : ['sdd', 'provider'], integratesSdd ? ['sdd'] : []);
|
|
26
|
+
const snapshotWithoutBudget = {
|
|
27
|
+
...legacy.value,
|
|
28
|
+
optionalSectionsOmitted,
|
|
29
|
+
...(sdd === undefined ? {} : { sdd }),
|
|
30
|
+
snapshotVersion: 2,
|
|
31
|
+
level,
|
|
32
|
+
references: [
|
|
33
|
+
{
|
|
34
|
+
id: 'legacy-context-cockpit',
|
|
35
|
+
kind: 'legacy-context-cockpit',
|
|
36
|
+
description: 'Wrapped output from the existing no-trace context cockpit path.',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
warnings: [
|
|
40
|
+
...(integratesSdd ? [] : ['sdd-snapshot-omitted:no-change']),
|
|
41
|
+
'provider-snapshot-omitted:no-workspace-root',
|
|
42
|
+
],
|
|
43
|
+
safety: {
|
|
44
|
+
...legacy.value.safety,
|
|
45
|
+
readOnly: true,
|
|
46
|
+
recordsTraces: false,
|
|
47
|
+
noTrace: true,
|
|
48
|
+
mutatesSessions: false,
|
|
49
|
+
noSessionMutation: true,
|
|
50
|
+
mutatesMemories: false,
|
|
51
|
+
noMemoryMutation: true,
|
|
52
|
+
mutatesArtifacts: false,
|
|
53
|
+
noArtifactMutation: true,
|
|
54
|
+
mutatesRuns: false,
|
|
55
|
+
noCheckpoints: true,
|
|
56
|
+
writesProviderConfig: false,
|
|
57
|
+
noProviderConfigWrites: true,
|
|
58
|
+
mutatesRepository: false,
|
|
59
|
+
executesProvider: false,
|
|
60
|
+
mutatesProviderConfig: false,
|
|
61
|
+
createsRun: false,
|
|
62
|
+
createsCheckpoint: false,
|
|
63
|
+
recordsTimelineEvent: false,
|
|
64
|
+
integratesSdd,
|
|
65
|
+
integratesProvider: false,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const report = this.budget.reportJson(budgetIdForLevel(level), snapshotWithoutBudget);
|
|
69
|
+
return {
|
|
70
|
+
ok: true,
|
|
71
|
+
value: {
|
|
72
|
+
...snapshotWithoutBudget,
|
|
73
|
+
budget: {
|
|
74
|
+
level,
|
|
75
|
+
policy: budgetIdForLevel(level),
|
|
76
|
+
report,
|
|
77
|
+
memoryPreviewLimit: input.limit ?? legacy.value.memoryPreviews.length,
|
|
78
|
+
includesSdd: integratesSdd,
|
|
79
|
+
includesProvider: false,
|
|
80
|
+
omittedSections: [
|
|
81
|
+
...(integratesSdd ? [] : ['sdd']),
|
|
82
|
+
'provider',
|
|
83
|
+
'runs',
|
|
84
|
+
'memory-content',
|
|
85
|
+
'session-transcripts',
|
|
86
|
+
'provider-config-contents',
|
|
87
|
+
'run-checkpoint-details',
|
|
88
|
+
],
|
|
89
|
+
heavyContentPolicy: 'references-only',
|
|
90
|
+
notes: ['Budget measured with ContextBudgetService.reportJson before adding the final budget section.'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
buildSddSection(input, level) {
|
|
96
|
+
if (this.dependencies.sdd === undefined) {
|
|
97
|
+
return { ok: false, error: { code: 'validation_failed', message: 'SDD cockpit service is not available' } };
|
|
98
|
+
}
|
|
99
|
+
const cockpit = this.dependencies.sdd.getCockpit(input);
|
|
100
|
+
if (!cockpit.ok)
|
|
101
|
+
return cockpit;
|
|
102
|
+
return { ok: true, value: toSddSection(cockpit.value, level) };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function toSddSection(cockpit, level) {
|
|
106
|
+
switch (level) {
|
|
107
|
+
case 'compact':
|
|
108
|
+
return toCompactSddSection(cockpit);
|
|
109
|
+
case 'expanded':
|
|
110
|
+
return toExpandedSddSection(cockpit);
|
|
111
|
+
case 'verbose':
|
|
112
|
+
return toVerboseSddSection(cockpit);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function toCompactSddSection(cockpit) {
|
|
116
|
+
return {
|
|
117
|
+
projection: 'compact',
|
|
118
|
+
project: cockpit.project,
|
|
119
|
+
change: cockpit.change,
|
|
120
|
+
recommendedAction: cockpit.recommendedAction,
|
|
121
|
+
...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
|
|
122
|
+
next: {
|
|
123
|
+
status: cockpit.next.status,
|
|
124
|
+
...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
|
|
125
|
+
reason: cockpit.next.reason,
|
|
126
|
+
},
|
|
127
|
+
phases: cockpit.phases.map((phase) => ({
|
|
128
|
+
phase: phase.phase,
|
|
129
|
+
topicKey: phase.topicKey,
|
|
130
|
+
present: phase.present,
|
|
131
|
+
accepted: phase.accepted,
|
|
132
|
+
legacy: phase.legacy,
|
|
133
|
+
state: phase.state,
|
|
134
|
+
readinessReady: phase.readiness.ready,
|
|
135
|
+
blockerCount: phase.blockers.length,
|
|
136
|
+
})),
|
|
137
|
+
counts: {
|
|
138
|
+
phases: cockpit.phases.length,
|
|
139
|
+
artifacts: cockpit.artifacts.length,
|
|
140
|
+
accepted: cockpit.acceptedCount,
|
|
141
|
+
legacy: cockpit.legacyCount,
|
|
142
|
+
blockers: cockpit.aggregateBlockers.length,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function toExpandedSddSection(cockpit) {
|
|
147
|
+
return {
|
|
148
|
+
projection: 'expanded',
|
|
149
|
+
project: cockpit.project,
|
|
150
|
+
change: cockpit.change,
|
|
151
|
+
recommendedAction: cockpit.recommendedAction,
|
|
152
|
+
...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
|
|
153
|
+
next: {
|
|
154
|
+
status: cockpit.next.status,
|
|
155
|
+
...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
|
|
156
|
+
reason: cockpit.next.reason,
|
|
157
|
+
},
|
|
158
|
+
phases: cockpit.phases.map(toExpandedSddPhase),
|
|
159
|
+
counts: {
|
|
160
|
+
phases: cockpit.phases.length,
|
|
161
|
+
artifacts: cockpit.artifacts.length,
|
|
162
|
+
accepted: cockpit.acceptedCount,
|
|
163
|
+
legacy: cockpit.legacyCount,
|
|
164
|
+
blockers: cockpit.aggregateBlockers.length,
|
|
165
|
+
},
|
|
166
|
+
blockers: cockpit.aggregateBlockers.map(toSddBlocker),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function toVerboseSddSection(cockpit) {
|
|
170
|
+
return {
|
|
171
|
+
projection: 'verbose',
|
|
172
|
+
project: cockpit.project,
|
|
173
|
+
change: cockpit.change,
|
|
174
|
+
recommendedAction: cockpit.recommendedAction,
|
|
175
|
+
...(cockpit.actionablePhase === undefined ? {} : { actionablePhase: cockpit.actionablePhase }),
|
|
176
|
+
next: {
|
|
177
|
+
status: cockpit.next.status,
|
|
178
|
+
...(cockpit.next.nextPhase === undefined ? {} : { nextPhase: cockpit.next.nextPhase }),
|
|
179
|
+
reason: cockpit.next.reason,
|
|
180
|
+
},
|
|
181
|
+
phases: cockpit.phases.map(toExpandedSddPhase),
|
|
182
|
+
counts: {
|
|
183
|
+
phases: cockpit.phases.length,
|
|
184
|
+
artifacts: cockpit.artifacts.length,
|
|
185
|
+
accepted: cockpit.acceptedCount,
|
|
186
|
+
legacy: cockpit.legacyCount,
|
|
187
|
+
blockers: cockpit.aggregateBlockers.length,
|
|
188
|
+
},
|
|
189
|
+
blockers: cockpit.aggregateBlockers.map(toSddBlocker),
|
|
190
|
+
artifacts: cockpit.artifacts.map(toArtifactMetadata),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function toExpandedSddPhase(phase) {
|
|
194
|
+
return {
|
|
195
|
+
phase: phase.phase,
|
|
196
|
+
topicKey: phase.topicKey,
|
|
197
|
+
present: phase.present,
|
|
198
|
+
accepted: phase.accepted,
|
|
199
|
+
legacy: phase.legacy,
|
|
200
|
+
state: phase.state,
|
|
201
|
+
readinessReady: phase.readiness.ready,
|
|
202
|
+
blockerCount: phase.blockers.length,
|
|
203
|
+
readiness: toReadinessMetadata(phase.readiness),
|
|
204
|
+
...(phase.artifact === undefined ? {} : { artifact: toArtifactMetadata(phase.artifact) }),
|
|
205
|
+
blockers: phase.blockers.map(toSddBlocker),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function toReadinessMetadata(readiness) {
|
|
209
|
+
return {
|
|
210
|
+
change: readiness.change,
|
|
211
|
+
phase: readiness.phase,
|
|
212
|
+
ready: readiness.ready,
|
|
213
|
+
satisfiedPrerequisites: readiness.satisfiedPrerequisites,
|
|
214
|
+
missingArtifactTopicKeys: readiness.missingArtifactTopicKeys,
|
|
215
|
+
blockedPrerequisites: (readiness.blockedPrerequisites ?? []).map(toPrerequisiteBlocker),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function toPrerequisiteBlocker(blocker) {
|
|
219
|
+
return {
|
|
220
|
+
phase: blocker.phase,
|
|
221
|
+
topicKey: blocker.topicKey,
|
|
222
|
+
reason: blocker.reason,
|
|
223
|
+
...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function toArtifactMetadata(artifact) {
|
|
227
|
+
return {
|
|
228
|
+
phase: artifact.phase,
|
|
229
|
+
topicKey: artifact.topicKey,
|
|
230
|
+
present: artifact.present,
|
|
231
|
+
accepted: artifact.accepted,
|
|
232
|
+
legacy: artifact.legacy,
|
|
233
|
+
state: artifact.state,
|
|
234
|
+
...(artifact.artifactId === undefined ? {} : { artifactId: artifact.artifactId }),
|
|
235
|
+
...(artifact.createdAt === undefined ? {} : { createdAt: artifact.createdAt }),
|
|
236
|
+
...(artifact.updatedAt === undefined ? {} : { updatedAt: artifact.updatedAt }),
|
|
237
|
+
...(artifact.acceptance === undefined ? {} : { acceptance: toAcceptanceMetadata(artifact.acceptance) }),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function toAcceptanceMetadata(acceptance) {
|
|
241
|
+
return {
|
|
242
|
+
actor: {
|
|
243
|
+
type: acceptance.actor.type,
|
|
244
|
+
id: acceptance.actor.id,
|
|
245
|
+
...(acceptance.actor.displayName === undefined ? {} : { displayName: acceptance.actor.displayName }),
|
|
246
|
+
},
|
|
247
|
+
acceptedAt: acceptance.acceptedAt,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function toSddBlocker(blocker) {
|
|
251
|
+
return {
|
|
252
|
+
kind: blocker.kind,
|
|
253
|
+
phase: blocker.phase,
|
|
254
|
+
topicKey: blocker.topicKey,
|
|
255
|
+
reason: blocker.reason,
|
|
256
|
+
...(blocker.artifactId === undefined ? {} : { artifactId: blocker.artifactId }),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function budgetIdForLevel(level) {
|
|
260
|
+
switch (level) {
|
|
261
|
+
case 'compact':
|
|
262
|
+
return 'snapshotCompact';
|
|
263
|
+
case 'expanded':
|
|
264
|
+
return 'snapshotExpanded';
|
|
265
|
+
case 'verbose':
|
|
266
|
+
return 'subagentVerbosePayload';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function omittedSections(legacy, required, resolved = []) {
|
|
270
|
+
const resolvedSet = new Set(resolved);
|
|
271
|
+
return [...new Set([...legacy, ...required])].filter((section) => !resolvedSet.has(section));
|
|
272
|
+
}
|