vgxness 1.20.4 → 1.20.6
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/adapters/claude/agent-renderer.js +82 -0
- package/dist/{mcp → adapters/claude/install}/claude-code-agent-config.js +64 -9
- package/dist/{mcp → adapters/claude/install}/claude-code-config.js +28 -6
- package/dist/{mcp → adapters/claude/install}/claude-code-project-memory.js +35 -5
- package/dist/{mcp → adapters/claude/install}/claude-code-scope.js +12 -3
- package/dist/{mcp → adapters/claude/install}/claude-code-user-config.js +26 -5
- package/dist/{mcp → adapters/claude/install}/claude-code-user-memory.js +47 -7
- package/dist/{mcp → adapters/claude/install}/client-install-claude-code-contract.js +45 -8
- package/dist/{mcp → adapters/claude/install}/client-install-claude-code.js +98 -14
- package/dist/adapters/json/agent-renderer.js +104 -0
- package/dist/adapters/opencode/agent-renderer.js +113 -0
- package/dist/{mcp → adapters/opencode/install}/client-install-opencode-contract.js +2 -2
- package/dist/{mcp → adapters/opencode/install}/client-install-opencode.js +1 -1
- package/dist/{mcp → adapters/opencode/install}/opencode-default-agent-config.js +2 -2
- package/dist/{mcp → adapters/opencode/install}/opencode-handoff-preview.js +60 -7
- package/dist/{mcp → adapters/opencode/install}/opencode-visibility.js +1 -1
- package/dist/adapters/provider-renderer.js +6 -0
- package/dist/agents/agent-activation-service.js +1 -263
- package/dist/agents/agent-lookup-contexts.js +1 -28
- package/dist/agents/agent-registry-service.js +1 -81
- package/dist/agents/agent-resolver.js +1 -287
- package/dist/agents/agent-seed-service.js +1 -157
- package/dist/agents/agent-seed-upgrade-service.js +1 -231
- package/dist/agents/agent-selector-resolver.js +1 -28
- package/dist/agents/boot-upgrade.js +1 -59
- package/dist/agents/canonical-agent-manifest.js +1 -245
- package/dist/agents/canonical-agent-projection.js +1 -236
- package/dist/agents/manager-profile-overlay-service.js +1 -60
- package/dist/agents/profile-model-routing.js +1 -30
- package/dist/agents/renderers/claude-renderer.js +1 -82
- package/dist/agents/renderers/index.js +1 -14
- package/dist/agents/renderers/json-renderer.js +1 -104
- package/dist/agents/renderers/opencode-renderer.js +1 -113
- package/dist/agents/renderers/provider-adapter.js +1 -6
- package/dist/agents/repositories/agent-seed-history.js +1 -128
- package/dist/agents/repositories/agents.js +1 -200
- package/dist/agents/repositories/manager-profile-overlays.js +1 -90
- package/dist/agents/schema.js +1 -1
- package/dist/application/agents/agent-activation-service.js +261 -0
- package/dist/application/agents/agent-lookup-contexts.js +28 -0
- package/dist/application/agents/agent-registry-service.js +81 -0
- package/dist/application/agents/agent-resolver.js +287 -0
- package/dist/application/agents/agent-seed-service.js +157 -0
- package/dist/application/agents/agent-seed-upgrade-service.js +231 -0
- package/dist/application/agents/agent-selector-resolver.js +28 -0
- package/dist/application/agents/boot-upgrade.js +59 -0
- package/dist/application/agents/canonical-agent-projection.js +236 -0
- package/dist/application/agents/manager-profile-overlay-service.js +68 -0
- package/dist/application/agents/profile-model-routing.js +30 -0
- package/dist/application/agents/renderers/claude-renderer.js +1 -0
- package/dist/application/agents/renderers/index.js +14 -0
- package/dist/application/agents/renderers/json-renderer.js +1 -0
- package/dist/application/agents/renderers/opencode-renderer.js +1 -0
- package/dist/application/agents/renderers/provider-adapter.js +1 -0
- package/dist/application/memory/active-work-preview.js +75 -0
- package/dist/application/memory/agent-memory-capture-service.js +498 -0
- package/dist/application/memory/import/dry-run-planner.js +58 -0
- package/dist/application/memory/import/index.js +3 -0
- package/dist/application/memory/import/observation-writer.js +226 -0
- package/dist/application/memory/import/package.js +179 -0
- package/dist/application/memory/memory-service.js +202 -0
- package/dist/application/memory/search.js +11 -0
- package/dist/{mcp → application/provider-setup}/provider-change-plan.js +74 -13
- package/dist/{mcp → application/provider-setup}/provider-doctor.js +201 -31
- package/dist/{mcp → application/provider-setup}/provider-health-types.js +1 -1
- package/dist/{mcp → application/provider-setup}/provider-status.js +170 -33
- package/dist/application/runs/execution-planning.js +180 -0
- package/dist/application/runs/operation-execution.js +1 -0
- package/dist/application/runs/operation-retry.js +129 -0
- package/dist/application/runs/resume-after-approval-result.js +88 -0
- package/dist/application/runs/run-insights.js +270 -0
- package/dist/application/runs/run-service.js +1480 -0
- package/dist/application/runs/run-snapshot-export-service.js +31 -0
- package/dist/application/runs/sandbox-process-execution.js +244 -0
- package/dist/application/runs/sandbox-worktree-planning.js +59 -0
- package/dist/application/runs/task-scoped-command-grant-service.js +224 -0
- package/dist/application/sdd/artifact-portability-service.js +137 -0
- package/dist/application/sdd/cockpit-read-model.js +195 -0
- package/dist/application/sdd/sdd-continuation-plan.js +265 -0
- package/dist/application/sdd/sdd-workflow-service.js +892 -0
- package/dist/application/skills/boot-seed.js +42 -0
- package/dist/application/skills/personal-skills.js +41 -0
- package/dist/application/skills/skill-export-service.js +34 -0
- package/dist/application/skills/skill-index-service.js +126 -0
- package/dist/application/skills/skill-payload.js +186 -0
- package/dist/application/skills/skill-registry-service.js +648 -0
- package/dist/application/skills/skill-resolver.js +368 -0
- package/dist/application/skills/skill-seed-service.js +242 -0
- package/dist/application/skills/skill-status-service.js +147 -0
- package/dist/cli/bun-bin.js +0 -0
- package/dist/cli/cli-flags.js +1 -387
- package/dist/cli/cli-help.js +1 -173
- package/dist/cli/cli-helpers.js +1 -77
- package/dist/cli/cli-types.js +1 -1
- package/dist/cli/commands/agent-skill-dispatcher.js +1 -700
- package/dist/cli/commands/index.js +1 -9
- package/dist/cli/commands/interactive-entrypoint-dispatcher.js +1 -14
- package/dist/cli/commands/mcp-dispatcher.js +1 -232
- package/dist/cli/commands/memory-sdd-dispatcher.js +1 -367
- package/dist/cli/commands/run-permission-dispatcher.js +1 -455
- package/dist/cli/commands/setup-dispatcher.js +1 -496
- package/dist/cli/commands/status-dispatcher.js +1 -130
- package/dist/cli/commands/verification-dispatcher.js +1 -47
- package/dist/cli/commands/workflow-dispatcher.js +1 -566
- package/dist/cli/dispatcher.js +1 -247
- package/dist/cli/doctor-renderer.js +1 -26
- package/dist/cli/home-tui-app.js +1 -411
- package/dist/cli/home-tui-controller.js +1 -314
- package/dist/cli/mcp-start-path.js +1 -21
- package/dist/cli/product-resume-renderer.js +1 -32
- package/dist/cli/product-status-renderer.js +1 -81
- package/dist/cli/sdd-renderer.js +1 -247
- package/dist/cli/setup-plan-renderer.js +1 -79
- package/dist/cli/setup-status-renderer.js +1 -50
- package/dist/cli/setup-tui-app.js +1 -130
- package/dist/cli/setup-tui-controller.js +1 -89
- package/dist/cli/tui/focus-reducer.js +1 -0
- package/dist/cli/tui/focus-types.js +1 -0
- package/dist/cli/tui/ink/app-shell.js +1 -0
- package/dist/cli/tui/ink/components.js +1 -38
- package/dist/cli/tui/ink/help-overlay.js +1 -0
- package/dist/cli/tui/ink/state-components.js +1 -0
- package/dist/cli/tui/ink/theme.js +1 -36
- package/dist/cli/tui/key-intents.js +1 -0
- package/dist/cli/tui/keymap.js +1 -20
- package/dist/cli/tui/navigation.js +1 -10
- package/dist/cli/tui/screen-registry.js +1 -0
- package/dist/cli/tui/terminal-capabilities.js +1 -12
- package/dist/cli/tui/tui-types.js +1 -1
- package/dist/cli/tui/visual/badges.js +1 -19
- package/dist/cli/tui/visual/footer.js +1 -27
- package/dist/cli/tui/visual/index.js +1 -3
- package/dist/cli/tui/visual/viewport.js +1 -11
- package/dist/cli/tui-render-helpers.js +1 -200
- package/dist/cli/verification-plan-renderer.js +1 -25
- package/dist/cli/verification-report-renderer.js +1 -19
- package/dist/domain/agents/canonical-agent-manifest.js +355 -0
- package/dist/domain/agents/schema.js +1 -0
- package/dist/domain/memory/active-work-topics.js +64 -0
- package/dist/domain/memory/memory-capture-schema.js +1 -0
- package/dist/domain/memory/schema.js +1 -0
- package/dist/domain/runs/schema.js +5 -0
- package/dist/domain/sdd/cockpit-types.js +1 -0
- package/dist/domain/sdd/schema.js +111 -0
- package/dist/domain/skills/schema.js +1 -0
- package/dist/governance/risk-classifier.js +2 -0
- package/dist/interfaces/cli/bun-bin.js +46 -0
- package/dist/interfaces/cli/cli-flags.js +391 -0
- package/dist/interfaces/cli/cli-help.js +173 -0
- package/dist/interfaces/cli/cli-helpers.js +77 -0
- package/dist/interfaces/cli/cli-types.js +1 -0
- package/dist/interfaces/cli/commands/agent-skill-dispatcher.js +700 -0
- package/dist/interfaces/cli/commands/index.js +9 -0
- package/dist/interfaces/cli/commands/interactive-entrypoint-dispatcher.js +14 -0
- package/dist/interfaces/cli/commands/mcp-dispatcher.js +252 -0
- package/dist/interfaces/cli/commands/memory-sdd-dispatcher.js +367 -0
- package/dist/interfaces/cli/commands/run-permission-dispatcher.js +460 -0
- package/dist/interfaces/cli/commands/setup-dispatcher.js +496 -0
- package/dist/interfaces/cli/commands/status-dispatcher.js +130 -0
- package/dist/interfaces/cli/commands/verification-dispatcher.js +47 -0
- package/dist/interfaces/cli/commands/workflow-dispatcher.js +566 -0
- package/dist/interfaces/cli/dispatcher.js +247 -0
- package/dist/interfaces/cli/doctor-renderer.js +26 -0
- package/dist/interfaces/cli/home-tui-app.js +462 -0
- package/dist/interfaces/cli/home-tui-controller.js +341 -0
- package/dist/interfaces/cli/index.js +27 -0
- package/dist/interfaces/cli/mcp-start-path.js +21 -0
- package/dist/interfaces/cli/product-resume-renderer.js +32 -0
- package/dist/interfaces/cli/product-status-renderer.js +81 -0
- package/dist/interfaces/cli/sdd-renderer.js +247 -0
- package/dist/interfaces/cli/setup-plan-renderer.js +79 -0
- package/dist/interfaces/cli/setup-status-renderer.js +50 -0
- package/dist/interfaces/cli/setup-tui-app.js +135 -0
- package/dist/interfaces/cli/setup-tui-controller.js +89 -0
- package/dist/interfaces/cli/tui/focus-reducer.js +60 -0
- package/dist/interfaces/cli/tui/focus-types.js +10 -0
- package/dist/interfaces/cli/tui/ink/app-shell.js +16 -0
- package/dist/interfaces/cli/tui/ink/components.js +38 -0
- package/dist/interfaces/cli/tui/ink/help-overlay.js +6 -0
- package/dist/interfaces/cli/tui/ink/state-components.js +22 -0
- package/dist/interfaces/cli/tui/ink/theme.js +36 -0
- package/dist/interfaces/cli/tui/key-intents.js +33 -0
- package/dist/interfaces/cli/tui/keymap.js +20 -0
- package/dist/interfaces/cli/tui/navigation.js +10 -0
- package/dist/interfaces/cli/tui/screen-registry.js +43 -0
- package/dist/interfaces/cli/tui/terminal-capabilities.js +12 -0
- package/dist/interfaces/cli/tui/tui-types.js +1 -0
- package/dist/interfaces/cli/tui/visual/badges.js +19 -0
- package/dist/interfaces/cli/tui/visual/footer.js +27 -0
- package/dist/interfaces/cli/tui/visual/index.js +3 -0
- package/dist/interfaces/cli/tui/visual/viewport.js +11 -0
- package/dist/interfaces/cli/tui-render-helpers.js +200 -0
- package/dist/interfaces/cli/verification-plan-renderer.js +25 -0
- package/dist/interfaces/cli/verification-report-renderer.js +19 -0
- package/dist/interfaces/mcp/contracts/agents.js +1 -0
- package/dist/interfaces/mcp/contracts/context.js +1 -0
- package/dist/interfaces/mcp/contracts/governance.js +1 -0
- package/dist/interfaces/mcp/contracts/index.js +1 -0
- package/dist/interfaces/mcp/contracts/memory.js +1 -0
- package/dist/interfaces/mcp/contracts/providers.js +1 -0
- package/dist/interfaces/mcp/contracts/runs.js +1 -0
- package/dist/interfaces/mcp/contracts/sdd.js +1 -0
- package/dist/interfaces/mcp/contracts/sessions.js +1 -0
- package/dist/interfaces/mcp/contracts/skills.js +1 -0
- package/dist/interfaces/mcp/contracts/verification.js +1 -0
- package/dist/interfaces/mcp/control-plane/handlers/agents.js +38 -0
- package/dist/interfaces/mcp/control-plane/handlers/context.js +12 -0
- package/dist/interfaces/mcp/control-plane/handlers/memory.js +95 -0
- package/dist/interfaces/mcp/control-plane/handlers/providers.js +19 -0
- package/dist/interfaces/mcp/control-plane/handlers/runs.js +134 -0
- package/dist/interfaces/mcp/control-plane/handlers/sdd.js +225 -0
- package/dist/interfaces/mcp/control-plane/handlers/sessions.js +39 -0
- package/dist/interfaces/mcp/control-plane/handlers/skills.js +82 -0
- package/dist/interfaces/mcp/control-plane/handlers/verification.js +11 -0
- package/dist/interfaces/mcp/control-plane/types.js +1 -0
- package/dist/interfaces/mcp/control-plane.js +235 -0
- package/dist/interfaces/mcp/schema.js +174 -0
- package/dist/interfaces/mcp/schemas/common.js +121 -0
- package/dist/interfaces/mcp/schemas/inputs/agents.js +69 -0
- package/dist/interfaces/mcp/schemas/inputs/context.js +32 -0
- package/dist/interfaces/mcp/schemas/inputs/governance.js +23 -0
- package/dist/interfaces/mcp/schemas/inputs/index.js +10 -0
- package/dist/interfaces/mcp/schemas/inputs/memory.js +50 -0
- package/dist/interfaces/mcp/schemas/inputs/providers.js +35 -0
- package/dist/interfaces/mcp/schemas/inputs/runs.js +146 -0
- package/dist/interfaces/mcp/schemas/inputs/sdd.js +102 -0
- package/dist/interfaces/mcp/schemas/inputs/sessions.js +32 -0
- package/dist/interfaces/mcp/schemas/inputs/skills.js +231 -0
- package/dist/interfaces/mcp/schemas/inputs/verification.js +9 -0
- package/dist/interfaces/mcp/schemas/outputs/index.js +2 -0
- package/dist/interfaces/mcp/schemas/outputs/providers.js +28 -0
- package/dist/interfaces/mcp/schemas/outputs/sdd.js +166 -0
- package/dist/interfaces/mcp/stdio-server.js +116 -0
- package/dist/interfaces/mcp/validation/agents.js +189 -0
- package/dist/interfaces/mcp/validation/common.js +415 -0
- package/dist/interfaces/mcp/validation/governance.js +50 -0
- package/dist/interfaces/mcp/validation/memory.js +133 -0
- package/dist/interfaces/mcp/validation/providers.js +94 -0
- package/dist/interfaces/mcp/validation/runs.js +514 -0
- package/dist/interfaces/mcp/validation/sdd.js +247 -0
- package/dist/interfaces/mcp/validation/sessions.js +51 -0
- package/dist/interfaces/mcp/validation/skills.js +685 -0
- package/dist/interfaces/mcp/validation/verification.js +24 -0
- package/dist/interfaces/mcp/validation.js +149 -0
- package/dist/mcp/control-plane-snapshot-service.js +65 -2
- package/dist/mcp/control-plane.js +1 -646
- package/dist/mcp/doctor.js +1 -1
- package/dist/mcp/index.js +17 -17
- package/dist/mcp/schema.js +1 -1072
- package/dist/mcp/stdio-server.js +1 -116
- package/dist/mcp/validation.js +1 -2239
- package/dist/memory/active-work-preview.js +1 -75
- package/dist/memory/active-work-topics.js +1 -64
- package/dist/memory/agent-memory-capture-service.js +1 -497
- package/dist/memory/import/dry-run-planner.js +1 -58
- package/dist/memory/import/index.js +1 -3
- package/dist/memory/import/observation-writer.js +1 -226
- package/dist/memory/import/package.js +1 -179
- package/dist/memory/memory-capture-schema.js +1 -1
- package/dist/memory/memory-service.js +1 -201
- package/dist/memory/repositories/artifacts.js +1 -67
- package/dist/memory/repositories/observations.js +1 -217
- package/dist/memory/repositories/sessions.js +1 -125
- package/dist/memory/repositories/traces.js +1 -78
- package/dist/memory/schema.js +1 -1
- package/dist/memory/search.js +1 -11
- package/dist/memory/sqlite/database.js +1 -228
- package/dist/memory/storage-paths.js +1 -72
- package/dist/payload/context-budget-policy.js +10 -0
- package/dist/payload/context-budget-service.js +26 -0
- package/dist/payload/payload-diagnostics.js +50 -0
- package/dist/permissions/explicit-request-authorization.js +126 -0
- package/dist/permissions/policy-evaluator.js +54 -14
- package/dist/runs/execution-planning.js +1 -178
- package/dist/runs/operation-execution.js +1 -1
- package/dist/runs/operation-retry.js +1 -129
- package/dist/runs/repositories/runs.js +1 -676
- package/dist/runs/repositories/task-scoped-command-grants.js +1 -462
- package/dist/runs/resume-after-approval-result.js +1 -84
- package/dist/runs/run-insights.js +1 -270
- package/dist/runs/run-service.js +1 -1439
- package/dist/runs/run-snapshot-export-service.js +1 -31
- package/dist/runs/sandbox-process-execution.js +1 -240
- package/dist/runs/sandbox-worktree-planning.js +1 -59
- package/dist/runs/schema.js +1 -5
- package/dist/runs/task-scoped-command-grant-service.js +1 -216
- package/dist/sdd/artifact-portability-service.js +1 -137
- package/dist/sdd/cockpit-read-model.js +1 -195
- package/dist/sdd/cockpit-types.js +1 -1
- package/dist/sdd/schema.js +1 -111
- package/dist/sdd/sdd-continuation-plan.js +1 -265
- package/dist/sdd/sdd-workflow-service.js +1 -822
- package/dist/setup/providers/claude-setup-adapter.js +11 -4
- package/dist/setup/providers/opencode-setup-adapter.js +10 -7
- package/dist/setup/setup-plan.js +20 -7
- package/dist/skills/boot-seed.js +1 -42
- package/dist/skills/personal-skills.js +1 -41
- package/dist/skills/repositories/skill-evaluation-requests.js +1 -190
- package/dist/skills/repositories/skill-evaluation-scenarios.js +1 -305
- package/dist/skills/repositories/skill-improvement-proposals.js +1 -355
- package/dist/skills/repositories/skills.js +1 -1077
- package/dist/skills/schema.js +1 -1
- package/dist/skills/skill-export-service.js +1 -34
- package/dist/skills/skill-index-service.js +1 -126
- package/dist/skills/skill-payload.js +1 -186
- package/dist/skills/skill-registry-service.js +1 -644
- package/dist/skills/skill-resolver.js +1 -368
- package/dist/skills/skill-seed-service.js +1 -242
- package/dist/skills/skill-status-service.js +1 -147
- package/dist/storage/agents/repositories/agent-seed-history.js +128 -0
- package/dist/storage/agents/repositories/agents.js +200 -0
- package/dist/storage/agents/repositories/manager-profile-overlays.js +90 -0
- package/dist/storage/memory/repositories/artifacts.js +67 -0
- package/dist/storage/memory/repositories/observations.js +217 -0
- package/dist/storage/memory/repositories/sessions.js +125 -0
- package/dist/storage/memory/repositories/traces.js +78 -0
- package/dist/storage/memory/sqlite/database.js +228 -0
- package/dist/storage/memory/storage-paths.js +72 -0
- package/dist/storage/runs/repositories/runs.js +676 -0
- package/dist/storage/runs/repositories/task-scoped-command-grants.js +464 -0
- package/dist/storage/skills/repositories/skill-evaluation-requests.js +190 -0
- package/dist/storage/skills/repositories/skill-evaluation-scenarios.js +305 -0
- package/dist/storage/skills/repositories/skill-improvement-proposals.js +355 -0
- package/dist/storage/skills/repositories/skills.js +1084 -0
- package/docs/architecture.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/mcp.md +1 -1
- package/docs/module-boundaries.md +114 -0
- package/docs/prd.md +1 -1
- package/docs/project-health-audit-v1.10.x.md +1 -1
- package/docs/providers.md +2 -2
- package/docs/safety.md +3 -3
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/apply-progress.md +122 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/archive-report.md +61 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/design.md +66 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/proposal.md +79 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/spec.md +67 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/tasks.md +63 -0
- package/docs/sdd/archive/2026-06-26-ambitious-structure-reorganization/verify-report.md +71 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/apply-progress.md +68 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/archive-report.md +61 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/design.md +43 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/proposal.md +61 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/spec.md +31 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/tasks.md +27 -0
- package/docs/sdd/archive/2026-06-26-legacy-facade-cleanup/verify-report.md +58 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/apply-progress.md +65 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/archive-report.md +53 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/design.md +64 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/proposal.md +62 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/spec.md +51 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/tasks.md +50 -0
- package/docs/sdd/archive/2026-06-26-provider-install-adapter-boundary/verify-report.md +66 -0
- package/docs/storage.md +2 -2
- package/package.json +2 -2
- /package/dist/{mcp → adapters/claude/install}/claude-code-cli.js +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/001_initial.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/002_observation_revisions.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/003_agent_registry.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/004_run_runtime.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/005_run_approvals.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/006_run_operation_attempts.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/007_abandoned_operation_attempts.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/008_run_execution_plan_events.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/009_multiple_operation_attempts.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/010_skill_registry.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/011_skill_usage_resolution_outcomes.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/012_skill_improvement_proposals.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/013_skill_evaluation_scenarios.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/014_manager_profile_overlays.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/015_artifact_metadata.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/016_agent_seed_history.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/017_intent_signal_skill_targets.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/018_skill_system_v2.sql +0 -0
- /package/dist/{memory → storage/memory}/sqlite/migrations/019_task_scoped_command_grants.sql +0 -0
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
export class SkillRepository {
|
|
3
|
+
db;
|
|
4
|
+
constructor(db) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
}
|
|
7
|
+
register(input) {
|
|
8
|
+
const validation = validateSkill(input);
|
|
9
|
+
if (!validation.ok)
|
|
10
|
+
return validation;
|
|
11
|
+
const result = this.db.transaction(() => {
|
|
12
|
+
const existing = this.findByNaturalKey(input.project, input.scope, input.name);
|
|
13
|
+
const id = existing?.id ?? randomUUID();
|
|
14
|
+
const now = new Date().toISOString();
|
|
15
|
+
this.db.connection
|
|
16
|
+
.prepare(`
|
|
17
|
+
INSERT INTO skills(id, project, scope, name, description, created_at, updated_at)
|
|
18
|
+
VALUES (@id, @project, @scope, @name, @description, @createdAt, @updatedAt)
|
|
19
|
+
ON CONFLICT(project, scope, name) DO UPDATE SET
|
|
20
|
+
description=excluded.description,
|
|
21
|
+
updated_at=excluded.updated_at
|
|
22
|
+
`)
|
|
23
|
+
.run({
|
|
24
|
+
id,
|
|
25
|
+
project: input.project,
|
|
26
|
+
scope: input.scope,
|
|
27
|
+
name: input.name,
|
|
28
|
+
description: input.description,
|
|
29
|
+
createdAt: existing?.created_at ?? now,
|
|
30
|
+
updatedAt: now,
|
|
31
|
+
});
|
|
32
|
+
const read = this.getById(id);
|
|
33
|
+
if (!read.ok)
|
|
34
|
+
throw new Error(read.error.message);
|
|
35
|
+
return read.value;
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
addVersion(input) {
|
|
40
|
+
const validation = validateVersion(input);
|
|
41
|
+
if (!validation.ok)
|
|
42
|
+
return validation;
|
|
43
|
+
const result = this.db.transaction(() => {
|
|
44
|
+
const skill = this.getById(input.skillId);
|
|
45
|
+
if (!skill.ok)
|
|
46
|
+
throw new SkillRegistryValidationError(skill.error.message);
|
|
47
|
+
const existing = this.findVersion(input.skillId, input.version);
|
|
48
|
+
const id = existing?.id ?? randomUUID();
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
const status = input.activate === true ? 'active' : (input.status ?? existing?.status ?? 'draft');
|
|
51
|
+
this.db.connection
|
|
52
|
+
.prepare(`
|
|
53
|
+
INSERT INTO skill_versions(id, skill_id, version, source_kind, source_path, source_url, source_inline_metadata_json, compatibility_json, status, created_at)
|
|
54
|
+
VALUES (@id, @skillId, @version, @sourceKind, @sourcePath, @sourceUrl, @sourceInlineMetadataJson, @compatibilityJson, @status, @createdAt)
|
|
55
|
+
ON CONFLICT(skill_id, version) DO UPDATE SET
|
|
56
|
+
source_kind=excluded.source_kind,
|
|
57
|
+
source_path=excluded.source_path,
|
|
58
|
+
source_url=excluded.source_url,
|
|
59
|
+
source_inline_metadata_json=excluded.source_inline_metadata_json,
|
|
60
|
+
compatibility_json=excluded.compatibility_json,
|
|
61
|
+
status=excluded.status
|
|
62
|
+
`)
|
|
63
|
+
.run({
|
|
64
|
+
id,
|
|
65
|
+
skillId: input.skillId,
|
|
66
|
+
version: input.version,
|
|
67
|
+
sourceKind: input.source.kind,
|
|
68
|
+
sourcePath: input.source.path ?? null,
|
|
69
|
+
sourceUrl: input.source.url ?? null,
|
|
70
|
+
sourceInlineMetadataJson: JSON.stringify(input.source.inlineMetadata ?? {}),
|
|
71
|
+
compatibilityJson: JSON.stringify(input.compatibility ?? {}),
|
|
72
|
+
status,
|
|
73
|
+
createdAt: existing?.created_at ?? now,
|
|
74
|
+
});
|
|
75
|
+
if (input.activate === true) {
|
|
76
|
+
this.db.connection.prepare('UPDATE skills SET current_version_id=?, updated_at=? WHERE id=?').run(id, now, input.skillId);
|
|
77
|
+
}
|
|
78
|
+
const read = this.getVersion(id);
|
|
79
|
+
if (!read.ok)
|
|
80
|
+
throw new Error(read.error.message);
|
|
81
|
+
return read.value;
|
|
82
|
+
});
|
|
83
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError) {
|
|
84
|
+
return validationFailure(result.error.cause.message);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
attach(input) {
|
|
89
|
+
const validation = validateAttachment(input);
|
|
90
|
+
if (!validation.ok)
|
|
91
|
+
return validation;
|
|
92
|
+
const result = this.db.transaction(() => {
|
|
93
|
+
const skill = this.getById(input.skillId);
|
|
94
|
+
if (!skill.ok)
|
|
95
|
+
throw new SkillRegistryValidationError(skill.error.message);
|
|
96
|
+
if (input.versionId !== undefined)
|
|
97
|
+
assertVersionBelongsToSkill(this.db, input.versionId, input.skillId);
|
|
98
|
+
const existing = this.db.connection
|
|
99
|
+
.prepare('SELECT * FROM skill_attachments WHERE skill_id=? AND target_type=? AND target_key=?')
|
|
100
|
+
.get(input.skillId, input.targetType, input.targetKey);
|
|
101
|
+
const id = existing?.id ?? randomUUID();
|
|
102
|
+
const now = new Date().toISOString();
|
|
103
|
+
this.db.connection
|
|
104
|
+
.prepare(`
|
|
105
|
+
INSERT INTO skill_attachments(id, skill_id, version_id, target_type, target_key, metadata_json, created_at)
|
|
106
|
+
VALUES (@id, @skillId, @versionId, @targetType, @targetKey, @metadataJson, @createdAt)
|
|
107
|
+
ON CONFLICT(skill_id, target_type, target_key) DO UPDATE SET
|
|
108
|
+
version_id=excluded.version_id,
|
|
109
|
+
metadata_json=excluded.metadata_json
|
|
110
|
+
`)
|
|
111
|
+
.run({
|
|
112
|
+
id,
|
|
113
|
+
skillId: input.skillId,
|
|
114
|
+
versionId: input.versionId ?? null,
|
|
115
|
+
targetType: input.targetType,
|
|
116
|
+
targetKey: input.targetKey,
|
|
117
|
+
metadataJson: JSON.stringify(input.metadata ?? {}),
|
|
118
|
+
createdAt: existing?.created_at ?? now,
|
|
119
|
+
});
|
|
120
|
+
const read = this.getAttachment(id);
|
|
121
|
+
if (!read.ok)
|
|
122
|
+
throw new Error(read.error.message);
|
|
123
|
+
return read.value;
|
|
124
|
+
});
|
|
125
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError) {
|
|
126
|
+
return validationFailure(result.error.cause.message);
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
detach(skillId, targetType, targetKey) {
|
|
131
|
+
try {
|
|
132
|
+
const result = this.db.connection
|
|
133
|
+
.prepare('DELETE FROM skill_attachments WHERE skill_id=? AND target_type=? AND target_key=?')
|
|
134
|
+
.run(skillId, targetType, targetKey);
|
|
135
|
+
return ok({ detached: result.changes > 0 });
|
|
136
|
+
}
|
|
137
|
+
catch (cause) {
|
|
138
|
+
return fail('Failed to detach skill', cause);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
recordUsage(input) {
|
|
142
|
+
const validation = validateUsage(input);
|
|
143
|
+
if (!validation.ok)
|
|
144
|
+
return validation;
|
|
145
|
+
const result = this.db.transaction(() => {
|
|
146
|
+
const skill = this.getById(input.skillId);
|
|
147
|
+
if (!skill.ok)
|
|
148
|
+
throw new SkillRegistryValidationError(skill.error.message);
|
|
149
|
+
if (input.versionId !== undefined)
|
|
150
|
+
assertVersionBelongsToSkill(this.db, input.versionId, input.skillId);
|
|
151
|
+
const id = randomUUID();
|
|
152
|
+
const now = new Date().toISOString();
|
|
153
|
+
this.db.connection
|
|
154
|
+
.prepare(`
|
|
155
|
+
INSERT INTO skill_usage_records(id, skill_id, version_id, run_id, target_type, target_key, outcome, notes, created_at, workflow, phase, provider_adapter, agent_id, intent_json, evidence_json)
|
|
156
|
+
VALUES (@id, @skillId, @versionId, @runId, @targetType, @targetKey, @outcome, @notes, @createdAt, @workflow, @phase, @providerAdapter, @agentId, @intentJson, @evidenceJson)
|
|
157
|
+
`)
|
|
158
|
+
.run({
|
|
159
|
+
id,
|
|
160
|
+
skillId: input.skillId,
|
|
161
|
+
versionId: input.versionId ?? null,
|
|
162
|
+
runId: input.runId ?? null,
|
|
163
|
+
targetType: input.targetType ?? null,
|
|
164
|
+
targetKey: input.targetKey ?? null,
|
|
165
|
+
outcome: input.outcome,
|
|
166
|
+
notes: input.notes ?? null,
|
|
167
|
+
createdAt: now,
|
|
168
|
+
workflow: input.workflow ?? null,
|
|
169
|
+
phase: input.phase ?? null,
|
|
170
|
+
providerAdapter: input.providerAdapter ?? null,
|
|
171
|
+
agentId: input.agentId ?? null,
|
|
172
|
+
intentJson: input.intent === undefined ? null : JSON.stringify(input.intent),
|
|
173
|
+
evidenceJson: input.evidence === undefined ? null : JSON.stringify(input.evidence),
|
|
174
|
+
});
|
|
175
|
+
const read = this.getUsage(id);
|
|
176
|
+
if (!read.ok)
|
|
177
|
+
throw new Error(read.error.message);
|
|
178
|
+
return read.value;
|
|
179
|
+
});
|
|
180
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError) {
|
|
181
|
+
return validationFailure(result.error.cause.message);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
recordActivation(input) {
|
|
186
|
+
const validation = validateActivation(input);
|
|
187
|
+
if (!validation.ok)
|
|
188
|
+
return validation;
|
|
189
|
+
const result = this.db.transaction(() => {
|
|
190
|
+
const skill = this.getById(input.skillId);
|
|
191
|
+
if (!skill.ok)
|
|
192
|
+
throw new SkillRegistryValidationError(skill.error.message);
|
|
193
|
+
if (input.versionId !== undefined)
|
|
194
|
+
assertVersionBelongsToSkill(this.db, input.versionId, input.skillId);
|
|
195
|
+
if (skill.value.project !== input.project || skill.value.scope !== input.scope)
|
|
196
|
+
throw new SkillRegistryValidationError('Skill does not belong to the requested project/scope');
|
|
197
|
+
const id = randomUUID();
|
|
198
|
+
const now = new Date().toISOString();
|
|
199
|
+
this.db.connection
|
|
200
|
+
.prepare(`
|
|
201
|
+
INSERT INTO skill_activation_records(id, project, scope, skill_id, version_id, run_id, agent_id, workflow, phase, provider_adapter, intent_json, reason, payload_metadata_json, created_at)
|
|
202
|
+
VALUES (@id, @project, @scope, @skillId, @versionId, @runId, @agentId, @workflow, @phase, @providerAdapter, @intentJson, @reason, @payloadMetadataJson, @createdAt)
|
|
203
|
+
`)
|
|
204
|
+
.run({
|
|
205
|
+
id,
|
|
206
|
+
project: input.project,
|
|
207
|
+
scope: input.scope,
|
|
208
|
+
skillId: input.skillId,
|
|
209
|
+
versionId: input.versionId ?? null,
|
|
210
|
+
runId: input.runId ?? null,
|
|
211
|
+
agentId: input.agentId ?? null,
|
|
212
|
+
workflow: input.workflow ?? null,
|
|
213
|
+
phase: input.phase ?? null,
|
|
214
|
+
providerAdapter: input.providerAdapter ?? null,
|
|
215
|
+
intentJson: input.intent === undefined ? null : JSON.stringify(input.intent),
|
|
216
|
+
reason: input.reason ?? null,
|
|
217
|
+
payloadMetadataJson: JSON.stringify(input.payloadMetadata ?? {}),
|
|
218
|
+
createdAt: now,
|
|
219
|
+
});
|
|
220
|
+
const read = this.getActivation(id);
|
|
221
|
+
if (!read.ok)
|
|
222
|
+
throw new Error(read.error.message);
|
|
223
|
+
return read.value;
|
|
224
|
+
});
|
|
225
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError) {
|
|
226
|
+
return validationFailure(result.error.cause.message);
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
createDraft(input) {
|
|
231
|
+
const validation = validateCreateDraft(input);
|
|
232
|
+
if (!validation.ok)
|
|
233
|
+
return validation;
|
|
234
|
+
const result = this.db.transaction(() => {
|
|
235
|
+
const existing = this.findByNaturalKey(input.project, input.scope ?? 'project', input.name);
|
|
236
|
+
const skill = existing === undefined
|
|
237
|
+
? this.register({ project: input.project, scope: input.scope ?? 'project', name: input.name, description: input.description })
|
|
238
|
+
: ok(mapSkill(existing));
|
|
239
|
+
if (!skill.ok)
|
|
240
|
+
throw new SkillRegistryValidationError(skill.error.message);
|
|
241
|
+
if (input.draftParentVersionId !== undefined)
|
|
242
|
+
assertVersionBelongsToSkill(this.db, input.draftParentVersionId, skill.value.id);
|
|
243
|
+
if (this.findVersion(skill.value.id, input.version) !== undefined)
|
|
244
|
+
throw new SkillRegistryValidationError(`Skill version already exists: ${input.version}`);
|
|
245
|
+
const now = new Date().toISOString();
|
|
246
|
+
const id = randomUUID();
|
|
247
|
+
this.db.connection
|
|
248
|
+
.prepare(`
|
|
249
|
+
INSERT INTO skill_versions(id, skill_id, version, source_kind, source_path, source_url, source_inline_metadata_json, compatibility_json, status, created_at, lifecycle_label, draft_parent_version_id, draft_revision, governance_json)
|
|
250
|
+
VALUES (@id, @skillId, @version, @sourceKind, @sourcePath, @sourceUrl, @sourceInlineMetadataJson, @compatibilityJson, 'draft', @createdAt, 'draft', @draftParentVersionId, 1, @governanceJson)
|
|
251
|
+
`)
|
|
252
|
+
.run({
|
|
253
|
+
id,
|
|
254
|
+
skillId: skill.value.id,
|
|
255
|
+
version: input.version,
|
|
256
|
+
sourceKind: input.source.kind,
|
|
257
|
+
sourcePath: input.source.path ?? null,
|
|
258
|
+
sourceUrl: input.source.url ?? null,
|
|
259
|
+
sourceInlineMetadataJson: JSON.stringify(input.source.inlineMetadata ?? {}),
|
|
260
|
+
compatibilityJson: JSON.stringify(input.compatibility ?? {}),
|
|
261
|
+
createdAt: now,
|
|
262
|
+
draftParentVersionId: input.draftParentVersionId ?? null,
|
|
263
|
+
governanceJson: JSON.stringify(input.governance ?? {}),
|
|
264
|
+
});
|
|
265
|
+
const version = this.getVersion(id);
|
|
266
|
+
if (!version.ok)
|
|
267
|
+
throw new Error(version.error.message);
|
|
268
|
+
const after = versionSnapshot(version.value);
|
|
269
|
+
const event = this.recordLifecycleEvent({
|
|
270
|
+
project: skill.value.project,
|
|
271
|
+
scope: skill.value.scope,
|
|
272
|
+
skillId: skill.value.id,
|
|
273
|
+
versionId: version.value.id,
|
|
274
|
+
eventType: 'draft-created',
|
|
275
|
+
actor: input.actor,
|
|
276
|
+
...(input.runId === undefined ? {} : { runId: input.runId }),
|
|
277
|
+
...(input.agentId === undefined ? {} : { agentId: input.agentId }),
|
|
278
|
+
...(input.reason === undefined ? {} : { reason: input.reason }),
|
|
279
|
+
before: existing === undefined ? {} : { skillExisted: true, currentVersionId: existing.current_version_id ?? null },
|
|
280
|
+
after,
|
|
281
|
+
...(input.evidence === undefined ? {} : { evidence: input.evidence }),
|
|
282
|
+
});
|
|
283
|
+
if (!event.ok)
|
|
284
|
+
throw new Error(event.error.message);
|
|
285
|
+
return { skill: skill.value, version: version.value, event: event.value };
|
|
286
|
+
});
|
|
287
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError)
|
|
288
|
+
return validationFailure(result.error.cause.message);
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
updateDraft(input) {
|
|
292
|
+
const validation = validateUpdateDraft(input);
|
|
293
|
+
if (!validation.ok)
|
|
294
|
+
return validation;
|
|
295
|
+
const result = this.db.transaction(() => {
|
|
296
|
+
const skill = resolveSkillForMutation(this, input.project, input.scope ?? 'project', input.skillId, input.name);
|
|
297
|
+
const version = resolveVersionForMutation(this, skill.id, input.versionId, input.version);
|
|
298
|
+
if (version.status !== 'draft')
|
|
299
|
+
throw new SkillRegistryValidationError(`Cannot update ${version.status} skill versions; only draft versions can be updated`);
|
|
300
|
+
if (input.expectedDraftRevision !== undefined && (version.draftRevision ?? 0) !== input.expectedDraftRevision) {
|
|
301
|
+
throw new SkillRegistryValidationError(`Draft revision conflict: expected ${input.expectedDraftRevision}, found ${version.draftRevision ?? 0}`);
|
|
302
|
+
}
|
|
303
|
+
const before = versionSnapshot(version);
|
|
304
|
+
const nextRevision = (version.draftRevision ?? 0) + 1;
|
|
305
|
+
this.db.connection
|
|
306
|
+
.prepare(`
|
|
307
|
+
UPDATE skill_versions SET
|
|
308
|
+
source_kind=COALESCE(@sourceKind, source_kind),
|
|
309
|
+
source_path=@sourcePath,
|
|
310
|
+
source_url=@sourceUrl,
|
|
311
|
+
source_inline_metadata_json=COALESCE(@sourceInlineMetadataJson, source_inline_metadata_json),
|
|
312
|
+
compatibility_json=COALESCE(@compatibilityJson, compatibility_json),
|
|
313
|
+
draft_revision=@draftRevision,
|
|
314
|
+
governance_json=COALESCE(@governanceJson, governance_json)
|
|
315
|
+
WHERE id=@id
|
|
316
|
+
`)
|
|
317
|
+
.run({
|
|
318
|
+
id: version.id,
|
|
319
|
+
sourceKind: input.source?.kind ?? null,
|
|
320
|
+
sourcePath: input.source === undefined ? (version.source.path ?? null) : (input.source.path ?? null),
|
|
321
|
+
sourceUrl: input.source === undefined ? (version.source.url ?? null) : (input.source.url ?? null),
|
|
322
|
+
sourceInlineMetadataJson: input.source === undefined ? null : JSON.stringify(input.source.inlineMetadata ?? {}),
|
|
323
|
+
compatibilityJson: input.compatibility === undefined ? null : JSON.stringify(input.compatibility),
|
|
324
|
+
draftRevision: nextRevision,
|
|
325
|
+
governanceJson: input.governance === undefined ? null : JSON.stringify(input.governance),
|
|
326
|
+
});
|
|
327
|
+
const updated = this.getVersion(version.id);
|
|
328
|
+
if (!updated.ok)
|
|
329
|
+
throw new Error(updated.error.message);
|
|
330
|
+
const event = this.recordLifecycleEvent({
|
|
331
|
+
project: skill.project,
|
|
332
|
+
scope: skill.scope,
|
|
333
|
+
skillId: skill.id,
|
|
334
|
+
versionId: updated.value.id,
|
|
335
|
+
eventType: 'draft-updated',
|
|
336
|
+
actor: input.actor,
|
|
337
|
+
...(input.runId === undefined ? {} : { runId: input.runId }),
|
|
338
|
+
...(input.agentId === undefined ? {} : { agentId: input.agentId }),
|
|
339
|
+
...(input.reason === undefined ? {} : { reason: input.reason }),
|
|
340
|
+
before,
|
|
341
|
+
after: versionSnapshot(updated.value),
|
|
342
|
+
...(input.evidence === undefined ? {} : { evidence: input.evidence }),
|
|
343
|
+
});
|
|
344
|
+
if (!event.ok)
|
|
345
|
+
throw new Error(event.error.message);
|
|
346
|
+
return { skill, version: updated.value, event: event.value };
|
|
347
|
+
});
|
|
348
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError)
|
|
349
|
+
return validationFailure(result.error.cause.message);
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
publishVersion(input) {
|
|
353
|
+
const validation = validatePublishVersion(input);
|
|
354
|
+
if (!validation.ok)
|
|
355
|
+
return validation;
|
|
356
|
+
const result = this.db.transaction(() => {
|
|
357
|
+
const skill = resolveSkillForMutation(this, input.project, input.scope ?? 'project', input.skillId, input.name);
|
|
358
|
+
const version = resolveVersionForMutation(this, skill.id, input.versionId, input.version);
|
|
359
|
+
if (version.status !== 'draft')
|
|
360
|
+
throw new SkillRegistryValidationError(`Cannot publish ${version.status} skill versions; only draft versions can be published`);
|
|
361
|
+
const before = { skill: { currentVersionId: skill.currentVersionId ?? null }, version: versionSnapshot(version) };
|
|
362
|
+
const previousCurrentVersionId = skill.currentVersionId ?? null;
|
|
363
|
+
const now = new Date().toISOString();
|
|
364
|
+
this.db.connection
|
|
365
|
+
.prepare(`
|
|
366
|
+
UPDATE skill_versions SET status='active', lifecycle_label='published', published_at=@publishedAt, published_by=@publishedBy, governance_json=COALESCE(@governanceJson, governance_json)
|
|
367
|
+
WHERE id=@id
|
|
368
|
+
`)
|
|
369
|
+
.run({
|
|
370
|
+
id: version.id,
|
|
371
|
+
publishedAt: now,
|
|
372
|
+
publishedBy: input.actor.id,
|
|
373
|
+
governanceJson: input.governance === undefined ? null : JSON.stringify(input.governance),
|
|
374
|
+
});
|
|
375
|
+
this.db.connection.prepare('UPDATE skills SET current_version_id=?, updated_at=? WHERE id=?').run(version.id, now, skill.id);
|
|
376
|
+
const updatedSkill = this.getById(skill.id);
|
|
377
|
+
if (!updatedSkill.ok)
|
|
378
|
+
throw new Error(updatedSkill.error.message);
|
|
379
|
+
const updated = this.getVersion(version.id);
|
|
380
|
+
if (!updated.ok)
|
|
381
|
+
throw new Error(updated.error.message);
|
|
382
|
+
const event = this.recordLifecycleEvent({
|
|
383
|
+
project: updatedSkill.value.project,
|
|
384
|
+
scope: updatedSkill.value.scope,
|
|
385
|
+
skillId: updatedSkill.value.id,
|
|
386
|
+
versionId: updated.value.id,
|
|
387
|
+
eventType: 'version-published',
|
|
388
|
+
actor: input.actor,
|
|
389
|
+
...(input.runId === undefined ? {} : { runId: input.runId }),
|
|
390
|
+
...(input.agentId === undefined ? {} : { agentId: input.agentId }),
|
|
391
|
+
reason: input.reason,
|
|
392
|
+
before,
|
|
393
|
+
after: { skill: { currentVersionId: updated.value.id }, version: versionSnapshot(updated.value) },
|
|
394
|
+
...(input.evidence === undefined ? {} : { evidence: input.evidence }),
|
|
395
|
+
});
|
|
396
|
+
if (!event.ok)
|
|
397
|
+
throw new Error(event.error.message);
|
|
398
|
+
return { skill: updatedSkill.value, version: updated.value, event: event.value, previousCurrentVersionId };
|
|
399
|
+
});
|
|
400
|
+
if (!result.ok && result.error.cause instanceof SkillRegistryValidationError)
|
|
401
|
+
return validationFailure(result.error.cause.message);
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
getById(id) {
|
|
405
|
+
try {
|
|
406
|
+
const row = this.db.connection.prepare('SELECT * FROM skills WHERE id=?').get(id);
|
|
407
|
+
return row ? ok(mapSkill(row)) : missing(`Skill not found: ${id}`);
|
|
408
|
+
}
|
|
409
|
+
catch (cause) {
|
|
410
|
+
return fail('Failed to read skill', cause);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
getByName(project, scope, name) {
|
|
414
|
+
try {
|
|
415
|
+
const row = this.findByNaturalKey(project, scope, name);
|
|
416
|
+
return row ? ok(mapSkill(row)) : missing(`Skill not found: ${project}/${scope}/${name}`);
|
|
417
|
+
}
|
|
418
|
+
catch (cause) {
|
|
419
|
+
return fail('Failed to read skill by name', cause);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
getDetails(id) {
|
|
423
|
+
const skill = this.getById(id);
|
|
424
|
+
if (!skill.ok)
|
|
425
|
+
return skill;
|
|
426
|
+
const versions = this.listVersions(id);
|
|
427
|
+
if (!versions.ok)
|
|
428
|
+
return versions;
|
|
429
|
+
const attachments = this.listAttachments(id);
|
|
430
|
+
if (!attachments.ok)
|
|
431
|
+
return attachments;
|
|
432
|
+
const usage = this.listUsage(id);
|
|
433
|
+
if (!usage.ok)
|
|
434
|
+
return usage;
|
|
435
|
+
const details = { ...skill.value, versions: versions.value, attachments: attachments.value, usage: usage.value };
|
|
436
|
+
const currentVersion = versions.value.find((version) => version.id === skill.value.currentVersionId);
|
|
437
|
+
if (currentVersion !== undefined)
|
|
438
|
+
details.currentVersion = currentVersion;
|
|
439
|
+
return ok(details);
|
|
440
|
+
}
|
|
441
|
+
list(filters = {}) {
|
|
442
|
+
try {
|
|
443
|
+
const where = [];
|
|
444
|
+
const parameters = {};
|
|
445
|
+
if (filters.project !== undefined) {
|
|
446
|
+
where.push('project=@project');
|
|
447
|
+
parameters.project = filters.project;
|
|
448
|
+
}
|
|
449
|
+
if (filters.scope !== undefined) {
|
|
450
|
+
where.push('scope=@scope');
|
|
451
|
+
parameters.scope = filters.scope;
|
|
452
|
+
}
|
|
453
|
+
const rows = this.db.connection
|
|
454
|
+
.prepare(`
|
|
455
|
+
SELECT * FROM skills
|
|
456
|
+
${where.length ? `WHERE ${where.join(' AND ')}` : ''}
|
|
457
|
+
ORDER BY name ASC
|
|
458
|
+
`)
|
|
459
|
+
.all(parameters);
|
|
460
|
+
return ok(rows.map((row) => {
|
|
461
|
+
const summary = mapSkill(row);
|
|
462
|
+
if (summary.currentVersionId !== undefined) {
|
|
463
|
+
const version = this.getVersion(summary.currentVersionId);
|
|
464
|
+
if (version.ok)
|
|
465
|
+
summary.currentVersion = version.value;
|
|
466
|
+
}
|
|
467
|
+
return summary;
|
|
468
|
+
}));
|
|
469
|
+
}
|
|
470
|
+
catch (cause) {
|
|
471
|
+
return fail('Failed to list skills', cause);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
search(input) {
|
|
475
|
+
try {
|
|
476
|
+
const limit = normalizeLimit(input.limit);
|
|
477
|
+
const where = ['s.project=@project', 's.scope=@scope'];
|
|
478
|
+
const parameters = { project: input.project, scope: input.scope ?? 'project', limit };
|
|
479
|
+
if (input.query !== undefined && input.query.trim().length > 0) {
|
|
480
|
+
where.push('(s.name LIKE @query OR s.description LIKE @query OR cv.version LIKE @query)');
|
|
481
|
+
parameters.query = `%${input.query.trim()}%`;
|
|
482
|
+
}
|
|
483
|
+
if (input.status !== undefined) {
|
|
484
|
+
where.push('cv.status=@status');
|
|
485
|
+
parameters.status = input.status;
|
|
486
|
+
}
|
|
487
|
+
if (input.sourceKind !== undefined) {
|
|
488
|
+
where.push('cv.source_kind=@sourceKind');
|
|
489
|
+
parameters.sourceKind = input.sourceKind;
|
|
490
|
+
}
|
|
491
|
+
const rows = this.db.connection
|
|
492
|
+
.prepare(`
|
|
493
|
+
SELECT
|
|
494
|
+
s.*,
|
|
495
|
+
cv.id AS current_version_row_id,
|
|
496
|
+
cv.version AS current_version,
|
|
497
|
+
cv.source_kind AS current_source_kind,
|
|
498
|
+
cv.source_path AS current_source_path,
|
|
499
|
+
cv.source_url AS current_source_url,
|
|
500
|
+
cv.source_inline_metadata_json AS current_source_inline_metadata_json,
|
|
501
|
+
cv.status AS current_status,
|
|
502
|
+
(SELECT COUNT(*) FROM skill_versions sv WHERE sv.skill_id=s.id) AS version_count,
|
|
503
|
+
(SELECT COUNT(*) FROM skill_attachments sa WHERE sa.skill_id=s.id) AS attachment_count
|
|
504
|
+
FROM skills s
|
|
505
|
+
LEFT JOIN skill_versions cv ON cv.id=s.current_version_id
|
|
506
|
+
WHERE ${where.join(' AND ')}
|
|
507
|
+
ORDER BY s.name ASC, s.id ASC
|
|
508
|
+
LIMIT @limit
|
|
509
|
+
`)
|
|
510
|
+
.all(parameters);
|
|
511
|
+
return ok(rows.map(mapSearchItem));
|
|
512
|
+
}
|
|
513
|
+
catch (cause) {
|
|
514
|
+
return fail('Failed to search skills', cause);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
countSearchMatches(input) {
|
|
518
|
+
try {
|
|
519
|
+
const where = ['s.project=@project', 's.scope=@scope'];
|
|
520
|
+
const parameters = { project: input.project, scope: input.scope ?? 'project' };
|
|
521
|
+
if (input.query !== undefined && input.query.trim().length > 0) {
|
|
522
|
+
where.push('(s.name LIKE @query OR s.description LIKE @query OR cv.version LIKE @query)');
|
|
523
|
+
parameters.query = `%${input.query.trim()}%`;
|
|
524
|
+
}
|
|
525
|
+
if (input.status !== undefined) {
|
|
526
|
+
where.push('cv.status=@status');
|
|
527
|
+
parameters.status = input.status;
|
|
528
|
+
}
|
|
529
|
+
if (input.sourceKind !== undefined) {
|
|
530
|
+
where.push('cv.source_kind=@sourceKind');
|
|
531
|
+
parameters.sourceKind = input.sourceKind;
|
|
532
|
+
}
|
|
533
|
+
const row = this.db.connection
|
|
534
|
+
.prepare(`SELECT COUNT(*) AS count FROM skills s LEFT JOIN skill_versions cv ON cv.id=s.current_version_id WHERE ${where.join(' AND ')}`)
|
|
535
|
+
.get(parameters);
|
|
536
|
+
return ok(row.count);
|
|
537
|
+
}
|
|
538
|
+
catch (cause) {
|
|
539
|
+
return fail('Failed to count skill search matches', cause);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
getV2Counts(project, scope) {
|
|
543
|
+
try {
|
|
544
|
+
const versions = zeroVersionCounts();
|
|
545
|
+
for (const row of this.db.connection
|
|
546
|
+
.prepare(`SELECT sv.status AS status, COUNT(*) AS count FROM skill_versions sv JOIN skills s ON s.id=sv.skill_id WHERE s.project=? AND s.scope=? GROUP BY sv.status`)
|
|
547
|
+
.all(project, scope)) {
|
|
548
|
+
versions[row.status] = row.count;
|
|
549
|
+
}
|
|
550
|
+
const proposals = zeroProposalCounts();
|
|
551
|
+
for (const row of this.db.connection
|
|
552
|
+
.prepare(`SELECT p.status AS status, COUNT(*) AS count FROM skill_improvement_proposals p JOIN skills s ON s.id=p.skill_id WHERE s.project=? AND s.scope=? GROUP BY p.status`)
|
|
553
|
+
.all(project, scope)) {
|
|
554
|
+
proposals[row.status] = row.count;
|
|
555
|
+
}
|
|
556
|
+
const evaluationRequests = zeroEvaluationRequestCounts();
|
|
557
|
+
for (const row of this.db.connection
|
|
558
|
+
.prepare('SELECT status, COUNT(*) AS count FROM skill_evaluation_requests WHERE project=? AND scope=? GROUP BY status')
|
|
559
|
+
.all(project, scope)) {
|
|
560
|
+
evaluationRequests[row.status] = row.count;
|
|
561
|
+
}
|
|
562
|
+
const evaluationResults = zeroEvaluationResultCounts();
|
|
563
|
+
for (const row of this.db.connection
|
|
564
|
+
.prepare(`SELECT r.status AS status, COUNT(*) AS count FROM skill_evaluation_results r JOIN skill_evaluation_scenarios e ON e.id=r.scenario_id JOIN skills s ON s.id=e.skill_id WHERE s.project=? AND s.scope=? GROUP BY r.status`)
|
|
565
|
+
.all(project, scope)) {
|
|
566
|
+
evaluationResults[row.status] = row.count;
|
|
567
|
+
}
|
|
568
|
+
return ok({
|
|
569
|
+
versions,
|
|
570
|
+
attachments: countScoped(this.db, 'skill_attachments', project, scope),
|
|
571
|
+
usageRecords: countScoped(this.db, 'skill_usage_records', project, scope),
|
|
572
|
+
activationRecords: countDirectScoped(this.db, 'skill_activation_records', project, scope),
|
|
573
|
+
improvementProposals: proposals,
|
|
574
|
+
evaluationScenarios: countScoped(this.db, 'skill_evaluation_scenarios', project, scope),
|
|
575
|
+
evaluationRequests,
|
|
576
|
+
evaluationResults,
|
|
577
|
+
drafts: versions.draft,
|
|
578
|
+
deprecated: versions.deprecated,
|
|
579
|
+
archived: versions.archived,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
catch (cause) {
|
|
583
|
+
return fail('Failed to count skill registry v2 records', cause);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
listVersions(skillId) {
|
|
587
|
+
try {
|
|
588
|
+
const rows = this.db.connection.prepare('SELECT * FROM skill_versions WHERE skill_id=? ORDER BY created_at DESC, version DESC').all(skillId);
|
|
589
|
+
return ok(rows.map(mapVersion));
|
|
590
|
+
}
|
|
591
|
+
catch (cause) {
|
|
592
|
+
return fail('Failed to list skill versions', cause);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
listAttachments(skillId) {
|
|
596
|
+
try {
|
|
597
|
+
const rows = this.db.connection.prepare('SELECT * FROM skill_attachments WHERE skill_id=? ORDER BY target_type ASC, target_key ASC').all(skillId);
|
|
598
|
+
return ok(rows.map(mapAttachment));
|
|
599
|
+
}
|
|
600
|
+
catch (cause) {
|
|
601
|
+
return fail('Failed to list skill attachments', cause);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
listAttachmentsForTargets(targets) {
|
|
605
|
+
try {
|
|
606
|
+
if (targets.length === 0)
|
|
607
|
+
return ok([]);
|
|
608
|
+
const rows = [];
|
|
609
|
+
const read = this.db.connection.prepare('SELECT * FROM skill_attachments WHERE target_type=? AND target_key=? ORDER BY created_at ASC, id ASC');
|
|
610
|
+
for (const target of targets)
|
|
611
|
+
rows.push(...read.all(target.targetType, target.targetKey));
|
|
612
|
+
return ok(rows.map(mapAttachment));
|
|
613
|
+
}
|
|
614
|
+
catch (cause) {
|
|
615
|
+
return fail('Failed to list skill attachments for targets', cause);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
listUsage(skillId) {
|
|
619
|
+
try {
|
|
620
|
+
const rows = this.db.connection.prepare('SELECT * FROM skill_usage_records WHERE skill_id=? ORDER BY created_at DESC').all(skillId);
|
|
621
|
+
return ok(rows.map(mapUsage));
|
|
622
|
+
}
|
|
623
|
+
catch (cause) {
|
|
624
|
+
return fail('Failed to list skill usage', cause);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
getVersion(id) {
|
|
628
|
+
try {
|
|
629
|
+
const row = this.db.connection.prepare('SELECT * FROM skill_versions WHERE id=?').get(id);
|
|
630
|
+
return row ? ok(mapVersion(row)) : missing(`Skill version not found: ${id}`);
|
|
631
|
+
}
|
|
632
|
+
catch (cause) {
|
|
633
|
+
return fail('Failed to read skill version', cause);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
getVersionBySkillVersion(skillId, version) {
|
|
637
|
+
try {
|
|
638
|
+
const row = this.findVersion(skillId, version);
|
|
639
|
+
return row ? ok(mapVersion(row)) : missing(`Skill version not found: ${version}`);
|
|
640
|
+
}
|
|
641
|
+
catch (cause) {
|
|
642
|
+
return fail('Failed to read skill version', cause);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
getAttachment(id) {
|
|
646
|
+
try {
|
|
647
|
+
const row = this.db.connection.prepare('SELECT * FROM skill_attachments WHERE id=?').get(id);
|
|
648
|
+
return row ? ok(mapAttachment(row)) : missing(`Skill attachment not found: ${id}`);
|
|
649
|
+
}
|
|
650
|
+
catch (cause) {
|
|
651
|
+
return fail('Failed to read skill attachment', cause);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
getUsage(id) {
|
|
655
|
+
try {
|
|
656
|
+
const row = this.db.connection.prepare('SELECT * FROM skill_usage_records WHERE id=?').get(id);
|
|
657
|
+
return row ? ok(mapUsage(row)) : missing(`Skill usage record not found: ${id}`);
|
|
658
|
+
}
|
|
659
|
+
catch (cause) {
|
|
660
|
+
return fail('Failed to read skill usage record', cause);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
getActivation(id) {
|
|
664
|
+
try {
|
|
665
|
+
const row = this.db.connection.prepare('SELECT * FROM skill_activation_records WHERE id=?').get(id);
|
|
666
|
+
return row ? ok(mapActivation(row)) : missing(`Skill activation record not found: ${id}`);
|
|
667
|
+
}
|
|
668
|
+
catch (cause) {
|
|
669
|
+
return fail('Failed to read skill activation record', cause);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
recordLifecycleEvent(input) {
|
|
673
|
+
try {
|
|
674
|
+
const id = randomUUID();
|
|
675
|
+
const now = new Date().toISOString();
|
|
676
|
+
this.db.connection
|
|
677
|
+
.prepare(`
|
|
678
|
+
INSERT INTO skill_lifecycle_events(id, project, scope, skill_id, version_id, event_type, actor_type, actor_id, run_id, agent_id, reason, before_json, after_json, evidence_json, created_at)
|
|
679
|
+
VALUES (@id, @project, @scope, @skillId, @versionId, @eventType, @actorType, @actorId, @runId, @agentId, @reason, @beforeJson, @afterJson, @evidenceJson, @createdAt)
|
|
680
|
+
`)
|
|
681
|
+
.run({
|
|
682
|
+
id,
|
|
683
|
+
project: input.project,
|
|
684
|
+
scope: input.scope,
|
|
685
|
+
skillId: input.skillId,
|
|
686
|
+
versionId: input.versionId ?? null,
|
|
687
|
+
eventType: input.eventType,
|
|
688
|
+
actorType: input.actor?.type ?? null,
|
|
689
|
+
actorId: input.actor?.id ?? null,
|
|
690
|
+
runId: input.runId ?? null,
|
|
691
|
+
agentId: input.agentId ?? null,
|
|
692
|
+
reason: input.reason ?? null,
|
|
693
|
+
beforeJson: input.before === undefined ? null : JSON.stringify(input.before),
|
|
694
|
+
afterJson: input.after === undefined ? null : JSON.stringify(input.after),
|
|
695
|
+
evidenceJson: input.evidence === undefined ? null : JSON.stringify(input.evidence),
|
|
696
|
+
createdAt: now,
|
|
697
|
+
});
|
|
698
|
+
const row = this.db.connection.prepare('SELECT * FROM skill_lifecycle_events WHERE id=?').get(id);
|
|
699
|
+
return row ? ok(mapLifecycleEvent(row)) : missing(`Skill lifecycle event not found: ${id}`);
|
|
700
|
+
}
|
|
701
|
+
catch (cause) {
|
|
702
|
+
return fail('Failed to record skill lifecycle event', cause);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
findByNaturalKey(project, scope, name) {
|
|
706
|
+
return this.db.connection.prepare('SELECT * FROM skills WHERE project=? AND scope=? AND name=?').get(project, scope, name);
|
|
707
|
+
}
|
|
708
|
+
findVersion(skillId, version) {
|
|
709
|
+
return this.db.connection.prepare('SELECT * FROM skill_versions WHERE skill_id=? AND version=?').get(skillId, version);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function validateSkill(input) {
|
|
713
|
+
if (!input.project.trim())
|
|
714
|
+
return validationFailure('Skill project is required');
|
|
715
|
+
if (!input.name.trim())
|
|
716
|
+
return validationFailure('Skill name is required');
|
|
717
|
+
if (!input.description.trim())
|
|
718
|
+
return validationFailure('Skill description is required');
|
|
719
|
+
return ok(undefined);
|
|
720
|
+
}
|
|
721
|
+
function validateVersion(input) {
|
|
722
|
+
if (!input.skillId.trim())
|
|
723
|
+
return validationFailure('Skill id is required');
|
|
724
|
+
if (!input.version.trim())
|
|
725
|
+
return validationFailure('Skill version is required');
|
|
726
|
+
if (input.source.kind === 'path' && !input.source.path?.trim())
|
|
727
|
+
return validationFailure('Path skill sources require source.path');
|
|
728
|
+
if (input.source.kind === 'url' && !input.source.url?.trim())
|
|
729
|
+
return validationFailure('URL skill sources require source.url');
|
|
730
|
+
if (input.source.kind === 'inline' && (input.source.path !== undefined || input.source.url !== undefined))
|
|
731
|
+
return validationFailure('Inline skill sources cannot include source.path or source.url');
|
|
732
|
+
return ok(undefined);
|
|
733
|
+
}
|
|
734
|
+
function validateAttachment(input) {
|
|
735
|
+
if (!input.skillId.trim())
|
|
736
|
+
return validationFailure('Skill id is required');
|
|
737
|
+
if (!input.targetKey.trim())
|
|
738
|
+
return validationFailure('Skill attachment target key is required');
|
|
739
|
+
return ok(undefined);
|
|
740
|
+
}
|
|
741
|
+
function validateUsage(input) {
|
|
742
|
+
if (!input.skillId.trim())
|
|
743
|
+
return validationFailure('Skill id is required');
|
|
744
|
+
if (input.targetType !== undefined && !input.targetKey?.trim())
|
|
745
|
+
return validationFailure('Skill usage target key is required when target type is set');
|
|
746
|
+
if (input.targetType === undefined && input.targetKey !== undefined)
|
|
747
|
+
return validationFailure('Skill usage target type is required when target key is set');
|
|
748
|
+
if (input.versionId !== undefined && !input.versionId.trim())
|
|
749
|
+
return validationFailure('Skill version id must not be empty');
|
|
750
|
+
return ok(undefined);
|
|
751
|
+
}
|
|
752
|
+
function validateActivation(input) {
|
|
753
|
+
if (!input.project.trim())
|
|
754
|
+
return validationFailure('Skill activation project is required');
|
|
755
|
+
if (!input.skillId.trim())
|
|
756
|
+
return validationFailure('Skill id is required');
|
|
757
|
+
if (input.versionId !== undefined && !input.versionId.trim())
|
|
758
|
+
return validationFailure('Skill version id must not be empty');
|
|
759
|
+
return ok(undefined);
|
|
760
|
+
}
|
|
761
|
+
function validateCreateDraft(input) {
|
|
762
|
+
if (!input.project.trim())
|
|
763
|
+
return validationFailure('Skill draft project is required');
|
|
764
|
+
if (!input.name.trim())
|
|
765
|
+
return validationFailure('Skill draft name is required');
|
|
766
|
+
if (!input.description.trim())
|
|
767
|
+
return validationFailure('Skill draft description is required');
|
|
768
|
+
if (!input.version.trim())
|
|
769
|
+
return validationFailure('Skill draft version is required');
|
|
770
|
+
const source = validateSource(input.source);
|
|
771
|
+
if (!source.ok)
|
|
772
|
+
return source;
|
|
773
|
+
return validateLifecycleActor(input.actor);
|
|
774
|
+
}
|
|
775
|
+
function validateUpdateDraft(input) {
|
|
776
|
+
if (!input.project.trim())
|
|
777
|
+
return validationFailure('Skill draft project is required');
|
|
778
|
+
if ((input.skillId === undefined || !input.skillId.trim()) && (input.name === undefined || !input.name.trim()))
|
|
779
|
+
return validationFailure('Either skillId or name is required');
|
|
780
|
+
if ((input.versionId === undefined || !input.versionId.trim()) && (input.version === undefined || !input.version.trim()))
|
|
781
|
+
return validationFailure('Either versionId or version is required');
|
|
782
|
+
if (input.versionId !== undefined && input.version !== undefined)
|
|
783
|
+
return validationFailure('Use either versionId or version, not both');
|
|
784
|
+
if (input.source === undefined && input.compatibility === undefined && input.governance === undefined)
|
|
785
|
+
return validationFailure('At least one draft update field is required');
|
|
786
|
+
if (input.expectedDraftRevision !== undefined && (!Number.isSafeInteger(input.expectedDraftRevision) || input.expectedDraftRevision < 1)) {
|
|
787
|
+
return validationFailure('expectedDraftRevision must be a positive safe integer');
|
|
788
|
+
}
|
|
789
|
+
if (input.source !== undefined) {
|
|
790
|
+
const source = validateSource(input.source);
|
|
791
|
+
if (!source.ok)
|
|
792
|
+
return source;
|
|
793
|
+
}
|
|
794
|
+
return validateLifecycleActor(input.actor);
|
|
795
|
+
}
|
|
796
|
+
function validatePublishVersion(input) {
|
|
797
|
+
if (!input.project.trim())
|
|
798
|
+
return validationFailure('Skill publish project is required');
|
|
799
|
+
if ((input.skillId === undefined || !input.skillId.trim()) && (input.name === undefined || !input.name.trim()))
|
|
800
|
+
return validationFailure('Either skillId or name is required');
|
|
801
|
+
if ((input.versionId === undefined || !input.versionId.trim()) && (input.version === undefined || !input.version.trim()))
|
|
802
|
+
return validationFailure('Either versionId or version is required');
|
|
803
|
+
if (input.versionId !== undefined && input.version !== undefined)
|
|
804
|
+
return validationFailure('Use either versionId or version, not both');
|
|
805
|
+
if (!input.reason.trim())
|
|
806
|
+
return validationFailure('Publish reason is required');
|
|
807
|
+
return validateLifecycleActor(input.actor);
|
|
808
|
+
}
|
|
809
|
+
function validateLifecycleActor(actor) {
|
|
810
|
+
if (!['human', 'agent', 'system'].includes(actor.type))
|
|
811
|
+
return validationFailure('Lifecycle actor type must be human, agent, or system');
|
|
812
|
+
if (!actor.id.trim())
|
|
813
|
+
return validationFailure('Lifecycle actor id is required');
|
|
814
|
+
return ok(undefined);
|
|
815
|
+
}
|
|
816
|
+
function validateSource(source) {
|
|
817
|
+
if (source.kind === 'path' && !source.path?.trim())
|
|
818
|
+
return validationFailure('Path skill sources require source.path');
|
|
819
|
+
if (source.kind === 'url' && !source.url?.trim())
|
|
820
|
+
return validationFailure('URL skill sources require source.url');
|
|
821
|
+
if (source.kind === 'inline' && (source.path !== undefined || source.url !== undefined))
|
|
822
|
+
return validationFailure('Inline skill sources cannot include source.path or source.url');
|
|
823
|
+
return ok(undefined);
|
|
824
|
+
}
|
|
825
|
+
function assertVersionBelongsToSkill(db, versionId, skillId) {
|
|
826
|
+
const row = db.connection.prepare('SELECT skill_id FROM skill_versions WHERE id=?').get(versionId);
|
|
827
|
+
if (row === undefined)
|
|
828
|
+
throw new SkillRegistryValidationError(`Skill version not found: ${versionId}`);
|
|
829
|
+
if (row.skill_id !== skillId)
|
|
830
|
+
throw new SkillRegistryValidationError('Skill version must belong to the attached skill');
|
|
831
|
+
}
|
|
832
|
+
function mapSkill(row) {
|
|
833
|
+
const skill = {
|
|
834
|
+
id: row.id,
|
|
835
|
+
project: row.project,
|
|
836
|
+
scope: row.scope,
|
|
837
|
+
name: row.name,
|
|
838
|
+
description: row.description,
|
|
839
|
+
createdAt: row.created_at,
|
|
840
|
+
updatedAt: row.updated_at,
|
|
841
|
+
};
|
|
842
|
+
if (row.current_version_id !== null)
|
|
843
|
+
skill.currentVersionId = row.current_version_id;
|
|
844
|
+
return skill;
|
|
845
|
+
}
|
|
846
|
+
function mapSearchItem(row) {
|
|
847
|
+
const source = mapNullableSource(row.current_source_kind, row.current_source_path, row.current_source_url, row.current_source_inline_metadata_json);
|
|
848
|
+
const currentVersionId = row.current_version_row_id ?? null;
|
|
849
|
+
return {
|
|
850
|
+
id: row.id,
|
|
851
|
+
project: row.project,
|
|
852
|
+
scope: row.scope,
|
|
853
|
+
name: row.name,
|
|
854
|
+
description: row.description,
|
|
855
|
+
currentVersionId,
|
|
856
|
+
currentVersion: row.current_version ?? null,
|
|
857
|
+
status: row.current_status ?? (row.current_version_id === null ? 'missing-current-version' : 'missing-current-version-record'),
|
|
858
|
+
source,
|
|
859
|
+
attachments: Number(row.attachment_count ?? 0),
|
|
860
|
+
versions: Number(row.version_count ?? 0),
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function mapNullableSource(kind, path, url, inlineMetadataJson) {
|
|
864
|
+
if (kind === null)
|
|
865
|
+
return null;
|
|
866
|
+
const source = { kind: kind };
|
|
867
|
+
if (path !== null)
|
|
868
|
+
source.path = path;
|
|
869
|
+
if (url !== null)
|
|
870
|
+
source.url = url;
|
|
871
|
+
const inlineMetadata = JSON.parse(inlineMetadataJson ?? '{}');
|
|
872
|
+
if (Object.keys(inlineMetadata).length > 0)
|
|
873
|
+
source.inlineMetadata = inlineMetadata;
|
|
874
|
+
return source;
|
|
875
|
+
}
|
|
876
|
+
function mapVersion(row) {
|
|
877
|
+
const source = { kind: row.source_kind };
|
|
878
|
+
if (row.source_path !== null)
|
|
879
|
+
source.path = row.source_path;
|
|
880
|
+
if (row.source_url !== null)
|
|
881
|
+
source.url = row.source_url;
|
|
882
|
+
const inlineMetadata = JSON.parse(row.source_inline_metadata_json ?? '{}');
|
|
883
|
+
if (Object.keys(inlineMetadata).length > 0)
|
|
884
|
+
source.inlineMetadata = inlineMetadata;
|
|
885
|
+
const version = {
|
|
886
|
+
id: row.id,
|
|
887
|
+
skillId: row.skill_id,
|
|
888
|
+
version: row.version,
|
|
889
|
+
source,
|
|
890
|
+
compatibility: JSON.parse(row.compatibility_json),
|
|
891
|
+
status: row.status,
|
|
892
|
+
createdAt: row.created_at,
|
|
893
|
+
};
|
|
894
|
+
if (row.lifecycle_label !== null && row.lifecycle_label !== undefined)
|
|
895
|
+
version.lifecycleLabel = row.lifecycle_label;
|
|
896
|
+
if (row.draft_parent_version_id !== null && row.draft_parent_version_id !== undefined)
|
|
897
|
+
version.draftParentVersionId = row.draft_parent_version_id;
|
|
898
|
+
if (row.draft_revision !== null && row.draft_revision !== undefined)
|
|
899
|
+
version.draftRevision = row.draft_revision;
|
|
900
|
+
if (row.governance_json !== null && row.governance_json !== undefined)
|
|
901
|
+
version.governance = JSON.parse(row.governance_json);
|
|
902
|
+
if (row.published_at !== null && row.published_at !== undefined)
|
|
903
|
+
version.publishedAt = row.published_at;
|
|
904
|
+
if (row.published_by !== null && row.published_by !== undefined)
|
|
905
|
+
version.publishedBy = row.published_by;
|
|
906
|
+
if (row.deprecated_at !== null && row.deprecated_at !== undefined)
|
|
907
|
+
version.deprecatedAt = row.deprecated_at;
|
|
908
|
+
if (row.archived_at !== null && row.archived_at !== undefined)
|
|
909
|
+
version.archivedAt = row.archived_at;
|
|
910
|
+
return version;
|
|
911
|
+
}
|
|
912
|
+
function mapLifecycleEvent(row) {
|
|
913
|
+
const event = {
|
|
914
|
+
id: row.id,
|
|
915
|
+
project: row.project,
|
|
916
|
+
scope: row.scope,
|
|
917
|
+
skillId: row.skill_id,
|
|
918
|
+
eventType: row.event_type,
|
|
919
|
+
createdAt: row.created_at,
|
|
920
|
+
};
|
|
921
|
+
if (row.version_id !== null)
|
|
922
|
+
event.versionId = row.version_id;
|
|
923
|
+
if (row.actor_type !== null && row.actor_id !== null)
|
|
924
|
+
event.actor = { type: row.actor_type, id: row.actor_id };
|
|
925
|
+
if (row.run_id !== null)
|
|
926
|
+
event.runId = row.run_id;
|
|
927
|
+
if (row.agent_id !== null)
|
|
928
|
+
event.agentId = row.agent_id;
|
|
929
|
+
if (row.reason !== null)
|
|
930
|
+
event.reason = row.reason;
|
|
931
|
+
if (row.before_json !== null)
|
|
932
|
+
event.before = JSON.parse(row.before_json);
|
|
933
|
+
if (row.after_json !== null)
|
|
934
|
+
event.after = JSON.parse(row.after_json);
|
|
935
|
+
if (row.evidence_json !== null)
|
|
936
|
+
event.evidence = JSON.parse(row.evidence_json);
|
|
937
|
+
return event;
|
|
938
|
+
}
|
|
939
|
+
function resolveSkillForMutation(repository, project, scope, skillId, name) {
|
|
940
|
+
const result = skillId !== undefined ? repository.getById(skillId) : repository.getByName(project, scope, name ?? '');
|
|
941
|
+
if (!result.ok)
|
|
942
|
+
throw new SkillRegistryValidationError(result.error.message);
|
|
943
|
+
if (result.value.project !== project || result.value.scope !== scope)
|
|
944
|
+
throw new SkillRegistryValidationError('Skill does not belong to the requested project/scope');
|
|
945
|
+
return result.value;
|
|
946
|
+
}
|
|
947
|
+
function resolveVersionForMutation(repository, skillId, versionId, version) {
|
|
948
|
+
const result = versionId !== undefined ? repository.getVersion(versionId) : repository.getVersionBySkillVersion(skillId, version ?? '');
|
|
949
|
+
if (!result.ok)
|
|
950
|
+
throw new SkillRegistryValidationError(result.error.message);
|
|
951
|
+
if (result.value.skillId !== skillId)
|
|
952
|
+
throw new SkillRegistryValidationError('Skill version must belong to the requested skill');
|
|
953
|
+
return result.value;
|
|
954
|
+
}
|
|
955
|
+
function versionSnapshot(version) {
|
|
956
|
+
return {
|
|
957
|
+
id: version.id,
|
|
958
|
+
skillId: version.skillId,
|
|
959
|
+
version: version.version,
|
|
960
|
+
status: version.status,
|
|
961
|
+
draftRevision: version.draftRevision ?? null,
|
|
962
|
+
lifecycleLabel: version.lifecycleLabel ?? null,
|
|
963
|
+
publishedAt: version.publishedAt ?? null,
|
|
964
|
+
publishedBy: version.publishedBy ?? null,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
function mapAttachment(row) {
|
|
968
|
+
const attachment = {
|
|
969
|
+
id: row.id,
|
|
970
|
+
skillId: row.skill_id,
|
|
971
|
+
targetType: row.target_type,
|
|
972
|
+
targetKey: row.target_key,
|
|
973
|
+
metadata: JSON.parse(row.metadata_json),
|
|
974
|
+
createdAt: row.created_at,
|
|
975
|
+
};
|
|
976
|
+
if (row.version_id !== null)
|
|
977
|
+
attachment.versionId = row.version_id;
|
|
978
|
+
return attachment;
|
|
979
|
+
}
|
|
980
|
+
function mapUsage(row) {
|
|
981
|
+
const usage = {
|
|
982
|
+
id: row.id,
|
|
983
|
+
skillId: row.skill_id,
|
|
984
|
+
outcome: row.outcome,
|
|
985
|
+
createdAt: row.created_at,
|
|
986
|
+
};
|
|
987
|
+
if (row.version_id !== null)
|
|
988
|
+
usage.versionId = row.version_id;
|
|
989
|
+
if (row.run_id !== null)
|
|
990
|
+
usage.runId = row.run_id;
|
|
991
|
+
if (row.target_type !== null)
|
|
992
|
+
usage.targetType = row.target_type;
|
|
993
|
+
if (row.target_key !== null)
|
|
994
|
+
usage.targetKey = row.target_key;
|
|
995
|
+
if (row.notes !== null)
|
|
996
|
+
usage.notes = row.notes;
|
|
997
|
+
if (row.workflow !== null)
|
|
998
|
+
usage.workflow = row.workflow;
|
|
999
|
+
if (row.phase !== null)
|
|
1000
|
+
usage.phase = row.phase;
|
|
1001
|
+
if (row.provider_adapter !== null)
|
|
1002
|
+
usage.providerAdapter = row.provider_adapter;
|
|
1003
|
+
if (row.agent_id !== null)
|
|
1004
|
+
usage.agentId = row.agent_id;
|
|
1005
|
+
if (row.intent_json !== null)
|
|
1006
|
+
usage.intent = JSON.parse(row.intent_json);
|
|
1007
|
+
if (row.evidence_json !== null)
|
|
1008
|
+
usage.evidence = JSON.parse(row.evidence_json);
|
|
1009
|
+
return usage;
|
|
1010
|
+
}
|
|
1011
|
+
function mapActivation(row) {
|
|
1012
|
+
const activation = {
|
|
1013
|
+
id: row.id,
|
|
1014
|
+
project: row.project,
|
|
1015
|
+
scope: row.scope,
|
|
1016
|
+
skillId: row.skill_id,
|
|
1017
|
+
createdAt: row.created_at,
|
|
1018
|
+
};
|
|
1019
|
+
if (row.version_id !== null)
|
|
1020
|
+
activation.versionId = row.version_id;
|
|
1021
|
+
if (row.run_id !== null)
|
|
1022
|
+
activation.runId = row.run_id;
|
|
1023
|
+
if (row.agent_id !== null)
|
|
1024
|
+
activation.agentId = row.agent_id;
|
|
1025
|
+
if (row.workflow !== null)
|
|
1026
|
+
activation.workflow = row.workflow;
|
|
1027
|
+
if (row.phase !== null)
|
|
1028
|
+
activation.phase = row.phase;
|
|
1029
|
+
if (row.provider_adapter !== null)
|
|
1030
|
+
activation.providerAdapter = row.provider_adapter;
|
|
1031
|
+
if (row.intent_json !== null)
|
|
1032
|
+
activation.intent = JSON.parse(row.intent_json);
|
|
1033
|
+
if (row.reason !== null)
|
|
1034
|
+
activation.reason = row.reason;
|
|
1035
|
+
if (row.payload_metadata_json !== null)
|
|
1036
|
+
activation.payloadMetadata = JSON.parse(row.payload_metadata_json);
|
|
1037
|
+
return activation;
|
|
1038
|
+
}
|
|
1039
|
+
function ok(value) {
|
|
1040
|
+
return { ok: true, value };
|
|
1041
|
+
}
|
|
1042
|
+
function missing(message) {
|
|
1043
|
+
return { ok: false, error: { code: 'not_found', message } };
|
|
1044
|
+
}
|
|
1045
|
+
function validationFailure(message) {
|
|
1046
|
+
return { ok: false, error: { code: 'validation_failed', message } };
|
|
1047
|
+
}
|
|
1048
|
+
function fail(message, cause) {
|
|
1049
|
+
const error = { code: 'validation_failed', message };
|
|
1050
|
+
if (cause !== undefined)
|
|
1051
|
+
error.cause = cause;
|
|
1052
|
+
return { ok: false, error };
|
|
1053
|
+
}
|
|
1054
|
+
class SkillRegistryValidationError extends Error {
|
|
1055
|
+
}
|
|
1056
|
+
function normalizeLimit(limit) {
|
|
1057
|
+
if (limit === undefined)
|
|
1058
|
+
return 25;
|
|
1059
|
+
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
1060
|
+
return 25;
|
|
1061
|
+
return Math.min(limit, 100);
|
|
1062
|
+
}
|
|
1063
|
+
function zeroVersionCounts() {
|
|
1064
|
+
return { draft: 0, active: 0, deprecated: 0, archived: 0 };
|
|
1065
|
+
}
|
|
1066
|
+
function zeroProposalCounts() {
|
|
1067
|
+
return { draft: 0, 'pending-approval': 0, approved: 0, rejected: 0, cancelled: 0, applied: 0 };
|
|
1068
|
+
}
|
|
1069
|
+
function zeroEvaluationResultCounts() {
|
|
1070
|
+
return { passed: 0, failed: 0, 'needs-review': 0, 'not-applicable': 0 };
|
|
1071
|
+
}
|
|
1072
|
+
function zeroEvaluationRequestCounts() {
|
|
1073
|
+
return { requested: 0, planned: 0, running: 0, completed: 0, failed: 0, cancelled: 0, skipped: 0 };
|
|
1074
|
+
}
|
|
1075
|
+
function countScoped(db, table, project, scope) {
|
|
1076
|
+
const row = db.connection
|
|
1077
|
+
.prepare(`SELECT COUNT(*) AS count FROM ${table} t JOIN skills s ON s.id=t.skill_id WHERE s.project=? AND s.scope=?`)
|
|
1078
|
+
.get(project, scope);
|
|
1079
|
+
return row.count;
|
|
1080
|
+
}
|
|
1081
|
+
function countDirectScoped(db, table, project, scope) {
|
|
1082
|
+
const row = db.connection.prepare(`SELECT COUNT(*) AS count FROM ${table} WHERE project=? AND scope=?`).get(project, scope);
|
|
1083
|
+
return row.count;
|
|
1084
|
+
}
|