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
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Serialized writer queue for `workflow_run_units` (orchestration plan,
|
|
6
|
+
* *Persistence changes*).
|
|
7
|
+
*
|
|
8
|
+
* `withWorkflowRunsRepo` opens a fresh SQLite connection per call, so N
|
|
9
|
+
* parallel units completing at once would contend on SQLite's single writer
|
|
10
|
+
* and burn the 30 s busy_timeout. Bun is single-threaded, so a promise-chained
|
|
11
|
+
* in-process queue is sufficient: every unit write is appended to one chain
|
|
12
|
+
* and executes strictly in enqueue order. Reads and gate evaluation stay OFF
|
|
13
|
+
* this queue — only writes serialize.
|
|
14
|
+
*
|
|
15
|
+
* A failed write rejects its own caller but never wedges the chain.
|
|
16
|
+
*/
|
|
17
|
+
let tail = Promise.resolve();
|
|
18
|
+
export function enqueueUnitWrite(fn) {
|
|
19
|
+
const run = tail.then(() => fn());
|
|
20
|
+
// Keep the chain alive regardless of individual outcomes.
|
|
21
|
+
tail = run.then(() => undefined, () => undefined);
|
|
22
|
+
return run;
|
|
23
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* `akm workflow watch <run>` — run-scoped event tail (redesign addendum R2).
|
|
6
|
+
*
|
|
7
|
+
* Prints the run's `workflow_*` / `workflow_unit_*` events from the state.db
|
|
8
|
+
* `events` table (matched on `metadata.runId`) as NDJSON — one
|
|
9
|
+
* {@link EventEnvelope} per line — and exits. With `stream: true` it keeps
|
|
10
|
+
* polling from the last seen event id (monotonic rowid cursor, so concurrent
|
|
11
|
+
* writers can never cause skips) and exits when the run reaches a terminal
|
|
12
|
+
* status.
|
|
13
|
+
*
|
|
14
|
+
* Design constraints (pinned R2 decisions):
|
|
15
|
+
* - FOREGROUND poll loop only — no daemon, no background process, no
|
|
16
|
+
* `tailEvents` subscription held past the terminal status.
|
|
17
|
+
* - Reads go through the existing events repository (via
|
|
18
|
+
* {@link readEvents}); this module issues no SQL of its own.
|
|
19
|
+
* - "Terminal" means any non-`active` run status (`completed`, `failed`,
|
|
20
|
+
* `blocked`): in all three the engine has stopped driving, so no further
|
|
21
|
+
* events arrive until a human resumes the run. The status is read BEFORE
|
|
22
|
+
* each drain, so events written before the status flip are always
|
|
23
|
+
* emitted before the loop exits. The engine commits the status flip
|
|
24
|
+
* (workflow.db transaction) BEFORE it appends the terminal
|
|
25
|
+
* `workflow_step_completed` / `workflow_finished` events to state.db, so
|
|
26
|
+
* after first observing a terminal status the loop keeps performing
|
|
27
|
+
* grace polls (sleep + drain) until one drains nothing new — the
|
|
28
|
+
* terminal events landing in that commit→append window are never
|
|
29
|
+
* dropped.
|
|
30
|
+
* - Event lines are the raw envelopes (ids/status metadata only — event
|
|
31
|
+
* emitters never journal workflow-authored content, 07 P1-B rule).
|
|
32
|
+
*/
|
|
33
|
+
import { NotFoundError } from "../../core/errors.js";
|
|
34
|
+
import { readEvents } from "../../core/events.js";
|
|
35
|
+
import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
36
|
+
/** Default `--stream` poll interval (ms). */
|
|
37
|
+
export const DEFAULT_WATCH_INTERVAL_MS = 1000;
|
|
38
|
+
/**
|
|
39
|
+
* True when `event` belongs to workflow run `runId`: the event type is in the
|
|
40
|
+
* `workflow_*` family (which includes `workflow_unit_*`) AND the metadata
|
|
41
|
+
* carries a matching `runId`. Events of other families that happen to carry
|
|
42
|
+
* a `runId` (e.g. `llm_usage`) are excluded by design.
|
|
43
|
+
*/
|
|
44
|
+
export function isWorkflowRunEvent(event, runId) {
|
|
45
|
+
if (!event.eventType.startsWith("workflow_"))
|
|
46
|
+
return false;
|
|
47
|
+
return event.metadata?.runId === runId;
|
|
48
|
+
}
|
|
49
|
+
/** Default status reader — repository lookup; a missing run is a structured not-found. */
|
|
50
|
+
async function readRunStatus(runId) {
|
|
51
|
+
return withWorkflowRunsRepo((repo) => {
|
|
52
|
+
const row = repo.getRunById(runId);
|
|
53
|
+
if (!row) {
|
|
54
|
+
throw new NotFoundError(`Workflow run "${runId}" not found.`, "WORKFLOW_NOT_FOUND", "Run `akm workflow list --active` to see runs.");
|
|
55
|
+
}
|
|
56
|
+
return row.status;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Watch one workflow run's events. Emits each matching event envelope as a
|
|
61
|
+
* single NDJSON line via `emit`, then returns a summary. See the module doc
|
|
62
|
+
* for the backlog/stream/terminal-status contract.
|
|
63
|
+
*/
|
|
64
|
+
export async function watchWorkflowRun(options) {
|
|
65
|
+
const intervalMs = options.intervalMs ?? DEFAULT_WATCH_INTERVAL_MS;
|
|
66
|
+
const emit = options.emit ?? ((line) => process.stdout.write(`${line}\n`));
|
|
67
|
+
const read = options.readEventsFn ?? ((readOptions) => readEvents(readOptions));
|
|
68
|
+
const getRunStatus = options.getRunStatus ?? readRunStatus;
|
|
69
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
70
|
+
// Existence check first so an unknown run id is a structured error, not an
|
|
71
|
+
// empty NDJSON stream.
|
|
72
|
+
let status = await getRunStatus(options.runId);
|
|
73
|
+
let eventCount = 0;
|
|
74
|
+
let cursor = 0;
|
|
75
|
+
const drain = () => {
|
|
76
|
+
const { events, nextOffset } = read({ sinceOffset: cursor });
|
|
77
|
+
cursor = nextOffset;
|
|
78
|
+
for (const event of events) {
|
|
79
|
+
if (!isWorkflowRunEvent(event, options.runId))
|
|
80
|
+
continue;
|
|
81
|
+
emit(JSON.stringify(event));
|
|
82
|
+
eventCount++;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Backlog: everything already journaled for this run.
|
|
86
|
+
drain();
|
|
87
|
+
if (options.stream === true) {
|
|
88
|
+
// Foreground poll loop (NO daemon). Status is re-read BEFORE each drain
|
|
89
|
+
// so events written before a terminal flip are emitted before exit.
|
|
90
|
+
while (status === "active") {
|
|
91
|
+
await sleep(intervalMs);
|
|
92
|
+
status = await getRunStatus(options.runId);
|
|
93
|
+
drain();
|
|
94
|
+
}
|
|
95
|
+
// Terminal grace polls: completeWorkflowStep commits the run-status flip
|
|
96
|
+
// (workflow.db) BEFORE appending workflow_step_completed /
|
|
97
|
+
// workflow_finished to state.db, so the drain that first observed the
|
|
98
|
+
// terminal status can predate those events. Keep sleeping + draining
|
|
99
|
+
// until an idle poll (a drain that emits nothing new for this run) —
|
|
100
|
+
// the engine has stopped driving, so this converges after the terminal
|
|
101
|
+
// events land (already-terminal runs pay exactly one idle poll).
|
|
102
|
+
let emittedBefore;
|
|
103
|
+
do {
|
|
104
|
+
emittedBefore = eventCount;
|
|
105
|
+
await sleep(intervalMs);
|
|
106
|
+
drain();
|
|
107
|
+
} while (eventCount > emittedBefore);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
runId: options.runId,
|
|
111
|
+
status,
|
|
112
|
+
eventCount,
|
|
113
|
+
lastEventId: cursor,
|
|
114
|
+
streamed: options.stream === true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Git worktree lifecycle for `isolation: worktree` units (redesign addendum,
|
|
6
|
+
* R2). Parallel file-mutating units on the agent/sdk runners each get a
|
|
7
|
+
* fresh DETACHED worktree of the run's base repository under a run-scoped
|
|
8
|
+
* tmp directory, so concurrent units can never trample each other's working
|
|
9
|
+
* tree. Lifecycle (driven by the native executor per journaled attempt):
|
|
10
|
+
*
|
|
11
|
+
* 1. {@link assertGitWorkTree} — preflight, once per step: a non-git base
|
|
12
|
+
* directory fails the step cleanly before anything dispatches.
|
|
13
|
+
* 2. {@link createUnitWorktree} — `git worktree add --detach` into
|
|
14
|
+
* `<tmp>/akm-worktrees/<runId>/<attemptId>`; the path is journaled on
|
|
15
|
+
* the unit row (`workflow_run_units.worktree_path`, migration 004) and
|
|
16
|
+
* passed to dispatch as the unit's cwd.
|
|
17
|
+
* 3. {@link cleanupUnitWorktree} — after the unit finishes:
|
|
18
|
+
* `git status --porcelain` CLEAN → the worktree is removed;
|
|
19
|
+
* DIRTY → it is RETAINED (the caller logs the path) so uncollected work
|
|
20
|
+
* is never destroyed.
|
|
21
|
+
*
|
|
22
|
+
* What "uncollected work" means (the honest contract): the clean probe is
|
|
23
|
+
* `git status --porcelain` WITHOUT `--ignored`, so it counts tracked-file
|
|
24
|
+
* modifications and untracked *unignored* files, but NOT files the base repo's
|
|
25
|
+
* own `.gitignore` matches (build outputs, caches, logs, dependency dirs such
|
|
26
|
+
* as `node_modules`/`dist`). Those ignored files are DISPOSABLE BY DEFINITION
|
|
27
|
+
* — the repository already declares them regenerable — so a worktree whose only
|
|
28
|
+
* residue is ignored files probes clean and IS removed. This is deliberate:
|
|
29
|
+
* adding `--ignored` would retain a worktree after essentially every unit that
|
|
30
|
+
* ran a package install or a build (the ignored `node_modules`/`dist` tree),
|
|
31
|
+
* blowing up disk under the run-scoped tmp root. Work a unit needs preserved
|
|
32
|
+
* must therefore be tracked or untracked-unignored; anything the workflow
|
|
33
|
+
* repo has chosen to `.gitignore` is treated as throwaway.
|
|
34
|
+
*
|
|
35
|
+
* All git invocations are `spawnSync` (the repo-wide pattern for git
|
|
36
|
+
* shell-outs) with explicit timeouts; this module never throws — every
|
|
37
|
+
* operation returns a result object so the executor maps failures onto its
|
|
38
|
+
* own step/unit failure vocabulary.
|
|
39
|
+
*/
|
|
40
|
+
import { spawnSync } from "node:child_process";
|
|
41
|
+
import fs from "node:fs";
|
|
42
|
+
import os from "node:os";
|
|
43
|
+
import path from "node:path";
|
|
44
|
+
const GIT_TIMEOUT_MS = 30_000;
|
|
45
|
+
/** Run one git command; `ok` = exit 0. Never throws (spawn errors → ok: false). */
|
|
46
|
+
function git(cwd, args) {
|
|
47
|
+
const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf8", timeout: GIT_TIMEOUT_MS });
|
|
48
|
+
if (result.error) {
|
|
49
|
+
return { ok: false, stdout: "", error: `git ${args[0]} failed to spawn: ${result.error.message}` };
|
|
50
|
+
}
|
|
51
|
+
if (result.status !== 0) {
|
|
52
|
+
const detail = (result.stderr || result.stdout || "").trim();
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
stdout: result.stdout ?? "",
|
|
56
|
+
error: `git ${args.join(" ")} exited ${result.status}${detail ? `: ${detail}` : ""}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, stdout: result.stdout ?? "" };
|
|
60
|
+
}
|
|
61
|
+
/** True when a usable `git` binary is on PATH (tests skip gracefully without one). */
|
|
62
|
+
export function isGitAvailable() {
|
|
63
|
+
const result = spawnSync("git", ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
64
|
+
return !result.error && result.status === 0;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Preflight for worktree isolation: `dir` must be inside a git work tree.
|
|
68
|
+
* Returns an error message (for a clean step failure) or undefined when ok.
|
|
69
|
+
* A missing git binary reports as the same clean failure — a workflow that
|
|
70
|
+
* declares isolation cannot run without git.
|
|
71
|
+
*/
|
|
72
|
+
export function assertGitWorkTree(dir) {
|
|
73
|
+
const result = git(dir, ["rev-parse", "--is-inside-work-tree"]);
|
|
74
|
+
if (!result.ok) {
|
|
75
|
+
return `"${dir}" is not a git repository (isolation: worktree requires one): ${result.error}`;
|
|
76
|
+
}
|
|
77
|
+
if (result.stdout.trim() !== "true") {
|
|
78
|
+
return `"${dir}" is not inside a git work tree (isolation: worktree requires one).`;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/** Journal-safe directory name for a unit attempt id (ids carry `:` / `~`). */
|
|
83
|
+
function sanitizeAttemptId(attemptId) {
|
|
84
|
+
return attemptId.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
85
|
+
}
|
|
86
|
+
/** Run-scoped parent directory for all of one run's unit worktrees. */
|
|
87
|
+
export function runWorktreeRoot(runId) {
|
|
88
|
+
return path.join(os.tmpdir(), "akm-worktrees", runId);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Move a leftover attempt directory aside to `<dest>.retained-<ts>[-n]`
|
|
92
|
+
* (never overwriting an earlier retained copy). Throws on fs errors — the
|
|
93
|
+
* caller maps them onto its result object.
|
|
94
|
+
*/
|
|
95
|
+
function moveLeftoverAside(dest) {
|
|
96
|
+
const base = `${dest}.retained-${Date.now()}`;
|
|
97
|
+
let aside = base;
|
|
98
|
+
for (let n = 1; fs.existsSync(aside); n++)
|
|
99
|
+
aside = `${base}-${n}`;
|
|
100
|
+
fs.renameSync(dest, aside);
|
|
101
|
+
return aside;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create a fresh DETACHED worktree of `baseDir`'s repository at
|
|
105
|
+
* `<tmp>/akm-worktrees/<runId>/<attemptId>` (detached HEAD — no branch is
|
|
106
|
+
* minted, so parallel units cannot collide on branch names).
|
|
107
|
+
*
|
|
108
|
+
* A leftover directory at the attempt path (a RETAINED dirty worktree from a
|
|
109
|
+
* prior invocation, or a crashed attempt's partial state) is handled with the
|
|
110
|
+
* same never-destroy-unverified-work rule as {@link cleanupUnitWorktree}:
|
|
111
|
+
* `git status --porcelain` CLEAN → removed; DIRTY or unverifiable (the probe
|
|
112
|
+
* fails — e.g. a half-created directory that is no longer a valid worktree)
|
|
113
|
+
* → moved aside to `<dest>.retained-<ts>` and reported via
|
|
114
|
+
* `preservedLeftover` so the caller can log where the work went. Either way
|
|
115
|
+
* `git worktree prune` clears the stale registration before re-creating.
|
|
116
|
+
*/
|
|
117
|
+
export function createUnitWorktree(baseDir, runId, attemptId) {
|
|
118
|
+
const dest = path.join(runWorktreeRoot(runId), sanitizeAttemptId(attemptId));
|
|
119
|
+
let preservedLeftover;
|
|
120
|
+
try {
|
|
121
|
+
if (fs.existsSync(dest)) {
|
|
122
|
+
const status = git(dest, ["status", "--porcelain"]);
|
|
123
|
+
if (status.ok && status.stdout.trim() === "") {
|
|
124
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
preservedLeftover = moveLeftoverAside(dest);
|
|
128
|
+
}
|
|
129
|
+
git(baseDir, ["worktree", "prune"]);
|
|
130
|
+
}
|
|
131
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
return { ok: false, error: `could not prepare worktree directory ${dest}: ${message(err)}` };
|
|
135
|
+
}
|
|
136
|
+
const added = git(baseDir, ["worktree", "add", "--detach", dest]);
|
|
137
|
+
if (!added.ok) {
|
|
138
|
+
return { ok: false, error: `could not create isolation worktree at ${dest}: ${added.error}` };
|
|
139
|
+
}
|
|
140
|
+
return { ok: true, path: dest, ...(preservedLeftover !== undefined ? { preservedLeftover } : {}) };
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Post-unit cleanup: remove the worktree when `git status --porcelain` shows
|
|
144
|
+
* it clean; retain it (dirty: true) when the unit left uncommitted work —
|
|
145
|
+
* the caller logs the retained path. Any git failure retains the worktree
|
|
146
|
+
* too (never destroy a tree whose state could not be verified).
|
|
147
|
+
*
|
|
148
|
+
* The probe deliberately omits `--ignored`: a worktree whose only residue is
|
|
149
|
+
* files matched by the base repo's `.gitignore` (build artifacts, caches,
|
|
150
|
+
* logs, `node_modules`) probes clean and IS removed. Those files are disposable
|
|
151
|
+
* by the repo's own declaration; retaining a worktree per build/install would
|
|
152
|
+
* blow up disk. "Uncollected work" the caller preserves is therefore
|
|
153
|
+
* tracked-or-untracked-unignored changes only (module doc).
|
|
154
|
+
*/
|
|
155
|
+
export function cleanupUnitWorktree(baseDir, worktreePath) {
|
|
156
|
+
const status = git(worktreePath, ["status", "--porcelain"]);
|
|
157
|
+
if (!status.ok) {
|
|
158
|
+
return { removed: false, dirty: false, error: status.error };
|
|
159
|
+
}
|
|
160
|
+
if (status.stdout.trim() !== "") {
|
|
161
|
+
return { removed: false, dirty: true };
|
|
162
|
+
}
|
|
163
|
+
const removed = git(baseDir, ["worktree", "remove", worktreePath]);
|
|
164
|
+
if (!removed.ok) {
|
|
165
|
+
return { removed: false, dirty: false, error: removed.error };
|
|
166
|
+
}
|
|
167
|
+
return { removed: true, dirty: false };
|
|
168
|
+
}
|
|
169
|
+
function message(err) {
|
|
170
|
+
return err instanceof Error ? err.message : String(err);
|
|
171
|
+
}
|