akm-cli 0.9.1 → 0.9.2-alpha.2
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/CHANGELOG.md +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { ENGINE_NAME_PATTERN_SOURCE } from "../../core/config/engine-semantics.js";
|
|
5
|
+
import { ConfigError } from "../../core/errors.js";
|
|
6
|
+
import { DURATION_UNITS, parseDuration } from "../../core/time.js";
|
|
7
|
+
import { cloneExecutionJson, cloneExecutionJsonObject, } from "../../execution/json.js";
|
|
8
|
+
import { EXECUTION_MAX_TIMEOUT_MS } from "../../execution/limits.js";
|
|
9
|
+
import { assertSnapshotKeys, requireSnapshotField, snapshotStrictRecord, } from "../../execution/record.js";
|
|
10
|
+
import { cloneResolvedCommandContent, cloneResolvedPersonaContent, createResolvedExecutionRequest, } from "../../execution/resolved-request.js";
|
|
11
|
+
import { cloneAdapterExtensions, cloneToolSelection, cloneUnresolvedExecutionDefaults, executionPersonaMatchesSelector, isPortableExecutionAgentSelector, requireStableExecutionSelector, } from "../../execution/source.js";
|
|
12
|
+
import { resolveModelMapAlias } from "./model-map.js";
|
|
13
|
+
export const EXECUTION_CASCADE_PLAN_VERSION = 1;
|
|
14
|
+
export const FIXED_EXECUTION_ENGINE_FALLBACK = "opencode-sdk";
|
|
15
|
+
const planInstances = new WeakSet();
|
|
16
|
+
const ENGINE_NAME_PATTERN = new RegExp(ENGINE_NAME_PATTERN_SOURCE);
|
|
17
|
+
const MODEL_ALIAS_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
18
|
+
const RESERVED_ENGINE_KEYS = new Set(["__proto__", "constructor", "prototype", "tostring"]);
|
|
19
|
+
function record(value, path) {
|
|
20
|
+
return snapshotStrictRecord(value, path);
|
|
21
|
+
}
|
|
22
|
+
function only(recordValue, allowed, path) {
|
|
23
|
+
assertSnapshotKeys(recordValue, allowed, path);
|
|
24
|
+
}
|
|
25
|
+
function own(recordValue, key) {
|
|
26
|
+
return Object.hasOwn(recordValue, key);
|
|
27
|
+
}
|
|
28
|
+
function required(recordValue, key, path) {
|
|
29
|
+
return requireSnapshotField(recordValue, key, path);
|
|
30
|
+
}
|
|
31
|
+
function stableIdentifier(value, path) {
|
|
32
|
+
return requireStableExecutionSelector(value, path);
|
|
33
|
+
}
|
|
34
|
+
function canonicalEngineName(value, path) {
|
|
35
|
+
const name = stableIdentifier(value, path);
|
|
36
|
+
if (!ENGINE_NAME_PATTERN.test(name) || RESERVED_ENGINE_KEYS.has(name.toLowerCase())) {
|
|
37
|
+
throw new TypeError(`${path} must be a canonical, non-reserved engine name`);
|
|
38
|
+
}
|
|
39
|
+
return name;
|
|
40
|
+
}
|
|
41
|
+
function frozenNullPrototypeRecord(entries) {
|
|
42
|
+
const out = Object.create(null);
|
|
43
|
+
for (const [key, value] of entries) {
|
|
44
|
+
Object.defineProperty(out, key, { value, enumerable: true, configurable: false, writable: false });
|
|
45
|
+
}
|
|
46
|
+
return Object.freeze(out);
|
|
47
|
+
}
|
|
48
|
+
function normalizeLayer(value, kind, path) {
|
|
49
|
+
const input = record(value, path);
|
|
50
|
+
only(input, ["id", "values"], path);
|
|
51
|
+
const id = stableIdentifier(required(input, "id", path), `${path}.id`);
|
|
52
|
+
const values = cloneUnresolvedExecutionDefaults(required(input, "values", path), `${path}.values`);
|
|
53
|
+
return Object.freeze({ id, kind, values });
|
|
54
|
+
}
|
|
55
|
+
function normalizeEngineSelection(value, path) {
|
|
56
|
+
const input = record(value, path);
|
|
57
|
+
only(input, ["name", "kind", "platform", "settings", "extensions"], path);
|
|
58
|
+
const name = canonicalEngineName(required(input, "name", path), `${path}.name`);
|
|
59
|
+
const kind = required(input, "kind", path);
|
|
60
|
+
if (kind !== "agent" && kind !== "sdk" && kind !== "llm") {
|
|
61
|
+
throw new TypeError(`${path}.kind must be agent, sdk, or llm`);
|
|
62
|
+
}
|
|
63
|
+
const out = { name, kind };
|
|
64
|
+
if (own(input, "platform")) {
|
|
65
|
+
if (input.platform !== null && typeof input.platform !== "string") {
|
|
66
|
+
throw new TypeError(`${path}.platform must be a string or null`);
|
|
67
|
+
}
|
|
68
|
+
out.platform = input.platform;
|
|
69
|
+
}
|
|
70
|
+
if (own(input, "settings")) {
|
|
71
|
+
out.settings = input.settings === null ? null : cloneExecutionJsonObject(input.settings, `${path}.settings`);
|
|
72
|
+
}
|
|
73
|
+
if (own(input, "extensions")) {
|
|
74
|
+
out.extensions = cloneAdapterExtensions(input.extensions, `${path}.extensions`);
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze(out);
|
|
77
|
+
}
|
|
78
|
+
function normalizeEngineDefinition(value, name) {
|
|
79
|
+
const path = `engines.${name}`;
|
|
80
|
+
const input = record(value, path);
|
|
81
|
+
only(input, ["selection", "defaults", "modelMapKey"], path);
|
|
82
|
+
const selection = normalizeEngineSelection(required(input, "selection", path), `${path}.selection`);
|
|
83
|
+
if (selection.name !== name) {
|
|
84
|
+
throw new TypeError(`${path}.selection.name must match its engine registry key`);
|
|
85
|
+
}
|
|
86
|
+
const defaults = own(input, "defaults")
|
|
87
|
+
? cloneUnresolvedExecutionDefaults(input.defaults, `${path}.defaults`)
|
|
88
|
+
: Object.freeze({});
|
|
89
|
+
if (own(defaults, "engine")) {
|
|
90
|
+
throw new TypeError(`${path}.defaults.engine is invalid because the engine layer cannot select itself`);
|
|
91
|
+
}
|
|
92
|
+
const rawMapKey = own(input, "modelMapKey") ? input.modelMapKey : (selection.platform ?? selection.name);
|
|
93
|
+
const modelMapKey = stableIdentifier(rawMapKey, `${path}.modelMapKey`);
|
|
94
|
+
return Object.freeze({
|
|
95
|
+
selection,
|
|
96
|
+
defaults,
|
|
97
|
+
modelMapKey,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function normalizeEngines(value) {
|
|
101
|
+
const input = record(value, "engines");
|
|
102
|
+
const entries = [];
|
|
103
|
+
for (const [name, definition] of Object.entries(input)) {
|
|
104
|
+
canonicalEngineName(name, `engines.${name}`);
|
|
105
|
+
entries.push([name, normalizeEngineDefinition(definition, name)]);
|
|
106
|
+
}
|
|
107
|
+
return frozenNullPrototypeRecord(entries);
|
|
108
|
+
}
|
|
109
|
+
function normalizeModelMap(value) {
|
|
110
|
+
const input = record(value, "modelMap");
|
|
111
|
+
only(input, ["version", "aliases"], "modelMap");
|
|
112
|
+
if (required(input, "version", "modelMap") !== 1) {
|
|
113
|
+
throw new TypeError("modelMap.version must be 1");
|
|
114
|
+
}
|
|
115
|
+
const aliasesInput = record(required(input, "aliases", "modelMap"), "modelMap.aliases");
|
|
116
|
+
const aliases = [];
|
|
117
|
+
for (const [alias, rawEngines] of Object.entries(aliasesInput)) {
|
|
118
|
+
stableIdentifier(alias, `modelMap.aliases.${alias}`);
|
|
119
|
+
if (!MODEL_ALIAS_PATTERN.test(alias) || RESERVED_ENGINE_KEYS.has(alias.toLowerCase())) {
|
|
120
|
+
throw new TypeError(`modelMap alias ${JSON.stringify(alias)} is not canonical`);
|
|
121
|
+
}
|
|
122
|
+
const engineInput = record(rawEngines, `modelMap.aliases.${alias}`);
|
|
123
|
+
const profiles = [];
|
|
124
|
+
for (const [engine, rawProfile] of Object.entries(engineInput)) {
|
|
125
|
+
if (engine !== "*")
|
|
126
|
+
canonicalEngineName(engine, `modelMap.aliases.${alias}.${engine}`);
|
|
127
|
+
const profile = record(rawProfile, `modelMap.aliases.${alias}.${engine}`);
|
|
128
|
+
only(profile, ["model", "inference"], `modelMap.aliases.${alias}.${engine}`);
|
|
129
|
+
const model = required(profile, "model", `modelMap.aliases.${alias}.${engine}`);
|
|
130
|
+
if (typeof model !== "string" || model.length === 0 || model.trim() !== model) {
|
|
131
|
+
throw new TypeError(`modelMap.aliases.${alias}.${engine}.model must be a non-empty exact identifier`);
|
|
132
|
+
}
|
|
133
|
+
const normalized = { model };
|
|
134
|
+
if (own(profile, "inference")) {
|
|
135
|
+
normalized.inference =
|
|
136
|
+
profile.inference === null
|
|
137
|
+
? null
|
|
138
|
+
: cloneExecutionJsonObject(profile.inference, `modelMap.aliases.${alias}.${engine}.inference`);
|
|
139
|
+
}
|
|
140
|
+
profiles.push([engine, Object.freeze(normalized)]);
|
|
141
|
+
}
|
|
142
|
+
aliases.push([alias, frozenNullPrototypeRecord(profiles)]);
|
|
143
|
+
}
|
|
144
|
+
return Object.freeze({ version: 1, aliases: frozenNullPrototypeRecord(aliases) });
|
|
145
|
+
}
|
|
146
|
+
function normalizeLayers(value) {
|
|
147
|
+
const input = record(value, "layers");
|
|
148
|
+
only(input, ["installation", "agent", "command", "invocationDefaults", "current"], "layers");
|
|
149
|
+
const out = {
|
|
150
|
+
installation: normalizeLayer(required(input, "installation", "layers"), "installation", "layers.installation"),
|
|
151
|
+
};
|
|
152
|
+
for (const [key, kind] of [
|
|
153
|
+
["agent", "agent"],
|
|
154
|
+
["command", "command"],
|
|
155
|
+
["invocationDefaults", "invocation-defaults"],
|
|
156
|
+
["current", "current"],
|
|
157
|
+
]) {
|
|
158
|
+
if (own(input, key)) {
|
|
159
|
+
if (input[key] === undefined)
|
|
160
|
+
throw new TypeError(`layers.${key} must be omitted rather than undefined`);
|
|
161
|
+
out[key] = normalizeLayer(input[key], kind, `layers.${key}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return Object.freeze(out);
|
|
165
|
+
}
|
|
166
|
+
function orderedWithoutEngine(layers) {
|
|
167
|
+
return [layers.installation, layers.agent, layers.command, layers.invocationDefaults, layers.current].filter((layer) => layer !== undefined);
|
|
168
|
+
}
|
|
169
|
+
function selectNearest(layers, key) {
|
|
170
|
+
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
171
|
+
const layer = layers[index];
|
|
172
|
+
if (layer && own(layer.values, key)) {
|
|
173
|
+
return { present: true, value: layer.values[key], layer, index };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { present: false, index: -1 };
|
|
177
|
+
}
|
|
178
|
+
function provenance(layer, via = "explicit") {
|
|
179
|
+
return Object.freeze({ layer: layer.id, kind: layer.kind, via });
|
|
180
|
+
}
|
|
181
|
+
function fallbackProvenance() {
|
|
182
|
+
return Object.freeze({ layer: FIXED_EXECUTION_ENGINE_FALLBACK, kind: "fallback", via: "fallback" });
|
|
183
|
+
}
|
|
184
|
+
function sourceProvenance(layer, kind) {
|
|
185
|
+
return Object.freeze({ layer, kind, via: "source" });
|
|
186
|
+
}
|
|
187
|
+
function chooseEngine(layers, engines) {
|
|
188
|
+
const selected = selectNearest(layers, "engine");
|
|
189
|
+
let name;
|
|
190
|
+
let field;
|
|
191
|
+
let fallback = false;
|
|
192
|
+
if (!selected.present || selected.value === null) {
|
|
193
|
+
name = FIXED_EXECUTION_ENGINE_FALLBACK;
|
|
194
|
+
field = fallbackProvenance();
|
|
195
|
+
fallback = true;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (typeof selected.value !== "string" || selected.value.length === 0) {
|
|
199
|
+
throw new ConfigError("Selected engine must be a non-empty configured engine name.", "INVALID_CONFIG_FILE");
|
|
200
|
+
}
|
|
201
|
+
name = selected.value;
|
|
202
|
+
field = provenance(selected.layer);
|
|
203
|
+
}
|
|
204
|
+
const definition = engines[name];
|
|
205
|
+
if (!definition) {
|
|
206
|
+
if (fallback) {
|
|
207
|
+
throw new ConfigError("No engine was selected and the fixed opencode-sdk fallback is unavailable.", "INVALID_CONFIG_FILE", "Configure defaults.engine or configure/install the opencode-sdk engine.");
|
|
208
|
+
}
|
|
209
|
+
throw new ConfigError(`Selected engine ${JSON.stringify(name)} is not configured.`, "INVALID_CONFIG_FILE");
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
definition,
|
|
213
|
+
field,
|
|
214
|
+
...(selected.present ? { requested: provenance(selected.layer) } : {}),
|
|
215
|
+
fallback,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function cloneJsonValue(value, path) {
|
|
219
|
+
return cloneExecutionJson(value, path);
|
|
220
|
+
}
|
|
221
|
+
function isObject(value) {
|
|
222
|
+
return value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value);
|
|
223
|
+
}
|
|
224
|
+
function deleteInferenceProvenance(fields) {
|
|
225
|
+
for (const key of Object.keys(fields)) {
|
|
226
|
+
if (key === "/inference" || key.startsWith("/inference/"))
|
|
227
|
+
delete fields[key];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function deleteInferenceDescendantProvenance(fields, path) {
|
|
231
|
+
for (const key of Object.keys(fields)) {
|
|
232
|
+
if (key.startsWith(`${path}/`))
|
|
233
|
+
delete fields[key];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function jsonPointerSegment(value) {
|
|
237
|
+
return value.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
238
|
+
}
|
|
239
|
+
function setInferenceTreeProvenance(value, path, source, fields) {
|
|
240
|
+
fields[path] = source;
|
|
241
|
+
if (!isObject(value))
|
|
242
|
+
return;
|
|
243
|
+
for (const [key, child] of Object.entries(value)) {
|
|
244
|
+
setInferenceTreeProvenance(child, `${path}/${jsonPointerSegment(key)}`, source, fields);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function mergeInferenceObject(base, overlay, prefix, source, fields) {
|
|
248
|
+
const out = new Map();
|
|
249
|
+
for (const [key, value] of Object.entries(base))
|
|
250
|
+
out.set(key, cloneJsonValue(value, `${prefix}.${key}`));
|
|
251
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
252
|
+
const path = `${prefix}/${jsonPointerSegment(key)}`;
|
|
253
|
+
const previous = out.get(key);
|
|
254
|
+
if (isObject(previous) && isObject(value)) {
|
|
255
|
+
out.set(key, mergeInferenceObject(previous, value, path, source, fields));
|
|
256
|
+
fields[path] = source;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
deleteInferenceDescendantProvenance(fields, path);
|
|
260
|
+
out.set(key, cloneJsonValue(value, path));
|
|
261
|
+
setInferenceTreeProvenance(value, path, source, fields);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return Object.freeze(Object.fromEntries(out));
|
|
265
|
+
}
|
|
266
|
+
function resolveInference(layers, aliasInferenceByLayer, fields) {
|
|
267
|
+
let present = false;
|
|
268
|
+
let current;
|
|
269
|
+
const apply = (value, layer, via) => {
|
|
270
|
+
const field = provenance(layer, via);
|
|
271
|
+
present = true;
|
|
272
|
+
if (value === null) {
|
|
273
|
+
current = null;
|
|
274
|
+
deleteInferenceProvenance(fields);
|
|
275
|
+
fields["/inference"] = field;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
fields["/inference"] = field;
|
|
279
|
+
current = mergeInferenceObject(isObject(current) ? current : Object.freeze({}), value, "/inference", field, fields);
|
|
280
|
+
};
|
|
281
|
+
for (const [index, layer] of layers.entries()) {
|
|
282
|
+
if (aliasInferenceByLayer.has(index)) {
|
|
283
|
+
apply(aliasInferenceByLayer.get(index), layer, "model-alias");
|
|
284
|
+
}
|
|
285
|
+
if (own(layer.values, "inference")) {
|
|
286
|
+
apply(layer.values.inference, layer, "explicit");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return present ? { present: true, value: current } : { present: false };
|
|
290
|
+
}
|
|
291
|
+
function resolveModels(layers, definition, modelMap) {
|
|
292
|
+
let selected = { present: false, index: -1 };
|
|
293
|
+
let resolved;
|
|
294
|
+
const aliasInferenceByLayer = new Map();
|
|
295
|
+
for (const [index, layer] of layers.entries()) {
|
|
296
|
+
if (!own(layer.values, "model"))
|
|
297
|
+
continue;
|
|
298
|
+
const value = layer.values.model;
|
|
299
|
+
selected = { present: true, value, layer, index };
|
|
300
|
+
if (value === null) {
|
|
301
|
+
resolved = null;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
305
|
+
throw new ConfigError("Resolved model must be null or a non-empty string.", "INVALID_CONFIG_FILE");
|
|
306
|
+
}
|
|
307
|
+
const selection = resolveModelMapAlias(value, definition.modelMapKey, modelMap);
|
|
308
|
+
if (selection.interpretation === "alias" && selection.inference !== undefined) {
|
|
309
|
+
aliasInferenceByLayer.set(index, selection.inference);
|
|
310
|
+
}
|
|
311
|
+
resolved = Object.freeze({
|
|
312
|
+
input: selection.input,
|
|
313
|
+
interpretation: selection.interpretation,
|
|
314
|
+
resolved: selection.model,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return Object.freeze({ selected, resolved, aliasInferenceByLayer });
|
|
318
|
+
}
|
|
319
|
+
function normalizeTimeout(value) {
|
|
320
|
+
if (value === null)
|
|
321
|
+
return null;
|
|
322
|
+
const milliseconds = typeof value === "string" ? parseDuration(value, DURATION_UNITS) : value;
|
|
323
|
+
if (milliseconds === null ||
|
|
324
|
+
!Number.isInteger(milliseconds) ||
|
|
325
|
+
milliseconds < 0 ||
|
|
326
|
+
milliseconds > EXECUTION_MAX_TIMEOUT_MS) {
|
|
327
|
+
throw new ConfigError(`Resolved execution timeout must be null, a bounded integer in milliseconds, or a duration such as 20m.`, "INVALID_CONFIG_FILE");
|
|
328
|
+
}
|
|
329
|
+
return milliseconds;
|
|
330
|
+
}
|
|
331
|
+
function toolsRequireAuthorization(value, present) {
|
|
332
|
+
if (!present || value === null)
|
|
333
|
+
return false;
|
|
334
|
+
if (typeof value === "string")
|
|
335
|
+
return value.length > 0;
|
|
336
|
+
if (Array.isArray(value))
|
|
337
|
+
return value.length > 0;
|
|
338
|
+
return typeof value === "object" && value !== null && Object.keys(value).length > 0;
|
|
339
|
+
}
|
|
340
|
+
function normalizeAuthorizationDecision(value) {
|
|
341
|
+
const input = record(value, "tool authorization decision");
|
|
342
|
+
only(input, ["status", "policy"], "tool authorization decision");
|
|
343
|
+
const status = required(input, "status", "tool authorization decision");
|
|
344
|
+
if (status !== "allowed" && status !== "denied") {
|
|
345
|
+
throw new TypeError("tool authorization decision.status must be allowed or denied");
|
|
346
|
+
}
|
|
347
|
+
const policy = stableIdentifier(required(input, "policy", "tool authorization decision"), "tool authorization decision.policy");
|
|
348
|
+
return Object.freeze({ status, policy });
|
|
349
|
+
}
|
|
350
|
+
function authorizationFor(requiresAuthorization, authorizer, input) {
|
|
351
|
+
if (!requiresAuthorization)
|
|
352
|
+
return Object.freeze({ status: "not-required" });
|
|
353
|
+
if (!authorizer || !input) {
|
|
354
|
+
return Object.freeze({
|
|
355
|
+
status: "denied",
|
|
356
|
+
reason: "Selected tools require an explicit machine/user authorization policy.",
|
|
357
|
+
policy: Object.freeze({ id: "unconfigured" }),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const decision = normalizeAuthorizationDecision(authorizer(Object.freeze(input)));
|
|
361
|
+
return Object.freeze({
|
|
362
|
+
status: decision.status,
|
|
363
|
+
reason: decision.status === "allowed"
|
|
364
|
+
? "Selected tools were authorized by operator policy."
|
|
365
|
+
: "Selected tools are not authorized by operator policy.",
|
|
366
|
+
policy: Object.freeze({ id: decision.policy }),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
function freezeProvenance(fields) {
|
|
370
|
+
return frozenNullPrototypeRecord(Object.entries(fields)
|
|
371
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
|
|
372
|
+
.map(([key, value]) => [key, Object.freeze({ ...value })]));
|
|
373
|
+
}
|
|
374
|
+
export function planExecutionCascade(raw) {
|
|
375
|
+
const input = record(raw, "execution cascade input");
|
|
376
|
+
only(input, ["command", "conversation", "persona", "layers", "engines", "modelMap", "invocationKind", "authorizeTools"], "execution cascade input");
|
|
377
|
+
const command = cloneResolvedCommandContent(required(input, "command", "execution cascade input"));
|
|
378
|
+
const invocationKind = required(input, "invocationKind", "execution cascade input");
|
|
379
|
+
if (invocationKind !== "direct" && invocationKind !== "task" && invocationKind !== "workflow") {
|
|
380
|
+
throw new TypeError("execution cascade input.invocationKind is invalid");
|
|
381
|
+
}
|
|
382
|
+
const normalizedInvocationKind = invocationKind;
|
|
383
|
+
const personaPresent = own(input, "persona");
|
|
384
|
+
if (personaPresent && input.persona === undefined) {
|
|
385
|
+
throw new TypeError("execution cascade input.persona must be omitted, null, or a resolved persona");
|
|
386
|
+
}
|
|
387
|
+
let persona = personaPresent
|
|
388
|
+
? input.persona === null
|
|
389
|
+
? null
|
|
390
|
+
: cloneResolvedPersonaContent(input.persona)
|
|
391
|
+
: undefined;
|
|
392
|
+
if (own(input, "authorizeTools") &&
|
|
393
|
+
input.authorizeTools !== undefined &&
|
|
394
|
+
typeof input.authorizeTools !== "function") {
|
|
395
|
+
throw new TypeError("execution cascade input.authorizeTools must be a function or omitted");
|
|
396
|
+
}
|
|
397
|
+
const normalizedLayers = normalizeLayers(required(input, "layers", "execution cascade input"));
|
|
398
|
+
const engines = normalizeEngines(required(input, "engines", "execution cascade input"));
|
|
399
|
+
const modelMap = normalizeModelMap(required(input, "modelMap", "execution cascade input"));
|
|
400
|
+
const beforeEngine = orderedWithoutEngine(normalizedLayers);
|
|
401
|
+
const selectedEngine = chooseEngine(beforeEngine, engines);
|
|
402
|
+
const engineLayer = Object.freeze({
|
|
403
|
+
id: selectedEngine.definition.selection.name,
|
|
404
|
+
kind: "engine",
|
|
405
|
+
values: selectedEngine.definition.defaults,
|
|
406
|
+
});
|
|
407
|
+
const layers = [
|
|
408
|
+
normalizedLayers.installation,
|
|
409
|
+
engineLayer,
|
|
410
|
+
normalizedLayers.agent,
|
|
411
|
+
normalizedLayers.command,
|
|
412
|
+
normalizedLayers.invocationDefaults,
|
|
413
|
+
normalizedLayers.current,
|
|
414
|
+
].filter((layer) => layer !== undefined);
|
|
415
|
+
const fields = Object.create(null);
|
|
416
|
+
fields.engine = selectedEngine.field;
|
|
417
|
+
if (selectedEngine.requested)
|
|
418
|
+
fields["engine.requested"] = selectedEngine.requested;
|
|
419
|
+
const commandRef = command.source?.ref ?? null;
|
|
420
|
+
fields.command = sourceProvenance(commandRef ?? "inline", "command");
|
|
421
|
+
const selectedAgent = selectNearest(layers, "agent");
|
|
422
|
+
let resolvedAgent;
|
|
423
|
+
let requestPersonaPresent = personaPresent;
|
|
424
|
+
if (selectedAgent.present) {
|
|
425
|
+
fields.agent = provenance(selectedAgent.layer);
|
|
426
|
+
if (selectedAgent.value === null) {
|
|
427
|
+
resolvedAgent = null;
|
|
428
|
+
persona = null;
|
|
429
|
+
requestPersonaPresent = true;
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
const selector = stableIdentifier(selectedAgent.value, "selected agent");
|
|
433
|
+
resolvedAgent = selector;
|
|
434
|
+
if (isPortableExecutionAgentSelector(selector)) {
|
|
435
|
+
if (!persona || !executionPersonaMatchesSelector(selector, persona.source.ref)) {
|
|
436
|
+
throw new ConfigError(`Selected agent ${JSON.stringify(selector)} does not match the resolved persona source.`, "INVALID_CONFIG_FILE", "Resolve the selected agent through its bundle adapter before planning execution.");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
persona = null;
|
|
441
|
+
requestPersonaPresent = true;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const personaRef = persona?.source.ref ?? null;
|
|
446
|
+
if (persona)
|
|
447
|
+
fields.persona = sourceProvenance(personaRef, "agent");
|
|
448
|
+
const modelResolution = resolveModels(layers, selectedEngine.definition, modelMap);
|
|
449
|
+
const selectedModel = modelResolution.selected;
|
|
450
|
+
if (selectedModel.present) {
|
|
451
|
+
fields.model = provenance(selectedModel.layer);
|
|
452
|
+
}
|
|
453
|
+
const inference = resolveInference(layers, modelResolution.aliasInferenceByLayer, fields);
|
|
454
|
+
const selectedSchema = selectNearest(layers, "outputSchema");
|
|
455
|
+
if (selectedSchema.present)
|
|
456
|
+
fields.outputSchema = provenance(selectedSchema.layer);
|
|
457
|
+
const selectedTools = selectNearest(layers, "tools");
|
|
458
|
+
if (selectedTools.present)
|
|
459
|
+
fields.tools = provenance(selectedTools.layer);
|
|
460
|
+
const selectedTimeout = selectNearest(layers, "timeout");
|
|
461
|
+
const selectedWorkspace = selectNearest(layers, "workspace");
|
|
462
|
+
const selectedEnvironment = selectNearest(layers, "environment");
|
|
463
|
+
const selectedRuntime = selectNearest(layers, "runtime");
|
|
464
|
+
const runtime = {};
|
|
465
|
+
if (selectedTimeout.present) {
|
|
466
|
+
runtime.timeoutMs = normalizeTimeout(selectedTimeout.value);
|
|
467
|
+
fields["runtime.timeoutMs"] = provenance(selectedTimeout.layer);
|
|
468
|
+
}
|
|
469
|
+
if (selectedWorkspace.present) {
|
|
470
|
+
runtime.workspace = selectedWorkspace.value;
|
|
471
|
+
fields["runtime.workspace"] = provenance(selectedWorkspace.layer);
|
|
472
|
+
}
|
|
473
|
+
if (selectedEnvironment.present) {
|
|
474
|
+
runtime.environment = selectedEnvironment.value;
|
|
475
|
+
fields["runtime.environment"] = provenance(selectedEnvironment.layer);
|
|
476
|
+
}
|
|
477
|
+
if (selectedRuntime.present) {
|
|
478
|
+
runtime.settings = selectedRuntime.value;
|
|
479
|
+
fields["runtime.settings"] = provenance(selectedRuntime.layer);
|
|
480
|
+
}
|
|
481
|
+
const notices = selectedEngine.fallback
|
|
482
|
+
? [
|
|
483
|
+
{
|
|
484
|
+
code: "engine-fallback",
|
|
485
|
+
severity: "info",
|
|
486
|
+
adapter: "akm",
|
|
487
|
+
field: "engine",
|
|
488
|
+
message: "No engine was selected; using the fixed opencode-sdk fallback.",
|
|
489
|
+
},
|
|
490
|
+
]
|
|
491
|
+
: [];
|
|
492
|
+
const tools = selectedTools.present ? cloneToolSelection(selectedTools.value, "resolved tools") : undefined;
|
|
493
|
+
const requiresAuthorization = toolsRequireAuthorization(tools, selectedTools.present);
|
|
494
|
+
const provisionalAuthorization = requiresAuthorization
|
|
495
|
+
? { status: "allowed", policy: { id: "pre-authorization-validation" } }
|
|
496
|
+
: { status: "not-required" };
|
|
497
|
+
const requestInput = {
|
|
498
|
+
command,
|
|
499
|
+
engine: selectedEngine.definition.selection,
|
|
500
|
+
authorization: provisionalAuthorization,
|
|
501
|
+
runtime,
|
|
502
|
+
notices,
|
|
503
|
+
};
|
|
504
|
+
if (own(input, "conversation"))
|
|
505
|
+
requestInput.conversation = input.conversation;
|
|
506
|
+
if (selectedAgent.present)
|
|
507
|
+
requestInput.agent = resolvedAgent;
|
|
508
|
+
if (requestPersonaPresent)
|
|
509
|
+
requestInput.persona = persona;
|
|
510
|
+
if (selectedModel.present)
|
|
511
|
+
requestInput.model = modelResolution.resolved;
|
|
512
|
+
if (inference.present)
|
|
513
|
+
requestInput.inference = inference.value;
|
|
514
|
+
if (selectedSchema.present)
|
|
515
|
+
requestInput.outputSchema = selectedSchema.value;
|
|
516
|
+
if (selectedTools.present)
|
|
517
|
+
requestInput.tools = tools;
|
|
518
|
+
// Validate every dispatch-significant value and source provenance before a
|
|
519
|
+
// policy callback can observe the request or any caller can dispatch it.
|
|
520
|
+
const provisionalRequest = createResolvedExecutionRequest(requestInput);
|
|
521
|
+
if (own(provisionalRequest, "conversation"))
|
|
522
|
+
requestInput.conversation = provisionalRequest.conversation;
|
|
523
|
+
const authorizationInput = requiresAuthorization
|
|
524
|
+
? {
|
|
525
|
+
tools: tools,
|
|
526
|
+
engine: Object.freeze({
|
|
527
|
+
name: selectedEngine.definition.selection.name,
|
|
528
|
+
kind: selectedEngine.definition.selection.kind,
|
|
529
|
+
...(own(selectedEngine.definition.selection, "platform")
|
|
530
|
+
? { platform: selectedEngine.definition.selection.platform }
|
|
531
|
+
: {}),
|
|
532
|
+
}),
|
|
533
|
+
invocationKind: normalizedInvocationKind,
|
|
534
|
+
commandRef,
|
|
535
|
+
personaRef,
|
|
536
|
+
}
|
|
537
|
+
: undefined;
|
|
538
|
+
const authorization = authorizationFor(requiresAuthorization, input.authorizeTools, authorizationInput);
|
|
539
|
+
fields.authorization = Object.freeze({
|
|
540
|
+
layer: authorization.policy && typeof authorization.policy.id === "string" ? authorization.policy.id : "not-required",
|
|
541
|
+
kind: "authorization",
|
|
542
|
+
via: "policy",
|
|
543
|
+
});
|
|
544
|
+
requestInput.authorization = authorization;
|
|
545
|
+
const request = createResolvedExecutionRequest(requestInput);
|
|
546
|
+
const planObject = {
|
|
547
|
+
schemaVersion: EXECUTION_CASCADE_PLAN_VERSION,
|
|
548
|
+
invocationKind: normalizedInvocationKind,
|
|
549
|
+
request,
|
|
550
|
+
provenance: freezeProvenance(fields),
|
|
551
|
+
};
|
|
552
|
+
if (selectedAgent.present)
|
|
553
|
+
planObject.selectedAgent = resolvedAgent;
|
|
554
|
+
const plan = Object.freeze(planObject);
|
|
555
|
+
planInstances.add(plan);
|
|
556
|
+
return plan;
|
|
557
|
+
}
|
|
558
|
+
export function requireAuthorizedExecutionPlan(plan) {
|
|
559
|
+
if (!planInstances.has(plan) || !Object.isFrozen(plan)) {
|
|
560
|
+
throw new TypeError("execution plan must be constructed by the common cascade resolver");
|
|
561
|
+
}
|
|
562
|
+
if (plan.request.authorization.status === "denied") {
|
|
563
|
+
throw new ConfigError(plan.request.authorization.reason ?? "Resolved execution is not authorized by operator policy.", "EXECUTION_NOT_AUTHORIZED");
|
|
564
|
+
}
|
|
565
|
+
return plan.request;
|
|
566
|
+
}
|