akm-cli 0.9.1 → 0.9.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
package/dist/tasks/runner.js
CHANGED
|
@@ -5,37 +5,24 @@
|
|
|
5
5
|
* `akm task run <id>` — what cron / launchd / schtasks invoke at the
|
|
6
6
|
* scheduled moment.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* validation error that the user already knows about.)
|
|
15
|
-
* 3. Skip disabled tasks only when the invocation is scheduler-generated;
|
|
16
|
-
* explicit manual runs are allowed for catch-up and testing.
|
|
17
|
-
* 4. Dispatch by target kind:
|
|
18
|
-
* • workflow → `runWorkflowSteps({ target: ref, params, signal, … })`
|
|
19
|
-
* under a whole-run timeout (issue 11): an unattended run
|
|
20
|
-
* gets the same abort path `akm workflow run --timeout`
|
|
21
|
-
* gives an interactive one.
|
|
22
|
-
* • prompt → `executeRunner(engine, prompt, { stdio: "captured" })`
|
|
23
|
-
* 5. Capture stdout / stderr as structured rows in logs.db (task_logs) and,
|
|
24
|
-
* transitionally, as a flat text tail at `<cacheDir>/tasks/logs/<id>/<ts>.log`
|
|
25
|
-
* (per the #579 logs audit).
|
|
26
|
-
* 6. Write a history row to state.db task_history table.
|
|
8
|
+
* The durable boundary is intentional: id/bundle resolution, source read,
|
|
9
|
+
* strict v3 parsing, target resolution, command authorization/lowering,
|
|
10
|
+
* workflow projectability, and frozen script-byte capture all finish before
|
|
11
|
+
* an attempt is reserved or a log is created. Once prepared, the runner skips
|
|
12
|
+
* disabled scheduler firings or dispatches the immutable command, workflow,
|
|
13
|
+
* shell, or script projection and records that actual attempt.
|
|
27
14
|
*
|
|
28
15
|
* Returns a structured result so the CLI handler can shape it for `output()`
|
|
29
16
|
* and so tests can assert against it without scraping stdout.
|
|
30
17
|
*/
|
|
31
18
|
import fs from "node:fs";
|
|
32
|
-
import os from "node:os";
|
|
33
19
|
import path from "node:path";
|
|
20
|
+
import { dispatchPreparedCommandInvocation } from "../commands/command/command-execution.js";
|
|
34
21
|
import { armAbortDeadline } from "../core/abort-deadline.js";
|
|
35
22
|
import { shouldSkipUnactivatedTask } from "../core/activation-policy.js";
|
|
23
|
+
import { detectAdapterId } from "../core/adapter/detect-adapter.js";
|
|
36
24
|
import { assertNever } from "../core/assert.js";
|
|
37
|
-
import {
|
|
38
|
-
import { parseRefInput } from "../core/asset/resolve-ref.js";
|
|
25
|
+
import { makeBundleRef } from "../core/asset/asset-ref.js";
|
|
39
26
|
import { loadConfig } from "../core/config/config.js";
|
|
40
27
|
import { AkmError, NotFoundError, rethrowIfTestIsolationError } from "../core/errors.js";
|
|
41
28
|
import { buildTaskRunId, insertTaskLogLines, openLogsDatabase, } from "../core/logs-db.js";
|
|
@@ -43,84 +30,79 @@ import { getTaskLogDir } from "../core/paths.js";
|
|
|
43
30
|
import { redactCredentialPatterns, redactSensitiveText } from "../core/redaction.js";
|
|
44
31
|
import { withStateDb } from "../core/state-db.js";
|
|
45
32
|
import { runManagedSubprocess } from "../core/subprocess.js";
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import { executeRunner } from "../integrations/agent/runner-dispatch.js";
|
|
50
|
-
import { chatCompletion } from "../llm/client.js";
|
|
33
|
+
import { resolveWriteTarget } from "../core/write-source.js";
|
|
34
|
+
import { assertFrozenDirectoryIdentity } from "../execution/directory-identity.js";
|
|
35
|
+
import { resolveAdapterConceptOwner } from "../indexer/lookup/adapter-concept-owner.js";
|
|
51
36
|
import { resolveAssetPath } from "../sources/resolve.js";
|
|
52
37
|
import { decodeTaskHistoryMetadata, finalizeTaskHistoryAttempt, getTaskHistory, getTaskHistoryRuns, queryTaskHistory, reserveTaskHistoryAttempt, upsertTaskHistory, } from "../storage/repositories/task-history-repository.js";
|
|
53
38
|
import { runWorkflowSteps } from "../workflows/exec/run-workflow.js";
|
|
54
|
-
import {
|
|
39
|
+
import { cleanupFrozenScript, frozenScriptCommand, materializeFrozenScript } from "./frozen-script.js";
|
|
55
40
|
import { collectTaskLogSensitiveValues } from "./log-redaction.js";
|
|
56
|
-
import { parseTaskDocument } from "./parser.js";
|
|
57
41
|
import { resolveAkmInvocation } from "./resolve-akm-bin.js";
|
|
42
|
+
import { prepareTaskV3Execution, } from "./runtime-v3.js";
|
|
58
43
|
import { scheduledTaskContextEnv } from "./scheduler-invocation.js";
|
|
59
|
-
import {
|
|
44
|
+
import { parseTaskV3Yaml } from "./source-v3.js";
|
|
45
|
+
import { validateTaskConceptId, validateTaskId } from "./task-id.js";
|
|
60
46
|
export const INVALID_TASK_ATTEMPT_ID = "_invalid-task-id";
|
|
47
|
+
const CONFIG_FREE_TASK_RUNTIME = Object.freeze({
|
|
48
|
+
configVersion: "0.9.0",
|
|
49
|
+
semanticSearchMode: "off",
|
|
50
|
+
});
|
|
61
51
|
export async function runTask(id, options) {
|
|
62
|
-
const runAgentImpl = options.runAgentImpl;
|
|
63
52
|
const runWorkflowStepsImpl = options.runWorkflowStepsImpl ?? runWorkflowSteps;
|
|
64
53
|
const now = options.now ?? (() => new Date());
|
|
65
54
|
const requestedStartedAt = now();
|
|
66
|
-
|
|
55
|
+
const stashDir = options.stashDir;
|
|
56
|
+
const adapterId = options.adapterId ?? detectAdapterId(stashDir);
|
|
57
|
+
if (adapterId === "akm-task")
|
|
58
|
+
validateTaskConceptId(id);
|
|
59
|
+
else
|
|
67
60
|
validateTaskId(id);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
61
|
+
const taskConceptId = adapterId === "akm" ? `tasks/${id}` : id;
|
|
62
|
+
const owner = resolveAdapterConceptOwner(stashDir, adapterId, taskConceptId);
|
|
63
|
+
if (!owner) {
|
|
64
|
+
throw new NotFoundError(`Task ${JSON.stringify(id)} was not found in the configured ${JSON.stringify(adapterId)} component.`, "ASSET_NOT_FOUND");
|
|
65
|
+
}
|
|
66
|
+
const filePath = owner.path;
|
|
67
|
+
const yaml = fs.readFileSync(filePath, "utf8");
|
|
68
|
+
const source = parseTaskV3Yaml({ yaml, filePath, workspaceRoot: stashDir });
|
|
69
|
+
const requiresCommandConfig = source.target.kind === "uses" &&
|
|
70
|
+
(source.target.uses.kind === "builtin-command" || source.target.uses.kind === "command");
|
|
71
|
+
const config = requiresCommandConfig ? loadConfig() : CONFIG_FREE_TASK_RUNTIME;
|
|
72
|
+
const bundleName = options.bundleName ?? config.defaultBundle ?? "stash";
|
|
73
|
+
const task = await prepareTaskV3Execution(source, {
|
|
74
|
+
taskId: id,
|
|
75
|
+
taskRef: makeBundleRef(bundleName, taskConceptId),
|
|
76
|
+
bundleName,
|
|
77
|
+
bundleRoot: stashDir,
|
|
78
|
+
config,
|
|
79
|
+
// Agent profiles build child env from an allowlist, so freeze the closed
|
|
80
|
+
// scheduler-restored AKM directory context before command preparation.
|
|
81
|
+
...(options.scheduled ? { schedulerContext: scheduledTaskContextEnv() } : {}),
|
|
82
|
+
resolveAsset: async ({ bundle, type, name }) => {
|
|
83
|
+
if (bundle === bundleName) {
|
|
84
|
+
return { file: await resolveAssetPath(stashDir, type, name), bundleRoot: stashDir };
|
|
85
|
+
}
|
|
86
|
+
const resolutionConfig = requiresCommandConfig ? config : loadConfig();
|
|
87
|
+
const resolvedBundle = resolveWriteTarget(resolutionConfig, bundle, { requireWritable: false });
|
|
88
|
+
return {
|
|
89
|
+
file: await resolveAssetPath(resolvedBundle.source.path, type, name),
|
|
90
|
+
bundleRoot: resolvedBundle.source.path,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
// All validation, parsing, source resolution, command cascade preparation,
|
|
95
|
+
// and frozen-byte capture above is non-mutating. Only a fully projectable
|
|
96
|
+
// task may reserve durable history or create a log.
|
|
82
97
|
const attempt = reserveTaskAttempt(id, requestedStartedAt);
|
|
83
98
|
const startedAt = attempt.startedAt;
|
|
84
|
-
|
|
99
|
+
const startedIso = startedAt.toISOString();
|
|
100
|
+
const logPath = resolveTaskLogPath(options.logDir, id, startedIso);
|
|
85
101
|
try {
|
|
86
|
-
const stashDir = options.stashDir;
|
|
87
|
-
const filePath = await resolveAssetPath(stashDir, "task", id);
|
|
88
|
-
const yaml = fs.readFileSync(filePath, "utf8");
|
|
89
|
-
failureReason = "task_parse_failed";
|
|
90
|
-
const task = parseTaskDocument({ yaml, filePath, id });
|
|
91
|
-
failureReason = "task_dispatch_failed";
|
|
92
|
-
const startedIso = startedAt.toISOString();
|
|
93
|
-
const logPath = resolveTaskLogPath(options.logDir, id, startedIso);
|
|
94
102
|
if (shouldSkipUnactivatedTask({ enabled: task.enabled, scheduled: options.scheduled === true })) {
|
|
95
|
-
|
|
96
|
-
const disabledTarget = task.target.kind === "workflow"
|
|
97
|
-
? { kind: "workflow", ref: task.target.ref }
|
|
98
|
-
: task.target.kind === "command"
|
|
99
|
-
? { kind: "command", cmd: task.target.cmd }
|
|
100
|
-
: { kind: "prompt", engine: task.target.engine ?? null };
|
|
101
|
-
const result = {
|
|
102
|
-
id,
|
|
103
|
-
status: "disabled",
|
|
104
|
-
startedAt: startedIso,
|
|
105
|
-
finishedAt: finishedAt.toISOString(),
|
|
106
|
-
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
107
|
-
log: logPath,
|
|
108
|
-
target: disabledTarget,
|
|
109
|
-
};
|
|
110
|
-
const disabledLine = `[akm task] task "${id}" is disabled — skipping run.`;
|
|
111
|
-
persistRunLog({
|
|
112
|
-
taskId: id,
|
|
113
|
-
startedAtIso: startedIso,
|
|
114
|
-
finishedAtIso: result.finishedAt,
|
|
115
|
-
logPath,
|
|
116
|
-
fileText: `${disabledLine}\n`,
|
|
117
|
-
dbLines: [{ line: disabledLine }],
|
|
118
|
-
redactNames: task.redact,
|
|
119
|
-
});
|
|
120
|
-
appendHistory(result, attempt.historyReserved);
|
|
121
|
-
return result;
|
|
103
|
+
return finishDisabledTask(task, logPath, startedAt, now(), attempt.historyReserved);
|
|
122
104
|
}
|
|
123
|
-
if (task.
|
|
105
|
+
if (task.kind === "workflow") {
|
|
124
106
|
return await runWorkflowTask({
|
|
125
107
|
task,
|
|
126
108
|
logPath,
|
|
@@ -132,34 +114,34 @@ export async function runTask(id, options) {
|
|
|
132
114
|
...(options.clearTimeoutFn ? { clearTimeoutFn: options.clearTimeoutFn } : {}),
|
|
133
115
|
});
|
|
134
116
|
}
|
|
135
|
-
if (task.
|
|
136
|
-
return await
|
|
117
|
+
if (task.kind === "command") {
|
|
118
|
+
return await runPreparedCommandTask({
|
|
137
119
|
task,
|
|
138
120
|
logPath,
|
|
139
121
|
startedAt,
|
|
140
122
|
now,
|
|
141
123
|
historyReserved: attempt.historyReserved,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
124
|
+
runAgentImpl: options.runAgentImpl,
|
|
125
|
+
agentOptions: options.agentOptions,
|
|
126
|
+
chatCompletionImpl: options.chatCompletionImpl,
|
|
145
127
|
});
|
|
146
128
|
}
|
|
147
|
-
|
|
129
|
+
options.beforeNativeDispatch?.(task);
|
|
130
|
+
return await runNativeTask({
|
|
148
131
|
task,
|
|
149
|
-
stashDir,
|
|
150
132
|
logPath,
|
|
151
133
|
startedAt,
|
|
152
134
|
now,
|
|
153
|
-
runAgentImpl,
|
|
154
|
-
agentOptions: options.agentOptions,
|
|
155
|
-
chatCompletionImpl: options.chatCompletionImpl ?? chatCompletion,
|
|
156
135
|
historyReserved: attempt.historyReserved,
|
|
136
|
+
...(options.spawnFn ? { spawnFn: options.spawnFn } : {}),
|
|
137
|
+
...(options.setTimeoutFn ? { setTimeoutFn: options.setTimeoutFn } : {}),
|
|
138
|
+
...(options.clearTimeoutFn ? { clearTimeoutFn: options.clearTimeoutFn } : {}),
|
|
157
139
|
});
|
|
158
140
|
}
|
|
159
141
|
catch (failure) {
|
|
160
142
|
recordTaskAttemptFailure({
|
|
161
143
|
taskId: id,
|
|
162
|
-
reason:
|
|
144
|
+
reason: "task_dispatch_failed",
|
|
163
145
|
failure,
|
|
164
146
|
startedAt,
|
|
165
147
|
finishedAt: now(),
|
|
@@ -169,31 +151,121 @@ export async function runTask(id, options) {
|
|
|
169
151
|
throw failure;
|
|
170
152
|
}
|
|
171
153
|
}
|
|
172
|
-
|
|
173
|
-
|
|
154
|
+
function preparedResultTarget(task) {
|
|
155
|
+
if (task.kind === "workflow")
|
|
156
|
+
return { kind: "workflow", ref: task.ref };
|
|
157
|
+
if (task.kind === "command")
|
|
158
|
+
return { kind: "prompt", engine: task.invocation.request.engine.name ?? null };
|
|
159
|
+
return { kind: "command" };
|
|
160
|
+
}
|
|
161
|
+
function finishDisabledTask(task, logPath, startedAt, observedFinishedAt, historyReserved) {
|
|
162
|
+
const finishedAt = finishAttempt(startedAt, observedFinishedAt);
|
|
163
|
+
const line = `[akm task] task "${task.taskId}" is disabled — skipping run.`;
|
|
164
|
+
const result = {
|
|
165
|
+
id: task.taskId,
|
|
166
|
+
status: "disabled",
|
|
167
|
+
startedAt: startedAt.toISOString(),
|
|
168
|
+
finishedAt: finishedAt.toISOString(),
|
|
169
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
170
|
+
log: logPath,
|
|
171
|
+
target: preparedResultTarget(task),
|
|
172
|
+
};
|
|
173
|
+
persistRunLog({
|
|
174
|
+
taskId: task.taskId,
|
|
175
|
+
startedAtIso: result.startedAt,
|
|
176
|
+
finishedAtIso: result.finishedAt,
|
|
177
|
+
logPath,
|
|
178
|
+
fileText: `${line}\n`,
|
|
179
|
+
dbLines: [{ line }],
|
|
180
|
+
redactNames: task.redact,
|
|
181
|
+
environment: task.environment,
|
|
182
|
+
});
|
|
183
|
+
appendHistory(result, historyReserved);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
// ── shell and frozen-script targets ─────────────────────────────────────────
|
|
187
|
+
function shellCommand(task) {
|
|
188
|
+
const command = resolveLeadingBareAkmCommand(task.command, task.shell);
|
|
189
|
+
switch (task.shell) {
|
|
190
|
+
case "sh":
|
|
191
|
+
case "bash":
|
|
192
|
+
case "zsh":
|
|
193
|
+
return [task.shell, "-c", command];
|
|
194
|
+
case "pwsh":
|
|
195
|
+
case "powershell":
|
|
196
|
+
return [task.shell, "-NoProfile", "-NonInteractive", "-Command", command];
|
|
197
|
+
case "cmd":
|
|
198
|
+
return ["cmd", "/d", "/s", "/c", command];
|
|
199
|
+
default:
|
|
200
|
+
return assertNever(task.shell, "shellCommand");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Bind an unambiguous leading bare `akm` (including the task-v2 migrator's
|
|
205
|
+
* quoted form) to this installation. Explicit paths and arbitrary shell
|
|
206
|
+
* fragments remain author-controlled.
|
|
207
|
+
*/
|
|
208
|
+
function resolveLeadingBareAkmCommand(command, shell) {
|
|
209
|
+
const leadingBareAkm = /^(\s*)(?:akm(?:\.exe)?|'akm(?:\.exe)?'|"akm(?:\.exe)?")(?=$|[\s;|&])/i;
|
|
210
|
+
if (!leadingBareAkm.test(command))
|
|
211
|
+
return command;
|
|
212
|
+
const invocation = resolveAkmInvocation()
|
|
213
|
+
.argv.map((part) => quoteShellArgument(part, shell))
|
|
214
|
+
.join(" ");
|
|
215
|
+
return command.replace(leadingBareAkm, (_match, leadingWhitespace) => `${leadingWhitespace}${invocation}`);
|
|
216
|
+
}
|
|
217
|
+
function quoteShellArgument(value, shell) {
|
|
218
|
+
switch (shell) {
|
|
219
|
+
case "sh":
|
|
220
|
+
case "bash":
|
|
221
|
+
case "zsh":
|
|
222
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
223
|
+
case "pwsh":
|
|
224
|
+
case "powershell":
|
|
225
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
226
|
+
case "cmd":
|
|
227
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
228
|
+
default:
|
|
229
|
+
return assertNever(shell, "quoteShellArgument");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function runNativeTask(input) {
|
|
174
233
|
const { task, logPath, startedAt, now, historyReserved } = input;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const { cmd } = task.target;
|
|
178
|
-
const spawnCmd = resolveNestedAkmCommand(cmd);
|
|
234
|
+
let materialized;
|
|
235
|
+
let cmd = task.kind === "shell" ? shellCommand(task) : [];
|
|
179
236
|
// Unset → the unattended default; `null` → the explicit no-timeout opt-out.
|
|
180
237
|
const timeoutMs = task.timeoutMs !== undefined ? task.timeoutMs : DEFAULT_SCHEDULED_TASK_TIMEOUT_MS;
|
|
181
|
-
const header =
|
|
238
|
+
const header = task.kind === "shell"
|
|
239
|
+
? `[akm task] task=${task.taskId} kind=run shell=${task.shell}`
|
|
240
|
+
: `[akm task] task=${task.taskId} kind=script ref=${task.sourceRef} sha256=${task.sha256}`;
|
|
182
241
|
const logLines = [header];
|
|
183
242
|
const dbLines = [{ line: header }];
|
|
184
243
|
let exitCode = null;
|
|
185
244
|
try {
|
|
245
|
+
// The projector froze both canonical paths and filesystem identities before
|
|
246
|
+
// history mutation. Re-resolve the authored root/cwd immediately before
|
|
247
|
+
// spawn so a symlink, ancestor, bundle-root, or directory/file swap cannot
|
|
248
|
+
// redirect execution outside that physical workspace.
|
|
249
|
+
assertFrozenDirectoryIdentity(task.cwdIdentity);
|
|
250
|
+
if (task.kind === "script") {
|
|
251
|
+
materialized = materializeFrozenScript(task);
|
|
252
|
+
cmd = frozenScriptCommand(task, materialized.file);
|
|
253
|
+
}
|
|
186
254
|
// Managed spawn (src/core/subprocess.ts): process-GROUP kill so a timeout
|
|
187
255
|
// reaps the whole command tree (no orphans), and a SIGTERM→SIGKILL ladder
|
|
188
256
|
// so a child that ignores SIGTERM can't wedge the run forever.
|
|
189
|
-
const result = await runManagedSubprocess(
|
|
257
|
+
const result = await runManagedSubprocess(cmd, {
|
|
190
258
|
capture: true,
|
|
191
|
-
cwd:
|
|
259
|
+
cwd: task.cwd,
|
|
192
260
|
// Stamp task-runner provenance so any akm invocation in the command tree
|
|
193
261
|
// records usage events as machine traffic, not user demand (DRIFT-6).
|
|
194
262
|
// A more specific stamp already in the environment (e.g. improve's
|
|
195
263
|
// AKM_EVENT_SOURCE=improve on its child spawns) still wins in children.
|
|
196
|
-
env: {
|
|
264
|
+
env: {
|
|
265
|
+
...process.env,
|
|
266
|
+
...task.environment,
|
|
267
|
+
AKM_EVENT_SOURCE: process.env.AKM_EVENT_SOURCE ?? "task",
|
|
268
|
+
},
|
|
197
269
|
timeoutMs,
|
|
198
270
|
...(input.spawnFn ? { spawnFn: input.spawnFn } : {}),
|
|
199
271
|
...(input.setTimeoutFn ? { setTimeoutFn: input.setTimeoutFn } : {}),
|
|
@@ -227,19 +299,24 @@ async function runCommandTask(input) {
|
|
|
227
299
|
dbLines.push({ level: "error", line: `spawn_error=${msg}` });
|
|
228
300
|
exitCode = 1;
|
|
229
301
|
}
|
|
302
|
+
finally {
|
|
303
|
+
if (materialized)
|
|
304
|
+
cleanupFrozenScript(materialized);
|
|
305
|
+
}
|
|
230
306
|
const finishedAt = finishAttempt(startedAt, now());
|
|
231
307
|
persistRunLog({
|
|
232
|
-
taskId: task.
|
|
308
|
+
taskId: task.taskId,
|
|
233
309
|
startedAtIso: startedAt.toISOString(),
|
|
234
310
|
finishedAtIso: finishedAt.toISOString(),
|
|
235
311
|
logPath,
|
|
236
312
|
fileText: `${logLines.join("\n")}\n`,
|
|
237
313
|
dbLines,
|
|
238
314
|
redactNames: task.redact,
|
|
315
|
+
environment: task.environment,
|
|
239
316
|
});
|
|
240
317
|
const status = exitCode === 0 ? "completed" : "failed";
|
|
241
318
|
const result = {
|
|
242
|
-
id: task.
|
|
319
|
+
id: task.taskId,
|
|
243
320
|
status,
|
|
244
321
|
startedAt: startedAt.toISOString(),
|
|
245
322
|
finishedAt: finishedAt.toISOString(),
|
|
@@ -251,13 +328,6 @@ async function runCommandTask(input) {
|
|
|
251
328
|
appendHistory(result, historyReserved);
|
|
252
329
|
return result;
|
|
253
330
|
}
|
|
254
|
-
/** Avoid a second PATH lookup when a task invokes the same AKM installation. */
|
|
255
|
-
function resolveNestedAkmCommand(cmd) {
|
|
256
|
-
const akmIndex = findBareAkmExecutableIndex(cmd);
|
|
257
|
-
if (akmIndex === undefined)
|
|
258
|
-
return cmd;
|
|
259
|
-
return [...cmd.slice(0, akmIndex), ...resolveAkmInvocation().argv, ...cmd.slice(akmIndex + 1)];
|
|
260
|
-
}
|
|
261
331
|
// ── workflow target ─────────────────────────────────────────────────────────
|
|
262
332
|
/**
|
|
263
333
|
* Whole-run timeout applied to a workflow-bound task that does not declare its
|
|
@@ -296,15 +366,8 @@ export const DEFAULT_WORKFLOW_TASK_TIMEOUT_MS = 6 * 60 * 60 * 1000;
|
|
|
296
366
|
export const DEFAULT_SCHEDULED_TASK_TIMEOUT_MS = DEFAULT_WORKFLOW_TASK_TIMEOUT_MS;
|
|
297
367
|
async function runWorkflowTask(input) {
|
|
298
368
|
const { task, logPath, startedAt, now, runWorkflowStepsImpl, historyReserved } = input;
|
|
299
|
-
if (task.target.kind !== "workflow")
|
|
300
|
-
throw new Error("invariant: workflow target");
|
|
301
|
-
const workflowTarget = task.target;
|
|
302
|
-
const ref = parseRefInput(workflowTarget.ref);
|
|
303
|
-
if (ref.type !== "workflow") {
|
|
304
|
-
throw new NotFoundError(`Task "${task.id}" workflow target must be a workflow ref (got "${workflowTarget.ref}").`, "WORKFLOW_NOT_FOUND");
|
|
305
|
-
}
|
|
306
369
|
// Unset → the unattended default; `null` → the explicit no-timeout opt-out.
|
|
307
|
-
const timeoutMs =
|
|
370
|
+
const timeoutMs = task.timeoutMs === undefined ? DEFAULT_WORKFLOW_TASK_TIMEOUT_MS : task.timeoutMs;
|
|
308
371
|
// The shared deadline `akm workflow run --timeout` also arms
|
|
309
372
|
// ({@link armAbortDeadline}): one AbortController for the run's lifetime,
|
|
310
373
|
// aborted by a timer. The engine reads `options.signal` at every step
|
|
@@ -314,7 +377,7 @@ async function runWorkflowTask(input) {
|
|
|
314
377
|
const controller = new AbortController();
|
|
315
378
|
const deadline = armAbortDeadline(controller, {
|
|
316
379
|
timeoutMs,
|
|
317
|
-
reason: `Workflow task "${task.
|
|
380
|
+
reason: `Workflow task "${task.taskId}" timed out after ${timeoutMs}ms.`,
|
|
318
381
|
...(input.setTimeoutFn ? { setTimeoutFn: input.setTimeoutFn } : {}),
|
|
319
382
|
...(input.clearTimeoutFn ? { clearTimeoutFn: input.clearTimeoutFn } : {}),
|
|
320
383
|
});
|
|
@@ -333,11 +396,11 @@ async function runWorkflowTask(input) {
|
|
|
333
396
|
process.env.AKM_EVENT_SOURCE = priorEventSource ?? "task";
|
|
334
397
|
try {
|
|
335
398
|
const execution = await runWorkflowStepsImpl({
|
|
336
|
-
target:
|
|
337
|
-
params:
|
|
399
|
+
target: task.ref,
|
|
400
|
+
params: task.params,
|
|
338
401
|
signal: controller.signal,
|
|
339
|
-
...(
|
|
340
|
-
...(
|
|
402
|
+
...(task.maxSteps !== undefined ? { maxSteps: task.maxSteps } : {}),
|
|
403
|
+
...(task.maxRetries !== undefined ? { maxRetries: task.maxRetries } : {}),
|
|
341
404
|
});
|
|
342
405
|
detail = execution.run;
|
|
343
406
|
runWarnings = execution.warnings ?? [];
|
|
@@ -387,25 +450,26 @@ async function runWorkflowTask(input) {
|
|
|
387
450
|
...(timedOutAfterMs !== undefined ? { timedOutAfterMs } : {}),
|
|
388
451
|
});
|
|
389
452
|
persistRunLog({
|
|
390
|
-
taskId: task.
|
|
453
|
+
taskId: task.taskId,
|
|
391
454
|
startedAtIso: startedAt.toISOString(),
|
|
392
455
|
finishedAtIso: finishedAt.toISOString(),
|
|
393
456
|
logPath,
|
|
394
457
|
fileText: log.fileText,
|
|
395
458
|
dbLines: log.dbLines,
|
|
396
459
|
redactNames: task.redact,
|
|
460
|
+
environment: task.environment,
|
|
397
461
|
});
|
|
398
462
|
const result = {
|
|
399
|
-
id: task.
|
|
463
|
+
id: task.taskId,
|
|
400
464
|
status,
|
|
401
465
|
startedAt: startedAt.toISOString(),
|
|
402
466
|
finishedAt: finishedAt.toISOString(),
|
|
403
467
|
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
404
468
|
log: logPath,
|
|
405
|
-
target: { kind: "workflow", ref: task.
|
|
469
|
+
target: { kind: "workflow", ref: task.ref },
|
|
406
470
|
detail: {
|
|
407
471
|
runId: detail?.id,
|
|
408
|
-
...(failure ? { error: failure.message } : {}),
|
|
472
|
+
...(failure ? { error: scrubTaskOutput(task, failure.message) } : {}),
|
|
409
473
|
},
|
|
410
474
|
};
|
|
411
475
|
appendHistory(result, historyReserved);
|
|
@@ -445,7 +509,7 @@ function mapWorkflowStatus(status) {
|
|
|
445
509
|
}
|
|
446
510
|
function renderWorkflowLog(input) {
|
|
447
511
|
const dbLines = [
|
|
448
|
-
{ line: `[akm task] task=${input.task.
|
|
512
|
+
{ line: `[akm task] task=${input.task.taskId} kind=workflow ref=${input.task.ref}` },
|
|
449
513
|
];
|
|
450
514
|
for (const warning of input.warnings ?? [])
|
|
451
515
|
dbLines.push({ level: "warn", line: warning });
|
|
@@ -461,98 +525,30 @@ function renderWorkflowLog(input) {
|
|
|
461
525
|
}
|
|
462
526
|
return { fileText: `${dbLines.map((entry) => entry.line).join("\n")}\n`, dbLines };
|
|
463
527
|
}
|
|
464
|
-
// ──
|
|
465
|
-
async function
|
|
466
|
-
const { task,
|
|
467
|
-
|
|
468
|
-
throw new Error("invariant: prompt target");
|
|
469
|
-
const promptTarget = task.target;
|
|
470
|
-
// Same implicit opencode-sdk fallback the workflow freeze boundary applies,
|
|
471
|
-
// so a scheduled prompt task on an engine-less install behaves identically.
|
|
472
|
-
const { config, fallbackEngineName } = withEngineFallback(loadConfig());
|
|
473
|
-
const engineName = promptTarget.engine ?? config.defaults?.engine;
|
|
474
|
-
// `promptTarget.engine` outranks `defaults.engine`, so the fallback is only
|
|
475
|
-
// reportable when it is the engine actually selected.
|
|
476
|
-
const engineAnnouncement = fallbackAnnouncement(fallbackEngineName, engineName);
|
|
477
|
-
if (!engineName)
|
|
478
|
-
throw new NotFoundError(`Task "${task.id}" ${NO_ENGINE_MESSAGE_SUFFIX} ${NO_ENGINE_REMEDY}`, "ASSET_NOT_FOUND");
|
|
479
|
-
let runner = resolveEngine(engineName, config);
|
|
480
|
-
if (runner.kind === "llm") {
|
|
481
|
-
const resolved = resolveLlmEngineUse(config, [
|
|
482
|
-
{
|
|
483
|
-
engine: engineName,
|
|
484
|
-
...(promptTarget.model !== undefined ? { model: promptTarget.model } : {}),
|
|
485
|
-
...(promptTarget.timeoutMs !== undefined ? { timeoutMs: promptTarget.timeoutMs } : {}),
|
|
486
|
-
...(promptTarget.llm !== undefined ? { llm: promptTarget.llm } : {}),
|
|
487
|
-
},
|
|
488
|
-
]);
|
|
489
|
-
runner = {
|
|
490
|
-
kind: "llm",
|
|
491
|
-
engine: resolved.engine,
|
|
492
|
-
connection: resolved.connection,
|
|
493
|
-
...(resolved.credential ? { credential: resolved.credential } : {}),
|
|
494
|
-
timeoutMs: resolved.timeoutMs,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
if (promptTarget.llm !== undefined) {
|
|
499
|
-
throw new NotFoundError(`Task "${task.id}" uses llm overrides with non-LLM engine "${engineName}".`, "ASSET_NOT_FOUND");
|
|
500
|
-
}
|
|
501
|
-
const requestedModel = promptTarget.model;
|
|
502
|
-
const platform = runner.profile.platform;
|
|
503
|
-
if (!platform)
|
|
504
|
-
throw new Error(`Engine "${engineName}" resolved without a platform.`);
|
|
505
|
-
const model = requestedModel
|
|
506
|
-
? resolveModel(requestedModel, platform, runner.profile.modelAliases, runner.profile.globalModelAliases)
|
|
507
|
-
: runner.profile.model;
|
|
508
|
-
runner = {
|
|
509
|
-
...runner,
|
|
510
|
-
profile: { ...runner.profile, ...(model ? { model, modelIsExact: true } : {}) },
|
|
511
|
-
// Unset → the unattended default (DEFAULT_AGENT_TIMEOUT_MS is null, which
|
|
512
|
-
// let a prompting or wedged agent CLI hang the schedule); `null` → the
|
|
513
|
-
// explicit no-timeout opt-out.
|
|
514
|
-
timeoutMs: promptTarget.timeoutMs !== undefined ? promptTarget.timeoutMs : DEFAULT_SCHEDULED_TASK_TIMEOUT_MS,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const promptText = await resolvePromptText(task, stashDir);
|
|
518
|
-
const result = await executeRunner(runner, promptText, {
|
|
519
|
-
stdio: "captured",
|
|
520
|
-
cwd: stashDir,
|
|
521
|
-
...agentOptions,
|
|
522
|
-
// Stamp task-runner provenance for any akm invocation the agent makes
|
|
523
|
-
// (DRIFT-6: agent-task traffic must not be recorded as user demand).
|
|
524
|
-
// Caller-supplied env still wins on conflicts.
|
|
525
|
-
//
|
|
526
|
-
// The agent child env is built from an allowlist, not inherited, so the
|
|
527
|
-
// scheduler's AKM_* directory context was dropped here — an agent's `akm`
|
|
528
|
-
// sub-commands then targeted the DEFAULT stash and DB rather than the
|
|
529
|
-
// ones the scheduled run was configured for. The command arm keeps this
|
|
530
|
-
// context because it inherits process.env; forward it explicitly.
|
|
531
|
-
env: { AKM_EVENT_SOURCE: "task", ...scheduledTaskContextEnv(), ...agentOptions?.env },
|
|
532
|
-
}, {
|
|
528
|
+
// ── common command target ───────────────────────────────────────────────────
|
|
529
|
+
async function runPreparedCommandTask(input) {
|
|
530
|
+
const { task, logPath, startedAt, now, agentOptions } = input;
|
|
531
|
+
const result = await dispatchPreparedCommandInvocation(task.invocation, {
|
|
533
532
|
...(input.runAgentImpl ? { runAgent: input.runAgentImpl } : {}),
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const stdout = await input.chatCompletionImpl(spec.connection, [{ role: "user", content: prompt }], {
|
|
537
|
-
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
538
|
-
});
|
|
539
|
-
return { ok: true, exitCode: 0, stdout, stderr: "", durationMs: Date.now() - started };
|
|
540
|
-
},
|
|
533
|
+
...(input.chatCompletionImpl ? { chat: input.chatCompletionImpl } : {}),
|
|
534
|
+
...(agentOptions ? { runOptions: agentOptions } : {}),
|
|
541
535
|
});
|
|
536
|
+
const engineName = result.engine;
|
|
542
537
|
const finishedAt = finishAttempt(startedAt, now());
|
|
543
|
-
const log = renderPromptLog({ task, engineName, result,
|
|
538
|
+
const log = renderPromptLog({ task, engineName, result, notices: result.notices, warnings: result.warnings });
|
|
544
539
|
persistRunLog({
|
|
545
|
-
taskId: task.
|
|
540
|
+
taskId: task.taskId,
|
|
546
541
|
startedAtIso: startedAt.toISOString(),
|
|
547
542
|
finishedAtIso: finishedAt.toISOString(),
|
|
548
543
|
logPath,
|
|
549
544
|
fileText: log.fileText,
|
|
550
545
|
dbLines: log.dbLines,
|
|
551
546
|
redactNames: task.redact,
|
|
547
|
+
environment: task.environment,
|
|
552
548
|
});
|
|
553
549
|
const status = result.ok ? "completed" : "failed";
|
|
554
550
|
const out = {
|
|
555
|
-
id: task.
|
|
551
|
+
id: task.taskId,
|
|
556
552
|
status,
|
|
557
553
|
startedAt: startedAt.toISOString(),
|
|
558
554
|
finishedAt: finishedAt.toISOString(),
|
|
@@ -561,46 +557,38 @@ async function runPromptTask(input) {
|
|
|
561
557
|
target: { kind: "prompt", engine: engineName },
|
|
562
558
|
detail: result.ok
|
|
563
559
|
? { exitCode: result.exitCode }
|
|
564
|
-
: {
|
|
560
|
+
: {
|
|
561
|
+
reason: result.reason === undefined ? undefined : scrubTaskOutput(task, result.reason),
|
|
562
|
+
error: result.error === undefined ? undefined : scrubTaskOutput(task, result.error),
|
|
563
|
+
exitCode: result.exitCode,
|
|
564
|
+
},
|
|
565
|
+
...(result.notices && result.notices.length > 0
|
|
566
|
+
? {
|
|
567
|
+
notices: result.notices.map((notice) => ({
|
|
568
|
+
...notice,
|
|
569
|
+
message: scrubTaskOutput(task, notice.message),
|
|
570
|
+
})),
|
|
571
|
+
}
|
|
572
|
+
: {}),
|
|
565
573
|
};
|
|
566
574
|
appendHistory(out, input.historyReserved);
|
|
567
575
|
return out;
|
|
568
576
|
}
|
|
569
|
-
async function resolvePromptText(task, stashDir) {
|
|
570
|
-
if (task.target.kind !== "prompt")
|
|
571
|
-
throw new Error("invariant: prompt target");
|
|
572
|
-
const src = task.target.source;
|
|
573
|
-
if (src.kind === "inline")
|
|
574
|
-
return src.text;
|
|
575
|
-
if (src.kind === "file") {
|
|
576
|
-
const taskDir = path.dirname(task.source.path);
|
|
577
|
-
const filePath = path.isAbsolute(src.path) ? src.path : path.resolve(taskDir, src.path);
|
|
578
|
-
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
579
|
-
throw new NotFoundError(`Prompt file not found: ${filePath}`, "FILE_NOT_FOUND");
|
|
580
|
-
}
|
|
581
|
-
return fs.readFileSync(filePath, "utf8");
|
|
582
|
-
}
|
|
583
|
-
// asset
|
|
584
|
-
const ref = parseRefInput(src.ref);
|
|
585
|
-
// D11 — see the matching guard in validator.ts: `resolveAssetPath`
|
|
586
|
-
// (src/sources/resolve.ts) is placement-dir-only and cannot route an
|
|
587
|
-
// opaque adapter conceptId, which `parseRefInput` now otherwise accepts.
|
|
588
|
-
if (placementSpecFor(ref.type) === undefined) {
|
|
589
|
-
throw new NotFoundError(`Task "${task.id}" prompt asset ref "${src.ref}" is not an AKM-placed asset — adapter-owned (opaque) prompt sources are not resolvable as task inputs yet.`, "ASSET_NOT_FOUND");
|
|
590
|
-
}
|
|
591
|
-
const assetPath = await resolveAssetPath(stashDir, ref.type, ref.name);
|
|
592
|
-
return fs.readFileSync(assetPath, "utf8");
|
|
593
|
-
}
|
|
594
577
|
function renderPromptLog(input) {
|
|
595
578
|
const lines = [];
|
|
596
579
|
const dbLines = [];
|
|
597
|
-
const header = `[akm task] task=${input.task.
|
|
580
|
+
const header = `[akm task] task=${input.task.taskId} kind=prompt engine=${input.engineName}`;
|
|
598
581
|
const summary = `ok=${input.result.ok} exit_code=${input.result.exitCode ?? "null"} duration_ms=${input.result.durationMs}`;
|
|
599
582
|
lines.push(header, summary);
|
|
600
583
|
dbLines.push({ line: header }, { level: input.result.ok ? "info" : "error", line: summary });
|
|
601
|
-
|
|
602
|
-
lines.push(
|
|
603
|
-
dbLines.push({ level: "warn", line:
|
|
584
|
+
for (const warning of input.warnings ?? []) {
|
|
585
|
+
lines.push(warning);
|
|
586
|
+
dbLines.push({ level: "warn", line: warning });
|
|
587
|
+
}
|
|
588
|
+
for (const notice of input.notices ?? []) {
|
|
589
|
+
const line = `lowering_notice=${notice.code} adapter=${notice.adapter} field=${notice.field ?? ""} message=${notice.message}`;
|
|
590
|
+
lines.push(line);
|
|
591
|
+
dbLines.push({ level: notice.severity === "warning" ? "warn" : "info", line });
|
|
604
592
|
}
|
|
605
593
|
if (!input.result.ok) {
|
|
606
594
|
const failure = `reason=${input.result.reason ?? ""} error=${input.result.error ?? ""}`;
|
|
@@ -698,10 +686,11 @@ function streamLines(text, stream, level) {
|
|
|
698
686
|
* to "log it anyway with no redaction at all" — `redactCredentialPatterns`
|
|
699
687
|
* still runs unconditionally in the caller.
|
|
700
688
|
*/
|
|
701
|
-
function taskLogSensitiveValues(redactNames) {
|
|
689
|
+
function taskLogSensitiveValues(redactNames, environment) {
|
|
690
|
+
const env = { ...process.env, ...environment };
|
|
702
691
|
try {
|
|
703
692
|
return collectTaskLogSensitiveValues({
|
|
704
|
-
env
|
|
693
|
+
env,
|
|
705
694
|
config: loadConfig(),
|
|
706
695
|
declaredNames: redactNames,
|
|
707
696
|
});
|
|
@@ -710,7 +699,7 @@ function taskLogSensitiveValues(redactNames) {
|
|
|
710
699
|
rethrowIfTestIsolationError(error);
|
|
711
700
|
// No config — the name heuristic and the task's own `redact:` list still apply.
|
|
712
701
|
try {
|
|
713
|
-
return collectTaskLogSensitiveValues({ env
|
|
702
|
+
return collectTaskLogSensitiveValues({ env, declaredNames: redactNames });
|
|
714
703
|
}
|
|
715
704
|
catch (fallbackError) {
|
|
716
705
|
rethrowIfTestIsolationError(fallbackError);
|
|
@@ -718,6 +707,11 @@ function taskLogSensitiveValues(redactNames) {
|
|
|
718
707
|
}
|
|
719
708
|
}
|
|
720
709
|
}
|
|
710
|
+
function scrubTaskOutput(task, text) {
|
|
711
|
+
const patterned = redactCredentialPatterns(text);
|
|
712
|
+
const sensitive = taskLogSensitiveValues(task.redact, task.environment);
|
|
713
|
+
return sensitive.length > 0 ? redactSensitiveText(patterned, sensitive) : patterned;
|
|
714
|
+
}
|
|
721
715
|
function persistRunLog(input) {
|
|
722
716
|
// Two arms, and both are needed. `redactCredentialPatterns` catches
|
|
723
717
|
// credential SHAPES nobody listed; the exact-value pass catches configured
|
|
@@ -727,7 +721,7 @@ function persistRunLog(input) {
|
|
|
727
721
|
// exact pass here — the one sink all three target kinds funnel through —
|
|
728
722
|
// covers every arm once rather than per-arm; prompt/workflow runs already
|
|
729
723
|
// scrub upstream, and redaction is idempotent, so the overlap is free.
|
|
730
|
-
const sensitive = taskLogSensitiveValues(input.redactNames);
|
|
724
|
+
const sensitive = taskLogSensitiveValues(input.redactNames, input.environment);
|
|
731
725
|
const scrub = (text) => sensitive.length > 0
|
|
732
726
|
? redactSensitiveText(redactCredentialPatterns(text), sensitive)
|
|
733
727
|
: redactCredentialPatterns(text);
|
|
@@ -914,13 +908,7 @@ function taskHistoryRowToResult(row) {
|
|
|
914
908
|
: row.target_kind === "command"
|
|
915
909
|
? { kind: "command" }
|
|
916
910
|
: row.target_kind === "prompt"
|
|
917
|
-
? meta.
|
|
918
|
-
? {
|
|
919
|
-
kind: "prompt",
|
|
920
|
-
engine: null,
|
|
921
|
-
...(meta.legacyProfile !== undefined ? { legacyProfile: meta.legacyProfile } : {}),
|
|
922
|
-
}
|
|
923
|
-
: { kind: "prompt", engine: meta.engine ?? null }
|
|
911
|
+
? { kind: "prompt", engine: meta.engine ?? null }
|
|
924
912
|
: { kind: "unknown" };
|
|
925
913
|
return {
|
|
926
914
|
id: row.task_id,
|