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,811 @@
|
|
|
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 { ConfigError, UsageError } from "../../core/errors.js";
|
|
5
|
+
import { cloneExecutionJsonObject } from "../../execution/json.js";
|
|
6
|
+
import { EXECUTION_MAX_TIMEOUT_MS } from "../../execution/limits.js";
|
|
7
|
+
import { assertSnapshotKeys, snapshotStrictRecord } from "../../execution/record.js";
|
|
8
|
+
import { canonicalResolvedExecutionRequest, } from "../../execution/resolved-request.js";
|
|
9
|
+
import { chatCompletion, LlmCallError, } from "../../llm/client.js";
|
|
10
|
+
import { HARNESS_REGISTRY } from "../harnesses/index.js";
|
|
11
|
+
import { resolveEngine } from "./engine-resolution.js";
|
|
12
|
+
import { SDK_FALLBACK_MODEL_FROM_REQUEST_SETTING } from "./execution-definitions.js";
|
|
13
|
+
import { acquireRunnerDispatchLease, assertRunnerDispatchLease, disposeRunnerDispatchLease, executeRunner, redactWithRunnerDispatchLease, } from "./runner-dispatch.js";
|
|
14
|
+
export const EXECUTION_LOWERING_SCHEMA_VERSION = 1;
|
|
15
|
+
const loweredExecutionInstances = new WeakSet();
|
|
16
|
+
function registerLoweredExecution(value) {
|
|
17
|
+
const frozen = Object.freeze(value);
|
|
18
|
+
loweredExecutionInstances.add(frozen);
|
|
19
|
+
return frozen;
|
|
20
|
+
}
|
|
21
|
+
function own(value, key) {
|
|
22
|
+
return Object.hasOwn(value, key);
|
|
23
|
+
}
|
|
24
|
+
function sterileRecord(value) {
|
|
25
|
+
return Object.assign(Object.create(null), value);
|
|
26
|
+
}
|
|
27
|
+
function sortedUnique(values) {
|
|
28
|
+
return Object.freeze([...new Set(values)].sort());
|
|
29
|
+
}
|
|
30
|
+
function freezeNotices(values) {
|
|
31
|
+
return Object.freeze([...values]);
|
|
32
|
+
}
|
|
33
|
+
const SAFE_ENGINE_FALLBACK_NOTICE = Object.freeze({
|
|
34
|
+
code: "engine-fallback",
|
|
35
|
+
severity: "info",
|
|
36
|
+
adapter: "akm",
|
|
37
|
+
field: "engine",
|
|
38
|
+
message: "No engine was selected; using the fixed opencode-sdk fallback.",
|
|
39
|
+
});
|
|
40
|
+
const UNRECOGNIZED_REQUEST_NOTICE = Object.freeze({
|
|
41
|
+
code: "unrecognized-request-notice",
|
|
42
|
+
severity: "warning",
|
|
43
|
+
adapter: "akm",
|
|
44
|
+
message: "An unrecognized durable execution notice was omitted at the engine lowering boundary.",
|
|
45
|
+
});
|
|
46
|
+
/** Reconstruct durable/untrusted notices without forwarding message/details bytes. */
|
|
47
|
+
function safeRequestNotices(request) {
|
|
48
|
+
let fallback = false;
|
|
49
|
+
let unknown = false;
|
|
50
|
+
for (const notice of request.notices) {
|
|
51
|
+
if (notice.code === "engine-fallback")
|
|
52
|
+
fallback = true;
|
|
53
|
+
else
|
|
54
|
+
unknown = true;
|
|
55
|
+
}
|
|
56
|
+
return Object.freeze([
|
|
57
|
+
...(fallback ? [SAFE_ENGINE_FALLBACK_NOTICE] : []),
|
|
58
|
+
...(unknown ? [UNRECOGNIZED_REQUEST_NOTICE] : []),
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
function untranslatedNotice(adapter, field) {
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
code: "untranslated-field",
|
|
64
|
+
severity: "warning",
|
|
65
|
+
adapter,
|
|
66
|
+
field,
|
|
67
|
+
message: `The ${adapter} lowerer does not translate resolved field ${field}; dispatch will continue optimistically.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function requireAuthorizedRequest(request) {
|
|
71
|
+
// The canonical round trip is the public brand/prototype/accessor guard. It
|
|
72
|
+
// deliberately happens before config or registry access.
|
|
73
|
+
canonicalResolvedExecutionRequest(request);
|
|
74
|
+
if (request.authorization.status === "denied") {
|
|
75
|
+
throw new ConfigError(request.authorization.reason ?? "Resolved execution is not authorized by operator policy.", "EXECUTION_NOT_AUTHORIZED");
|
|
76
|
+
}
|
|
77
|
+
return request;
|
|
78
|
+
}
|
|
79
|
+
function snapshotConfig(config) {
|
|
80
|
+
return cloneExecutionJsonObject(config, "execution lowering config");
|
|
81
|
+
}
|
|
82
|
+
function assertKeys(value, allowed, path) {
|
|
83
|
+
const allowedKeys = new Set(allowed);
|
|
84
|
+
for (const key of Object.keys(value)) {
|
|
85
|
+
if (!allowedKeys.has(key))
|
|
86
|
+
throw new TypeError(`${path} contains unsupported field: ${key}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function requireStringField(value, key, path) {
|
|
90
|
+
const field = value[key];
|
|
91
|
+
if (typeof field !== "string")
|
|
92
|
+
throw new TypeError(`${path}.${key} must be a string`);
|
|
93
|
+
return field;
|
|
94
|
+
}
|
|
95
|
+
function validateOptionalString(value, key, path) {
|
|
96
|
+
if (own(value, key) && typeof value[key] !== "string")
|
|
97
|
+
throw new TypeError(`${path}.${key} must be a string`);
|
|
98
|
+
}
|
|
99
|
+
function validateTimeout(value, path) {
|
|
100
|
+
if (value !== null &&
|
|
101
|
+
(typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > EXECUTION_MAX_TIMEOUT_MS)) {
|
|
102
|
+
throw new TypeError(`${path} must be null or an integer from 0 through ${EXECUTION_MAX_TIMEOUT_MS}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function validateStringArray(value, path, allowEmpty = true) {
|
|
106
|
+
if (!Array.isArray(value) ||
|
|
107
|
+
(!allowEmpty && value.length === 0) ||
|
|
108
|
+
value.some((entry) => typeof entry !== "string")) {
|
|
109
|
+
throw new TypeError(`${path} must be ${allowEmpty ? "an" : "a non-empty"} array of strings`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function validateCredential(value, path) {
|
|
113
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
114
|
+
throw new TypeError(`${path} must be an object`);
|
|
115
|
+
const record = value;
|
|
116
|
+
assertKeys(record, ["names", "required"], path);
|
|
117
|
+
validateStringArray(record.names, `${path}.names`, false);
|
|
118
|
+
if (typeof record.required !== "boolean")
|
|
119
|
+
throw new TypeError(`${path}.required must be a boolean`);
|
|
120
|
+
}
|
|
121
|
+
function validateConnection(value, path, requireModel = true) {
|
|
122
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
123
|
+
throw new TypeError(`${path} must be an object`);
|
|
124
|
+
const record = value;
|
|
125
|
+
requireStringField(record, "endpoint", path);
|
|
126
|
+
if (requireModel || own(record, "model"))
|
|
127
|
+
requireStringField(record, "model", path);
|
|
128
|
+
if (own(record, "apiKey")) {
|
|
129
|
+
throw new TypeError(`${path}.apiKey must remain a symbolic credential and cannot be frozen as secret material`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function validateProfile(value, path) {
|
|
133
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
134
|
+
throw new TypeError(`${path} must be an object`);
|
|
135
|
+
const record = value;
|
|
136
|
+
assertKeys(record, [
|
|
137
|
+
"name",
|
|
138
|
+
"platform",
|
|
139
|
+
"personaChannel",
|
|
140
|
+
"workspace",
|
|
141
|
+
"bin",
|
|
142
|
+
"args",
|
|
143
|
+
"stdio",
|
|
144
|
+
"env",
|
|
145
|
+
"envPassthrough",
|
|
146
|
+
"parseOutput",
|
|
147
|
+
"model",
|
|
148
|
+
], path);
|
|
149
|
+
requireStringField(record, "name", path);
|
|
150
|
+
requireStringField(record, "bin", path);
|
|
151
|
+
validateOptionalString(record, "platform", path);
|
|
152
|
+
validateOptionalString(record, "workspace", path);
|
|
153
|
+
validateOptionalString(record, "model", path);
|
|
154
|
+
validateStringArray(record.args, `${path}.args`);
|
|
155
|
+
validateStringArray(record.envPassthrough, `${path}.envPassthrough`);
|
|
156
|
+
if (record.stdio !== "captured" && record.stdio !== "interactive") {
|
|
157
|
+
throw new TypeError(`${path}.stdio is invalid`);
|
|
158
|
+
}
|
|
159
|
+
if (record.parseOutput !== "text" && record.parseOutput !== "json") {
|
|
160
|
+
throw new TypeError(`${path}.parseOutput is invalid`);
|
|
161
|
+
}
|
|
162
|
+
if (own(record, "personaChannel") && record.personaChannel !== "native" && record.personaChannel !== "prompt") {
|
|
163
|
+
throw new TypeError(`${path}.personaChannel is invalid`);
|
|
164
|
+
}
|
|
165
|
+
if (own(record, "env")) {
|
|
166
|
+
const env = record.env;
|
|
167
|
+
if (env === null ||
|
|
168
|
+
typeof env !== "object" ||
|
|
169
|
+
Array.isArray(env) ||
|
|
170
|
+
Object.values(env).some((entry) => typeof entry !== "string")) {
|
|
171
|
+
throw new TypeError(`${path}.env must be an object of strings`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Durable runner material may contain ordinary literals, never credential-shaped bytes. */
|
|
176
|
+
function runnerEnvironmentValueLooksSecret(name, value) {
|
|
177
|
+
const key = name.toLowerCase();
|
|
178
|
+
if ([
|
|
179
|
+
"secret",
|
|
180
|
+
"token",
|
|
181
|
+
"password",
|
|
182
|
+
"passwd",
|
|
183
|
+
"apikey",
|
|
184
|
+
"api_key",
|
|
185
|
+
"api-key",
|
|
186
|
+
"accesskey",
|
|
187
|
+
"access_key",
|
|
188
|
+
"privatekey",
|
|
189
|
+
"private_key",
|
|
190
|
+
"credential",
|
|
191
|
+
"bearer",
|
|
192
|
+
"client_secret",
|
|
193
|
+
].some((hint) => key.includes(hint))) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
const candidate = value.trim();
|
|
197
|
+
if (candidate.length < 20 || /\s/.test(candidate))
|
|
198
|
+
return false;
|
|
199
|
+
if (/^(?:sk-|rk-|ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|xox[baprs]-|AKIA|ASIA|AIza|ya29\.|-----BEGIN)/.test(candidate)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((pattern) => pattern.test(candidate)).length;
|
|
203
|
+
return (candidate.length >= 24 && classes >= 3) || (candidate.length >= 32 && /^[A-Za-z0-9+/=_-]+$/.test(candidate));
|
|
204
|
+
}
|
|
205
|
+
/** Strictly detach and deep-freeze non-secret runner material before lowering. */
|
|
206
|
+
function snapshotRunnerSpec(input, options = {}) {
|
|
207
|
+
const cloned = cloneExecutionJsonObject(input, "execution runner material");
|
|
208
|
+
const kind = cloned.kind;
|
|
209
|
+
validateOptionalString(cloned, "engine", "execution runner material");
|
|
210
|
+
if (own(cloned, "timeoutMs"))
|
|
211
|
+
validateTimeout(cloned.timeoutMs, "execution runner material.timeoutMs");
|
|
212
|
+
if (kind === "llm") {
|
|
213
|
+
assertKeys(cloned, ["kind", "engine", "connection", "credential", "timeoutMs"], "execution runner material");
|
|
214
|
+
validateConnection(cloned.connection, "execution runner material.connection", !options.allowMissingLlmModel);
|
|
215
|
+
if (own(cloned, "credential"))
|
|
216
|
+
validateCredential(cloned.credential, "execution runner material.credential");
|
|
217
|
+
}
|
|
218
|
+
else if (kind === "agent") {
|
|
219
|
+
assertKeys(cloned, ["kind", "engine", "profile", "timeoutMs"], "execution runner material");
|
|
220
|
+
validateProfile(cloned.profile, "execution runner material.profile");
|
|
221
|
+
}
|
|
222
|
+
else if (kind === "sdk") {
|
|
223
|
+
assertKeys(cloned, ["kind", "engine", "profile", "fallbackConnection", "fallbackCredential", "fallbackTimeoutMs", "timeoutMs"], "execution runner material");
|
|
224
|
+
validateProfile(cloned.profile, "execution runner material.profile");
|
|
225
|
+
if (own(cloned, "fallbackConnection")) {
|
|
226
|
+
validateConnection(cloned.fallbackConnection, "execution runner material.fallbackConnection", !options.allowMissingSdkFallbackModel);
|
|
227
|
+
}
|
|
228
|
+
if (own(cloned, "fallbackCredential")) {
|
|
229
|
+
validateCredential(cloned.fallbackCredential, "execution runner material.fallbackCredential");
|
|
230
|
+
}
|
|
231
|
+
if (own(cloned, "fallbackTimeoutMs")) {
|
|
232
|
+
validateTimeout(cloned.fallbackTimeoutMs, "execution runner material.fallbackTimeoutMs");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
throw new TypeError("execution runner material.kind must be llm, agent, or sdk");
|
|
237
|
+
}
|
|
238
|
+
return cloned;
|
|
239
|
+
}
|
|
240
|
+
/** Strict public decoder for persisted, non-secret runner/transport material. */
|
|
241
|
+
export function decodeFrozenRunnerSpec(input) {
|
|
242
|
+
const runner = snapshotRunnerSpec(input);
|
|
243
|
+
if (runner.kind !== "llm") {
|
|
244
|
+
for (const [name, value] of Object.entries(runner.profile.env ?? {})) {
|
|
245
|
+
if (runnerEnvironmentValueLooksSecret(name, value)) {
|
|
246
|
+
throw new TypeError("execution runner material.profile.env contains a secret-shaped literal; freeze only symbolic environment names");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return runner;
|
|
251
|
+
}
|
|
252
|
+
function lowererEntries() {
|
|
253
|
+
return HARNESS_REGISTRY.map((harness) => {
|
|
254
|
+
const lowerer = harness.agentBuilder?.lower !== undefined
|
|
255
|
+
? {
|
|
256
|
+
platform: harness.agentBuilder.platform,
|
|
257
|
+
personaChannel: harness.agentBuilder.personaChannel,
|
|
258
|
+
lower: harness.agentBuilder.lower,
|
|
259
|
+
}
|
|
260
|
+
: harness.executionLowerer;
|
|
261
|
+
if (!lowerer) {
|
|
262
|
+
throw new Error(`dispatch harness ${harness.id} has no registered resolved-request lowerer`);
|
|
263
|
+
}
|
|
264
|
+
if (lowerer.platform !== harness.id) {
|
|
265
|
+
throw new Error(`dispatch harness ${harness.id} registered mismatched lowerer ${lowerer.platform}`);
|
|
266
|
+
}
|
|
267
|
+
return [harness.id, lowerer];
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const AGENT_LOWERERS = new Map(lowererEntries());
|
|
271
|
+
function requireRunnerShape(request, runner) {
|
|
272
|
+
if (!own(runner, "engine") || runner.engine !== request.engine.name) {
|
|
273
|
+
throw new ConfigError(`Resolved engine ${JSON.stringify(request.engine.name)} changed engine identity before lowering.`, "INVALID_CONFIG_FILE");
|
|
274
|
+
}
|
|
275
|
+
if (request.engine.kind !== runner.kind) {
|
|
276
|
+
throw new ConfigError(`Resolved engine ${JSON.stringify(request.engine.name)} changed transport kind before lowering.`, "INVALID_CONFIG_FILE");
|
|
277
|
+
}
|
|
278
|
+
const platform = own(request.engine, "platform") ? request.engine.platform : undefined;
|
|
279
|
+
const runnerPlatform = runner.kind === "llm"
|
|
280
|
+
? (runner.connection.provider ?? runner.engine)
|
|
281
|
+
: (runner.profile.platform ?? runner.profile.name);
|
|
282
|
+
// A selected platform is an identity binding rather than a capability hint.
|
|
283
|
+
if (typeof platform === "string" && platform !== runnerPlatform) {
|
|
284
|
+
throw new ConfigError(`Resolved engine ${JSON.stringify(request.engine.name)} changed provider/harness platform before lowering.`, "INVALID_CONFIG_FILE");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const LLM_CALL_ERROR_CODES = new Set([
|
|
288
|
+
"aborted",
|
|
289
|
+
"timeout",
|
|
290
|
+
"rate_limited",
|
|
291
|
+
"parse_error",
|
|
292
|
+
"provider_html_error",
|
|
293
|
+
"network_error",
|
|
294
|
+
"provider_error",
|
|
295
|
+
]);
|
|
296
|
+
function safeLlmCallErrorCode(error) {
|
|
297
|
+
if (!(error instanceof LlmCallError))
|
|
298
|
+
return undefined;
|
|
299
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(error, "code");
|
|
300
|
+
if (!descriptor || !("value" in descriptor) || !LLM_CALL_ERROR_CODES.has(descriptor.value)) {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
return descriptor.value;
|
|
304
|
+
}
|
|
305
|
+
function directLlmFailure(error) {
|
|
306
|
+
const code = safeLlmCallErrorCode(error);
|
|
307
|
+
if (!code)
|
|
308
|
+
return { reason: "spawn_failed" };
|
|
309
|
+
switch (code) {
|
|
310
|
+
case "aborted":
|
|
311
|
+
return { reason: "aborted", code };
|
|
312
|
+
case "timeout":
|
|
313
|
+
return { reason: "timeout", code };
|
|
314
|
+
case "rate_limited":
|
|
315
|
+
return { reason: "llm_rate_limit", code };
|
|
316
|
+
case "parse_error":
|
|
317
|
+
case "provider_html_error":
|
|
318
|
+
return { reason: "parse_error", code };
|
|
319
|
+
case "network_error":
|
|
320
|
+
case "provider_error":
|
|
321
|
+
return { reason: "spawn_failed", code };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function safeCaughtErrorMessage(error) {
|
|
325
|
+
if (typeof error === "string")
|
|
326
|
+
return error;
|
|
327
|
+
if (error instanceof Error) {
|
|
328
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(error, "message");
|
|
329
|
+
if (descriptor && "value" in descriptor && typeof descriptor.value === "string")
|
|
330
|
+
return descriptor.value;
|
|
331
|
+
}
|
|
332
|
+
if (error === null || error === undefined || typeof error === "number" || typeof error === "boolean") {
|
|
333
|
+
return String(error);
|
|
334
|
+
}
|
|
335
|
+
return "LLM transport failed.";
|
|
336
|
+
}
|
|
337
|
+
function requestRuntimeOptions(request) {
|
|
338
|
+
const options = sterileRecord({ stdio: "captured", parseOutput: "text" });
|
|
339
|
+
if (own(request.runtime, "timeoutMs"))
|
|
340
|
+
options.timeoutMs = request.runtime.timeoutMs;
|
|
341
|
+
if (own(request.runtime, "workspace") && typeof request.runtime.workspace === "string") {
|
|
342
|
+
options.cwd = request.runtime.workspace;
|
|
343
|
+
}
|
|
344
|
+
if (own(request.runtime, "environment") && request.runtime.environment !== null) {
|
|
345
|
+
options.env = request.runtime.environment;
|
|
346
|
+
}
|
|
347
|
+
return Object.freeze(options);
|
|
348
|
+
}
|
|
349
|
+
function projectAgentRunner(base, request) {
|
|
350
|
+
const settings = own(request.engine, "settings") ? request.engine.settings : undefined;
|
|
351
|
+
const profile = sterileRecord({ ...base.profile });
|
|
352
|
+
delete profile.model;
|
|
353
|
+
delete profile.workspace;
|
|
354
|
+
if (own(request, "model") && request.model) {
|
|
355
|
+
profile.model = request.model.resolved;
|
|
356
|
+
}
|
|
357
|
+
if (own(request.runtime, "workspace") && typeof request.runtime.workspace === "string") {
|
|
358
|
+
profile.workspace = request.runtime.workspace;
|
|
359
|
+
}
|
|
360
|
+
if (settings) {
|
|
361
|
+
if (own(settings, "bin") && typeof settings.bin === "string")
|
|
362
|
+
profile.bin = settings.bin;
|
|
363
|
+
if (own(settings, "args") &&
|
|
364
|
+
Array.isArray(settings.args) &&
|
|
365
|
+
settings.args.every((entry) => typeof entry === "string")) {
|
|
366
|
+
profile.args = Object.freeze([...settings.args]);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const common = {
|
|
370
|
+
...base,
|
|
371
|
+
profile: Object.freeze(profile),
|
|
372
|
+
};
|
|
373
|
+
const sdkFallbackModelFromRequest = base.kind === "sdk" &&
|
|
374
|
+
settings !== null &&
|
|
375
|
+
settings !== undefined &&
|
|
376
|
+
own(settings, SDK_FALLBACK_MODEL_FROM_REQUEST_SETTING);
|
|
377
|
+
const clearsSdkFallbackModel = base.kind === "sdk" && base.fallbackConnection !== undefined && request.model === null;
|
|
378
|
+
if (sdkFallbackModelFromRequest && settings?.[SDK_FALLBACK_MODEL_FROM_REQUEST_SETTING] !== true) {
|
|
379
|
+
throw new ConfigError("Resolved SDK fallback model marker is invalid.", "INVALID_CONFIG_FILE");
|
|
380
|
+
}
|
|
381
|
+
if (sdkFallbackModelFromRequest && (!base.fallbackConnection || !own(request, "model"))) {
|
|
382
|
+
throw new ConfigError("Resolved SDK fallback model material is incomplete.", "INVALID_CONFIG_FILE");
|
|
383
|
+
}
|
|
384
|
+
if (base.kind === "sdk" && base.fallbackConnection) {
|
|
385
|
+
const fallbackConnection = sterileRecord({ ...base.fallbackConnection });
|
|
386
|
+
if (sdkFallbackModelFromRequest || clearsSdkFallbackModel) {
|
|
387
|
+
delete fallbackConnection.model;
|
|
388
|
+
if (sdkFallbackModelFromRequest && request.model)
|
|
389
|
+
fallbackConnection.model = request.model.resolved;
|
|
390
|
+
}
|
|
391
|
+
if (own(request, "inference")) {
|
|
392
|
+
for (const key of LLM_INFERENCE_CONNECTION_FIELDS)
|
|
393
|
+
delete fallbackConnection[key];
|
|
394
|
+
if (request.inference) {
|
|
395
|
+
for (const key of LLM_INFERENCE_CONNECTION_FIELDS) {
|
|
396
|
+
if (own(request.inference, key))
|
|
397
|
+
fallbackConnection[key] = request.inference[key];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
common.fallbackConnection = Object.freeze(fallbackConnection);
|
|
402
|
+
}
|
|
403
|
+
delete common.timeoutMs;
|
|
404
|
+
if (own(request.runtime, "timeoutMs"))
|
|
405
|
+
common.timeoutMs = request.runtime.timeoutMs;
|
|
406
|
+
else if (own(base, "timeoutMs"))
|
|
407
|
+
common.timeoutMs = base.timeoutMs;
|
|
408
|
+
return snapshotRunnerSpec(common, {
|
|
409
|
+
allowMissingSdkFallbackModel: clearsSdkFallbackModel,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function selectedExtensionPaths(request) {
|
|
413
|
+
const out = [];
|
|
414
|
+
if (own(request, "extensions"))
|
|
415
|
+
out.push("extensions");
|
|
416
|
+
if (own(request.command, "extensions"))
|
|
417
|
+
out.push("command.extensions");
|
|
418
|
+
const persona = own(request, "persona") ? request.persona : undefined;
|
|
419
|
+
if (persona && own(persona, "extensions"))
|
|
420
|
+
out.push("persona.extensions");
|
|
421
|
+
if (own(request.engine, "extensions"))
|
|
422
|
+
out.push("engine.extensions");
|
|
423
|
+
if (own(request.runtime, "extensions"))
|
|
424
|
+
out.push("runtime.extensions");
|
|
425
|
+
return out;
|
|
426
|
+
}
|
|
427
|
+
const LLM_INFERENCE_CONNECTION_FIELDS = new Set([
|
|
428
|
+
"temperature",
|
|
429
|
+
"maxTokens",
|
|
430
|
+
"supportsJsonSchema",
|
|
431
|
+
"extraParams",
|
|
432
|
+
"contextLength",
|
|
433
|
+
"enableThinking",
|
|
434
|
+
"reasoningEffort",
|
|
435
|
+
]);
|
|
436
|
+
function projectLlmRunner(base, request) {
|
|
437
|
+
const connection = sterileRecord({ ...base.connection });
|
|
438
|
+
for (const key of [
|
|
439
|
+
"model",
|
|
440
|
+
"temperature",
|
|
441
|
+
"maxTokens",
|
|
442
|
+
"supportsJsonSchema",
|
|
443
|
+
"extraParams",
|
|
444
|
+
"contextLength",
|
|
445
|
+
"enableThinking",
|
|
446
|
+
"reasoningEffort",
|
|
447
|
+
]) {
|
|
448
|
+
delete connection[key];
|
|
449
|
+
}
|
|
450
|
+
const settings = own(request.engine, "settings") ? request.engine.settings : undefined;
|
|
451
|
+
if (settings) {
|
|
452
|
+
if (own(settings, "endpoint") && typeof settings.endpoint === "string")
|
|
453
|
+
connection.endpoint = settings.endpoint;
|
|
454
|
+
if (own(settings, "provider") && typeof settings.provider === "string")
|
|
455
|
+
connection.provider = settings.provider;
|
|
456
|
+
}
|
|
457
|
+
if (own(request, "model") && request.model)
|
|
458
|
+
connection.model = request.model.resolved;
|
|
459
|
+
const inference = own(request, "inference") ? request.inference : undefined;
|
|
460
|
+
if (inference) {
|
|
461
|
+
for (const key of LLM_INFERENCE_CONNECTION_FIELDS) {
|
|
462
|
+
if (own(inference, key))
|
|
463
|
+
connection[key] = inference[key];
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const projected = {
|
|
467
|
+
...base,
|
|
468
|
+
connection: Object.freeze(connection),
|
|
469
|
+
};
|
|
470
|
+
delete projected.timeoutMs;
|
|
471
|
+
if (own(request.runtime, "timeoutMs"))
|
|
472
|
+
projected.timeoutMs = request.runtime.timeoutMs;
|
|
473
|
+
else if (own(base, "timeoutMs"))
|
|
474
|
+
projected.timeoutMs = base.timeoutMs;
|
|
475
|
+
return snapshotRunnerSpec(projected, { allowMissingLlmModel: true });
|
|
476
|
+
}
|
|
477
|
+
function lowerAgent(request, base) {
|
|
478
|
+
const platform = own(request.engine, "platform") ? request.engine.platform : undefined;
|
|
479
|
+
if (typeof platform !== "string") {
|
|
480
|
+
throw new ConfigError(`Resolved agent engine ${JSON.stringify(request.engine.name)} has no platform.`, "INVALID_CONFIG_FILE");
|
|
481
|
+
}
|
|
482
|
+
const lowerer = AGENT_LOWERERS.get(platform);
|
|
483
|
+
if (!lowerer) {
|
|
484
|
+
throw new ConfigError(`Agent platform ${JSON.stringify(platform)} has no registered lowerer.`, "INVALID_CONFIG_FILE");
|
|
485
|
+
}
|
|
486
|
+
const runner = projectAgentRunner(base, request);
|
|
487
|
+
const lowered = lowerer.lower(runner.profile, request);
|
|
488
|
+
const options = Object.freeze(sterileRecord({ ...requestRuntimeOptions(request), dispatch: lowered.dispatch }));
|
|
489
|
+
return registerLoweredExecution({
|
|
490
|
+
schemaVersion: EXECUTION_LOWERING_SCHEMA_VERSION,
|
|
491
|
+
request,
|
|
492
|
+
adapter: platform,
|
|
493
|
+
runner,
|
|
494
|
+
prompt: lowered.prompt,
|
|
495
|
+
dispatch: lowered.dispatch,
|
|
496
|
+
options,
|
|
497
|
+
translatedFields: sortedUnique([
|
|
498
|
+
...lowered.translatedFields,
|
|
499
|
+
...(own(request.engine, "settings") ? ["engine.settings"] : []),
|
|
500
|
+
]),
|
|
501
|
+
untranslatedFields: lowered.untranslatedFields,
|
|
502
|
+
notices: freezeNotices([...safeRequestNotices(request), ...lowered.notices]),
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function lowerLlm(request, base) {
|
|
506
|
+
const runner = projectLlmRunner(base, request);
|
|
507
|
+
const translated = new Set(["command.content", "engine"]);
|
|
508
|
+
const untranslated = new Set();
|
|
509
|
+
const notices = [...safeRequestNotices(request)];
|
|
510
|
+
const reject = (field) => {
|
|
511
|
+
untranslated.add(field);
|
|
512
|
+
notices.push(untranslatedNotice("llm", field));
|
|
513
|
+
};
|
|
514
|
+
if (own(request.engine, "settings"))
|
|
515
|
+
translated.add("engine.settings");
|
|
516
|
+
if (own(request, "model"))
|
|
517
|
+
translated.add("model");
|
|
518
|
+
if (own(request, "persona"))
|
|
519
|
+
translated.add("persona");
|
|
520
|
+
if (own(request, "agent")) {
|
|
521
|
+
if (typeof request.agent === "string" && request.persona === null) {
|
|
522
|
+
throw new ConfigError(`The direct LLM transport cannot consume native agent selector ${JSON.stringify(request.agent)}.`, "INVALID_CONFIG_FILE");
|
|
523
|
+
}
|
|
524
|
+
translated.add("agent");
|
|
525
|
+
}
|
|
526
|
+
if (own(request, "conversation"))
|
|
527
|
+
translated.add("conversation");
|
|
528
|
+
if (own(request, "inference")) {
|
|
529
|
+
const inference = request.inference;
|
|
530
|
+
if (inference && Object.keys(inference).length > 0) {
|
|
531
|
+
for (const key of Object.keys(inference).sort()) {
|
|
532
|
+
const field = `inference.${key}`;
|
|
533
|
+
if (LLM_INFERENCE_CONNECTION_FIELDS.has(key))
|
|
534
|
+
translated.add(field);
|
|
535
|
+
else
|
|
536
|
+
reject(field);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
translated.add("inference");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const chatOptions = sterileRecord({});
|
|
544
|
+
if (own(request.runtime, "timeoutMs")) {
|
|
545
|
+
translated.add("runtime.timeoutMs");
|
|
546
|
+
chatOptions.timeoutMs = request.runtime.timeoutMs;
|
|
547
|
+
}
|
|
548
|
+
if (own(request, "outputSchema")) {
|
|
549
|
+
if (request.outputSchema === null)
|
|
550
|
+
translated.add("outputSchema");
|
|
551
|
+
else if (runner.connection.supportsJsonSchema === true) {
|
|
552
|
+
translated.add("outputSchema");
|
|
553
|
+
chatOptions.responseSchema = request.outputSchema;
|
|
554
|
+
}
|
|
555
|
+
else
|
|
556
|
+
reject("outputSchema");
|
|
557
|
+
}
|
|
558
|
+
if (own(request, "tools")) {
|
|
559
|
+
const tools = request.tools;
|
|
560
|
+
const empty = tools === null ||
|
|
561
|
+
tools === "" ||
|
|
562
|
+
(Array.isArray(tools) && tools.length === 0) ||
|
|
563
|
+
(typeof tools === "object" && tools !== null && !Array.isArray(tools) && Object.keys(tools).length === 0);
|
|
564
|
+
if (empty)
|
|
565
|
+
translated.add("tools");
|
|
566
|
+
else
|
|
567
|
+
reject("tools");
|
|
568
|
+
}
|
|
569
|
+
if (own(request.runtime, "workspace")) {
|
|
570
|
+
if (request.runtime.workspace === null || request.runtime.workspace === "")
|
|
571
|
+
translated.add("runtime.workspace");
|
|
572
|
+
else
|
|
573
|
+
reject("runtime.workspace");
|
|
574
|
+
}
|
|
575
|
+
if (own(request.runtime, "environment")) {
|
|
576
|
+
const environment = request.runtime.environment;
|
|
577
|
+
if (environment === null || environment === undefined || Object.keys(environment).length === 0) {
|
|
578
|
+
translated.add("runtime.environment");
|
|
579
|
+
}
|
|
580
|
+
else
|
|
581
|
+
reject("runtime.environment");
|
|
582
|
+
}
|
|
583
|
+
if (own(request.runtime, "settings")) {
|
|
584
|
+
const settings = request.runtime.settings;
|
|
585
|
+
if (settings === null || settings === undefined || Object.keys(settings).length === 0) {
|
|
586
|
+
translated.add("runtime.settings");
|
|
587
|
+
}
|
|
588
|
+
else
|
|
589
|
+
reject("runtime.settings");
|
|
590
|
+
}
|
|
591
|
+
for (const field of selectedExtensionPaths(request))
|
|
592
|
+
reject(field);
|
|
593
|
+
const messages = Object.freeze([
|
|
594
|
+
...(own(request, "persona") && request.persona
|
|
595
|
+
? [{ role: "system", content: request.persona.content }]
|
|
596
|
+
: []),
|
|
597
|
+
...(request.conversation ?? []),
|
|
598
|
+
{ role: "user", content: request.command.content },
|
|
599
|
+
].map((message) => Object.freeze({ role: message.role, content: message.content })));
|
|
600
|
+
return registerLoweredExecution({
|
|
601
|
+
schemaVersion: EXECUTION_LOWERING_SCHEMA_VERSION,
|
|
602
|
+
request,
|
|
603
|
+
adapter: "llm",
|
|
604
|
+
runner,
|
|
605
|
+
prompt: request.command.content,
|
|
606
|
+
messages,
|
|
607
|
+
chatOptions: Object.freeze(chatOptions),
|
|
608
|
+
options: requestRuntimeOptions(request),
|
|
609
|
+
translatedFields: sortedUnique(translated),
|
|
610
|
+
untranslatedFields: sortedUnique(untranslated),
|
|
611
|
+
notices: freezeNotices(notices),
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
/** The sole live-config engine resolver used by every current execution mode. */
|
|
615
|
+
function resolveSnapshotRunner(engine, config) {
|
|
616
|
+
return snapshotRunnerSpec(resolveEngine(engine, config));
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Lower one payload-free interactive launch. The input vocabulary is closed:
|
|
620
|
+
* there is deliberately no command, prompt, model, tool, or arbitrary env.
|
|
621
|
+
*/
|
|
622
|
+
function lowerInteractiveAgentExecution(input) {
|
|
623
|
+
if (typeof input.engine !== "string" || input.engine.length === 0) {
|
|
624
|
+
throw new TypeError("interactive agent execution input.engine must be a non-empty string");
|
|
625
|
+
}
|
|
626
|
+
if (input.timeoutMs !== undefined) {
|
|
627
|
+
validateTimeout(input.timeoutMs, "interactive agent execution input.timeoutMs");
|
|
628
|
+
}
|
|
629
|
+
if (input.cwd !== undefined && typeof input.cwd !== "string") {
|
|
630
|
+
throw new TypeError("interactive agent execution input.cwd must be a string");
|
|
631
|
+
}
|
|
632
|
+
const runner = resolveSnapshotRunner(input.engine, snapshotConfig(input.config));
|
|
633
|
+
if (runner.kind === "llm") {
|
|
634
|
+
throw new UsageError(`Engine "${input.engine}" is an LLM engine; akm agent requires an agent engine.`, "INVALID_FLAG_VALUE");
|
|
635
|
+
}
|
|
636
|
+
const options = Object.freeze(sterileRecord({
|
|
637
|
+
stdio: runner.kind === "sdk" ? runner.profile.stdio : "interactive",
|
|
638
|
+
parseOutput: "text",
|
|
639
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
|
|
640
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
641
|
+
}));
|
|
642
|
+
return Object.freeze({
|
|
643
|
+
engine: input.engine,
|
|
644
|
+
runner,
|
|
645
|
+
options,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* The one optimistic lowering boundary. Authorization and request-brand
|
|
650
|
+
* validation precede config, registry, credential, and transport work.
|
|
651
|
+
*/
|
|
652
|
+
export function lowerResolvedExecutionRequest(input, config) {
|
|
653
|
+
const request = requireAuthorizedRequest(input);
|
|
654
|
+
const frozenConfig = snapshotConfig(config);
|
|
655
|
+
const base = resolveSnapshotRunner(request.engine.name, frozenConfig);
|
|
656
|
+
requireRunnerShape(request, base);
|
|
657
|
+
return base.kind === "llm" ? lowerLlm(request, base) : lowerAgent(request, base);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Lower from already-frozen, symbolic runner material. Workflow resume uses
|
|
661
|
+
* this entry point so no live config, alias, environment, or credential lookup
|
|
662
|
+
* can alter a journaled request before dispatch.
|
|
663
|
+
*/
|
|
664
|
+
export function lowerResolvedExecutionRequestWithRunner(input, runner) {
|
|
665
|
+
const request = requireAuthorizedRequest(input);
|
|
666
|
+
const base = snapshotRunnerSpec(runner);
|
|
667
|
+
requireRunnerShape(request, base);
|
|
668
|
+
return base.kind === "llm" ? lowerLlm(request, base) : lowerAgent(request, base);
|
|
669
|
+
}
|
|
670
|
+
function requireLoweredExecutionProvenance(lowered) {
|
|
671
|
+
if (typeof lowered !== "object" ||
|
|
672
|
+
lowered === null ||
|
|
673
|
+
!loweredExecutionInstances.has(lowered) ||
|
|
674
|
+
!Object.isFrozen(lowered)) {
|
|
675
|
+
throw new TypeError("lowered execution request must be produced by the engine lowerer registry");
|
|
676
|
+
}
|
|
677
|
+
canonicalResolvedExecutionRequest(lowered.request);
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Acquire an opaque operation capability only after genuine lowered-request
|
|
681
|
+
* provenance and canonical authorization have been revalidated.
|
|
682
|
+
*/
|
|
683
|
+
export function acquireLoweredExecutionDispatchLease(lowered, options = {}) {
|
|
684
|
+
requireLoweredExecutionProvenance(lowered);
|
|
685
|
+
const optionSnapshot = snapshotStrictRecord(options, "dispatch lease acquisition options");
|
|
686
|
+
assertSnapshotKeys(optionSnapshot, ["envSource"], "dispatch lease acquisition options");
|
|
687
|
+
const strictOptions = optionSnapshot;
|
|
688
|
+
return acquireRunnerDispatchLease(lowered.runner, strictOptions.envSource ?? process.env);
|
|
689
|
+
}
|
|
690
|
+
/** Idempotently scrub and invalidate a genuine operation dispatch lease. */
|
|
691
|
+
export function disposeLoweredExecutionDispatchLease(lease) {
|
|
692
|
+
disposeRunnerDispatchLease(lease);
|
|
693
|
+
}
|
|
694
|
+
/** Redact text with a lease's private credential inventory without exposing it. */
|
|
695
|
+
export function redactWithLoweredExecutionDispatchLease(lease, value) {
|
|
696
|
+
return redactWithRunnerDispatchLease(lease, value);
|
|
697
|
+
}
|
|
698
|
+
/** One raw runner symbol reference for every shared execution dispatch mode. */
|
|
699
|
+
function runnerDispatcher(candidate) {
|
|
700
|
+
return candidate ?? executeRunner;
|
|
701
|
+
}
|
|
702
|
+
async function dispatchLoweredInteractiveAgentExecution(lowered, seams) {
|
|
703
|
+
const lease = acquireRunnerDispatchLease(lowered.runner, process.env);
|
|
704
|
+
try {
|
|
705
|
+
return await runnerDispatcher()(lowered.runner, "", lowered.options, {
|
|
706
|
+
...(seams.runAgent ? { runAgent: seams.runAgent } : {}),
|
|
707
|
+
...(seams.runSdk ? { runSdk: seams.runSdk } : {}),
|
|
708
|
+
}, lease);
|
|
709
|
+
}
|
|
710
|
+
finally {
|
|
711
|
+
disposeRunnerDispatchLease(lease);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/** Resolve, lower, and dispatch one native interactive agent invocation. */
|
|
715
|
+
export async function executeInteractiveAgentInvocation(input, seams = {}) {
|
|
716
|
+
const lowered = lowerInteractiveAgentExecution(input);
|
|
717
|
+
return {
|
|
718
|
+
engine: lowered.engine,
|
|
719
|
+
result: await dispatchLoweredInteractiveAgentExecution(lowered, seams),
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
/** Dispatch a previously lowered request without re-resolving any model, ref, or persona. */
|
|
723
|
+
export async function dispatchLoweredExecutionRequest(lowered, options = {}) {
|
|
724
|
+
requireLoweredExecutionProvenance(lowered);
|
|
725
|
+
const optionSnapshot = snapshotStrictRecord(options, "lowered execution dispatch options");
|
|
726
|
+
assertSnapshotKeys(optionSnapshot, ["executeRunner", "chat", "onRetryAttempt", "runAgent", "runSdk", "lease", "runOptions"], "lowered execution dispatch options");
|
|
727
|
+
const strictOptions = optionSnapshot;
|
|
728
|
+
if (strictOptions.onRetryAttempt !== undefined && typeof strictOptions.onRetryAttempt !== "function") {
|
|
729
|
+
throw new TypeError("lowered execution dispatch options.onRetryAttempt must be a function");
|
|
730
|
+
}
|
|
731
|
+
const usesDefaultRunner = strictOptions.executeRunner === undefined;
|
|
732
|
+
const run = runnerDispatcher(strictOptions.executeRunner);
|
|
733
|
+
const operationalSnapshot = snapshotStrictRecord(strictOptions.runOptions ?? {}, "lowered execution operational options");
|
|
734
|
+
assertSnapshotKeys(operationalSnapshot, [
|
|
735
|
+
"stdio",
|
|
736
|
+
"timeoutMs",
|
|
737
|
+
"parseOutput",
|
|
738
|
+
"env",
|
|
739
|
+
"cwd",
|
|
740
|
+
"args",
|
|
741
|
+
"stdin",
|
|
742
|
+
"signal",
|
|
743
|
+
"envSource",
|
|
744
|
+
"spawn",
|
|
745
|
+
"setTimeoutFn",
|
|
746
|
+
"clearTimeoutFn",
|
|
747
|
+
"onEvent",
|
|
748
|
+
"dispatch",
|
|
749
|
+
"builderRegistry",
|
|
750
|
+
], "lowered execution operational options");
|
|
751
|
+
const operational = operationalSnapshot;
|
|
752
|
+
const runOptions = sterileRecord({
|
|
753
|
+
...lowered.options,
|
|
754
|
+
...(operational.stdio !== undefined ? { stdio: operational.stdio } : {}),
|
|
755
|
+
...(operational.parseOutput !== undefined ? { parseOutput: operational.parseOutput } : {}),
|
|
756
|
+
...(operational.signal !== undefined ? { signal: operational.signal } : {}),
|
|
757
|
+
...(operational.envSource !== undefined ? { envSource: operational.envSource } : {}),
|
|
758
|
+
...(operational.spawn !== undefined ? { spawn: operational.spawn } : {}),
|
|
759
|
+
...(operational.setTimeoutFn !== undefined ? { setTimeoutFn: operational.setTimeoutFn } : {}),
|
|
760
|
+
...(operational.clearTimeoutFn !== undefined ? { clearTimeoutFn: operational.clearTimeoutFn } : {}),
|
|
761
|
+
...(operational.onEvent !== undefined ? { onEvent: operational.onEvent } : {}),
|
|
762
|
+
});
|
|
763
|
+
const ownedLease = strictOptions.lease === undefined && usesDefaultRunner;
|
|
764
|
+
const lease = strictOptions.lease ??
|
|
765
|
+
(ownedLease ? acquireRunnerDispatchLease(lowered.runner, runOptions.envSource ?? process.env) : undefined);
|
|
766
|
+
if (lease)
|
|
767
|
+
assertRunnerDispatchLease(lease, lowered.runner);
|
|
768
|
+
try {
|
|
769
|
+
return await run(lowered.runner, lowered.prompt, runOptions, {
|
|
770
|
+
...(strictOptions.runAgent ? { runAgent: strictOptions.runAgent } : {}),
|
|
771
|
+
...(strictOptions.runSdk ? { runSdk: strictOptions.runSdk } : {}),
|
|
772
|
+
...("messages" in lowered
|
|
773
|
+
? {
|
|
774
|
+
llm: async (spec, _prompt, runOptions) => {
|
|
775
|
+
const started = Date.now();
|
|
776
|
+
try {
|
|
777
|
+
const stdout = await (strictOptions.chat ?? chatCompletion)(spec.connection, [...lowered.messages], {
|
|
778
|
+
...lowered.chatOptions,
|
|
779
|
+
...(!own(lowered.chatOptions, "timeoutMs") && own(runOptions, "timeoutMs")
|
|
780
|
+
? { timeoutMs: runOptions.timeoutMs }
|
|
781
|
+
: {}),
|
|
782
|
+
...(runOptions.signal ? { signal: runOptions.signal } : {}),
|
|
783
|
+
...(strictOptions.onRetryAttempt ? { onRetryAttempt: strictOptions.onRetryAttempt } : {}),
|
|
784
|
+
});
|
|
785
|
+
return { ok: true, exitCode: 0, stdout, stderr: "", durationMs: Date.now() - started };
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
// Return through executeRunner so its credential-aware redactor
|
|
789
|
+
// handles provider bodies before any caller can persist them.
|
|
790
|
+
const failure = directLlmFailure(error);
|
|
791
|
+
return {
|
|
792
|
+
ok: false,
|
|
793
|
+
exitCode: null,
|
|
794
|
+
stdout: "",
|
|
795
|
+
stderr: "",
|
|
796
|
+
durationMs: Date.now() - started,
|
|
797
|
+
error: safeCaughtErrorMessage(error),
|
|
798
|
+
reason: failure.reason,
|
|
799
|
+
...(failure.code ? { llmErrorCode: failure.code } : {}),
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
}
|
|
804
|
+
: {}),
|
|
805
|
+
}, lease);
|
|
806
|
+
}
|
|
807
|
+
finally {
|
|
808
|
+
if (ownedLease && lease)
|
|
809
|
+
disposeRunnerDispatchLease(lease);
|
|
810
|
+
}
|
|
811
|
+
}
|