@geraldmaron/construct 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +36 -0
- package/README.md +41 -7
- package/bin/construct +1029 -65
- package/bin/construct-postinstall.mjs +27 -2
- package/config/tag-vocabulary.json +264 -0
- package/examples/distribution/sources/deck-one-pager.md +1 -1
- package/lib/acp/server.mjs +21 -7
- package/lib/adapters-sync.mjs +2 -1
- package/lib/audit-trail.mjs +185 -2
- package/lib/beads/auto-close.mjs +2 -1
- package/lib/beads/drift.mjs +2 -1
- package/lib/bridges/copilot-proxy.mjs +20 -5
- package/lib/certification/skill-inventory.mjs +10 -1
- package/lib/cli/approvals.mjs +147 -0
- package/lib/cli-commands.mjs +105 -14
- package/lib/comment-lint.mjs +127 -8
- package/lib/config/schema.mjs +7 -30
- package/lib/config/source-target-registry.mjs +70 -0
- package/lib/config/source-targets.mjs +179 -205
- package/lib/contracts/coverage.mjs +77 -0
- package/lib/contracts/validate.mjs +102 -13
- package/lib/contracts/violation-log.mjs +50 -4
- package/lib/db/migrate.mjs +69 -0
- package/lib/db/migrations/001_orchestration_runs.sql +9 -0
- package/lib/db/migrations/002_queue_provider.sql +50 -0
- package/lib/db/migrations/003_worker_registry.sql +17 -0
- package/lib/db/migrations/004_trace_events.sql +16 -0
- package/lib/db/migrations/005_shared_memory.sql +15 -0
- package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
- package/lib/decisions/enforced-baseline.json +0 -2
- package/lib/decisions/registry.mjs +3 -2
- package/lib/deployment/parity-contract.mjs +1 -1
- package/lib/deployment-mode.mjs +78 -2
- package/lib/diagram-export.mjs +10 -1
- package/lib/distill.mjs +5 -2
- package/lib/docs-verify.mjs +2 -1
- package/lib/doctor/cli.mjs +1 -0
- package/lib/doctor/command-on-path.mjs +24 -0
- package/lib/doctor/diagnosis.mjs +109 -0
- package/lib/doctor/embedding-health.mjs +50 -0
- package/lib/doctor/engine-health.mjs +93 -0
- package/lib/doctor/graph-validate.mjs +47 -0
- package/lib/doctor/index.mjs +23 -4
- package/lib/doctor/sidecar-providers.mjs +56 -0
- package/lib/doctor/watchers/consistency.mjs +93 -1
- package/lib/doctor/watchers/cx-budget.mjs +1 -1
- package/lib/doctor/watchers/graph-staleness.mjs +1 -1
- package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
- package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
- package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
- package/lib/doctor/watchers/provider-breaker.mjs +78 -0
- package/lib/document-export.mjs +52 -27
- package/lib/document-extract/docling-client.mjs +9 -5
- package/lib/document-extract/docling-sidecar.py +30 -0
- package/lib/document-extract.mjs +1 -1
- package/lib/document-ingest.mjs +9 -0
- package/lib/embed/approval-queue.mjs +184 -88
- package/lib/embed/authority-guard.mjs +65 -2
- package/lib/embed/capability-jobs.mjs +365 -0
- package/lib/embed/capability-lifecycle.mjs +219 -0
- package/lib/embed/capability-loader.mjs +303 -0
- package/lib/embed/capability-runtime.mjs +58 -0
- package/lib/embed/cli.mjs +185 -8
- package/lib/embed/config.mjs +4 -0
- package/lib/embed/daemon.mjs +125 -6
- package/lib/embed/demand-fetch.mjs +190 -54
- package/lib/embed/inbox.mjs +12 -10
- package/lib/embed/presets/ops-triage.mjs +236 -0
- package/lib/embed/presets/pm-feedback.mjs +341 -0
- package/lib/embed/presets/tpm.mjs +401 -0
- package/lib/embed/providers/jira.mjs +72 -1
- package/lib/embed/providers/registry.mjs +140 -33
- package/lib/embed/worker.mjs +5 -0
- package/lib/embedded-contract/capability.mjs +4 -0
- package/lib/embedded-contract/execution.mjs +1 -1
- package/lib/embedded-contract/model-resolve.mjs +53 -3
- package/lib/embedded-contract/workflow-defs.mjs +48 -73
- package/lib/embedded-contract/workflow-invoke.mjs +144 -4
- package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
- package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
- package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
- package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
- package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
- package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
- package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
- package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
- package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
- package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
- package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
- package/lib/env-config.mjs +60 -11
- package/lib/export-validate.mjs +34 -2
- package/lib/extensions/index.mjs +27 -0
- package/lib/extensions/loader.mjs +120 -0
- package/lib/extensions/manifest-schema.mjs +141 -0
- package/lib/extensions/manifests/anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
- package/lib/extensions/manifests/directory.manifest.json +12 -0
- package/lib/extensions/manifests/docling.manifest.json +37 -0
- package/lib/extensions/manifests/echo.manifest.json +8 -0
- package/lib/extensions/manifests/feedback.manifest.json +12 -0
- package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
- package/lib/extensions/manifests/github.manifest.json +52 -0
- package/lib/extensions/manifests/linear.manifest.json +50 -0
- package/lib/extensions/manifests/local.manifest.json +12 -0
- package/lib/extensions/manifests/ollama.manifest.json +12 -0
- package/lib/extensions/manifests/openai.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter.manifest.json +12 -0
- package/lib/extensions/manifests/postgres.manifest.json +12 -0
- package/lib/extensions/manifests/salesforce.manifest.json +12 -0
- package/lib/extensions/manifests/slack.manifest.json +58 -0
- package/lib/extensions/manifests/whisper.manifest.json +36 -0
- package/lib/extensions/validate.mjs +175 -0
- package/lib/features.mjs +13 -9
- package/lib/flows/checkpoint.mjs +184 -0
- package/lib/flows/constants.mjs +27 -0
- package/lib/flows/define.mjs +146 -0
- package/lib/flows/engine.mjs +249 -0
- package/lib/flows/errors.mjs +19 -0
- package/lib/flows/index.mjs +27 -0
- package/lib/flows/joins.mjs +18 -0
- package/lib/flows/schema.mjs +90 -0
- package/lib/flows/state.mjs +31 -0
- package/lib/frameworks/loader.mjs +99 -0
- package/lib/frameworks/schema.mjs +157 -0
- package/lib/graph/build-from-corpus.mjs +67 -0
- package/lib/graph/build-from-embed.mjs +82 -0
- package/lib/graph/build-from-registry.mjs +164 -3
- package/lib/graph/cli.mjs +456 -12
- package/lib/graph/gap-queries.mjs +156 -0
- package/lib/graph/gaps.mjs +41 -0
- package/lib/graph/impacted.mjs +129 -0
- package/lib/graph/runtime-evidence.mjs +177 -0
- package/lib/graph/security-coverage.mjs +113 -0
- package/lib/graph/staleness.mjs +241 -10
- package/lib/graph/store.mjs +24 -7
- package/lib/graph/validate.mjs +161 -0
- package/lib/headhunt.mjs +9 -8
- package/lib/health-check.mjs +15 -7
- package/lib/hook-health.mjs +1 -1
- package/lib/hooks/agent-tracker.mjs +10 -4
- package/lib/hooks/edit-guard.mjs +3 -3
- package/lib/hooks/graph-impact-advisory.mjs +2 -2
- package/lib/hooks/guard-bash.mjs +41 -15
- package/lib/hooks/mcp-health-check.mjs +11 -4
- package/lib/hooks/model-fallback.mjs +50 -6
- package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
- package/lib/hooks/pre-compact.mjs +181 -173
- package/lib/hooks/rule-verifier.mjs +2 -1
- package/lib/hooks/session-reflect.mjs +2 -1
- package/lib/hooks/session-start.mjs +3 -3
- package/lib/host/readiness.mjs +109 -0
- package/lib/host-capabilities.mjs +13 -2
- package/lib/host-disposition.mjs +29 -8
- package/lib/identity.mjs +92 -0
- package/lib/ingest/degraded-extract.mjs +96 -0
- package/lib/ingest/docling-remote.mjs +2 -1
- package/lib/ingest/provider-extract.mjs +7 -19
- package/lib/ingest/sidecar-providers.mjs +196 -0
- package/lib/ingest-tooling.mjs +11 -5
- package/lib/init-unified.mjs +55 -38
- package/lib/init-update.mjs +2 -1
- package/lib/install/legacy-global-cleanup.mjs +25 -0
- package/lib/install/stage-project.mjs +41 -4
- package/lib/intake/daemon.mjs +99 -8
- package/lib/intake/git-queue.mjs +110 -38
- package/lib/intake/prepare.mjs +2 -0
- package/lib/intake/queue-registry.mjs +50 -0
- package/lib/intake/queue.mjs +133 -17
- package/lib/intake/session-prelude.mjs +57 -8
- package/lib/integrations/intake-integrations.mjs +61 -111
- package/lib/intent-classifier.mjs +43 -5
- package/lib/libreoffice-export.mjs +8 -8
- package/lib/logging/rotate.mjs +5 -4
- package/lib/mcp/broker.mjs +332 -17
- package/lib/mcp/denied-store.mjs +95 -0
- package/lib/mcp/destructive-approval.mjs +57 -0
- package/lib/mcp/destructive-gate.mjs +30 -0
- package/lib/mcp/dispatch-envelope.mjs +199 -0
- package/lib/mcp/memory-bridge.mjs +2 -1
- package/lib/mcp/server.mjs +185 -1268
- package/lib/mcp/tool-definitions-memory.mjs +376 -0
- package/lib/mcp/tool-definitions-project.mjs +271 -0
- package/lib/mcp/tool-definitions-skills.mjs +311 -0
- package/lib/mcp/tool-definitions-workflow.mjs +398 -0
- package/lib/mcp/tool-definitions.mjs +25 -0
- package/lib/mcp/tool-rate-limit.mjs +47 -0
- package/lib/mcp/tool-registry.mjs +107 -0
- package/lib/mcp/tool-safety.mjs +95 -0
- package/lib/mcp/tool-surface-parity.mjs +60 -0
- package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
- package/lib/mcp/tools/orchestration-run.mjs +197 -19
- package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
- package/lib/mcp/tools/project.mjs +25 -8
- package/lib/mcp/tools/provider-write.mjs +187 -0
- package/lib/mcp/tools/scope.mjs +0 -3
- package/lib/mcp/tools/skills.mjs +1 -1
- package/lib/mcp/tools/storage.mjs +2 -2
- package/lib/mcp/tools/web-search-governance.mjs +96 -0
- package/lib/mcp/tools/web-search.mjs +5 -76
- package/lib/mcp/transport/auth.mjs +238 -0
- package/lib/mcp/transport/http.mjs +136 -0
- package/lib/mcp/transport/mode.mjs +31 -0
- package/lib/mcp/transport/stdio.mjs +22 -0
- package/lib/mcp-catalog.json +4 -4
- package/lib/mcp-manager.mjs +34 -23
- package/lib/mcp-platform-config.mjs +53 -20
- package/lib/mode-capabilities.mjs +109 -0
- package/lib/model-router.mjs +42 -9
- package/lib/models/catalog.mjs +40 -1
- package/lib/net-guard.mjs +247 -0
- package/lib/observation-store.mjs +6 -2
- package/lib/ollama/provision-context.mjs +2 -1
- package/lib/opencode-config.mjs +22 -3
- package/lib/opencode-runtime-plugin.mjs +120 -2
- package/lib/oracle/actions.mjs +204 -10
- package/lib/oracle/cli.mjs +24 -3
- package/lib/oracle/daemon-entry.mjs +6 -0
- package/lib/oracle/dispatch.mjs +2 -1
- package/lib/oracle/execute.mjs +2 -2
- package/lib/oracle/gaps.mjs +3 -3
- package/lib/oracle/heartbeat.mjs +53 -0
- package/lib/oracle/read-model.mjs +69 -13
- package/lib/oracle/reconcile.mjs +3 -46
- package/lib/oracle/routing.mjs +37 -31
- package/lib/oracle/synthesize.mjs +1 -1
- package/lib/orchestration/classification.mjs +434 -0
- package/lib/orchestration/delegation-flow.mjs +132 -0
- package/lib/orchestration/flow-selection.mjs +418 -0
- package/lib/orchestration/gates.mjs +244 -0
- package/lib/orchestration/host-sampling.mjs +121 -0
- package/lib/orchestration/policy-constants.mjs +48 -0
- package/lib/orchestration/provider-outcome.mjs +197 -0
- package/lib/orchestration/readiness.mjs +114 -13
- package/lib/orchestration/run-store-postgres.mjs +32 -26
- package/lib/orchestration/run-store-sqlite.mjs +8 -14
- package/lib/orchestration/run-store.mjs +25 -12
- package/lib/orchestration/runtime.mjs +508 -62
- package/lib/orchestration/store.mjs +87 -12
- package/lib/orchestration/trace-store.mjs +125 -0
- package/lib/orchestration/web-capability.mjs +60 -0
- package/lib/orchestration/worker-runtime.mjs +162 -0
- package/lib/orchestration/worker.mjs +763 -80
- package/lib/orchestration-policy.mjs +67 -1111
- package/lib/output-quality.mjs +61 -2
- package/lib/packs/cli.mjs +144 -0
- package/lib/packs/core-pack.mjs +112 -0
- package/lib/packs/enablement.mjs +187 -0
- package/lib/packs/index.mjs +23 -0
- package/lib/packs/loader.mjs +176 -0
- package/lib/packs/manifest-schema.mjs +58 -0
- package/lib/packs/prompts.mjs +120 -0
- package/lib/packs/validate.mjs +208 -0
- package/lib/path-policy.mjs +56 -0
- package/lib/plugin-registry.mjs +4 -5
- package/lib/policy/audit-gate.mjs +42 -0
- package/lib/policy/consumption-budget.mjs +149 -0
- package/lib/policy/engine.mjs +81 -6
- package/lib/policy/role-authority.mjs +89 -0
- package/lib/prompt-composer.js +19 -3
- package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
- package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
- package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
- package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
- package/lib/providers/contract/adapters/github/index.mjs +38 -3
- package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
- package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
- package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
- package/lib/providers/contract/adapters/jira/manifest.json +17 -0
- package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
- package/lib/providers/contract.mjs +207 -0
- package/lib/providers/credential-bootstrap.mjs +7 -4
- package/lib/providers/credential-sources.mjs +14 -2
- package/lib/providers/directory/index.mjs +261 -0
- package/lib/providers/feedback/index.mjs +392 -0
- package/lib/providers/filter-audit.mjs +68 -0
- package/lib/providers/filter-schema.mjs +54 -0
- package/lib/providers/github/index.mjs +31 -0
- package/lib/providers/instance-config.mjs +215 -0
- package/lib/providers/op-locate.mjs +96 -0
- package/lib/providers/op-run.mjs +64 -9
- package/lib/providers/registry.mjs +26 -3
- package/lib/providers/secret-audit-wiring.mjs +41 -18
- package/lib/providers/secret-resolver.mjs +263 -31
- package/lib/publish-template.mjs +6 -3
- package/lib/publish-tooling.mjs +4 -0
- package/lib/publish.mjs +32 -4
- package/lib/queue/pg-queue.mjs +395 -0
- package/lib/reflect.mjs +2 -1
- package/lib/registry/assemble.mjs +28 -2
- package/lib/registry/catalog.mjs +6 -0
- package/lib/registry/consolidation.mjs +8 -1
- package/lib/registry/custom-scaffold.mjs +200 -0
- package/lib/registry/custom-schema.mjs +137 -0
- package/lib/registry/loader.mjs +15 -4
- package/lib/registry/manifests/format-engines.default.json +15 -0
- package/lib/registry/manifests/surface-map.default.json +46 -0
- package/lib/registry/retired-paths.mjs +1 -0
- package/lib/registry/surface-map.mjs +100 -50
- package/lib/registry/validate.mjs +3 -3
- package/lib/render-visual-check.mjs +240 -0
- package/lib/resources/budget.mjs +40 -17
- package/lib/roles/flavor-bindings.mjs +36 -14
- package/lib/roles/gateway.mjs +15 -8
- package/lib/roots.mjs +107 -0
- package/lib/runtime/uv-bootstrap.mjs +32 -6
- package/lib/runtime/whisper-bootstrap.mjs +11 -3
- package/lib/runtime-env.mjs +5 -2
- package/lib/sandbox.mjs +1 -1
- package/lib/scheduler/index.mjs +8 -20
- package/lib/schema-infer.mjs +31 -2
- package/lib/scopes/lifecycle.mjs +29 -25
- package/lib/scopes/loader.mjs +49 -12
- package/lib/scopes/teams.mjs +1 -1
- package/lib/security/ingest-boundary.mjs +80 -0
- package/lib/security/recall-wrapper.mjs +99 -0
- package/lib/security/trust.mjs +132 -0
- package/lib/service-manager.mjs +93 -24
- package/lib/setup.mjs +41 -23
- package/lib/skills-apply.mjs +4 -2
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/state-root.mjs +147 -0
- package/lib/status.mjs +597 -6
- package/lib/storage/admin.mjs +84 -7
- package/lib/storage/backend-registry.mjs +73 -0
- package/lib/storage/backend.mjs +63 -9
- package/lib/storage/embeddings-openai.mjs +12 -2
- package/lib/storage/hybrid-query.mjs +11 -3
- package/lib/storage/retrieval-hardening.mjs +384 -0
- package/lib/storage/shared-memory.mjs +158 -0
- package/lib/storage/vector-client.mjs +69 -2
- package/lib/team/health.mjs +72 -0
- package/lib/telemetry/backends/local.mjs +9 -3
- package/lib/telemetry/client.mjs +3 -7
- package/lib/template-registry.mjs +10 -12
- package/lib/tenant/context.mjs +82 -0
- package/lib/tenant/isolation.mjs +129 -0
- package/lib/test-corpus-inventory.mjs +103 -2
- package/lib/uninstall/uninstall.mjs +78 -12
- package/lib/validator.mjs +4 -6
- package/lib/worker/entrypoint.mjs +2 -1
- package/lib/worker/run.mjs +2 -2
- package/lib/worker/trace.mjs +5 -11
- package/lib/workflow-state.mjs +52 -8
- package/lib/workflows/liveness.mjs +203 -0
- package/lib/workflows/loader.mjs +177 -0
- package/lib/workflows/manifest-schema.mjs +71 -0
- package/lib/workflows/surface-parity.mjs +118 -0
- package/lib/workflows/validate.mjs +127 -0
- package/lib/writes/control-plane.mjs +140 -0
- package/lib/writes/envelope.mjs +206 -0
- package/lib/writes/sent-log.mjs +86 -0
- package/lib/writes/write-intent.mjs +109 -0
- package/package.json +20 -7
- package/personas/construct.md +12 -3
- package/registry/agent-manifest.json +117 -0
- package/registry/capabilities.json +1987 -0
- package/rules/common/comments.md +1 -0
- package/schemas/brand-voice.schema.json +24 -0
- package/schemas/capability-registry.schema.json +72 -0
- package/schemas/certification-run.schema.json +130 -0
- package/schemas/demo-recording.schema.json +46 -0
- package/schemas/eval-dataset.schema.json +79 -0
- package/schemas/execution-capability-profile.schema.json +46 -0
- package/schemas/execution-policy.schema.json +114 -0
- package/schemas/improvement-proposal.schema.json +65 -0
- package/schemas/mcp-tool-output.schema.json +61 -0
- package/schemas/platform-capabilities.schema.json +83 -0
- package/schemas/project-config.schema.json +215 -0
- package/schemas/project-demo.schema.json +60 -0
- package/schemas/provider-behavior-matrix.schema.json +91 -0
- package/schemas/scope.schema.json +197 -0
- package/schemas/specialist-trace.schema.json +107 -0
- package/schemas/team.schema.json +99 -0
- package/schemas/unified-registry.schema.json +546 -0
- package/scripts/sync-specialists.mjs +336 -106
- package/skills/docs/adr-workflow.md +1 -1
- package/skills/docs/codebase-research-workflow.md +3 -3
- package/skills/docs/prd-workflow.md +5 -6
- package/skills/docs/runbook-workflow.md +2 -2
- package/skills/docs/user-research-workflow.md +3 -3
- package/skills/operating/fleet-health-routing.md +77 -0
- package/skills/roles/ai-engineer.md +1 -1
- package/skills/roles/architect.md +0 -1
- package/skills/roles/business-strategist.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/data-engineer.md +1 -1
- package/skills/roles/data-engineer.pipeline.md +1 -1
- package/skills/roles/data-engineer.vector-retrieval.md +1 -2
- package/skills/roles/data-engineer.warehouse.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +0 -1
- package/skills/roles/devil-advocate.md +1 -1
- package/skills/roles/docs-keeper.md +1 -1
- package/skills/roles/evaluator.md +1 -1
- package/skills/roles/explorer.md +1 -1
- package/skills/roles/platform-engineer.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -2
- package/skills/roles/qa.api-contract.md +0 -1
- package/skills/roles/qa.data-pipeline.md +0 -1
- package/skills/roles/qa.md +0 -1
- package/skills/roles/qa.web-ui.md +0 -1
- package/skills/roles/release-manager.md +1 -1
- package/skills/roles/researcher.md +0 -2
- package/skills/roles/reviewer.md +0 -3
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +0 -1
- package/skills/roles/security.privacy.md +0 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/sre.md +1 -1
- package/skills/roles/test-automation.md +1 -1
- package/skills/roles/trace-reviewer.md +1 -1
- package/skills/roles/ux-researcher.md +1 -1
- package/specialists/artifact-manifest.json +36 -36
- package/specialists/org/contracts/accessibility-to-qa.json +11 -3
- package/specialists/org/contracts/any-to-business-strategist.json +19 -7
- package/specialists/org/contracts/any-to-debugger.json +7 -1
- package/specialists/org/contracts/any-to-designer.json +7 -1
- package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
- package/specialists/org/contracts/any-to-explorer.json +13 -4
- package/specialists/org/contracts/any-to-sre-incident.json +6 -2
- package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
- package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
- package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
- package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
- package/specialists/org/contracts/architect-to-engineer.json +20 -4
- package/specialists/org/contracts/architect-to-evaluator.json +15 -5
- package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
- package/specialists/org/contracts/architect-to-operations.json +17 -4
- package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
- package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
- package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
- package/specialists/org/contracts/engineer-to-qa.json +20 -4
- package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
- package/specialists/org/contracts/explorer-to-engineer.json +11 -3
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
- package/specialists/org/contracts/operations-to-user.json +53 -0
- package/specialists/org/contracts/operations-triage-output.json +53 -0
- package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
- package/specialists/org/contracts/product-manager-to-architect.json +30 -10
- package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
- package/specialists/org/contracts/qa-to-release-manager.json +11 -3
- package/specialists/org/contracts/researcher-to-architect.json +10 -2
- package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
- package/specialists/org/contracts/reviewer-to-security.json +17 -3
- package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
- package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
- package/specialists/org/contracts/user-to-construct.json +11 -4
- package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
- package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
- package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
- package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
- package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
- package/specialists/org/groups/engineering-group.json +2 -6
- package/specialists/org/groups/governance-group.json +2 -3
- package/specialists/org/groups/operations-group.json +5 -8
- package/specialists/org/groups/product-group.json +4 -8
- package/specialists/org/groups/quality-group.json +3 -7
- package/specialists/org/groups/strategy-group.json +6 -9
- package/specialists/org/models.json.example +8 -0
- package/specialists/org/scopes/creative.json +6 -1
- package/specialists/org/scopes/operations.json +7 -1
- package/specialists/org/scopes/research.json +6 -1
- package/specialists/org/scopes/rnd.json +7 -1
- package/specialists/org/specialists/cx-architect.json +27 -8
- package/specialists/org/specialists/cx-designer.json +22 -8
- package/specialists/org/specialists/cx-engineer.json +71 -7
- package/specialists/org/specialists/cx-operations.json +89 -15
- package/specialists/org/specialists/cx-orchestrator.json +16 -4
- package/specialists/org/specialists/cx-product-manager.json +28 -9
- package/specialists/org/specialists/cx-qa.json +16 -6
- package/specialists/org/specialists/cx-researcher.json +41 -7
- package/specialists/org/specialists/cx-reviewer.json +61 -11
- package/specialists/org/specialists/cx-security.json +30 -10
- package/specialists/org/teams/design-team.json +3 -4
- package/specialists/org/teams/engineering-team.json +3 -10
- package/specialists/org/teams/governance-team.json +3 -5
- package/specialists/org/teams/operations-team.json +6 -12
- package/specialists/org/teams/product-management-team.json +2 -3
- package/specialists/org/teams/quality-team.json +4 -12
- package/specialists/org/teams/research-team.json +3 -3
- package/specialists/org/teams/strategy-team.json +7 -14
- package/specialists/prompts/cx-architect.md +20 -1
- package/specialists/prompts/cx-data-analyst.md +1 -1
- package/specialists/prompts/cx-designer.md +12 -4
- package/specialists/prompts/cx-engineer.md +15 -1
- package/specialists/prompts/cx-operations.md +14 -2
- package/specialists/prompts/cx-orchestrator.md +9 -5
- package/specialists/prompts/cx-product-manager.md +5 -1
- package/specialists/prompts/cx-qa.md +6 -2
- package/specialists/prompts/cx-researcher.md +27 -31
- package/specialists/prompts/cx-reviewer.md +19 -1
- package/specialists/prompts/cx-security.md +5 -1
- package/templates/distribution/construct-brand.typ +123 -59
- package/templates/distribution/construct-decision.typ +6 -3
- package/templates/distribution/construct-pdf.typ +11 -4
- package/templates/distribution/construct-prd.typ +6 -3
- package/templates/distribution/construct-research.typ +7 -4
- package/vendor/pandoc-ext/README.md +3 -0
- package/vendor/pandoc-ext/diagram.lua +687 -0
- package/lib/providers/contract/adapters/git/index.mjs +0 -115
- package/lib/providers/contract/adapters/slack/index.mjs +0 -175
- package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
- package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
- package/specialists/org/contracts/designer-to-accessibility.json +0 -36
- package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
- package/specialists/org/contracts/qa-to-test-automation.json +0 -42
- package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
- package/specialists/org/contracts/sre-to-release-manager.json +0 -36
- package/specialists/org/specialists/cx-accessibility.json +0 -69
- package/specialists/org/specialists/cx-ai-engineer.json +0 -75
- package/specialists/org/specialists/cx-business-strategist.json +0 -72
- package/specialists/org/specialists/cx-data-engineer.json +0 -71
- package/specialists/org/specialists/cx-devil-advocate.json +0 -71
- package/specialists/org/specialists/cx-docs-keeper.json +0 -82
- package/specialists/org/specialists/cx-evaluator.json +0 -69
- package/specialists/org/specialists/cx-explorer.json +0 -69
- package/specialists/org/specialists/cx-legal-compliance.json +0 -74
- package/specialists/org/specialists/cx-oracle.json +0 -46
- package/specialists/org/specialists/cx-platform-engineer.json +0 -80
- package/specialists/org/specialists/cx-rd-lead.json +0 -73
- package/specialists/org/specialists/cx-release-manager.json +0 -77
- package/specialists/org/specialists/cx-sre.json +0 -94
- package/specialists/org/specialists/cx-test-automation.json +0 -69
- package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
- package/specialists/org/specialists/cx-ux-researcher.json +0 -68
- package/specialists/org/teams/accessibility-team.json +0 -39
- package/specialists/org/teams/ux-research-team.json +0 -39
- package/specialists/prompts/cx-accessibility.md +0 -55
- package/specialists/prompts/cx-ai-engineer.md +0 -124
- package/specialists/prompts/cx-business-strategist.md +0 -58
- package/specialists/prompts/cx-data-engineer.md +0 -55
- package/specialists/prompts/cx-devil-advocate.md +0 -59
- package/specialists/prompts/cx-docs-keeper.md +0 -164
- package/specialists/prompts/cx-evaluator.md +0 -49
- package/specialists/prompts/cx-explorer.md +0 -71
- package/specialists/prompts/cx-legal-compliance.md +0 -58
- package/specialists/prompts/cx-oracle.md +0 -98
- package/specialists/prompts/cx-platform-engineer.md +0 -97
- package/specialists/prompts/cx-rd-lead.md +0 -59
- package/specialists/prompts/cx-release-manager.md +0 -54
- package/specialists/prompts/cx-sre.md +0 -111
- package/specialists/prompts/cx-test-automation.md +0 -55
- package/specialists/prompts/cx-trace-reviewer.md +0 -101
- package/specialists/prompts/cx-ux-researcher.md +0 -53
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/security/trust.mjs — Trust taxonomy and stamping primitives.
|
|
3
|
+
*
|
|
4
|
+
* Provides a machine-checkable trust hierarchy for all content that flows
|
|
5
|
+
* through Construct: ingested documents, recalled observations, provider
|
|
6
|
+
* responses, and built-in prompts. Callers stamp records at ingestion time
|
|
7
|
+
* and downstream consumers check the stamp before assembling model context.
|
|
8
|
+
*
|
|
9
|
+
* Integration into callers (ingest/embed/storage paths) is handled by
|
|
10
|
+
* follow-on beads (N2, N4). This module is additive — it exports utilities
|
|
11
|
+
* only; no existing code is modified.
|
|
12
|
+
*
|
|
13
|
+
* References: CX-AUDIT-LLMSEC-001, OWASP LLM01 [S12][S13], construct-9oi4.14.1
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Trust level taxonomy (ordered lowest → highest trust)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/** @type {Record<string, string>} */
|
|
21
|
+
export const TRUST_LEVELS = {
|
|
22
|
+
/** Scraped or parsed content with no authorship verification (web pages,
|
|
23
|
+
* docling-extracted text, unknown origin). Highest risk of injection. */
|
|
24
|
+
EXTERNAL_UNAUTHENTICATED: 'external-unauthenticated',
|
|
25
|
+
|
|
26
|
+
/** Content from an authenticated external actor: a known GitHub user's
|
|
27
|
+
* issue-tracker comment, a named Jira reporter, a Confluence page with
|
|
28
|
+
* attributed authorship. Lower risk but still untrusted. */
|
|
29
|
+
EXTERNAL_AUTHENTICATED: 'external-authenticated',
|
|
30
|
+
|
|
31
|
+
/** Authored by a member of the Construct team or project (e.g. a local
|
|
32
|
+
* markdown file committed by the team). Trusted but not built-in. */
|
|
33
|
+
TEAM_AUTHORED: 'team-authored',
|
|
34
|
+
|
|
35
|
+
/** Built-in prompts, validated skill packs, and internal system content.
|
|
36
|
+
* Full trust — assembled directly into the instruction channel. */
|
|
37
|
+
TRUSTED_INTERNAL: 'trusted-internal',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Ordered list for comparison (index = rank, higher index = higher trust).
|
|
41
|
+
const LEVEL_ORDER = [
|
|
42
|
+
TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED,
|
|
43
|
+
TRUST_LEVELS.EXTERNAL_AUTHENTICATED,
|
|
44
|
+
TRUST_LEVELS.TEAM_AUTHORED,
|
|
45
|
+
TRUST_LEVELS.TRUSTED_INTERNAL,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Stamping
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Stamp a trust label onto an observation or record object.
|
|
54
|
+
*
|
|
55
|
+
* @param {Record<string, unknown>} record Source object (not mutated).
|
|
56
|
+
* @param {string} level One of TRUST_LEVELS values.
|
|
57
|
+
* @param {string} source Human-readable source descriptor.
|
|
58
|
+
* @returns {Record<string, unknown>} New object with `_trust` metadata added.
|
|
59
|
+
*/
|
|
60
|
+
export function stampTrust(record, level, source) {
|
|
61
|
+
if (!Object.values(TRUST_LEVELS).includes(level)) {
|
|
62
|
+
throw new TypeError(
|
|
63
|
+
`stampTrust: unknown trust level "${level}". ` +
|
|
64
|
+
`Valid values: ${Object.values(TRUST_LEVELS).join(', ')}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
...record,
|
|
69
|
+
_trust: {
|
|
70
|
+
level,
|
|
71
|
+
source: String(source ?? 'unknown'),
|
|
72
|
+
stampedAt: Date.now(),
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Level comparison
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Return true if `level` meets or exceeds `required` in the trust hierarchy.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} level The trust level of the content being checked.
|
|
85
|
+
* @param {string} required The minimum acceptable trust level.
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
export function meetsMinTrustLevel(level, required) {
|
|
89
|
+
const levelIdx = LEVEL_ORDER.indexOf(level);
|
|
90
|
+
const requiredIdx = LEVEL_ORDER.indexOf(required);
|
|
91
|
+
if (levelIdx === -1 || requiredIdx === -1) return false;
|
|
92
|
+
return levelIdx >= requiredIdx;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Context assembly helpers
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Wrap external content with explicit untrusted delimiters so the model
|
|
101
|
+
* context assembler can visually and programmatically distinguish data from
|
|
102
|
+
* instructions.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} content Raw text content.
|
|
105
|
+
* @param {{ level: string, source: string }} trustMeta Trust metadata.
|
|
106
|
+
* @returns {string} Delimited content string.
|
|
107
|
+
*/
|
|
108
|
+
export function wrapUntrusted(content, trustMeta) {
|
|
109
|
+
const level = trustMeta?.level ?? TRUST_LEVELS.EXTERNAL_UNAUTHENTICATED;
|
|
110
|
+
const source = trustMeta?.source ?? 'unknown';
|
|
111
|
+
return `[UNTRUSTED:${level}:${source}]\n${content}\n[/UNTRUSTED]`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Recall re-grading
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Re-grade a recalled record — returns the `_trust` meta object if present,
|
|
120
|
+
* or null if the record was stored without a trust stamp.
|
|
121
|
+
*
|
|
122
|
+
* Callers should treat a null return as EXTERNAL_UNAUTHENTICATED per policy.
|
|
123
|
+
*
|
|
124
|
+
* @param {Record<string, unknown>} record A recalled observation or document.
|
|
125
|
+
* @returns {{ level: string, source: string, stampedAt: number } | null}
|
|
126
|
+
*/
|
|
127
|
+
export function recallTrustGrade(record) {
|
|
128
|
+
if (record && typeof record._trust === 'object' && record._trust !== null) {
|
|
129
|
+
return record._trust;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
package/lib/service-manager.mjs
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* spawns whatever is available and returns a result per service.
|
|
7
7
|
*/
|
|
8
8
|
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
9
10
|
import fs from 'node:fs';
|
|
10
11
|
import net from 'node:net';
|
|
11
12
|
import os from 'node:os';
|
|
12
13
|
import path from 'node:path';
|
|
13
14
|
import { fileURLToPath } from 'node:url';
|
|
14
15
|
|
|
15
|
-
import { findAvailablePort } from './host-capabilities.mjs';
|
|
16
|
+
import { findAvailablePort, detectActiveSessions } from './host-capabilities.mjs';
|
|
16
17
|
import { wrapWithOpRun } from './providers/op-run.mjs';
|
|
17
18
|
import { getUserEnvPath, loadConstructEnv, parseEnvFile, writeEnvValues } from './env-config.mjs';
|
|
18
19
|
import { runPressureRelease } from './runtime-pressure.mjs';
|
|
@@ -22,6 +23,7 @@ import { readHeartbeat } from './daemons/contract.mjs';
|
|
|
22
23
|
import { heartbeatPath } from './oracle/index.mjs';
|
|
23
24
|
import { stateDir } from './config/xdg.mjs';
|
|
24
25
|
import { doctorRoot } from './config/xdg.mjs';
|
|
26
|
+
import { resolveStateDir } from './state-root.mjs';
|
|
25
27
|
|
|
26
28
|
const INSTALL_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
29
|
|
|
@@ -163,13 +165,19 @@ async function isCopilotBridgeRunning(port) {
|
|
|
163
165
|
return probeRuntimePort(port);
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
// Opt-in 1Password resolution (ADR-0049 Design A):
|
|
167
|
-
// under `op run --env-file
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
168
|
+
// Opt-in 1Password resolution (ADR-0049 Design A): under `construct dev` the process
|
|
169
|
+
// re-execs once under a single `op run --env-file` (maybeReExecUnderOpRun), so op://
|
|
170
|
+
// references resolve one time at that stable parent and every daemon here inherits the
|
|
171
|
+
// resolved keys through liveEnv — the CONSTRUCT_OP_RUN_ACTIVE sentinel then makes
|
|
172
|
+
// wrapWithOpRun a no-op so no daemon nests a second op run. wrapWithOpRun stays the
|
|
173
|
+
// fallback for daemons started outside that parent (an opencode/copilot-bridge restart
|
|
174
|
+
// from a hook), wrapping them individually. It returns the command unchanged when
|
|
175
|
+
// CONSTRUCT_OP_ENV_FILE is unset, the file is missing, `op` is absent, or the sentinel
|
|
176
|
+
// is set, so the un-opted path is byte-for-byte the prior behavior. doctor, oracle, and
|
|
177
|
+
// cm consume no provider API keys (construct-0wmj) and are spawned unwrapped; every
|
|
178
|
+
// service is still spawned with the merged liveEnv, so config.env-only vars (including
|
|
179
|
+
// CONSTRUCT_OP_ENV_FILE and an OP_SERVICE_ACCOUNT_TOKEN) reach the child rather than
|
|
180
|
+
// only the wrapped op-run subset.
|
|
173
181
|
|
|
174
182
|
function wrapServiceSpawn(command, args, homeDir, options = {}) {
|
|
175
183
|
const env = options.env || process.env;
|
|
@@ -225,9 +233,9 @@ export function startDoctor({ rootDir, homeDir = os.homedir() } = {}) {
|
|
|
225
233
|
if (existing) return { started: false, reused: true, pid: existing.pid };
|
|
226
234
|
const indexPath = path.join(INSTALL_ROOT, 'lib', 'doctor', 'index.mjs');
|
|
227
235
|
if (!fs.existsSync(indexPath)) return { started: false, reason: 'missing-binary' };
|
|
236
|
+
// doctor consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
228
237
|
const doctorEnv = { ...process.env };
|
|
229
|
-
const
|
|
230
|
-
const { logPath } = spawnDetached(launch.command, launch.args, homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
|
|
238
|
+
const { logPath } = spawnDetached('node', [indexPath], homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
|
|
231
239
|
return { started: true, logPath };
|
|
232
240
|
}
|
|
233
241
|
|
|
@@ -244,8 +252,8 @@ export function startOracle({ rootDir, homeDir = os.homedir(), projectDir = root
|
|
|
244
252
|
CONSTRUCT_ORACLE_ROOT: rootDir,
|
|
245
253
|
CONSTRUCT_ORACLE_PROJECT: projectDir,
|
|
246
254
|
};
|
|
247
|
-
|
|
248
|
-
const { logPath } = spawnDetached(
|
|
255
|
+
// oracle consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
256
|
+
const { logPath } = spawnDetached('node', [entry], homeDir, 'oracle-daemon.log', {
|
|
249
257
|
cwd: projectDir,
|
|
250
258
|
env: oracleEnv,
|
|
251
259
|
});
|
|
@@ -338,6 +346,17 @@ export async function startServices({
|
|
|
338
346
|
const operatorKeepAlive = resolveOllamaKeepAlive(liveEnv);
|
|
339
347
|
if (operatorKeepAlive) liveEnv.OLLAMA_KEEP_ALIVE = operatorKeepAlive;
|
|
340
348
|
|
|
349
|
+
// The Copilot bridge fronts the user's Copilot entitlement over loopback HTTP, so it
|
|
350
|
+
// requires a bearer token. Generate a stable token once, persist it to the 0600
|
|
351
|
+
// config.env, and set it on liveEnv before OpenCode (the only caller) spawns so both
|
|
352
|
+
// the bridge and OpenCode inherit it; writeOpenCodeConfig wires it as an {env:} auth
|
|
353
|
+
// header. Reusing the persisted value keeps a restarted bridge and OpenCode in sync.
|
|
354
|
+
if (wants('copilot-bridge') && support.gh && detectActiveSessions().includes('github-copilot') && !liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN) {
|
|
355
|
+
const copilotBridgeToken = randomBytes(24).toString('hex');
|
|
356
|
+
liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN = copilotBridgeToken;
|
|
357
|
+
writeEnvValues(envPath, { CONSTRUCT_COPILOT_BRIDGE_TOKEN: copilotBridgeToken });
|
|
358
|
+
}
|
|
359
|
+
|
|
341
360
|
const pressureReport = runPressureReleaseFn({ env: { ...liveEnv, HOME: homeDir } });
|
|
342
361
|
if (pressureReport?.killed?.length) {
|
|
343
362
|
results.push({
|
|
@@ -354,14 +373,14 @@ export async function startServices({
|
|
|
354
373
|
if (traceBackend === 'local') {
|
|
355
374
|
results.push({
|
|
356
375
|
name: 'Telemetry',
|
|
357
|
-
url:
|
|
376
|
+
url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
|
|
358
377
|
status: 'configured',
|
|
359
378
|
note: 'local JSONL traces; remote export not configured',
|
|
360
379
|
});
|
|
361
380
|
} else if (traceBackend === 'none') {
|
|
362
381
|
results.push({
|
|
363
382
|
name: 'Telemetry',
|
|
364
|
-
url:
|
|
383
|
+
url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
|
|
365
384
|
status: 'configured',
|
|
366
385
|
note: 'remote export disabled; local JSONL traces preserved',
|
|
367
386
|
});
|
|
@@ -390,8 +409,17 @@ export async function startServices({
|
|
|
390
409
|
if (await memoryProbeFn(ports.memory)) {
|
|
391
410
|
results.push({ name: 'Memory (cm)', url: `http://127.0.0.1:${ports.memory}`, status: 'reused' });
|
|
392
411
|
} else {
|
|
393
|
-
|
|
394
|
-
spawnDetachedFn(cm
|
|
412
|
+
// cm consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
413
|
+
const cmSpawn = spawnDetachedFn('cm', ['serve', '--port', String(ports.memory)], homeDir, 'cm.log', { env: liveEnv });
|
|
414
|
+
try {
|
|
415
|
+
if (cmSpawn?.child?.pid) {
|
|
416
|
+
writeJson(portOwnershipPath(ports.memory, homeDir), {
|
|
417
|
+
pid: cmSpawn.child.pid, command: `cm serve --port ${ports.memory}`,
|
|
418
|
+
cwd: process.cwd(), marker: 'construct', constructManaged: true,
|
|
419
|
+
startedAt: new Date().toISOString(),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
} catch { /* ownership record is advisory */ }
|
|
395
423
|
results.push({ name: 'Memory (cm)', url: `http://127.0.0.1:${ports.memory}`, status: 'started' });
|
|
396
424
|
}
|
|
397
425
|
} else if (wants('memory')) {
|
|
@@ -404,7 +432,16 @@ export async function startServices({
|
|
|
404
432
|
results.push({ name: 'OpenCode', url: `http://127.0.0.1:${ports.bridge}`, status: 'reused' });
|
|
405
433
|
} else {
|
|
406
434
|
const opencode = wrapServiceSpawn('opencode', ['serve', '--port', String(ports.bridge)], homeDir, { env: liveEnv });
|
|
407
|
-
spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log');
|
|
435
|
+
const ocSpawn = spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log', { env: liveEnv });
|
|
436
|
+
try {
|
|
437
|
+
if (ocSpawn?.child?.pid) {
|
|
438
|
+
writeJson(portOwnershipPath(ports.bridge, homeDir), {
|
|
439
|
+
pid: ocSpawn.child.pid, command: `${opencode.command} ${opencode.args.join(' ')}`,
|
|
440
|
+
cwd: process.cwd(), marker: 'construct', constructManaged: true,
|
|
441
|
+
startedAt: new Date().toISOString(),
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
} catch { /* ownership record is advisory */ }
|
|
408
445
|
results.push({ name: 'OpenCode', url: `http://127.0.0.1:${ports.bridge}`, status: 'started' });
|
|
409
446
|
}
|
|
410
447
|
}
|
|
@@ -420,7 +457,16 @@ export async function startServices({
|
|
|
420
457
|
const proxyPath = path.join(INSTALL_ROOT, 'lib', 'bridges', 'copilot-proxy.mjs');
|
|
421
458
|
if (fs.existsSync(proxyPath)) {
|
|
422
459
|
const copilot = wrapServiceSpawn('node', [proxyPath, '--port', String(ports.copilotBridge)], homeDir, { env: liveEnv });
|
|
423
|
-
spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log');
|
|
460
|
+
const copilotSpawn = spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log', { env: liveEnv });
|
|
461
|
+
try {
|
|
462
|
+
if (copilotSpawn?.child?.pid) {
|
|
463
|
+
writeJson(portOwnershipPath(ports.copilotBridge, homeDir), {
|
|
464
|
+
pid: copilotSpawn.child.pid, command: `${copilot.command} ${copilot.args.join(' ')}`,
|
|
465
|
+
cwd: process.cwd(), marker: 'construct', constructManaged: true,
|
|
466
|
+
startedAt: new Date().toISOString(),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
} catch { /* ownership record is advisory */ }
|
|
424
470
|
results.push({ name: 'Copilot Bridge', url: `http://127.0.0.1:${ports.copilotBridge}`, status: 'started' });
|
|
425
471
|
} else {
|
|
426
472
|
results.push({ name: 'Copilot Bridge', status: 'unavailable', note: 'copilot-proxy binary missing in lib/bridges/' });
|
|
@@ -476,16 +522,39 @@ async function emitServiceFailures(results) {
|
|
|
476
522
|
} catch { /* best effort */ }
|
|
477
523
|
}
|
|
478
524
|
|
|
479
|
-
|
|
525
|
+
// Ownership record path for a port. startServices writes one when it spawns a service;
|
|
526
|
+
// killPortOwners reads it before sending SIGTERM so a foreign process that happens to
|
|
527
|
+
// occupy a configured port is never signalled.
|
|
528
|
+
|
|
529
|
+
function portOwnershipPath(port, homeDir) {
|
|
530
|
+
return path.join(runtimeStateDir(homeDir), `port-${port}.json`);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Predicate: does a process record represent a Construct-owned service? A record is
|
|
534
|
+
// owned when it carries a Construct env/marker set at spawn time. A foreign process or
|
|
535
|
+
// a record from before the ownership contract was introduced is not owned.
|
|
536
|
+
|
|
537
|
+
export function isConstructOwnedPort(processRecord, { port } = {}) {
|
|
538
|
+
if (!processRecord || typeof processRecord !== 'object') return false;
|
|
539
|
+
return Boolean(processRecord.marker === 'construct' || processRecord.constructManaged === true);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function killPortOwners(port, spawnSyncFn = spawnSync, homeDir = os.homedir()) {
|
|
480
543
|
if (!port || !Number.isInteger(port) || port <= 0) return false;
|
|
481
544
|
try {
|
|
482
545
|
const result = spawnSyncFn('lsof', ['-t', `-i:${port}`], { encoding: 'utf8' });
|
|
483
546
|
const pids = (result.stdout || '').trim().split(/\s+/).filter(Boolean).map(Number).filter((n) => n > 0);
|
|
484
547
|
if (pids.length === 0) return false;
|
|
548
|
+
|
|
549
|
+
// Only signal a PID if we have a durable Construct ownership record for this port
|
|
550
|
+
// AND it names this exact PID. A foreign owner gets reported, not killed.
|
|
551
|
+
const ownerRecord = readJson(portOwnershipPath(port, homeDir));
|
|
552
|
+
let killed = false;
|
|
485
553
|
for (const pid of pids) {
|
|
486
|
-
|
|
554
|
+
if (!ownerRecord || ownerRecord.pid !== pid || !isConstructOwnedPort(ownerRecord)) continue;
|
|
555
|
+
try { process.kill(pid, 'SIGTERM'); killed = true; } catch { /* already gone */ }
|
|
487
556
|
}
|
|
488
|
-
return
|
|
557
|
+
return killed;
|
|
489
558
|
} catch {
|
|
490
559
|
return false;
|
|
491
560
|
}
|
|
@@ -507,15 +576,15 @@ export async function stopServices({
|
|
|
507
576
|
const envPath = getUserEnvPath(homeDir);
|
|
508
577
|
const envValues = parseEnvFile(envPath);
|
|
509
578
|
const memoryPort = Number(envValues.MEMORY_PORT) || derivedMemoryPort();
|
|
510
|
-
const cmKilled = killPortOwners(memoryPort, spawnSyncFn);
|
|
579
|
+
const cmKilled = killPortOwners(memoryPort, spawnSyncFn, homeDir);
|
|
511
580
|
results.push({ name: 'Memory (cm)', status: cmKilled ? 'stopped' : 'not-running' });
|
|
512
581
|
|
|
513
582
|
const bridgePort = Number(envValues.BRIDGE_PORT) || 5173;
|
|
514
|
-
const openCodeKilled = killPortOwners(bridgePort, spawnSyncFn);
|
|
583
|
+
const openCodeKilled = killPortOwners(bridgePort, spawnSyncFn, homeDir);
|
|
515
584
|
results.push({ name: 'OpenCode', status: openCodeKilled ? 'stopped' : 'not-running' });
|
|
516
585
|
|
|
517
586
|
const copilotBridgePort = Number(envValues.COPILOT_BRIDGE_PORT) || 5174;
|
|
518
|
-
const copilotBridgeKilled = killPortOwners(copilotBridgePort, spawnSyncFn);
|
|
587
|
+
const copilotBridgeKilled = killPortOwners(copilotBridgePort, spawnSyncFn, homeDir);
|
|
519
588
|
results.push({ name: 'Copilot Bridge', status: copilotBridgeKilled ? 'stopped' : 'not-running' });
|
|
520
589
|
|
|
521
590
|
const stopped = results.filter((r) => r.status === 'stopped' || r.status === 'cleaned').map((r) => r.name);
|
package/lib/setup.mjs
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* lib/setup.mjs — Machine-scoped first-run setup wizard for Construct.
|
|
4
4
|
*
|
|
5
|
-
* Installs cm/cass, configures managed defaults,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Installs cm/cass, configures managed defaults, wires MCP integrations, and
|
|
6
|
+
* writes the XDG user config (~/.config/construct/config.env). Invoked by
|
|
7
|
+
* `construct install` and by lib/install/first-invocation.mjs when a TTY
|
|
8
|
+
* user is missing resources.
|
|
9
|
+
*
|
|
10
|
+
* The embedding model and LanceDB vector store provision lazily by default
|
|
11
|
+
* (construct-rf26.17) — pass --with-embeddings to warm both up during
|
|
12
|
+
* install instead of at first semantic-search use, mirroring --with-docling.
|
|
9
13
|
*
|
|
10
14
|
* Project-scoped scaffolding (`.cx/`, AGENTS.md, plan.md, adapters) lives
|
|
11
15
|
* in lib/init-unified.mjs — invoked by `construct init`. These two flows
|
|
@@ -35,6 +39,7 @@ import { getEmbeddingModelInfo, warmupEmbeddingModel } from './storage/embedding
|
|
|
35
39
|
import { buildPressureGuardValues, installPressureGuardLaunchAgent, loadPressureGuardLaunchAgent } from './runtime-pressure.mjs';
|
|
36
40
|
import { consentToInstall } from './setup-prompts.mjs';
|
|
37
41
|
import { cleanupLegacyGlobalConstruct } from './install/legacy-global-cleanup.mjs';
|
|
42
|
+
import { isMainModule } from './roots.mjs';
|
|
38
43
|
|
|
39
44
|
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
|
|
40
45
|
const HOME = os.homedir();
|
|
@@ -59,6 +64,9 @@ Flags:
|
|
|
59
64
|
--with-docling eagerly provision the docling document-extraction venv now
|
|
60
65
|
(heavy, ~10 min via uv; otherwise provisioned lazily on
|
|
61
66
|
first document ingest)
|
|
67
|
+
--with-embeddings warm the embedding model + LanceDB vector store dir now
|
|
68
|
+
(loads from local cache only, offline-safe; otherwise
|
|
69
|
+
both provision lazily on first semantic-search use)
|
|
62
70
|
--cleanup-legacy-global
|
|
63
71
|
remove legacy user-global Construct adapters/MCP/history
|
|
64
72
|
before setup; does not uninstall a global construct CLI
|
|
@@ -330,7 +338,7 @@ function runLegacyGlobalCleanup({ homeDir, dryRun }) {
|
|
|
330
338
|
// network call, or service registration that the real run would perform — and
|
|
331
339
|
// returns before touching disk so the preview is provably side-effect-free.
|
|
332
340
|
|
|
333
|
-
function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupLegacyGlobal }) {
|
|
341
|
+
function printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent, cleanupLegacyGlobal }) {
|
|
334
342
|
console.log('Construct install — dry-run (no changes written)');
|
|
335
343
|
console.log('────────────────────────────────────────────────');
|
|
336
344
|
console.log(`Scope: ${scope}`);
|
|
@@ -350,11 +358,11 @@ function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupL
|
|
|
350
358
|
console.log(` · ${getCanonicalOpenCodeConfigPath(homeDir)} (OpenCode config)`);
|
|
351
359
|
console.log(` · ${path.join(configDir(homeDir), 'lib')} → ${path.join(ROOT_DIR, 'lib')} (hook lib symlink)`);
|
|
352
360
|
console.log(` · ${path.join(stateDir(homeDir), 'workspace')} (workspace docs scaffold)`);
|
|
353
|
-
console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
|
|
361
|
+
if (withEmbeddings) console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
|
|
354
362
|
console.log('\nWould check / install runtime tooling:');
|
|
355
363
|
console.log(' · cm (memory CLI) and cass (session search) when available on PATH');
|
|
356
|
-
console.log('\nWould reach the network / external services:');
|
|
357
|
-
console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
|
|
364
|
+
if (withEmbeddings || withDocling) console.log('\nWould reach the network / external services:');
|
|
365
|
+
if (withEmbeddings) console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
|
|
358
366
|
if (withDocling) console.log(' · docling Python venv provisioning via uv (~10 min)');
|
|
359
367
|
console.log(' · construct sync --quiet, construct sync --global, construct doctor');
|
|
360
368
|
if (process.platform === 'darwin' && !noLaunchAgent) {
|
|
@@ -375,9 +383,10 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
375
383
|
return;
|
|
376
384
|
}
|
|
377
385
|
|
|
378
|
-
const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--cleanup-legacy-global', '--help', '-h']);
|
|
386
|
+
const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--with-embeddings', '--cleanup-legacy-global', '--help', '-h']);
|
|
379
387
|
const dryRun = argSet.has('--dry-run');
|
|
380
388
|
const withDocling = argSet.has('--with-docling');
|
|
389
|
+
const withEmbeddings = argSet.has('--with-embeddings');
|
|
381
390
|
const unknownFlags = args.filter((a) => {
|
|
382
391
|
if (!a.startsWith('-')) return false;
|
|
383
392
|
if (a.startsWith('--scope=') || a === '--scope') return false;
|
|
@@ -402,7 +411,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
402
411
|
printProjectScopeGuidance();
|
|
403
412
|
return;
|
|
404
413
|
}
|
|
405
|
-
printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
|
|
414
|
+
printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
|
|
406
415
|
return;
|
|
407
416
|
}
|
|
408
417
|
|
|
@@ -470,20 +479,29 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
470
479
|
|
|
471
480
|
const telemetryResult = process.env.CONSTRUCT_TELEMETRY_URL
|
|
472
481
|
? { status: 'configured', note: `remote export configured (${process.env.CONSTRUCT_TELEMETRY_URL})` }
|
|
473
|
-
: { status: 'local', note: 'local JSONL traces in
|
|
482
|
+
: { status: 'local', note: 'local JSONL traces in the machine-scoped state root; remote export optional' };
|
|
474
483
|
console.log(`Telemetry: ${telemetryResult.note}`);
|
|
475
484
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
485
|
+
// The embedding model and LanceDB vector store dir are provisioned lazily
|
|
486
|
+
// by default (construct-rf26.17): a project that never runs semantic
|
|
487
|
+
// search never pays for the ONNX model cache or an index directory. Both
|
|
488
|
+
// provision on first real use (`construct ingest`, first observation
|
|
489
|
+
// store/search) exactly like docling below. --with-embeddings opts into
|
|
490
|
+
// warming both up now instead of at first query.
|
|
491
|
+
if (withEmbeddings) {
|
|
492
|
+
fs.mkdirSync(path.dirname(defaultVectorIndexPath(homeDir)), { recursive: true });
|
|
493
|
+
try {
|
|
494
|
+
const warm = await warmupEmbeddingModel({ env: process.env });
|
|
495
|
+
if (warm.degraded) {
|
|
496
|
+
console.log(`Embeddings: degraded fallback (${warm.fallbackReason || 'model unavailable'}) — semantic search will use the hashing backend`);
|
|
497
|
+
} else {
|
|
498
|
+
console.log(`Embeddings: ${warm.model} ready (${warm.dimensions}d, warmed in ${warm.durationMs}ms)`);
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
console.log(`Embeddings: warmup skipped (${err?.message || 'unknown error'}) — model will load on first use`);
|
|
484
502
|
}
|
|
485
|
-
}
|
|
486
|
-
console.log(
|
|
503
|
+
} else {
|
|
504
|
+
console.log('Embeddings: will provision on first semantic-search use (pass --with-embeddings to warm up now)');
|
|
487
505
|
}
|
|
488
506
|
|
|
489
507
|
// Docling (document/PDF extraction) provisions a pinned Python venv via uv,
|
|
@@ -592,7 +610,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
592
610
|
console.log('Setup complete.');
|
|
593
611
|
|
|
594
612
|
console.log('\nLocal services:');
|
|
595
|
-
console.log(' Traces: local JSONL (
|
|
613
|
+
console.log(' Traces: local JSONL (machine-scoped state root)');
|
|
596
614
|
console.log(` Vector: LanceDB (embedded)`);
|
|
597
615
|
console.log(` ${managedValues.CONSTRUCT_LANCEDB_PATH}`);
|
|
598
616
|
console.log(` Credentials are saved to ${getUserEnvPath(homeDir)} for later reference.`);
|
|
@@ -623,6 +641,6 @@ export function ensureWorkspace(homeDir = HOME) {
|
|
|
623
641
|
return wsPath;
|
|
624
642
|
}
|
|
625
643
|
|
|
626
|
-
if (import.meta.url
|
|
644
|
+
if (isMainModule(import.meta.url)) {
|
|
627
645
|
runSetup({ args: process.argv.slice(2) });
|
|
628
646
|
}
|
package/lib/skills-apply.mjs
CHANGED
|
@@ -87,8 +87,10 @@ export function writeConstructManifest(cwd, profile, classification) {
|
|
|
87
87
|
* that records the recommended per-project scoping for Claude Code.
|
|
88
88
|
*
|
|
89
89
|
* Why a sidecar and not `settings.json`? Claude Code today only reads
|
|
90
|
-
* `permissions`, `hooks`, and
|
|
91
|
-
* `
|
|
90
|
+
* `permissions`, `hooks`, and policy keys (`allowedMcpServers`,
|
|
91
|
+
* `deniedMcpServers`, etc.) from settings.json — MCP server definitions
|
|
92
|
+
* live in `~/.claude.json` or `.mcp.json` instead (construct-ranh). A
|
|
93
|
+
* `disabledPlugins` key would be ignored silently either way. Writing a sidecar is
|
|
92
94
|
* honest — it documents the recommendation without pretending the harness
|
|
93
95
|
* enforces it. The construct-mcp session-start path can read this sidecar
|
|
94
96
|
* and inject a "skills out of scope for this project" note.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - cx-reviewer prevents silent rubber-stamp reviews
|
|
19
19
|
* - cx-security prevents post-hoc threat models
|
|
20
20
|
* - cx-debugger prevents symptom-only fixes
|
|
21
|
-
* - cx-
|
|
21
|
+
* - cx-operations prevents stale-doc PRs (docs-keeper folded in, construct-rf26.11)
|
|
22
22
|
* - cx-designer prevents post-hoc accessibility
|
|
23
23
|
*/
|
|
24
24
|
|
|
@@ -75,7 +75,7 @@ export const POSTCONDITIONS = {
|
|
|
75
75
|
reason: `rootCauseConfirmedVia must be one of: ${[...ROOT_CAUSE_SOURCES].join(', ')}.`,
|
|
76
76
|
},
|
|
77
77
|
],
|
|
78
|
-
'cx-
|
|
78
|
+
'cx-operations': [
|
|
79
79
|
{
|
|
80
80
|
id: 'docs-keeper.cross-doc-coherence-check-ran',
|
|
81
81
|
description: 'Docs-keeper must run the cross-doc coherence check and attach a named diff.',
|