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
|
@@ -1,30 +1,11 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import path from "node:path";
|
|
5
4
|
import { isContainedRelativePath } from "../../core/common.js";
|
|
6
5
|
import { UsageError } from "../../core/errors.js";
|
|
7
|
-
import { formatExtraParamsIssue, validateExtraParams } from "../../core/extra-params.js";
|
|
8
|
-
import { HARNESS_BY_ID } from "../../integrations/harnesses/index.js";
|
|
9
6
|
import { parseReference } from "../program/expressions.js";
|
|
10
7
|
import { PROGRAM_PARAM_NAME_PATTERN, PROGRAM_RETRY_REASONS, PROGRAM_STEP_ID_PATTERN } from "../program/schema.js";
|
|
11
|
-
import { jsonBytes, utf8Bytes,
|
|
12
|
-
/** The only executable persisted workflow plan format. */
|
|
13
|
-
export const WORKFLOW_IR_VERSION = 3;
|
|
14
|
-
/**
|
|
15
|
-
* The {@link IrRuntimeKind} a frozen engine dispatches as — the value journaled
|
|
16
|
-
* in `workflow_run_units.runner` and read back by `status --units`.
|
|
17
|
-
*
|
|
18
|
-
* Lives here, beside the union, because the mapping is a property OF the union:
|
|
19
|
-
* spelled at each call site instead, a kind added to `FrozenEngineSnapshot`
|
|
20
|
-
* would type-check against whichever site happens to end with a default and be
|
|
21
|
-
* silently mis-journaled. Callers supply their own answer for the ABSENCE of an
|
|
22
|
-
* engine, which genuinely differs — a unit without one is an `exec` unit, while
|
|
23
|
-
* a gate without one is an llm judge.
|
|
24
|
-
*/
|
|
25
|
-
export function engineRuntimeKind(engine) {
|
|
26
|
-
return engine.kind === "llm" ? "llm" : engine.runnerKind;
|
|
27
|
-
}
|
|
8
|
+
import { jsonBytes, utf8Bytes, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_EXEC_ARG_BYTES, WORKFLOW_MAX_EXEC_ARGV, WORKFLOW_MAX_EXEC_CWD_LENGTH, WORKFLOW_MAX_EXEC_PASS_ENV, WORKFLOW_MAX_GATE_LOOPS, WORKFLOW_MAX_INPUTS, WORKFLOW_MAX_INSTRUCTION_BYTES, WORKFLOW_MAX_JSON_DEPTH, WORKFLOW_MAX_MAP_EXPANSION, WORKFLOW_MAX_PARAMS, WORKFLOW_MAX_PLAN_BYTES, WORKFLOW_MAX_RETRIES, WORKFLOW_MAX_ROUTE_BRANCHES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_STEPS, WORKFLOW_MAX_TIMEOUT_MS, } from "../resource-limits.js";
|
|
28
9
|
// Shared dispatch-significant bounds now live in `../resource-limits` so the
|
|
29
10
|
// parser, the published JSON Schema, and this decoder enforce identical
|
|
30
11
|
// values. Re-exported here for existing importers (e.g. `commands/workflow-cli.ts`).
|
|
@@ -32,16 +13,16 @@ export { WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_GATE_LOOPS, WORKFLOW_MAX_RETRIES
|
|
|
32
13
|
export const WORKFLOW_MAX_UNITS = WORKFLOW_MAX_MAP_EXPANSION;
|
|
33
14
|
const MAX_LIST_ITEMS = 1024;
|
|
34
15
|
const MAX_STRING_LENGTH = 1_000_000;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
16
|
+
/** Validate the current durable execution graph. */
|
|
17
|
+
export function validateWorkflowPlanStructure(input, options, hooks = {}) {
|
|
18
|
+
if (!isRecord(input) || input.irVersion !== options.expectedVersion) {
|
|
19
|
+
fail(`irVersion must be ${options.expectedVersion}`);
|
|
20
|
+
}
|
|
40
21
|
assertJson(input);
|
|
41
22
|
if (jsonBytes(input) > WORKFLOW_MAX_PLAN_BYTES)
|
|
42
23
|
fail("plan exceeds the 2 MiB resource limit");
|
|
43
24
|
const plan = input;
|
|
44
|
-
assertKeys(input, ["irVersion", "title", "params", "paramSchemas", "budget", "execution", "steps"], "plan");
|
|
25
|
+
assertKeys(input, ["irVersion", "title", "params", "paramSchemas", "budget", "execution", "steps", ...(options.planExtraKeys ?? [])], "plan");
|
|
45
26
|
assertString(plan.title, "title");
|
|
46
27
|
validateParams(plan.params, plan.paramSchemas);
|
|
47
28
|
validateBudget(plan.budget);
|
|
@@ -51,15 +32,7 @@ export function decodeWorkflowPlanV3(input, hooks = {}) {
|
|
|
51
32
|
plan.execution.maxConcurrency > WORKFLOW_MAX_CONCURRENCY) {
|
|
52
33
|
fail(`execution.maxConcurrency must be an integer from 1 through ${WORKFLOW_MAX_CONCURRENCY}`);
|
|
53
34
|
}
|
|
54
|
-
assertKeys(plan.execution, ["maxConcurrency"
|
|
55
|
-
if (!isRecord(plan.execution.engines))
|
|
56
|
-
fail("execution.engines must be an object");
|
|
57
|
-
const engines = plan.execution.engines;
|
|
58
|
-
if (Object.keys(engines).length > WORKFLOW_MAX_ENGINES)
|
|
59
|
-
fail(`execution.engines exceeds ${WORKFLOW_MAX_ENGINES} entries`);
|
|
60
|
-
const references = new Set();
|
|
61
|
-
for (const [key, engine] of Object.entries(engines))
|
|
62
|
-
validateEngine(key, engine, references, hooks);
|
|
35
|
+
assertKeys(plan.execution, ["maxConcurrency"], "execution");
|
|
63
36
|
if (!Array.isArray(plan.steps) || plan.steps.length === 0 || plan.steps.length > WORKFLOW_MAX_STEPS)
|
|
64
37
|
fail("steps must contain 1 through 256 entries");
|
|
65
38
|
const stepIds = new Set();
|
|
@@ -81,10 +54,10 @@ export function decodeWorkflowPlanV3(input, hooks = {}) {
|
|
|
81
54
|
if (step.outputSchema !== undefined)
|
|
82
55
|
validateSchema(step.outputSchema, `step ${step.stepId} outputSchema`);
|
|
83
56
|
if (step.root)
|
|
84
|
-
validateNode(step.root, step.stepId,
|
|
57
|
+
validateNode(step.root, step.stepId, nodeIds, options.unitExtraKeys ?? []);
|
|
85
58
|
if (step.route)
|
|
86
59
|
validateRoute(step.route, step.stepId);
|
|
87
|
-
validateGate(step.gate, step.stepId,
|
|
60
|
+
validateGate(step.gate, step.stepId, nodeIds, options.gateExtraKeys ?? []);
|
|
88
61
|
}
|
|
89
62
|
const stepIndex = new Map(plan.steps.map((step, index) => [step.stepId, index]));
|
|
90
63
|
for (const [index, step] of plan.steps.entries()) {
|
|
@@ -99,136 +72,8 @@ export function decodeWorkflowPlanV3(input, hooks = {}) {
|
|
|
99
72
|
}
|
|
100
73
|
validateStepExpressions(step, index, stepIndex);
|
|
101
74
|
}
|
|
102
|
-
for (const name of references) {
|
|
103
|
-
if (!engines[name])
|
|
104
|
-
fail(`engine reference ${name} is not in execution.engines`);
|
|
105
|
-
}
|
|
106
|
-
for (const name of Object.keys(engines))
|
|
107
|
-
if (!references.has(name))
|
|
108
|
-
fail(`engine ${name} is not referenced`);
|
|
109
|
-
for (const engine of Object.values(engines)) {
|
|
110
|
-
if (engine.kind !== "agent")
|
|
111
|
-
continue;
|
|
112
|
-
if (engine.fallbackLlmEngine !== null && engines[engine.fallbackLlmEngine]?.kind !== "llm")
|
|
113
|
-
fail(`SDK engine ${engine.name} fallback must name an LLM engine`);
|
|
114
|
-
}
|
|
115
|
-
for (const step of plan.steps) {
|
|
116
|
-
if (step.root)
|
|
117
|
-
assertUnitEngineCompatibility(step.root, engines);
|
|
118
|
-
if (step.gate.judge) {
|
|
119
|
-
const judge = engines[step.gate.judge.engine];
|
|
120
|
-
if (!judge)
|
|
121
|
-
fail(`gate ${step.gate.id} references an unavailable engine`);
|
|
122
|
-
if (judge.kind === "llm" && step.gate.judge.model === null)
|
|
123
|
-
fail(`LLM gate ${step.gate.id} has no exact model`);
|
|
124
|
-
if (judge.kind === "agent" && step.gate.judge.llm !== undefined)
|
|
125
|
-
fail(`agent gate ${step.gate.id} cannot carry LLM invocation settings`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return plan;
|
|
129
75
|
}
|
|
130
|
-
function
|
|
131
|
-
if (!ENGINE_NAME_PATTERN.test(key) ||
|
|
132
|
-
key.length > WORKFLOW_MAX_ENGINE_NAME_LENGTH ||
|
|
133
|
-
!isRecord(engine) ||
|
|
134
|
-
engine.name !== key ||
|
|
135
|
-
(engine.kind !== "llm" && engine.kind !== "agent"))
|
|
136
|
-
fail("catalog keys must equal a valid snapshot name");
|
|
137
|
-
if (engine.kind === "llm") {
|
|
138
|
-
assertKeys(engine, [
|
|
139
|
-
"name",
|
|
140
|
-
"kind",
|
|
141
|
-
"provider",
|
|
142
|
-
"endpoint",
|
|
143
|
-
"model",
|
|
144
|
-
"credential",
|
|
145
|
-
"temperature",
|
|
146
|
-
"maxTokens",
|
|
147
|
-
"concurrency",
|
|
148
|
-
"supportsJsonSchema",
|
|
149
|
-
"extraParams",
|
|
150
|
-
"contextLength",
|
|
151
|
-
"enableThinking",
|
|
152
|
-
], `LLM engine ${key}`);
|
|
153
|
-
if (typeof engine.endpoint !== "string" ||
|
|
154
|
-
!engine.endpoint ||
|
|
155
|
-
typeof engine.model !== "string" ||
|
|
156
|
-
!engine.model ||
|
|
157
|
-
!Number.isInteger(engine.concurrency) ||
|
|
158
|
-
engine.concurrency < 1 ||
|
|
159
|
-
engine.concurrency > WORKFLOW_MAX_CONCURRENCY)
|
|
160
|
-
fail(`LLM engine ${key} is invalid`);
|
|
161
|
-
if (engine.provider !== undefined)
|
|
162
|
-
assertString(engine.provider, `LLM engine ${key} provider`);
|
|
163
|
-
validateEndpoint(engine.endpoint, key);
|
|
164
|
-
validateOptionalFiniteNumber(engine.temperature, `LLM engine ${key} temperature`);
|
|
165
|
-
validateOptionalPositiveInteger(engine.maxTokens, `LLM engine ${key} maxTokens`);
|
|
166
|
-
validateOptionalPositiveInteger(engine.contextLength, `LLM engine ${key} contextLength`);
|
|
167
|
-
if (engine.supportsJsonSchema !== undefined && typeof engine.supportsJsonSchema !== "boolean")
|
|
168
|
-
fail(`LLM engine ${key} supportsJsonSchema must be boolean`);
|
|
169
|
-
if (engine.enableThinking !== undefined && typeof engine.enableThinking !== "boolean")
|
|
170
|
-
fail(`LLM engine ${key} enableThinking must be boolean`);
|
|
171
|
-
if (engine.extraParams !== undefined) {
|
|
172
|
-
const issue = validateExtraParams(engine.extraParams)[0];
|
|
173
|
-
if (issue)
|
|
174
|
-
fail(formatExtraParamsIssue(`LLM engine ${key} extraParams`, issue));
|
|
175
|
-
}
|
|
176
|
-
validateExtraParamsValue(engine.extraParams, `LLM engine ${key} extraParams`, hooks);
|
|
177
|
-
if (engine.credential !== undefined) {
|
|
178
|
-
if (!isRecord(engine.credential) ||
|
|
179
|
-
!Array.isArray(engine.credential.names) ||
|
|
180
|
-
engine.credential.names.length === 0 ||
|
|
181
|
-
engine.credential.names.length > 32 ||
|
|
182
|
-
!engine.credential.names.every((n) => typeof n === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(n)) ||
|
|
183
|
-
new Set(engine.credential.names).size !== engine.credential.names.length ||
|
|
184
|
-
typeof engine.credential.required !== "boolean") {
|
|
185
|
-
fail(`LLM engine ${key} has an invalid credential descriptor`);
|
|
186
|
-
}
|
|
187
|
-
assertKeys(engine.credential, ["names", "required"], `LLM engine ${key} credential`);
|
|
188
|
-
}
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
assertKeys(engine, [
|
|
192
|
-
"name",
|
|
193
|
-
"kind",
|
|
194
|
-
"runnerKind",
|
|
195
|
-
"platform",
|
|
196
|
-
"bin",
|
|
197
|
-
"args",
|
|
198
|
-
"workspace",
|
|
199
|
-
"envPassthrough",
|
|
200
|
-
"commandBuilder",
|
|
201
|
-
"fallbackLlmEngine",
|
|
202
|
-
], `agent engine ${key}`);
|
|
203
|
-
if ((engine.runnerKind !== "agent" && engine.runnerKind !== "sdk") ||
|
|
204
|
-
typeof engine.platform !== "string" ||
|
|
205
|
-
!engine.platform ||
|
|
206
|
-
typeof engine.bin !== "string" ||
|
|
207
|
-
!engine.bin ||
|
|
208
|
-
!Array.isArray(engine.args) ||
|
|
209
|
-
engine.args.length > MAX_LIST_ITEMS ||
|
|
210
|
-
!engine.args.every((arg) => typeof arg === "string" && arg.length <= MAX_STRING_LENGTH) ||
|
|
211
|
-
!(typeof engine.workspace === "string" || engine.workspace === null) ||
|
|
212
|
-
!Array.isArray(engine.envPassthrough) ||
|
|
213
|
-
engine.envPassthrough.length > MAX_LIST_ITEMS ||
|
|
214
|
-
!engine.envPassthrough.every((name) => typeof name === "string" && WORKFLOW_ENV_VAR_NAME_PATTERN.test(name)) ||
|
|
215
|
-
new Set(engine.envPassthrough).size !== engine.envPassthrough.length ||
|
|
216
|
-
typeof engine.commandBuilder !== "string" ||
|
|
217
|
-
engine.commandBuilder !== engine.platform ||
|
|
218
|
-
(engine.workspace !== null && !path.isAbsolute(engine.workspace)) ||
|
|
219
|
-
!HARNESS_BY_ID.get(engine.platform)?.capabilities.agentDispatch)
|
|
220
|
-
fail(`agent engine ${key} is invalid`);
|
|
221
|
-
if ((engine.platform === "opencode-sdk") !== (engine.runnerKind === "sdk"))
|
|
222
|
-
fail(`agent engine ${key} has incompatible platform and runnerKind`);
|
|
223
|
-
if (engine.runnerKind === "agent" && engine.fallbackLlmEngine !== null)
|
|
224
|
-
fail(`agent engine ${key} cannot have an SDK fallback`);
|
|
225
|
-
if (engine.fallbackLlmEngine !== null) {
|
|
226
|
-
if (typeof engine.fallbackLlmEngine !== "string" || !engine.fallbackLlmEngine)
|
|
227
|
-
fail(`agent engine ${key} has an invalid fallback`);
|
|
228
|
-
references.add(engine.fallbackLlmEngine);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
function validateNode(node, stepId, references, nodeIds, hooks) {
|
|
76
|
+
function validateNode(node, stepId, nodeIds, unitExtraKeys) {
|
|
232
77
|
if (!isRecord(node) ||
|
|
233
78
|
(node.kind !== "unit" && node.kind !== "map") ||
|
|
234
79
|
typeof node.id !== "string" ||
|
|
@@ -248,7 +93,7 @@ function validateNode(node, stepId, references, nodeIds, hooks) {
|
|
|
248
93
|
if (node.id !== `${stepId}.map`)
|
|
249
94
|
fail(`map ${node.id} does not match step ${stepId}`);
|
|
250
95
|
validateSource(node.source, `map ${node.id} source`);
|
|
251
|
-
validateNode(node.template, stepId,
|
|
96
|
+
validateNode(node.template, stepId, nodeIds, unitExtraKeys);
|
|
252
97
|
if (!isRecord(node.template) || node.template.kind !== "unit")
|
|
253
98
|
fail(`map ${node.id} template must be a unit`);
|
|
254
99
|
if (node.template.id !== `${stepId}.unit`)
|
|
@@ -259,20 +104,17 @@ function validateNode(node, stepId, references, nodeIds, hooks) {
|
|
|
259
104
|
"kind",
|
|
260
105
|
"id",
|
|
261
106
|
"instructions",
|
|
262
|
-
"templating",
|
|
263
107
|
"inputs",
|
|
264
|
-
"invocation",
|
|
265
|
-
"exec",
|
|
266
108
|
"schema",
|
|
267
109
|
"retry",
|
|
268
110
|
"onError",
|
|
269
111
|
"env",
|
|
270
112
|
"isolation",
|
|
271
113
|
"source",
|
|
114
|
+
...unitExtraKeys,
|
|
272
115
|
], `unit ${node.id}`);
|
|
273
116
|
if (typeof node.instructions !== "string" ||
|
|
274
117
|
!node.instructions ||
|
|
275
|
-
node.templating !== "verbatim" ||
|
|
276
118
|
(node.onError !== "fail" && node.onError !== "continue") ||
|
|
277
119
|
(node.isolation !== "none" && node.isolation !== "worktree"))
|
|
278
120
|
fail(`unit ${node.id} is invalid`);
|
|
@@ -286,17 +128,6 @@ function validateNode(node, stepId, references, nodeIds, hooks) {
|
|
|
286
128
|
validateStringArray(node.env, `unit ${node.id} env`, MAX_LIST_ITEMS, true);
|
|
287
129
|
validateStringArray(node.inputs, `unit ${node.id} inputs`, WORKFLOW_MAX_INPUTS, true);
|
|
288
130
|
validateSource(node.source, `unit ${node.id} source`);
|
|
289
|
-
// Exactly one dispatch mechanism per unit. A node carrying both would let a
|
|
290
|
-
// tampered plan smuggle a shell command past the engine-compatibility checks
|
|
291
|
-
// (which key off the invocation); one carrying neither has nothing to run.
|
|
292
|
-
if ((node.invocation === undefined) === (node.exec === undefined)) {
|
|
293
|
-
fail(`unit ${node.id} must declare exactly one of invocation or exec`);
|
|
294
|
-
}
|
|
295
|
-
if (node.exec !== undefined) {
|
|
296
|
-
validateExecSpec(node.exec, `unit ${node.id} exec`);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
validateInvocation(node.invocation, references, hooks);
|
|
300
131
|
}
|
|
301
132
|
/**
|
|
302
133
|
* Strictly decode a frozen {@link IrExecSpec}. This is the corruption gate for
|
|
@@ -307,7 +138,7 @@ function validateNode(node, stepId, references, nodeIds, hooks) {
|
|
|
307
138
|
function validateExecSpec(value, label) {
|
|
308
139
|
if (!isRecord(value))
|
|
309
140
|
fail(`${label} must be an object`);
|
|
310
|
-
assertKeys(value, ["command", "cwd", "passEnv", "
|
|
141
|
+
assertKeys(value, ["command", "cwd", "passEnv", "timeoutMs"], label);
|
|
311
142
|
validateExecEnvScope(value, label);
|
|
312
143
|
const command = value.command;
|
|
313
144
|
if (!Array.isArray(command) ||
|
|
@@ -331,20 +162,22 @@ function validateExecSpec(value, label) {
|
|
|
331
162
|
fail(`${label}.timeoutMs must be null or an integer from 1 through ${WORKFLOW_MAX_TIMEOUT_MS}`);
|
|
332
163
|
}
|
|
333
164
|
}
|
|
165
|
+
/** Decode one current frozen exec spec. */
|
|
166
|
+
export function decodeWorkflowExecSpec(value, label) {
|
|
167
|
+
validateExecSpec(value, label);
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
334
170
|
/**
|
|
335
171
|
* The child's ENVIRONMENT SCOPE half of a frozen exec spec. Split out so
|
|
336
172
|
* {@link validateExecSpec} keeps its shape (and the src-fn-size ratchet stays
|
|
337
173
|
* shrink-only).
|
|
338
174
|
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
175
|
+
* `passEnv` is canonical-form-only and may only be a non-empty deduplicated
|
|
176
|
+
* name list. A persisted `[]` means exactly what absence means, and admitting a
|
|
177
|
+
* second spelling of the default would give the same unit two different input
|
|
178
|
+
* hashes.
|
|
343
179
|
*/
|
|
344
180
|
function validateExecEnvScope(value, label) {
|
|
345
|
-
if (value.inheritEnv !== undefined && value.inheritEnv !== true) {
|
|
346
|
-
fail(`${label}.inheritEnv must be true when present (the allowlist default is the key's ABSENCE)`);
|
|
347
|
-
}
|
|
348
181
|
if (value.passEnv === undefined)
|
|
349
182
|
return;
|
|
350
183
|
const passEnv = value.passEnv;
|
|
@@ -357,7 +190,7 @@ function validateExecEnvScope(value, label) {
|
|
|
357
190
|
`matching ${WORKFLOW_ENV_VAR_NAME_PATTERN.source}`);
|
|
358
191
|
}
|
|
359
192
|
}
|
|
360
|
-
function validateGate(gate, stepId,
|
|
193
|
+
function validateGate(gate, stepId, nodeIds, gateExtraKeys) {
|
|
361
194
|
if (!isRecord(gate) ||
|
|
362
195
|
gate.kind !== "gate" ||
|
|
363
196
|
gate.id !== `${stepId}.gate` ||
|
|
@@ -371,26 +204,8 @@ function validateGate(gate, stepId, references, nodeIds, hooks) {
|
|
|
371
204
|
fail(`gate for step ${stepId} is invalid`);
|
|
372
205
|
if (nodeIds.has(gate.id))
|
|
373
206
|
fail(`gate id ${gate.id} collides with a node`);
|
|
374
|
-
assertKeys(gate, ["kind", "id", "stepId", "criteria", "maxLoops",
|
|
207
|
+
assertKeys(gate, ["kind", "id", "stepId", "criteria", "maxLoops", ...gateExtraKeys], `gate ${stepId}`);
|
|
375
208
|
nodeIds.add(gate.id);
|
|
376
|
-
if (gate.criteria.length === 0 && gate.judge !== null)
|
|
377
|
-
fail(`gate ${gate.id} without criteria cannot have a judge`);
|
|
378
|
-
if (gate.judge !== null)
|
|
379
|
-
validateInvocation(gate.judge, references, hooks);
|
|
380
|
-
}
|
|
381
|
-
function validateInvocation(invocation, references, hooks) {
|
|
382
|
-
if (!isRecord(invocation) ||
|
|
383
|
-
typeof invocation.engine !== "string" ||
|
|
384
|
-
!invocation.engine ||
|
|
385
|
-
!((typeof invocation.model === "string" && invocation.model.length > 0) || invocation.model === null) ||
|
|
386
|
-
!(invocation.timeoutMs === null ||
|
|
387
|
-
(Number.isSafeInteger(invocation.timeoutMs) &&
|
|
388
|
-
invocation.timeoutMs >= 1 &&
|
|
389
|
-
invocation.timeoutMs <= WORKFLOW_MAX_TIMEOUT_MS)))
|
|
390
|
-
fail("invocation is invalid");
|
|
391
|
-
references.add(invocation.engine);
|
|
392
|
-
assertKeys(invocation, ["engine", "model", "timeoutMs", "llm"], "invocation");
|
|
393
|
-
validateLlmOverrides(invocation.llm, hooks);
|
|
394
209
|
}
|
|
395
210
|
function validateRoute(route, stepId) {
|
|
396
211
|
if (!isRecord(route) ||
|
|
@@ -406,27 +221,6 @@ function validateRoute(route, stepId) {
|
|
|
406
221
|
if (route.defaultStepId !== undefined && (typeof route.defaultStepId !== "string" || !route.defaultStepId))
|
|
407
222
|
fail(`route for step ${stepId} has an invalid default target`);
|
|
408
223
|
}
|
|
409
|
-
function assertUnitEngineCompatibility(node, engines) {
|
|
410
|
-
const unit = node.kind === "map" ? node.template : node;
|
|
411
|
-
// An exec unit names no engine at all, so there is no engine pairing to
|
|
412
|
-
// check. It is the ONE kind that may carry `env` + `isolation: worktree`
|
|
413
|
-
// without an engine behind it: it spawns a real child process, which is
|
|
414
|
-
// exactly the precondition those two features require. `validateNode` has
|
|
415
|
-
// already proven it carries no `invocation`, so nothing here can be bypassed
|
|
416
|
-
// by declaring both.
|
|
417
|
-
if (unit.exec !== undefined)
|
|
418
|
-
return;
|
|
419
|
-
const engine = unit.invocation ? engines[unit.invocation.engine] : undefined;
|
|
420
|
-
if (!engine)
|
|
421
|
-
return;
|
|
422
|
-
if (engine.kind === "llm" && unit.invocation?.model === null)
|
|
423
|
-
fail(`LLM unit ${unit.id} has no exact model`);
|
|
424
|
-
if (engine.kind === "agent" && unit.invocation?.llm !== undefined)
|
|
425
|
-
fail(`agent unit ${unit.id} cannot carry LLM invocation settings`);
|
|
426
|
-
if (engine.kind === "llm" && ((unit.env?.length ?? 0) > 0 || unit.isolation === "worktree")) {
|
|
427
|
-
fail(`LLM unit ${unit.id} cannot use env injection or worktree isolation`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
224
|
function assertKeys(value, allowed, label) {
|
|
431
225
|
for (const key of Object.keys(value))
|
|
432
226
|
if (!allowed.includes(key))
|
|
@@ -508,46 +302,12 @@ function validateRetry(retry, nodeId) {
|
|
|
508
302
|
retry.on.some((reason) => !PROGRAM_RETRY_REASONS.includes(reason)))
|
|
509
303
|
fail(`unit ${nodeId} retry.on is invalid`);
|
|
510
304
|
}
|
|
511
|
-
function validateLlmOverrides(value, hooks) {
|
|
512
|
-
if (value === undefined)
|
|
513
|
-
return;
|
|
514
|
-
if (!isRecord(value))
|
|
515
|
-
fail("invocation.llm must be an object");
|
|
516
|
-
assertKeys(value, ["temperature", "maxTokens", "supportsJsonSchema", "extraParams", "contextLength", "enableThinking"], "invocation.llm");
|
|
517
|
-
validateOptionalFiniteNumber(value.temperature, "invocation.llm.temperature");
|
|
518
|
-
validateOptionalPositiveInteger(value.maxTokens, "invocation.llm.maxTokens");
|
|
519
|
-
validateOptionalPositiveInteger(value.contextLength, "invocation.llm.contextLength");
|
|
520
|
-
if (value.supportsJsonSchema !== undefined && typeof value.supportsJsonSchema !== "boolean")
|
|
521
|
-
fail("invocation.llm.supportsJsonSchema must be boolean");
|
|
522
|
-
if (value.enableThinking !== undefined && typeof value.enableThinking !== "boolean")
|
|
523
|
-
fail("invocation.llm.enableThinking must be boolean");
|
|
524
|
-
if (value.extraParams !== undefined) {
|
|
525
|
-
const issue = validateExtraParams(value.extraParams)[0];
|
|
526
|
-
if (issue)
|
|
527
|
-
fail(formatExtraParamsIssue("invocation.llm.extraParams", issue));
|
|
528
|
-
}
|
|
529
|
-
validateExtraParamsValue(value.extraParams, "invocation.llm.extraParams", hooks);
|
|
530
|
-
}
|
|
531
305
|
function validateSchema(value, label) {
|
|
532
306
|
if (!isRecord(value))
|
|
533
307
|
fail(`${label} must be an object`);
|
|
534
308
|
if (jsonBytes(value) > WORKFLOW_MAX_SCHEMA_BYTES)
|
|
535
309
|
fail(`${label} exceeds the 256 KiB resource limit`);
|
|
536
310
|
}
|
|
537
|
-
function validateExtraParamsValue(value, label, hooks) {
|
|
538
|
-
if (value === undefined)
|
|
539
|
-
return;
|
|
540
|
-
if (!isRecord(value))
|
|
541
|
-
fail(`${label} must be an object`);
|
|
542
|
-
if (jsonBytes(value) > WORKFLOW_MAX_EXTRA_PARAMS_BYTES)
|
|
543
|
-
fail(`${label} exceeds the 64 KiB resource limit`);
|
|
544
|
-
const issue = validateExtraParams(value)[0];
|
|
545
|
-
if (issue)
|
|
546
|
-
fail(formatExtraParamsIssue(label, issue));
|
|
547
|
-
const policyError = hooks.validateExtraParams?.(value, label);
|
|
548
|
-
if (policyError)
|
|
549
|
-
fail(`${label}: ${policyError}`);
|
|
550
|
-
}
|
|
551
311
|
function validateStepExpressions(step, index, steps) {
|
|
552
312
|
const validateReference = (text, label, paramsAllowed) => {
|
|
553
313
|
const parsed = parseReference(text);
|
|
@@ -594,29 +354,10 @@ function validateStringArray(value, label, max, unique) {
|
|
|
594
354
|
(unique && new Set(value).size !== value.length))
|
|
595
355
|
fail(`${label} is invalid`);
|
|
596
356
|
}
|
|
597
|
-
function validateOptionalFiniteNumber(value, label) {
|
|
598
|
-
if (value !== undefined && (typeof value !== "number" || !Number.isFinite(value)))
|
|
599
|
-
fail(`${label} must be finite`);
|
|
600
|
-
}
|
|
601
357
|
function validateOptionalPositiveInteger(value, label) {
|
|
602
358
|
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
|
|
603
359
|
fail(`${label} must be a positive safe integer`);
|
|
604
360
|
}
|
|
605
|
-
function validateEndpoint(value, engine) {
|
|
606
|
-
try {
|
|
607
|
-
const url = new URL(value);
|
|
608
|
-
if ((url.protocol !== "http:" && url.protocol !== "https:") ||
|
|
609
|
-
url.username ||
|
|
610
|
-
url.password ||
|
|
611
|
-
url.search ||
|
|
612
|
-
url.hash ||
|
|
613
|
-
!url.pathname.endsWith("/chat/completions"))
|
|
614
|
-
fail(`LLM engine ${engine} endpoint is not canonical`);
|
|
615
|
-
}
|
|
616
|
-
catch {
|
|
617
|
-
fail(`LLM engine ${engine} endpoint is invalid`);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
361
|
function assertString(value, label) {
|
|
621
362
|
if (typeof value !== "string" || value.length === 0 || value.length > MAX_STRING_LENGTH)
|
|
622
363
|
fail(`${label} must be a non-empty bounded string`);
|