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
|
@@ -71,9 +71,10 @@ function valueLooksSecret(value) {
|
|
|
71
71
|
return true;
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
|
-
const MOVE_TO_ENV = "Workflow params are copied verbatim into every unit
|
|
75
|
-
"
|
|
76
|
-
"
|
|
74
|
+
const MOVE_TO_ENV = "Workflow params are copied verbatim into every native unit execution context and returned in `akm workflow run` and " +
|
|
75
|
+
"`akm workflow status` output (they are part of the unit input hash and CANNOT be redacted) — move " +
|
|
76
|
+
"secrets to an env binding (`env:` ref), whose value native execution resolves only at dispatch instead of storing " +
|
|
77
|
+
"it as a run param.";
|
|
77
78
|
/**
|
|
78
79
|
* Scan run params for secret-shaped values. Returns human-readable WARNING
|
|
79
80
|
* strings (best-effort; never throws, never blocks). Recurses into nested
|
|
@@ -69,11 +69,11 @@ import { disposeDispatchResources } from "../../integrations/agent/runner-dispat
|
|
|
69
69
|
import { withWorkflowRunsConnection, withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
70
70
|
import { assertRunParamsSatisfyPlan } from "../ir/params.js";
|
|
71
71
|
import { computePlanHash } from "../ir/plan-hash.js";
|
|
72
|
-
import {
|
|
72
|
+
import { decodeWorkflowPlanV4 } from "../ir/schema-v4.js";
|
|
73
73
|
import { requireExecutableWorkflowPlan } from "../runtime/plan-classifier.js";
|
|
74
74
|
import { completeWorkflowStep, getNextWorkflowStep, resumeWorkflowRun } from "../runtime/runs.js";
|
|
75
|
-
import { GATE_EVALUATION_PHASE } from "../runtime/unit-phases.js";
|
|
76
75
|
import { frozenSummaryJudge } from "./frozen-judge.js";
|
|
76
|
+
import { mergeLoweringNotices } from "./lowering-notices.js";
|
|
77
77
|
import { defaultUnitDispatcher, executeStepPlan, } from "./native-executor.js";
|
|
78
78
|
// Shared step semantics — route evaluation + cascaded-skip bookkeeping,
|
|
79
79
|
// gate-evaluation journaling, and the whole step-completion path
|
|
@@ -103,7 +103,8 @@ export async function runWorkflowSteps(options) {
|
|
|
103
103
|
}, liveEvidence);
|
|
104
104
|
executed.push(...result.executed);
|
|
105
105
|
stepsProcessed += result.stepsProcessed;
|
|
106
|
-
const
|
|
106
|
+
const notices = mergeLoweringNotices(...executed.map((step) => step.notices));
|
|
107
|
+
const aggregate = { ...result, executed, stepsProcessed, ...(notices ? { notices } : {}) };
|
|
107
108
|
if (result.run.status !== "failed" || result.aborted || result.gateRejection || remainingRetries <= 0) {
|
|
108
109
|
return aggregate;
|
|
109
110
|
}
|
|
@@ -128,7 +129,7 @@ async function runWorkflowAttempt(options, liveEvidence) {
|
|
|
128
129
|
});
|
|
129
130
|
// Version/canonical/hash validation precedes every executable mutation,
|
|
130
131
|
// including lease acquisition. Historical rows remain inspectable/abandonable.
|
|
131
|
-
if (!next.done
|
|
132
|
+
if (!next.done) {
|
|
132
133
|
await withWorkflowRunsRepo((repo) => {
|
|
133
134
|
const row = repo.getRunById(next.run.id);
|
|
134
135
|
if (!row)
|
|
@@ -367,22 +368,20 @@ async function completedRunResult(runId) {
|
|
|
367
368
|
...(doneState.run.status === "completed" ? { done: true } : {}),
|
|
368
369
|
};
|
|
369
370
|
}
|
|
370
|
-
function workflowSummaryJudge(options,
|
|
371
|
+
function workflowSummaryJudge(options, stepPlan, signal, owner) {
|
|
371
372
|
if (options.summaryJudge !== undefined)
|
|
372
373
|
return options.summaryJudge;
|
|
373
374
|
// The judge dispatches under the REAL run/step identity; the per-loop gate row
|
|
374
375
|
// identity is threaded in per call by the completion path that journals it.
|
|
375
|
-
return frozenSummaryJudge(
|
|
376
|
+
return frozenSummaryJudge(stepPlan.gate.frozenJudge, signal, options.dispatcher ?? defaultUnitDispatcher, owner);
|
|
376
377
|
}
|
|
377
378
|
/**
|
|
378
379
|
* Seed the lifetime unit cap AND the budget ceilings from the journal so
|
|
379
380
|
* both are truly per-RUN: a resumed or re-invoked run must not restart the
|
|
380
|
-
* runaway backstop — or a declared `budget` — at zero.
|
|
381
|
-
*
|
|
382
|
-
* `
|
|
383
|
-
* `budget.max_tokens
|
|
384
|
-
* (durable-row reuses are free), so a large partially-completed fan-out
|
|
385
|
-
* stays resumable.
|
|
381
|
+
* runaway backstop — or a declared `budget` — at zero. The append-only attempt
|
|
382
|
+
* journal is authoritative: dispatch attempts count against
|
|
383
|
+
* `budget.max_units`, and their known tokens count against
|
|
384
|
+
* `budget.max_tokens`. Durable result reuse is free.
|
|
386
385
|
*
|
|
387
386
|
* Gate-evaluation rows (`phase = "gate"`, journaled by the completion-gate
|
|
388
387
|
* judge) are EXCLUDED from the seed: the live path never consumes
|
|
@@ -392,20 +391,14 @@ function workflowSummaryJudge(options, plan, stepPlan, signal, owner) {
|
|
|
392
391
|
* that `on_error` cannot soften. The seed must reproduce exactly what live
|
|
393
392
|
* accounting would have accumulated.
|
|
394
393
|
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
* row that resume re-dispatches under the SAME content-derived unit_id, and
|
|
398
|
-
* `insertUnit` REPLACES that one row while bumping `attempts`. Counting rows
|
|
399
|
-
* would erase every prior crash-retried dispatch from budget/lifetime
|
|
400
|
-
* accounting, letting the run spend past its declared ceiling; summing
|
|
401
|
-
* `attempts` charges each dispatch exactly once.
|
|
394
|
+
* Attempt rows are append-only, so retries cannot collapse or erase prior
|
|
395
|
+
* dispatch accounting.
|
|
402
396
|
*/
|
|
403
397
|
async function seedRunAccountingFromJournal(runId) {
|
|
404
|
-
const
|
|
405
|
-
const journaledDispatches = journaledUnits.filter((row) => row.phase !== GATE_EVALUATION_PHASE);
|
|
398
|
+
const accounting = await withWorkflowRunsRepo((repo) => repo.getAttemptAccounting(runId));
|
|
406
399
|
return {
|
|
407
|
-
unitsDispatched:
|
|
408
|
-
tokensUsed:
|
|
400
|
+
unitsDispatched: accounting.dispatchAttempts,
|
|
401
|
+
tokensUsed: accounting.dispatchTokens,
|
|
409
402
|
};
|
|
410
403
|
}
|
|
411
404
|
/**
|
|
@@ -418,14 +411,14 @@ async function seedRunAccountingFromJournal(runId) {
|
|
|
418
411
|
* unit is dispatched (start already validated the params it stored).
|
|
419
412
|
*/
|
|
420
413
|
async function loadAuthoritativeRunPlan(options, next) {
|
|
421
|
-
const
|
|
414
|
+
const stored = await loadStoredPlan(next.run.id);
|
|
422
415
|
if (options.loadPlan) {
|
|
423
|
-
const expected =
|
|
424
|
-
if (computePlanHash(expected) !== computePlanHash(
|
|
416
|
+
const expected = decodeWorkflowPlanV4(await options.loadPlan(next.run.workflowRef));
|
|
417
|
+
if (computePlanHash(expected) !== computePlanHash(stored))
|
|
425
418
|
throw new UsageError(`Injected workflow plan for run ${next.run.id} differs from its frozen plan.`);
|
|
426
419
|
}
|
|
427
|
-
assertRunParamsSatisfyPlan(next.run.id,
|
|
428
|
-
return
|
|
420
|
+
assertRunParamsSatisfyPlan(next.run.id, stored, next.run.params ?? {});
|
|
421
|
+
return stored;
|
|
429
422
|
}
|
|
430
423
|
/**
|
|
431
424
|
* Complete a branch target no completed router selected as `skipped` — no
|
|
@@ -515,7 +508,6 @@ async function executeStepSubgraph(ctx, loop) {
|
|
|
515
508
|
// Budget ceilings ride the FROZEN plan (addendum R2): a mid-run
|
|
516
509
|
// asset edit can never loosen or tighten a run's budget.
|
|
517
510
|
...(plan.budget ? { budget: plan.budget } : {}),
|
|
518
|
-
...(plan.execution ? { engines: plan.execution.engines } : {}),
|
|
519
511
|
gateLoop,
|
|
520
512
|
...(gateFeedback ? { gateFeedback } : {}),
|
|
521
513
|
// The heartbeat's signal is the effective dispatch signal: a lost
|
|
@@ -571,6 +563,7 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
571
563
|
unitCount: result.units.length,
|
|
572
564
|
failedUnits: result.units.filter((u) => !u.ok).length,
|
|
573
565
|
summary: result.summary,
|
|
566
|
+
...(result.notices ? { notices: result.notices } : {}),
|
|
574
567
|
});
|
|
575
568
|
// Route evaluation + artifact-judged completion gate + gate-row
|
|
576
569
|
// journaling + the bounded-loop rejection contract are the SHARED
|
|
@@ -594,7 +587,6 @@ async function runStepGateLoop(ctx, gate, totals) {
|
|
|
594
587
|
routeSelected,
|
|
595
588
|
routeUnselected,
|
|
596
589
|
summaryJudge,
|
|
597
|
-
...(ctx.plan.execution ? { engines: ctx.plan.execution.engines } : {}),
|
|
598
590
|
signal: options.signal,
|
|
599
591
|
// The judge runs under the DISPATCH signal, so the completion path must
|
|
600
592
|
// see it too: an abort delivered there (a lost lease, a caller Ctrl-C)
|
|
@@ -783,7 +775,7 @@ liveEvidence) {
|
|
|
783
775
|
// verify. No gate loop is consumed and nothing is dispatched.
|
|
784
776
|
let summaryJudge;
|
|
785
777
|
try {
|
|
786
|
-
summaryJudge = workflowSummaryJudge(options,
|
|
778
|
+
summaryJudge = workflowSummaryJudge(options, stepPlan, dispatchSignal, {
|
|
787
779
|
runId: next.run.id,
|
|
788
780
|
stepId: step.id,
|
|
789
781
|
});
|
|
@@ -838,10 +830,12 @@ liveEvidence) {
|
|
|
838
830
|
}
|
|
839
831
|
// Re-read for the freshest run state (the loop may have exited on maxSteps).
|
|
840
832
|
const finalState = await getNextWorkflowStep(next.run.id);
|
|
833
|
+
const notices = mergeLoweringNotices(...executed.map((step) => step.notices));
|
|
841
834
|
return {
|
|
842
835
|
run: finalState.run,
|
|
843
836
|
executed,
|
|
844
837
|
stepsProcessed,
|
|
838
|
+
...(notices ? { notices } : {}),
|
|
845
839
|
...(finalState.run.status === "completed" ? { done: true } : {}),
|
|
846
840
|
...(gateRejection ? { gateRejection } : {}),
|
|
847
841
|
...(judgeFailure ? { judgeFailure } : {}),
|
|
@@ -858,7 +852,7 @@ liveEvidence) {
|
|
|
858
852
|
* Missing and non-current plans fail validation and are never rebuilt from a
|
|
859
853
|
* mutable source asset.
|
|
860
854
|
*/
|
|
861
|
-
async function
|
|
855
|
+
async function loadStoredPlan(runId) {
|
|
862
856
|
const row = await withWorkflowRunsRepo((repo) => {
|
|
863
857
|
const run = repo.getRunById(runId);
|
|
864
858
|
return run;
|
|
@@ -38,14 +38,12 @@
|
|
|
38
38
|
* the executor's job (`native-executor.ts`), advancing the gated spine is the
|
|
39
39
|
* engine loop's job (`run-workflow.ts` via `completeWorkflowStep`).
|
|
40
40
|
*/
|
|
41
|
-
import { createHash } from "node:crypto";
|
|
41
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
42
42
|
import unitPreambleTemplate from "../../assets/prompts/workflow-unit-preamble.md" with { type: "text" };
|
|
43
43
|
import { UsageError } from "../../core/errors.js";
|
|
44
|
-
import { appendEvent } from "../../core/events.js";
|
|
45
44
|
import { validateJsonSchemaSubset } from "../../core/json-schema.js";
|
|
46
|
-
import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
45
|
+
import { withWorkflowRunsRepo, } from "../../storage/repositories/workflow-runs-repository.js";
|
|
47
46
|
import { canonicalJson as canonicalJsonString } from "../ir/plan-hash.js";
|
|
48
|
-
import { engineRuntimeKind } from "../ir/schema.js";
|
|
49
47
|
import { parseReference, resolveReferenceString, } from "../program/expressions.js";
|
|
50
48
|
import { clip, WORKFLOW_MAX_MAP_EXPANSION, WORKFLOW_UNIT_DIAGNOSTIC_CLIP } from "../resource-limits.js";
|
|
51
49
|
import { completeWorkflowStep, isTruncatedEvidence, } from "../runtime/runs.js";
|
|
@@ -67,7 +65,7 @@ const GATE_ARTIFACT_CLIP = 4_000;
|
|
|
67
65
|
*
|
|
68
66
|
* Whole-list failures (missing subgraph, unresolvable / non-array `over`,
|
|
69
67
|
* null or duplicate fan-out items) return `{ ok: false }`. Per-unit resolution
|
|
70
|
-
* cannot fail in the
|
|
68
|
+
* cannot fail in the shared source IR — prose is never scanned for references,
|
|
71
69
|
* and everything that CAN fail (map.over / route.input / inputs:) resolves
|
|
72
70
|
* once per step, failing the whole list above.
|
|
73
71
|
*/
|
|
@@ -119,8 +117,13 @@ function validateFanOutItems(stepId, items) {
|
|
|
119
117
|
* goes through here.
|
|
120
118
|
*/
|
|
121
119
|
function resolveStepReference(reference, scope) {
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
// Source adapters may retain GitHub's whole-value `${{ ... }}` spelling.
|
|
121
|
+
// The source IR owns GitHub's whole-value spelling; this work-list seam
|
|
122
|
+
// unwraps only an exact whole-value wrapper and never interpolates prose.
|
|
123
|
+
const exactWrapper = /^\$\{\{\s*([^{}]+?)\s*\}\}$/.exec(reference);
|
|
124
|
+
const canonicalReference = exactWrapper?.[1] ?? reference;
|
|
125
|
+
const resolved = resolveReferenceString(canonicalReference, scope);
|
|
126
|
+
const truncated = truncatedReferenceTarget(canonicalReference, scope, resolved);
|
|
124
127
|
if (!truncated)
|
|
125
128
|
return resolved;
|
|
126
129
|
return {
|
|
@@ -212,22 +215,11 @@ export function computeStepWorkList(plan, input) {
|
|
|
212
215
|
return { ok: false, error: fanOutProblem };
|
|
213
216
|
// Content-derived unit identity: compute every id up front (duplicate items
|
|
214
217
|
// were rejected above — identity requires distinct items).
|
|
215
|
-
const unitIds = items.map((item) => unitIdFor(template.id, item, isFanOut));
|
|
218
|
+
const unitIds = items.map((item) => unitIdFor(template.id, item, isFanOut, true));
|
|
216
219
|
const gateLoop = input.gateLoop ?? 1;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// journaling, retry, budget — is shared; only the dispatch mechanism differs.
|
|
221
|
-
const frozenExec = template.exec;
|
|
222
|
-
const frozenInvocation = template.invocation;
|
|
223
|
-
if (!frozenExec && !frozenInvocation)
|
|
224
|
-
return { ok: false, error: `Step "${plan.stepId}" has no frozen invocation.` };
|
|
225
|
-
const frozenEngine = frozenInvocation ? input.engines?.[frozenInvocation.engine] : undefined;
|
|
226
|
-
if (frozenInvocation && !frozenEngine) {
|
|
227
|
-
return { ok: false, error: `Step "${plan.stepId}" references missing frozen engine "${frozenInvocation.engine}".` };
|
|
228
|
-
}
|
|
229
|
-
// No frozen engine at all means an exec unit: it carries argv, not an invocation.
|
|
230
|
-
const runner = frozenEngine ? engineRuntimeKind(frozenEngine) : "exec";
|
|
220
|
+
const target = template.frozenTarget;
|
|
221
|
+
const frozenExec = target.kind === "shell" || target.kind === "script" ? target.exec : undefined;
|
|
222
|
+
const runner = target.kind === "command" ? target.runner.kind : "exec";
|
|
231
223
|
// Taken VERBATIM from the frozen plan — there is no engine-side backstop, by
|
|
232
224
|
// design. The whole timeout decision happens once at freeze time
|
|
233
225
|
// (`ir/freeze.ts` `effectiveTimeout`: unit `timeout:` → document
|
|
@@ -243,7 +235,7 @@ export function computeStepWorkList(plan, input) {
|
|
|
243
235
|
// An exec unit's budget is frozen on its exec spec (there is no engine to
|
|
244
236
|
// inherit one from); `ir/freeze.ts` resolved it once from unit `timeout:` →
|
|
245
237
|
// `defaults.timeout` → DEFAULT_EXEC_TIMEOUT_MS.
|
|
246
|
-
const timeoutMs =
|
|
238
|
+
const timeoutMs = target.kind === "command" ? (target.runner.timeoutMs ?? null) : target.exec.timeoutMs;
|
|
247
239
|
// Step-constant exec context: `AKM_PARAMS` / `AKM_INPUTS` depend only on
|
|
248
240
|
// step-level values, so they are serialized ONCE here and shared by every
|
|
249
241
|
// unit. Building them inside the per-unit loop deep-cloned and re-stringified
|
|
@@ -262,8 +254,7 @@ export function computeStepWorkList(plan, input) {
|
|
|
262
254
|
resolvedInputs,
|
|
263
255
|
runner,
|
|
264
256
|
timeoutMs,
|
|
265
|
-
|
|
266
|
-
...(frozenInvocation ? { frozenInvocation } : {}),
|
|
257
|
+
target,
|
|
267
258
|
...(frozenExec ? { frozenExec } : {}),
|
|
268
259
|
...(execParamsJson !== undefined ? { execParamsJson } : {}),
|
|
269
260
|
...(execInputsJson !== undefined ? { execInputsJson } : {}),
|
|
@@ -286,7 +277,7 @@ export function computeStepWorkList(plan, input) {
|
|
|
286
277
|
* repo's 220-line function bar.
|
|
287
278
|
*/
|
|
288
279
|
function buildStepWorkUnit(ctx, unitId, item, index) {
|
|
289
|
-
const { plan, input, template, isFanOut, resolvedInputs,
|
|
280
|
+
const { plan, input, template, isFanOut, resolvedInputs, target, frozenExec } = ctx;
|
|
290
281
|
// Gate loops (>= 2) journal under `<unitId>~l<loop>` so loop 1's rows are
|
|
291
282
|
// never clobbered; the content-derived identity (and the prompt's
|
|
292
283
|
// {{UNIT_ID}}) stays the base id.
|
|
@@ -324,21 +315,12 @@ function buildStepWorkUnit(ctx, unitId, item, index) {
|
|
|
324
315
|
isFanOut,
|
|
325
316
|
journalBaseId,
|
|
326
317
|
runner: ctx.runner,
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
frozenEngine.fallbackLlmEngine &&
|
|
330
|
-
input.engines?.[frozenEngine.fallbackLlmEngine]?.kind === "llm"
|
|
331
|
-
? {
|
|
332
|
-
fallbackEngine: input.engines[frozenEngine.fallbackLlmEngine],
|
|
333
|
-
}
|
|
334
|
-
: {}),
|
|
335
|
-
...(frozenInvocation ? { invocation: frozenInvocation } : {}),
|
|
336
|
-
...(frozenExec ? { exec: frozenExec } : {}),
|
|
318
|
+
frozenTarget: target,
|
|
319
|
+
environment: template.environment,
|
|
337
320
|
...(frozenExec ? { execContext: buildExecContextEnv({ ctx, unitId, item, index }) } : {}),
|
|
338
|
-
...(
|
|
321
|
+
...(target.kind === "command" && target.request.model?.resolved ? { model: target.request.model.resolved } : {}),
|
|
339
322
|
timeoutMs: ctx.timeoutMs,
|
|
340
323
|
...(template.schema ? { schema: template.schema } : {}),
|
|
341
|
-
...(template.env ? { env: template.env } : {}),
|
|
342
324
|
...(template.retry ? { retry: template.retry } : {}),
|
|
343
325
|
onError: template.onError,
|
|
344
326
|
...(template.isolation ? { isolation: template.isolation } : {}),
|
|
@@ -349,7 +331,7 @@ function buildStepWorkUnit(ctx, unitId, item, index) {
|
|
|
349
331
|
/**
|
|
350
332
|
* The `AKM_*` context environment an exec unit's child receives.
|
|
351
333
|
*
|
|
352
|
-
* An exec unit's argv is FROZEN and never interpolated (the
|
|
334
|
+
* An exec unit's argv is FROZEN and never interpolated (the shared source IR has
|
|
353
335
|
* no substitution language at all), so this is how a fan-out item, the run
|
|
354
336
|
* params, and the step's declared `inputs:` artifacts actually reach a command
|
|
355
337
|
* — as attached environment, exactly as they reach an engine unit as attached
|
|
@@ -405,8 +387,8 @@ function buildExecContextEnv(args) {
|
|
|
405
387
|
* declared-input artifacts + the params snapshot — instead of a
|
|
406
388
|
* resolved/spliced prompt string, since there is no more splicing.
|
|
407
389
|
*
|
|
408
|
-
* Included beyond the
|
|
409
|
-
* timeoutMs
|
|
390
|
+
* Included beyond the template/runner/model/schema baseline: resolved
|
|
391
|
+
* timeoutMs, named environment bindings, and isolation —
|
|
410
392
|
* each reaches dispatch and a changed one yields a materially different call.
|
|
411
393
|
* `env` carries NAMES ONLY, never resolved values: hashing a resolved secret
|
|
412
394
|
* would leak it into a durable hash oracle and would spuriously re-dispatch on
|
|
@@ -421,33 +403,30 @@ function buildExecContextEnv(args) {
|
|
|
421
403
|
* the rejected attempt. Replay-safe: feedback is re-derived from the journaled
|
|
422
404
|
* gate decision, so a resumed retry re-hashes identically.
|
|
423
405
|
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* previously-frozen llm/agent/sdk unit therefore hashes byte-identically to
|
|
429
|
-
* before, and no in-flight run re-dispatches work it already completed.
|
|
406
|
+
* Command targets carry their frozen argv/script/cwd/timeout identity in the
|
|
407
|
+
* same target slot used by agent, SDK, and direct-LLM work. The complete
|
|
408
|
+
* current target is hashed once, so a completed unit is reused only for the
|
|
409
|
+
* exact durable request that originally produced it.
|
|
430
410
|
*
|
|
431
|
-
* Ambient config is
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
* boundary (redesign addendum determinism bar #2): config drift under an
|
|
435
|
-
* in-flight run is out of scope by design.
|
|
411
|
+
* Ambient config is deliberately excluded because it is not consulted during
|
|
412
|
+
* execution. The frozen target and named environment bindings are the runtime
|
|
413
|
+
* identity boundary; only the values behind those names remain live.
|
|
436
414
|
*/
|
|
437
415
|
function computeUnitInputHash(ctx, item) {
|
|
438
|
-
const dispatch = ctx.frozenEngine ? transitiveDispatchSnapshot(ctx.frozenEngine, ctx.input.engines ?? {}) : null;
|
|
439
416
|
return createHash("sha256")
|
|
417
|
+
.update("akm.workflow.unit\0v5\0")
|
|
440
418
|
.update(canonicalJsonString({
|
|
441
|
-
hashVersion:
|
|
419
|
+
hashVersion: 5,
|
|
420
|
+
role: "unit",
|
|
421
|
+
stepId: ctx.plan.stepId,
|
|
422
|
+
nodeId: ctx.template.id,
|
|
442
423
|
template: ctx.template.instructions,
|
|
443
424
|
item: ctx.isFanOut ? (item ?? null) : null,
|
|
444
425
|
inputs: ctx.resolvedInputs,
|
|
445
426
|
params: ctx.input.params,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
...(ctx.frozenExec ? { exec: ctx.frozenExec } : {}),
|
|
427
|
+
frozenTarget: ctx.target,
|
|
428
|
+
environment: ctx.template.environment,
|
|
449
429
|
schema: ctx.template.schema ?? null,
|
|
450
|
-
env: ctx.template.env ?? null,
|
|
451
430
|
isolation: ctx.template.isolation ?? "none",
|
|
452
431
|
...(ctx.input.gateFeedback ? { gateFeedback: ctx.input.gateFeedback } : {}),
|
|
453
432
|
}))
|
|
@@ -498,27 +477,18 @@ export function buildUnitPrompt(input) {
|
|
|
498
477
|
return `${preamble}\n${instructions}${itemBlock}${inputsBlock}${gateBlock}${schemaDirective}`;
|
|
499
478
|
}
|
|
500
479
|
/**
|
|
501
|
-
* Content-derived unit identity (module doc): `<node_id>:<
|
|
480
|
+
* Content-derived unit identity (module doc): `<node_id>:<sha256>` for a
|
|
502
481
|
* fan-out item, `<node_id>:solo` otherwise. The hash is over the item's
|
|
503
482
|
* canonical JSON (sorted keys — same canonicalization the vote reducer
|
|
504
483
|
* counts with), so identity survives list reordering/regeneration and is
|
|
505
484
|
* independent of item position. Retry attempts stack `~r<n>` on top.
|
|
506
485
|
*/
|
|
507
|
-
export function unitIdFor(nodeId, item, isFanOut) {
|
|
486
|
+
export function unitIdFor(nodeId, item, isFanOut, collisionSafe = false) {
|
|
508
487
|
if (!isFanOut)
|
|
509
488
|
return `${nodeId}:solo`;
|
|
510
489
|
const canonical = canonicalJson(item) ?? "null";
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
/** Include an SDK fallback in v3 call identity without copying catalog entries onto nodes. */
|
|
514
|
-
function transitiveDispatchSnapshot(engine, engines) {
|
|
515
|
-
if (engine.kind !== "agent" || !engine.fallbackLlmEngine)
|
|
516
|
-
return engine;
|
|
517
|
-
const fallback = engines[engine.fallbackLlmEngine];
|
|
518
|
-
if (!fallback || fallback.kind !== "llm") {
|
|
519
|
-
throw new UsageError(`Frozen agent engine "${engine.name}" has no valid LLM fallback snapshot.`);
|
|
520
|
-
}
|
|
521
|
-
return { engine, fallback };
|
|
490
|
+
const digest = createHash("sha256").update(canonical).digest("hex");
|
|
491
|
+
return `${nodeId}:${collisionSafe ? digest : digest.slice(0, 12)}`;
|
|
522
492
|
}
|
|
523
493
|
// ── Step outputs + reducers + typed artifacts ────────────────────────────────
|
|
524
494
|
/**
|
|
@@ -856,7 +826,8 @@ export function gateUnitId(stepId, loop) {
|
|
|
856
826
|
*/
|
|
857
827
|
export function effectiveGateMaxLoops(stepPlan) {
|
|
858
828
|
const declared = Math.max(1, stepPlan.gate.maxLoops ?? 1);
|
|
859
|
-
|
|
829
|
+
const target = stepTemplate(stepPlan)?.frozenTarget;
|
|
830
|
+
return target && target.kind !== "command" ? 1 : declared;
|
|
860
831
|
}
|
|
861
832
|
/**
|
|
862
833
|
* The gate loop the engine is about to (re-)run for an ACTIVE step, derived
|
|
@@ -958,26 +929,27 @@ function gateCorruptionMessage(row, why) {
|
|
|
958
929
|
/** Insert the gate-evaluation unit row (running) just before the judge runs. */
|
|
959
930
|
export async function journalGateEvaluationStart(gate) {
|
|
960
931
|
const unitId = gateUnitId(gate.stepId, gate.loop);
|
|
961
|
-
|
|
932
|
+
const now = new Date().toISOString();
|
|
933
|
+
const claimHolder = gate.claimHolder ?? `direct:${randomUUID()}`;
|
|
934
|
+
const reserved = await enqueueUnitWrite(() => withWorkflowRunsRepo((repo) => repo.reserveUnitAttempt({
|
|
962
935
|
runId: gate.runId,
|
|
963
936
|
unitId,
|
|
964
937
|
stepId: gate.stepId,
|
|
965
938
|
nodeId: gateNodeId(gate.stepId),
|
|
966
|
-
parentUnitId: null,
|
|
967
|
-
// Marks the row as a judge call, NOT a dispatch: the budget/lifetime
|
|
968
|
-
// seed in `driveRun` skips these so resume accounting matches live.
|
|
969
939
|
phase: GATE_EVALUATION_PHASE,
|
|
970
940
|
runner: gate.runner,
|
|
971
|
-
engine: gate.
|
|
972
|
-
model: gate.
|
|
941
|
+
engine: gate.engine,
|
|
942
|
+
model: gate.model,
|
|
973
943
|
inputHash: gate.inputHash,
|
|
974
|
-
|
|
944
|
+
claimHolder,
|
|
945
|
+
claimExpiresAt: new Date(Date.parse(now) + 90_000).toISOString(),
|
|
946
|
+
now,
|
|
947
|
+
leaseMode: gate.claimHolder === undefined ? "direct" : "engine",
|
|
975
948
|
})));
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
});
|
|
949
|
+
if (reserved.kind === "busy") {
|
|
950
|
+
throw new UsageError(`Gate ${unitId} has a live durable attempt held by another engine.`);
|
|
951
|
+
}
|
|
952
|
+
return { ...gate, claimHolder, durableAttempt: reserved.attempt };
|
|
981
953
|
}
|
|
982
954
|
/**
|
|
983
955
|
* Finish the gate-evaluation unit row with the verdict as observed from the
|
|
@@ -997,20 +969,26 @@ export async function journalGateEvaluationFinish(gate, errored, rejection) {
|
|
|
997
969
|
? { complete: false, missing: rejection.missing, feedback: rejection.feedback }
|
|
998
970
|
: { complete: true, missing: [] };
|
|
999
971
|
const status = errored ? "failed" : "completed";
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
972
|
+
if (!gate.durableAttempt)
|
|
973
|
+
throw new UsageError(`Gate ${unitId} has no durable attempt to finish.`);
|
|
974
|
+
const durableAttempt = gate.durableAttempt;
|
|
975
|
+
const finished = await enqueueUnitWrite(() => withWorkflowRunsRepo((repo) => {
|
|
976
|
+
return repo.finishUnitAttempt({
|
|
977
|
+
runId: gate.runId,
|
|
978
|
+
unitId,
|
|
979
|
+
attempt: durableAttempt.attempt,
|
|
980
|
+
dispatchId: durableAttempt.dispatch_id,
|
|
981
|
+
claimHolder: durableAttempt.claim_holder,
|
|
982
|
+
status,
|
|
983
|
+
resultJson: verdict ? JSON.stringify(verdict) : null,
|
|
984
|
+
tokens: gate.tokens ?? null,
|
|
985
|
+
failureReason: errored ? "dispatch_error" : null,
|
|
986
|
+
finishedAt: new Date().toISOString(),
|
|
987
|
+
});
|
|
988
|
+
}));
|
|
989
|
+
if (!finished) {
|
|
990
|
+
throw new UsageError(`Gate ${unitId} no longer owns its durable attempt; refusing a late terminal write.`);
|
|
991
|
+
}
|
|
1014
992
|
}
|
|
1015
993
|
/**
|
|
1016
994
|
* Resolve a route's input (a single whole-value reference string — `params.x` or
|
|
@@ -1290,8 +1268,7 @@ export async function finalizeExecutedStep(input) {
|
|
|
1290
1268
|
// Journal engine-driven judge calls as unit rows. With no criteria there is
|
|
1291
1269
|
// no judge invocation or row; a criteria-bearing plan without a judge is a
|
|
1292
1270
|
// configuration error rather than a silent bypass.
|
|
1293
|
-
const
|
|
1294
|
-
const gateEngine = gateInvocation ? (input.engines?.[gateInvocation.engine] ?? null) : null;
|
|
1271
|
+
const gateTarget = innerJudge ? stepPlan.gate.frozenJudge : null;
|
|
1295
1272
|
let gateUnit;
|
|
1296
1273
|
// `judgeFailure` records a verifier INFRASTRUCTURE failure observed during
|
|
1297
1274
|
// the judge call — a throw (transport/service error) or a response that is
|
|
@@ -1301,32 +1278,51 @@ export async function finalizeExecutedStep(input) {
|
|
|
1301
1278
|
let judgeFailure;
|
|
1302
1279
|
const summaryJudge = innerJudge
|
|
1303
1280
|
? async (prompt) => {
|
|
1304
|
-
if (
|
|
1281
|
+
if (gateTarget) {
|
|
1282
|
+
const engineName = gateTarget.request.engine.name;
|
|
1283
|
+
if (!engineName)
|
|
1284
|
+
throw new Error(`Gate ${stepId} has no frozen engine identity.`);
|
|
1305
1285
|
gateUnit = {
|
|
1306
1286
|
runId,
|
|
1307
1287
|
workflowRef,
|
|
1308
1288
|
stepId,
|
|
1309
1289
|
loop: gateLoop,
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
runner:
|
|
1290
|
+
engine: engineName,
|
|
1291
|
+
model: gateTarget.request.model?.resolved ?? null,
|
|
1292
|
+
runner: gateTarget.runner.kind,
|
|
1313
1293
|
inputHash: createHash("sha256")
|
|
1294
|
+
.update("akm.workflow.gate\0v5\0")
|
|
1314
1295
|
.update(canonicalJsonString({
|
|
1315
|
-
hashVersion:
|
|
1316
|
-
dispatch:
|
|
1317
|
-
invocation:
|
|
1296
|
+
hashVersion: 5,
|
|
1297
|
+
dispatch: gateTarget,
|
|
1298
|
+
invocation: null,
|
|
1318
1299
|
prompt,
|
|
1319
1300
|
}))
|
|
1320
1301
|
.digest("hex"),
|
|
1302
|
+
...(input.leaseHolder !== undefined ? { claimHolder: input.leaseHolder } : {}),
|
|
1321
1303
|
};
|
|
1322
|
-
await journalGateEvaluationStart(gateUnit);
|
|
1304
|
+
gateUnit = await journalGateEvaluationStart(gateUnit);
|
|
1323
1305
|
}
|
|
1324
1306
|
// The judge dispatch must describe the SAME thing the gate row does, so
|
|
1325
1307
|
// the row identity is threaded down to the dispatcher from right here —
|
|
1326
1308
|
// the one place that computes it — instead of being re-derived (or, as
|
|
1327
1309
|
// before, synthesized as a constant "gate"). Both ids come from the same
|
|
1328
1310
|
// helpers `journalGateEvaluationStart/Finish` use.
|
|
1329
|
-
const identity = {
|
|
1311
|
+
const identity = {
|
|
1312
|
+
runId,
|
|
1313
|
+
stepId,
|
|
1314
|
+
unitId: gateUnitId(stepId, gateLoop),
|
|
1315
|
+
...(gateUnit?.durableAttempt
|
|
1316
|
+
? {
|
|
1317
|
+
attempt: gateUnit.durableAttempt.attempt,
|
|
1318
|
+
dispatchId: gateUnit.durableAttempt.dispatch_id,
|
|
1319
|
+
recordTokens: (tokens) => {
|
|
1320
|
+
if (gateUnit)
|
|
1321
|
+
gateUnit.tokens = tokens;
|
|
1322
|
+
},
|
|
1323
|
+
}
|
|
1324
|
+
: {}),
|
|
1325
|
+
};
|
|
1330
1326
|
let raw;
|
|
1331
1327
|
try {
|
|
1332
1328
|
raw = await innerJudge(prompt, identity);
|