@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
package/dist/src/decision/ask.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { aggregateModalAnswers } from './ensemble/aggregate.js';
|
|
2
|
+
import { readAiwgConfigCandidate } from '../config/aiwg-config.js';
|
|
1
3
|
import { readFile } from 'node:fs/promises';
|
|
2
4
|
import { JEV_ENDPOINT, JevDecisionAdapter } from './adapters/jev.js';
|
|
3
5
|
import { evaluateDecisionRuleset } from './evaluate.js';
|
|
4
6
|
import { artifactPin } from './validate.js';
|
|
5
|
-
import { JEV_CREDENTIAL_REF, JEV_DEFAULT_REGION, lookupJevCredential, removeJevCredentialFile,
|
|
7
|
+
import { JEV_CREDENTIAL_REF, JEV_DEFAULT_REGION, lookupJevCredential, removeJevCredentialFile, writeJevCredentialFile, } from './jev-credentials.js';
|
|
6
8
|
const CONTEXT_LIMIT_BYTES = 32 * 1024;
|
|
7
9
|
const MODEL = 'jev-latest';
|
|
8
10
|
const PURPOSE = 'agent-bounded-decision';
|
|
@@ -39,9 +41,22 @@ export function validateDecisionAskInput(input) {
|
|
|
39
41
|
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
|
|
40
42
|
usageError('invalid-threshold', '--threshold must be a probability from 0 to 1');
|
|
41
43
|
}
|
|
44
|
+
for (const key of ['minAgreement', 'agreementConfidenceFloor']) {
|
|
45
|
+
const value = input[key];
|
|
46
|
+
if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1)) {
|
|
47
|
+
usageError('invalid-agreement-policy', `${key} must be a probability from 0 to 1`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
42
50
|
const timeoutMs = input.timeoutMs ?? 15_000;
|
|
43
51
|
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0)
|
|
44
52
|
usageError('invalid-timeout', '--timeout-ms must be a positive integer');
|
|
53
|
+
const passes = input.passes ?? DEFAULT_DECISION_PASSES;
|
|
54
|
+
const escalationPasses = input.escalationPasses ?? defaultEscalationPasses(passes);
|
|
55
|
+
if (!Number.isSafeInteger(passes) || passes <= 0)
|
|
56
|
+
usageError('invalid-passes', '--passes must be a positive safe integer');
|
|
57
|
+
if (!Number.isSafeInteger(escalationPasses) || escalationPasses < passes) {
|
|
58
|
+
usageError('invalid-escalation-passes', '--escalation-passes must be a positive safe integer >= passes');
|
|
59
|
+
}
|
|
45
60
|
if (input.mode.kind === 'choices') {
|
|
46
61
|
if (input.mode.choices.length < 2 || input.mode.choices.length > 255)
|
|
47
62
|
usageError('invalid-choices', '--choices requires 2..255 ids');
|
|
@@ -239,66 +254,209 @@ function publicAnswer(mode, value, distribution) {
|
|
|
239
254
|
}
|
|
240
255
|
return typeof value === 'string' ? { answer: value } : { answer: null };
|
|
241
256
|
}
|
|
257
|
+
/** Product default: 5 passes, escalating to 10 when the first round is not decisive. */
|
|
258
|
+
export const DEFAULT_DECISION_PASSES = 5;
|
|
259
|
+
/** Escalation defaults to twice the initial pass count (5 -> 10, 10 -> 20). */
|
|
260
|
+
export function defaultEscalationPasses(passes) {
|
|
261
|
+
return passes * 2;
|
|
262
|
+
}
|
|
242
263
|
export async function askDecision(input, options = {}) {
|
|
264
|
+
if (options.cwd) {
|
|
265
|
+
let config;
|
|
266
|
+
try {
|
|
267
|
+
config = await readAiwgConfigCandidate(options.cwd);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
usageError('invalid-config', error instanceof Error ? error.message : 'Invalid project config');
|
|
271
|
+
}
|
|
272
|
+
input = { ...input, passes: input.passes ?? config?.decision?.ask?.passes,
|
|
273
|
+
escalationPasses: input.escalationPasses ?? config?.decision?.ask?.escalation_passes,
|
|
274
|
+
minAgreement: input.minAgreement ?? config?.decision?.ask?.min_agreement,
|
|
275
|
+
agreementConfidenceFloor: input.agreementConfidenceFloor ?? config?.decision?.ask?.agreement_confidence_floor };
|
|
276
|
+
}
|
|
243
277
|
validateDecisionAskInput(input);
|
|
244
278
|
const started = options.now?.() ?? Date.now();
|
|
279
|
+
const wallStarted = Date.now();
|
|
280
|
+
const passesRequested = input.passes ?? DEFAULT_DECISION_PASSES;
|
|
281
|
+
const escalationPasses = input.escalationPasses ?? defaultEscalationPasses(passesRequested);
|
|
282
|
+
const threshold = input.threshold ?? 0.8;
|
|
283
|
+
const minAgreement = input.minAgreement;
|
|
284
|
+
const agreementConfidenceFloor = input.agreementConfidenceFloor ?? 0.55;
|
|
285
|
+
const timeoutMs = input.timeoutMs ?? 15_000;
|
|
286
|
+
const passes = [];
|
|
287
|
+
let escalated = false;
|
|
288
|
+
let earlyStop = null;
|
|
289
|
+
const labels = input.mode.kind === 'yes-no' ? ['false', 'true']
|
|
290
|
+
: input.mode.kind === 'choices' ? input.mode.choices.map(parseChoice).map(choice => choice.id)
|
|
291
|
+
: Array.from({ length: input.mode.high - input.mode.low + 1 }, (_, index) => String(input.mode.kind === 'scale' ? input.mode.low + index : index));
|
|
292
|
+
const aggregate = () => {
|
|
293
|
+
const successful = passes.filter(pass => pass.status === 'succeeded');
|
|
294
|
+
const modal = aggregateModalAnswers(labels, successful.map(pass => String(pass.answer)));
|
|
295
|
+
const confidences = successful.flatMap(pass => pass.confidence === null ? [] : [pass.confidence]);
|
|
296
|
+
return {
|
|
297
|
+
algorithm: 'majority-v1', semantics: 'stability-signal-not-correctness',
|
|
298
|
+
passesRequested, escalationPasses, passesRun: passes.length, escalated, earlyStop,
|
|
299
|
+
minAgreement: minAgreement ?? null, agreementConfidenceFloor,
|
|
300
|
+
// Failed/abstaining passes have no vote but remain in the denominator.
|
|
301
|
+
agreementRatio: passes.length ? Math.max(0, ...Object.values(modal.votes)) / passes.length : 0,
|
|
302
|
+
votes: modal.votes,
|
|
303
|
+
answer: modal.value === null ? null : input.mode.kind === 'yes-no' ? modal.value === 'true'
|
|
304
|
+
: input.mode.kind === 'scale' ? Number(modal.value) : modal.value,
|
|
305
|
+
confidenceSummary: { count: confidences.length, missing: passes.length - confidences.length,
|
|
306
|
+
mean: confidences.length ? confidences.reduce((sum, value) => sum + value, 0) / confidences.length : null,
|
|
307
|
+
min: confidences.length ? Math.min(...confidences) : null, max: confidences.length ? Math.max(...confidences) : null },
|
|
308
|
+
passes: [...passes],
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
// Sampling agreement is stability evidence, not calibrated correctness.
|
|
312
|
+
// Confidence acceptance retains the original requirement that every pass succeeds.
|
|
313
|
+
const answeredBy = (summary) => {
|
|
314
|
+
const successful = passes.filter(pass => pass.status === 'succeeded');
|
|
315
|
+
if (successful.length < passesRequested || summary.answer === null)
|
|
316
|
+
return null;
|
|
317
|
+
const mean = summary.confidenceSummary.mean;
|
|
318
|
+
if (successful.length === passes.length && !summary.confidenceSummary.missing
|
|
319
|
+
&& summary.agreementRatio >= threshold && mean !== null && mean >= threshold)
|
|
320
|
+
return 'confidence';
|
|
321
|
+
if (minAgreement !== undefined && summary.agreementRatio >= minAgreement
|
|
322
|
+
&& summary.confidenceSummary.count === successful.length && mean !== null
|
|
323
|
+
&& mean + Number.EPSILON >= agreementConfidenceFloor)
|
|
324
|
+
return 'agreement';
|
|
325
|
+
return null;
|
|
326
|
+
};
|
|
327
|
+
const problem = (summary) => {
|
|
328
|
+
if (answeredBy(summary))
|
|
329
|
+
return null;
|
|
330
|
+
const failed = passes.find(pass => pass.status !== 'succeeded');
|
|
331
|
+
if (failed)
|
|
332
|
+
return failed.reason;
|
|
333
|
+
if (summary.agreementRatio < threshold)
|
|
334
|
+
return 'low-agreement';
|
|
335
|
+
return 'low-confidence';
|
|
336
|
+
};
|
|
337
|
+
const finish = (forcedReason) => {
|
|
338
|
+
const summary = aggregate();
|
|
339
|
+
const reason = forcedReason ?? problem(summary);
|
|
340
|
+
const latencyMs = Math.max(0, (options.now?.() ?? Date.now()) - started);
|
|
341
|
+
const model = passes.map(pass => pass.model).filter(model => model !== null).at(-1) ?? null;
|
|
342
|
+
const totalTokens = (key) => {
|
|
343
|
+
const values = passes.flatMap(pass => pass.usage?.[key] == null ? [] : [pass.usage[key]]);
|
|
344
|
+
return values.length ? values.reduce((sum, value) => sum + value, 0) : null;
|
|
345
|
+
};
|
|
346
|
+
const combinedUsage = passes.some(pass => pass.usage !== null)
|
|
347
|
+
? { inputTokens: totalTokens('inputTokens'), outputTokens: totalTokens('outputTokens') } : null;
|
|
348
|
+
const result = reason ? fallback(reason, latencyMs) : {
|
|
349
|
+
schema: 'aiwg-decision-ask/v1', status: 'answered', answer: summary.answer,
|
|
350
|
+
confidence: summary.agreementRatio * summary.confidenceSummary.mean, fallback: null, reason: 'none',
|
|
351
|
+
model, usage: combinedUsage, latencyMs,
|
|
352
|
+
...(input.mode.kind === 'yes-no' ? { probability: passes.reduce((sum, pass) => sum + (pass.probability ?? 0), 0)
|
|
353
|
+
/ passes.filter(pass => pass.status === 'succeeded').length } : {}),
|
|
354
|
+
};
|
|
355
|
+
return { ...result, model, usage: combinedUsage, aggregate: summary, answeredBy: reason ? null : answeredBy(summary) };
|
|
356
|
+
};
|
|
245
357
|
let credential;
|
|
246
358
|
try {
|
|
247
359
|
credential = await lookupJevCredential(options);
|
|
248
360
|
}
|
|
249
361
|
catch {
|
|
250
|
-
return
|
|
362
|
+
return finish('not-configured');
|
|
251
363
|
}
|
|
252
364
|
if (!credential?.enabled || !credential.token)
|
|
253
|
-
return
|
|
254
|
-
const timeoutMs = input.timeoutMs ?? 15_000;
|
|
365
|
+
return finish('not-configured');
|
|
255
366
|
const def = definition(input);
|
|
256
367
|
const rules = ruleset(def);
|
|
257
|
-
const bind = binding(rules, def.spec.answer.kind, timeoutMs);
|
|
258
368
|
const adapter = options.adapter ?? options.createAdapter?.({ region: credential.region, endpoint: credential.endpoint })
|
|
259
369
|
?? new JevDecisionAdapter({ region: credential.region, ...(credential.endpoint ? {
|
|
260
|
-
endpoint: credential.endpoint,
|
|
261
|
-
allowedOrigins: [new URL(credential.endpoint).origin],
|
|
370
|
+
endpoint: credential.endpoint, allowedOrigins: [new URL(credential.endpoint).origin],
|
|
262
371
|
} : {}) });
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
372
|
+
const controller = new AbortController();
|
|
373
|
+
const remainingMs = () => timeoutMs - (Date.now() - wallStarted);
|
|
374
|
+
const timer = setTimeout(() => controller.abort(), Math.max(0, remainingMs()));
|
|
375
|
+
const invocationId = options.invocationId ?? `ask-${Date.now()}`;
|
|
376
|
+
const runPass = async (index) => {
|
|
377
|
+
const passStarted = Date.now();
|
|
378
|
+
let removeListener = () => { };
|
|
379
|
+
const interrupted = new Promise((_resolve, reject) => {
|
|
380
|
+
const abort = () => reject(new Error('ask deadline'));
|
|
381
|
+
if (controller.signal.aborted)
|
|
382
|
+
abort();
|
|
383
|
+
else {
|
|
384
|
+
controller.signal.addEventListener('abort', abort, { once: true });
|
|
385
|
+
removeListener = () => controller.signal.removeEventListener('abort', abort);
|
|
386
|
+
}
|
|
275
387
|
});
|
|
388
|
+
try {
|
|
389
|
+
const result = await Promise.race([interrupted, (options.evaluate ?? evaluateDecisionRuleset)({
|
|
390
|
+
ruleset: rules, binding: binding(rules, def.spec.answer.kind, Math.max(1, remainingMs())),
|
|
391
|
+
definitions: { [def.metadata.id]: def }, input: { message: contextMessage(input) },
|
|
392
|
+
runId: options.runId ?? 'aiwg-decision-ask', invocationId: `${invocationId}-pass-${index}`,
|
|
393
|
+
signal: controller.signal, adapters: { jev: adapter },
|
|
394
|
+
resolveCredential: options.resolveCredential ?? (() => Promise.resolve(new TextEncoder().encode(credential.token))),
|
|
395
|
+
projection: { resolve: () => projectionPolicy(credential.region, credential.endpoint) },
|
|
396
|
+
})]);
|
|
397
|
+
const evaluation = result.spec.evaluations.answer;
|
|
398
|
+
const attempt = evaluation?.spec.attempts.at(-1);
|
|
399
|
+
const succeeded = evaluation?.spec.status === 'success';
|
|
400
|
+
return { index, status: succeeded ? 'succeeded' : evaluation?.spec.status === 'abstained' ? 'abstained' : 'failed',
|
|
401
|
+
reason: succeeded ? 'none' : evaluation?.spec.reason ?? result.spec.reason,
|
|
402
|
+
...(succeeded ? publicAnswer(input.mode, evaluation.spec.value, evaluation.spec.uncertainty?.distribution) : { answer: null }),
|
|
403
|
+
confidence: succeeded ? confidenceFor(input.mode, evaluation) : null,
|
|
404
|
+
model: attempt?.actualModel ?? null, usage: usage(attempt?.usage), latencyMs: Date.now() - passStarted };
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return { index, status: 'failed', reason: controller.signal.aborted ? 'timeout' : 'jev-error', answer: null,
|
|
408
|
+
confidence: null, model: null, usage: null, latencyMs: Date.now() - passStarted };
|
|
409
|
+
}
|
|
410
|
+
finally {
|
|
411
|
+
removeListener();
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const certainFailure = (total) => {
|
|
415
|
+
const summary = aggregate();
|
|
416
|
+
const remaining = total - passes.length;
|
|
417
|
+
const bestVotes = Math.max(0, ...Object.values(summary.votes));
|
|
418
|
+
const successful = passes.filter(pass => pass.status === 'succeeded');
|
|
419
|
+
const confidenceSum = successful.reduce((sum, pass) => sum + (pass.confidence ?? 0), 0);
|
|
420
|
+
// Give every unspent pass a matching vote and perfect confidence. Stop only
|
|
421
|
+
// when even that best case cannot answer, allowing roundoff at the boundary.
|
|
422
|
+
const bestAgreement = (bestVotes + remaining) / total;
|
|
423
|
+
const knownConfidence = successful.every(pass => pass.confidence !== null);
|
|
424
|
+
const confidenceReachable = successful.length === passes.length && knownConfidence
|
|
425
|
+
&& bestAgreement + 1e-12 >= threshold && (confidenceSum + remaining) / total + 1e-12 >= threshold;
|
|
426
|
+
const agreementReachable = minAgreement !== undefined && knownConfidence
|
|
427
|
+
&& successful.length + remaining >= passesRequested
|
|
428
|
+
&& bestAgreement + 1e-12 >= minAgreement
|
|
429
|
+
&& (confidenceSum + remaining) / (successful.length + remaining) + 1e-12 >= agreementConfidenceFloor;
|
|
430
|
+
return !confidenceReachable && !agreementReachable;
|
|
431
|
+
};
|
|
432
|
+
try {
|
|
433
|
+
let target = passesRequested;
|
|
434
|
+
while (passes.length < target) {
|
|
435
|
+
if (controller.signal.aborted || remainingMs() <= 0) {
|
|
436
|
+
earlyStop = 'timeout';
|
|
437
|
+
return finish('timeout');
|
|
438
|
+
}
|
|
439
|
+
const offset = passes.length;
|
|
440
|
+
if (offset >= passesRequested)
|
|
441
|
+
escalated = true;
|
|
442
|
+
passes.push(...await Promise.all(Array.from({ length: Math.min(4, target - offset) }, (_, index) => runPass(offset + index))));
|
|
443
|
+
if (controller.signal.aborted || remainingMs() <= 0) {
|
|
444
|
+
earlyStop = 'timeout';
|
|
445
|
+
return finish('timeout');
|
|
446
|
+
}
|
|
447
|
+
if (passes.length >= passesRequested && problem(aggregate()) && escalationPasses > passesRequested) {
|
|
448
|
+
target = escalationPasses;
|
|
449
|
+
}
|
|
450
|
+
if (passes.length < target && (passes.length >= passesRequested || certainFailure(passesRequested)) && certainFailure(escalationPasses)) {
|
|
451
|
+
earlyStop = 'certain-failure';
|
|
452
|
+
return finish();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return finish();
|
|
276
456
|
}
|
|
277
|
-
|
|
278
|
-
|
|
457
|
+
finally {
|
|
458
|
+
clearTimeout(timer);
|
|
279
459
|
}
|
|
280
|
-
const latencyMs = Math.max(0, (options.now?.() ?? Date.now()) - started);
|
|
281
|
-
const evaluation = result.spec.evaluations.answer;
|
|
282
|
-
const attempt = evaluation?.spec.attempts.at(-1);
|
|
283
|
-
if (!evaluation || evaluation.spec.status !== 'success')
|
|
284
|
-
return fallback(evaluation?.spec.reason ?? result.spec.reason, latencyMs, attempt);
|
|
285
|
-
const confidence = confidenceFor(input.mode, evaluation);
|
|
286
|
-
const threshold = input.threshold ?? 0.8;
|
|
287
|
-
if (confidence === null || confidence < threshold)
|
|
288
|
-
return fallback('low-confidence', latencyMs, attempt);
|
|
289
|
-
const answer = publicAnswer(input.mode, evaluation.spec.value, evaluation.spec.uncertainty?.distribution);
|
|
290
|
-
return {
|
|
291
|
-
schema: 'aiwg-decision-ask/v1',
|
|
292
|
-
status: 'answered',
|
|
293
|
-
answer: answer.answer,
|
|
294
|
-
confidence,
|
|
295
|
-
...(answer.probability === undefined ? {} : { probability: answer.probability }),
|
|
296
|
-
fallback: null,
|
|
297
|
-
reason: 'none',
|
|
298
|
-
model: attempt?.actualModel ?? null,
|
|
299
|
-
usage: usage(attempt?.usage),
|
|
300
|
-
latencyMs,
|
|
301
|
-
};
|
|
302
460
|
}
|
|
303
461
|
export async function readDecisionContextFile(filePath) {
|
|
304
462
|
const content = await readFile(filePath, 'utf8');
|
|
@@ -342,6 +500,8 @@ export async function setupJev(input = {}, options = {}) {
|
|
|
342
500
|
mode: { kind: 'yes-no' },
|
|
343
501
|
threshold: 0,
|
|
344
502
|
timeoutMs: 15_000,
|
|
503
|
+
passes: 1,
|
|
504
|
+
escalationPasses: 1,
|
|
345
505
|
}, options);
|
|
346
506
|
result.verified = verified.status === 'answered';
|
|
347
507
|
result.model = verified.model;
|
|
@@ -223,7 +223,7 @@ export function validateContextLiveApproval(approval, corpus) {
|
|
|
223
223
|
throw new Error('TV-12 collection margin is below the preregistered maximum');
|
|
224
224
|
}
|
|
225
225
|
export async function assertContextLiveSource(root, expected) {
|
|
226
|
-
const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8', timeout: 10_000 }).trim();
|
|
226
|
+
const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8', timeout: 10_000, killSignal: 'SIGKILL' }).trim();
|
|
227
227
|
if (git('rev-parse', 'HEAD') !== expected || git('status', '--porcelain', '--untracked-files=all'))
|
|
228
228
|
throw new Error('TV-12 requires clean exact source commit');
|
|
229
229
|
}
|
|
@@ -452,7 +452,7 @@ export async function collectContextLiveCase(item, corpus, approval, host, budge
|
|
|
452
452
|
}
|
|
453
453
|
/** `exact` (run evidence) requires the canonical root itself; `within` (prepared inputs) allows a descendant. */
|
|
454
454
|
export async function assertContextArtifactRoot(sourceRoot, artifactRoot, mode = 'exact') {
|
|
455
|
-
const route = JSON.parse(execFileSync('aiwg', ['artifacts', 'path', '--json', '--check-write'], { cwd: sourceRoot, encoding: 'utf8', timeout: 10_000 }));
|
|
455
|
+
const route = JSON.parse(execFileSync('aiwg', ['artifacts', 'path', '--json', '--check-write'], { cwd: sourceRoot, encoding: 'utf8', timeout: 10_000, killSignal: 'SIGKILL' }));
|
|
456
456
|
if (typeof route.artifact_root !== 'string')
|
|
457
457
|
throw new Error('Canonical artifact root unavailable');
|
|
458
458
|
const delta = relative(await realpath(route.artifact_root), await realpath(artifactRoot));
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { resolveDecisionEnablement } from '../config/decision-config.js';
|
|
1
2
|
import { existsSync } from 'node:fs';
|
|
2
3
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
6
|
import { askDecision, setupJev, DecisionAskUsageError, readDecisionContextFile, validateDecisionAskInput, } from './ask.js';
|
|
7
|
+
import { recordDecisionAsk, decisionLedgerHistory, decisionLedgerStats, resolveDecisionSessionId, resolveDecisionSession, DECISION_SESSION_ENV_VARS, parseDecisionLedgerSince, DecisionLedgerSinceError, DECISION_LEDGER_SCHEMA, DECISION_LEDGER_SCHEMA_V1, ATTRIBUTION_FIELD_PATTERN, MAX_ATTRIBUTION_FIELD_LENGTH, MAX_RECORDED_CHOICE_IDS, MAX_RECORD_TEXT_BYTES, MAX_LEDGER_RECORD_BYTES, DECISION_LEDGER_RECORD_TIMEOUT_MS, DECISION_SAVINGS_REASONING_ALLOWANCE_TOKENS, DECISION_SAVINGS_ESTIMATION_METHOD, buildDecisionLedgerRecord, appendDecisionLedgerRecord, filterDecisionLedgerRecords, } from './ledger.js';
|
|
8
|
+
export { DEFAULT_DECISION_SAVINGS_WEIGHTS, decisionSavingsWeights } from './savings.js';
|
|
9
|
+
import { resolveActiveProvider } from '../cli/provider-resolution.js';
|
|
10
|
+
export { appendDecisionOpportunity, buildDecisionOffloadSnapshot, decisionOffloadCategory, DECISION_OFFLOAD_CHECKPOINT_COVERAGE, DECISION_OFFLOAD_EXECUTABLE_COVERAGE, DECISION_OFFLOAD_SNAPSHOT_SCHEMA, DECISION_OPPORTUNITY_SCHEMA, runDecisionOffloadCheckpoint, } from './offload.js';
|
|
6
11
|
import { assertDecisionEvaluateDispatcherConfig, decisionPatternPacks, getDecisionPatternPack, governedPatternArtifacts, listDecisionPatterns, patternInputSchema, patternOutputSchema, planLiveDecisionPattern, runOfflineDecisionPattern, validateAgainstSchema, validateDecisionDocument, validateDefinition, validateRuleset, } from './index.js';
|
|
7
|
-
export { askDecision, setupJev, readDecisionContextFile, validateDecisionAskInput, DecisionAskUsageError, };
|
|
12
|
+
export { askDecision, setupJev, readDecisionContextFile, validateDecisionAskInput, DecisionAskUsageError, recordDecisionAsk, decisionLedgerHistory, decisionLedgerStats, resolveDecisionSessionId, resolveDecisionSession, DECISION_SESSION_ENV_VARS, parseDecisionLedgerSince, DecisionLedgerSinceError, DECISION_LEDGER_SCHEMA, DECISION_LEDGER_SCHEMA_V1, ATTRIBUTION_FIELD_PATTERN, MAX_ATTRIBUTION_FIELD_LENGTH, MAX_RECORDED_CHOICE_IDS, MAX_RECORD_TEXT_BYTES, MAX_LEDGER_RECORD_BYTES, DECISION_LEDGER_RECORD_TIMEOUT_MS, DECISION_SAVINGS_REASONING_ALLOWANCE_TOKENS, DECISION_SAVINGS_ESTIMATION_METHOD, buildDecisionLedgerRecord, appendDecisionLedgerRecord, filterDecisionLedgerRecords, resolveActiveProvider, };
|
|
8
13
|
const DECISION_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
9
14
|
const VALIDATION_TARGETS = ['request', 'definition', 'ruleset', 'binding'];
|
|
10
15
|
const OFFLINE_ADAPTER = 'agentic/code/addons/decision-engine/examples/fixture-jev-adapter.mjs';
|
|
@@ -222,9 +227,11 @@ export function decisionCapabilities(options = {}) {
|
|
|
222
227
|
const env = driverEnv(options);
|
|
223
228
|
const profiles = parseMcpRequestProfiles(env.AIWG_DECISION_MCP_REQUESTS, options.cwd ?? process.cwd());
|
|
224
229
|
const packs = decisionPatternPacks;
|
|
230
|
+
const enablement = resolveDecisionEnablement(options);
|
|
225
231
|
return {
|
|
226
232
|
schema: 'aiwg-decision-driver-capabilities/v1',
|
|
227
|
-
enabled:
|
|
233
|
+
enabled: enablement.enabled,
|
|
234
|
+
enabledSource: enablement.source,
|
|
228
235
|
offlineReady: true,
|
|
229
236
|
primitives: [...new Set(packs.map(pack => pack.primitive))].sort(),
|
|
230
237
|
patterns: {
|
|
@@ -236,19 +243,19 @@ export function decisionCapabilities(options = {}) {
|
|
|
236
243
|
features: {
|
|
237
244
|
patterns: ['list', 'show', 'offline-run', 'live-plan'],
|
|
238
245
|
validation: [...VALIDATION_TARGETS],
|
|
239
|
-
evaluate: {
|
|
246
|
+
evaluate: { requiresEnablement: true, mcpRequiresOptIn: true, mcpProfiles: profiles.map(profile => profile.name) },
|
|
240
247
|
setup: ['synthetic-classification'],
|
|
241
248
|
experimental: packs.filter(pack => pack.status === 'experimental').map(pack => pack.id),
|
|
242
249
|
},
|
|
243
250
|
backend: {
|
|
244
|
-
configured:
|
|
251
|
+
configured: enablement.enabled,
|
|
245
252
|
probed: false,
|
|
246
253
|
status: 'not-probed',
|
|
247
254
|
note: 'Capabilities do not infer credential, network, dispatcher, or backend readiness.',
|
|
248
255
|
},
|
|
249
256
|
configuration: {
|
|
250
257
|
env: {
|
|
251
|
-
AIWG_DECISION_ENABLED: env.AIWG_DECISION_ENABLED
|
|
258
|
+
AIWG_DECISION_ENABLED: ['0', '1'].includes(env.AIWG_DECISION_ENABLED ?? '') ? 'set' : 'unset',
|
|
252
259
|
AIWG_DECISION_MCP_REQUESTS: profiles.length ? 'configured' : 'unset',
|
|
253
260
|
},
|
|
254
261
|
},
|
|
@@ -321,7 +328,7 @@ export async function validateDecisionInput(target, filePath, options = {}) {
|
|
|
321
328
|
export async function evaluateRequestPath(requestPath, options = {}, evaluateOptions = {}) {
|
|
322
329
|
const env = driverEnv(options);
|
|
323
330
|
const resolved = resolvePath(requestPath, options.cwd ?? process.cwd());
|
|
324
|
-
if (
|
|
331
|
+
if (!resolveDecisionEnablement(options).enabled) {
|
|
325
332
|
return { schema: 'aiwg-decision-evaluate/v1', status: 'denied', reason: 'explicit-enable-required', exitCode: 2, result: null };
|
|
326
333
|
}
|
|
327
334
|
const validation = await validateDecisionInput('request', resolved, options);
|
|
@@ -336,7 +343,7 @@ export async function evaluateRequestPath(requestPath, options = {}, evaluateOpt
|
|
|
336
343
|
argv.push('--host-policy-module', resolvePath(evaluateOptions.hostPolicyModulePath, options.cwd ?? process.cwd()));
|
|
337
344
|
const exitCode = await dispatcher.runDecisionEvaluate({
|
|
338
345
|
argv,
|
|
339
|
-
env,
|
|
346
|
+
env: { ...env, AIWG_DECISION_ENABLED: '1' },
|
|
340
347
|
runtime,
|
|
341
348
|
stdout: { write(chunk) { stdout += String(chunk); return true; } },
|
|
342
349
|
stderr: { write(chunk) { stderr += String(chunk); return true; } },
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { admitEntry } from '../entry.js';
|
|
2
2
|
import { compareEnsembleKeys, ensembleContractDigest, EnsembleContractError, validateEnsemblePolicy, } from './contract.js';
|
|
3
|
+
/** Modal buckets, with the existing locale-independent canonical tie break. */
|
|
4
|
+
export function aggregateModalAnswers(labels, answers) {
|
|
5
|
+
const ordered = [...labels].sort(compareEnsembleKeys);
|
|
6
|
+
const votes = Object.fromEntries(ordered.map(label => [label, 0]));
|
|
7
|
+
for (const answer of answers) {
|
|
8
|
+
if (!Object.hasOwn(votes, answer))
|
|
9
|
+
throw new EnsembleContractError('unknown modal answer', 'semantic');
|
|
10
|
+
votes[answer] += 1;
|
|
11
|
+
}
|
|
12
|
+
const top = Math.max(0, ...Object.values(votes));
|
|
13
|
+
const winners = ordered.filter(label => votes[label] === top);
|
|
14
|
+
return { value: answers.length ? winners[0] : null, votes, tie: winners.length > 1,
|
|
15
|
+
agreementRatio: answers.length ? top / answers.length : 0 };
|
|
16
|
+
}
|
|
3
17
|
/** Deterministic reference aggregation over already-recorded member results. It never dispatches.
|
|
4
18
|
* Member order does not matter: results are put in canonical (memberId, sampleIndex) order first,
|
|
5
19
|
* so every sum and tie-break runs in the same sequence for any permutation of the input. */
|
|
@@ -24,12 +38,8 @@ export function aggregateEnsembleResults(policyInput, resultsInput, options = {}
|
|
|
24
38
|
const { metric } = policy.disagreement;
|
|
25
39
|
let candidate = null;
|
|
26
40
|
let tie = false;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
for (const result of succeeded)
|
|
30
|
-
counts[vote(policy, result)] += 1;
|
|
31
|
-
return counts;
|
|
32
|
-
};
|
|
41
|
+
const modal = policy.primitive === 'ordinal-score' ? null
|
|
42
|
+
: aggregateModalAnswers(labels, succeeded.map(result => vote(policy, result)));
|
|
33
43
|
const distributions = () => succeeded.map(result => distributionOf(policy, labels, result));
|
|
34
44
|
const meanOf = (items) => Object.fromEntries(labels.map(label => [label, n ? round(items.reduce((sum, item) => sum + item[label], 0) / n) : 0]));
|
|
35
45
|
const scores = () => succeeded.map(result => result.value);
|
|
@@ -40,7 +50,7 @@ export function aggregateEnsembleResults(policyInput, resultsInput, options = {}
|
|
|
40
50
|
return n ? winners[0] : null;
|
|
41
51
|
};
|
|
42
52
|
if (algorithm === 'majority-v1' || metric === 'vote-share-v1' || metric === 'normalized-entropy-v1')
|
|
43
|
-
statistics.votes = votes
|
|
53
|
+
statistics.votes = modal.votes;
|
|
44
54
|
if (policy.primitive === 'truth-probability' && (algorithm === 'mean-probability-v1' || metric === 'jensen-shannon-v1')) {
|
|
45
55
|
statistics.meanProbability = n ? round(succeeded.reduce((sum, result) => sum + result.value, 0) / n) : null;
|
|
46
56
|
}
|
|
@@ -55,8 +65,10 @@ export function aggregateEnsembleResults(policyInput, resultsInput, options = {}
|
|
|
55
65
|
const middle = Math.floor(n / 2);
|
|
56
66
|
statistics.medianScore = round(n % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2);
|
|
57
67
|
}
|
|
58
|
-
if (algorithm === 'majority-v1')
|
|
59
|
-
candidate =
|
|
68
|
+
if (algorithm === 'majority-v1') {
|
|
69
|
+
candidate = modal.value;
|
|
70
|
+
tie = modal.tie;
|
|
71
|
+
}
|
|
60
72
|
else if (algorithm === 'mean-probability-v1')
|
|
61
73
|
candidate = policy.primitive === 'choice' ? pick(statistics.meanDistribution) : statistics.meanProbability;
|
|
62
74
|
else if (algorithm === 'score-distribution-mean-v1')
|
|
@@ -68,7 +80,8 @@ export function aggregateEnsembleResults(policyInput, resultsInput, options = {}
|
|
|
68
80
|
tie = n % 2 === 0 && sorted[middle - 1] !== sorted[middle];
|
|
69
81
|
candidate = tie ? sorted[middle - 1] : statistics.medianScore;
|
|
70
82
|
}
|
|
71
|
-
const disagreementValue = n === 0 ? 1 :
|
|
83
|
+
const disagreementValue = n === 0 ? 1 : metric === 'vote-share-v1' ? 1 - modal.agreementRatio
|
|
84
|
+
: disagreement(policy, statistics, distributions, n);
|
|
72
85
|
const valueBps = Math.round(Math.min(1, Math.max(0, disagreementValue)) * 10_000);
|
|
73
86
|
const exceeded = valueBps > policy.disagreement.thresholdBps;
|
|
74
87
|
let disposition = 'accept';
|
|
@@ -249,9 +249,13 @@ function dispatchOrder(rows, seed) {
|
|
|
249
249
|
return [...development, ...test];
|
|
250
250
|
}
|
|
251
251
|
export function prepareD17MultifactStudy(seed, moduleDigest, sourceDigests) {
|
|
252
|
-
if (
|
|
252
|
+
if (typeof seed === 'string' && seed.length > 32)
|
|
253
|
+
refuse('seed-too-long');
|
|
254
|
+
if (typeof seed !== 'string' || !/^[a-z0-9][a-z0-9-]{0,31}$/.test(seed))
|
|
255
|
+
refuse('seed-invalid');
|
|
256
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(moduleDigest)
|
|
253
257
|
|| !Object.keys(sourceDigests).length || Object.values(sourceDigests).some(v => !/^sha256:[a-f0-9]{64}$/.test(v)))
|
|
254
|
-
|
|
258
|
+
refuse('source-pin-mismatch');
|
|
255
259
|
const generated = [];
|
|
256
260
|
const gold = { schemaVersion: 'decision-d17mf-gold/v1', labels: {}, worlds: {} };
|
|
257
261
|
for (let index = 0; index < D17MF_ROWS; index++) {
|
|
@@ -16,10 +16,29 @@ export class EntryAdmissionError extends Error {
|
|
|
16
16
|
this.name = 'EntryAdmissionError';
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Starts an admission-budget clock using per-thread CPU time.
|
|
21
|
+
*
|
|
22
|
+
* Per-thread CPU time measures only the work the admitting call itself did
|
|
23
|
+
* on this thread, so it is immune both to host scheduling contention
|
|
24
|
+
* pausing this thread between reads (the root cause of #3047's false
|
|
25
|
+
* `time-budget` rejections of legitimate input under host load) and to
|
|
26
|
+
* other threads' or background GC's CPU time landing on this same process
|
|
27
|
+
* (which a process-wide `process.cpuUsage()` clock would still charge here
|
|
28
|
+
* -- not a safe substitute for the same reason `performance.now()` was not).
|
|
29
|
+
*/
|
|
30
|
+
export function admissionClockStart() {
|
|
31
|
+
return process.threadCpuUsage();
|
|
32
|
+
}
|
|
33
|
+
/** Elapsed time since `start` (from `admissionClockStart`), in milliseconds. */
|
|
34
|
+
export function elapsedAdmissionMs(start) {
|
|
35
|
+
const diff = process.threadCpuUsage(start);
|
|
36
|
+
return (diff.user + diff.system) / 1000;
|
|
37
|
+
}
|
|
19
38
|
/** Check the object graph before JSON serialization, schema validation or digesting. */
|
|
20
39
|
export function admitEntry(value, limits = DEFAULT_ENTRY_LIMITS) {
|
|
21
40
|
const counts = { entries: 0, properties: 0, bytes: 0, estimatedMemoryBytes: 0 };
|
|
22
|
-
const started =
|
|
41
|
+
const started = admissionClockStart();
|
|
23
42
|
const encoder = new TextEncoder();
|
|
24
43
|
const stack = new Set();
|
|
25
44
|
const fail = (reason) => { throw new EntryAdmissionError(reason, counts); };
|
|
@@ -32,7 +51,7 @@ export function admitEntry(value, limits = DEFAULT_ENTRY_LIMITS) {
|
|
|
32
51
|
fail('memory-budget');
|
|
33
52
|
};
|
|
34
53
|
const visit = (item, depth) => {
|
|
35
|
-
if (
|
|
54
|
+
if (elapsedAdmissionMs(started) > limits.timeMs)
|
|
36
55
|
fail('time-budget');
|
|
37
56
|
if (++counts.entries > limits.entries)
|
|
38
57
|
fail('entry-count');
|
|
@@ -107,7 +126,7 @@ export function parseDecisionJson(source, limits = DEFAULT_ENTRY_LIMITS) {
|
|
|
107
126
|
throw new EntryAdmissionError('serialized-bytes', { bytes });
|
|
108
127
|
if (bytes * 4 > limits.memoryBytes)
|
|
109
128
|
throw new EntryAdmissionError('memory-budget', { bytes });
|
|
110
|
-
const started =
|
|
129
|
+
const started = admissionClockStart();
|
|
111
130
|
let value;
|
|
112
131
|
try {
|
|
113
132
|
value = JSON.parse(source);
|
|
@@ -118,7 +137,7 @@ export function parseDecisionJson(source, limits = DEFAULT_ENTRY_LIMITS) {
|
|
|
118
137
|
const document = parseDocument(source, { uniqueKeys: true });
|
|
119
138
|
if (document.errors.length)
|
|
120
139
|
throw new EntryAdmissionError('duplicate-or-invalid-key', { bytes });
|
|
121
|
-
if (
|
|
140
|
+
if (elapsedAdmissionMs(started) > limits.timeMs)
|
|
122
141
|
throw new EntryAdmissionError('time-budget', { bytes });
|
|
123
142
|
admitEntry(value, limits);
|
|
124
143
|
return value;
|
|
@@ -130,7 +149,7 @@ export function parseDecisionYaml(source, limits = DEFAULT_ENTRY_LIMITS) {
|
|
|
130
149
|
throw new EntryAdmissionError('serialized-bytes', { bytes });
|
|
131
150
|
if (bytes * 4 > limits.memoryBytes)
|
|
132
151
|
throw new EntryAdmissionError('memory-budget', { bytes });
|
|
133
|
-
const started =
|
|
152
|
+
const started = admissionClockStart();
|
|
134
153
|
const document = parseDocument(source, { uniqueKeys: true });
|
|
135
154
|
if (document.errors.length)
|
|
136
155
|
throw new EntryAdmissionError('duplicate-or-invalid-key', { bytes });
|
|
@@ -160,7 +179,7 @@ export function parseDecisionYaml(source, limits = DEFAULT_ENTRY_LIMITS) {
|
|
|
160
179
|
}
|
|
161
180
|
};
|
|
162
181
|
inspect(document.contents, 0);
|
|
163
|
-
if (
|
|
182
|
+
if (elapsedAdmissionMs(started) > limits.timeMs)
|
|
164
183
|
throw new EntryAdmissionError('time-budget', { bytes, entries });
|
|
165
184
|
const value = document.toJS();
|
|
166
185
|
admitEntry(value, limits);
|
|
@@ -796,7 +796,7 @@ export async function dagLivePriorSpend(artifactRoot) {
|
|
|
796
796
|
export async function assertDagLiveArtifactRoot(sourceRoot, artifactRoot) {
|
|
797
797
|
let route;
|
|
798
798
|
try {
|
|
799
|
-
route = JSON.parse(execFileSync('aiwg', ['artifacts', 'path', '--json', '--check-write'], { cwd: sourceRoot, encoding: 'utf8', timeout: 30_000 }));
|
|
799
|
+
route = JSON.parse(execFileSync('aiwg', ['artifacts', 'path', '--json', '--check-write'], { cwd: sourceRoot, encoding: 'utf8', timeout: 30_000, killSignal: 'SIGKILL' }));
|
|
800
800
|
}
|
|
801
801
|
catch {
|
|
802
802
|
throw new Error('D12 requires the canonical artifact root');
|
|
@@ -99,6 +99,15 @@ export function finalizeDecisionGraphRun(graph, plan, report, records, ceilings
|
|
|
99
99
|
report.realizedResources.nodeRuns !== flowRecords.length + approvedGates ||
|
|
100
100
|
report.realizedResources.tokens !== flowRecords.reduce((sum, item) => sum + item.response.usage.tokens, 0) ||
|
|
101
101
|
Math.abs(report.realizedResources.costUsd - flowRecords.reduce((sum, item) => sum + item.response.usage.costUsd, 0)) > 1e-9) {
|
|
102
|
+
// Admission or settlement can reject (for example after a wall-clock deadline)
|
|
103
|
+
// before the adapter emits an observation. Flow retains that failure in its
|
|
104
|
+
// trace; surface the cause instead of misdiagnosing it as lost evidence.
|
|
105
|
+
// Still reject the run: an unobserved invocation cannot produce a receipt.
|
|
106
|
+
const failure = report.status === 'failed' && report.trace.find(event => event.type === 'node-attempt-failed' && event.nodeId && !observed.has(event.nodeId) &&
|
|
107
|
+
graph.nodes.some(node => node.id === event.nodeId) &&
|
|
108
|
+
report.checkpoint.failed?.includes(event.nodeId) && typeof event.message === 'string');
|
|
109
|
+
if (failure)
|
|
110
|
+
throw new DecisionGraphError(`Flow graph node '${failure.nodeId}' failed before observation: ${failure.message}`);
|
|
102
111
|
throw new DecisionGraphError('missing Flow graph observation');
|
|
103
112
|
}
|
|
104
113
|
const candidates = graph.terminals.filter(id => observed.get(id)?.status === 'ok');
|