@aiwg/cli 2026.10.1 → 2026.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3183 -24
- package/bin/aiwg.mjs +135 -47
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +78 -12
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +218 -5
- package/dist/src/cli/handlers/diagnose.js +13 -5
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +248 -30
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +169 -10
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +28 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +25 -11
- package/dist/src/cli/handlers/script-runner.js +19 -8
- package/dist/src/cli/handlers/session.js +406 -62
- package/dist/src/cli/handlers/sessions.js +243 -76
- package/dist/src/cli/handlers/setup-manifest.js +474 -79
- package/dist/src/cli/handlers/setup.js +180 -39
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +60 -3
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +307 -55
- package/dist/src/cli/handlers/utilities.js +125 -26
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +349 -35
- package/dist/src/config/cli.js +256 -23
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +38 -11
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/adapters/jev.js +75 -27
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +14 -7
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +25 -6
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +15 -14
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/index.js +1 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +942 -0
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +64 -18
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +15 -3
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +95 -5
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +366 -56
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +620 -85
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +17 -27
- package/dist/src/mcp/helpers.mjs +109 -4
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +248 -131
- package/dist/src/mcp/registry.mjs +169 -100
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +127 -82
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +22 -3
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +50 -2
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +647 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +76 -14
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3183 -24
- package/dist/src/models/model-discovery.js +931 -67
- package/dist/src/models/provider-policy.js +719 -11
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +261 -0
- package/dist/src/models/worker-direct-readiness.js +550 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +144 -0
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +986 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +430 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +53 -23
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +47 -12
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +558 -0
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +49 -0
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +43 -9
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +434 -24
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
- package/dist/src/sessions/adapters/muse.js +200 -104
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +30 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +25 -5
- package/dist/src/sessions/batch-import.js +72 -51
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +75 -2
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +84 -46
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +199 -63
- package/dist/src/sessions/workspace-discovery.js +315 -88
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +32 -2
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
- package/tools/_resolve-impl.mjs +39 -2
- package/tools/agents/deploy-agents.mjs +64 -23
- package/tools/agents/providers/base.mjs +153 -166
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +62 -49
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +360 -0
- package/tools/providers/omp-transport.mjs +194 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP profile tool filters.
|
|
3
|
+
*
|
|
4
|
+
* A profile's providerOverrides hold toolDeny / toolAllow patterns of the form
|
|
5
|
+
* `<server>__<tool>`, where <tool> may contain `*`. The `*` provider key applies
|
|
6
|
+
* to every harness; a harness's own key adds to it.
|
|
7
|
+
*
|
|
8
|
+
* Each harness enforces tool filters differently, and some not at all:
|
|
9
|
+
*
|
|
10
|
+
* claude-code permissions.deny rules `mcp__<server>__<tool>` in
|
|
11
|
+
* .claude/settings.local.json (project) or ~/.claude/settings.json (user).
|
|
12
|
+
* toolAllow is refused: Claude has no restrict-only allowlist.
|
|
13
|
+
* https://code.claude.com/docs/en/permissions
|
|
14
|
+
* codex per-server disabled_tools / enabled_tools, exact names; enabled = false
|
|
15
|
+
* for a whole-server deny. https://learn.chatgpt.com/docs/extend/mcp
|
|
16
|
+
* opencode top-level tools map of `<server>_<tool>` globs; the last matching entry
|
|
17
|
+
* wins. https://opencode.ai/docs/mcp-servers
|
|
18
|
+
* factory per-server disabledTools; disabled = true for a whole-server deny.
|
|
19
|
+
* https://docs.factory.com/cli/configuration/mcp
|
|
20
|
+
* windsurf per-server disabledTools. https://docs.devin.ai/desktop/cascade/mcp
|
|
21
|
+
* antigravity per-server disabledTools; disabled = true for a whole-server deny.
|
|
22
|
+
* https://antigravity.google/docs/mcp
|
|
23
|
+
*
|
|
24
|
+
* Cursor, Warp, OMP and Grok Build document no file-level tool filter.
|
|
25
|
+
* Anything a harness cannot express is returned as a warning, never dropped silently.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { readFile } from 'fs/promises';
|
|
29
|
+
import { createHash } from 'node:crypto';
|
|
30
|
+
import { assertConfigDestination, canonicalConfigPath, prepareConfigWrite, writeConfigTransaction } from './config-file.mjs';
|
|
31
|
+
import { resolve } from 'path';
|
|
32
|
+
import { homedir } from 'os';
|
|
33
|
+
import { normalizeRuntimeProviderId } from '../providers/provider-definitions.mjs';
|
|
34
|
+
|
|
35
|
+
const PER_SERVER_DISABLED_TOOLS = new Set(['factory', 'windsurf', 'antigravity']);
|
|
36
|
+
const SERVER_DISABLE_KEY = { factory: 'disabled', antigravity: 'disabled' };
|
|
37
|
+
|
|
38
|
+
function unique(values) {
|
|
39
|
+
return [...new Set(values)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Merge the `*` overrides with the harness's own. Keys are normalized, so `claude` and `claude-code` both apply. */
|
|
43
|
+
export function resolveToolFilters(profile, provider) {
|
|
44
|
+
const target = normalizeRuntimeProviderId(provider) || provider;
|
|
45
|
+
const deny = [];
|
|
46
|
+
const allow = [];
|
|
47
|
+
for (const [key, override] of Object.entries(profile?.providerOverrides || {})) {
|
|
48
|
+
if (key !== '*' && (normalizeRuntimeProviderId(key) || key) !== target) continue;
|
|
49
|
+
deny.push(...(override?.toolDeny || []));
|
|
50
|
+
allow.push(...(override?.toolAllow || []));
|
|
51
|
+
}
|
|
52
|
+
return { deny: unique(deny), allow: unique(allow) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function hasToolFilters(filters) {
|
|
56
|
+
return Boolean(filters && (filters.deny.length > 0 || filters.allow.length > 0));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Split `<server>__<tool>`; returns null when there is no server part. */
|
|
60
|
+
export function parseToolPattern(pattern) {
|
|
61
|
+
const index = pattern.indexOf('__');
|
|
62
|
+
if (index <= 0 || index + 2 >= pattern.length) return null;
|
|
63
|
+
return { server: pattern.slice(0, index), tool: pattern.slice(index + 2) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const isGlob = value => value.includes('*');
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Group patterns by injected server. Patterns for servers outside the injected
|
|
70
|
+
* set do not apply; malformed patterns and glob server names are warnings.
|
|
71
|
+
*/
|
|
72
|
+
function groupByServer(patterns, serverNames, kind, warnings) {
|
|
73
|
+
const groups = new Map();
|
|
74
|
+
for (const pattern of patterns) {
|
|
75
|
+
const parsed = parseToolPattern(pattern);
|
|
76
|
+
if (!parsed || isGlob(parsed.server)) {
|
|
77
|
+
warnings.push(`${kind} pattern "${pattern}" is not <server>__<tool> with a literal server name; not rendered.`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!serverNames.includes(parsed.server)) continue;
|
|
81
|
+
if (!groups.has(parsed.server)) groups.set(parsed.server, []);
|
|
82
|
+
groups.get(parsed.server).push(parsed.tool);
|
|
83
|
+
}
|
|
84
|
+
return groups;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Claude Code and opencode name a server's tools after the server with these characters replaced. */
|
|
88
|
+
const sanitize = value => value.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compute what a harness should receive.
|
|
92
|
+
*
|
|
93
|
+
* Returns:
|
|
94
|
+
* serverFields per-server fields to merge into each entry (JSON harnesses)
|
|
95
|
+
* tomlLines per-server TOML lines to append to each [mcp_servers.<name>] table
|
|
96
|
+
* topLevel fields to merge into the harness config root (opencode `tools`)
|
|
97
|
+
* claudePermissions permission rules for Claude Code's settings file
|
|
98
|
+
* warnings filters the harness cannot express
|
|
99
|
+
*/
|
|
100
|
+
export function planToolFilters(provider, serverNames, filters) {
|
|
101
|
+
const target = normalizeRuntimeProviderId(provider) || provider;
|
|
102
|
+
const plan = { serverFields: {}, tomlLines: {}, topLevel: {}, claudePermissions: null, warnings: [] };
|
|
103
|
+
if (target === 'claude-code' && filters?.allow.length) {
|
|
104
|
+
throw new Error(`claude-code: refusing toolAllow patterns ${filters.allow.map(pattern => JSON.stringify(pattern)).join(', ')}: `
|
|
105
|
+
+ 'Claude Code has no restrict-only allowlist. permissions.allow pre-approves tools; deny rules cannot express all except, '
|
|
106
|
+
+ 'and allow rules cannot carve exceptions out of deny rules. Use toolDeny instead.');
|
|
107
|
+
}
|
|
108
|
+
if (!hasToolFilters(filters)) return plan;
|
|
109
|
+
const deny = groupByServer(filters.deny, serverNames, 'toolDeny', plan.warnings);
|
|
110
|
+
const allow = groupByServer(filters.allow, serverNames, 'toolAllow', plan.warnings);
|
|
111
|
+
if (deny.size === 0 && allow.size === 0) return plan;
|
|
112
|
+
|
|
113
|
+
if (target === 'claude-code') {
|
|
114
|
+
const rule = (server, tool) => (tool === '*' ? `mcp__${sanitize(server)}` : `mcp__${sanitize(server)}__${tool}`);
|
|
115
|
+
const denyRules = [...deny].flatMap(([server, tools]) => tools.map(tool => rule(server, tool)));
|
|
116
|
+
plan.claudePermissions = { deny: unique(denyRules), allow: [] };
|
|
117
|
+
return plan;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (target === 'opencode') {
|
|
121
|
+
const tools = {};
|
|
122
|
+
for (const server of allow.keys()) tools[`${sanitize(server)}_*`] = false;
|
|
123
|
+
for (const [server, list] of deny) {
|
|
124
|
+
for (const tool of list) {
|
|
125
|
+
const key = `${sanitize(server)}_${tool}`;
|
|
126
|
+
delete tools[key];
|
|
127
|
+
tools[key] = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Specific allows follow denies under last-match-wins. An identical deny key wins.
|
|
131
|
+
for (const [server, list] of allow) {
|
|
132
|
+
for (const tool of list) {
|
|
133
|
+
if (deny.get(server)?.includes(tool)) continue;
|
|
134
|
+
const key = `${sanitize(server)}_${tool}`;
|
|
135
|
+
delete tools[key];
|
|
136
|
+
tools[key] = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
plan.topLevel.tools = tools;
|
|
140
|
+
return plan;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (target === 'codex') {
|
|
144
|
+
if (allow.size) {
|
|
145
|
+
const unrestricted = serverNames.filter(name => !allow.has(name) && !deny.get(name)?.includes('*'));
|
|
146
|
+
if (unrestricted.length) {
|
|
147
|
+
plan.warnings.push(`codex: toolAllow is per-server; servers without an allowlist remain unrestricted by toolAllow: ${unrestricted.join(', ')}. Add toolAllow patterns or toolDeny <server>__* for them.`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const name of serverNames) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
const denied = deny.get(name) || [];
|
|
153
|
+
const allowed = allow.get(name) || [];
|
|
154
|
+
if (denied.includes('*')) {
|
|
155
|
+
plan.tomlLines[name] = ['enabled = false'];
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const exact = (list, kind) => list.filter(tool => {
|
|
159
|
+
if (!isGlob(tool)) return true;
|
|
160
|
+
plan.warnings.push(`codex: ${kind} "${name}__${tool}" is a glob; Codex matches tool names exactly, so it is not rendered.`);
|
|
161
|
+
return false;
|
|
162
|
+
});
|
|
163
|
+
const enabled = allowed.includes('*') ? [] : exact(allowed, 'toolAllow');
|
|
164
|
+
const disabled = exact(denied, 'toolDeny');
|
|
165
|
+
if (enabled.length > 0) lines.push(`enabled_tools = [${enabled.map(tool => JSON.stringify(tool)).join(', ')}]`);
|
|
166
|
+
if (disabled.length > 0) lines.push(`disabled_tools = [${disabled.map(tool => JSON.stringify(tool)).join(', ')}]`);
|
|
167
|
+
if (lines.length > 0) plan.tomlLines[name] = lines;
|
|
168
|
+
}
|
|
169
|
+
return plan;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (PER_SERVER_DISABLED_TOOLS.has(target)) {
|
|
173
|
+
for (const [server, list] of deny) {
|
|
174
|
+
if (list.includes('*')) {
|
|
175
|
+
const key = SERVER_DISABLE_KEY[target];
|
|
176
|
+
if (key) plan.serverFields[server] = { [key]: true };
|
|
177
|
+
else plan.warnings.push(`${target}: toolDeny "${server}__*" disables a whole server, which ${target} has no documented file key for; not rendered.`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const exact = list.filter(tool => {
|
|
181
|
+
if (!isGlob(tool)) return true;
|
|
182
|
+
plan.warnings.push(`${target}: toolDeny "${server}__${tool}" is a glob; disabledTools takes exact names, so it is not rendered.`);
|
|
183
|
+
return false;
|
|
184
|
+
});
|
|
185
|
+
if (exact.length > 0) plan.serverFields[server] = { disabledTools: exact };
|
|
186
|
+
}
|
|
187
|
+
for (const server of allow.keys()) {
|
|
188
|
+
plan.warnings.push(`${target}: toolAllow for "${server}" is not rendered; ${target} has no tool allowlist.`);
|
|
189
|
+
}
|
|
190
|
+
return plan;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
plan.warnings.push(`${target}: tool filters are not rendered; ${target} documents no per-tool filter in its MCP config.`);
|
|
194
|
+
return plan;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Merge the per-server fields and top-level fields of a plan into a JSON harness config. */
|
|
198
|
+
export function applyJsonToolFilterPlan(config, serversKey, plan) {
|
|
199
|
+
const servers = config[serversKey] || {};
|
|
200
|
+
for (const [name, fields] of Object.entries(plan.serverFields)) {
|
|
201
|
+
if (servers[name]) {
|
|
202
|
+
const previous = servers[name];
|
|
203
|
+
if (Object.hasOwn(previous, 'disabledTools') && !Array.isArray(previous.disabledTools)) {
|
|
204
|
+
throw new Error(`Refusing tool filters for server "${name}": existing disabledTools must be an array`);
|
|
205
|
+
}
|
|
206
|
+
servers[name] = { ...previous, ...fields };
|
|
207
|
+
if (fields.disabledTools && Array.isArray(previous.disabledTools)) {
|
|
208
|
+
servers[name].disabledTools = unique([...previous.disabledTools, ...fields.disabledTools]);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (plan.topLevel.tools) {
|
|
213
|
+
const tools = { ...(config.tools || {}) };
|
|
214
|
+
// opencode uses last-match-wins, including when an existing wildcard conflicts.
|
|
215
|
+
for (const key of Object.keys(plan.topLevel.tools)) delete tools[key];
|
|
216
|
+
config.tools = { ...tools, ...plan.topLevel.tools };
|
|
217
|
+
}
|
|
218
|
+
return config;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function claudeSettingsPath(projectDir = '.', scope = 'project') {
|
|
222
|
+
if (scope === 'user') return resolve(process.env.HOME || process.env.USERPROFILE || homedir(), '.claude/settings.json');
|
|
223
|
+
return resolve(projectDir, '.claude/settings.local.json');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const isObject = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
227
|
+
const digest = value => createHash('sha256').update(value).digest('hex');
|
|
228
|
+
|
|
229
|
+
function validateSettings(settings, settingsPath) {
|
|
230
|
+
if (!isObject(settings)) throw new Error(`Claude Code settings ${settingsPath} must contain an object`);
|
|
231
|
+
if (Object.hasOwn(settings, 'permissions')) {
|
|
232
|
+
if (!isObject(settings.permissions)) {
|
|
233
|
+
throw new Error(`Claude Code settings ${settingsPath}: permissions must be an object`);
|
|
234
|
+
}
|
|
235
|
+
for (const key of ['allow', 'deny', 'ask', 'additionalDirectories']) {
|
|
236
|
+
if (Object.hasOwn(settings.permissions, key)
|
|
237
|
+
&& (!Array.isArray(settings.permissions[key]) || settings.permissions[key].some(rule => typeof rule !== 'string'))) {
|
|
238
|
+
throw new Error(`Claude Code settings ${settingsPath}: permissions.${key} must be an array of strings`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Validate destinations and settings before any MCP writes; keep ownership outside Claude's schema. */
|
|
245
|
+
export async function prepareClaudePermissions(settingsPath, permissions, {
|
|
246
|
+
userScope = false, projectRoot, managedDir, sidecar = false, mcpPath,
|
|
247
|
+
} = {}) {
|
|
248
|
+
if (permissions.allow?.length) {
|
|
249
|
+
throw new Error(`Claude Code: refusing permissions.allow ${permissions.allow.map(rule => JSON.stringify(rule)).join(', ')}: no restrict-only allowlist; use permissions.deny instead.`);
|
|
250
|
+
}
|
|
251
|
+
settingsPath = resolve(settingsPath);
|
|
252
|
+
if (mcpPath && settingsPath === resolve(mcpPath)) {
|
|
253
|
+
throw new Error(`Refusing Claude Code settings sidecar ${settingsPath}: collides with MCP config`);
|
|
254
|
+
}
|
|
255
|
+
const info = await assertConfigDestination(settingsPath, projectRoot);
|
|
256
|
+
if (mcpPath && info) {
|
|
257
|
+
const mcpInfo = await assertConfigDestination(mcpPath, projectRoot);
|
|
258
|
+
if (mcpInfo && info.dev === mcpInfo.dev && info.ino === mcpInfo.ino) {
|
|
259
|
+
throw new Error(`Refusing Claude Code settings sidecar ${settingsPath}: collides with MCP config`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
let content;
|
|
263
|
+
let existing = {};
|
|
264
|
+
if (info) {
|
|
265
|
+
content = await readFile(settingsPath, 'utf-8');
|
|
266
|
+
try {
|
|
267
|
+
existing = JSON.parse(content);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error instanceof SyntaxError) {
|
|
270
|
+
throw new Error(`Refusing to overwrite malformed Claude Code settings ${settingsPath}: invalid JSON`);
|
|
271
|
+
}
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
validateSettings(existing, settingsPath);
|
|
276
|
+
|
|
277
|
+
const canonicalPath = await canonicalConfigPath(settingsPath);
|
|
278
|
+
const recordPath = managedDir ? resolve(managedDir, 'claude-tool-permissions', `${digest(canonicalPath)}.json`) : null;
|
|
279
|
+
let record = null;
|
|
280
|
+
if (recordPath && await assertConfigDestination(recordPath, managedDir)) {
|
|
281
|
+
try {
|
|
282
|
+
record = JSON.parse(await readFile(recordPath, 'utf-8'));
|
|
283
|
+
if (!isObject(record) || record.settingsPath !== canonicalPath || !Array.isArray(record.denyAdded)
|
|
284
|
+
|| record.denyAdded.some(rule => typeof rule !== 'string')
|
|
285
|
+
|| (record.denyDigest !== undefined && (typeof record.denyDigest !== 'string' || !/^[a-f0-9]{64}$/.test(record.denyDigest)))
|
|
286
|
+
|| (record.denyPreserved !== undefined && (!Array.isArray(record.denyPreserved)
|
|
287
|
+
|| record.denyPreserved.some(rule => typeof rule !== 'string' || !record.denyAdded.includes(rule))))) {
|
|
288
|
+
throw new Error('unsupported ownership record shape');
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
throw new Error(`Invalid Claude permission ownership record ${recordPath}: ${error.message}. Delete ${recordPath} to reset ownership; existing deny rules will be treated as user-owned.`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (sidecar && info && (!record?.sidecar || record.contentDigest !== digest(content))) {
|
|
295
|
+
throw new Error(`Refusing implicit Claude Code settings sidecar ${settingsPath}: existing file was not created by AIWG or was changed. Choose another --out path.`);
|
|
296
|
+
}
|
|
297
|
+
if (sidecar && !recordPath) throw new Error('Claude settings sidecars require an ownership record directory');
|
|
298
|
+
|
|
299
|
+
const desired = unique(permissions.deny);
|
|
300
|
+
const current = existing.permissions || {};
|
|
301
|
+
const deny = [...(current.deny || [])];
|
|
302
|
+
const owned = [];
|
|
303
|
+
const preserved = [];
|
|
304
|
+
const warnings = [];
|
|
305
|
+
const unchangedDeny = record?.denyDigest === digest(JSON.stringify(deny));
|
|
306
|
+
for (const rule of record?.denyAdded || []) {
|
|
307
|
+
const index = deny.indexOf(rule);
|
|
308
|
+
if (index < 0) continue;
|
|
309
|
+
if (!unchangedDeny || record.denyPreserved?.includes(rule)) {
|
|
310
|
+
owned.push(rule);
|
|
311
|
+
preserved.push(rule);
|
|
312
|
+
} else if (desired.includes(rule)) owned.push(rule);
|
|
313
|
+
else deny.splice(index, 1); // Remove only our occurrence; preserve user-added duplicates.
|
|
314
|
+
}
|
|
315
|
+
for (const rule of desired) {
|
|
316
|
+
if (deny.includes(rule)) continue; // A pre-existing user rule never becomes AIWG-owned.
|
|
317
|
+
deny.push(rule);
|
|
318
|
+
owned.push(rule);
|
|
319
|
+
}
|
|
320
|
+
const retained = preserved.filter(rule => !desired.includes(rule));
|
|
321
|
+
if (retained.length) {
|
|
322
|
+
warnings.push(`Claude Code settings ${settingsPath}: preserving previously tracked deny rules because permissions.deny changed or ownership is uncertain: ${unique(retained).join(', ')}. Remove these rules manually from permissions.deny in ${settingsPath} if no longer wanted.`);
|
|
323
|
+
}
|
|
324
|
+
const active = Boolean(record || desired.length || sidecar);
|
|
325
|
+
const merged = active ? { ...existing, permissions: { ...current, deny } } : existing;
|
|
326
|
+
const nextContent = JSON.stringify(merged, null, 2) + '\n';
|
|
327
|
+
const writes = active ? [{
|
|
328
|
+
file: settingsPath, content: nextContent,
|
|
329
|
+
options: { userScope: userScope || sidecar, projectRoot, newFileMode: 0o600 },
|
|
330
|
+
}] : [];
|
|
331
|
+
if (active && recordPath) {
|
|
332
|
+
writes.push({
|
|
333
|
+
file: recordPath,
|
|
334
|
+
content: JSON.stringify({
|
|
335
|
+
settingsPath: canonicalPath, denyAdded: owned, denyPreserved: unique(preserved),
|
|
336
|
+
denyDigest: digest(JSON.stringify(deny)), sidecar,
|
|
337
|
+
...(sidecar ? { contentDigest: digest(nextContent) } : {}),
|
|
338
|
+
}, null, 2) + '\n',
|
|
339
|
+
options: { userScope: true, projectRoot: managedDir },
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
for (const { file, options } of writes) await prepareConfigWrite(file, options);
|
|
343
|
+
return {
|
|
344
|
+
merged, active, warnings, writes,
|
|
345
|
+
async commit() { await writeConfigTransaction(writes); },
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Merge deny rules after validation, optionally tracking ownership for profile switching. */
|
|
350
|
+
export async function mergeClaudePermissions(settingsPath, permissions, { dryRun = false, ...options } = {}) {
|
|
351
|
+
const prepared = await prepareClaudePermissions(settingsPath, permissions, options);
|
|
352
|
+
if (!dryRun) {
|
|
353
|
+
await prepared.commit();
|
|
354
|
+
for (const warning of prepared.warnings) console.warn(`WARNING ${warning}`);
|
|
355
|
+
}
|
|
356
|
+
return prepared.merged;
|
|
357
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { readFile, stat } from 'node:fs/promises';
|
|
12
12
|
import { z } from 'zod';
|
|
13
|
-
import { mcpError, mcpJson } from '../helpers.mjs';
|
|
13
|
+
import { assertNoWorkerBinding, mcpError, mcpJson } from '../helpers.mjs';
|
|
14
14
|
|
|
15
15
|
const FLEET_VERSION = 'agentic-orchestration/v1';
|
|
16
16
|
const ACTIVITY_VERSION = 'activity.event/v1';
|
|
@@ -143,6 +143,7 @@ export class AgenticSandboxMcpClient {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
async request(path, { method = 'GET', body, headers = {}, validate = (value) => safeResponse(value) } = {}) {
|
|
146
|
+
assertNoWorkerBinding(body, 'mcp');
|
|
146
147
|
const baseUrl = configuredBaseUrl(this.env);
|
|
147
148
|
const token = await managementBearer(this.env);
|
|
148
149
|
const response = await this.fetch(`${baseUrl}${path}`, {
|
|
@@ -158,6 +159,7 @@ export class AgenticSandboxMcpClient {
|
|
|
158
159
|
else return mcpError(`sandbox returned non-JSON HTTP ${response.status}`);
|
|
159
160
|
}
|
|
160
161
|
try {
|
|
162
|
+
assertNoWorkerBinding(parsed, 'mcp');
|
|
161
163
|
return typedResult(parsed, response.status, validate);
|
|
162
164
|
} catch (error) {
|
|
163
165
|
return mcpError(`${error.code ?? 'sandbox_malformed_response'}: ${error.message}`);
|
|
@@ -202,18 +202,37 @@ export function registerDecisionToolset(server) {
|
|
|
202
202
|
context: z.string().optional(),
|
|
203
203
|
threshold: z.number().min(0).max(1).default(0.8),
|
|
204
204
|
timeout_ms: z.number().int().positive().default(15000),
|
|
205
|
+
passes: z.number().int().positive().optional(),
|
|
206
|
+
escalation_passes: z.number().int().positive().optional(),
|
|
207
|
+
caller: z.string().optional().describe('Optional decision-point attribution tag, recorded in the decision ledger (#2946)'),
|
|
205
208
|
},
|
|
206
209
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
207
|
-
}, async ({ question, yes_no, choices, scale, context, threshold, timeout_ms }) => {
|
|
210
|
+
}, async ({ question, yes_no, choices, scale, context, threshold, timeout_ms, passes, escalation_passes, caller }) => {
|
|
208
211
|
const modeCount = (yes_no ? 1 : 0) + (choices ? 1 : 0) + (scale ? 1 : 0);
|
|
209
212
|
if (modeCount !== 1) throw new Error('Select exactly one of yes_no, choices, or scale');
|
|
210
|
-
|
|
213
|
+
const driverModule = await driver();
|
|
214
|
+
const opts = options();
|
|
215
|
+
const input = {
|
|
211
216
|
question,
|
|
212
217
|
mode: yes_no ? { kind: 'yes-no' } : choices ? { kind: 'choices', choices } : { kind: 'scale', low: scale.low, high: scale.high },
|
|
213
218
|
...(context === undefined ? {} : { context }),
|
|
214
219
|
threshold,
|
|
215
220
|
timeoutMs: timeout_ms,
|
|
216
|
-
|
|
221
|
+
...(passes === undefined ? {} : { passes }),
|
|
222
|
+
...(escalation_passes === undefined ? {} : { escalationPasses: escalation_passes }),
|
|
223
|
+
};
|
|
224
|
+
const sanitizedCaller = typeof caller === 'string' && driverModule.ATTRIBUTION_FIELD_PATTERN.test(caller) ? caller : null;
|
|
225
|
+
const invocation = await driverModule.runDecisionOffloadCheckpoint(input, {
|
|
226
|
+
transport: 'mcp',
|
|
227
|
+
projectRoot: opts.cwd,
|
|
228
|
+
env: opts.env,
|
|
229
|
+
caller: sanitizedCaller ?? 'decision-mcp:ask',
|
|
230
|
+
ledgerCaller: sanitizedCaller,
|
|
231
|
+
category: sanitizedCaller ? undefined : 'unattributed',
|
|
232
|
+
askRuntime: opts,
|
|
233
|
+
});
|
|
234
|
+
if (!invocation.result) throw invocation.error ?? new Error('Decision ask was skipped');
|
|
235
|
+
return invocation.result;
|
|
217
236
|
}, ASK);
|
|
218
237
|
|
|
219
238
|
registerJsonTool(server, 'decision-setup-synthetic-classification', {
|
|
@@ -23,21 +23,22 @@
|
|
|
23
23
|
|
|
24
24
|
import { z } from 'zod';
|
|
25
25
|
import { runAiwgCli, mcpError, mcpJson, AIWG_ROOT } from '../helpers.mjs';
|
|
26
|
-
import fs from 'node:fs/promises';
|
|
27
26
|
import path from 'node:path';
|
|
27
|
+
import { pinContainedRoot, readContainedFile, listContainedFiles, validateContainedPath } from '../../utils/contained-filesystem.mjs';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Register discovery tools on the MCP server.
|
|
31
31
|
*
|
|
32
32
|
* @param {import('@modelcontextprotocol/sdk/server/mcp.js').McpServer} server
|
|
33
33
|
*/
|
|
34
|
-
export function registerDiscoveryTools(server) {
|
|
34
|
+
export function registerDiscoveryTools(server, { corpusStore } = {}) {
|
|
35
|
+
const store = corpusStore ? Promise.resolve(corpusStore) : pinContainedRoot(path.resolve(AIWG_ROOT));
|
|
35
36
|
// ============================================================
|
|
36
37
|
// discover — cross-type semantic search
|
|
37
38
|
// ============================================================
|
|
38
39
|
server.registerTool('discover', {
|
|
39
40
|
title: 'Discover AIWG Capabilities',
|
|
40
|
-
description: 'Semantic search across AIWG operational assets: skills, agents, commands, rules, flows, runbooks, templates, and behaviors. Returns ranked candidates with capability summaries. Mirrors `aiwg discover --json`.',
|
|
41
|
+
description: 'Semantic search across AIWG operational assets: skills, agents, commands, rules, flows, runbooks, templates, and behaviors. Returns ranked candidates with capability summaries, governing rules, rule stats, and required fetch-and-follow checks before governed work. Mirrors `aiwg discover --json`.',
|
|
41
42
|
inputSchema: {
|
|
42
43
|
phrase: z.string().describe('Natural-language capability description (e.g. "create intake form")'),
|
|
43
44
|
type: z.enum(['skill', 'agent', 'command', 'rule', 'flow', 'runbook', 'template', 'behavior']).optional().describe('Restrict to one artifact type'),
|
|
@@ -84,12 +85,13 @@ export function registerDiscoveryTools(server) {
|
|
|
84
85
|
title: 'Show AIWG Skill',
|
|
85
86
|
description: 'Fetch the full body of an AIWG SKILL.md by name. Returns path + content envelope.',
|
|
86
87
|
inputSchema: {
|
|
88
|
+
bound_rule_bodies: z.boolean().optional().describe('Include full bound rule bodies'),
|
|
87
89
|
name: z.string().describe('Skill name (slug; e.g. "intake-wizard")'),
|
|
88
90
|
},
|
|
89
91
|
annotations: { readOnlyHint: true },
|
|
90
|
-
}, async ({ name }) => {
|
|
92
|
+
}, async ({ name, bound_rule_bodies }) => {
|
|
91
93
|
try {
|
|
92
|
-
const { stdout, code, stderr } = await runAiwgCli(['show', 'skill', name, '--json']);
|
|
94
|
+
const { stdout, code, stderr } = await runAiwgCli(['show', 'skill', name, '--json', ...(bound_rule_bodies ? ['--bound-rule-bodies'] : [])]);
|
|
93
95
|
if (code !== 0) return mcpError(`skill-show failed: ${stderr}`);
|
|
94
96
|
return mcpJson(stdout);
|
|
95
97
|
} catch (err) {
|
|
@@ -138,6 +140,27 @@ export function registerDiscoveryTools(server) {
|
|
|
138
140
|
// ============================================================
|
|
139
141
|
// rule-list / rule-show (#1320)
|
|
140
142
|
// ============================================================
|
|
143
|
+
server.registerTool('rule-inventory', {
|
|
144
|
+
title: 'Inventory configured AIWG rules',
|
|
145
|
+
description: 'Inspect scoped rule configuration and activation without asserting native session loading. Mirrors aiwg rules inventory --json.',
|
|
146
|
+
inputSchema: {
|
|
147
|
+
scope: z.enum(['workspace', 'user', 'installation', 'catalog']).default('workspace'),
|
|
148
|
+
capability: z.string().optional(), activation: z.string().optional(),
|
|
149
|
+
tier: z.string().optional(), provider: z.string().optional(), bodies: z.boolean().default(false),
|
|
150
|
+
},
|
|
151
|
+
annotations: { readOnlyHint: true },
|
|
152
|
+
}, async ({ scope, capability, activation, tier, provider, bodies }) => {
|
|
153
|
+
try {
|
|
154
|
+
const args = ['rules', 'inventory', '--scope', scope ?? 'workspace', '--json'];
|
|
155
|
+
for (const [flag, value] of [['--capability', capability], ['--activation', activation], ['--tier', tier], ['--provider', provider]]) {
|
|
156
|
+
if (value) args.push(flag, value);
|
|
157
|
+
}
|
|
158
|
+
if (bodies) args.push('--bodies');
|
|
159
|
+
const { stdout, code, stderr } = await runAiwgCli(args);
|
|
160
|
+
if (code !== 0) return mcpError(`rule-inventory failed: ${stderr || stdout}`);
|
|
161
|
+
return mcpJson(stdout);
|
|
162
|
+
} catch (err) { return mcpError(`rule-inventory: ${err.message}`); }
|
|
163
|
+
});
|
|
141
164
|
server.registerTool('rule-list', {
|
|
142
165
|
title: 'List AIWG Rules',
|
|
143
166
|
description: 'Enumerate AIWG enforcement rules (e.g. skill-discovery, no-attribution, anti-laziness, citation-policy). Top-7 CRITICAL/HIGH rules are inlined into Hermes AGENTS.md priming (skill-discovery added #1347); this tool reaches the rest.',
|
|
@@ -181,12 +204,13 @@ export function registerDiscoveryTools(server) {
|
|
|
181
204
|
title: 'Show AIWG Agent Definition',
|
|
182
205
|
description: 'Fetch the full agent definition (system prompt + frontmatter) by name.',
|
|
183
206
|
inputSchema: {
|
|
207
|
+
bound_rule_bodies: z.boolean().optional().describe('Include full bound rule bodies'),
|
|
184
208
|
name: z.string().describe('Agent name (e.g. "test-engineer", "security-auditor")'),
|
|
185
209
|
},
|
|
186
210
|
annotations: { readOnlyHint: true },
|
|
187
|
-
}, async ({ name }) => {
|
|
211
|
+
}, async ({ name, bound_rule_bodies }) => {
|
|
188
212
|
try {
|
|
189
|
-
const { stdout, code, stderr } = await runAiwgCli(['show', 'agent', name, '--json']);
|
|
213
|
+
const { stdout, code, stderr } = await runAiwgCli(['show', 'agent', name, '--json', ...(bound_rule_bodies ? ['--bound-rule-bodies'] : [])]);
|
|
190
214
|
if (code !== 0) return mcpError(`agent-show failed: ${stderr}`);
|
|
191
215
|
return mcpJson(stdout);
|
|
192
216
|
} catch (err) {
|
|
@@ -207,7 +231,7 @@ export function registerDiscoveryTools(server) {
|
|
|
207
231
|
annotations: { readOnlyHint: true },
|
|
208
232
|
}, async ({ framework, filter }) => {
|
|
209
233
|
try {
|
|
210
|
-
const results = await scanTemplates(
|
|
234
|
+
const results = await scanTemplates(await store, { framework, filter });
|
|
211
235
|
return mcpJson({ count: results.length, templates: results });
|
|
212
236
|
} catch (err) {
|
|
213
237
|
return mcpError(`template-list: ${err.message}`);
|
|
@@ -223,21 +247,27 @@ export function registerDiscoveryTools(server) {
|
|
|
223
247
|
annotations: { readOnlyHint: true },
|
|
224
248
|
}, async ({ template }) => {
|
|
225
249
|
try {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
];
|
|
232
|
-
|
|
250
|
+
validateContainedPath(template);
|
|
251
|
+
// Preserve corpus-relative and template-relative selection, but never
|
|
252
|
+
// try cwd/absolute candidates. Each candidate is read through one pin.
|
|
253
|
+
const root = await store;
|
|
254
|
+
const frameworks = 'agentic/code/frameworks';
|
|
255
|
+
const [framework, ...parts] = template.split('/');
|
|
256
|
+
const candidates = template.startsWith(`${frameworks}/`)
|
|
257
|
+
? [template]
|
|
258
|
+
: [
|
|
259
|
+
`${frameworks}/${template}`,
|
|
260
|
+
`${frameworks}/${framework}/templates/${parts.join('/')}`,
|
|
261
|
+
`${frameworks}/sdlc-complete/templates/${template}`,
|
|
262
|
+
];
|
|
263
|
+
for (const candidate of candidates) {
|
|
264
|
+
// Only template subtrees, including basename selection, are eligible.
|
|
265
|
+
if (!/^agentic\/code\/frameworks\/[^/]+\/templates\/.+/.test(candidate)) continue;
|
|
233
266
|
try {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (!resolved.startsWith(path.resolve(AIWG_ROOT))) continue;
|
|
237
|
-
const content = await fs.readFile(resolved, 'utf-8');
|
|
238
|
-
return mcpJson({ path: resolved, content });
|
|
267
|
+
const content = await readContainedFile(root, candidate);
|
|
268
|
+
if (content !== null) return mcpJson({ path: path.join(root.root, candidate), content });
|
|
239
269
|
} catch {
|
|
240
|
-
//
|
|
270
|
+
// Try the next contained candidate; no alternate root is adopted.
|
|
241
271
|
}
|
|
242
272
|
}
|
|
243
273
|
return mcpError(`template not found: ${template}`);
|
|
@@ -251,41 +281,18 @@ export function registerDiscoveryTools(server) {
|
|
|
251
281
|
* Enumerate templates by walking the canonical corpus.
|
|
252
282
|
* Restricted to the AIWG_ROOT subtree (no .aiwg/ project artifacts).
|
|
253
283
|
*/
|
|
254
|
-
async function scanTemplates(
|
|
284
|
+
async function scanTemplates(store, { framework, filter } = {}) {
|
|
285
|
+
if (framework) validateContainedPath(framework);
|
|
286
|
+
const prefix = framework ? `agentic/code/frameworks/${framework}/templates/` : 'agentic/code/frameworks/';
|
|
287
|
+
const entries = await listContainedFiles(store, prefix);
|
|
255
288
|
const results = [];
|
|
256
|
-
const root = path.join(aiwgRoot, 'agentic/code/frameworks');
|
|
257
|
-
let frameworks = [];
|
|
258
|
-
try {
|
|
259
|
-
frameworks = (await fs.readdir(root, { withFileTypes: true }))
|
|
260
|
-
.filter(d => d.isDirectory())
|
|
261
|
-
.map(d => d.name);
|
|
262
|
-
} catch {
|
|
263
|
-
return results;
|
|
264
|
-
}
|
|
265
|
-
for (const fw of frameworks) {
|
|
266
|
-
if (framework && fw !== framework) continue;
|
|
267
|
-
const tplRoot = path.join(root, fw, 'templates');
|
|
268
|
-
await walkTemplates(tplRoot, tplRoot, fw, results, filter);
|
|
269
|
-
}
|
|
270
|
-
return results;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async function walkTemplates(base, dir, framework, out, filter) {
|
|
274
|
-
let entries = [];
|
|
275
|
-
try {
|
|
276
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
277
|
-
} catch {
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
289
|
for (const entry of entries) {
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (filter && !name.includes(filter) && !rel.includes(filter)) continue;
|
|
288
|
-
out.push({ framework, name, relativePath: rel, path: full });
|
|
289
|
-
}
|
|
290
|
+
const match = /^agentic\/code\/frameworks\/([^/]+)\/templates\/(.+\.md)$/.exec(entry.path);
|
|
291
|
+
if (!match) continue;
|
|
292
|
+
const [, fw, relativePath] = match;
|
|
293
|
+
const name = path.basename(relativePath, '.md');
|
|
294
|
+
if (filter && !name.includes(filter) && !relativePath.includes(filter)) continue;
|
|
295
|
+
results.push({ framework: fw, name, relativePath, path: path.join(store.root, entry.path) });
|
|
290
296
|
}
|
|
297
|
+
return results;
|
|
291
298
|
}
|