akm-cli 0.9.0-beta.9 → 0.9.0-rc.1
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 +715 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +228 -292
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +291 -13
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +79 -5
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +403 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/json-schema.js +142 -0
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +261 -770
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +48 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +41 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +79 -6
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +83 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +206 -81
- package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
- package/dist/integrations/harnesses/aider/index.js +58 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
- package/dist/integrations/harnesses/amazonq/index.js +59 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
- package/dist/integrations/harnesses/claude/index.js +27 -23
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
- package/dist/integrations/harnesses/codex/index.js +63 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +60 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
- package/dist/integrations/harnesses/gemini/index.js +60 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +28 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +17 -16
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
- package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
- package/dist/integrations/harnesses/openhands/index.js +58 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
- package/dist/integrations/harnesses/pi/index.js +58 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -0
- package/dist/integrations/session-logs/index.js +40 -11
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +87 -15
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +6 -1
- package/dist/output/text/helpers.js +241 -2
- package/dist/output/text/workflow.js +4 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/schemas/akm-config.json +14225 -0
- package/dist/schemas/akm-workflow.json +328 -0
- package/dist/scripts/migrate-storage.js +2743 -8390
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/repositories/workflow-runs-repository.js +189 -1
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +5 -0
- package/dist/workflows/db.js +138 -4
- package/dist/workflows/exec/brief.js +484 -0
- package/dist/workflows/exec/native-executor.js +975 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1295 -0
- package/dist/workflows/exec/run-workflow.js +596 -0
- package/dist/workflows/exec/scheduler.js +100 -0
- package/dist/workflows/exec/step-work.js +1156 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +388 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +33 -0
- package/dist/workflows/ir/schema.js +4 -0
- package/dist/workflows/parser.js +3 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +760 -0
- package/dist/workflows/program/project.js +105 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/runs.js +248 -153
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
- package/dist/workflows/validate-summary.js +26 -10
- package/dist/workflows/validator.js +1 -1
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +7 -5
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -2,25 +2,89 @@
|
|
|
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
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import fs from "node:fs";
|
|
6
5
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
7
|
-
import {
|
|
6
|
+
import { canonicalizeWorkflowName } from "../../core/asset/asset-spec.js";
|
|
7
|
+
import { getDefaultLlmConfig, loadConfig } from "../../core/config/config.js";
|
|
8
8
|
import { NotFoundError, UsageError } from "../../core/errors.js";
|
|
9
9
|
import { appendEvent } from "../../core/events.js";
|
|
10
|
-
import {
|
|
11
|
-
import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
|
|
12
|
-
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
13
|
-
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
14
|
-
import { resolveAssetPath } from "../../sources/resolve.js";
|
|
10
|
+
import { warn } from "../../core/warn.js";
|
|
15
11
|
import { withWorkflowRunsRepo, } from "../../storage/repositories/workflow-runs-repository.js";
|
|
16
|
-
import { formatWorkflowErrors } from "../authoring/authoring.js";
|
|
17
12
|
import { getCurrentWorkflowScopeKey } from "../authoring/scope-key.js";
|
|
18
|
-
import {
|
|
13
|
+
import { detectSecretShapedParams } from "../exec/param-secrets.js";
|
|
14
|
+
import { collectProgramWarnings } from "../ir/compile.js";
|
|
15
|
+
import { validateWorkflowParams } from "../ir/params.js";
|
|
16
|
+
import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
|
|
19
17
|
import { validateStepSummary } from "../validate-summary.js";
|
|
20
18
|
import { resolveAgentIdentity } from "./agent-identity.js";
|
|
21
19
|
import { evaluateCheckin } from "./checkin.js";
|
|
20
|
+
import { evaluateStaleUnits } from "./unit-checkin.js";
|
|
21
|
+
import { compileWorkflowAssetPlan, loadWorkflowAsset, resolveWorkflowEntryId } from "./workflow-asset-loader.js";
|
|
22
|
+
/** Clip bound for a unit's `result_json` on the `--units` diagnostic surface. */
|
|
23
|
+
const UNIT_DIAGNOSTIC_CLIP = 2000;
|
|
24
|
+
function toUnitDiagnostic(row, stale) {
|
|
25
|
+
let diagnostic = null;
|
|
26
|
+
if (row.result_json !== null) {
|
|
27
|
+
// `result_json` is a JSON-encoded value: a bare JSON string for a free-text
|
|
28
|
+
// unit, an object/array for a schema unit. Render the decoded string as-is
|
|
29
|
+
// (no surrounding quotes) and other shapes as compact JSON, then clip so a
|
|
30
|
+
// large artifact can't flood the diagnostic surface.
|
|
31
|
+
let text = row.result_json;
|
|
32
|
+
try {
|
|
33
|
+
const parsed = JSON.parse(row.result_json);
|
|
34
|
+
text = typeof parsed === "string" ? parsed : JSON.stringify(parsed);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* leave the raw journaled text */
|
|
38
|
+
}
|
|
39
|
+
diagnostic = text.length > UNIT_DIAGNOSTIC_CLIP ? `${text.slice(0, UNIT_DIAGNOSTIC_CLIP)}…` : text;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
unitId: row.unit_id,
|
|
43
|
+
nodeId: row.node_id,
|
|
44
|
+
stepId: row.step_id,
|
|
45
|
+
phase: row.phase,
|
|
46
|
+
status: row.status,
|
|
47
|
+
attempts: row.attempts,
|
|
48
|
+
tokens: row.tokens,
|
|
49
|
+
failureReason: row.failure_reason,
|
|
50
|
+
sessionId: row.session_id,
|
|
51
|
+
diagnostic,
|
|
52
|
+
startedAt: row.started_at,
|
|
53
|
+
finishedAt: row.finished_at,
|
|
54
|
+
stale: stale !== undefined,
|
|
55
|
+
staleIdleMs: stale ? (Number.isFinite(stale.idleMs) ? stale.idleMs : null) : null,
|
|
56
|
+
claimHolder: row.claim_holder,
|
|
57
|
+
claimExpiresAt: row.claim_expires_at,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
22
60
|
export async function startWorkflowRun(ref, params = {}, options) {
|
|
23
61
|
const asset = await loadWorkflowAsset(ref);
|
|
62
|
+
// Frozen plan (redesign addendum, R1): compile the plan ONCE at start and
|
|
63
|
+
// persist it on the run row in the same transaction as the insert. Every
|
|
64
|
+
// later invocation executes this snapshot — the asset file is never re-read
|
|
65
|
+
// for an in-flight run; re-planning is an explicit new run.
|
|
66
|
+
const plan = compileWorkflowAssetPlan(asset);
|
|
67
|
+
// Non-fatal WARNINGS (redesign addendum): a YAML program's untyped-step and
|
|
68
|
+
// undeclared-param advisories surface as `warn()` lines at start (stderr,
|
|
69
|
+
// consistent with the repo's other author-facing warnings) without blocking
|
|
70
|
+
// the run. Markdown workflows carry no `program` and warn about nothing.
|
|
71
|
+
if (asset.program) {
|
|
72
|
+
for (const w of collectProgramWarnings(asset.program)) {
|
|
73
|
+
warn(`workflow start: ${asset.path}:${w.line} — ${w.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Reviewer #12: validate supplied `--params` against the frozen param
|
|
77
|
+
// schemas BEFORE creating the run, so a type-mismatched param (e.g. a string
|
|
78
|
+
// for a `{ type: array }` param) is rejected with actionable errors instead
|
|
79
|
+
// of flowing silently into a unit prompt. Programs without declared param
|
|
80
|
+
// schemas (and every Markdown workflow) validate trivially.
|
|
81
|
+
const paramErrors = validateWorkflowParams(plan, params);
|
|
82
|
+
if (paramErrors.length > 0) {
|
|
83
|
+
throw new UsageError(`Cannot start ${asset.ref}: the supplied --params do not satisfy the workflow's declared parameter schemas:\n` +
|
|
84
|
+
paramErrors.map((e) => ` - ${e}`).join("\n"), "INVALID_JSON_ARGUMENT");
|
|
85
|
+
}
|
|
86
|
+
const planJson = canonicalPlanJson(plan);
|
|
87
|
+
const planHash = computePlanHash(plan);
|
|
24
88
|
return withWorkflowRunsRepo(async (repo) => {
|
|
25
89
|
const now = new Date().toISOString();
|
|
26
90
|
const runId = randomUUID();
|
|
@@ -72,21 +136,49 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
72
136
|
completionJson: step.completionCriteria ? JSON.stringify(step.completionCriteria) : null,
|
|
73
137
|
sequenceIndex: step.sequenceIndex ?? 0,
|
|
74
138
|
})));
|
|
139
|
+
// Same transaction as the insert: a run row never exists without its
|
|
140
|
+
// frozen plan (rows with NULL plan_json are pre-006 legacy runs).
|
|
141
|
+
repo.setRunPlan(runId, planJson, planHash);
|
|
75
142
|
});
|
|
76
143
|
const result = await getWorkflowStatus(runId);
|
|
144
|
+
// #13: params are declared non-secret (they are copied verbatim into every
|
|
145
|
+
// unit prompt and hashed into the unit identity, so they cannot be redacted
|
|
146
|
+
// without breaking the driver protocol). Surface a loud, best-effort warning
|
|
147
|
+
// when a param LOOKS like a credential so the author moves it to an env
|
|
148
|
+
// binding. Advisory only — never blocks the start.
|
|
149
|
+
const secretWarnings = detectSecretShapedParams(params);
|
|
150
|
+
if (secretWarnings.length > 0)
|
|
151
|
+
result.warnings = [...(result.warnings ?? []), ...secretWarnings];
|
|
152
|
+
// 07 P1-B: emit only the run id + status — NOT the raw workflowTitle (which
|
|
153
|
+
// comes verbatim from the workflow asset's frontmatter and is therefore
|
|
154
|
+
// attacker-influenceable). Keeping raw titles out of the events stream
|
|
155
|
+
// shrinks the injectable footprint for any consumer that re-surfaces events
|
|
156
|
+
// into agent context.
|
|
77
157
|
appendEvent({
|
|
78
158
|
eventType: "workflow_started",
|
|
79
159
|
ref: ref,
|
|
80
|
-
metadata: { runId: result.run.id,
|
|
160
|
+
metadata: { runId: result.run.id, status: result.run.status },
|
|
81
161
|
});
|
|
82
162
|
return result;
|
|
83
163
|
});
|
|
84
164
|
}
|
|
85
|
-
export async function getWorkflowStatus(runId) {
|
|
165
|
+
export async function getWorkflowStatus(runId, opts) {
|
|
86
166
|
return withWorkflowRunsRepo((repo) => {
|
|
87
167
|
const run = readWorkflowRun(repo, runId);
|
|
88
168
|
const steps = readWorkflowRunSteps(repo, run.id);
|
|
89
|
-
|
|
169
|
+
const detail = buildWorkflowRunDetail(run, steps);
|
|
170
|
+
if (opts?.includeUnits) {
|
|
171
|
+
// The honest diagnostic surface (#22): read the unit journal straight and
|
|
172
|
+
// project each row, INCLUDING failures whose diagnostic text the
|
|
173
|
+
// deterministic evidence graph drops. Read-only; never mutates the run.
|
|
174
|
+
const rows = repo.getUnitsForRun(run.id);
|
|
175
|
+
// Codex round-3 finding B: run the SAME pure stale-claim evaluator `brief`
|
|
176
|
+
// uses (`now` injected for deterministic tests) so a dead driver's claimed
|
|
177
|
+
// `running` unit surfaces as stale here too, not just as raw `running`.
|
|
178
|
+
const staleById = new Map(evaluateStaleUnits(rows, opts.now ?? Date.now()).map((u) => [u.unitId, u]));
|
|
179
|
+
detail.units = rows.map((row) => toUnitDiagnostic(row, staleById.get(row.unit_id)));
|
|
180
|
+
}
|
|
181
|
+
return detail;
|
|
90
182
|
});
|
|
91
183
|
}
|
|
92
184
|
export async function hasWorkflowRun(runId) {
|
|
@@ -101,7 +193,7 @@ export async function listWorkflowRuns(input) {
|
|
|
101
193
|
if (parsed.type !== "workflow") {
|
|
102
194
|
throw new UsageError(`Expected a workflow ref (workflow:<name>), got "${input.workflowRef}".`);
|
|
103
195
|
}
|
|
104
|
-
workflowRef = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
|
|
196
|
+
workflowRef = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${canonicalizeWorkflowName(parsed.name)}`;
|
|
105
197
|
}
|
|
106
198
|
const rows = repo.listRuns({
|
|
107
199
|
scopeKey,
|
|
@@ -115,31 +207,58 @@ export async function getNextWorkflowStep(specifier, params) {
|
|
|
115
207
|
return withWorkflowRunsRepo(async (repo) => {
|
|
116
208
|
const { run, autoStarted } = await resolveRunSpecifier(repo, specifier, params);
|
|
117
209
|
const steps = readWorkflowRunSteps(repo, run.id);
|
|
118
|
-
|
|
119
|
-
const done = run.status === "completed" ? true : undefined;
|
|
120
|
-
// #506: surface a check-in directive through the normal command output when
|
|
121
|
-
// the run looks stalled. Pure timestamp evaluation — no background thread.
|
|
122
|
-
const checkin = evaluateCheckin({
|
|
123
|
-
status: run.status,
|
|
124
|
-
updatedAt: run.updated_at,
|
|
125
|
-
checkinArmedAt: run.checkin_armed_at,
|
|
126
|
-
agentHarness: run.agent_harness,
|
|
127
|
-
agentSessionId: run.agent_session_id,
|
|
128
|
-
}) ?? undefined;
|
|
129
|
-
return {
|
|
130
|
-
run: toWorkflowRunSummary(run),
|
|
131
|
-
workflow: {
|
|
132
|
-
ref: run.workflow_ref,
|
|
133
|
-
title: run.workflow_title,
|
|
134
|
-
steps: steps.map(toWorkflowRunStepState),
|
|
135
|
-
},
|
|
136
|
-
step: currentStep ? toWorkflowRunStepState(currentStep) : null,
|
|
137
|
-
...(done ? { done } : {}),
|
|
138
|
-
...(autoStarted ? { autoStarted } : {}),
|
|
139
|
-
...(checkin ? { checkin } : {}),
|
|
140
|
-
};
|
|
210
|
+
return { ...projectNextResult(run, steps), ...(autoStarted ? { autoStarted: true } : {}) };
|
|
141
211
|
});
|
|
142
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Project a run row + its step rows into a {@link WorkflowNextResult}. The pure
|
|
215
|
+
* read-shaping half of {@link getNextWorkflowStep}, extracted so the driver
|
|
216
|
+
* snapshot below reproduces the exact same projection without re-running the
|
|
217
|
+
* auto-start-capable {@link resolveRunSpecifier}.
|
|
218
|
+
*/
|
|
219
|
+
function projectNextResult(run, steps) {
|
|
220
|
+
const currentStep = resolveCurrentStep(run, steps);
|
|
221
|
+
const done = run.status === "completed" ? true : undefined;
|
|
222
|
+
// #506: surface a check-in directive through the normal command output when
|
|
223
|
+
// the run looks stalled. Pure timestamp evaluation — no background thread.
|
|
224
|
+
const checkin = evaluateCheckin({
|
|
225
|
+
status: run.status,
|
|
226
|
+
updatedAt: run.updated_at,
|
|
227
|
+
checkinArmedAt: run.checkin_armed_at,
|
|
228
|
+
agentHarness: run.agent_harness,
|
|
229
|
+
agentSessionId: run.agent_session_id,
|
|
230
|
+
}) ?? undefined;
|
|
231
|
+
return {
|
|
232
|
+
run: toWorkflowRunSummary(run),
|
|
233
|
+
workflow: {
|
|
234
|
+
ref: run.workflow_ref,
|
|
235
|
+
title: run.workflow_title,
|
|
236
|
+
steps: steps.map(toWorkflowRunStepState),
|
|
237
|
+
},
|
|
238
|
+
step: currentStep ? toWorkflowRunStepState(currentStep) : null,
|
|
239
|
+
...(done ? { done } : {}),
|
|
240
|
+
...(checkin ? { checkin } : {}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A consistent point-in-time snapshot of a run for the harness-neutral driver
|
|
245
|
+
* protocol (PR #714 review round 2, #14). `brief`/`report` previously read the
|
|
246
|
+
* spine (`getNextWorkflowStep`) and then, in a SEPARATE connection, the run row
|
|
247
|
+
* + unit journal — so a concurrent `report`/`run`/manual completion could change
|
|
248
|
+
* the active step BETWEEN the two reads, leaving the described work-list
|
|
249
|
+
* inconsistent with the run row it was stamped against. This reads the run row,
|
|
250
|
+
* its steps, AND its unit rows inside ONE transaction (one connection, one
|
|
251
|
+
* snapshot) so all three agree. It never auto-starts — `runId` must already
|
|
252
|
+
* resolve to a concrete run — because the driver protocol never mutates on read.
|
|
253
|
+
*/
|
|
254
|
+
export async function snapshotRunForDriver(runId) {
|
|
255
|
+
return withWorkflowRunsRepo((repo) => repo.transaction(() => {
|
|
256
|
+
const run = readWorkflowRun(repo, runId);
|
|
257
|
+
const steps = readWorkflowRunSteps(repo, run.id);
|
|
258
|
+
const units = repo.getUnitsForRun(run.id);
|
|
259
|
+
return { next: projectNextResult(run, steps), run, units };
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
143
262
|
export async function resumeWorkflowRun(runId) {
|
|
144
263
|
return withWorkflowRunsRepo((repo) => {
|
|
145
264
|
const run = readWorkflowRun(repo, runId);
|
|
@@ -164,6 +283,42 @@ export async function resumeWorkflowRun(runId) {
|
|
|
164
283
|
return buildWorkflowRunDetail(updated, steps);
|
|
165
284
|
});
|
|
166
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Give up on a run (08-F6): flip it to `failed` so it stops counting as
|
|
288
|
+
* active — the run-level verb the concurrency-guard message in
|
|
289
|
+
* {@link startWorkflowRun} advertises. Terminal-state runs are refused;
|
|
290
|
+
* {@link resumeWorkflowRun} can reopen an abandoned run if it was a mistake.
|
|
291
|
+
*/
|
|
292
|
+
export async function abandonWorkflowRun(runId) {
|
|
293
|
+
return withWorkflowRunsRepo((repo) => {
|
|
294
|
+
const run = readWorkflowRun(repo, runId);
|
|
295
|
+
if (run.status === "completed" || run.status === "failed") {
|
|
296
|
+
throw new UsageError(`Workflow run ${run.id} is already ${run.status}.`);
|
|
297
|
+
}
|
|
298
|
+
const now = new Date().toISOString();
|
|
299
|
+
repo.updateRunState({
|
|
300
|
+
status: "failed",
|
|
301
|
+
currentStepId: run.current_step_id,
|
|
302
|
+
updatedAt: now,
|
|
303
|
+
completedAt: now,
|
|
304
|
+
checkinArmedAt: now,
|
|
305
|
+
runId: run.id,
|
|
306
|
+
});
|
|
307
|
+
const updated = {
|
|
308
|
+
...run,
|
|
309
|
+
status: "failed",
|
|
310
|
+
updated_at: now,
|
|
311
|
+
completed_at: now,
|
|
312
|
+
checkin_armed_at: now,
|
|
313
|
+
};
|
|
314
|
+
const steps = readWorkflowRunSteps(repo, run.id);
|
|
315
|
+
const detail = buildWorkflowRunDetail(updated, steps);
|
|
316
|
+
// Same injectable-footprint rule as workflow_started (07 P1-B): ids and
|
|
317
|
+
// status only, never the frontmatter-derived title.
|
|
318
|
+
appendEvent({ eventType: "workflow_abandoned", ref: run.workflow_ref, metadata: { runId: run.id } });
|
|
319
|
+
return detail;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
167
322
|
export async function completeWorkflowStep(input) {
|
|
168
323
|
// Read the step (read-only) up front so the LLM validation gate runs OUTSIDE
|
|
169
324
|
// the write transaction — a slow/hung LLM must never hold a db write lock.
|
|
@@ -172,6 +327,7 @@ export async function completeWorkflowStep(input) {
|
|
|
172
327
|
if (run.status !== "active") {
|
|
173
328
|
throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
|
|
174
329
|
}
|
|
330
|
+
assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
|
|
175
331
|
const existing = repo.getStep(run.id, input.stepId);
|
|
176
332
|
if (!existing) {
|
|
177
333
|
throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
|
|
@@ -195,7 +351,7 @@ export async function completeWorkflowStep(input) {
|
|
|
195
351
|
if (input.status === "completed" && summary) {
|
|
196
352
|
const criteria = parseJsonArray(preflight.existing.completion_json) ?? [];
|
|
197
353
|
const judge = input.summaryJudge === undefined ? buildDefaultSummaryJudge() : input.summaryJudge;
|
|
198
|
-
const verdict = await validateStepSummary({ stepTitle: preflight.existing.step_title, completionCriteria: criteria, summary }, judge ?? undefined);
|
|
354
|
+
const verdict = await validateStepSummary({ stepTitle: preflight.existing.step_title, completionCriteria: criteria, summary }, judge ?? undefined, { required: input.requireGate === true });
|
|
199
355
|
if (!verdict.complete) {
|
|
200
356
|
// Re-arm the check-in so a subsequent stall is still nudged, but leave the
|
|
201
357
|
// step pending and return corrective feedback instead of completing.
|
|
@@ -208,6 +364,9 @@ export async function completeWorkflowStep(input) {
|
|
|
208
364
|
stepId: input.stepId,
|
|
209
365
|
missing: verdict.missing,
|
|
210
366
|
feedback: verdict.feedback ?? "The summary does not satisfy the step's completion criteria.",
|
|
367
|
+
// A REQUIRED gate that could not be judged (finding A): the caller BLOCKS
|
|
368
|
+
// rather than treating this as a normal, retryable gate rejection.
|
|
369
|
+
...(verdict.errored ? { errored: true } : {}),
|
|
211
370
|
};
|
|
212
371
|
}
|
|
213
372
|
}
|
|
@@ -219,6 +378,10 @@ export async function completeWorkflowStep(input) {
|
|
|
219
378
|
if (run.status !== "active") {
|
|
220
379
|
throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
|
|
221
380
|
}
|
|
381
|
+
// Re-checked inside the write transaction (like every other preflight
|
|
382
|
+
// condition): an engine may have claimed the run while the summary gate
|
|
383
|
+
// above was awaiting its LLM judge.
|
|
384
|
+
assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
|
|
222
385
|
const existing = repo.getStep(run.id, input.stepId);
|
|
223
386
|
if (!existing) {
|
|
224
387
|
throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
|
|
@@ -261,10 +424,16 @@ export async function completeWorkflowStep(input) {
|
|
|
261
424
|
};
|
|
262
425
|
});
|
|
263
426
|
const detail = buildWorkflowRunDetail(updatedRun, refreshedSteps);
|
|
427
|
+
// #11: emit `workflow_step_completed` ONLY for a genuine `completed`
|
|
428
|
+
// transition; every other non-pending status (failed/skipped/blocked)
|
|
429
|
+
// carries the honest `workflow_step_updated` name. The status is ALWAYS
|
|
430
|
+
// in metadata so consumers never infer it from the event name. Raw `notes`
|
|
431
|
+
// are workflow/model-authored content — an event-stream prompt-injection
|
|
432
|
+
// surface — and never enter the events log; they live on the step row only.
|
|
264
433
|
appendEvent({
|
|
265
|
-
eventType: "workflow_step_completed",
|
|
434
|
+
eventType: input.status === "completed" ? "workflow_step_completed" : "workflow_step_updated",
|
|
266
435
|
ref: detail.run.workflowRef,
|
|
267
|
-
metadata: { runId: input.runId, stepId: input.stepId,
|
|
436
|
+
metadata: { runId: input.runId, stepId: input.stepId, status: input.status },
|
|
268
437
|
});
|
|
269
438
|
if (detail.run.status === "completed") {
|
|
270
439
|
appendEvent({ eventType: "workflow_finished", ref: detail.run.workflowRef, metadata: { runId: input.runId } });
|
|
@@ -287,7 +456,7 @@ async function resolveRunSpecifier(repo, specifier, params) {
|
|
|
287
456
|
if (parsed.type !== "workflow") {
|
|
288
457
|
throw new UsageError(`Expected a workflow ref or workflow run id, got "${specifier}".`);
|
|
289
458
|
}
|
|
290
|
-
const ref = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
|
|
459
|
+
const ref = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${canonicalizeWorkflowName(parsed.name)}`;
|
|
291
460
|
const scopeKey = getCurrentWorkflowScopeKey();
|
|
292
461
|
const active = repo.getActiveRunRowForScope(ref, scopeKey);
|
|
293
462
|
if (active) {
|
|
@@ -299,115 +468,6 @@ async function resolveRunSpecifier(repo, specifier, params) {
|
|
|
299
468
|
const started = await startWorkflowRun(ref, params ?? {});
|
|
300
469
|
return { run: readWorkflowRun(repo, started.run.id), autoStarted: true };
|
|
301
470
|
}
|
|
302
|
-
async function loadWorkflowAsset(ref) {
|
|
303
|
-
const parsed = parseAssetRef(ref);
|
|
304
|
-
if (parsed.type !== "workflow") {
|
|
305
|
-
throw new UsageError(`Expected a workflow ref (workflow:<name>), got "${ref}".`);
|
|
306
|
-
}
|
|
307
|
-
const config = loadConfig();
|
|
308
|
-
const allSources = resolveSourceEntries(undefined, config);
|
|
309
|
-
const searchSources = resolveSourcesForOrigin(parsed.origin, allSources);
|
|
310
|
-
let assetPath;
|
|
311
|
-
let sourcePath;
|
|
312
|
-
for (const source of searchSources) {
|
|
313
|
-
try {
|
|
314
|
-
assetPath = await resolveAssetPath(source.path, "workflow", parsed.name);
|
|
315
|
-
sourcePath = source.path;
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
/* continue */
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (!assetPath) {
|
|
323
|
-
throw new NotFoundError(`Workflow not found for ref: workflow:${parsed.name}`);
|
|
324
|
-
}
|
|
325
|
-
const resolvedSourcePath = sourcePath ?? config.stashDir ?? assetPath;
|
|
326
|
-
const fullRef = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
|
|
327
|
-
const cached = readWorkflowDocumentFromIndex(resolvedSourcePath, fullRef);
|
|
328
|
-
const document = cached ?? loadWorkflowDocumentFromDisk(assetPath);
|
|
329
|
-
return projectAsset(document, fullRef, assetPath, resolvedSourcePath);
|
|
330
|
-
}
|
|
331
|
-
function loadWorkflowDocumentFromDisk(assetPath) {
|
|
332
|
-
const content = fs.readFileSync(assetPath, "utf8");
|
|
333
|
-
const result = parseWorkflow(content, { path: assetPath });
|
|
334
|
-
if (!result.ok) {
|
|
335
|
-
throw new UsageError(formatWorkflowErrors(assetPath, result.errors));
|
|
336
|
-
}
|
|
337
|
-
return result.document;
|
|
338
|
-
}
|
|
339
|
-
function readWorkflowDocumentFromIndex(sourcePath, ref) {
|
|
340
|
-
const dbPath = getDbPath();
|
|
341
|
-
if (!fs.existsSync(dbPath))
|
|
342
|
-
return null;
|
|
343
|
-
const db = openExistingDatabase(dbPath);
|
|
344
|
-
try {
|
|
345
|
-
const parsed = parseAssetRef(ref);
|
|
346
|
-
const entryKey = `${sourcePath}:${parsed.type}:${parsed.name}`;
|
|
347
|
-
const row = db
|
|
348
|
-
.prepare(`SELECT wd.document_json AS document_json
|
|
349
|
-
FROM workflow_documents wd
|
|
350
|
-
JOIN entries e ON e.id = wd.entry_id
|
|
351
|
-
WHERE e.entry_type = 'workflow' AND e.entry_key = ?
|
|
352
|
-
LIMIT 1`)
|
|
353
|
-
.get(entryKey);
|
|
354
|
-
if (!row)
|
|
355
|
-
return null;
|
|
356
|
-
try {
|
|
357
|
-
return JSON.parse(row.document_json);
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
finally {
|
|
364
|
-
closeDatabase(db);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
function projectAsset(doc, ref, assetPath, sourcePath) {
|
|
368
|
-
return {
|
|
369
|
-
ref,
|
|
370
|
-
path: assetPath,
|
|
371
|
-
sourcePath,
|
|
372
|
-
title: doc.title,
|
|
373
|
-
...(doc.parameters
|
|
374
|
-
? {
|
|
375
|
-
parameters: doc.parameters.map((p) => ({
|
|
376
|
-
name: p.name,
|
|
377
|
-
...(p.description ? { description: p.description } : {}),
|
|
378
|
-
})),
|
|
379
|
-
}
|
|
380
|
-
: {}),
|
|
381
|
-
steps: doc.steps.map((s) => ({
|
|
382
|
-
id: s.id,
|
|
383
|
-
title: s.title,
|
|
384
|
-
instructions: s.instructions.text,
|
|
385
|
-
...(s.completionCriteria ? { completionCriteria: s.completionCriteria.map((c) => c.text) } : {}),
|
|
386
|
-
sequenceIndex: s.sequenceIndex,
|
|
387
|
-
})),
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
function resolveWorkflowEntryId(sourcePath, ref) {
|
|
391
|
-
const dbPath = getDbPath();
|
|
392
|
-
if (!fs.existsSync(dbPath))
|
|
393
|
-
return null;
|
|
394
|
-
const db = openExistingDatabase(dbPath);
|
|
395
|
-
try {
|
|
396
|
-
const parsed = parseAssetRef(ref);
|
|
397
|
-
const entryKey = `${sourcePath}:${parsed.type}:${parsed.name}`;
|
|
398
|
-
const row = db
|
|
399
|
-
.prepare(`SELECT id
|
|
400
|
-
FROM entries
|
|
401
|
-
WHERE entry_type = 'workflow'
|
|
402
|
-
AND entry_key = ?
|
|
403
|
-
LIMIT 1`)
|
|
404
|
-
.get(entryKey);
|
|
405
|
-
return row?.id ?? null;
|
|
406
|
-
}
|
|
407
|
-
finally {
|
|
408
|
-
closeDatabase(db);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
471
|
function readWorkflowRun(repo, runId) {
|
|
412
472
|
const run = repo.getRunById(runId);
|
|
413
473
|
if (!run) {
|
|
@@ -419,6 +479,16 @@ function readWorkflowRunSteps(repo, runId) {
|
|
|
419
479
|
return repo.getStepsForRun(runId);
|
|
420
480
|
}
|
|
421
481
|
function buildWorkflowRunDetail(run, steps) {
|
|
482
|
+
// Review M1: `workflow status` (and every other detail-shaped response) now
|
|
483
|
+
// evaluates the check-in, not just `workflow next`. Pure timestamp check —
|
|
484
|
+
// no background thread (see checkin.ts).
|
|
485
|
+
const checkin = evaluateCheckin({
|
|
486
|
+
status: run.status,
|
|
487
|
+
updatedAt: run.updated_at,
|
|
488
|
+
checkinArmedAt: run.checkin_armed_at,
|
|
489
|
+
agentHarness: run.agent_harness,
|
|
490
|
+
agentSessionId: run.agent_session_id,
|
|
491
|
+
});
|
|
422
492
|
return {
|
|
423
493
|
run: toWorkflowRunSummary(run),
|
|
424
494
|
workflow: {
|
|
@@ -426,6 +496,7 @@ function buildWorkflowRunDetail(run, steps) {
|
|
|
426
496
|
title: run.workflow_title,
|
|
427
497
|
steps: steps.map(toWorkflowRunStepState),
|
|
428
498
|
},
|
|
499
|
+
...(checkin ? { checkin } : {}),
|
|
429
500
|
};
|
|
430
501
|
}
|
|
431
502
|
function toWorkflowRunSummary(run) {
|
|
@@ -443,8 +514,31 @@ function toWorkflowRunSummary(run) {
|
|
|
443
514
|
params: parseJsonObject(run.params_json),
|
|
444
515
|
agentHarness: run.agent_harness ?? null,
|
|
445
516
|
agentSessionId: run.agent_session_id ?? null,
|
|
517
|
+
// Surface the engine lease (holder id + expiry — never workflow-authored
|
|
518
|
+
// content) so `workflow next`/`status` show who is driving the run.
|
|
519
|
+
...(run.engine_lease_holder && run.engine_lease_until
|
|
520
|
+
? { engineLease: { holder: run.engine_lease_holder, until: run.engine_lease_until } }
|
|
521
|
+
: {}),
|
|
446
522
|
};
|
|
447
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Single-driver enforcement (R2 run lease): while a LIVE (unexpired) engine
|
|
526
|
+
* lease is held, only the holding engine may advance the gate spine. Manual
|
|
527
|
+
* `akm workflow complete` (no `leaseHolder`) — or a stale engine invocation
|
|
528
|
+
* whose lease was claimed by another — is refused with the holder + expiry.
|
|
529
|
+
* An EXPIRED lease never blocks: the engine that held it is presumed dead.
|
|
530
|
+
*/
|
|
531
|
+
function assertLeaseAllowsSpineAdvance(run, leaseHolder) {
|
|
532
|
+
if (!run.engine_lease_holder || !run.engine_lease_until)
|
|
533
|
+
return;
|
|
534
|
+
if (leaseHolder === run.engine_lease_holder)
|
|
535
|
+
return;
|
|
536
|
+
if (run.engine_lease_until < new Date().toISOString())
|
|
537
|
+
return; // expired ⇒ claimable, not live
|
|
538
|
+
throw new UsageError(`Workflow run ${run.id} is being driven by engine ${run.engine_lease_holder} ` +
|
|
539
|
+
`(run lease expires ${run.engine_lease_until}). The engine owns the step spine while it runs — ` +
|
|
540
|
+
`wait for it to finish or for the lease to expire before advancing steps manually.`);
|
|
541
|
+
}
|
|
448
542
|
function toWorkflowRunStepState(step) {
|
|
449
543
|
return {
|
|
450
544
|
id: step.step_id,
|
|
@@ -485,17 +579,18 @@ function deriveRunState(steps) {
|
|
|
485
579
|
.at(-1);
|
|
486
580
|
return { status: "completed", currentStepId: null, completedAt: completedAt ?? null };
|
|
487
581
|
}
|
|
488
|
-
/**
|
|
489
582
|
/**
|
|
490
583
|
* Build the default summary-validation judge from the configured LLM, or return
|
|
491
584
|
* `null` when no LLM is configured (gate is then skipped — fail-open). Lazily
|
|
492
585
|
* imports the client/config so the workflow engine has no hard LLM dependency.
|
|
586
|
+
*
|
|
587
|
+
* Exported for the engine loop (`exec/run-workflow.ts`), which wraps the judge
|
|
588
|
+
* to journal engine-driven gate evaluations as unit rows (addendum R2).
|
|
493
589
|
*/
|
|
494
|
-
function buildDefaultSummaryJudge() {
|
|
590
|
+
export function buildDefaultSummaryJudge() {
|
|
495
591
|
let llm;
|
|
496
592
|
try {
|
|
497
593
|
const config = loadConfig();
|
|
498
|
-
const { getDefaultLlmConfig } = require("../../core/config/config");
|
|
499
594
|
llm = getDefaultLlmConfig(config);
|
|
500
595
|
}
|
|
501
596
|
catch {
|
|
@@ -505,7 +600,7 @@ function buildDefaultSummaryJudge() {
|
|
|
505
600
|
return null;
|
|
506
601
|
const resolved = llm;
|
|
507
602
|
return async ({ system, user }) => {
|
|
508
|
-
const { chatCompletion } =
|
|
603
|
+
const { chatCompletion } = await import("../../llm/client.js");
|
|
509
604
|
return chatCompletion(resolved, [
|
|
510
605
|
{ role: "system", content: system },
|
|
511
606
|
{ role: "user", content: user },
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import { GATE_EVALUATION_PHASE } from "../exec/step-work.js";
|
|
5
|
+
/**
|
|
6
|
+
* Default staleness window. A unit claimed `running` whose last heartbeat (or,
|
|
7
|
+
* absent any heartbeat, its first claim) is older than this is surfaced as
|
|
8
|
+
* stale. Matches the run-level {@link ../runtime/checkin.CHECKIN_STALL_MS}.
|
|
9
|
+
*/
|
|
10
|
+
export const UNIT_STALE_MS = 90_000;
|
|
11
|
+
function parseIso(value) {
|
|
12
|
+
if (!value)
|
|
13
|
+
return null;
|
|
14
|
+
const ms = Date.parse(value);
|
|
15
|
+
return Number.isNaN(ms) ? null : ms;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pure stale-unit evaluator. Returns every `running` DISPATCH unit whose last
|
|
19
|
+
* heartbeat (`last_checkin_at`, else the first-claim `started_at`) is older than
|
|
20
|
+
* `staleMs`. Gate-evaluation rows (`phase = "gate"`) are excluded — they are
|
|
21
|
+
* synchronous engine judge calls, never driver-claimed work. A running row with
|
|
22
|
+
* no usable timestamp at all is treated as stale (a claim we can no longer age).
|
|
23
|
+
* Deterministic in `now` — free of timer flakiness.
|
|
24
|
+
*/
|
|
25
|
+
export function evaluateStaleUnits(rows, now = Date.now(), staleMs = UNIT_STALE_MS) {
|
|
26
|
+
const stale = [];
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
if (row.status !== "running")
|
|
29
|
+
continue;
|
|
30
|
+
if (row.phase === GATE_EVALUATION_PHASE)
|
|
31
|
+
continue;
|
|
32
|
+
const lastSeenAt = row.last_checkin_at ?? row.started_at;
|
|
33
|
+
const lastSeen = parseIso(lastSeenAt);
|
|
34
|
+
const idleMs = lastSeen === null ? Number.POSITIVE_INFINITY : now - lastSeen;
|
|
35
|
+
if (idleMs < staleMs)
|
|
36
|
+
continue;
|
|
37
|
+
stale.push({
|
|
38
|
+
unitId: row.unit_id,
|
|
39
|
+
stepId: row.step_id,
|
|
40
|
+
idleMs,
|
|
41
|
+
lastSeenAt: lastSeenAt ?? null,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return stale;
|
|
45
|
+
}
|