akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
6
6
|
import { loadConfig } from "../../core/config/config.js";
|
|
7
|
-
import { ConfigError, NotFoundError,
|
|
7
|
+
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
8
8
|
import { appendEvent } from "../../core/events.js";
|
|
9
9
|
import { warn } from "../../core/warn.js";
|
|
10
10
|
import { insertEventOnce } from "../../storage/repositories/events-repository.js";
|
|
@@ -12,28 +12,25 @@ import { withWorkflowRunsRepo, } from "../../storage/repositories/workflow-runs-
|
|
|
12
12
|
import { getCurrentWorkflowScopeKey } from "../authoring/scope-key.js";
|
|
13
13
|
import { frozenSummaryJudge } from "../exec/frozen-judge.js";
|
|
14
14
|
import { detectSecretShapedParams } from "../exec/param-secrets.js";
|
|
15
|
-
import {
|
|
16
|
-
import { compileResolveFreezeWorkflowV4 } from "../ir/freeze-v4.js";
|
|
15
|
+
import { freezeWorkflow } from "../freeze/freeze.js";
|
|
17
16
|
import { materializeWorkflowParameterFlags, validateWorkflowParams } from "../ir/params.js";
|
|
18
17
|
import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
|
|
19
18
|
import { clip, WORKFLOW_UNIT_DIAGNOSTIC_CLIP } from "../resource-limits.js";
|
|
20
19
|
import { validateStepSummary } from "../validate-summary.js";
|
|
21
20
|
import { resolveAgentIdentity } from "./agent-identity.js";
|
|
22
|
-
import { evaluateCheckin } from "./checkin.js";
|
|
23
|
-
import { assertRunStatusMatchesSpine, classifyWorkflowRunPlan, frozenStepRows, reconcileWorkflowSpineWithPlan, requireExecutableWorkflowPlan, } from "./plan-classifier.js";
|
|
24
21
|
import { resolveWorkflowRunOutputs } from "./run-outputs.js";
|
|
25
|
-
import {
|
|
22
|
+
import { frozenStepRows, readRunPlan } from "./run-plan.js";
|
|
26
23
|
import { canonicalizeWorkflowRefInput, loadWorkflowAsset, resolveWorkflowEntryId } from "./workflow-asset-loader.js";
|
|
27
24
|
/**
|
|
28
25
|
* Membership test for the journaled `runner` column, which is an untyped string.
|
|
29
|
-
* The `Record<
|
|
26
|
+
* The `Record<WorkflowRuntimeKind, …>` is exhaustiveness-checked, so a new runtime
|
|
30
27
|
* kind cannot be added to the union without being accepted here too.
|
|
31
28
|
*/
|
|
32
29
|
const IR_RUNTIME_KINDS = { llm: true, agent: true, sdk: true, exec: true };
|
|
33
30
|
function runtimeKindOf(runner) {
|
|
34
31
|
return runner !== null && Object.hasOwn(IR_RUNTIME_KINDS, runner) ? runner : null;
|
|
35
32
|
}
|
|
36
|
-
function toUnitDiagnostic(row
|
|
33
|
+
function toUnitDiagnostic(row) {
|
|
37
34
|
let diagnostic = null;
|
|
38
35
|
if (row.result_json !== null) {
|
|
39
36
|
// `result_json` is a JSON-encoded value: a bare JSON string for a free-text
|
|
@@ -63,10 +60,6 @@ function toUnitDiagnostic(row, stale) {
|
|
|
63
60
|
diagnostic,
|
|
64
61
|
startedAt: row.started_at,
|
|
65
62
|
finishedAt: row.finished_at,
|
|
66
|
-
stale: stale !== undefined,
|
|
67
|
-
staleIdleMs: stale ? (Number.isFinite(stale.idleMs) ? stale.idleMs : null) : null,
|
|
68
|
-
claimHolder: row.claim_holder,
|
|
69
|
-
claimExpiresAt: row.claim_expires_at,
|
|
70
63
|
engine: row.engine ?? null,
|
|
71
64
|
runtimeKind: runtimeKindOf(row.runner),
|
|
72
65
|
platform: null,
|
|
@@ -74,11 +67,11 @@ function toUnitDiagnostic(row, stale) {
|
|
|
74
67
|
}
|
|
75
68
|
export async function startWorkflowRun(ref, params = {}, options) {
|
|
76
69
|
const asset = await loadWorkflowAsset(ref);
|
|
77
|
-
// Frozen plan
|
|
70
|
+
// Frozen plan: compile the plan ONCE at start and
|
|
78
71
|
// persist it on the run row in the same transaction as the insert. Every
|
|
79
72
|
// later invocation executes this snapshot — the asset file is never re-read
|
|
80
73
|
// for an in-flight run; re-planning is an explicit new run.
|
|
81
|
-
const frozen = await
|
|
74
|
+
const frozen = await freezeWorkflow(asset, loadConfig());
|
|
82
75
|
const plan = frozen.plan;
|
|
83
76
|
if (options?.parameterFlags?.length && Object.keys(params).length > 0) {
|
|
84
77
|
throw new UsageError("Workflow parameters must use either an object or per-parameter flags, not both.");
|
|
@@ -89,10 +82,10 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
89
82
|
// Non-fatal WARNINGS: untyped-step and undeclared-param advisories surface
|
|
90
83
|
// as `warn()` lines at start (stderr, consistent with the repo's other
|
|
91
84
|
// author-facing warnings) without blocking the run.
|
|
92
|
-
for (const w of
|
|
85
|
+
for (const w of frozen.warnings) {
|
|
93
86
|
warn(`workflow run: ${asset.path}:${w.line} — ${w.message}`);
|
|
94
87
|
}
|
|
95
|
-
//
|
|
88
|
+
// validate supplied parameters against the frozen param
|
|
96
89
|
// schemas BEFORE creating the run, so a type-mismatched param (e.g. a string
|
|
97
90
|
// for a `{ type: array }` param) is rejected with actionable errors instead
|
|
98
91
|
// of flowing silently into a unit prompt. Programs without declared param
|
|
@@ -121,24 +114,10 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
121
114
|
// (workflow_ref, scope_key) pair, refuse to create a parallel run unless
|
|
122
115
|
// `force: true` is set. Previously every call inserted unconditionally,
|
|
123
116
|
// so two terminals starting the same workflow could leave two runs racing.
|
|
124
|
-
// The
|
|
125
|
-
// active
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// re-targeted with a `continue` directive. The agent harness + session id
|
|
129
|
-
// are already resolved above (agentHarness/agentSessionId, from #501).
|
|
130
|
-
// #942: an active run of this ref may already exist in a DIFFERENT
|
|
131
|
-
// scope — the incident this issue reports (a scheduled task's cwd and a
|
|
132
|
-
// human's shell hash to different scope keys, so each believed it held
|
|
133
|
-
// no active run and each started one). The scope-local uniqueness guard
|
|
134
|
-
// stays scope-local (a documented, deliberate per-project partition —
|
|
135
|
-
// see storage-locations.md); this only warns, once, so the operator can
|
|
136
|
-
// resume or abandon the other run instead of silently accumulating a
|
|
137
|
-
// second one. `findActiveRunOutsideScope` excludes the caller's own
|
|
138
|
-
// scope IN SQL (never merely post-filtered) so the caller's own active
|
|
139
|
-
// run can never sort first under `LIMIT 1` and mask a genuinely different
|
|
140
|
-
// scope's run — the failure mode a same-scope-inclusive query plus a
|
|
141
|
-
// post-filter has with `--new`/`--force`.
|
|
117
|
+
// The active-alias query and all inserts share one immediate transaction.
|
|
118
|
+
// #942: an active run of this ref in a different scope (e.g. a scheduled
|
|
119
|
+
// task's cwd vs. a shell) is warned about once; the uniqueness guard stays
|
|
120
|
+
// scope-local. The query excludes the caller's own scope in SQL.
|
|
142
121
|
const crossScopeActive = repo.findActiveRunOutsideScope(workflowRefs, scopeKey);
|
|
143
122
|
const crossScopeWarning = crossScopeActive
|
|
144
123
|
? `Workflow ${asset.ref} already has an active run in another scope ` +
|
|
@@ -161,7 +140,6 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
161
140
|
updatedAt: now,
|
|
162
141
|
agentHarness,
|
|
163
142
|
agentSessionId,
|
|
164
|
-
checkinArmedAt: now,
|
|
165
143
|
},
|
|
166
144
|
steps: frozenStepRows(plan).map((step) => ({
|
|
167
145
|
runId,
|
|
@@ -173,16 +151,14 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
173
151
|
})),
|
|
174
152
|
planJson,
|
|
175
153
|
planHash,
|
|
176
|
-
revalidateSources: () => frozen.sourceCollector.revalidate(),
|
|
177
154
|
});
|
|
178
155
|
const result = await getWorkflowStatus(runId);
|
|
179
156
|
if (crossScopeWarning)
|
|
180
157
|
result.warnings = [...(result.warnings ?? []), crossScopeWarning];
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// binding. Advisory only — never blocks the start.
|
|
158
|
+
// Params reach every unit prompt in clear (only the journal scrubs them).
|
|
159
|
+
// Surface a loud, best-effort warning when a param LOOKS like a credential
|
|
160
|
+
// so the author moves it to an env binding. Advisory only — never blocks
|
|
161
|
+
// the start.
|
|
186
162
|
const secretWarnings = detectSecretShapedParams(effectiveParams);
|
|
187
163
|
if (secretWarnings.length > 0)
|
|
188
164
|
result.warnings = [...(result.warnings ?? []), ...secretWarnings];
|
|
@@ -203,12 +179,7 @@ export async function getWorkflowStatus(runId, opts) {
|
|
|
203
179
|
// The honest diagnostic surface (#22): read the unit journal straight and
|
|
204
180
|
// project each row, INCLUDING failures whose diagnostic text the
|
|
205
181
|
// deterministic evidence graph drops. Read-only; never mutates the run.
|
|
206
|
-
|
|
207
|
-
// Codex round-3 finding B: run the pure stale-claim evaluator (`now`
|
|
208
|
-
// injected for deterministic tests) so a unit left `running` by a process
|
|
209
|
-
// that died surfaces as stale here, not just as raw `running`.
|
|
210
|
-
const staleById = new Map(evaluateStaleUnits(rows, opts.now ?? Date.now()).map((u) => [u.unitId, u]));
|
|
211
|
-
detail.units = rows.map((row) => toUnitDiagnostic(row, staleById.get(row.unit_id)));
|
|
182
|
+
detail.units = repo.getUnitsForRun(run.id).map(toUnitDiagnostic);
|
|
212
183
|
}
|
|
213
184
|
return detail;
|
|
214
185
|
});
|
|
@@ -271,9 +242,6 @@ export async function getNextWorkflowStep(specifier, params, options) {
|
|
|
271
242
|
return withWorkflowRunsRepo(async (repo) => {
|
|
272
243
|
const { run, autoStarted, resumed, startWarnings } = await resolveRunSpecifier(repo, specifier, params, options?.parameterFlags, options?.newRun);
|
|
273
244
|
const steps = readWorkflowRunSteps(repo, run.id);
|
|
274
|
-
const plan = requireExecutableWorkflowPlan(run);
|
|
275
|
-
reconcileWorkflowSpineWithPlan(plan, run, steps);
|
|
276
|
-
assertRunStatusMatchesSpine(run, steps);
|
|
277
245
|
return {
|
|
278
246
|
...projectNextResult(run, steps),
|
|
279
247
|
...(autoStarted ? { autoStarted: true } : {}),
|
|
@@ -291,15 +259,6 @@ export async function getNextWorkflowStep(specifier, params, options) {
|
|
|
291
259
|
function projectNextResult(run, steps) {
|
|
292
260
|
const currentStep = resolveCurrentStep(run, steps);
|
|
293
261
|
const done = run.status === "completed" ? true : undefined;
|
|
294
|
-
// #506: surface a check-in directive through the normal command output when
|
|
295
|
-
// the run looks stalled. Pure timestamp evaluation — no background thread.
|
|
296
|
-
const checkin = evaluateCheckin({
|
|
297
|
-
status: run.status,
|
|
298
|
-
updatedAt: run.updated_at,
|
|
299
|
-
checkinArmedAt: run.checkin_armed_at,
|
|
300
|
-
agentHarness: run.agent_harness,
|
|
301
|
-
agentSessionId: run.agent_session_id,
|
|
302
|
-
}) ?? undefined;
|
|
303
262
|
return {
|
|
304
263
|
run: toWorkflowRunSummary(run),
|
|
305
264
|
workflow: {
|
|
@@ -309,20 +268,16 @@ function projectNextResult(run, steps) {
|
|
|
309
268
|
},
|
|
310
269
|
step: currentStep ? toWorkflowRunStepState(currentStep) : null,
|
|
311
270
|
...(done ? { done } : {}),
|
|
312
|
-
...(checkin ? { checkin } : {}),
|
|
313
271
|
};
|
|
314
272
|
}
|
|
315
273
|
export async function resumeWorkflowRun(runId) {
|
|
316
274
|
return withWorkflowRunsRepo((repo) => {
|
|
317
275
|
const run = readWorkflowRunOrPrefix(repo, runId);
|
|
318
|
-
const storedPlan = requireExecutableWorkflowPlan(run);
|
|
319
276
|
const steps = readWorkflowRunSteps(repo, run.id);
|
|
320
|
-
reconcileWorkflowSpineWithPlan(storedPlan, run, steps);
|
|
321
277
|
if (run.status === "completed") {
|
|
322
278
|
throw new UsageError(`Workflow run ${run.id} is already completed and cannot be resumed.`);
|
|
323
279
|
}
|
|
324
280
|
if (run.status === "active") {
|
|
325
|
-
assertRunStatusMatchesSpine(run, steps);
|
|
326
281
|
return buildWorkflowRunDetail(repo, run, steps);
|
|
327
282
|
}
|
|
328
283
|
// blocked or failed → flip back to active and re-open the current step so
|
|
@@ -335,9 +290,7 @@ export async function resumeWorkflowRun(runId) {
|
|
|
335
290
|
repo.markRunActive(run.id, now);
|
|
336
291
|
});
|
|
337
292
|
const updated = { ...run, status: "active", updated_at: now };
|
|
338
|
-
|
|
339
|
-
assertRunStatusMatchesSpine(updated, refreshedSteps);
|
|
340
|
-
return buildWorkflowRunDetail(repo, updated, refreshedSteps);
|
|
293
|
+
return buildWorkflowRunDetail(repo, updated, readWorkflowRunSteps(repo, run.id));
|
|
341
294
|
});
|
|
342
295
|
}
|
|
343
296
|
/**
|
|
@@ -372,11 +325,8 @@ export async function abandonWorkflowRun(runId) {
|
|
|
372
325
|
status: "failed",
|
|
373
326
|
updated_at: now,
|
|
374
327
|
completed_at: now,
|
|
375
|
-
checkin_armed_at: now,
|
|
376
328
|
};
|
|
377
|
-
|
|
378
|
-
const detail = buildWorkflowRunDetail(repo, updated, steps);
|
|
379
|
-
return detail;
|
|
329
|
+
return buildWorkflowRunDetail(repo, updated, readWorkflowRunSteps(repo, run.id));
|
|
380
330
|
});
|
|
381
331
|
}
|
|
382
332
|
export async function completeWorkflowStep(input) {
|
|
@@ -384,14 +334,10 @@ export async function completeWorkflowStep(input) {
|
|
|
384
334
|
// the write transaction — a slow/hung LLM must never hold a db write lock.
|
|
385
335
|
const preflight = await withWorkflowRunsRepo((repo) => {
|
|
386
336
|
const run = readWorkflowRun(repo, input.runId);
|
|
387
|
-
const storedPlan =
|
|
388
|
-
const steps = readWorkflowRunSteps(repo, run.id);
|
|
389
|
-
reconcileWorkflowSpineWithPlan(storedPlan, run, steps);
|
|
390
|
-
assertRunStatusMatchesSpine(run, steps);
|
|
337
|
+
const storedPlan = requireRunPlan(run);
|
|
391
338
|
if (run.status !== "active") {
|
|
392
339
|
throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
|
|
393
340
|
}
|
|
394
|
-
assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
|
|
395
341
|
const existing = repo.getStep(run.id, input.stepId);
|
|
396
342
|
if (!existing) {
|
|
397
343
|
throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
|
|
@@ -433,11 +379,7 @@ export async function completeWorkflowStep(input) {
|
|
|
433
379
|
}
|
|
434
380
|
const verdict = await validateStepSummary({ stepTitle: preflight.stepPlan.title, completionCriteria: criteria, summary }, judge ?? undefined, input.signal);
|
|
435
381
|
if (!verdict.complete) {
|
|
436
|
-
//
|
|
437
|
-
// step pending and return corrective feedback instead of completing.
|
|
438
|
-
await withWorkflowRunsRepo((repo) => {
|
|
439
|
-
repo.rearmCheckin(input.runId, new Date().toISOString());
|
|
440
|
-
});
|
|
382
|
+
// Leave the step pending and return corrective feedback instead of completing.
|
|
441
383
|
return {
|
|
442
384
|
ok: false,
|
|
443
385
|
runId: input.runId,
|
|
@@ -455,17 +397,10 @@ export async function completeWorkflowStep(input) {
|
|
|
455
397
|
let outputWarnings;
|
|
456
398
|
repo.transaction(() => {
|
|
457
399
|
const run = readWorkflowRun(repo, input.runId);
|
|
458
|
-
const plan =
|
|
459
|
-
const spine = readWorkflowRunSteps(repo, run.id);
|
|
460
|
-
reconcileWorkflowSpineWithPlan(plan, run, spine);
|
|
461
|
-
assertRunStatusMatchesSpine(run, spine);
|
|
400
|
+
const plan = requireRunPlan(run);
|
|
462
401
|
if (run.status !== "active") {
|
|
463
402
|
throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
|
|
464
403
|
}
|
|
465
|
-
// Re-checked inside the write transaction (like every other preflight
|
|
466
|
-
// condition): an engine may have claimed the run while the summary gate
|
|
467
|
-
// above was awaiting its LLM judge.
|
|
468
|
-
assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
|
|
469
404
|
const existing = repo.getStep(run.id, input.stepId);
|
|
470
405
|
if (!existing) {
|
|
471
406
|
throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
|
|
@@ -479,13 +414,8 @@ export async function completeWorkflowStep(input) {
|
|
|
479
414
|
if (input.signal?.aborted)
|
|
480
415
|
throw interruptionReason(input.signal);
|
|
481
416
|
const completedAt = new Date().toISOString();
|
|
482
|
-
// The promoted artifact is persisted
|
|
483
|
-
//
|
|
484
|
-
// truncation marker at this exact write, and the run looked fine right
|
|
485
|
-
// up until a LATER invocation (a resume, or any downstream step
|
|
486
|
-
// referencing it) found the marker instead of the value and failed
|
|
487
|
-
// permanently, with every prior paid step now unrecoverable. Persisting
|
|
488
|
-
// the real value here is what makes it readable again on resume.
|
|
417
|
+
// The promoted artifact is persisted whole, so a resume and every later
|
|
418
|
+
// reference read the real value.
|
|
489
419
|
const evidenceJson = input.evidence ? JSON.stringify(input.evidence) : null;
|
|
490
420
|
repo.updateStepCompletion({
|
|
491
421
|
status: input.status,
|
|
@@ -498,13 +428,8 @@ export async function completeWorkflowStep(input) {
|
|
|
498
428
|
});
|
|
499
429
|
refreshedSteps = readWorkflowRunSteps(repo, run.id);
|
|
500
430
|
const state = deriveRunState(refreshedSteps);
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
// COMPLETED. A resolution failure throws here, and the transaction
|
|
504
|
-
// rolls back whole — the step completion included — so the observable
|
|
505
|
-
// outcome is fail-before-mutation: the step stays pending, the run
|
|
506
|
-
// stays active, and (since appendEvent runs outside this transaction)
|
|
507
|
-
// no event is appended.
|
|
431
|
+
// Declared outputs resolve inside this transaction once the run
|
|
432
|
+
// completes; a failure rolls the step completion back whole.
|
|
508
433
|
let outputsJson; // undefined = untouched, keep the row's existing value
|
|
509
434
|
if (state.status === "completed" && plan.outputs) {
|
|
510
435
|
const resolved = resolveWorkflowRunOutputs(plan, refreshedSteps);
|
|
@@ -512,14 +437,11 @@ export async function completeWorkflowStep(input) {
|
|
|
512
437
|
repo.setRunOutputs(run.id, outputsJson);
|
|
513
438
|
outputWarnings = resolved.errors;
|
|
514
439
|
}
|
|
515
|
-
// Re-arm the check-in on every state change: a healthy, progressing run
|
|
516
|
-
// keeps pushing the stall window forward so the directive never fires.
|
|
517
440
|
repo.updateRunState({
|
|
518
441
|
status: state.status,
|
|
519
442
|
currentStepId: state.currentStepId,
|
|
520
443
|
updatedAt: completedAt,
|
|
521
444
|
completedAt: state.completedAt,
|
|
522
|
-
checkinArmedAt: completedAt,
|
|
523
445
|
runId: run.id,
|
|
524
446
|
});
|
|
525
447
|
updatedRun = {
|
|
@@ -528,7 +450,6 @@ export async function completeWorkflowStep(input) {
|
|
|
528
450
|
current_step_id: state.currentStepId,
|
|
529
451
|
updated_at: completedAt,
|
|
530
452
|
completed_at: state.completedAt,
|
|
531
|
-
checkin_armed_at: completedAt,
|
|
532
453
|
...(outputsJson !== undefined ? { outputs_json: outputsJson } : {}),
|
|
533
454
|
};
|
|
534
455
|
});
|
|
@@ -663,48 +584,33 @@ function readWorkflowRunOrPrefix(repo, specifier) {
|
|
|
663
584
|
const run = findRunByIdOrPrefix(repo, specifier);
|
|
664
585
|
if (!run)
|
|
665
586
|
throw new NotFoundError(`Workflow run "${specifier}" not found.`, "WORKFLOW_NOT_FOUND");
|
|
666
|
-
return
|
|
587
|
+
return run;
|
|
667
588
|
}
|
|
668
589
|
function readWorkflowRun(repo, runId) {
|
|
669
590
|
const run = repo.getRunById(runId);
|
|
670
591
|
if (!run) {
|
|
671
592
|
throw new NotFoundError(`Workflow run "${runId}" not found.`, "WORKFLOW_NOT_FOUND");
|
|
672
593
|
}
|
|
673
|
-
return
|
|
594
|
+
return run;
|
|
674
595
|
}
|
|
675
596
|
/**
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
* live lease: {@link WorkflowRunsRepository.reclaimExpiredEngineLease} is a
|
|
681
|
-
* compare-and-swap on the exact (holder, until) this call observed, so a
|
|
682
|
-
* lease renewed or re-acquired between the read and this write is left alone.
|
|
597
|
+
* The frozen plan a step completion resolves its gate/outputs against. The
|
|
598
|
+
* engine has already read (or abandoned) the plan before it gets here; this
|
|
599
|
+
* throw only covers a direct caller completing a step of a run whose plan is
|
|
600
|
+
* unreadable.
|
|
683
601
|
*/
|
|
684
|
-
function
|
|
685
|
-
const
|
|
686
|
-
if (
|
|
687
|
-
return
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
repo.reclaimExpiredEngineLease(run.id, holder, until, now);
|
|
692
|
-
return { ...run, engine_lease_holder: null, engine_lease_until: null };
|
|
602
|
+
function requireRunPlan(run) {
|
|
603
|
+
const read = readRunPlan(run);
|
|
604
|
+
if (read.ok)
|
|
605
|
+
return read.plan;
|
|
606
|
+
throw new UsageError(read.newer
|
|
607
|
+
? read.problem
|
|
608
|
+
: `${read.problem} Run 'akm workflow abandon ${run.id}' and start a new run with 'akm workflow run ${run.workflow_ref}'.`);
|
|
693
609
|
}
|
|
694
610
|
function readWorkflowRunSteps(repo, runId) {
|
|
695
611
|
return repo.getStepsForRun(runId);
|
|
696
612
|
}
|
|
697
613
|
function buildWorkflowRunDetail(repo, run, steps) {
|
|
698
|
-
// Review M1: `workflow status` (and every other detail-shaped response) now
|
|
699
|
-
// evaluates the check-in, not just `workflow run`. Pure timestamp check —
|
|
700
|
-
// no background thread (see checkin.ts).
|
|
701
|
-
const checkin = evaluateCheckin({
|
|
702
|
-
status: run.status,
|
|
703
|
-
updatedAt: run.updated_at,
|
|
704
|
-
checkinArmedAt: run.checkin_armed_at,
|
|
705
|
-
agentHarness: run.agent_harness,
|
|
706
|
-
agentSessionId: run.agent_session_id,
|
|
707
|
-
});
|
|
708
614
|
const children = childRunTree(repo, run.id, run.id);
|
|
709
615
|
return {
|
|
710
616
|
run: toWorkflowRunSummary(run),
|
|
@@ -713,39 +619,14 @@ function buildWorkflowRunDetail(repo, run, steps) {
|
|
|
713
619
|
title: run.workflow_title,
|
|
714
620
|
steps: steps.map(toWorkflowRunStepState),
|
|
715
621
|
},
|
|
716
|
-
...(checkin ? { checkin } : {}),
|
|
717
622
|
...(children ? { children } : {}),
|
|
718
623
|
};
|
|
719
624
|
}
|
|
720
625
|
/**
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
* depth: `akm workflow resume <rootRunId> && akm workflow run <rootRunId>` —
|
|
726
|
-
* never each node's own immediate parent, which the tree's caller has no
|
|
727
|
-
* command for.
|
|
728
|
-
*
|
|
729
|
-
* That command is sufficient to clear a block exactly ONE level deep (the
|
|
730
|
-
* root's own composing step blocked directly on this node) but NOT deeper
|
|
731
|
-
* (code-review round 4, finding 6 / Review log R6 — corrects a false claim
|
|
732
|
-
* this comment used to make here). Re-driving the root does **not** cascade
|
|
733
|
-
* back down through every intermediate composing step: `driveChildWorkflowUnit`
|
|
734
|
-
* (child-workflow.ts) never re-drives a child whose OWN status is `blocked`
|
|
735
|
-
* (row A-22) — no lease is even taken — so a re-drive just RE-OBSERVES the
|
|
736
|
-
* still-blocked status and re-propagates the block upward (an intermediate
|
|
737
|
-
* run is always blocked when a descendant is, row A-21, applied
|
|
738
|
-
* recursively), never reaching the deepest blocked node. Clearing a
|
|
739
|
-
* depth-2-or-deeper block requires resuming EVERY blocked run in the chain,
|
|
740
|
-
* deepest first, then re-running only the root — see "Recovering a blocked
|
|
741
|
-
* child" in docs/guides/run-workflows.md and "Blocked-child recovery" in
|
|
742
|
-
* docs/reference/workflow-schema.md for the worked multi-level sequence.
|
|
743
|
-
* `resume.then` is deliberately not widened to enumerate that chain (no
|
|
744
|
-
* envelope change, no new field) — the docs carry the multi-level sequence
|
|
745
|
-
* instead.
|
|
746
|
-
*
|
|
747
|
-
* Absent, never `[]`, when `forRunId` has no children (P3a's `childRunsOf`
|
|
748
|
-
* order: `created_at, id`).
|
|
626
|
+
* The parent-child status tree for `forRunId`'s children, recursively. Every
|
|
627
|
+
* blocked node's `resume.then` names the top-of-query run (`rootRunId`); a
|
|
628
|
+
* block deeper than one level needs every blocked run resumed, deepest first
|
|
629
|
+
* (docs/guides/run-workflows.md). Absent, never `[]`, when there are no children.
|
|
749
630
|
*/
|
|
750
631
|
function childRunTree(repo, rootRunId, forRunId) {
|
|
751
632
|
const rows = repo.childRunsOf(forRunId);
|
|
@@ -755,7 +636,7 @@ function childRunTree(repo, rootRunId, forRunId) {
|
|
|
755
636
|
}
|
|
756
637
|
function toChildRunNode(repo, rootRunId, row) {
|
|
757
638
|
const spawnedByUnitId = row.parent_unit_id ?? "";
|
|
758
|
-
//
|
|
639
|
+
// the parent STEP that spawned it, resolved via the real journaled
|
|
759
640
|
// unit row — null when that unit row is gone.
|
|
760
641
|
const stepId = row.parent_run_id && row.parent_unit_id
|
|
761
642
|
? (repo.getUnit(row.parent_run_id, row.parent_unit_id)?.step_id ?? null)
|
|
@@ -784,7 +665,6 @@ function toChildRunNode(repo, rootRunId, row) {
|
|
|
784
665
|
};
|
|
785
666
|
}
|
|
786
667
|
function toWorkflowRunSummary(run) {
|
|
787
|
-
const plan = classifyWorkflowRunPlan(run);
|
|
788
668
|
return {
|
|
789
669
|
id: run.id,
|
|
790
670
|
workflowRef: run.workflow_ref,
|
|
@@ -799,47 +679,15 @@ function toWorkflowRunSummary(run) {
|
|
|
799
679
|
params: parseJsonObject(run.params_json),
|
|
800
680
|
agentHarness: run.agent_harness ?? null,
|
|
801
681
|
agentSessionId: run.agent_session_id ?? null,
|
|
802
|
-
planIrVersion:
|
|
803
|
-
|
|
804
|
-
// Surface the engine lease (holder id + expiry — never workflow-authored
|
|
805
|
-
// content) so `workflow run`/`status` show which native execution
|
|
806
|
-
// invocation currently holds the run lease. Gated on `until` still being
|
|
807
|
-
// in the future: a crashed engine's lease self-expires, and a run
|
|
808
|
-
// whose holder is provably gone must stop reading as engine-driven the
|
|
809
|
-
// instant that happens, not just once something next attempts to acquire
|
|
810
|
-
// it (`readWorkflowRun`/`readWorkflowRunOrPrefix` also reclaim the DB
|
|
811
|
-
// columns outright on the same condition).
|
|
812
|
-
...(run.engine_lease_holder && run.engine_lease_until && run.engine_lease_until >= new Date().toISOString()
|
|
813
|
-
? { engineLease: { holder: run.engine_lease_holder, until: run.engine_lease_until } }
|
|
814
|
-
: {}),
|
|
815
|
-
// P3b (spec §4.5): all three optional and conditionally spread, so every
|
|
682
|
+
planIrVersion: run.plan_ir_version ?? null,
|
|
683
|
+
// P3b: all three optional and conditionally spread, so every
|
|
816
684
|
// pre-existing (non-child, no-outputs-declared) run's envelope is
|
|
817
|
-
// byte-identical
|
|
685
|
+
// byte-identical.
|
|
818
686
|
...(run.outputs_json ? { outputs: parseJsonObject(run.outputs_json) ?? {} } : {}),
|
|
819
687
|
...(run.parent_run_id ? { parentRunId: run.parent_run_id } : {}),
|
|
820
688
|
...(run.parent_unit_id ? { spawnedByUnitId: run.parent_unit_id } : {}),
|
|
821
689
|
};
|
|
822
690
|
}
|
|
823
|
-
/**
|
|
824
|
-
* Single-driver enforcement (R2 run lease): while a LIVE (unexpired) engine
|
|
825
|
-
* lease is held, only the holding engine may advance the gate spine. Manual
|
|
826
|
-
* A call with no `leaseHolder` — or a stale engine invocation
|
|
827
|
-
* whose lease was claimed by another — is refused with the holder + expiry.
|
|
828
|
-
* An EXPIRED lease never blocks: the engine that held it is presumed dead.
|
|
829
|
-
*/
|
|
830
|
-
function assertLeaseAllowsSpineAdvance(run, leaseHolder) {
|
|
831
|
-
if (!run.engine_lease_holder || !run.engine_lease_until)
|
|
832
|
-
return;
|
|
833
|
-
if (leaseHolder === run.engine_lease_holder)
|
|
834
|
-
return;
|
|
835
|
-
if (run.engine_lease_until < new Date().toISOString())
|
|
836
|
-
return; // expired ⇒ claimable, not live
|
|
837
|
-
// #948 addendum: moved off UsageError (exit 75, not exit 2) — a held lease
|
|
838
|
-
// is ordinary contention, not a bad command line.
|
|
839
|
-
throw new TransientError(`Workflow run ${run.id} is being driven by engine ${run.engine_lease_holder} ` +
|
|
840
|
-
`(run lease expires ${run.engine_lease_until}). The engine owns the step spine while it runs — ` +
|
|
841
|
-
`wait for it to finish or for the lease to expire before advancing steps manually.`, "RUN_LEASE_HELD");
|
|
842
|
-
}
|
|
843
691
|
function toWorkflowRunStepState(step) {
|
|
844
692
|
return {
|
|
845
693
|
id: step.step_id,
|
|
@@ -1,6 +1,7 @@
|
|
|
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 { createHash } from "node:crypto";
|
|
4
5
|
import fs from "node:fs";
|
|
5
6
|
import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
|
|
6
7
|
import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
@@ -12,8 +13,7 @@ import { deriveInstallations } from "../../indexer/installations.js";
|
|
|
12
13
|
import { resolveAdapterConceptOwner } from "../../indexer/lookup/adapter-concept-owner.js";
|
|
13
14
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
14
15
|
import { withIndexDb } from "../../storage/repositories/index-db.js";
|
|
15
|
-
import { compileWorkflowSource } from "../
|
|
16
|
-
import { sourceStepInstructions } from "../source-ir/program.js";
|
|
16
|
+
import { compileWorkflowSource } from "../compile.js";
|
|
17
17
|
/**
|
|
18
18
|
* Build the canonical `workflow_runs.workflow_ref` for an AKM workflow asset.
|
|
19
19
|
*/
|
|
@@ -36,7 +36,7 @@ export async function canonicalizeWorkflowRefInput(ref) {
|
|
|
36
36
|
return (await loadWorkflowAsset(ref)).ref;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Resolve a workflow ref and compile its authored bytes into
|
|
39
|
+
* Resolve a workflow ref and compile its authored bytes into a plan.
|
|
40
40
|
*/
|
|
41
41
|
export async function loadWorkflowAsset(ref) {
|
|
42
42
|
const bundleRef = parseWorkflowRefInput(ref);
|
|
@@ -91,8 +91,27 @@ export async function loadWorkflowAsset(ref) {
|
|
|
91
91
|
const fullRef = workflowAdapterId === "akm-workflow"
|
|
92
92
|
? makeBundleRef(sourceBundleId, canonicalName)
|
|
93
93
|
: canonicalWorkflowRunRef(sourceBundleId, canonicalName);
|
|
94
|
-
const
|
|
95
|
-
|
|
94
|
+
const title = canonicalName.split("/").pop() || canonicalName;
|
|
95
|
+
const bytes = fs.readFileSync(assetPath);
|
|
96
|
+
const result = compileWorkflowSource(bytes.toString("utf8"), {
|
|
97
|
+
path: assetPath,
|
|
98
|
+
workspaceRoot: resolvedSourcePath,
|
|
99
|
+
title,
|
|
100
|
+
});
|
|
101
|
+
if (!result.ok) {
|
|
102
|
+
const details = result.errors.map((error) => ` ${error.path}:${error.line} — ${error.message}`).join("\n");
|
|
103
|
+
const isMultiJob = result.errors.length === 1 && result.errors[0]?.code === "multi-job-unsupported";
|
|
104
|
+
throw new UsageError(`Workflow source has ${result.errors.length} error(s):\n${details}`, isMultiJob ? "COMPOSITION_INVALID" : "WORKFLOW_SOURCE_INVALID", isMultiJob ? COMPOSITION_INVALID_MULTI_JOB_HINT : undefined);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
ref: fullRef,
|
|
108
|
+
path: assetPath,
|
|
109
|
+
sourcePath: resolvedSourcePath,
|
|
110
|
+
adapterId: workflowAdapterId,
|
|
111
|
+
title,
|
|
112
|
+
plan: result.plan,
|
|
113
|
+
sourceHash: createHash("sha256").update(bytes).digest("hex"),
|
|
114
|
+
};
|
|
96
115
|
}
|
|
97
116
|
function ownsNativeWorkflowRuntime(source) {
|
|
98
117
|
return source.adapterId === "akm" || source.adapterId === "akm-workflow";
|
|
@@ -120,55 +139,3 @@ export function resolveWorkflowEntryId(_sourcePath, ref, adapterId) {
|
|
|
120
139
|
return row?.id ?? null;
|
|
121
140
|
});
|
|
122
141
|
}
|
|
123
|
-
function compileWorkflowSourceFromDisk(assetPath, workspaceRoot) {
|
|
124
|
-
const content = fs.readFileSync(assetPath, "utf8");
|
|
125
|
-
const result = compileWorkflowSource(content, { path: assetPath, workspaceRoot });
|
|
126
|
-
if (!result.ok) {
|
|
127
|
-
// P4-N2's mapping (docs/plans/specs/p4-deletions-closeout.md §3.3.4): this
|
|
128
|
-
// is the FIRST compile of a workflow's own source that a ref resolves
|
|
129
|
-
// through (loadWorkflowAsset runs before compileResolveFreezeWorkflowV4),
|
|
130
|
-
// so it must apply the same code split as the freeze wrapper — otherwise
|
|
131
|
-
// row B-44's COMPOSITION_INVALID promise never reaches `startWorkflowRun`
|
|
132
|
-
// callers, which never get past this point on a multi-job source.
|
|
133
|
-
const details = result.errors.map((error) => ` ${error.path}:${error.line} — ${error.message}`).join("\n");
|
|
134
|
-
const isMultiJob = result.errors.length === 1 && result.errors[0]?.code === "multi-job-unsupported";
|
|
135
|
-
const code = isMultiJob ? "COMPOSITION_INVALID" : "WORKFLOW_SOURCE_INVALID";
|
|
136
|
-
throw new UsageError(`Workflow source has ${result.errors.length} error(s):\n${details}`, code, isMultiJob ? COMPOSITION_INVALID_MULTI_JOB_HINT : undefined);
|
|
137
|
-
}
|
|
138
|
-
return result.ir;
|
|
139
|
-
}
|
|
140
|
-
function projectAsset(sourceIr, ref, assetPath, sourcePath, adapterId, canonicalName) {
|
|
141
|
-
const title = canonicalName.split("/").pop() || canonicalName;
|
|
142
|
-
return {
|
|
143
|
-
ref,
|
|
144
|
-
path: assetPath,
|
|
145
|
-
sourcePath,
|
|
146
|
-
adapterId,
|
|
147
|
-
title,
|
|
148
|
-
...(sourceIr.params
|
|
149
|
-
? {
|
|
150
|
-
parameters: Object.entries(sourceIr.params).map(([name, schema]) => {
|
|
151
|
-
const description = schema.description;
|
|
152
|
-
return { name, ...(typeof description === "string" && description ? { description } : {}) };
|
|
153
|
-
}),
|
|
154
|
-
}
|
|
155
|
-
: {}),
|
|
156
|
-
steps: sourceIr.jobs.flatMap((job) => job.steps.map((step, sequenceIndex) => ({
|
|
157
|
-
id: step.id,
|
|
158
|
-
title: step.id,
|
|
159
|
-
instructions: step.route ? stepFallbackInstructions(step) : sourceStepInstructions(step),
|
|
160
|
-
...(step.gate?.rubric?.trim() ? { completionCriteria: [step.gate.rubric] } : {}),
|
|
161
|
-
sequenceIndex,
|
|
162
|
-
}))),
|
|
163
|
-
sourceIr,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
/** A route-only step with no body section still needs a non-empty spine instructions string. */
|
|
167
|
-
function stepFallbackInstructions(step) {
|
|
168
|
-
if (!step.route)
|
|
169
|
-
return "";
|
|
170
|
-
const branches = step.route.branches.map((b) => `"${b.match}" -> ${b.stepId}`);
|
|
171
|
-
if (step.route.defaultStepId !== undefined)
|
|
172
|
-
branches.push(`default -> ${step.route.defaultStepId}`);
|
|
173
|
-
return `Route on ${step.route.input}: ${branches.join(", ")}.`;
|
|
174
|
-
}
|