akm-cli 0.9.1 → 0.9.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- 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/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 +2 -2
- 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.js +521 -342
- 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 +10 -0
- 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 +835 -152
- 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 +83 -34
- 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 +21 -20
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- 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 +65 -34
- package/dist/indexer/indexer.js +443 -222
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/scan/doc-to-entry.js +7 -9
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +98 -22
- package/dist/indexer/search/search-fields.js +1 -1
- 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 +28 -32
- 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 +19122 -52218
- package/dist/scripts/akm-migrate.js +19064 -51567
- 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 +167 -207
- 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 +10 -10
- 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 +71 -342
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +1 -1
- 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 +153 -27
- 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 -216
- 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
|
@@ -1,14 +1,27 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Database-backed (SQLite + FTS5/vector) source search implementation.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from source-search.ts to break the circular import:
|
|
8
|
+
* source-search.ts → sources/providers/filesystem.ts → db-search.ts (no cycle)
|
|
9
|
+
*
|
|
10
|
+
* source-search.ts imports this module for the `searchLocal` export.
|
|
11
|
+
* sources/providers/filesystem.ts also imports `searchLocal` from here.
|
|
12
|
+
*
|
|
13
|
+
* Renamed from `local-search.ts` to signal that this is the DB-layer search
|
|
14
|
+
* implementation, not a "local vs. remote" distinction.
|
|
15
|
+
*/
|
|
4
16
|
import path from "node:path";
|
|
5
17
|
import { buildActionFromContributors, defaultActionContributors } from "../../core/action-contributors.js";
|
|
6
18
|
import { stashDirFor } from "../../core/asset/asset-placement.js";
|
|
7
19
|
import { displayRef } from "../../core/asset/resolve-ref.js";
|
|
8
20
|
import { classifyPathAccess } from "../../core/path-access.js";
|
|
9
21
|
import { getDbPath } from "../../core/paths.js";
|
|
22
|
+
import { systemErrorCode } from "../../core/system-error.js";
|
|
10
23
|
import { defaultRendererRegistry } from "../../core/type-presentation.js";
|
|
11
|
-
import {
|
|
24
|
+
import { normalizeEmbeddingEndpoint } from "../../llm/embedders/remote.js";
|
|
12
25
|
import { assertIndexPathReadable, closeDatabase, openExistingDatabase, } from "../../storage/repositories/index-connection.js";
|
|
13
26
|
import { getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getPositiveFeedbackCountsByIds, } from "../../storage/repositories/index-entries-repository.js";
|
|
14
27
|
import { searchFts } from "../../storage/repositories/index-fts-repository.js";
|
|
@@ -188,7 +201,9 @@ export async function searchLocal(input) {
|
|
|
188
201
|
const staleHint = buildStaleIndexHint(db);
|
|
189
202
|
if (staleHint)
|
|
190
203
|
warnings.push(staleHint);
|
|
191
|
-
const { hits, embedMs, rankMs,
|
|
204
|
+
const { hits, embedMs, rankMs, mode, semanticWarning } = await searchDatabase(db, query, searchType, limit, stashDir, allSourceDirs, config, sources, rendererRegistry, filters, includeProposed, beliefFilter, restrictToSources, includeExcludedTypes, disableProjectContext, disableScopedUtility);
|
|
205
|
+
if (semanticWarning)
|
|
206
|
+
warnings.push(semanticWarning);
|
|
192
207
|
return {
|
|
193
208
|
hits,
|
|
194
209
|
tip: hits.length === 0 ? emptyResultTip(query) : undefined,
|
|
@@ -197,7 +212,7 @@ export async function searchLocal(input) {
|
|
|
197
212
|
rankMs,
|
|
198
213
|
// Report the mode the search ACTUALLY used, carried explicitly from the
|
|
199
214
|
// vector scorer — not inferred from elapsed embedding milliseconds.
|
|
200
|
-
mode
|
|
215
|
+
mode,
|
|
201
216
|
};
|
|
202
217
|
}
|
|
203
218
|
finally {
|
|
@@ -246,7 +261,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
246
261
|
conceptIdPrefix: refPrefix.conceptIdPrefix,
|
|
247
262
|
...(refPrefix.bundle !== undefined ? { bundle: refPrefix.bundle } : {}),
|
|
248
263
|
})),
|
|
249
|
-
|
|
264
|
+
mode: "keyword",
|
|
250
265
|
};
|
|
251
266
|
}
|
|
252
267
|
// Empty queries — including ones that sanitize down to no searchable FTS
|
|
@@ -260,23 +275,13 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
260
275
|
typeFilter: searchType === "any" ? undefined : searchType,
|
|
261
276
|
excludeTypes: defaultExcludes,
|
|
262
277
|
})),
|
|
263
|
-
|
|
278
|
+
mode: "keyword",
|
|
264
279
|
};
|
|
265
280
|
}
|
|
266
281
|
// Start the async embedding request without awaiting, then run FTS
|
|
267
282
|
// synchronously while the HTTP/local embedding request is in-flight.
|
|
268
283
|
const typeFilter = searchType === "any" ? undefined : searchType;
|
|
269
|
-
const
|
|
270
|
-
const embeddingPromise = tryVecScores(db, query, limit * 3, config);
|
|
271
|
-
const ftsResults = searchFts(db, query, limit * 3, typeFilter, defaultExcludes);
|
|
272
|
-
const embeddingScores = await embeddingPromise;
|
|
273
|
-
const embedMs = Date.now() - tEmbed0;
|
|
274
|
-
// The vector scorer returns a (possibly empty) Map when the embedding + vector
|
|
275
|
-
// search actually executed, or null when semantic was not runnable (disabled,
|
|
276
|
-
// no embeddings, or the embed call threw). This is the AUTHORITATIVE "semantic
|
|
277
|
-
// mode was used" signal — carried out to telemetry instead of guessing from
|
|
278
|
-
// elapsed milliseconds (which timed the concurrent FTS work too).
|
|
279
|
-
const usedSemantic = embeddingScores !== null;
|
|
284
|
+
const { ftsResults, embeddingScores, embedMs, mode, semanticWarning } = await collectSearchSignals(db, query, limit * 3, typeFilter, defaultExcludes, config);
|
|
280
285
|
const tRank0 = Date.now();
|
|
281
286
|
// ── Score normalization ──────────────────────────────────────────────
|
|
282
287
|
// Normalized BM25 + cosine similarity with weighted addition
|
|
@@ -437,7 +442,25 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
437
442
|
db,
|
|
438
443
|
});
|
|
439
444
|
}));
|
|
440
|
-
return { embedMs, rankMs, hits,
|
|
445
|
+
return { embedMs, rankMs, hits, mode, semanticWarning };
|
|
446
|
+
}
|
|
447
|
+
async function collectSearchSignals(db, query, candidateLimit, typeFilter, excludeTypes, config) {
|
|
448
|
+
const startedAt = Date.now();
|
|
449
|
+
const embeddingPromise = tryVecScores(db, query, candidateLimit, config);
|
|
450
|
+
const ftsResults = searchFts(db, query, candidateLimit, typeFilter, excludeTypes);
|
|
451
|
+
const embeddingResult = await embeddingPromise;
|
|
452
|
+
const mode = embeddingResult.warning
|
|
453
|
+
? "fts-fallback"
|
|
454
|
+
: embeddingResult.scores !== null
|
|
455
|
+
? "semantic"
|
|
456
|
+
: "keyword";
|
|
457
|
+
return {
|
|
458
|
+
ftsResults,
|
|
459
|
+
embeddingScores: embeddingResult.scores,
|
|
460
|
+
embedMs: Date.now() - startedAt,
|
|
461
|
+
mode,
|
|
462
|
+
semanticWarning: embeddingResult.warning,
|
|
463
|
+
};
|
|
441
464
|
}
|
|
442
465
|
/**
|
|
443
466
|
* The no-hits tip. A query in the retired `<type>:` / `<type>:<prefix>/` browse
|
|
@@ -623,10 +646,10 @@ function matchBeliefFilter(beliefState, filter) {
|
|
|
623
646
|
async function tryVecScores(db, query, k, config) {
|
|
624
647
|
const semanticStatus = getEffectiveSemanticStatus(config, readSemanticStatus());
|
|
625
648
|
if (!isSemanticRuntimeReady(semanticStatus))
|
|
626
|
-
return null;
|
|
649
|
+
return { scores: null };
|
|
627
650
|
const hasEmbeddings = getMeta(db, "hasEmbeddings");
|
|
628
651
|
if (hasEmbeddings !== "1")
|
|
629
|
-
return null;
|
|
652
|
+
return { scores: null };
|
|
630
653
|
try {
|
|
631
654
|
const { embed } = await import("../../llm/embedder.js");
|
|
632
655
|
const queryEmbedding = await embed(query, config.embedding);
|
|
@@ -638,12 +661,64 @@ async function tryVecScores(db, query, k, config) {
|
|
|
638
661
|
const raw = 1 - (distance * distance) / 2;
|
|
639
662
|
scores.set(id, Number.isFinite(raw) ? Math.max(0, raw) : 0);
|
|
640
663
|
}
|
|
641
|
-
return scores;
|
|
664
|
+
return { scores };
|
|
642
665
|
}
|
|
643
666
|
catch (error) {
|
|
644
|
-
|
|
645
|
-
|
|
667
|
+
return { scores: null, warning: buildVectorFallbackWarning(config, error) };
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function buildVectorFallbackWarning(config, error) {
|
|
671
|
+
const endpoint = safeEmbeddingEndpoint(config);
|
|
672
|
+
const reason = classifyVectorFailure(error);
|
|
673
|
+
const target = endpoint
|
|
674
|
+
? `embedding endpoint ${endpoint}`
|
|
675
|
+
: config.embedding?.endpoint
|
|
676
|
+
? "configured embedding endpoint"
|
|
677
|
+
: "local embedding model";
|
|
678
|
+
const unavailable = reason === "connection failed" ? `cannot reach ${target}` : `${target} is unavailable`;
|
|
679
|
+
return `Vector search unavailable: ${unavailable} (${reason}) — falling back to keyword search.`;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Name the useful endpoint without ever carrying URL userinfo, query secrets,
|
|
683
|
+
* or fragments into a warning. Invalid authored values fail closed.
|
|
684
|
+
*/
|
|
685
|
+
function safeEmbeddingEndpoint(config) {
|
|
686
|
+
const endpoint = config.embedding?.endpoint;
|
|
687
|
+
if (!endpoint)
|
|
688
|
+
return undefined;
|
|
689
|
+
try {
|
|
690
|
+
const parsed = new URL(normalizeEmbeddingEndpoint(endpoint));
|
|
691
|
+
parsed.username = "";
|
|
692
|
+
parsed.password = "";
|
|
693
|
+
parsed.search = "";
|
|
694
|
+
parsed.hash = "";
|
|
695
|
+
return parsed.toString();
|
|
696
|
+
}
|
|
697
|
+
catch {
|
|
698
|
+
return undefined;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
/** Map untrusted runtime failures onto a small, non-secret diagnostic set. */
|
|
702
|
+
function classifyVectorFailure(error) {
|
|
703
|
+
const code = systemErrorCode(error);
|
|
704
|
+
const message = error instanceof Error ? error.message : "";
|
|
705
|
+
if (code === "ECONNREFUSED" ||
|
|
706
|
+
code === "ECONNRESET" ||
|
|
707
|
+
code === "ENETUNREACH" ||
|
|
708
|
+
code === "EHOSTUNREACH" ||
|
|
709
|
+
code === "ENOTFOUND" ||
|
|
710
|
+
code === "EAI_AGAIN" ||
|
|
711
|
+
/typo in the url or port|connection (?:refused|failed|reset)|fetch failed/i.test(message)) {
|
|
712
|
+
return "connection failed";
|
|
646
713
|
}
|
|
714
|
+
if (code === "ETIMEDOUT" || /timed? out|timeout/i.test(message))
|
|
715
|
+
return "request timed out";
|
|
716
|
+
const httpStatus = message.match(/Embedding (?:batch )?request failed \((\d{3})\)/i)?.[1];
|
|
717
|
+
if (httpStatus)
|
|
718
|
+
return `HTTP ${httpStatus}`;
|
|
719
|
+
if (/unexpected embedding response|missing data\[0\]\.embedding/i.test(message))
|
|
720
|
+
return "invalid embedding response";
|
|
721
|
+
return "request failed";
|
|
647
722
|
}
|
|
648
723
|
// ── Hit building ────────────────────────────────────────────────────────────
|
|
649
724
|
export async function buildDbHit(input) {
|
|
@@ -703,6 +778,7 @@ export async function buildDbHit(input) {
|
|
|
703
778
|
await enrichSearchHit(hit, {
|
|
704
779
|
type: input.entry.type,
|
|
705
780
|
stashDir: entryStashDir,
|
|
781
|
+
bundleId: input.bundleId,
|
|
706
782
|
rendererRegistry,
|
|
707
783
|
db: input.db,
|
|
708
784
|
});
|
|
@@ -70,7 +70,7 @@ export function buildSearchFields(entry) {
|
|
|
70
70
|
// folds into the lowest-weight catch-all column — never name/description/
|
|
71
71
|
// tags/hints — so orientation prose is retrievable without outranking
|
|
72
72
|
// structured-field matches. The fold is unconditional on the entry field:
|
|
73
|
-
// `rebuildFts` rebuilds FTS rows from stored
|
|
73
|
+
// `rebuildFts` rebuilds FTS rows from stored document_json and must reproduce
|
|
74
74
|
// the same fields without re-reading config.
|
|
75
75
|
if (entry.bodyOpening)
|
|
76
76
|
contentParts.push(entry.bodyOpening);
|
|
@@ -53,7 +53,7 @@ export const derivedMemoryEnricher = {
|
|
|
53
53
|
// column now stores this same conceptId grammar (Group-C item 2 flip — the
|
|
54
54
|
// metadata producer + this consumer move together).
|
|
55
55
|
const parentRef = `memories/${hit.name}`;
|
|
56
|
-
const derived = getDerivedForParent(ctx.db, parentRef, ctx.
|
|
56
|
+
const derived = getDerivedForParent(ctx.db, parentRef, ctx.bundleId);
|
|
57
57
|
if (!derived)
|
|
58
58
|
return;
|
|
59
59
|
// Swap description / searchHints / tags from the derived child.
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { isSourceWriteActivated } from "../../core/activation-policy.js";
|
|
7
6
|
import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
8
7
|
import { bundleComponentConfig, bundlesToSourceEntries, getSources, loadConfig } from "../../core/config/config.js";
|
|
9
8
|
import { resolveGitContentRoot, resolveWritable } from "../../core/write-source.js";
|
|
10
9
|
import { lockContentRootFor } from "../../integrations/lockfile.js";
|
|
11
10
|
import { resolveSourceProviderFactory } from "../../sources/provider-factory.js";
|
|
11
|
+
import { ensureWebsiteMirror } from "../../sources/snapshot-fetchers/website-ingest.js";
|
|
12
12
|
// Eager side-effect imports so all built-in source providers self-register
|
|
13
13
|
// before resolveEntryContentDir() runs.
|
|
14
14
|
import "../../sources/providers/index.js";
|
|
@@ -22,9 +22,8 @@ import { warn } from "../../core/warn.js";
|
|
|
22
22
|
* 2. The configured `defaultBundle`, after component-root validation.
|
|
23
23
|
* 3. Remaining configured bundles in installation-priority order.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* after deduplication. Missing configured roots remain in the result so the
|
|
25
|
+
* Disabled entries (`enabled: false`) are filtered after deduplication.
|
|
26
|
+
* Missing configured roots remain in the result so the
|
|
28
27
|
* indexer can classify their scan as incomplete instead of mistaking them for
|
|
29
28
|
* removed sources.
|
|
30
29
|
*/
|
|
@@ -126,8 +125,8 @@ export function resolveEntryContentDir(entry) {
|
|
|
126
125
|
// and writes agree on where content is); the localRoot is the already-walkable
|
|
127
126
|
// content root (installed sources are extracted to their content dir), so no
|
|
128
127
|
// content/-subdir step is applied. Fall back to the provider path logic when no
|
|
129
|
-
// lock entry exists (
|
|
130
|
-
//
|
|
128
|
+
// lock entry exists (for example while a configured source is being
|
|
129
|
+
// materialized for the first time).
|
|
131
130
|
const localRoot = lockContentRootFor(entry.name, entry.type);
|
|
132
131
|
if (localRoot != null)
|
|
133
132
|
return localRoot;
|
|
@@ -165,14 +164,6 @@ export function resolveEntryContentDir(entry) {
|
|
|
165
164
|
export function resolveAllStashDirs(overrideStashDir) {
|
|
166
165
|
return resolveSourceEntries(overrideStashDir).map((s) => s.path);
|
|
167
166
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Return the resolved absolute paths of all writable stash sources.
|
|
170
|
-
*/
|
|
171
|
-
export function getWritableStashDirs(overrideStashDir, existingConfig) {
|
|
172
|
-
return resolveSourceEntries(overrideStashDir, existingConfig)
|
|
173
|
-
.filter((s) => isSourceWriteActivated(s))
|
|
174
|
-
.map((s) => s.path);
|
|
175
|
-
}
|
|
176
167
|
/**
|
|
177
168
|
* Find which source a file path belongs to.
|
|
178
169
|
*
|
|
@@ -263,7 +254,7 @@ export async function ensureSourceCaches(config, options) {
|
|
|
263
254
|
// provider and call `sync()`. Every cache-backed kind (git, website, npm)
|
|
264
255
|
// refreshes the same way — a bad source warns and is skipped without
|
|
265
256
|
// aborting the others. The git content/-subdir layout convention stays in
|
|
266
|
-
// resolveEntryContentDir.
|
|
257
|
+
// resolveEntryContentDir. Provider projection comes only from `bundles`.
|
|
267
258
|
//
|
|
268
259
|
// DISTINCTION (deliberately NOT lock-first): refresh derives the PROVIDER's
|
|
269
260
|
// own cache path to git-pull/re-materialize INTO — that derived path is where
|
|
@@ -304,7 +295,7 @@ export async function ensureSourceCaches(config, options) {
|
|
|
304
295
|
continue;
|
|
305
296
|
}
|
|
306
297
|
try {
|
|
307
|
-
await provider.sync({ force, secrets: options?.secrets });
|
|
298
|
+
await provider.sync({ force, secrets: options?.secrets, ensureWebsiteMirror });
|
|
308
299
|
}
|
|
309
300
|
catch (err) {
|
|
310
301
|
warn(`Warning: failed to refresh ${provider.kind} source "${provider.name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -6,6 +6,11 @@ import { writeFileAtomic } from "../../core/common.js";
|
|
|
6
6
|
import { getCacheDir, getSemanticStatusPath } from "../../core/paths.js";
|
|
7
7
|
import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../../llm/embedders/deterministic.js";
|
|
8
8
|
import { DEFAULT_LOCAL_MODEL } from "../../llm/embedders/local.js";
|
|
9
|
+
let semanticStatusMutationForTests;
|
|
10
|
+
/** TEST-ONLY. Inject a semantic-status filesystem publication fault. */
|
|
11
|
+
export function _setSemanticStatusMutationForTests(mutation) {
|
|
12
|
+
semanticStatusMutationForTests = mutation;
|
|
13
|
+
}
|
|
9
14
|
export function deriveSemanticProviderFingerprint(embedding) {
|
|
10
15
|
if (isDeterministicEmbedEnabled()) {
|
|
11
16
|
return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
|
|
@@ -49,11 +54,13 @@ export function readSemanticStatus() {
|
|
|
49
54
|
return undefined;
|
|
50
55
|
}
|
|
51
56
|
export function writeSemanticStatus(status) {
|
|
57
|
+
semanticStatusMutationForTests?.("write");
|
|
52
58
|
const dir = getCacheDir();
|
|
53
59
|
fs.mkdirSync(dir, { recursive: true });
|
|
54
60
|
writeFileAtomic(getSemanticStatusPath(), `${JSON.stringify(status, null, 2)}\n`);
|
|
55
61
|
}
|
|
56
62
|
export function clearSemanticStatus() {
|
|
63
|
+
semanticStatusMutationForTests?.("clear");
|
|
57
64
|
try {
|
|
58
65
|
fs.unlinkSync(getSemanticStatusPath());
|
|
59
66
|
}
|
|
@@ -110,7 +117,9 @@ export function classifySemanticFailure(message) {
|
|
|
110
117
|
if (lower.includes("404") || lower.includes("model not found") || lower.includes("bad request")) {
|
|
111
118
|
return "remote-model";
|
|
112
119
|
}
|
|
113
|
-
if (lower.includes("transformers") ||
|
|
120
|
+
if (lower.includes("transformers") ||
|
|
121
|
+
lower.includes("missing-package") ||
|
|
122
|
+
lower.includes("local embedding dependency is unavailable")) {
|
|
114
123
|
return "missing-package";
|
|
115
124
|
}
|
|
116
125
|
if (lower.includes("download")) {
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
5
|
+
import { rethrowIfTestIsolationError } from "../../core/errors.js";
|
|
6
|
+
import { appendEvent, readEvents } from "../../core/events.js";
|
|
7
|
+
import { withStateDbTelemetry } from "../../core/state-db.js";
|
|
8
|
+
import { TELEMETRY_BUSY_TIMEOUT_MS, withIndexDb } from "../../storage/repositories/index-db.js";
|
|
9
|
+
import { findEntryIdByRef, getEntryById, getEntryIdByFilePath, getItemRefById, } from "../../storage/repositories/index-entries-repository.js";
|
|
10
|
+
import { usageEventAttributionMetadata } from "../search/search-attribution.js";
|
|
11
|
+
import { insertUsageEvent } from "./usage-events.js";
|
|
12
|
+
/** Count prior show events in the current one-hour loop-detection window. */
|
|
13
|
+
export function recentShowCount(ref) {
|
|
14
|
+
try {
|
|
15
|
+
return readEvents({
|
|
16
|
+
type: "show",
|
|
17
|
+
ref,
|
|
18
|
+
since: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
|
|
19
|
+
}).events.length;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function appendShowTrace(ref, type, name) {
|
|
26
|
+
appendEvent({ eventType: "show", ref, metadata: { type, name } });
|
|
27
|
+
try {
|
|
28
|
+
const { events: recentSearches } = readEvents({
|
|
29
|
+
type: "search",
|
|
30
|
+
since: new Date(Date.now() - 60_000).toISOString(),
|
|
31
|
+
});
|
|
32
|
+
const cutoffMs = Date.now() - 60_000;
|
|
33
|
+
const matchingSearch = [...recentSearches].reverse().find((event) => {
|
|
34
|
+
if (!event.ts || new Date(event.ts).getTime() < cutoffMs)
|
|
35
|
+
return false;
|
|
36
|
+
const refs = event.metadata?.resultRefs ?? [];
|
|
37
|
+
return refs.includes(ref);
|
|
38
|
+
});
|
|
39
|
+
if (!matchingSearch)
|
|
40
|
+
return;
|
|
41
|
+
const resultRefs = matchingSearch.metadata?.resultRefs ?? [];
|
|
42
|
+
appendEvent({
|
|
43
|
+
eventType: "select",
|
|
44
|
+
ref,
|
|
45
|
+
metadata: {
|
|
46
|
+
query: matchingSearch.metadata?.query,
|
|
47
|
+
searchTs: matchingSearch.ts,
|
|
48
|
+
rankPosition: resultRefs.indexOf(ref),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Selection attribution is best-effort.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function insertShowUsage(entryId, entryRef, derivedFrom, eventSource) {
|
|
57
|
+
withStateDbTelemetry((stateDb) => {
|
|
58
|
+
insertUsageEvent(stateDb, {
|
|
59
|
+
event_type: "show",
|
|
60
|
+
entry_ref: entryRef,
|
|
61
|
+
entry_id: entryId,
|
|
62
|
+
metadata: usageEventAttributionMetadata(derivedFrom ? { memoryInference: { exposure: "direct" } } : undefined, entryRef),
|
|
63
|
+
source: eventSource,
|
|
64
|
+
});
|
|
65
|
+
}, TELEMETRY_BUSY_TIMEOUT_MS);
|
|
66
|
+
}
|
|
67
|
+
/** Record a successful show using the already-rendered response identity. */
|
|
68
|
+
export function recordShowUsage(ref, type, name, eventSource = "user", filePath) {
|
|
69
|
+
const parsed = parseBundleRef(ref);
|
|
70
|
+
const eventRef = makeBundleRef(parsed.bundle, parsed.conceptId);
|
|
71
|
+
appendShowTrace(eventRef, type, name);
|
|
72
|
+
try {
|
|
73
|
+
withIndexDb((db) => {
|
|
74
|
+
const entryId = filePath ? getEntryIdByFilePath(db, filePath) : findEntryIdByRef(db, eventRef);
|
|
75
|
+
if (entryId === undefined)
|
|
76
|
+
return;
|
|
77
|
+
const entryRef = getItemRefById(db, entryId);
|
|
78
|
+
if (!entryRef)
|
|
79
|
+
return;
|
|
80
|
+
insertShowUsage(entryId, entryRef, getEntryById(db, entryId)?.entry.derivedFrom, eventSource);
|
|
81
|
+
}, { busyTimeoutMs: TELEMETRY_BUSY_TIMEOUT_MS });
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
rethrowIfTestIsolationError(error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Record a nested execution-source read only after dispatch has crossed preflight. */
|
|
88
|
+
export function recordIndexedShowUsage(ref, eventSource = "user") {
|
|
89
|
+
try {
|
|
90
|
+
withIndexDb((db) => {
|
|
91
|
+
const entryId = findEntryIdByRef(db, ref);
|
|
92
|
+
if (entryId === undefined)
|
|
93
|
+
return;
|
|
94
|
+
const indexed = getEntryById(db, entryId);
|
|
95
|
+
const entryRef = getItemRefById(db, entryId);
|
|
96
|
+
if (!indexed || !entryRef)
|
|
97
|
+
return;
|
|
98
|
+
appendShowTrace(entryRef, indexed.entry.type, indexed.entry.name);
|
|
99
|
+
insertShowUsage(entryId, entryRef, indexed.entry.derivedFrom, eventSource);
|
|
100
|
+
}, { busyTimeoutMs: TELEMETRY_BUSY_TIMEOUT_MS });
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
rethrowIfTestIsolationError(error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -75,12 +75,17 @@ export function countUsageEventsByType(db, eventType) {
|
|
|
75
75
|
/**
|
|
76
76
|
* Delete usage events older than the given number of days.
|
|
77
77
|
*/
|
|
78
|
-
export function purgeOldUsageEvents(db, retentionDays) {
|
|
78
|
+
export function purgeOldUsageEvents(db, retentionDays, options) {
|
|
79
79
|
if (!Number.isFinite(retentionDays) || retentionDays <= 0)
|
|
80
80
|
return;
|
|
81
81
|
try {
|
|
82
|
+
const stateSchema = options?.stateSchema;
|
|
83
|
+
if (stateSchema !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(stateSchema)) {
|
|
84
|
+
throw new Error("Invalid attached state schema name.");
|
|
85
|
+
}
|
|
86
|
+
const usageEvents = stateSchema === undefined ? "usage_events" : `"${stateSchema}".usage_events`;
|
|
82
87
|
const cutoff = new Date(Date.now() - retentionDays * 86_400_000).toISOString();
|
|
83
|
-
db.prepare(
|
|
88
|
+
db.prepare(`DELETE FROM ${usageEvents} WHERE created_at < ?`).run(cutoff);
|
|
84
89
|
}
|
|
85
90
|
catch {
|
|
86
91
|
/* Table may not exist yet */
|
|
@@ -49,7 +49,7 @@ const DIR_TYPE_MAP = [
|
|
|
49
49
|
{
|
|
50
50
|
dir: "workflows",
|
|
51
51
|
type: "workflow",
|
|
52
|
-
test: (ext) => ext === ".md",
|
|
52
|
+
test: (ext) => ext === ".md" || ext === ".yml",
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
dir: "memories",
|
|
@@ -102,6 +102,13 @@ const DIR_TYPE_MAP = [
|
|
|
102
102
|
},
|
|
103
103
|
];
|
|
104
104
|
const COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
|
|
105
|
+
const SMART_MD_FACTS = {
|
|
106
|
+
workflow: { type: "workflow", specificity: 19 },
|
|
107
|
+
toolsAgent: { type: "agent", specificity: 20 },
|
|
108
|
+
command: { type: "command", specificity: 18 },
|
|
109
|
+
modelAgent: { type: "agent", specificity: 8 },
|
|
110
|
+
knowledge: { type: "knowledge", specificity: 5 },
|
|
111
|
+
};
|
|
105
112
|
// Files that should never be treated as the typed asset for the surrounding
|
|
106
113
|
// directory (e.g. `workflows/README.md` is documentation, not a workflow).
|
|
107
114
|
// Lower-cased and matched case-insensitively against `ctx.fileName`. They are
|
|
@@ -175,7 +182,7 @@ function classifyBySmartMd(ctx) {
|
|
|
175
182
|
// body shape would otherwise classify (e.g. step-list inside a project
|
|
176
183
|
// README under workflows/). Fall straight through to `knowledge`.
|
|
177
184
|
if (isTypedDirDocFile(ctx.fileName)) {
|
|
178
|
-
return
|
|
185
|
+
return SMART_MD_FACTS.knowledge;
|
|
179
186
|
}
|
|
180
187
|
const body = ctx.content();
|
|
181
188
|
const fm = ctx.frontmatter();
|
|
@@ -185,33 +192,34 @@ function classifyBySmartMd(ctx) {
|
|
|
185
192
|
// this only catches a workflow living OUTSIDE `workflows/` that still
|
|
186
193
|
// declares its type explicitly.
|
|
187
194
|
if (fm && fm.type === "workflow") {
|
|
188
|
-
return
|
|
195
|
+
return SMART_MD_FACTS.workflow;
|
|
189
196
|
}
|
|
190
197
|
if (fm) {
|
|
191
198
|
// `tools` is the one authoring key for an agent's tool grant. Recognition
|
|
192
199
|
// covers only the key the renderer honors.
|
|
193
200
|
if ("tools" in fm) {
|
|
194
|
-
return
|
|
201
|
+
return SMART_MD_FACTS.toolsAgent;
|
|
195
202
|
}
|
|
196
203
|
if ("agent" in fm) {
|
|
197
|
-
return
|
|
204
|
+
return SMART_MD_FACTS.command;
|
|
198
205
|
}
|
|
199
206
|
}
|
|
200
207
|
if (COMMAND_PLACEHOLDER_RE.test(body)) {
|
|
201
|
-
return
|
|
208
|
+
return SMART_MD_FACTS.command;
|
|
202
209
|
}
|
|
203
210
|
if (fm && "model" in fm) {
|
|
204
|
-
return
|
|
211
|
+
return SMART_MD_FACTS.modelAgent;
|
|
205
212
|
}
|
|
206
|
-
return
|
|
213
|
+
return SMART_MD_FACTS.knowledge;
|
|
207
214
|
}
|
|
208
215
|
// ---------------------------------------------------------------------------
|
|
209
216
|
// Adapter: MatchFact → MatchResult
|
|
210
217
|
// ---------------------------------------------------------------------------
|
|
211
218
|
function toMatchResult(ctx, classify) {
|
|
212
219
|
const fact = classify(ctx);
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
return fact ? matchResultForFact(fact) : null;
|
|
221
|
+
}
|
|
222
|
+
function matchResultForFact(fact) {
|
|
215
223
|
// Renderer name resolved via TYPE_PRESENTATION (core leaf), so matchers.ts
|
|
216
224
|
// carries no edge into the taxonomy SCC (chunk-3 cutover enabler).
|
|
217
225
|
// TYPE_PRESENTATION.renderer is the single source of truth for renderer names.
|
|
@@ -225,6 +233,28 @@ function toMatchResult(ctx, classify) {
|
|
|
225
233
|
...(fact.meta ? { meta: fact.meta } : {}),
|
|
226
234
|
};
|
|
227
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Every smart-Markdown result possible from path fields alone. The actual
|
|
238
|
+
* classifier above returns only facts from this shared table, so owner
|
|
239
|
+
* discovery can conservatively model a byte request without reading bytes.
|
|
240
|
+
*/
|
|
241
|
+
export function smartMdPathCandidates(ctx) {
|
|
242
|
+
if (ctx.ext !== ".md" || ctx.ancestorDirs.includes("secrets"))
|
|
243
|
+
return [];
|
|
244
|
+
const facts = isTypedDirDocFile(ctx.fileName)
|
|
245
|
+
? [SMART_MD_FACTS.knowledge]
|
|
246
|
+
: [
|
|
247
|
+
SMART_MD_FACTS.workflow,
|
|
248
|
+
SMART_MD_FACTS.toolsAgent,
|
|
249
|
+
SMART_MD_FACTS.command,
|
|
250
|
+
SMART_MD_FACTS.modelAgent,
|
|
251
|
+
SMART_MD_FACTS.knowledge,
|
|
252
|
+
];
|
|
253
|
+
return facts.flatMap((fact) => {
|
|
254
|
+
const result = matchResultForFact(fact);
|
|
255
|
+
return result ? [result] : [];
|
|
256
|
+
});
|
|
257
|
+
}
|
|
228
258
|
// ---------------------------------------------------------------------------
|
|
229
259
|
// Public matchers (API unchanged)
|
|
230
260
|
// ---------------------------------------------------------------------------
|
|
@@ -8,6 +8,7 @@ import { parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
|
8
8
|
import { isWithin } from "../../core/common.js";
|
|
9
9
|
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
10
10
|
import { lookup } from "../indexer.js";
|
|
11
|
+
import { deriveInstallations } from "../installations.js";
|
|
11
12
|
import { resolveSourceEntries } from "../search/search-source.js";
|
|
12
13
|
function normalizeRef(ref) {
|
|
13
14
|
// String inputs use the current `[bundle//]conceptId` grammar.
|
|
@@ -65,8 +66,10 @@ function resolveViaDisk(ref, options) {
|
|
|
65
66
|
}
|
|
66
67
|
const directoryIndexNames = options.directoryIndexNames ?? ["SKILL.md"];
|
|
67
68
|
const preserveDirectNameFallback = options.preserveDirectNameFallback ?? true;
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const installations = deriveInstallations(sources);
|
|
70
|
+
for (const [index, source] of sources.entries()) {
|
|
71
|
+
const bundleId = installations[index]?.id;
|
|
72
|
+
if (options.writableBundleIds && (!bundleId || !options.writableBundleIds.has(bundleId)))
|
|
70
73
|
continue;
|
|
71
74
|
const candidates = buildDiskCandidates(source.path, ref, preserveDirectNameFallback);
|
|
72
75
|
for (const candidate of candidates) {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from "node:fs";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { isRelevantAssetFile } from "../../core/asset/asset-placement.js";
|
|
14
|
+
import { WORKFLOW_EXTENSIONS } from "../../core/recognition-util.js";
|
|
14
15
|
import { spawnSync } from "../../runtime.js";
|
|
15
16
|
import { buildFileContext } from "./file-context.js";
|
|
16
17
|
const ALWAYS_SKIP_DIRS = new Set([".git"]);
|
|
@@ -128,7 +129,8 @@ function walkStashGit(stashRoot, options) {
|
|
|
128
129
|
// indexed. The manual walk below already skips symlinks for exactly this
|
|
129
130
|
// reason; the two walkers have to agree regardless of whether the stash
|
|
130
131
|
// happens to sit inside a git repo.
|
|
131
|
-
|
|
132
|
+
const stat = fs.lstatSync(absPath);
|
|
133
|
+
if (stat.isFile() || (stat.isSymbolicLink() && isOwnedWorkflowSymlink(stashRoot, absPath, options))) {
|
|
132
134
|
results.push(buildFileContext(stashRoot, absPath));
|
|
133
135
|
}
|
|
134
136
|
}
|
|
@@ -219,7 +221,11 @@ function walkStashManual(stashRoot, options) {
|
|
|
219
221
|
continue;
|
|
220
222
|
const fullPath = path.join(current, entry.name);
|
|
221
223
|
if (entry.isSymbolicLink()) {
|
|
222
|
-
//
|
|
224
|
+
// Native workflow components arbitrate file symlinks explicitly in the
|
|
225
|
+
// drain before reading them. Directory symlinks remain unfollowed.
|
|
226
|
+
if (isOwnedWorkflowSymlink(stashRoot, fullPath, options)) {
|
|
227
|
+
results.push(buildFileContext(stashRoot, fullPath));
|
|
228
|
+
}
|
|
223
229
|
continue;
|
|
224
230
|
}
|
|
225
231
|
if (entry.isDirectory()) {
|
|
@@ -235,3 +241,15 @@ function walkStashManual(stashRoot, options) {
|
|
|
235
241
|
}
|
|
236
242
|
return { files: results, complete };
|
|
237
243
|
}
|
|
244
|
+
function isOwnedWorkflowSymlink(stashRoot, candidatePath, options) {
|
|
245
|
+
const adapterId = options.workflowSymlinkAdapter;
|
|
246
|
+
if (!adapterId)
|
|
247
|
+
return false;
|
|
248
|
+
const relativePath = path.relative(path.resolve(stashRoot), path.resolve(candidatePath)).replaceAll("\\", "/");
|
|
249
|
+
if (!relativePath || relativePath.startsWith("../") || path.posix.isAbsolute(relativePath))
|
|
250
|
+
return false;
|
|
251
|
+
if (adapterId === "akm" && !relativePath.startsWith("workflows/"))
|
|
252
|
+
return false;
|
|
253
|
+
const extension = path.posix.extname(relativePath).toLowerCase();
|
|
254
|
+
return WORKFLOW_EXTENSIONS.includes(extension);
|
|
255
|
+
}
|
|
@@ -14,12 +14,9 @@
|
|
|
14
14
|
* their previous home in `builders.ts`.
|
|
15
15
|
*/
|
|
16
16
|
import { UsageError } from "../../core/errors.js";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!request.model || request.modelIsExact)
|
|
21
|
-
return request.model;
|
|
22
|
-
return resolveModel(request.model, platform, profile.modelAliases, profile.globalModelAliases);
|
|
17
|
+
/** Models reaching a harness have already crossed the single model-map boundary. */
|
|
18
|
+
export function resolveDispatchModel(request, _profile, _platform) {
|
|
19
|
+
return request.model;
|
|
23
20
|
}
|
|
24
21
|
/**
|
|
25
22
|
* Guard against values that start with `--`, which would be mis-interpreted as
|
|
@@ -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
|
+
export const CONVERSATION_FALLBACK_BEGIN = "<AKM_CONVERSATION_JSON>";
|
|
5
|
+
export const CONVERSATION_FALLBACK_END = "</AKM_CONVERSATION_JSON>";
|
|
6
|
+
/**
|
|
7
|
+
* Preserve ordered message roles for a one-shot transport with no native
|
|
8
|
+
* conversation channel. The fixed `{role,content}` projection and UTF-8 byte
|
|
9
|
+
* length make the embedded JSON unambiguous even when content contains marker
|
|
10
|
+
* text. The terminal command remains outside the prefix block.
|
|
11
|
+
*/
|
|
12
|
+
export function composeConversationFallbackPrompt(conversation, command) {
|
|
13
|
+
const json = JSON.stringify(conversation.map((message) => ({ role: message.role, content: message.content })));
|
|
14
|
+
const byteLength = new TextEncoder().encode(json).byteLength;
|
|
15
|
+
return `${CONVERSATION_FALLBACK_BEGIN} ${byteLength}\n${json}\n${CONVERSATION_FALLBACK_END}\n\n${command}`;
|
|
16
|
+
}
|