akm-cli 0.9.31 → 0.10.261002-beta
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 +420 -0
- package/STABILITY.md +99 -57
- package/dist/api-node.mjs +20 -0
- package/dist/api.d.ts +23 -0
- package/dist/api.js +45 -0
- package/dist/assets/hints/cli-hints-full.md +15 -29
- package/dist/assets/hints/cli-hints-short.md +2 -2
- package/dist/assets/improve-strategies/default.json +3 -3
- package/dist/assets/prompts/consolidate-pair.md +9 -7
- package/dist/assets/stash-skeleton/README.md +1 -1
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
- package/dist/cli/shared.js +31 -48
- package/dist/cli/unknown-flags.js +6 -43
- package/dist/cli.js +8 -34
- package/dist/commands/agent/contribute-cli.js +1 -1
- package/dist/commands/completions.js +1 -2
- package/dist/commands/config-cli.js +5 -28
- package/dist/commands/env/env-cli.js +5 -5
- package/dist/commands/env/secret-cli.js +2 -2
- package/dist/commands/feedback-cli.js +75 -49
- package/dist/commands/health/checks.js +41 -16
- package/dist/commands/health/plugin-staleness.js +52 -22
- package/dist/commands/improve/autonomy-gate.js +5 -3
- package/dist/commands/improve/consolidate/chunking.js +3 -10
- package/dist/commands/improve/consolidate/continuity-check.js +4 -4
- package/dist/commands/improve/consolidate/pair-pass.js +29 -10
- package/dist/commands/improve/consolidate.js +10 -65
- package/dist/commands/improve/distill/lesson-update.js +91 -0
- package/dist/commands/improve/distill-guards.js +1 -43
- package/dist/commands/improve/distill.js +148 -21
- package/dist/commands/improve/eligibility.js +14 -1
- package/dist/commands/improve/extract-cli.js +1 -1
- package/dist/commands/improve/extract-prompt.js +17 -7
- package/dist/commands/improve/extract.js +5 -7
- package/dist/commands/improve/improve-cli.js +92 -106
- package/dist/commands/improve/improve-strategies.js +8 -12
- package/dist/commands/improve/improve.js +134 -52
- package/dist/commands/improve/loop-stages.js +18 -5
- package/dist/commands/improve/planner.js +0 -1
- package/dist/commands/improve/preparation.js +40 -99
- package/dist/commands/improve/proactive-maintenance.js +2 -2
- package/dist/commands/improve/reflect-noise.js +3 -55
- package/dist/commands/improve/reflect.js +29 -34
- package/dist/commands/improve/retrieval-queries.js +37 -0
- package/dist/commands/improve/retrieval-scope.js +2 -2
- package/dist/commands/improve/salience.js +2 -9
- package/dist/commands/improve/session-asset.js +4 -1
- package/dist/commands/improve/stage.js +23 -12
- package/dist/commands/lint/index.js +5 -20
- package/dist/commands/observability-cli.js +3 -9
- package/dist/commands/proposal/drain.js +43 -6
- package/dist/commands/proposal/proposal-cli.js +11 -31
- package/dist/commands/proposal/repository.js +20 -14
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
- package/dist/commands/read/knowledge.js +3 -3
- package/dist/commands/read/remember-cli.js +0 -13
- package/dist/commands/read/search-cli.js +29 -97
- package/dist/commands/read/search.js +0 -11
- package/dist/commands/read/show.js +0 -19
- package/dist/commands/sources/plugin-upgrade.js +164 -101
- package/dist/commands/sources/sources-cli.js +1 -15
- package/dist/commands/sources/stash-cli.js +6 -14
- package/dist/commands/tasks/tasks-cli.js +4 -115
- package/dist/commands/workflow/plan.js +11 -33
- package/dist/commands/workflow-cli.js +2 -7
- package/dist/core/adapter/adapter-ids.js +1 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
- package/dist/core/adapter/adapters/akm-adapter.js +9 -48
- package/dist/core/adapter/adapters/akm-lint.js +10 -26
- package/dist/core/adapter/adapters/akm-metadata.js +2 -72
- package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
- package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
- package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
- package/dist/core/adapter/recognize-match.js +3 -4
- package/dist/core/common.js +18 -2
- package/dist/core/config/config-schema.js +12 -15
- package/dist/core/config/config-types.js +6 -15
- package/dist/core/config/config-walker.js +6 -23
- package/dist/core/config/config.js +17 -80
- package/dist/core/config/engine-semantics.js +7 -3
- package/dist/core/config/schema/engines.js +7 -7
- package/dist/core/config/schema/improve-processes.js +10 -63
- package/dist/core/config/schema/improve.js +2 -21
- package/dist/core/config/schema/index-config.js +6 -39
- package/dist/core/env-secret-ref.js +2 -45
- package/dist/core/errors.js +1 -2
- package/dist/core/extra-params.js +2 -86
- package/dist/core/file-lock.js +2 -6
- package/dist/core/improve-result.js +13 -1
- package/dist/core/mutation-target.js +2 -2
- package/dist/core/paths.js +4 -0
- package/dist/core/recognition-util.js +4 -5
- package/dist/core/run-lock.js +1 -1
- package/dist/core/subprocess.js +49 -0
- package/dist/core/warn.js +21 -4
- package/dist/core/write-source.js +3 -3
- package/dist/execution/target-ref.js +1 -2
- package/dist/indexer/indexer.js +15 -6
- package/dist/indexer/passes/memory-inference.js +15 -6
- package/dist/indexer/search/db-search.js +49 -36
- package/dist/indexer/search/fts-query.js +0 -19
- package/dist/indexer/search/ranking.js +29 -1
- package/dist/indexer/usage/usage-events.js +21 -16
- package/dist/integrations/agent/engine-resolution.js +5 -2
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/profiles.js +41 -98
- package/dist/integrations/agent/request-lowering.js +3 -3
- package/dist/integrations/harnesses/aider/index.js +5 -0
- package/dist/integrations/harnesses/amazonq/index.js +1 -0
- package/dist/integrations/harnesses/claude/config-import.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +5 -0
- package/dist/integrations/harnesses/codex/index.js +1 -0
- package/dist/integrations/harnesses/copilot/index.js +1 -0
- package/dist/integrations/harnesses/gemini/index.js +1 -0
- package/dist/integrations/harnesses/index.js +18 -39
- package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
- package/dist/integrations/harnesses/opencode/config-import.js +121 -52
- package/dist/integrations/harnesses/opencode/index.js +22 -0
- package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
- package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
- package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
- package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
- package/dist/integrations/harnesses/opencode/session-log.js +104 -115
- package/dist/integrations/harnesses/opencode/version.js +98 -0
- package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
- package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
- package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
- package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
- package/dist/integrations/harnesses/openhands/index.js +1 -0
- package/dist/integrations/harnesses/pi/index.js +1 -0
- package/dist/integrations/harnesses/shared.js +2 -0
- package/dist/integrations/harnesses/types.js +2 -0
- package/dist/llm/client.js +41 -11
- package/dist/output/cli-hints.js +1 -1
- package/dist/output/context.js +10 -19
- package/dist/output/format-exempt.js +5 -8
- package/dist/output/shapes/helpers.js +1 -22
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/shapes.js +3 -31
- package/dist/output/stdout.js +5 -1
- package/dist/output/text/command-format.js +6 -1
- package/dist/output/text/workflow-format.js +4 -22
- package/dist/scripts/akm-migrate-node.js +15745 -16711
- package/dist/scripts/akm-migrate.js +16318 -17280
- package/dist/setup/detect.js +10 -4
- package/dist/setup/harness-config-import.js +5 -14
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +12 -13
- package/dist/sources/providers/git-stash.js +113 -16
- package/dist/storage/repositories/index-utility-repository.js +2 -32
- package/dist/storage/repositories/index-vec-repository.js +4 -0
- package/dist/storage/repositories/task-history-repository.js +0 -5
- package/dist/storage/repositories/workflow-runs-repository.js +5 -15
- package/dist/tasks/embedded.js +2 -1
- package/dist/tasks/run/run-command-task.js +9 -1
- package/dist/tasks/run/run-workflow-task.js +4 -3
- package/dist/tasks/run/task-history.js +0 -2
- package/dist/tasks/run/task-log.js +38 -1
- package/dist/tasks/schedule-gates.js +39 -0
- package/dist/tasks/scheduler-binding.js +0 -16
- package/dist/tasks/scheduler-sync.js +3 -97
- package/dist/tasks/source/bounded-document.js +1 -1
- package/dist/tasks/source/task-source-v4.js +2 -12
- package/dist/tasks/task-run-reserved-flags.js +2 -31
- package/dist/workflows/authoring/authoring.js +6 -6
- package/dist/workflows/compile.js +8 -65
- package/dist/workflows/exec/child-workflow.js +22 -3
- package/dist/workflows/exec/native-executor.js +12 -147
- package/dist/workflows/exec/run-workflow.js +31 -103
- package/dist/workflows/exec/step-work.js +9 -178
- package/dist/workflows/exec/unit-dispatch.js +0 -10
- package/dist/workflows/freeze/environment.js +3 -44
- package/dist/workflows/freeze/freeze.js +8 -41
- package/dist/workflows/freeze/targets/child-workflow.js +27 -63
- package/dist/workflows/freeze/targets/command.js +2 -16
- package/dist/workflows/freeze/targets/shell.js +0 -2
- package/dist/workflows/freeze/task-bindings.js +2 -28
- package/dist/workflows/ir/params.js +12 -2
- package/dist/workflows/parser.js +82 -232
- package/dist/workflows/plan.js +1 -1
- package/dist/workflows/renderer.js +10 -19
- package/dist/workflows/runtime/run-plan.js +11 -53
- package/dist/workflows/runtime/runs.js +4 -27
- package/dist/workflows/source-files.js +12 -16
- package/dist/workflows/source-semantics.js +1 -93
- package/docs/README.md +1 -1
- package/docs/agents/README.md +3 -3
- package/docs/agents/agent-install.md +1 -1
- package/docs/architecture/0.10-process-rules.md +827 -0
- package/docs/architecture/adapters.md +17 -31
- package/docs/architecture/architecture.md +4 -5
- package/docs/architecture/improvement.md +115 -76
- package/docs/architecture/internals/improve-workflow.md +22 -16
- package/docs/architecture/internals/indexing.md +1 -1
- package/docs/architecture/internals/search.md +1 -2
- package/docs/architecture/internals/storage-locations.md +6 -6
- package/docs/architecture/persisted-data-compat.md +13 -4
- package/docs/architecture/specs/0.9.0-decisions.md +6 -2
- package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
- package/docs/architecture/specs/workflow-format-unification.md +1 -6
- package/docs/architecture/testing/manual-testing-checklist.md +21 -30
- package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
- package/docs/architecture/testing/testing-workflow.md +1 -1
- package/docs/architecture/workflow-engine.md +5 -56
- package/docs/guides/author-workflows.md +39 -69
- package/docs/guides/bundles.md +3 -1
- package/docs/guides/capture-knowledge.md +1 -1
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
- package/docs/guides/discover-and-load.md +4 -4
- package/docs/guides/getting-started.md +1 -1
- package/docs/guides/improve-the-library.md +10 -9
- package/docs/guides/recipes/headless-install.md +2 -2
- package/docs/guides/run-workflows.md +1 -1
- package/docs/guides/scheduling.md +9 -1
- package/docs/guides/use-with-any-agent.md +49 -4
- package/docs/integration/bundling-akm.md +7 -6
- package/docs/maintainers/curate-workmap.md +1 -2
- package/docs/maintainers/release-checklist.md +33 -1
- package/docs/migration/README.md +2 -0
- package/docs/migration/release-notes/0.10.0.md +201 -0
- package/docs/migration/release-notes/README.md +1 -0
- package/docs/migration/v0.9-to-v0.10.md +507 -0
- package/docs/posts/akm-command-tour.md +3 -3
- package/docs/product-surface.md +1 -1
- package/docs/reference/README.md +1 -0
- package/docs/reference/api.md +65 -0
- package/docs/reference/asset-types.md +3 -5
- package/docs/reference/bundle-types.md +2 -4
- package/docs/reference/cli.md +160 -188
- package/docs/reference/configuration.md +101 -20
- package/docs/reference/data-and-telemetry.md +2 -2
- package/docs/reference/env-and-secrets.md +2 -2
- package/docs/reference/supported-formats.md +3 -4
- package/docs/reference/tasks.md +12 -19
- package/docs/reference/website-sources.md +1 -1
- package/docs/reference/workflow-schema.md +89 -317
- package/docs/reference/workflows.md +20 -48
- package/package.json +12 -2
- package/schemas/akm-config.json +2 -243
- package/schemas/akm-task.json +2 -3
- package/schemas/akm-workflow.json +47 -84
- package/dist/assets/improve-strategies/catchup.json +0 -41
- package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
- package/dist/assets/improve-strategies/quick.json +0 -17
- package/dist/assets/improve-strategies/reflect-distill.json +0 -29
- package/dist/assets/improve-strategies/thorough.json +0 -49
- package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
- package/dist/cli/retired-commands.js +0 -118
- package/dist/commands/improve/retrieval-gate.js +0 -132
- package/dist/commands/tasks/validate.js +0 -176
- package/dist/core/config/legacy-source-shape-shim.js +0 -117
- package/dist/indexer/feedback/utility-policy.js +0 -75
- package/dist/integrations/harnesses/ids.js +0 -28
- package/dist/tasks/source/task-source-v3-frozen.js +0 -424
- package/dist/tasks/source/task-to-v4.js +0 -863
- package/dist/workflows/exec/worktree.js +0 -372
- package/dist/workflows/freeze/child-output-references.js +0 -83
- package/dist/workflows/freeze/identity.js +0 -22
- package/dist/workflows/freeze/targets/script.js +0 -50
- package/dist/workflows/freeze/targets/task.js +0 -124
- package/dist/workflows/github-yaml.js +0 -506
- package/dist/workflows/runtime/run-outputs.js +0 -80
package/dist/setup/detect.js
CHANGED
|
@@ -14,6 +14,7 @@ import { runManagedSubprocess } from "../core/subprocess.js";
|
|
|
14
14
|
import { defaultWhich } from "../integrations/agent/detect.js";
|
|
15
15
|
import { getBuiltinAgentProfile, OPENCODE_SDK_SERVER_BIN } from "../integrations/agent/profiles.js";
|
|
16
16
|
import { AGENT_DISPATCH_HARNESSES, SESSION_LOG_HARNESSES } from "../integrations/harnesses/index.js";
|
|
17
|
+
import { detectOpencodeMajor, resolveOpencodeBin } from "../integrations/harnesses/opencode/version.js";
|
|
17
18
|
import { detectHarnessConfigs } from "./harness-config-import.js";
|
|
18
19
|
let detectOverrides;
|
|
19
20
|
/** TEST-ONLY. Swap the network/host probes; pass undefined to restore. */
|
|
@@ -342,7 +343,8 @@ function findGitRepoRoot(start) {
|
|
|
342
343
|
* 3. none.
|
|
343
344
|
*
|
|
344
345
|
* The SDK import resolving is NOT evidence the SDK path can run: `@opencode-ai/sdk`
|
|
345
|
-
*
|
|
346
|
+
* (OpenCode 1) and `@opencode/client` (OpenCode 2; the binary's detected major
|
|
347
|
+
* selects) are hard dependencies of akm-cli itself, so `import()` always succeeds and an
|
|
346
348
|
* import-only check would report `opencode-sdk` on every machine — including
|
|
347
349
|
* ones where the first agentic command then dies with spawn ENOENT. The SDK
|
|
348
350
|
* runner spawns `opencode serve` (see `harnesses/opencode-sdk/sdk-runner.ts`),
|
|
@@ -355,10 +357,14 @@ function findGitRepoRoot(start) {
|
|
|
355
357
|
export async function detectHarness(whichFn = defaultWhich) {
|
|
356
358
|
// Probed once; reused below so the `opencode` CLI fallback doesn't repeat
|
|
357
359
|
// the identical full-PATH walk this just performed.
|
|
358
|
-
const opencodePath = whichFn(
|
|
360
|
+
const opencodePath = whichFn(resolveOpencodeBin(undefined, whichFn));
|
|
359
361
|
if (opencodePath) {
|
|
360
362
|
try {
|
|
361
|
-
|
|
363
|
+
// The client package of the major the binary reports.
|
|
364
|
+
if (detectOpencodeMajor(opencodePath).major === 1)
|
|
365
|
+
await import("@opencode-ai/sdk");
|
|
366
|
+
else
|
|
367
|
+
await import("@opencode/client");
|
|
362
368
|
return "opencode-sdk";
|
|
363
369
|
}
|
|
364
370
|
catch {
|
|
@@ -368,7 +374,7 @@ export async function detectHarness(whichFn = defaultWhich) {
|
|
|
368
374
|
}
|
|
369
375
|
for (const harness of AGENT_DISPATCH_HARNESSES) {
|
|
370
376
|
// The SDK harness dispatches without a CLI profile and was decided above;
|
|
371
|
-
// an explicit skip so a future
|
|
377
|
+
// an explicit skip so a future `profile` for it can't silently
|
|
372
378
|
// reintroduce it here after the import guard already rejected it.
|
|
373
379
|
if (harness.id === "opencode-sdk")
|
|
374
380
|
continue;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* no network) and, if so, reads its config to extract LLM connection details.
|
|
9
9
|
* API key VALUES are never stored — only the env var names that hold them.
|
|
10
10
|
*
|
|
11
|
-
* To add a new harness:
|
|
12
|
-
*
|
|
11
|
+
* To add a new harness importer: declare `configImporter` on the harness
|
|
12
|
+
* descriptor (`integrations/harnesses/<id>/index.ts`).
|
|
13
13
|
*
|
|
14
14
|
* NOTE: The `detect()` method in each importer overlaps intentionally with
|
|
15
15
|
* `detectAgentPlatforms()` in `detect.ts`. That function scans for harness
|
|
@@ -17,18 +17,9 @@
|
|
|
17
17
|
* further by reading and parsing the harness config. They serve different
|
|
18
18
|
* purposes and should not be deduplicated.
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// (`harnesses/claude/config-import.ts`) and the OpenCode importer in #564
|
|
24
|
-
// (`harnesses/opencode/config-import.ts`). Both are imported back into
|
|
25
|
-
// HARNESS_CONFIG_IMPORTERS below so detection order is unchanged.
|
|
26
|
-
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
27
|
-
/**
|
|
28
|
-
* Registry of all supported harness config importers.
|
|
29
|
-
* To add a new harness: implement {@link HarnessConfigImporter} and append here.
|
|
30
|
-
*/
|
|
31
|
-
export const HARNESS_CONFIG_IMPORTERS = [claudeCodeImporter, openCodeImporter];
|
|
20
|
+
import { HARNESS_REGISTRY } from "../integrations/harnesses/index.js";
|
|
21
|
+
/** Importers declared by the registered harnesses, in registry order. */
|
|
22
|
+
export const HARNESS_CONFIG_IMPORTERS = HARNESS_REGISTRY.flatMap((h) => h.configImporter ? [h.configImporter] : []);
|
|
32
23
|
/**
|
|
33
24
|
* Run all importers whose `detect()` returns `true` and collect their configs.
|
|
34
25
|
*
|
package/dist/setup/setup.js
CHANGED
|
@@ -625,7 +625,7 @@ export async function runSetupWizard(opts) {
|
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
627
|
p.outro(`Configuration saved to ${configPath}\n` +
|
|
628
|
-
'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm
|
|
628
|
+
'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm hints`');
|
|
629
629
|
}
|
|
630
630
|
// ── Non-interactive / scripting entry points ─────────────────────────────────
|
|
631
631
|
/**
|
|
@@ -19,13 +19,12 @@ import { parseSchedule } from "../../tasks/schedule.js";
|
|
|
19
19
|
import { parseTaskSource } from "../../tasks/source/parse-task-source.js";
|
|
20
20
|
import { prompt } from "../prompt.js";
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* "battery heuristic" {@link detectServerDefault} preserved from that path.
|
|
22
|
+
* The nightly improve template (`core/improve`). This id is preselected in the
|
|
23
|
+
* review multiselect on a detected server install — the "battery heuristic"
|
|
24
|
+
* {@link detectServerDefault} preserved from the retired
|
|
25
|
+
* `registerDefaultTasks`/`akm tasks init` path (0.9, S6).
|
|
27
26
|
*/
|
|
28
|
-
const SERVER_SUGGESTED_TASK_ID = "
|
|
27
|
+
const SERVER_SUGGESTED_TASK_ID = "improve";
|
|
29
28
|
let scheduledTasksEnvOverrides;
|
|
30
29
|
/** TEST-ONLY. Swap the CI/server-detection probes; pass undefined to restore. */
|
|
31
30
|
export function _setScheduledTasksEnvForTests(fakes) {
|
|
@@ -66,7 +65,7 @@ export function detectServerDefault() {
|
|
|
66
65
|
}
|
|
67
66
|
catch {
|
|
68
67
|
// If we cannot read power-supply info, prefer the safe server default on
|
|
69
|
-
// Linux (the nightly
|
|
68
|
+
// Linux (the nightly improve run is low-impact and re-runnable).
|
|
70
69
|
return true;
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -200,11 +199,11 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
|
|
|
200
199
|
p.log.info("Non-interactive setup leaves task files and scheduler state unchanged. Run `akm setup` interactively to review tasks.");
|
|
201
200
|
return;
|
|
202
201
|
}
|
|
203
|
-
// ALL templates are offered, including ships-disabled ones
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
202
|
+
// ALL templates are offered, including ships-disabled ones: an unselected
|
|
203
|
+
// template is still PREPARED, so its YAML exists for `akm task run <id>`
|
|
204
|
+
// while its ref remains absent from local scheduler activation. Filtering on
|
|
205
|
+
// `task.enabled` here would make ships-disabled templates invisible and
|
|
206
|
+
// unpreparable.
|
|
208
207
|
const embedded = listEmbeddedTasks();
|
|
209
208
|
if (embedded.length === 0)
|
|
210
209
|
return;
|
|
@@ -228,7 +227,7 @@ export async function stepScheduledTasks(deps = DEFAULT_SCHEDULED_TASKS_DEPS, op
|
|
|
228
227
|
byId.set(normaliseTaskIdForMatch(task.id), task);
|
|
229
228
|
const preChecked = embedded.filter((task) => byId.get(task.id)?.enabled === true).map((task) => task.id);
|
|
230
229
|
// Battery heuristic preserved from the retired `registerDefaultTasks` path
|
|
231
|
-
// (S6): suggest the nightly
|
|
230
|
+
// (S6): suggest the nightly improve task on a detected server install, same
|
|
232
231
|
// as every other embedded template, still gated behind the confirmation
|
|
233
232
|
// below.
|
|
234
233
|
if (!byId.has(SERVER_SUGGESTED_TASK_ID) &&
|
|
@@ -257,8 +257,11 @@ export function saveGitStash(name, message, writableOverride, options) {
|
|
|
257
257
|
if (statusResult.error || statusResult.status !== 0) {
|
|
258
258
|
throw new Error(`git status failed: ${statusResult.error?.message || statusResult.stderr?.trim() || "unknown error"}`);
|
|
259
259
|
}
|
|
260
|
+
const hasRemote = readHasRemote(repoDir);
|
|
261
|
+
// `--no-push`, a read-only bundle and a bundle without a remote never touch the network.
|
|
262
|
+
const syncWithRemote = hasRemote && writable && allowPush;
|
|
260
263
|
if (!statusResult.stdout.trim() && options?.paths === undefined) {
|
|
261
|
-
return
|
|
264
|
+
return nothingToCommit(repoDir, "nothing to commit, working tree clean", syncWithRemote);
|
|
262
265
|
}
|
|
263
266
|
// Scoped staging (#476 + the auto-sync incident): NEVER refuse akm's commit
|
|
264
267
|
// because unrelated non-akm files exist in the working tree. When the stash
|
|
@@ -277,22 +280,14 @@ export function saveGitStash(name, message, writableOverride, options) {
|
|
|
277
280
|
: "";
|
|
278
281
|
const changedPaths = listGitChangedPaths(repoDir);
|
|
279
282
|
const requestedPaths = normalizeExactPaths(repoDir, options?.paths ??
|
|
280
|
-
(managedRelativeRoot && managedRelativeRoot !== ".." && !managedRelativeRoot.startsWith("../")
|
|
283
|
+
withoutIgnoredPaths(repoDir, managedRelativeRoot && managedRelativeRoot !== ".." && !managedRelativeRoot.startsWith("../")
|
|
281
284
|
? changedPaths.filter((changedPath) => changedPath === managedRelativeRoot || changedPath.startsWith(`${managedRelativeRoot}/`))
|
|
282
285
|
: managedFallbackPaths(repoDir, changedPaths)));
|
|
283
286
|
if (requestedPaths.length === 0) {
|
|
284
|
-
return
|
|
287
|
+
return nothingToCommit(repoDir, "nothing to commit", syncWithRemote);
|
|
285
288
|
}
|
|
286
289
|
assertNoIgnoredExactPaths(repoDir, requestedPaths);
|
|
287
290
|
const baseHead = readOptionalHead(repoDir);
|
|
288
|
-
const remoteResult = runGit(["-C", repoDir, "remote"]);
|
|
289
|
-
if (remoteResult.status !== 0) {
|
|
290
|
-
throw new Error(`git remote failed: ${remoteResult.stderr?.trim() || "unknown error"}`);
|
|
291
|
-
}
|
|
292
|
-
const hasRemote = remoteResult.stdout.trim().length > 0;
|
|
293
|
-
// A branch that cannot be pushed still gets its commit: only the push is skipped, and the result says why.
|
|
294
|
-
const upstream = hasRemote && writable && allowPush ? readActualUpstream(repoDir, baseHead) : undefined;
|
|
295
|
-
const pushTarget = typeof upstream === "object" ? upstream : undefined;
|
|
296
291
|
const exactCommit = createExactPathCommit(repoDir, {
|
|
297
292
|
baseHead,
|
|
298
293
|
commitMessage,
|
|
@@ -301,8 +296,17 @@ export function saveGitStash(name, message, writableOverride, options) {
|
|
|
301
296
|
expectedSnapshots: options?.expectedSnapshots,
|
|
302
297
|
});
|
|
303
298
|
if (!exactCommit) {
|
|
304
|
-
return
|
|
305
|
-
}
|
|
299
|
+
return nothingToCommit(repoDir, "nothing to commit", syncWithRemote);
|
|
300
|
+
}
|
|
301
|
+
// Bring in whatever another host pushed, then push. A branch that cannot be pushed still keeps its
|
|
302
|
+
// commit: only the push is skipped, and the result says why.
|
|
303
|
+
const integration = syncWithRemote ? integrateUpstream(repoDir) : undefined;
|
|
304
|
+
const upstream = integration && "blocked" in integration
|
|
305
|
+
? integration.blocked
|
|
306
|
+
: syncWithRemote
|
|
307
|
+
? readActualUpstream(repoDir, readOptionalHead(repoDir))
|
|
308
|
+
: undefined;
|
|
309
|
+
const pushTarget = typeof upstream === "object" ? upstream : undefined;
|
|
306
310
|
if (!pushTarget) {
|
|
307
311
|
return {
|
|
308
312
|
committed: true,
|
|
@@ -319,7 +323,7 @@ export function saveGitStash(name, message, writableOverride, options) {
|
|
|
319
323
|
"push",
|
|
320
324
|
`--force-with-lease=${pushTarget.mergeRef}:${pushTarget.upstreamHead}`,
|
|
321
325
|
pushTarget.remote,
|
|
322
|
-
`${exactCommit}:${pushTarget.mergeRef}`,
|
|
326
|
+
`${readOptionalHead(repoDir) ?? exactCommit}:${pushTarget.mergeRef}`,
|
|
323
327
|
], { timeout: GIT_PUSH_TIMEOUT_MS });
|
|
324
328
|
if (pushResult.status !== 0) {
|
|
325
329
|
throw new GitStashPushError(`git push failed: ${pushResult.stderr?.trim() || "unknown error"}`, exactCommit);
|
|
@@ -332,11 +336,98 @@ export function saveGitStash(name, message, writableOverride, options) {
|
|
|
332
336
|
commit: exactCommit,
|
|
333
337
|
};
|
|
334
338
|
}
|
|
339
|
+
function readHasRemote(repoDir) {
|
|
340
|
+
const remoteResult = runGit(["-C", repoDir, "remote"]);
|
|
341
|
+
if (remoteResult.status !== 0) {
|
|
342
|
+
throw new Error(`git remote failed: ${remoteResult.stderr?.trim() || "unknown error"}`);
|
|
343
|
+
}
|
|
344
|
+
return remoteResult.stdout.trim().length > 0;
|
|
345
|
+
}
|
|
346
|
+
/** Nothing to commit still brings in the upstream, so a host with no changes of its own keeps up. */
|
|
347
|
+
function nothingToCommit(repoDir, output, syncWithRemote) {
|
|
348
|
+
const integration = syncWithRemote ? integrateUpstream(repoDir) : undefined;
|
|
349
|
+
if (integration && "blocked" in integration) {
|
|
350
|
+
return { committed: false, pushed: false, skipped: false, reason: `not pushed: ${integration.blocked}`, output };
|
|
351
|
+
}
|
|
352
|
+
return { committed: false, pushed: false, skipped: false, output: integration?.integrated ?? output };
|
|
353
|
+
}
|
|
354
|
+
function readUpstreamConfig(repoDir) {
|
|
355
|
+
const branchRef = runGit(["-C", repoDir, "symbolic-ref", "--quiet", "HEAD"]);
|
|
356
|
+
if (branchRef.status !== 0 || !branchRef.stdout.trim())
|
|
357
|
+
return undefined;
|
|
358
|
+
const branch = branchRef.stdout.trim().replace(/^refs\/heads\//, "");
|
|
359
|
+
const remote = runGit(["-C", repoDir, "config", "--get", `branch.${branch}.remote`]);
|
|
360
|
+
const merge = runGit(["-C", repoDir, "config", "--get", `branch.${branch}.merge`]);
|
|
361
|
+
if (remote.status !== 0 || merge.status !== 0)
|
|
362
|
+
return undefined;
|
|
363
|
+
return { remote: remote.stdout.trim(), mergeRef: merge.stdout.trim() };
|
|
364
|
+
}
|
|
365
|
+
function isAncestor(repoDir, ancestor, descendant) {
|
|
366
|
+
return runGit(["-C", repoDir, "merge-base", "--is-ancestor", ancestor, descendant]).status === 0;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Fetch the branch's remote and bring its upstream into HEAD: nothing to do when HEAD already has it,
|
|
370
|
+
* a fast-forward when HEAD is behind, otherwise git's default merge. A merge that conflicts is aborted
|
|
371
|
+
* so the working tree and the local commits stay exactly as they were. `blocked` says why the branch
|
|
372
|
+
* could not be brought up to date (and therefore must not be pushed); `integrated` describes what moved.
|
|
373
|
+
*/
|
|
374
|
+
function integrateUpstream(repoDir) {
|
|
375
|
+
const config = readUpstreamConfig(repoDir);
|
|
376
|
+
if (!config)
|
|
377
|
+
return {};
|
|
378
|
+
const fetch = runGit(["-C", repoDir, "fetch", config.remote], { timeout: GIT_PUSH_TIMEOUT_MS });
|
|
379
|
+
if (fetch.status !== 0) {
|
|
380
|
+
return { blocked: `could not fetch ${config.remote}: ${fetch.stderr?.trim() || "unknown error"}` };
|
|
381
|
+
}
|
|
382
|
+
const upstream = runGit(["-C", repoDir, "rev-parse", "--verify", "@{u}"]);
|
|
383
|
+
const head = readOptionalHead(repoDir);
|
|
384
|
+
if (upstream.status !== 0 || !head)
|
|
385
|
+
return {};
|
|
386
|
+
const upstreamHead = upstream.stdout.trim();
|
|
387
|
+
if (isAncestor(repoDir, upstreamHead, head))
|
|
388
|
+
return {};
|
|
389
|
+
const fastForward = isAncestor(repoDir, head, upstreamHead);
|
|
390
|
+
const merge = runGit([
|
|
391
|
+
"-C",
|
|
392
|
+
repoDir,
|
|
393
|
+
"-c",
|
|
394
|
+
"user.name=akm",
|
|
395
|
+
"-c",
|
|
396
|
+
"user.email=akm@local",
|
|
397
|
+
"merge",
|
|
398
|
+
...(fastForward ? ["--ff-only"] : ["--no-edit", "-m", `akm sync: merge ${config.remote} upstream`]),
|
|
399
|
+
upstreamHead,
|
|
400
|
+
]);
|
|
401
|
+
if (merge.status === 0)
|
|
402
|
+
return { integrated: fastForward ? "fast-forwarded to upstream" : "merged upstream" };
|
|
403
|
+
const conflicted = runGit(["-C", repoDir, "diff", "--name-only", "--diff-filter=U"])
|
|
404
|
+
.stdout.split("\n")
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
// Not every refusal leaves a merge in progress (e.g. local changes in the way); abort only if one is.
|
|
407
|
+
if (runGit(["-C", repoDir, "rev-parse", "--verify", "--quiet", "MERGE_HEAD"]).status === 0) {
|
|
408
|
+
runGit(["-C", repoDir, "merge", "--abort"]);
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
blocked: conflicted.length > 0
|
|
412
|
+
? `merging upstream conflicts in ${conflicted.join(", ")}; the merge was aborted and the local commit kept`
|
|
413
|
+
: `could not merge upstream (${merge.stderr?.trim() || merge.stdout?.trim() || "unknown error"}); the local commit was kept`,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
335
416
|
function managedFallbackPaths(repoDir, changedPaths) {
|
|
336
417
|
const ownedDirs = akmAdapter.directoryList?.({ id: "akm", adapter: "akm", root: repoDir, writable: false }) ?? stashDirNames();
|
|
337
418
|
const prefixes = [...ownedDirs, ".akm"];
|
|
338
419
|
return changedPaths.filter((changedPath) => prefixes.some((prefix) => changedPath === prefix || changedPath.startsWith(`${prefix}/`)));
|
|
339
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Drop paths a `.gitignore` rule matches from the paths sync picks for itself. Ignoring means "do
|
|
423
|
+
* not publish", also for a file committed before the rule existed (`.akm/evals/runs/latest` in a
|
|
424
|
+
* stash whose eval runs are ignored): git refuses to stage such a file, which would fail the whole
|
|
425
|
+
* sync. Only an explicit `paths` request still reaches {@link assertNoIgnoredExactPaths}.
|
|
426
|
+
*/
|
|
427
|
+
function withoutIgnoredPaths(repoDir, paths) {
|
|
428
|
+
const ignored = new Set(listIgnoredExactPaths(repoDir, paths, { includeTracked: true }));
|
|
429
|
+
return ignored.size === 0 ? paths : paths.filter((candidate) => !ignored.has(candidate));
|
|
430
|
+
}
|
|
340
431
|
function normalizeExactPaths(repoDir, paths) {
|
|
341
432
|
const normalized = [...new Set(paths.map((value) => value.replaceAll(path.sep, "/")))];
|
|
342
433
|
for (const filePath of normalized) {
|
|
@@ -363,11 +454,17 @@ function runExactPathChunks(repoDir, command, paths, options = {}) {
|
|
|
363
454
|
}
|
|
364
455
|
return results;
|
|
365
456
|
}
|
|
366
|
-
|
|
457
|
+
/**
|
|
458
|
+
* The subset of `paths` a `.gitignore` rule matches. By default a tracked file never counts (git's
|
|
459
|
+
* own `check-ignore` skips it); `includeTracked` asks about the rule alone, as `git add` does.
|
|
460
|
+
*/
|
|
461
|
+
export function listIgnoredExactPaths(repoDir, paths, options = {}) {
|
|
367
462
|
if (paths.length === 0)
|
|
368
463
|
return [];
|
|
369
464
|
const input = `${paths.join("\0")}\0`;
|
|
370
|
-
const result = runGit(["-C", repoDir, "check-ignore", "-z", "--stdin"
|
|
465
|
+
const result = runGit(["-C", repoDir, "check-ignore", "-z", "--stdin", ...(options.includeTracked ? ["--no-index"] : [])], {
|
|
466
|
+
input,
|
|
467
|
+
});
|
|
371
468
|
if (result.status === 1)
|
|
372
469
|
return [];
|
|
373
470
|
if (result.status !== 0) {
|
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
* `index.db` utility-score + retrieval-count repository (MemRL signals).
|
|
6
6
|
*
|
|
7
7
|
* Owns the raw SQL for `utility_scores` and the retrieval-frequency counting
|
|
8
|
-
* over `usage_events`. The
|
|
9
|
-
* `indexer/
|
|
8
|
+
* over `usage_events`. The utility formula itself lives in
|
|
9
|
+
* `recomputeUtilityScores` (`indexer/indexer.ts`); this repo only reads/writes.
|
|
10
10
|
*/
|
|
11
11
|
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
12
|
-
import { computeNextUtility } from "../../indexer/feedback/utility-policy.js";
|
|
13
12
|
import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
|
|
14
13
|
/**
|
|
15
14
|
* Get the utility score for an entry, or undefined if none exists.
|
|
@@ -242,35 +241,6 @@ function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
|
|
|
242
241
|
}
|
|
243
242
|
return result;
|
|
244
243
|
}
|
|
245
|
-
/**
|
|
246
|
-
* Apply accumulated feedback counts to the utility score of an entry, persisting
|
|
247
|
-
* the result. The bounded-step EMA policy itself (MemRL, F-5 / #386,
|
|
248
|
-
* arXiv:2601.03192) lives in {@link computeNextUtility} (feedback/utility-policy);
|
|
249
|
-
* this function only reads the current utility, applies the policy, and writes
|
|
250
|
-
* the new value.
|
|
251
|
-
*
|
|
252
|
-
* A new entry starts at 0.5 (neutral midpoint) before the EMA step is applied.
|
|
253
|
-
* When there is no feedback (both counts zero) the score is left untouched — no
|
|
254
|
-
* DB write. Returns a {@link FeedbackUtilityResult} so the caller can detect a
|
|
255
|
-
* previously high-utility asset crossing below the review threshold and escalate.
|
|
256
|
-
*/
|
|
257
|
-
export function applyFeedbackToUtilityScore(db, entryId, positiveCount, negativeCount) {
|
|
258
|
-
const existing = getUtilityScore(db, entryId);
|
|
259
|
-
const previousUtility = existing?.utility ?? 0.5;
|
|
260
|
-
const result = computeNextUtility(previousUtility, positiveCount, negativeCount);
|
|
261
|
-
if (positiveCount === 0 && negativeCount === 0) {
|
|
262
|
-
return result;
|
|
263
|
-
}
|
|
264
|
-
const now = new Date().toISOString();
|
|
265
|
-
db.prepare(`
|
|
266
|
-
INSERT INTO utility_scores (entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at)
|
|
267
|
-
VALUES (?, ?, 0, 0, 0, ?, ?)
|
|
268
|
-
ON CONFLICT(entry_id) DO UPDATE SET
|
|
269
|
-
utility = ?,
|
|
270
|
-
updated_at = ?
|
|
271
|
-
`).run(entryId, result.nextUtility, now, now, result.nextUtility, now);
|
|
272
|
-
return result;
|
|
273
|
-
}
|
|
274
244
|
/**
|
|
275
245
|
* Every indexed entry with its `utility_scores` row (when it has one), for
|
|
276
246
|
* `akm metrics`. `utility_scores` is keyed by the unstable `entry_id`, so the
|
|
@@ -140,6 +140,10 @@ export function getNeighborsByEntryId(db, id, k, scope) {
|
|
|
140
140
|
return [];
|
|
141
141
|
return searchVec(db, new Float32Array(row.embedding.slice().buffer), k, scope);
|
|
142
142
|
}
|
|
143
|
+
/** Whether the entry has a stored vector (what {@link getNeighborsByEntryId} reads). */
|
|
144
|
+
export function hasStoredEmbedding(db, id) {
|
|
145
|
+
return db.prepare("SELECT 1 FROM embeddings WHERE id = ?").get(id) != null;
|
|
146
|
+
}
|
|
143
147
|
/**
|
|
144
148
|
* Every entry that has no embedding row for `model` (any row when no model is
|
|
145
149
|
* given), with its embedding input derived from the stored document
|
|
@@ -67,10 +67,6 @@ export function decodeTaskHistoryMetadata(input) {
|
|
|
67
67
|
// A malformed `engine` (wrong type) is dropped like any other unrecognized
|
|
68
68
|
// field rather than rejecting the whole row over a non-essential value.
|
|
69
69
|
const engine = parsed.engine === null || typeof parsed.engine === "string" ? parsed.engine : undefined;
|
|
70
|
-
if (parsed.targetVocab !== undefined && parsed.targetVocab !== 2) {
|
|
71
|
-
warnOnce(`task-history-target-vocab:${String(parsed.targetVocab)}`, `task_history row has targetVocab ${String(parsed.targetVocab)}, newer than this akm's 2 — falling back to ` +
|
|
72
|
-
"the legacy target_kind mapping rather than rejecting the row.");
|
|
73
|
-
}
|
|
74
70
|
validateDetail(detail);
|
|
75
71
|
const cleanDetail = detail
|
|
76
72
|
? {
|
|
@@ -85,7 +81,6 @@ export function decodeTaskHistoryMetadata(input) {
|
|
|
85
81
|
durationMs: parsed.durationMs,
|
|
86
82
|
detail: cleanDetail,
|
|
87
83
|
...(engine !== undefined ? { engine } : {}),
|
|
88
|
-
...(parsed.targetVocab === 2 ? { targetVocab: 2 } : {}),
|
|
89
84
|
};
|
|
90
85
|
}
|
|
91
86
|
/**
|
|
@@ -353,15 +353,6 @@ export class WorkflowRunsRepository {
|
|
|
353
353
|
return db.prepare("SELECT * FROM workflow_runs WHERE id = ?").get(input.run.id);
|
|
354
354
|
});
|
|
355
355
|
}
|
|
356
|
-
/**
|
|
357
|
-
* Persist a run's resolved declared `outputs:` (migration 024, P3b §4.3).
|
|
358
|
-
* Called from INSIDE `completeWorkflowStep`'s own write transaction — this
|
|
359
|
-
* method opens none of its own, matching `updateStepCompletion` /
|
|
360
|
-
* `updateRunState` immediately above.
|
|
361
|
-
*/
|
|
362
|
-
setRunOutputs(runId, outputsJson) {
|
|
363
|
-
this.db.prepare("UPDATE workflow_runs SET outputs_json = ? WHERE id = ?").run(outputsJson, runId);
|
|
364
|
-
}
|
|
365
356
|
/** Every child run published under `parentRunId`, oldest first. `[]` when none. */
|
|
366
357
|
childRunsOf(parentRunId) {
|
|
367
358
|
return this.db
|
|
@@ -432,13 +423,13 @@ export class WorkflowRunsRepository {
|
|
|
432
423
|
const dispatchId = randomUUID();
|
|
433
424
|
db.prepare(`INSERT INTO workflow_run_unit_attempts (
|
|
434
425
|
run_id, unit_id, attempt, dispatch_id, step_id, node_id, phase,
|
|
435
|
-
runner, engine, model, input_hash, status,
|
|
426
|
+
runner, engine, model, input_hash, status, started_at,
|
|
436
427
|
claim_holder, claim_expires_at
|
|
437
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?,
|
|
428
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?)`).run(input.runId, input.unitId, attemptNumber, dispatchId, input.stepId, input.nodeId, input.phase, input.runner, input.engine, input.model, input.inputHash, input.now, holder, input.now);
|
|
438
429
|
db.prepare(`INSERT INTO workflow_run_units (
|
|
439
430
|
run_id, unit_id, step_id, node_id, parent_unit_id, phase, runner, engine, model,
|
|
440
|
-
status, input_hash,
|
|
441
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?,
|
|
431
|
+
status, input_hash, started_at, claim_holder, claim_expires_at, attempts
|
|
432
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?, ?, ?)
|
|
442
433
|
ON CONFLICT(run_id, unit_id) DO UPDATE SET
|
|
443
434
|
step_id = excluded.step_id,
|
|
444
435
|
node_id = excluded.node_id,
|
|
@@ -449,7 +440,6 @@ export class WorkflowRunsRepository {
|
|
|
449
440
|
model = excluded.model,
|
|
450
441
|
status = 'running',
|
|
451
442
|
input_hash = excluded.input_hash,
|
|
452
|
-
worktree_path = excluded.worktree_path,
|
|
453
443
|
started_at = excluded.started_at,
|
|
454
444
|
claim_holder = excluded.claim_holder,
|
|
455
445
|
claim_expires_at = excluded.claim_expires_at,
|
|
@@ -458,7 +448,7 @@ export class WorkflowRunsRepository {
|
|
|
458
448
|
failure_reason = NULL,
|
|
459
449
|
session_id = NULL,
|
|
460
450
|
finished_at = NULL,
|
|
461
|
-
attempts = excluded.attempts`).run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId ?? null, input.phase, input.runner, input.engine, input.model, input.inputHash, input.
|
|
451
|
+
attempts = excluded.attempts`).run(input.runId, input.unitId, input.stepId, input.nodeId, input.parentUnitId ?? null, input.phase, input.runner, input.engine, input.model, input.inputHash, input.now, holder, input.now, attemptNumber);
|
|
462
452
|
insertEventStrict(db, {
|
|
463
453
|
eventType: "workflow_unit_started",
|
|
464
454
|
ts: input.now,
|
package/dist/tasks/embedded.js
CHANGED
|
@@ -25,7 +25,8 @@ import { getDirname } from "../runtime.js";
|
|
|
25
25
|
import { parseTaskSource } from "./source/parse-task-source.js";
|
|
26
26
|
/** Directory holding the bundled task template categories. */
|
|
27
27
|
const TASKS_ASSETS_DIR = path.join(getDirname(import.meta.url), "../assets/tasks");
|
|
28
|
-
|
|
28
|
+
/** Templates setup offers but ships disabled (`category/id`). None ship disabled since catchup was removed (#1130). */
|
|
29
|
+
const DEFAULT_DISABLED_TASKS = new Set();
|
|
29
30
|
/**
|
|
30
31
|
* Enumerate the embedded task templates from every category subdirectory of
|
|
31
32
|
* the bundled assets directory. Sorted by category then id for deterministic
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { dispatchPreparedCommandInvocation } from "../../commands/command/command-execution.js";
|
|
5
5
|
import { finishAttempt } from "./attempt-lifecycle.js";
|
|
6
|
+
import { DEFAULT_SCHEDULED_TASK_TIMEOUT_MS } from "./run-workflow-task.js";
|
|
6
7
|
import { appendHistory } from "./task-history.js";
|
|
7
8
|
import { persistRunLog, scrubTaskOutput, streamLines } from "./task-log.js";
|
|
8
9
|
export async function runPreparedCommandTask(input) {
|
|
@@ -15,7 +16,14 @@ export async function runPreparedCommandTask(input) {
|
|
|
15
16
|
// into the dispatched engine's child env and the recorded usage events.
|
|
16
17
|
eventSource: provenance.eventSource,
|
|
17
18
|
};
|
|
18
|
-
|
|
19
|
+
// Unset anywhere (task `timeout:`, command asset, engine) → the unattended
|
|
20
|
+
// default; an explicit value or `null` (opt-out) already sits on the request.
|
|
21
|
+
const { request, runner } = task.invocation;
|
|
22
|
+
const unset = !Object.hasOwn(request.runtime, "timeoutMs") && runner.timeoutMs === undefined;
|
|
23
|
+
const invocation = unset
|
|
24
|
+
? { ...task.invocation, runner: { ...runner, timeoutMs: DEFAULT_SCHEDULED_TASK_TIMEOUT_MS } }
|
|
25
|
+
: task.invocation;
|
|
26
|
+
const result = await dispatchPreparedCommandInvocation(invocation, dispatchOptions);
|
|
19
27
|
const engineName = result.engine;
|
|
20
28
|
const finishedAt = finishAttempt(startedAt, now());
|
|
21
29
|
const log = renderPromptLog({ task, engineName, result, notices: result.notices, warnings: result.warnings });
|
|
@@ -50,11 +50,12 @@ import { persistRunLog, scrubTaskOutput } from "./task-log.js";
|
|
|
50
50
|
*/
|
|
51
51
|
export const DEFAULT_WORKFLOW_TASK_TIMEOUT_MS = 6 * 60 * 60 * 1000;
|
|
52
52
|
/**
|
|
53
|
-
* The same unattended default for
|
|
53
|
+
* The same unattended default for the other scheduled task kinds: `uses: commands/…`
|
|
54
|
+
* command tasks and native shell/script tasks.
|
|
54
55
|
*
|
|
55
56
|
* The reasoning above is about SCHEDULED runs, not about workflows: nobody is
|
|
56
|
-
* watching, and one wedged run silently stops the schedule.
|
|
57
|
-
* defaulted to `null` (no kill timer) and
|
|
57
|
+
* watching, and one wedged run silently stops the schedule. Native shell/script
|
|
58
|
+
* tasks defaulted to `null` (no kill timer) and command tasks inherited
|
|
58
59
|
* DEFAULT_AGENT_TIMEOUT_MS, also null — so a hung `curl`, a prompting agent
|
|
59
60
|
* waiting on stdin, or a stuck engine wedged the task forever while the
|
|
60
61
|
* workflow arm was protected. Same value, same opt-out: an explicit
|
|
@@ -49,8 +49,6 @@ export function appendHistory(result, historyReserved = false) {
|
|
|
49
49
|
metadataVersion: 2,
|
|
50
50
|
durationMs: result.durationMs,
|
|
51
51
|
detail: result.detail ?? null,
|
|
52
|
-
// D8 (spec §5.3): every NEW row carries the vocabulary marker.
|
|
53
|
-
targetVocab: 2,
|
|
54
52
|
...(result.target.kind === "command" ? { engine: result.target.engine } : {}),
|
|
55
53
|
}),
|
|
56
54
|
};
|
|
@@ -20,7 +20,7 @@ import path from "node:path";
|
|
|
20
20
|
import { loadConfig } from "../../core/config/config.js";
|
|
21
21
|
import { rethrowIfTestIsolationError } from "../../core/errors.js";
|
|
22
22
|
import { buildTaskRunId, insertTaskLogLines, openLogsDatabase, } from "../../core/logs-db.js";
|
|
23
|
-
import { getTaskLogDir } from "../../core/paths.js";
|
|
23
|
+
import { getRunLogDir, getTaskLogDir } from "../../core/paths.js";
|
|
24
24
|
import { redactCredentialPatterns, redactSensitiveText } from "../../core/redaction.js";
|
|
25
25
|
import { collectTaskLogSensitiveValues } from "../log-redaction.js";
|
|
26
26
|
function taskLogPath(logDir, taskId, startedAtIso) {
|
|
@@ -95,6 +95,43 @@ export function purgeOldTaskLogFiles(logDir, retentionDays = 90) {
|
|
|
95
95
|
}
|
|
96
96
|
return deleted;
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Delete the flat per-run `akm improve` and `akm index` log files
|
|
100
|
+
* (`$CACHE/logs/{improve,index}/<ISO>.log`) older than `retentionDays`. Same
|
|
101
|
+
* best-effort contract as {@link purgeOldTaskLogFiles}; `.log` files directly
|
|
102
|
+
* inside each directory only.
|
|
103
|
+
*/
|
|
104
|
+
export function purgeOldRunLogFiles(retentionDays = 90) {
|
|
105
|
+
if (!Number.isFinite(retentionDays) || retentionDays <= 0)
|
|
106
|
+
return 0;
|
|
107
|
+
const cutoffMs = Date.now() - retentionDays * 86_400_000;
|
|
108
|
+
let deleted = 0;
|
|
109
|
+
for (const dir of [getRunLogDir("improve"), getRunLogDir("index")]) {
|
|
110
|
+
let entries;
|
|
111
|
+
try {
|
|
112
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
rethrowIfTestIsolationError(error);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (!entry.isFile() || !entry.name.endsWith(".log"))
|
|
120
|
+
continue;
|
|
121
|
+
const filePath = path.join(dir, entry.name);
|
|
122
|
+
try {
|
|
123
|
+
if (fs.statSync(filePath).mtimeMs < cutoffMs) {
|
|
124
|
+
fs.unlinkSync(filePath);
|
|
125
|
+
deleted++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
rethrowIfTestIsolationError(error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return deleted;
|
|
134
|
+
}
|
|
98
135
|
/**
|
|
99
136
|
* Redact logs.db rows against the SAME contiguous text the file sink sees.
|
|
100
137
|
*
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
* The two per-source schedule gates `akm task sync` runs before it installs a
|
|
6
|
+
* schedule, kept in a leaf module so `akm lint` can run them without importing
|
|
7
|
+
* the scheduler's task-preparation graph (which reaches the adapter registry
|
|
8
|
+
* that lint itself is part of).
|
|
9
|
+
*/
|
|
10
|
+
import { UsageError } from "../core/errors.js";
|
|
11
|
+
import { applyInputDefaults, validateInputs } from "../execution/input-contract.js";
|
|
12
|
+
import { parseSchedule } from "./schedule.js";
|
|
13
|
+
/**
|
|
14
|
+
* Validate every v4 `schedule:` entry's `inputs` against the task's own
|
|
15
|
+
* declared contract with defaults applied — the exact set of values a
|
|
16
|
+
* compiled invocation delivers — so a violation is reported at sync rather
|
|
17
|
+
* than when the scheduler fires. Shared with `akm lint`.
|
|
18
|
+
*/
|
|
19
|
+
export function assertTaskScheduleInputsSatisfyContract(v4, refLabel) {
|
|
20
|
+
const contract = v4.inputs ?? {};
|
|
21
|
+
for (const scheduleEntry of v4.schedule) {
|
|
22
|
+
const defaultedInputs = applyInputDefaults(contract, { ...scheduleEntry.inputs });
|
|
23
|
+
const errors = validateInputs(contract, defaultedInputs);
|
|
24
|
+
if (errors.length > 0) {
|
|
25
|
+
throw new UsageError(`Task ${JSON.stringify(refLabel)} schedule[${scheduleEntry.ordinal}].inputs does not satisfy ` +
|
|
26
|
+
`its declared inputs once defaults are applied: ${errors.join("; ")}`, "TASK_SOURCE_INVALID");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate every v4 `schedule:` entry's `cron` against the active backend's
|
|
32
|
+
* dialect: cron is the most permissive of the three, so a task authored on
|
|
33
|
+
* Linux can carry an expression launchd/schtasks cannot translate. Shared
|
|
34
|
+
* with `akm lint`.
|
|
35
|
+
*/
|
|
36
|
+
export function assertTaskScheduleCronValid(v4, backend) {
|
|
37
|
+
for (const scheduleEntry of v4.schedule)
|
|
38
|
+
parseSchedule(scheduleEntry.cron, backend);
|
|
39
|
+
}
|
|
@@ -62,22 +62,6 @@ function schedulerInputFlagValueText(value) {
|
|
|
62
62
|
return String(value);
|
|
63
63
|
return canonicalInputJson(value);
|
|
64
64
|
}
|
|
65
|
-
export function compileWorkflowSchedulerBindings(input) {
|
|
66
|
-
const ref = assertQualifiedRef(input.qualifiedRef, "workflow");
|
|
67
|
-
return Object.freeze(input.schedules.map((schedule) => {
|
|
68
|
-
const bindingId = digestBindingId("workflow", ref, schedule.ordinal);
|
|
69
|
-
return freezeBinding({
|
|
70
|
-
id: bindingId,
|
|
71
|
-
nativeId: bindingId,
|
|
72
|
-
logicalSource: { kind: "workflow", ref },
|
|
73
|
-
cron: schedule.cron,
|
|
74
|
-
source: schedule.source,
|
|
75
|
-
ordinal: schedule.ordinal,
|
|
76
|
-
enabled: true,
|
|
77
|
-
invocation: ["workflow", "run", ref],
|
|
78
|
-
});
|
|
79
|
-
}));
|
|
80
|
-
}
|
|
81
65
|
/**
|
|
82
66
|
* Map a logical binding id to the flat portable token persisted by native
|
|
83
67
|
* schedulers. Flat ids are byte-for-byte stable; only nested standalone
|