akm-cli 0.9.1 → 0.9.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -17,34 +17,34 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import path from "node:path";
|
|
20
|
+
import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
|
|
20
21
|
import { recognizeMatch } from "../../core/adapter/recognize-match.js";
|
|
22
|
+
import { assetPathForName, stashDirFor } from "../../core/asset/asset-placement.js";
|
|
21
23
|
import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
22
24
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
23
25
|
import { extractSection, markdownFragmentSlugs } from "../../core/asset/markdown.js";
|
|
24
26
|
import { displayRef, typeNameFromConceptId } from "../../core/asset/resolve-ref.js";
|
|
25
|
-
import { META_DIR, parseMetaRef,
|
|
26
|
-
import { asNonEmptyString } from "../../core/common.js";
|
|
27
|
+
import { META_DIR, parseMetaRef, readMetaFile } from "../../core/asset/stash-meta.js";
|
|
28
|
+
import { asNonEmptyString, isWithin } from "../../core/common.js";
|
|
27
29
|
import { getIndexPassConfig, loadConfig } from "../../core/config/config.js";
|
|
28
30
|
import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
|
|
29
|
-
import { appendEvent
|
|
30
|
-
import {
|
|
31
|
+
import { appendEvent } from "../../core/events.js";
|
|
32
|
+
import { SCRIPT_EXTENSIONS } from "../../core/recognition-util.js";
|
|
31
33
|
import { presentationFor } from "../../core/type-presentation.js";
|
|
34
|
+
import { warn } from "../../core/warn.js";
|
|
32
35
|
import { hasGraphData } from "../../indexer/db/graph-db.js";
|
|
33
36
|
import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
|
|
34
37
|
import { extractGraphForSingleFile } from "../../indexer/graph/graph-extraction.js";
|
|
35
|
-
import { lookupBundleRef } from "../../indexer/indexer.js";
|
|
38
|
+
import { lookupBundleRef, lookupBundleRefWithResolution } from "../../indexer/indexer.js";
|
|
36
39
|
import { ensurePrimaryIndexForRead, resolveReadSources } from "../../indexer/read-preflight.js";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { insertUsageEvent } from "../../indexer/usage/usage-events.js";
|
|
40
|
+
import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries, } from "../../indexer/search/search-source.js";
|
|
41
|
+
import { recentShowCount, recordShowUsage } from "../../indexer/usage/show-usage.js";
|
|
40
42
|
import { buildFileContext, buildRenderContext, getRenderer, } from "../../indexer/walk/file-context.js";
|
|
41
|
-
import {
|
|
42
|
-
import { resolveIndexPassLLM } from "../../llm/index-passes.js";
|
|
43
|
+
import { resolveIndexPassExecution } from "../../llm/index-passes.js";
|
|
43
44
|
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
44
45
|
import { resolveStorageLocations } from "../../storage/locations.js";
|
|
45
46
|
import { closeDatabase, openExistingDatabase } from "../../storage/repositories/index-connection.js";
|
|
46
47
|
import { TELEMETRY_BUSY_TIMEOUT_MS, withIndexDb } from "../../storage/repositories/index-db.js";
|
|
47
|
-
import { findEntryIdByRef, getEntryById, getEntryIdByFilePath, getItemRefById, } from "../../storage/repositories/index-entries-repository.js";
|
|
48
48
|
import { computeBodyHash } from "../../storage/repositories/index-llm-cache-repository.js";
|
|
49
49
|
// Eagerly import source providers to trigger self-registration.
|
|
50
50
|
import "../../sources/providers/index.js";
|
|
@@ -52,8 +52,9 @@ import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.
|
|
|
52
52
|
import { buildWorkflowAction } from "../../workflows/renderer.js";
|
|
53
53
|
import { getActiveWorkflowRun } from "../../workflows/runtime/runs.js";
|
|
54
54
|
/**
|
|
55
|
-
* Unified show: queries the local FTS5 index, then
|
|
56
|
-
*
|
|
55
|
+
* Unified show: queries the local FTS5 index, then reads that row's file path.
|
|
56
|
+
* A physical owner can arbitrate collisions during lookup, but show never
|
|
57
|
+
* renders an asset without an indexed row. Spec §6.2; no provider or disk
|
|
57
58
|
* fallback.
|
|
58
59
|
*
|
|
59
60
|
* When `detail` is `"brief"` or `"summary"`, the response omits
|
|
@@ -70,6 +71,12 @@ export async function akmShowUnified(input) {
|
|
|
70
71
|
if (metaRef)
|
|
71
72
|
return showStashMeta(metaRef);
|
|
72
73
|
}
|
|
74
|
+
// Env/secret bodies have no safe fragment surface. Reject from the canonical
|
|
75
|
+
// ref namespace before auto-index or lookup can touch authored bytes. This is
|
|
76
|
+
// deliberately independent of on-disk suffix probing: secret filenames keep
|
|
77
|
+
// their natural extension, and a misspelled extensionless ref must not move
|
|
78
|
+
// the sensitive-fragment policy behind a not-found result.
|
|
79
|
+
assertSensitiveFragmentUnsupported(parseBundleRef(ref));
|
|
73
80
|
// Auto-index when stale so the index is current before lookup.
|
|
74
81
|
const { primarySource } = resolveReadSources();
|
|
75
82
|
await ensurePrimaryIndexForRead(primarySource);
|
|
@@ -86,7 +93,7 @@ export async function akmShowUnified(input) {
|
|
|
86
93
|
if (!input.skipLogging) {
|
|
87
94
|
const consumedRef = result.ref ?? makeBundleRef(undefined, parseBundleRef(ref).conceptId);
|
|
88
95
|
const priorShowCount = recentShowCount(consumedRef);
|
|
89
|
-
|
|
96
|
+
recordShowUsage(consumedRef, result.type, result.name, input.eventSource, result.path);
|
|
90
97
|
if (priorShowCount >= 2) {
|
|
91
98
|
// Agent has shown this same asset 3+ times — inject a loop-break hint.
|
|
92
99
|
result.showLoopWarning = priorShowCount + 1;
|
|
@@ -111,18 +118,17 @@ async function showStashMeta(metaRef) {
|
|
|
111
118
|
}
|
|
112
119
|
const config = loadConfig();
|
|
113
120
|
for (const source of sources) {
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
121
|
+
const metaFile = readMetaFile(source.path, metaRef.name);
|
|
122
|
+
if (!metaFile)
|
|
116
123
|
continue;
|
|
117
|
-
const
|
|
118
|
-
const editable = isEditable(filePath, config, allSources);
|
|
124
|
+
const editable = isEditable(metaFile.path, config, allSources);
|
|
119
125
|
appendEvent({ eventType: "show", ref: `meta:${metaRef.name}`, metadata: { type: "meta", name: metaRef.name } });
|
|
120
126
|
return {
|
|
121
127
|
type: "meta",
|
|
122
128
|
name: metaRef.name,
|
|
123
|
-
path:
|
|
129
|
+
path: metaFile.path,
|
|
124
130
|
ref: `meta:${metaRef.name}`,
|
|
125
|
-
content,
|
|
131
|
+
content: metaFile.content,
|
|
126
132
|
origin: source.registryId ?? null,
|
|
127
133
|
editable,
|
|
128
134
|
};
|
|
@@ -168,127 +174,43 @@ function enforceScopeOrThrow(filePath, ref, scope) {
|
|
|
168
174
|
}
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Count how many times `ref` has been shown in the current session by reading
|
|
173
|
-
* recent events. Returns the count BEFORE the current invocation.
|
|
174
|
-
*/
|
|
175
|
-
function recentShowCount(ref) {
|
|
176
|
-
try {
|
|
177
|
-
const { events } = readEvents({
|
|
178
|
-
type: "show",
|
|
179
|
-
ref,
|
|
180
|
-
since: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
|
181
|
-
});
|
|
182
|
-
return events.length;
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
return 0;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function logShowEvent(ref, type, name, eventSource = "user", filePath) {
|
|
189
|
-
// Emit a structured event to events.jsonl so workflow-trace consumers
|
|
190
|
-
// detect akm show invocations without relying on stdout scraping.
|
|
191
|
-
const eventRef = makeBundleRef(parseBundleRef(ref).bundle, parseBundleRef(ref).conceptId);
|
|
192
|
-
appendEvent({ eventType: "show", ref: eventRef, metadata: { type, name } });
|
|
193
|
-
// Detect if this show is a selection from a recent search result.
|
|
194
|
-
try {
|
|
195
|
-
// D7: bound the query to the last 60 s so we never scan unbounded history
|
|
196
|
-
const { events: recentSearches } = readEvents({
|
|
197
|
-
type: "search",
|
|
198
|
-
since: new Date(Date.now() - 60_000).toISOString(),
|
|
199
|
-
});
|
|
200
|
-
const cutoffMs = Date.now() - 60_000;
|
|
201
|
-
const matchingSearch = [...recentSearches].reverse().find((e) => {
|
|
202
|
-
if (!e.ts || new Date(e.ts).getTime() < cutoffMs)
|
|
203
|
-
return false;
|
|
204
|
-
const refs = e.metadata?.resultRefs ?? [];
|
|
205
|
-
return refs.includes(ref);
|
|
206
|
-
});
|
|
207
|
-
if (matchingSearch) {
|
|
208
|
-
appendEvent({
|
|
209
|
-
eventType: "select",
|
|
210
|
-
ref,
|
|
211
|
-
metadata: {
|
|
212
|
-
query: matchingSearch.metadata?.query,
|
|
213
|
-
searchTs: matchingSearch.ts,
|
|
214
|
-
rankPosition: (matchingSearch.metadata?.resultRefs ?? []).indexOf(ref),
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
catch {
|
|
220
|
-
/* fire-and-forget — select is best-effort */
|
|
221
|
-
}
|
|
222
|
-
try {
|
|
223
|
-
withIndexDb((db) => {
|
|
224
|
-
const entryId = filePath ? getEntryIdByFilePath(db, filePath) : findEntryIdByRef(db, eventRef);
|
|
225
|
-
if (entryId === undefined)
|
|
226
|
-
return;
|
|
227
|
-
// Usage events carry only the resolved row's fully-qualified item_ref.
|
|
228
|
-
// A disk-only show has no durable indexed identity yet, so it does not
|
|
229
|
-
// create a per-entry usage row.
|
|
230
|
-
const entryRef = getItemRefById(db, entryId);
|
|
231
|
-
if (!entryRef)
|
|
232
|
-
return;
|
|
233
|
-
const entry = getEntryById(db, entryId);
|
|
234
|
-
withStateDbTelemetry((stateDb) => {
|
|
235
|
-
insertUsageEvent(stateDb, {
|
|
236
|
-
event_type: "show",
|
|
237
|
-
entry_ref: entryRef,
|
|
238
|
-
entry_id: entryId,
|
|
239
|
-
metadata: usageEventAttributionMetadata(entry?.entry.derivedFrom ? { memoryInference: { exposure: "direct" } } : undefined, entryRef),
|
|
240
|
-
source: eventSource,
|
|
241
|
-
});
|
|
242
|
-
}, TELEMETRY_BUSY_TIMEOUT_MS);
|
|
243
|
-
}, { busyTimeoutMs: TELEMETRY_BUSY_TIMEOUT_MS });
|
|
244
|
-
}
|
|
245
|
-
catch (err) {
|
|
246
|
-
rethrowIfTestIsolationError(err);
|
|
247
|
-
/* fire-and-forget */
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
177
|
/** @internal Use akmShowUnified() for all external callers. */
|
|
251
178
|
export async function showLocal(input) {
|
|
252
179
|
const parsed = parseBundleRef(input.ref);
|
|
180
|
+
assertSensitiveFragmentUnsupported(parsed);
|
|
253
181
|
const assetParts = typeNameFromConceptId(parsed.conceptId);
|
|
254
182
|
const config = loadConfig();
|
|
255
183
|
const allSources = resolveSourceEntries(input.stashDir);
|
|
256
184
|
const searchSources = resolveSourcesForOrigin(parsed.bundle, allSources);
|
|
257
185
|
const allSourceDirs = searchSources.map((s) => s.path);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
indexedEntry = null;
|
|
265
|
-
}
|
|
266
|
-
const resolvedAssetPath = indexedEntry?.filePath ??
|
|
267
|
-
(assetParts
|
|
268
|
-
? await resolveAssetPath({ type: assetParts.type, name: assetParts.name, origin: parsed.bundle }, {
|
|
269
|
-
stashDir: input.stashDir,
|
|
270
|
-
mode: "disk-only",
|
|
271
|
-
})
|
|
272
|
-
: null);
|
|
273
|
-
const assetPath = resolvedAssetPath ?? undefined;
|
|
274
|
-
const displayType = indexedEntry?.type ?? assetParts?.type ?? "asset";
|
|
275
|
-
const displayName = indexedEntry?.name ?? assetParts?.name ?? parsed.conceptId;
|
|
276
|
-
if (!assetPath && parsed.bundle && searchSources.length === 0) {
|
|
186
|
+
const resolution = await lookupBundleRefWithResolution(parsed);
|
|
187
|
+
if (resolution.indexError !== undefined)
|
|
188
|
+
throw resolution.indexError;
|
|
189
|
+
const indexedEntry = resolution.entry;
|
|
190
|
+
const assetPath = indexedEntry?.filePath;
|
|
191
|
+
if (!indexedEntry && parsed.bundle && searchSources.length === 0) {
|
|
277
192
|
const installCmd = `akm bundle add ${parsed.bundle}`;
|
|
278
193
|
throw new NotFoundError(`Stash asset not found for ref: ${makeBundleRef(parsed.bundle, parsed.conceptId)}. ` +
|
|
279
194
|
`Stash "${parsed.bundle}" is not installed. Run: ${installCmd}`);
|
|
280
195
|
}
|
|
281
|
-
if (!
|
|
196
|
+
if (!indexedEntry && !resolution.owner) {
|
|
197
|
+
const unsupportedExtension = existingUnsupportedScriptExtension(assetParts, searchSources);
|
|
198
|
+
if (unsupportedExtension !== undefined) {
|
|
199
|
+
const displayExtension = unsupportedExtension || "no extension";
|
|
200
|
+
throw new NotFoundError(`Script ref "${makeBundleRef(parsed.bundle, parsed.conceptId)}" resolves to an existing file with ` +
|
|
201
|
+
`unsupported extension "${displayExtension}". Script refs must use a supported script extension: ` +
|
|
202
|
+
`${[...SCRIPT_EXTENSIONS].join(", ")}.`, "ASSET_NOT_FOUND");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (!indexedEntry || !assetPath) {
|
|
282
206
|
throw new NotFoundError(`Stash asset not found for ref: ${makeBundleRef(parsed.bundle, parsed.conceptId)}. ` +
|
|
283
207
|
"Check the name with `akm search` or verify the asset exists in your stash.");
|
|
284
208
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
throwIndexedPathNotFound(error, input.ref);
|
|
291
|
-
}
|
|
209
|
+
try {
|
|
210
|
+
fs.accessSync(assetPath, fs.constants.R_OK);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
throwIndexedPathNotFound(error, input.ref);
|
|
292
214
|
}
|
|
293
215
|
const source = findSourceForPath(assetPath, allSources);
|
|
294
216
|
const sourceStashDir = source?.path ?? allSourceDirs[0];
|
|
@@ -297,25 +219,24 @@ export async function showLocal(input) {
|
|
|
297
219
|
"Run `akm bundle create` to create the stash directory, or check `akm bundle list` for configured paths.");
|
|
298
220
|
}
|
|
299
221
|
const fileCtx = buildFileContext(sourceStashDir, assetPath);
|
|
300
|
-
const
|
|
222
|
+
const presentedName = indexedEntry.name;
|
|
223
|
+
const indexedRenderer = rendererForIndexedEntry(indexedEntry, fileCtx);
|
|
301
224
|
let response;
|
|
302
225
|
try {
|
|
303
|
-
if (
|
|
226
|
+
if (indexedRenderer === null) {
|
|
304
227
|
response = buildIndexedProjectionResponse(indexedEntry, assetPath, parsed.fragment);
|
|
305
228
|
}
|
|
306
229
|
else {
|
|
307
|
-
const match =
|
|
308
|
-
? indexedMatch(indexedEntry, indexedRenderer)
|
|
309
|
-
: recognizeMatch(fileCtx);
|
|
230
|
+
const match = typeof indexedRenderer === "string" ? indexedMatch(indexedEntry, indexedRenderer) : recognizeMatch(fileCtx);
|
|
310
231
|
if (!match) {
|
|
311
232
|
throw new UsageError(`Could not display asset "${makeBundleRef(parsed.bundle, parsed.conceptId)}" — unsupported file type or unrecognized layout`);
|
|
312
233
|
}
|
|
313
|
-
match.meta = { ...match.meta, name:
|
|
234
|
+
match.meta = { ...match.meta, name: presentedName };
|
|
314
235
|
const renderer = await getRenderer(match.renderer);
|
|
315
236
|
if (!renderer) {
|
|
316
237
|
throw new UsageError(`Renderer "${match.renderer}" not found for asset: ${makeBundleRef(parsed.bundle, parsed.conceptId)}`);
|
|
317
238
|
}
|
|
318
|
-
const renderBundle = indexedEntry
|
|
239
|
+
const renderBundle = indexedEntry.bundleId;
|
|
319
240
|
const renderDefaultBundle = config.defaultBundle ?? (source?.path === allSources[0]?.path ? renderBundle : undefined);
|
|
320
241
|
const renderCtx = buildRenderContext(fileCtx, match, allSourceDirs, renderBundle, renderDefaultBundle);
|
|
321
242
|
response = renderer.buildShowResponse(renderCtx);
|
|
@@ -323,26 +244,22 @@ export async function showLocal(input) {
|
|
|
323
244
|
if (!match.renderer.endsWith("-md")) {
|
|
324
245
|
throw new UsageError(`Fragments are not supported for ${makeBundleRef(parsed.bundle, parsed.conceptId)}. Only Markdown documents support heading fragments.`, "INVALID_FLAG_VALUE");
|
|
325
246
|
}
|
|
326
|
-
applyMarkdownFragment(response, fileCtx.content(), parsed.fragment,
|
|
247
|
+
applyMarkdownFragment(response, fileCtx.content(), parsed.fragment, presentedName);
|
|
327
248
|
}
|
|
328
249
|
}
|
|
329
250
|
}
|
|
330
251
|
catch (error) {
|
|
331
|
-
|
|
332
|
-
throwIndexedPathNotFound(error, input.ref);
|
|
333
|
-
throw error;
|
|
334
|
-
}
|
|
335
|
-
if (indexedEntry) {
|
|
336
|
-
response.type = indexedEntry.type;
|
|
337
|
-
response.name = indexedEntry.name;
|
|
252
|
+
throwIndexedPathNotFound(error, input.ref);
|
|
338
253
|
}
|
|
254
|
+
response.type = indexedEntry.type;
|
|
255
|
+
response.name = indexedEntry.name;
|
|
339
256
|
const isPrimaryStash = source !== undefined && source.path === allSources[0]?.path;
|
|
340
257
|
const canonicalRef = displayRef({
|
|
341
|
-
type:
|
|
342
|
-
name:
|
|
343
|
-
conceptId: indexedEntry
|
|
344
|
-
bundleId: indexedEntry
|
|
345
|
-
}, config.defaultBundle ?? (isPrimaryStash ? indexedEntry
|
|
258
|
+
type: indexedEntry.type,
|
|
259
|
+
name: presentedName,
|
|
260
|
+
conceptId: indexedEntry.conceptId,
|
|
261
|
+
bundleId: indexedEntry.bundleId,
|
|
262
|
+
}, config.defaultBundle ?? (isPrimaryStash ? indexedEntry.bundleId : undefined));
|
|
346
263
|
if (response.type === "workflow")
|
|
347
264
|
response.action = buildWorkflowAction(canonicalRef);
|
|
348
265
|
// 07 P1-D: provenance-aware toolPolicy CEILING. An agent's self-declared
|
|
@@ -400,21 +317,83 @@ export async function showLocal(input) {
|
|
|
400
317
|
}
|
|
401
318
|
return fullResponse;
|
|
402
319
|
}
|
|
320
|
+
/** Reject body fragments for namespaces whose authored bytes are sensitive. */
|
|
321
|
+
function assertSensitiveFragmentUnsupported(ref) {
|
|
322
|
+
if (ref.fragment === undefined)
|
|
323
|
+
return;
|
|
324
|
+
const type = typeNameFromConceptId(ref.conceptId)?.type;
|
|
325
|
+
if (type !== "env" && type !== "secret")
|
|
326
|
+
return;
|
|
327
|
+
throw new UsageError(`Fragments are not supported for ${makeBundleRef(ref.bundle, ref.conceptId)}. Sensitive ${type} assets do not expose body fragments.`, "INVALID_FLAG_VALUE");
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Return the unsupported extension only when the exact canonical AKM script
|
|
331
|
+
* path exists as a contained regular file. Physical owner arbitration runs
|
|
332
|
+
* first; this is a diagnostic for its miss, never an alternate owner or
|
|
333
|
+
* runnable-file classifier. No authored bytes are read.
|
|
334
|
+
*/
|
|
335
|
+
function existingUnsupportedScriptExtension(assetParts, sources) {
|
|
336
|
+
if (assetParts?.type !== "script")
|
|
337
|
+
return undefined;
|
|
338
|
+
const extension = path.extname(assetParts.name);
|
|
339
|
+
if (SCRIPT_EXTENSIONS.has(extension.toLowerCase()))
|
|
340
|
+
return undefined;
|
|
341
|
+
const scriptDir = stashDirFor("script");
|
|
342
|
+
if (!scriptDir)
|
|
343
|
+
return undefined;
|
|
344
|
+
for (const source of sources) {
|
|
345
|
+
try {
|
|
346
|
+
if ((source.adapterId ?? detectAdapterId(source.path)) !== "akm")
|
|
347
|
+
continue;
|
|
348
|
+
const sourceRoot = path.resolve(source.path);
|
|
349
|
+
const candidate = path.resolve(assetPathForName("script", path.join(sourceRoot, scriptDir), assetParts.name));
|
|
350
|
+
if (!isWithin(candidate, sourceRoot))
|
|
351
|
+
continue;
|
|
352
|
+
const authoredStat = fs.lstatSync(candidate);
|
|
353
|
+
if (!authoredStat.isFile() && !authoredStat.isSymbolicLink())
|
|
354
|
+
continue;
|
|
355
|
+
const realRoot = fs.realpathSync(sourceRoot);
|
|
356
|
+
const realCandidate = fs.realpathSync(candidate);
|
|
357
|
+
if (!isWithin(realCandidate, realRoot) || !fs.statSync(realCandidate).isFile())
|
|
358
|
+
continue;
|
|
359
|
+
return extension;
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// Missing, unreadable, dangling, or otherwise unsafe paths remain normal
|
|
363
|
+
// not-found misses; this diagnostic never widens physical ownership.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
403
368
|
/**
|
|
404
369
|
* #624-P3 — opt-in inline graph extraction for `akm show`. Best-effort and
|
|
405
370
|
* timeout-bounded: never throws, never hangs, never mutates the response.
|
|
406
371
|
*
|
|
407
372
|
* Preconditions (caller already checked the flag): a model must be configured
|
|
408
|
-
* (model-available guard via {@link
|
|
409
|
-
* ungraphed ({@link hasGraphData}). Extraction races a 30s timeout so
|
|
410
|
-
* cannot block on a slow provider; any timeout/error/missing-model path
|
|
411
|
-
* swallowed and `show` returns its already-assembled response unchanged.
|
|
373
|
+
* (model-available guard via {@link resolveIndexPassExecution}) and the asset
|
|
374
|
+
* must be ungraphed ({@link hasGraphData}). Extraction races a 30s timeout so
|
|
375
|
+
* `show` cannot block on a slow provider; any timeout/error/missing-model path
|
|
376
|
+
* is swallowed and `show` returns its already-assembled response unchanged.
|
|
412
377
|
*/
|
|
413
378
|
async function maybeExtractGraphInline(config, sourceStashDir, assetPath) {
|
|
414
379
|
try {
|
|
415
|
-
//
|
|
416
|
-
if
|
|
380
|
+
// Resolve readiness and the symbolic runner once. The inline dispatch must
|
|
381
|
+
// consume this same snapshot even if models.json changes while show runs.
|
|
382
|
+
const graphExecution = resolveIndexPassExecution("graph", config);
|
|
383
|
+
if (!graphExecution.runner)
|
|
417
384
|
return;
|
|
385
|
+
const emittedNoticeKeys = new Set();
|
|
386
|
+
const reportNotices = (notices) => {
|
|
387
|
+
for (const notice of notices) {
|
|
388
|
+
const key = JSON.stringify(notice);
|
|
389
|
+
if (emittedNoticeKeys.has(key))
|
|
390
|
+
continue;
|
|
391
|
+
emittedNoticeKeys.add(key);
|
|
392
|
+
const field = typeof notice.field === "string" ? ` field=${notice.field}` : "";
|
|
393
|
+
warn(`[akm] lazy graph extraction notice ${notice.code} adapter=${notice.adapter}${field}: ${notice.message}`);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
reportNotices(graphExecution.notices);
|
|
418
397
|
let alreadyGraphed = false;
|
|
419
398
|
let bodyHash;
|
|
420
399
|
try {
|
|
@@ -439,7 +418,14 @@ async function maybeExtractGraphInline(config, sourceStashDir, assetPath) {
|
|
|
439
418
|
timer = setTimeout(resolve, 30_000);
|
|
440
419
|
});
|
|
441
420
|
try {
|
|
442
|
-
await Promise.race([
|
|
421
|
+
await Promise.race([
|
|
422
|
+
extractGraphForSingleFile(db, sourceStashDir, assetPath, bodyHash, {
|
|
423
|
+
config,
|
|
424
|
+
llmRunner: graphExecution.runner,
|
|
425
|
+
onNotices: reportNotices,
|
|
426
|
+
}),
|
|
427
|
+
timeout,
|
|
428
|
+
]);
|
|
443
429
|
}
|
|
444
430
|
finally {
|
|
445
431
|
if (timer)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { defineGroupCommand, defineJsonCommand, output } from "../cli/shared.js";
|
|
5
5
|
import { getEffectiveRegistries, loadUserConfig, mutateConfig } from "../core/config/config.js";
|
|
6
6
|
import { NotFoundError, UsageError } from "../core/errors.js";
|
|
7
|
+
import { formatRegistryUrl, hasRegistryUrlCredentials, REGISTRY_CREDENTIALS_UNSUPPORTED, registryEntryForOutput, } from "../core/registry-url.js";
|
|
7
8
|
import { warn } from "../core/warn.js";
|
|
8
9
|
export const registryCommand = defineGroupCommand({
|
|
9
10
|
meta: { name: "registry", description: "Manage bundle registries" },
|
|
@@ -14,7 +15,7 @@ export const registryCommand = defineGroupCommand({
|
|
|
14
15
|
const config = loadUserConfig();
|
|
15
16
|
// R-066 #5: mirror the shared config↔default-fallback helper instead
|
|
16
17
|
// of re-deriving it inline.
|
|
17
|
-
output("registry-list", { registries: getEffectiveRegistries(config) });
|
|
18
|
+
output("registry-list", { registries: getEffectiveRegistries(config).map(registryEntryForOutput) });
|
|
18
19
|
},
|
|
19
20
|
}),
|
|
20
21
|
add: defineJsonCommand({
|
|
@@ -23,7 +24,7 @@ export const registryCommand = defineGroupCommand({
|
|
|
23
24
|
url: { type: "positional", description: "Registry index URL", required: true },
|
|
24
25
|
name: { type: "string", description: "Human-friendly name for the registry" },
|
|
25
26
|
provider: { type: "string", description: "Provider type (e.g. static-index, skills-sh)" },
|
|
26
|
-
options: { type: "string", description:
|
|
27
|
+
options: { type: "string", description: "Provider-specific options as JSON." },
|
|
27
28
|
"allow-insecure": {
|
|
28
29
|
type: "boolean",
|
|
29
30
|
description: "Allow a plain HTTP registry URL (otherwise rejected)",
|
|
@@ -31,6 +32,9 @@ export const registryCommand = defineGroupCommand({
|
|
|
31
32
|
},
|
|
32
33
|
},
|
|
33
34
|
async run({ args }) {
|
|
35
|
+
if (hasRegistryUrlCredentials(args.url)) {
|
|
36
|
+
throw new UsageError(REGISTRY_CREDENTIALS_UNSUPPORTED);
|
|
37
|
+
}
|
|
34
38
|
if (!args.url.startsWith("http")) {
|
|
35
39
|
throw new UsageError("Registry URL must start with http:// or https://");
|
|
36
40
|
}
|
|
@@ -65,7 +69,7 @@ export const registryCommand = defineGroupCommand({
|
|
|
65
69
|
return { ...config, registries };
|
|
66
70
|
}).config;
|
|
67
71
|
output("registry-add", {
|
|
68
|
-
registries: updated.registries ?? [],
|
|
72
|
+
registries: (updated.registries ?? []).map(registryEntryForOutput),
|
|
69
73
|
added,
|
|
70
74
|
...(!added ? { message: "Registry URL already configured" } : {}),
|
|
71
75
|
});
|
|
@@ -80,15 +84,20 @@ export const registryCommand = defineGroupCommand({
|
|
|
80
84
|
async run({ args }) {
|
|
81
85
|
const config = loadUserConfig();
|
|
82
86
|
const registries = [...(config.registries ?? [])];
|
|
87
|
+
const displayTarget = hasRegistryUrlCredentials(args.target) ||
|
|
88
|
+
args.target.startsWith("http://") ||
|
|
89
|
+
args.target.startsWith("https://")
|
|
90
|
+
? formatRegistryUrl(args.target)
|
|
91
|
+
: args.target;
|
|
83
92
|
const idx = registries.findIndex((r) => r.url === args.target || r.name === args.target);
|
|
84
93
|
if (idx === -1) {
|
|
85
94
|
// Was a success envelope with `removed: false` and exit 0, so
|
|
86
95
|
// `akm registry remove typo && ...` proceeded as if it had removed
|
|
87
96
|
// something. A missing target is exit 1, like every other not-found.
|
|
88
|
-
throw new NotFoundError(`No registry matching "${
|
|
97
|
+
throw new NotFoundError(`No registry matching "${displayTarget}" is configured.`, "SOURCE_NOT_FOUND", "Run `akm registry list` to see configured registries.");
|
|
89
98
|
}
|
|
90
99
|
const { confirmDestructive } = await import("../cli/confirm.js");
|
|
91
|
-
const confirmed = await confirmDestructive(`Remove registry "${
|
|
100
|
+
const confirmed = await confirmDestructive(`Remove registry "${displayTarget}"? This cannot be undone.`, {
|
|
92
101
|
yes: args.yes === true,
|
|
93
102
|
});
|
|
94
103
|
if (!confirmed) {
|
|
@@ -105,9 +114,9 @@ export const registryCommand = defineGroupCommand({
|
|
|
105
114
|
return { ...latest, registries: current };
|
|
106
115
|
}).config;
|
|
107
116
|
output("registry-remove", {
|
|
108
|
-
registries: updated.registries ?? [],
|
|
117
|
+
registries: (updated.registries ?? []).map(registryEntryForOutput),
|
|
109
118
|
removed: removed !== undefined,
|
|
110
|
-
...(removed ? { entry: removed } : { message: "No matching registry found" }),
|
|
119
|
+
...(removed ? { entry: registryEntryForOutput(removed) } : { message: "No matching registry found" }),
|
|
111
120
|
});
|
|
112
121
|
},
|
|
113
122
|
}),
|
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
import { serializeFrontmatter } from "../core/asset/asset-serialize.js";
|
|
12
12
|
import { toErrorMessage, tryReadStdinText } from "../core/common.js";
|
|
13
13
|
import { loadConfig } from "../core/config/config.js";
|
|
14
|
-
import { UsageError } from "../core/errors.js";
|
|
14
|
+
import { ConfigError, UsageError } from "../core/errors.js";
|
|
15
15
|
import { parseEmbeddedJsonResponse } from "../core/parse.js";
|
|
16
16
|
import { DURATION_UNITS, parseDuration as parseDurationSpec } from "../core/time.js";
|
|
17
17
|
import { warn } from "../core/warn.js";
|
|
18
18
|
import { SCOPE_KEYS } from "../indexer/passes/metadata.js";
|
|
19
|
-
import {
|
|
19
|
+
import { callStructured } from "../llm/structured-call.js";
|
|
20
|
+
import { withLlmStage } from "../llm/usage-telemetry.js";
|
|
21
|
+
import { resolveImproveLlmExecution } from "./improve/execution.js";
|
|
20
22
|
/**
|
|
21
23
|
* Parse a shorthand duration string to a number of milliseconds.
|
|
22
24
|
* Supports the CLI-wide canonical grammar: `30d` (days), `12h` (hours),
|
|
@@ -140,21 +142,20 @@ function detectObservedAt(body) {
|
|
|
140
142
|
const LLM_ENRICH_TIMEOUT_MS = 10_000;
|
|
141
143
|
/**
|
|
142
144
|
* Attempt LLM enrichment of memory metadata. Returns merged metadata
|
|
143
|
-
* fields on success.
|
|
144
|
-
*
|
|
145
|
+
* fields on success. Provider, timeout, and parse failures return an empty
|
|
146
|
+
* result and emit a warning. Invalid configuration remains a hard pre-write
|
|
147
|
+
* error and preserves its original {@link ConfigError}.
|
|
145
148
|
*/
|
|
146
149
|
export async function runLlmEnrich(body) {
|
|
147
150
|
const config = loadConfig();
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
151
|
+
const resolved = resolveImproveLlmExecution({ config, processName: "remember-enrich" });
|
|
152
|
+
if (!resolved) {
|
|
150
153
|
warn("Warning: --enrich requires an LLM to be configured. Run `akm setup` to configure one.");
|
|
151
154
|
return { tags: [] };
|
|
152
155
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// it is a no-op.
|
|
157
|
-
const { withLlmStage } = await import("../llm/usage-telemetry.js");
|
|
156
|
+
const runner = resolved.runner;
|
|
157
|
+
const noticesByKey = new Map(resolved.notices.map((notice) => [JSON.stringify(notice), notice]));
|
|
158
|
+
const noticeFields = () => noticesByKey.size > 0 ? { notices: Object.freeze([...noticesByKey.values()]) } : {};
|
|
158
159
|
const prompt = `You are a memory tagger for a developer knowledge base.
|
|
159
160
|
Given the memory text below, return ONLY a JSON object with these fields:
|
|
160
161
|
- "tags": array of 1-5 short lowercase keyword tags
|
|
@@ -170,10 +171,22 @@ Return ONLY the JSON object, no prose, no markdown fences.`;
|
|
|
170
171
|
const result = await (async () => {
|
|
171
172
|
try {
|
|
172
173
|
return await Promise.race([
|
|
173
|
-
withLlmStage("remember", () =>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
withLlmStage("remember", () => callStructured({
|
|
175
|
+
feature: "remember_enrich",
|
|
176
|
+
runner,
|
|
177
|
+
messages: [
|
|
178
|
+
{ role: "system", content: "Return only valid JSON. No prose." },
|
|
179
|
+
{ role: "user", content: prompt },
|
|
180
|
+
],
|
|
181
|
+
request: { maxTokens: 256, temperature: 0.1 },
|
|
182
|
+
onNotices: (value) => {
|
|
183
|
+
for (const notice of value)
|
|
184
|
+
noticesByKey.set(JSON.stringify(notice), notice);
|
|
185
|
+
},
|
|
186
|
+
parse: (raw) => raw ?? "",
|
|
187
|
+
onError: () => "",
|
|
188
|
+
fallback: "",
|
|
189
|
+
})),
|
|
177
190
|
new Promise((_, reject) => {
|
|
178
191
|
timeoutHandle = setTimeout(() => reject(new Error("LLM enrichment timed out")), LLM_ENRICH_TIMEOUT_MS);
|
|
179
192
|
}),
|
|
@@ -188,7 +201,7 @@ Return ONLY the JSON object, no prose, no markdown fences.`;
|
|
|
188
201
|
const parsed = parseEmbeddedJsonResponse(result);
|
|
189
202
|
if (!parsed) {
|
|
190
203
|
warn("Warning: --enrich received invalid JSON from the LLM. Writing memory without enrichment.");
|
|
191
|
-
return { tags: [] };
|
|
204
|
+
return { tags: [], ...noticeFields() };
|
|
192
205
|
}
|
|
193
206
|
const tags = Array.isArray(parsed.tags)
|
|
194
207
|
? parsed.tags.filter((t) => typeof t === "string" && t.trim().length > 0)
|
|
@@ -197,11 +210,13 @@ Return ONLY the JSON object, no prose, no markdown fences.`;
|
|
|
197
210
|
const observed_at = typeof parsed.observed_at === "string" && /^\d{4}-\d{2}-\d{2}$/.test(parsed.observed_at.trim())
|
|
198
211
|
? parsed.observed_at.trim()
|
|
199
212
|
: undefined;
|
|
200
|
-
return { tags, description, observed_at };
|
|
213
|
+
return { tags, description, observed_at, ...noticeFields() };
|
|
201
214
|
}
|
|
202
215
|
catch (err) {
|
|
216
|
+
if (err instanceof ConfigError)
|
|
217
|
+
throw err;
|
|
203
218
|
warn(`Warning: --enrich failed (${toErrorMessage(err)}). Writing memory without enrichment.`);
|
|
204
|
-
return { tags: [] };
|
|
219
|
+
return { tags: [], ...noticeFields() };
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
222
|
// R-061(c): `resolveRememberContentArg` / `wasRememberFlagValueConsumedAsContent`
|