akm-cli 0.8.6 → 0.8.14
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 +442 -0
- package/dist/assets/help/help-proposals.md +1 -2
- package/dist/assets/hints/cli-hints-full.md +34 -19
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/profiles/catchup.json +13 -0
- package/dist/assets/profiles/consolidate.json +13 -0
- package/dist/assets/profiles/frequent.json +13 -0
- package/dist/assets/tasks/core/backup.yml +4 -0
- package/dist/assets/tasks/core/extract.yml +4 -0
- package/dist/assets/tasks/core/improve.yml +4 -0
- package/dist/assets/tasks/core/index-refresh.yml +4 -0
- package/dist/assets/tasks/core/sync.yml +4 -0
- package/dist/assets/tasks/core/update-stashes.yml +4 -0
- package/dist/assets/tasks/core/version-check.yml +4 -0
- package/dist/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +560 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
- package/dist/cli/config-migrate.js +6 -6
- package/dist/cli/config-validate.js +4 -4
- package/dist/cli/confirm.js +3 -3
- package/dist/cli/parse-args.js +1 -1
- package/dist/cli/shared.js +72 -19
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +206 -3866
- package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
- package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
- package/dist/commands/agent/contribute-cli.js +200 -0
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +230 -3
- package/dist/commands/db-cli.js +2 -2
- package/dist/commands/env/env-cli.js +529 -0
- package/dist/commands/env/env.js +410 -0
- package/dist/commands/env/secret-cli.js +259 -0
- package/dist/commands/{secret.js → env/secret.js} +6 -47
- package/dist/commands/events.js +4 -4
- package/dist/commands/feedback-cli.js +18 -34
- package/dist/commands/graph/graph-cli.js +132 -0
- package/dist/commands/{graph.js → graph/graph.js} +22 -16
- package/dist/commands/health/checks.js +279 -0
- package/dist/commands/health/html-report.js +448 -0
- package/dist/commands/health.js +189 -266
- package/dist/commands/{consolidate.js → improve/consolidate.js} +63 -38
- package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
- package/dist/commands/{distill.js → improve/distill.js} +39 -18
- package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
- package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
- package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
- package/dist/commands/{extract.js → improve/extract.js} +221 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
- package/dist/commands/{improve.js → improve/improve.js} +672 -292
- package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
- package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
- package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
- package/dist/commands/improve/session-asset.js +248 -0
- package/dist/commands/lint/agent-linter.js +1 -1
- package/dist/commands/lint/base-linter.js +55 -37
- package/dist/commands/lint/command-linter.js +1 -1
- package/dist/commands/lint/default-linter.js +1 -1
- package/dist/commands/lint/env-key-rules.js +1 -1
- package/dist/commands/lint/index.js +19 -25
- package/dist/commands/lint/knowledge-linter.js +1 -1
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +8 -8
- package/dist/commands/lint/skill-linter.js +1 -1
- package/dist/commands/lint/task-linter.js +1 -1
- package/dist/commands/lint/workflow-linter.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/observability-cli.js +244 -0
- package/dist/commands/proposal/drain-policies.js +3 -3
- package/dist/commands/proposal/drain.js +87 -15
- package/dist/commands/proposal/proposal-cli.js +490 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
- package/dist/commands/{propose.js → proposal/propose.js} +11 -11
- package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
- package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
- package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
- package/dist/commands/{curate.js → read/curate.js} +7 -7
- package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
- package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
- package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
- package/dist/commands/read/search-cli.js +207 -0
- package/dist/commands/{search.js → read/search.js} +22 -27
- package/dist/commands/{show.js → read/show.js} +31 -45
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +14 -10
- package/dist/commands/sources/add-cli.js +293 -0
- package/dist/commands/{history.js → sources/history.js} +27 -25
- package/dist/commands/{info.js → sources/info.js} +6 -6
- package/dist/commands/{init.js → sources/init.js} +6 -6
- package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
- package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
- package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
- package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
- package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
- package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
- package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
- package/dist/commands/sources/sources-cli.js +305 -0
- package/dist/commands/sources/stash-cli.js +219 -0
- package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
- package/dist/commands/tasks/default-tasks.js +173 -0
- package/dist/commands/tasks/tasks-cli.js +210 -0
- package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
- package/dist/commands/wiki-cli.js +307 -0
- package/dist/commands/workflow-cli.js +329 -0
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/assert.js +40 -0
- package/dist/core/asset/asset-create.js +54 -0
- package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
- package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
- package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
- package/dist/core/{markdown.js → asset/markdown.js} +1 -1
- package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
- package/dist/core/best-effort.js +64 -0
- package/dist/core/common.js +32 -18
- package/dist/core/{config-io.js → config/config-io.js} +29 -19
- package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
- package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
- package/dist/core/config/config-types.js +16 -0
- package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
- package/dist/core/{config.js → config/config.js} +10 -8
- package/dist/core/env-secret-ref.js +90 -0
- package/dist/core/errors.js +13 -3
- package/dist/core/events.js +27 -4
- package/dist/core/file-lock.js +1 -1
- package/dist/core/improve-types.js +48 -0
- package/dist/core/lesson-lint.js +2 -2
- package/dist/core/logs-db.js +304 -0
- package/dist/core/paths.js +2 -2
- package/dist/core/ripgrep/install.js +2 -2
- package/dist/core/ripgrep/resolve.js +2 -2
- package/dist/core/state-db.js +195 -60
- package/dist/core/text-truncation.js +148 -0
- package/dist/core/time.js +1 -1
- package/dist/core/write-source.js +98 -85
- package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
- package/dist/indexer/{db.js → db/db.js} +128 -118
- package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
- package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
- package/dist/indexer/ensure-index.js +4 -4
- package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
- package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
- package/dist/indexer/indexer.js +37 -30
- package/dist/indexer/init.js +54 -0
- package/dist/indexer/manifest.js +10 -10
- package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
- package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
- package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
- package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
- package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
- package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
- package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
- package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
- package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
- package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
- package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
- package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
- package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
- package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
- package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
- package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
- package/dist/indexer/{walker.js → walk/walker.js} +4 -3
- package/dist/integrations/agent/builder-shared.js +39 -0
- package/dist/integrations/agent/builders.js +14 -81
- package/dist/integrations/agent/config.js +6 -4
- package/dist/integrations/agent/detect.js +1 -1
- package/dist/integrations/agent/index.js +23 -8
- package/dist/integrations/agent/prompts.js +2 -3
- package/dist/integrations/agent/runner.js +22 -3
- package/dist/integrations/agent/spawn.js +9 -10
- package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
- package/dist/integrations/harnesses/claude/config-import.js +70 -0
- package/dist/integrations/harnesses/claude/index.js +64 -0
- package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
- package/dist/integrations/harnesses/index.js +144 -0
- package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
- package/dist/integrations/harnesses/opencode/config-import.js +82 -0
- package/dist/integrations/harnesses/opencode/index.js +59 -0
- package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
- package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
- package/dist/integrations/harnesses/types.js +43 -0
- package/dist/integrations/lockfile.js +7 -16
- package/dist/integrations/session-logs/index.js +82 -9
- package/dist/llm/call-ai.js +4 -4
- package/dist/llm/client.js +146 -6
- package/dist/llm/embedder.js +6 -6
- package/dist/llm/embedders/local.js +9 -22
- package/dist/llm/embedders/remote.js +2 -2
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +31 -12
- package/dist/llm/index-passes.js +1 -1
- package/dist/llm/memory-infer.js +12 -5
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +9 -46
- package/dist/output/html-render.js +73 -0
- package/dist/output/renderers.js +88 -58
- package/dist/output/shapes/curate.js +7 -3
- package/dist/output/shapes/distill.js +7 -3
- package/dist/output/shapes/env-list.js +18 -16
- package/dist/output/shapes/events.js +5 -4
- package/dist/output/shapes/helpers.js +19 -5
- package/dist/output/shapes/history.js +7 -3
- package/dist/output/shapes/passthrough.js +8 -11
- package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
- package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
- package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
- package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
- package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
- package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
- package/dist/output/shapes/registry-search.js +7 -3
- package/dist/output/shapes/registry.js +12 -0
- package/dist/output/shapes/search.js +7 -3
- package/dist/output/shapes/secret-list.js +18 -16
- package/dist/output/shapes/show.js +7 -3
- package/dist/output/shapes.js +55 -30
- package/dist/output/text/add.js +2 -3
- package/dist/output/text/clone.js +2 -3
- package/dist/output/text/config.js +2 -3
- package/dist/output/text/curate.js +4 -3
- package/dist/output/text/distill.js +2 -3
- package/dist/output/text/enable-disable.js +5 -4
- package/dist/output/text/env.js +13 -0
- package/dist/output/text/events.js +5 -4
- package/dist/output/text/feedback.js +4 -3
- package/dist/output/text/helpers.js +123 -40
- package/dist/output/text/history.js +2 -3
- package/dist/output/text/import.js +2 -3
- package/dist/output/text/index.js +2 -3
- package/dist/output/text/info.js +2 -3
- package/dist/output/text/init.js +2 -3
- package/dist/output/text/list.js +2 -3
- package/dist/output/text/proposal/producer.js +9 -0
- package/dist/output/text/proposal/proposal.js +13 -0
- package/dist/output/text/registry-commands.js +8 -7
- package/dist/output/text/registry.js +12 -0
- package/dist/output/text/remember.js +4 -3
- package/dist/output/text/remove.js +2 -3
- package/dist/output/text/save.js +2 -3
- package/dist/output/text/search.js +4 -3
- package/dist/output/text/show.js +4 -3
- package/dist/output/text/update.js +2 -3
- package/dist/output/text/upgrade.js +2 -3
- package/dist/output/text/wiki.js +12 -11
- package/dist/output/text/workflow.js +12 -10
- package/dist/output/text.js +66 -32
- package/dist/registry/build-index.js +11 -10
- package/dist/registry/factory.js +1 -1
- package/dist/registry/origin-resolve.js +1 -1
- package/dist/registry/providers/index.js +2 -2
- package/dist/registry/providers/skills-sh.js +91 -72
- package/dist/registry/providers/static-index.js +75 -52
- package/dist/registry/resolve.js +3 -3
- package/dist/runtime.js +242 -0
- package/dist/scripts/migrate-storage.js +1654 -683
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
- package/dist/setup/detect.js +311 -9
- package/dist/setup/harness-config-import.js +6 -120
- package/dist/setup/setup.js +454 -43
- package/dist/sources/include.js +1 -1
- package/dist/sources/provider-factory.js +2 -2
- package/dist/sources/providers/filesystem.js +3 -3
- package/dist/sources/providers/git.js +9 -9
- package/dist/sources/providers/index.js +4 -4
- package/dist/sources/providers/npm.js +6 -6
- package/dist/sources/providers/provider-utils.js +13 -20
- package/dist/sources/providers/sync-from-ref.js +5 -5
- package/dist/sources/providers/tar-utils.js +2 -2
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/website-ingest.js +5 -5
- package/dist/storage/database.js +102 -0
- package/dist/storage/engines/sqlite-migrations.js +42 -0
- package/dist/storage/locations.js +25 -0
- package/dist/storage/repositories/index-db.js +43 -0
- package/dist/storage/repositories/workflow-runs-repository.js +141 -0
- package/dist/tasks/backends/cron.js +4 -4
- package/dist/tasks/backends/exec-utils.js +32 -0
- package/dist/tasks/backends/index.js +3 -3
- package/dist/tasks/backends/launchd.js +7 -14
- package/dist/tasks/backends/schtasks.js +7 -16
- package/dist/tasks/embedded.js +71 -0
- package/dist/tasks/parser.js +2 -2
- package/dist/tasks/resolveAkmBin.js +1 -1
- package/dist/tasks/runner.js +127 -31
- package/dist/tasks/schedule.js +1 -1
- package/dist/tasks/validator.js +7 -7
- package/dist/text-import-hook.mjs +51 -0
- package/dist/version.js +2 -1
- package/dist/wiki/wiki.js +7 -7
- package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
- package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
- package/dist/workflows/cli.js +1 -1
- package/dist/workflows/db.js +54 -32
- package/dist/workflows/parser.js +4 -4
- package/dist/workflows/renderer.js +5 -5
- package/dist/workflows/runtime/agent-identity.js +56 -0
- package/dist/workflows/runtime/checkin.js +57 -0
- package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
- package/dist/workflows/validate-summary.js +82 -0
- package/docs/README.md +1 -1
- package/docs/data-and-telemetry.md +6 -6
- package/package.json +17 -8
- package/dist/commands/add-cli.js +0 -279
- package/dist/commands/env.js +0 -213
- package/dist/integrations/agent/sdk-runner.js +0 -126
- package/dist/output/shapes/vault-list.js +0 -19
- package/dist/output/text/proposal-producer.js +0 -8
- package/dist/output/text/proposal.js +0 -12
- package/dist/output/text/vault.js +0 -16
- /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
- /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
- /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
- /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
- /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
- /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
- /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
- /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
* assert.ts — exhaustiveness keystone.
|
|
6
|
+
*
|
|
7
|
+
* `assertNever` is the single helper the exhaustive-switch refactors consume.
|
|
8
|
+
* Placing it in the `never` arm of a `switch`/`if` chain turns any unhandled
|
|
9
|
+
* union variant into a *compile-time* error (the argument no longer narrows to
|
|
10
|
+
* `never`), and — if the impossible case is somehow reached at runtime — throws
|
|
11
|
+
* with the offending value serialized for diagnostics instead of silently
|
|
12
|
+
* falling through.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Assert that a code path is unreachable.
|
|
16
|
+
*
|
|
17
|
+
* Call this in the default/else arm of an exhaustive dispatch over a union. If
|
|
18
|
+
* a new variant is added to the union without a handling arm, the call site
|
|
19
|
+
* stops type-checking, surfacing the drift at compile time.
|
|
20
|
+
*
|
|
21
|
+
* @param x the value the type system has narrowed to `never`
|
|
22
|
+
* @param context optional label included in the thrown message for diagnostics
|
|
23
|
+
* @throws always — this function never returns
|
|
24
|
+
*/
|
|
25
|
+
export function assertNever(x, context) {
|
|
26
|
+
let serialized;
|
|
27
|
+
try {
|
|
28
|
+
serialized = JSON.stringify(x);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Circular structures / non-serializable values fall back to String().
|
|
32
|
+
serialized = String(x);
|
|
33
|
+
}
|
|
34
|
+
if (serialized === undefined) {
|
|
35
|
+
// JSON.stringify(undefined) returns undefined, not a string.
|
|
36
|
+
serialized = String(x);
|
|
37
|
+
}
|
|
38
|
+
const where = context ? ` (${context})` : "";
|
|
39
|
+
throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
* Shared `--path` / `--name` semantics for asset-creating commands
|
|
6
|
+
* (`remember`, `import`, `propose`, `workflow create`, ...).
|
|
7
|
+
*
|
|
8
|
+
* The contract, applied consistently across the command surface:
|
|
9
|
+
* - `--name` (or the name positional) is a FLAT asset name — no `/`.
|
|
10
|
+
* - `--path` is a relative directory, applied rooted at the asset's type
|
|
11
|
+
* directory. The final asset name is `<path>/<name>`.
|
|
12
|
+
*
|
|
13
|
+
* Subdirectory placement is `--path`'s job, not `--name`'s. System-derived
|
|
14
|
+
* names (e.g. a URL-path-derived knowledge name) are exempt — only the user's
|
|
15
|
+
* explicit name should be flat-checked, at the command layer.
|
|
16
|
+
*/
|
|
17
|
+
import { UsageError } from "../errors.js";
|
|
18
|
+
/**
|
|
19
|
+
* Normalise an optional `--path` value: a relative directory, applied rooted
|
|
20
|
+
* at the asset's type directory (e.g. `personal/projects` under `memories/`).
|
|
21
|
+
* Rejects absolute paths and `.`/`..` segments. Returns `""` when unset.
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeCreateSubPath(subPath) {
|
|
24
|
+
if (subPath === undefined)
|
|
25
|
+
return "";
|
|
26
|
+
const trimmed = subPath
|
|
27
|
+
.trim()
|
|
28
|
+
.replace(/\\/g, "/")
|
|
29
|
+
.replace(/^\/+|\/+$/g, "");
|
|
30
|
+
if (!trimmed)
|
|
31
|
+
return "";
|
|
32
|
+
if (trimmed.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
33
|
+
throw new UsageError("--path must be a relative directory without '.' or '..' segments.");
|
|
34
|
+
}
|
|
35
|
+
return trimmed;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Enforce that an explicit, user-supplied name is a flat (single-segment)
|
|
39
|
+
* name. A `/` in the name is rejected with guidance to use `--path`. Applied
|
|
40
|
+
* at the command layer to the user's name only — system-derived names may nest.
|
|
41
|
+
*/
|
|
42
|
+
export function assertFlatAssetName(name) {
|
|
43
|
+
if (name?.replace(/\\/g, "/").replace(/\.md$/i, "").includes("/")) {
|
|
44
|
+
throw new UsageError("Asset --name must be a flat name without '/'. Use --path to choose a subdirectory " +
|
|
45
|
+
"(e.g. --path personal --name grocery-list).");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Combine a normalised `--path` subdirectory with a flat base name into the
|
|
50
|
+
* nested asset name the path resolver expects. `subPath` may be `""`.
|
|
51
|
+
*/
|
|
52
|
+
export function combineCreatePath(subPath, baseName) {
|
|
53
|
+
return subPath ? `${subPath}/${baseName}` : baseName;
|
|
54
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { isAssetType } from "
|
|
6
|
-
import { UsageError } from "
|
|
5
|
+
import { isAssetType } from "../common.js";
|
|
6
|
+
import { UsageError } from "../errors.js";
|
|
7
7
|
/** Accepted spelling aliases mapping to a canonical asset type. */
|
|
8
8
|
const TYPE_ALIASES = {
|
|
9
9
|
environment: "env",
|
|
@@ -30,6 +30,19 @@ export function makeAssetRef(type, name, origin) {
|
|
|
30
30
|
return asset;
|
|
31
31
|
return `${origin}//${asset}`;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Serialize a parsed {@link AssetRef} value-object back to its canonical
|
|
35
|
+
* `[origin//]type:name` string form. The single formatter for refs — call
|
|
36
|
+
* this instead of hand-building `${type}:${name}` template strings so the
|
|
37
|
+
* serialization rules (origin prefix, name normalization) live in one place
|
|
38
|
+
* and stay in lockstep with {@link parseAssetRef}.
|
|
39
|
+
*
|
|
40
|
+
* `refToString(parseAssetRef(s))` round-trips for any `s` that
|
|
41
|
+
* `parseAssetRef` accepts.
|
|
42
|
+
*/
|
|
43
|
+
export function refToString(ref) {
|
|
44
|
+
return makeAssetRef(ref.type, ref.name, ref.origin);
|
|
45
|
+
}
|
|
33
46
|
// ── Parsing ─────────────────────────────────────────────────────────────────
|
|
34
47
|
/**
|
|
35
48
|
* Parse a ref string in the format `[origin//]type:name`.
|
|
@@ -53,9 +66,13 @@ export function parseAssetRef(ref) {
|
|
|
53
66
|
}
|
|
54
67
|
const rawType = body.slice(0, colon);
|
|
55
68
|
const rawName = body.slice(colon + 1);
|
|
69
|
+
// The `vault` asset type was removed in 0.9.0. Point callers at its
|
|
70
|
+
// replacements rather than failing with a generic unknown-type error.
|
|
71
|
+
if (rawType === "vault") {
|
|
72
|
+
throw new UsageError("The `vault` asset type was removed in 0.9.0 — use `env:` (whole .env config) or `secret:` (a single value).", "MISSING_REQUIRED_ARGUMENT");
|
|
73
|
+
}
|
|
56
74
|
// Type aliases: `environment:` is an accepted spelling of the canonical
|
|
57
|
-
// `env:` type.
|
|
58
|
-
// `vaults/` copy keeps resolving through the 0.8.x window.)
|
|
75
|
+
// `env:` type.
|
|
59
76
|
const resolvedType = TYPE_ALIASES[rawType] ?? rawType;
|
|
60
77
|
if (!isAssetType(resolvedType)) {
|
|
61
78
|
throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* `db-search.ts` import from, eliminating the import-order dependency
|
|
14
14
|
* entirely.
|
|
15
15
|
*/
|
|
16
|
-
import { buildWorkflowAction } from "
|
|
16
|
+
import { buildWorkflowAction } from "../../output/renderers.js";
|
|
17
17
|
/** Map asset types to their primary renderer names. */
|
|
18
18
|
export const TYPE_TO_RENDERER = {
|
|
19
19
|
script: "script-source",
|
|
@@ -25,10 +25,10 @@ export const TYPE_TO_RENDERER = {
|
|
|
25
25
|
memory: "memory-md",
|
|
26
26
|
workflow: "workflow-md",
|
|
27
27
|
env: "env-file",
|
|
28
|
-
vault: "vault-env",
|
|
29
28
|
secret: "secret-file",
|
|
30
29
|
wiki: "wiki-md",
|
|
31
30
|
task: "task-yaml",
|
|
31
|
+
session: "session-md",
|
|
32
32
|
};
|
|
33
33
|
/** Map asset types to action builder functions for search results. */
|
|
34
34
|
export const ACTION_BUILDERS = {
|
|
@@ -41,10 +41,10 @@ export const ACTION_BUILDERS = {
|
|
|
41
41
|
memory: (ref) => `akm show ${ref} -> recall context`,
|
|
42
42
|
workflow: (ref) => buildWorkflowAction(ref),
|
|
43
43
|
env: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (the agent-safe path — values never reach stdout). akm env export ${ref} --out <file> writes a sourceable script (values to a file, not stdout).`,
|
|
44
|
-
vault: (ref) => `DEPRECATED (use env): akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with injected env`,
|
|
45
44
|
secret: (ref) => `akm show ${ref} -> name only (value never shown); akm secret path ${ref} -> file path; akm secret run ${ref} <VAR> -- <command> -> run with value injected into $VAR`,
|
|
46
45
|
wiki: (ref) => `akm show ${ref} -> read the wiki page`,
|
|
47
46
|
task: (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`,
|
|
47
|
+
session: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``,
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* Register a type-to-renderer mapping.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { buildWorkflowAction } from "
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { buildWorkflowAction } from "../../output/renderers.js";
|
|
6
|
+
import { toPosix } from "../common.js";
|
|
7
|
+
import { registerActionBuilder, registerTypeRenderer } from "./asset-registry.js";
|
|
8
8
|
const buildTaskAction = (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`;
|
|
9
9
|
const markdownSpec = {
|
|
10
10
|
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
|
|
@@ -66,9 +66,9 @@ const ASSET_SPECS_INTERNAL = {
|
|
|
66
66
|
},
|
|
67
67
|
script: { stashDir: "scripts", ...scriptSpec },
|
|
68
68
|
memory: { stashDir: "memories", ...markdownSpec },
|
|
69
|
-
// Environment assets — whole `.env` files sourced/injected wholesale.
|
|
70
|
-
// the deprecated `vault` type (
|
|
71
|
-
// are surfaced as metadata; values are never read for indexing.
|
|
69
|
+
// Environment assets — whole `.env` files sourced/injected wholesale. Replaced
|
|
70
|
+
// the deprecated `vault` type (removed in 0.9.0). Key NAMES + start-of-line
|
|
71
|
+
// comments are surfaced as metadata; values are never read for indexing.
|
|
72
72
|
env: {
|
|
73
73
|
stashDir: "env",
|
|
74
74
|
isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
|
|
@@ -91,31 +91,6 @@ const ASSET_SPECS_INTERNAL = {
|
|
|
91
91
|
rendererName: "env-file",
|
|
92
92
|
actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (values never reach stdout); akm env export ${ref} --out <file> -> write a sourceable script to a file`,
|
|
93
93
|
},
|
|
94
|
-
// DEPRECATED in 0.8.0, removed in 0.9.0 — use `env` instead. Retained so the
|
|
95
|
-
// frozen `vaults/` copy left by the migration still resolves and so existing
|
|
96
|
-
// `vault:` refs keep working through the deprecation window.
|
|
97
|
-
vault: {
|
|
98
|
-
stashDir: "vaults",
|
|
99
|
-
isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
|
|
100
|
-
toCanonicalName: (typeRoot, filePath) => {
|
|
101
|
-
const rel = toPosix(path.relative(typeRoot, filePath));
|
|
102
|
-
const fileName = path.basename(rel);
|
|
103
|
-
// Treat ".env" as the "default" vault; "<name>.env" → "<name>"
|
|
104
|
-
if (fileName === ".env") {
|
|
105
|
-
const dir = path.dirname(rel);
|
|
106
|
-
return dir === "." || dir === "" ? "default" : `${dir}/default`;
|
|
107
|
-
}
|
|
108
|
-
const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
|
|
109
|
-
return stripped;
|
|
110
|
-
},
|
|
111
|
-
toAssetPath: (typeRoot, name) => {
|
|
112
|
-
if (name === "default")
|
|
113
|
-
return path.join(typeRoot, ".env");
|
|
114
|
-
return path.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
|
|
115
|
-
},
|
|
116
|
-
rendererName: "vault-env",
|
|
117
|
-
actionBuilder: (ref) => `DEPRECATED (use env): akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with injected env`,
|
|
118
|
-
},
|
|
119
94
|
// Secrets — a single sensitive value used on its own for authentication (a
|
|
120
95
|
// PEM key, API token, TLS cert). Unlike `env` (a group of related .env
|
|
121
96
|
// configuration), the ENTIRE file is the one secret value — there is no safe
|
|
@@ -168,6 +143,17 @@ const ASSET_SPECS_INTERNAL = {
|
|
|
168
143
|
rendererName: "task-yaml",
|
|
169
144
|
actionBuilder: buildTaskAction,
|
|
170
145
|
},
|
|
146
|
+
// #561 — agent sessions indexed as a first-class searchable asset type.
|
|
147
|
+
// Generated (derived) markdown written by the `extract` pass to
|
|
148
|
+
// `sessions/<harness>/<session-id>.md`; carries `log_path` + `access`
|
|
149
|
+
// frontmatter so any agent can navigate into the raw session log. A plain
|
|
150
|
+
// markdown spec — the summary body is the searchable surface.
|
|
151
|
+
session: {
|
|
152
|
+
stashDir: "sessions",
|
|
153
|
+
...markdownSpec,
|
|
154
|
+
rendererName: "session-md",
|
|
155
|
+
actionBuilder: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``,
|
|
156
|
+
},
|
|
171
157
|
};
|
|
172
158
|
export const ASSET_SPECS = ASSET_SPECS_INTERNAL;
|
|
173
159
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import { parseFrontmatter } from "./frontmatter";
|
|
4
|
+
import { parseFrontmatter } from "./frontmatter.js";
|
|
5
5
|
// ── Parsing ─────────────────────────────────────────────────────────────────
|
|
6
6
|
export function parseMarkdownToc(content) {
|
|
7
7
|
const lines = content.split(/\r?\n/);
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { UsageError } from "
|
|
21
|
+
import { UsageError } from "../errors.js";
|
|
22
22
|
/** Root-relative directory holding a stash's meta docs. */
|
|
23
23
|
export const META_DIR = ".meta";
|
|
24
24
|
/** Default meta doc shown when no name is given (`akm show <ref>//meta`). */
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
* `bestEffort` — a single chokepoint for the project's many intentionally
|
|
6
|
+
* silent error swallows (`} catch { /* nothing *\/ }`).
|
|
7
|
+
*
|
|
8
|
+
* Historically these were scattered as bare `try/catch {}` blocks with no
|
|
9
|
+
* binding and no handling, hiding errors with no way to opt into visibility.
|
|
10
|
+
* This helper centralizes that pattern WITHOUT changing default behaviour:
|
|
11
|
+
*
|
|
12
|
+
* - It runs `fn`. On success it returns the result.
|
|
13
|
+
* - On throw it SWALLOWS the error and returns `undefined` — byte-identical
|
|
14
|
+
* to the previous bare swallow at the default verbosity level.
|
|
15
|
+
* - ONLY when verbose/debug output is enabled (`isVerbose()`) does it route a
|
|
16
|
+
* one-line diagnostic to the existing verbose stderr seam (`warnVerbose`).
|
|
17
|
+
* At the default (non-verbose) verbosity this emits nothing — no new
|
|
18
|
+
* stdout, no new stderr, no changed control flow.
|
|
19
|
+
*
|
|
20
|
+
* It deliberately does NOT add a test-isolation rethrow or any other handling:
|
|
21
|
+
* the contract is "centralize the existing silent swallow + add opt-in verbose
|
|
22
|
+
* visibility", nothing more. Sites that need `rethrowIfTestIsolationError` or
|
|
23
|
+
* any real handling already bind the error and are out of scope for this
|
|
24
|
+
* helper.
|
|
25
|
+
*/
|
|
26
|
+
import { isVerbose, warnVerbose } from "./warn.js";
|
|
27
|
+
/**
|
|
28
|
+
* Run `fn` for its side effect/value, swallowing any thrown error and
|
|
29
|
+
* returning `undefined` on failure — exactly as a bare `try { … } catch {}`
|
|
30
|
+
* would. When verbose output is enabled, the swallowed error is surfaced on the
|
|
31
|
+
* existing verbose stderr seam (prefixed with `context` when provided).
|
|
32
|
+
*
|
|
33
|
+
* @param fn the operation to attempt
|
|
34
|
+
* @param context short human-readable reason for the swallow (for verbose logs)
|
|
35
|
+
* @returns the result of `fn`, or `undefined` if it threw
|
|
36
|
+
*/
|
|
37
|
+
export function bestEffort(fn, context) {
|
|
38
|
+
try {
|
|
39
|
+
return fn();
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (isVerbose()) {
|
|
43
|
+
warnVerbose(`[akm:best-effort] ${context ? `${context}: ` : ""}swallowed error`, err);
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Async variant of {@link bestEffort}. Awaits `fn()`, swallowing any rejection
|
|
50
|
+
* and resolving to `undefined` on failure — byte-identical to a bare
|
|
51
|
+
* `try { await … } catch {}` at the default verbosity level. Surfaces the
|
|
52
|
+
* swallowed error on the verbose seam only when verbose output is enabled.
|
|
53
|
+
*/
|
|
54
|
+
export async function bestEffortAsync(fn, context) {
|
|
55
|
+
try {
|
|
56
|
+
return await fn();
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (isVerbose()) {
|
|
60
|
+
warnVerbose(`[akm:best-effort] ${context ? `${context}: ` : ""}swallowed error`, err);
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/core/common.js
CHANGED
|
@@ -4,24 +4,26 @@
|
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import { TYPE_DIRS } from "./asset-spec";
|
|
8
|
-
import { ConfigError } from "./errors";
|
|
9
|
-
import { getConfigPath, getDefaultStashDir } from "./paths";
|
|
7
|
+
import { getAssetTypes, TYPE_DIRS } from "./asset/asset-spec.js";
|
|
8
|
+
import { ConfigError } from "./errors.js";
|
|
9
|
+
import { getConfigPath, getDefaultStashDir } from "./paths.js";
|
|
10
10
|
// ── Types ───────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The canonical catalog of built-in asset types.
|
|
13
|
+
*
|
|
14
|
+
* SINGLE SOURCE OF TRUTH: derived from the {@link ASSET_SPECS} registry in
|
|
15
|
+
* `asset-spec.ts` rather than hand-maintained here. Before #490/WS7 this was a
|
|
16
|
+
* hand-written literal array that had DRIFTED from the registry (it omitted
|
|
17
|
+
* `task`, which the registry has always carried). Deriving from the registry
|
|
18
|
+
* kills that drift — see `tests/asset-type-union-source.test.ts` for the
|
|
19
|
+
* intentional-`task`-delta guard.
|
|
20
|
+
*
|
|
21
|
+
* Note: `AkmAssetType` stays a static literal union of the BUILT-IN types
|
|
22
|
+
* (those present at module-eval time). Dynamically `registerAssetType`-d types
|
|
23
|
+
* are accepted at runtime via {@link isAssetType} but are not part of the
|
|
24
|
+
* static union — identical to the pre-WS7 contract.
|
|
25
|
+
*/
|
|
26
|
+
export const ASSET_TYPES = Object.freeze([...getAssetTypes()]);
|
|
25
27
|
export const ASSET_TYPE_SET = new Set(ASSET_TYPES);
|
|
26
28
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
27
29
|
export const IS_WINDOWS = process.platform === "win32";
|
|
@@ -67,6 +69,11 @@ export function isAssetType(type) {
|
|
|
67
69
|
* The temp file is opened with the target `mode` (default 0o600) from the
|
|
68
70
|
* start, so it is never world-readable even briefly.
|
|
69
71
|
*
|
|
72
|
+
* `content` may be a string or a `Buffer`. Buffer callers (e.g. secrets, where
|
|
73
|
+
* binary certs and CRLF/LF endings must round-trip byte-exact) get the same
|
|
74
|
+
* fsync'd temp-file-plus-rename guarantees as string callers — there is a
|
|
75
|
+
* single atomic-write implementation.
|
|
76
|
+
*
|
|
70
77
|
* Durability: fsync'd against the May 2026 config-clobber incident (#472).
|
|
71
78
|
* On ext4 (data=ordered) and NVMe-with-TRIM, a power-loss inside the kernel
|
|
72
79
|
* writeback window could leave the renamed file truncated to zero — defeating
|
|
@@ -82,7 +89,14 @@ export function writeFileAtomic(target, content, mode) {
|
|
|
82
89
|
const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
|
|
83
90
|
const fd = fs.openSync(tmp, "w", mode ?? 0o600);
|
|
84
91
|
try {
|
|
85
|
-
fs.writeSync(
|
|
92
|
+
// fs.writeSync has two non-overlapping overloads (Buffer vs string); branch
|
|
93
|
+
// so each call resolves to a single overload. Both write byte-exact.
|
|
94
|
+
if (typeof content === "string") {
|
|
95
|
+
fs.writeSync(fd, content);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
fs.writeSync(fd, content);
|
|
99
|
+
}
|
|
86
100
|
try {
|
|
87
101
|
fs.fdatasyncSync(fd);
|
|
88
102
|
}
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import fs from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
17
|
+
import { sleepSync } from "../../runtime.js";
|
|
18
|
+
import { writeFileAtomic } from "../common.js";
|
|
19
|
+
import { ConfigError } from "../errors.js";
|
|
20
|
+
import { probeLock, releaseLock, tryAcquireLockSync } from "../file-lock.js";
|
|
21
|
+
import { getCacheDir, getConfigDir } from "../paths.js";
|
|
21
22
|
/**
|
|
22
23
|
* Read the raw text of a config file. Returns `undefined` when the file does
|
|
23
24
|
* not exist (legitimate cold-start). Other I/O errors propagate.
|
|
@@ -79,22 +80,18 @@ export function writeConfigAtomic(configPath, config) {
|
|
|
79
80
|
}
|
|
80
81
|
/** Maximum number of timestamped config backups to retain (#459). */
|
|
81
82
|
const MAX_CONFIG_BACKUPS = 5;
|
|
82
|
-
/**
|
|
83
|
-
* Snapshot the current config file to `<cacheDir>/config-backups/`. Writes
|
|
84
|
-
* both a timestamped copy and a `config.latest.json` pointer, then prunes the
|
|
85
|
-
* timestamped set to {@link MAX_CONFIG_BACKUPS} most-recent entries.
|
|
86
|
-
*
|
|
87
|
-
* No-op when the source file does not exist (cold-start safe).
|
|
88
|
-
*/
|
|
89
83
|
export function backupExistingConfig(configPath) {
|
|
90
84
|
if (!fs.existsSync(configPath))
|
|
91
|
-
return;
|
|
85
|
+
return undefined;
|
|
92
86
|
const backupDir = path.join(getCacheDir(), "config-backups");
|
|
93
87
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
94
88
|
const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
const timestamped = path.join(backupDir, `config-${timestamp}.json`);
|
|
90
|
+
const latest = path.join(backupDir, "config.latest.json");
|
|
91
|
+
fs.copyFileSync(configPath, timestamped);
|
|
92
|
+
fs.copyFileSync(configPath, latest);
|
|
97
93
|
pruneOldBackups(backupDir);
|
|
94
|
+
return { timestamped, latest };
|
|
98
95
|
}
|
|
99
96
|
function pruneOldBackups(backupDir) {
|
|
100
97
|
let entries;
|
|
@@ -140,6 +137,14 @@ function getConfigLockPath() {
|
|
|
140
137
|
}
|
|
141
138
|
const CONFIG_LOCK_MAX_RETRIES = 10;
|
|
142
139
|
const CONFIG_LOCK_RETRY_DELAY_MS = 50;
|
|
140
|
+
/**
|
|
141
|
+
* Block the current thread for `ms` without busy-spinning (H8). Delegates to
|
|
142
|
+
* the runtime boundary's `sleepSync`, a real blocking sleep that yields the
|
|
143
|
+
* thread to the OS scheduler.
|
|
144
|
+
*/
|
|
145
|
+
function sleepSyncMs(ms) {
|
|
146
|
+
sleepSync(ms);
|
|
147
|
+
}
|
|
143
148
|
/**
|
|
144
149
|
* Acquire an exclusive sentinel around config writes.
|
|
145
150
|
*
|
|
@@ -169,11 +174,16 @@ export function acquireConfigLock() {
|
|
|
169
174
|
continue; // Reclaimed — retry immediately.
|
|
170
175
|
}
|
|
171
176
|
if (attempt < CONFIG_LOCK_MAX_RETRIES - 1) {
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
// H8: yield the thread between retries instead of busy-spinning.
|
|
178
|
+
// The previous `while (Date.now() < deadline)` loop burned CPU for up to
|
|
179
|
+
// 50ms per retry (≈500ms total), freezing the single JS thread and
|
|
180
|
+
// starving co-scheduled work under parallel load. `sleepSync` is a
|
|
181
|
+
// real blocking sleep that releases the thread to the OS scheduler.
|
|
182
|
+
// Kept synchronous (rather than an async sleep) to preserve the sync
|
|
183
|
+
// `withConfigLock` signature and avoid an async ripple through every
|
|
184
|
+
// `saveConfig`/`loadConfig` caller. Lock semantics are unchanged: same
|
|
185
|
+
// retry count, same delay budget, same best-effort fall-through.
|
|
186
|
+
sleepSyncMs(CONFIG_LOCK_RETRY_DELAY_MS);
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
189
|
// Best-effort: proceed without lock.
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* location and stripped from the raw config. Production code reads ONLY the
|
|
13
13
|
* new shape. There are no backward-compat shims after this migration.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { v1ProfilePlatform } from "../../integrations/harnesses/index.js";
|
|
16
|
+
import { warn } from "../warn.js";
|
|
16
17
|
/**
|
|
17
18
|
* Current config schema version sentinel.
|
|
18
19
|
* Configs at this version are considered fully migrated and will not be rewritten.
|
|
@@ -579,14 +580,15 @@ export function migrateConfigShape(raw) {
|
|
|
579
580
|
* platform).
|
|
580
581
|
*/
|
|
581
582
|
function guessAgentPlatform(name) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
583
|
+
// #566: consult the harness registry instead of re-deriving the heuristic.
|
|
584
|
+
// `v1ProfilePlatform` iterates HARNESS_REGISTRY and asks each harness's own
|
|
585
|
+
// `matchesV1ProfileName()`, so:
|
|
586
|
+
// - 'claude' AND the 'claude-code' alias both resolve to canonical 'claude'
|
|
587
|
+
// (the normalization bridge keeps legacy v1 names round-tripping);
|
|
588
|
+
// - decorated names like 'opencode-sdk-x' resolve most-specific-first;
|
|
589
|
+
// - an unknown harness name returns undefined (caller drops it) instead of
|
|
590
|
+
// being silently misclassified. Adding a harness needs no edit here.
|
|
591
|
+
return v1ProfilePlatform(name);
|
|
590
592
|
}
|
|
591
593
|
/**
|
|
592
594
|
* Map a v1 process name (e.g. `"reflect"`, `"propose"`) to its v2 improve
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* enforced at save time via `superRefine` on the top-level schema.
|
|
29
29
|
*/
|
|
30
30
|
import { z } from "zod";
|
|
31
|
+
import { VALID_HARNESS_IDS } from "./config-types.js";
|
|
31
32
|
// ── Reusable atomic schemas ─────────────────────────────────────────────────
|
|
32
33
|
/** Positive integer (used for tokens, timeouts, batch sizes). */
|
|
33
34
|
const positiveInt = z.number().int().positive();
|
|
@@ -108,7 +109,9 @@ export const EmbeddingConnectionConfigSchema = z
|
|
|
108
109
|
})
|
|
109
110
|
.strict();
|
|
110
111
|
// ── Agent profiles ──────────────────────────────────────────────────────────
|
|
111
|
-
|
|
112
|
+
// Derives from the canonical VALID_HARNESS_IDS (#565) so the Zod gate cannot
|
|
113
|
+
// drift from the TS union / parse check / setup detection.
|
|
114
|
+
const AgentPlatformSchema = z.enum(VALID_HARNESS_IDS);
|
|
112
115
|
export const AgentProfileConfigSchema = z
|
|
113
116
|
.object({
|
|
114
117
|
platform: AgentPlatformSchema,
|
|
@@ -126,18 +129,33 @@ export const ImproveProcessConfigSchema = z
|
|
|
126
129
|
profile: z.string().min(1).optional(),
|
|
127
130
|
timeoutMs: z.union([positiveInt, z.null()]).optional(),
|
|
128
131
|
allowedTypes: z.array(z.string().min(1)).optional(),
|
|
132
|
+
// Consolidate process: minimum eligible-memory pool size below which the
|
|
133
|
+
// consolidation pass skips entirely (emits `pool_below_min_size`). 0 disables
|
|
134
|
+
// the guard. Only meaningful on the `consolidate` process. Default 500.
|
|
135
|
+
minPoolSize: z.number().int().min(0).optional(),
|
|
129
136
|
qualityGate: z.object({ enabled: z.boolean().optional() }).strict().optional(),
|
|
130
137
|
contradictionDetection: z.object({ enabled: z.boolean().optional() }).strict().optional(),
|
|
131
138
|
// Extract process config (only meaningful for extract process)
|
|
132
139
|
defaultSince: z.string().min(1).optional(),
|
|
133
140
|
maxTotalChars: positiveInt.optional(),
|
|
141
|
+
// Extract process: minimum raw session size (pre-filter inputCount) below
|
|
142
|
+
// which the extract LLM call is skipped (#595/#596). 0 disables the gate.
|
|
143
|
+
// Absent = default 10 (skip only truly empty sessions). Only meaningful
|
|
144
|
+
// on the `extract` process.
|
|
145
|
+
minContentChars: z.number().int().min(0).optional(),
|
|
134
146
|
maxChunkSize: z.number().int().min(1).max(50).optional(),
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
147
|
+
// Extract process: minimum number of new (unseen, in-window) candidate
|
|
148
|
+
// sessions below which the extract pass skips entirely (emits an
|
|
149
|
+
// `improve_skipped` event with `reason: "below_min_new_sessions"`). 0
|
|
150
|
+
// disables the guard. Only meaningful on the `extract` process. Default 0
|
|
151
|
+
// (disabled) so existing behaviour is preserved; only opted-in profiles set it.
|
|
152
|
+
minNewSessions: z.number().int().min(0).optional(),
|
|
153
|
+
// #561 — index agent sessions as a searchable `session` asset (extract
|
|
154
|
+
// process). Absent = on-when-an-LLM-is-available (fail-open when offline).
|
|
155
|
+
indexSessions: z.boolean().optional(),
|
|
156
|
+
// #561 — minimum session duration in minutes for session indexing. 0
|
|
157
|
+
// disables the gate. Absent = default 5. Only meaningful on `extract`.
|
|
158
|
+
minSessionDuration: z.number().min(0).optional(),
|
|
141
159
|
// Triage process config (only meaningful for the `triage` process)
|
|
142
160
|
applyMode: z.enum(["queue", "promote"]).optional(),
|
|
143
161
|
policy: z.string().min(1).optional(),
|
|
@@ -231,6 +249,11 @@ export const DefaultsSchema = z
|
|
|
231
249
|
// ── Sources / registries / installed ────────────────────────────────────────
|
|
232
250
|
const SourceConfigEntryOptionsSchema = z
|
|
233
251
|
.object({
|
|
252
|
+
/**
|
|
253
|
+
* @deprecated 0.9.0 (issue #507). Retired per-asset push-on-commit. Kept so
|
|
254
|
+
* old configs still parse; its intent maps onto the batch push gate and
|
|
255
|
+
* encountering it emits a one-time deprecation warning.
|
|
256
|
+
*/
|
|
234
257
|
pushOnCommit: z.boolean().optional(),
|
|
235
258
|
})
|
|
236
259
|
.passthrough();
|
|
@@ -479,6 +502,25 @@ export const IndexConfigSchema = z.preprocess((raw, ctx) => {
|
|
|
479
502
|
stalenessDetection: StalenessDetectionSchema.optional(),
|
|
480
503
|
})
|
|
481
504
|
.catchall(IndexPassConfigSchema));
|
|
505
|
+
// ── Setup-derived recommendations ──────────────────────────────────────────
|
|
506
|
+
/**
|
|
507
|
+
* Cron-style schedule hints derived by `akm setup --reset-recommended`.
|
|
508
|
+
*
|
|
509
|
+
* These record the *recommended* cadence for the improve and index background
|
|
510
|
+
* tasks. They are advisory metadata persisted into config so the value
|
|
511
|
+
* survives a re-run; actual task scheduling lives in the tasks subsystem.
|
|
512
|
+
*/
|
|
513
|
+
export const SetupTaskSchedulesSchema = z
|
|
514
|
+
.object({
|
|
515
|
+
improve: z.string().min(1).optional(),
|
|
516
|
+
index: z.string().min(1).optional(),
|
|
517
|
+
})
|
|
518
|
+
.strict();
|
|
519
|
+
export const SetupConfigSchema = z
|
|
520
|
+
.object({
|
|
521
|
+
taskSchedules: SetupTaskSchedulesSchema.optional(),
|
|
522
|
+
})
|
|
523
|
+
.strict();
|
|
482
524
|
// ── Top-level AkmConfig ────────────────────────────────────────────────────
|
|
483
525
|
/**
|
|
484
526
|
* Base object schema used both as the top-level shape and as the source of
|
|
@@ -508,6 +550,7 @@ export const AkmConfigShape = {
|
|
|
508
550
|
feedback: FeedbackConfigSchema.optional(),
|
|
509
551
|
archiveRetentionDays: nonNegativeNumber.optional(),
|
|
510
552
|
improve: ImproveConfigSchema.optional(),
|
|
553
|
+
setup: SetupConfigSchema.optional(),
|
|
511
554
|
};
|
|
512
555
|
export const AkmConfigBaseSchema = z.object(AkmConfigShape).strict();
|
|
513
556
|
export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
// VALID_HARNESS_IDS now derives from the unified HARNESS_REGISTRY (#562), which
|
|
5
|
+
// is the single source of truth replacing the previously-disconnected
|
|
6
|
+
// registries. config ← harnesses is the only import direction (harnesses/ is a
|
|
7
|
+
// dependency-graph leaf), so there is no cycle.
|
|
8
|
+
import { VALID_HARNESS_IDS } from "../../integrations/harnesses/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Canonical list of valid agent harness / platform ids. Re-exported from the
|
|
11
|
+
* unified harness registry (#562) so the Zod `AgentPlatformSchema` enum, the
|
|
12
|
+
* `AgentProfileConfigV2` platform union, `parseAgentProfilesMapV2`'s membership
|
|
13
|
+
* check, and setup's `DetectedHarness` union all derive from one place and
|
|
14
|
+
* cannot drift. Add a harness in `src/integrations/harnesses/index.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export { VALID_HARNESS_IDS };
|