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
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
* This module is intentionally tiny and stateless so tests can stub it via
|
|
16
16
|
* `mock.module("../src/llm/graph-extract", ...)` without hitting a network.
|
|
17
17
|
*
|
|
18
|
-
* The LLM
|
|
19
|
-
*
|
|
18
|
+
* The symbolic LLM runner comes from the current index-pass execution
|
|
19
|
+
* resolution and is passed straight through.
|
|
20
20
|
*/
|
|
21
21
|
import systemPromptTemplate from "../assets/prompts/graph-extract-system.md" with { type: "text" };
|
|
22
22
|
import userPromptTemplate from "../assets/prompts/graph-extract-user-prompt.md" with { type: "text" };
|
|
23
23
|
import { toErrorMessage } from "../core/common.js";
|
|
24
|
+
import { ConfigError } from "../core/errors.js";
|
|
24
25
|
import { parseEmbeddedJsonResponse } from "../core/parse.js";
|
|
25
26
|
import { warn, warnVerbose } from "../core/warn.js";
|
|
26
|
-
import {
|
|
27
|
+
import { isContextSizeError } from "./client.js";
|
|
27
28
|
import { tryLlmFeature } from "./feature-gate.js";
|
|
28
29
|
import { callStructured } from "./structured-call.js";
|
|
29
30
|
/**
|
|
@@ -447,8 +448,24 @@ function buildBatchUserPrompt(bodies) {
|
|
|
447
448
|
`- The array MUST have exactly ${count} elements — one placeholder per asset even if empty.\n\n` +
|
|
448
449
|
assetBlocks);
|
|
449
450
|
}
|
|
450
|
-
function formatContextHint(
|
|
451
|
-
return
|
|
451
|
+
function formatContextHint(llmRunner) {
|
|
452
|
+
return llmRunner.connection.contextLength ? `, configured contextLength=${llmRunner.connection.contextLength}` : "";
|
|
453
|
+
}
|
|
454
|
+
/** Dispatch one raw graph prompt through the common resolved-request adapter. */
|
|
455
|
+
async function callGraphLlm(runner, messages, request, lease, onNotices) {
|
|
456
|
+
return callStructured({
|
|
457
|
+
feature: "graph_extraction",
|
|
458
|
+
runner,
|
|
459
|
+
...(lease ? { lease } : {}),
|
|
460
|
+
messages,
|
|
461
|
+
request,
|
|
462
|
+
onNotices,
|
|
463
|
+
parse: (raw) => raw ?? "",
|
|
464
|
+
onError: (_cls, error) => {
|
|
465
|
+
throw error;
|
|
466
|
+
},
|
|
467
|
+
fallback: "",
|
|
468
|
+
});
|
|
452
469
|
}
|
|
453
470
|
/**
|
|
454
471
|
* Parse and validate a single item from the batch response array.
|
|
@@ -457,6 +474,21 @@ function formatContextHint(llmConfig) {
|
|
|
457
474
|
function parseBatchItem(raw) {
|
|
458
475
|
return parseGraphExtraction(raw);
|
|
459
476
|
}
|
|
477
|
+
function applySuccessfulBatchResults(results, batchResult, nonEmptyBodies, nonEmptyIndices, batchState) {
|
|
478
|
+
if (batchState)
|
|
479
|
+
batchState.nonArrayBatchFailures = 0;
|
|
480
|
+
if (batchResult.length > nonEmptyBodies.length) {
|
|
481
|
+
warn(`graph extraction (batch): response had ${batchResult.length} items for ${nonEmptyBodies.length} assets; ` +
|
|
482
|
+
`ignoring ${batchResult.length - nonEmptyBodies.length} extra item(s).`);
|
|
483
|
+
}
|
|
484
|
+
for (let j = 0; j < nonEmptyBodies.length; j++) {
|
|
485
|
+
const originalIndex = nonEmptyIndices[j];
|
|
486
|
+
if (originalIndex === undefined)
|
|
487
|
+
continue;
|
|
488
|
+
if (j < batchResult.length)
|
|
489
|
+
results[originalIndex] = parseBatchItem(batchResult[j]);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
460
492
|
/**
|
|
461
493
|
* Extract entities and relations from multiple asset bodies in a single LLM
|
|
462
494
|
* call (batched graph extraction).
|
|
@@ -474,13 +506,13 @@ function parseBatchItem(raw) {
|
|
|
474
506
|
* Routes through `tryLlmFeature("graph_extraction", ...)` so the feature gate
|
|
475
507
|
* and onFallback hook are honoured uniformly.
|
|
476
508
|
*
|
|
477
|
-
* @param
|
|
509
|
+
* @param llmRunner - Symbolic LLM runner selected through shared execution lowering.
|
|
478
510
|
* @param bodies - Asset body strings to process in one batch.
|
|
479
511
|
* @param signal - Optional AbortSignal for cancellation.
|
|
480
512
|
* @param akmConfig - Full AKM config (for feature-gate checks).
|
|
481
513
|
* @param onFallback - Optional fallback event sink.
|
|
482
514
|
*/
|
|
483
|
-
export async function extractGraphFromBodies(
|
|
515
|
+
export async function extractGraphFromBodies(llmRunner, bodies, signal, akmConfig, onFallback, options = {}) {
|
|
484
516
|
const empty = () => ({ entities: [], relations: [] });
|
|
485
517
|
const batchState = normalizeBatchState(options.batchState);
|
|
486
518
|
// Degenerate case: no bodies → empty array (not an error).
|
|
@@ -488,7 +520,7 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
488
520
|
return [];
|
|
489
521
|
// Single body: delegate to the single-asset path for identical behaviour.
|
|
490
522
|
if (bodies.length === 1) {
|
|
491
|
-
const result = await extractGraphFromBody(
|
|
523
|
+
const result = await extractGraphFromBody(llmRunner, bodies[0] ?? "", signal, akmConfig, onFallback, options);
|
|
492
524
|
return [result];
|
|
493
525
|
}
|
|
494
526
|
// Filter out bodies that are empty so we don't waste tokens, but keep
|
|
@@ -511,13 +543,13 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
511
543
|
}
|
|
512
544
|
if (oversizedIndices.length > 0) {
|
|
513
545
|
await Promise.all(oversizedIndices.map(async (index) => {
|
|
514
|
-
results[index] = await extractGraphFromBody(
|
|
546
|
+
results[index] = await extractGraphFromBody(llmRunner, bodies[index] ?? "", signal, akmConfig, onFallback, options);
|
|
515
547
|
}));
|
|
516
548
|
}
|
|
517
549
|
if (nonEmptyBodies.length === 0)
|
|
518
550
|
return results;
|
|
519
551
|
if (batchState?.batchingDisabled) {
|
|
520
|
-
return Promise.all(bodies.map((body) => extractGraphFromBody(
|
|
552
|
+
return Promise.all(bodies.map((body) => extractGraphFromBody(llmRunner, body, signal, akmConfig, onFallback, options)));
|
|
521
553
|
}
|
|
522
554
|
const systemPrompt = buildBatchSystemPrompt();
|
|
523
555
|
const userPrompt = buildBatchUserPrompt(nonEmptyBodies);
|
|
@@ -527,19 +559,19 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
527
559
|
}
|
|
528
560
|
let batchContextError = false;
|
|
529
561
|
let nonArrayResponse = false;
|
|
530
|
-
const
|
|
562
|
+
const batchOutcome = await tryLlmFeature("graph_extraction", akmConfig, async () => {
|
|
531
563
|
try {
|
|
532
|
-
const raw = await
|
|
564
|
+
const raw = await callGraphLlm(llmRunner, [
|
|
533
565
|
{ role: "system", content: systemPrompt },
|
|
534
566
|
{ role: "user", content: userPrompt },
|
|
535
567
|
], {
|
|
536
568
|
temperature: 0.1,
|
|
537
|
-
timeoutMs:
|
|
569
|
+
timeoutMs: llmRunner.timeoutMs,
|
|
538
570
|
signal,
|
|
539
571
|
onRetryAttempt: () => bumpTelemetry(options.telemetry, "retryAttempts"),
|
|
540
|
-
});
|
|
572
|
+
}, options.lease, options.onNotices);
|
|
541
573
|
if (!raw)
|
|
542
|
-
return null;
|
|
574
|
+
return { kind: "value", value: null };
|
|
543
575
|
// Array-preferring salvage (#635): the batch contract is a top-level
|
|
544
576
|
// JSON array. A leading/example `{…}` object in the response must not
|
|
545
577
|
// mask a valid `[…]` array as a false "non-array" failure.
|
|
@@ -549,10 +581,10 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
549
581
|
// (#635). Many genuine non-array responses recover when the model is
|
|
550
582
|
// told explicitly to emit only the raw array.
|
|
551
583
|
bumpTelemetry(options.telemetry, "retryAttempts");
|
|
552
|
-
const retryRaw = await
|
|
584
|
+
const retryRaw = await callGraphLlm(llmRunner, [
|
|
553
585
|
{ role: "system", content: buildBatchRetrySystemPrompt() },
|
|
554
586
|
{ role: "user", content: userPrompt },
|
|
555
|
-
], { temperature: 0, timeoutMs:
|
|
587
|
+
], { temperature: 0, timeoutMs: llmRunner.timeoutMs, signal }, options.lease, options.onNotices);
|
|
556
588
|
parsed = retryRaw ? parseEmbeddedJsonResponse(retryRaw, { expect: "array" }) : undefined;
|
|
557
589
|
}
|
|
558
590
|
if (!Array.isArray(parsed)) {
|
|
@@ -566,51 +598,42 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
566
598
|
}
|
|
567
599
|
warn(`graph extraction (batch): LLM response was not a JSON array for ${nonEmptyBodies.length} asset(s) ` +
|
|
568
600
|
`even after a stricter retry; will fall back per-asset. ` +
|
|
569
|
-
`promptChars=${userPrompt.length}${formatContextHint(
|
|
570
|
-
return null;
|
|
601
|
+
`promptChars=${userPrompt.length}${formatContextHint(llmRunner)}`);
|
|
602
|
+
return { kind: "value", value: null };
|
|
571
603
|
}
|
|
572
|
-
return parsed;
|
|
604
|
+
return { kind: "value", value: parsed };
|
|
573
605
|
}
|
|
574
606
|
catch (err) {
|
|
607
|
+
if (err instanceof ConfigError)
|
|
608
|
+
return { kind: "config-error", error: err };
|
|
575
609
|
const errMsg = toErrorMessage(err);
|
|
576
610
|
if (isContextSizeError(errMsg)) {
|
|
577
611
|
batchContextError = true;
|
|
578
612
|
bumpTelemetry(options.telemetry, "contextBatchRetries");
|
|
579
613
|
warn(`graph extraction (batch): context size exceeded for ${nonEmptyBodies.length} asset(s); ` +
|
|
580
|
-
`skipping batch. promptChars=${userPrompt.length}${formatContextHint(
|
|
614
|
+
`skipping batch. promptChars=${userPrompt.length}${formatContextHint(llmRunner)}`);
|
|
581
615
|
}
|
|
582
616
|
else {
|
|
583
617
|
warn(`graph extraction (batch) failed for ${nonEmptyBodies.length} asset(s); ` +
|
|
584
|
-
`promptChars=${userPrompt.length}${formatContextHint(
|
|
618
|
+
`promptChars=${userPrompt.length}${formatContextHint(llmRunner)}: ${errMsg}`);
|
|
585
619
|
}
|
|
586
|
-
return null;
|
|
620
|
+
return { kind: "value", value: null };
|
|
587
621
|
}
|
|
588
|
-
}, null, {
|
|
589
|
-
timeoutMs:
|
|
622
|
+
}, { kind: "value", value: null }, {
|
|
623
|
+
timeoutMs: llmRunner.timeoutMs,
|
|
590
624
|
onFallback,
|
|
591
625
|
});
|
|
626
|
+
if (batchOutcome.kind === "config-error")
|
|
627
|
+
throw batchOutcome.error;
|
|
628
|
+
const batchResult = batchOutcome.value;
|
|
592
629
|
// Map successful batch results back to their original indices.
|
|
593
630
|
if (batchResult !== null) {
|
|
594
|
-
|
|
595
|
-
batchState.nonArrayBatchFailures = 0;
|
|
596
|
-
if (batchResult.length > nonEmptyBodies.length) {
|
|
597
|
-
warn(`graph extraction (batch): response had ${batchResult.length} items for ${nonEmptyBodies.length} assets; ` +
|
|
598
|
-
`ignoring ${batchResult.length - nonEmptyBodies.length} extra item(s).`);
|
|
599
|
-
}
|
|
600
|
-
for (let j = 0; j < nonEmptyBodies.length; j++) {
|
|
601
|
-
const originalIndex = nonEmptyIndices[j];
|
|
602
|
-
if (originalIndex === undefined)
|
|
603
|
-
continue;
|
|
604
|
-
if (j < batchResult.length) {
|
|
605
|
-
results[originalIndex] = parseBatchItem(batchResult[j]);
|
|
606
|
-
}
|
|
607
|
-
// j >= batchResult.length → partial failure; handled below.
|
|
608
|
-
}
|
|
631
|
+
applySuccessfulBatchResults(results, batchResult, nonEmptyBodies, nonEmptyIndices, batchState);
|
|
609
632
|
}
|
|
610
633
|
if (batchContextError && nonEmptyBodies.length > 1) {
|
|
611
634
|
const splitAt = Math.ceil(nonEmptyBodies.length / 2);
|
|
612
|
-
const left = await extractGraphFromBodies(
|
|
613
|
-
const right = await extractGraphFromBodies(
|
|
635
|
+
const left = await extractGraphFromBodies(llmRunner, nonEmptyBodies.slice(0, splitAt), signal, akmConfig, onFallback, options);
|
|
636
|
+
const right = await extractGraphFromBodies(llmRunner, nonEmptyBodies.slice(splitAt), signal, akmConfig, onFallback, options);
|
|
614
637
|
const combined = [...left, ...right];
|
|
615
638
|
for (let j = 0; j < nonEmptyIndices.length; j++) {
|
|
616
639
|
const origIdx = nonEmptyIndices[j];
|
|
@@ -642,7 +665,7 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
642
665
|
}
|
|
643
666
|
await Promise.all(fallbackIndices.map(async (origIdx) => {
|
|
644
667
|
const body = bodies[origIdx] ?? "";
|
|
645
|
-
results[origIdx] = await extractGraphFromBody(
|
|
668
|
+
results[origIdx] = await extractGraphFromBody(llmRunner, body, signal, akmConfig, onFallback, options);
|
|
646
669
|
}));
|
|
647
670
|
}
|
|
648
671
|
else if (batchContextError) {
|
|
@@ -664,7 +687,7 @@ export async function extractGraphFromBodies(llmConfig, bodies, signal, akmConfi
|
|
|
664
687
|
* Routes through `tryLlmFeature("graph_extraction", ...)` so the feature gate
|
|
665
688
|
* and onFallback hook are honoured uniformly (Fix C5).
|
|
666
689
|
*/
|
|
667
|
-
export async function extractGraphFromBody(
|
|
690
|
+
export async function extractGraphFromBody(llmRunner, body, signal, akmConfig, onFallback, options = {}) {
|
|
668
691
|
const empty = (reason, status) => ({
|
|
669
692
|
entities: [],
|
|
670
693
|
relations: [],
|
|
@@ -682,7 +705,7 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
682
705
|
if (chunked.chunks.length > 1) {
|
|
683
706
|
const chunkResults = [];
|
|
684
707
|
for (const chunk of chunked.chunks) {
|
|
685
|
-
chunkResults.push(await extractGraphFromBody(
|
|
708
|
+
chunkResults.push(await extractGraphFromBody(llmRunner, chunk, signal, akmConfig, onFallback, options));
|
|
686
709
|
}
|
|
687
710
|
const merged = mergeGraphExtractions(chunkResults);
|
|
688
711
|
merged.truncationCount = (merged.truncationCount ?? 0) + chunked.truncationCount;
|
|
@@ -692,17 +715,19 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
692
715
|
return callStructured({
|
|
693
716
|
feature: "graph_extraction",
|
|
694
717
|
akmConfig,
|
|
695
|
-
|
|
718
|
+
runner: llmRunner,
|
|
719
|
+
...(options.lease ? { lease: options.lease } : {}),
|
|
696
720
|
messages: [
|
|
697
721
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
698
722
|
{ role: "user", content: userPrompt },
|
|
699
723
|
],
|
|
700
724
|
request: {
|
|
701
725
|
temperature: 0.1,
|
|
702
|
-
timeoutMs:
|
|
726
|
+
timeoutMs: llmRunner.timeoutMs,
|
|
703
727
|
signal,
|
|
704
728
|
onRetryAttempt: () => bumpTelemetry(options.telemetry, "retryAttempts"),
|
|
705
729
|
},
|
|
730
|
+
onNotices: options.onNotices,
|
|
706
731
|
parse: (raw) => {
|
|
707
732
|
if (!raw)
|
|
708
733
|
return empty();
|
|
@@ -724,18 +749,18 @@ export async function extractGraphFromBody(llmConfig, body, signal, akmConfig, o
|
|
|
724
749
|
const errMsg = toErrorMessage(err);
|
|
725
750
|
if (cls === "context_limit") {
|
|
726
751
|
bumpTelemetry(options.telemetry, "failureCount");
|
|
727
|
-
warn(`graph extraction: context size exceeded for asset; promptChars=${userPrompt.length}${formatContextHint(
|
|
752
|
+
warn(`graph extraction: context size exceeded for asset; promptChars=${userPrompt.length}${formatContextHint(llmRunner)}. ` +
|
|
728
753
|
`Consider increasing llm.contextLength in config.json.`);
|
|
729
754
|
return empty("context_limit", "failed");
|
|
730
755
|
}
|
|
731
756
|
else if (cls === "html") {
|
|
732
757
|
bumpTelemetry(options.telemetry, "htmlErrorCount");
|
|
733
|
-
warn(`graph extraction: provider returned HTML instead of JSON for asset; promptChars=${userPrompt.length}${formatContextHint(
|
|
758
|
+
warn(`graph extraction: provider returned HTML instead of JSON for asset; promptChars=${userPrompt.length}${formatContextHint(llmRunner)}: ${errMsg}`);
|
|
734
759
|
return empty("llm_error", "failed");
|
|
735
760
|
}
|
|
736
761
|
else {
|
|
737
762
|
bumpTelemetry(options.telemetry, "failureCount");
|
|
738
|
-
warn(`graph extraction failed for asset; promptChars=${userPrompt.length}${formatContextHint(
|
|
763
|
+
warn(`graph extraction failed for asset; promptChars=${userPrompt.length}${formatContextHint(llmRunner)}: ${errMsg}`);
|
|
739
764
|
return empty("llm_error", "failed");
|
|
740
765
|
}
|
|
741
766
|
},
|
package/dist/llm/index-passes.js
CHANGED
|
@@ -1,16 +1,54 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import {
|
|
4
|
+
import { ConfigError } from "../core/errors.js";
|
|
5
|
+
import { cloneExecutionJsonObject } from "../execution/json.js";
|
|
6
|
+
import { lowerResolvedExecutionRequest } from "../integrations/agent/execution-lowering.js";
|
|
7
|
+
import { prepareInlineExecution } from "../integrations/agent/inline-execution.js";
|
|
8
|
+
const NO_LOWERING_NOTICES = Object.freeze([]);
|
|
9
|
+
function own(value, key) {
|
|
10
|
+
return value !== undefined && Object.hasOwn(value, key);
|
|
11
|
+
}
|
|
12
|
+
/** Adapt one index invocation layer into the shared execution vocabulary. */
|
|
13
|
+
function indexExecutionDefaults(layer) {
|
|
14
|
+
if (!layer)
|
|
15
|
+
return {};
|
|
16
|
+
return {
|
|
17
|
+
...(own(layer, "engine") ? { engine: layer.engine } : {}),
|
|
18
|
+
...(own(layer, "model") ? { model: layer.model } : {}),
|
|
19
|
+
...(own(layer, "timeoutMs") ? { timeout: layer.timeoutMs } : {}),
|
|
20
|
+
...(own(layer, "llm") && layer.llm !== undefined
|
|
21
|
+
? { inference: cloneExecutionJsonObject(layer.llm, "index pass LLM inference") }
|
|
22
|
+
: {}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
5
25
|
/**
|
|
6
26
|
* Resolve standalone index passes from the index section only. Improve
|
|
7
27
|
* strategies own improve-triggered calls and are intentionally not consulted.
|
|
8
28
|
*/
|
|
9
|
-
export function
|
|
29
|
+
export function resolveIndexPassExecution(passName, config) {
|
|
10
30
|
const pass = config.index?.[passName];
|
|
11
31
|
if (pass?.enabled === false)
|
|
12
|
-
return undefined;
|
|
32
|
+
return Object.freeze({ runner: undefined, notices: NO_LOWERING_NOTICES });
|
|
13
33
|
const defaults = config.index?.defaults;
|
|
14
|
-
const
|
|
15
|
-
|
|
34
|
+
const fallbackLlmEngine = config.defaults?.llmEngine;
|
|
35
|
+
const selectedEngine = pass?.engine ?? defaults?.engine ?? fallbackLlmEngine;
|
|
36
|
+
if (!selectedEngine)
|
|
37
|
+
return Object.freeze({ runner: undefined, notices: NO_LOWERING_NOTICES });
|
|
38
|
+
const invocationDefaults = {
|
|
39
|
+
...indexExecutionDefaults(defaults),
|
|
40
|
+
...(!own(defaults, "engine") && fallbackLlmEngine ? { engine: fallbackLlmEngine } : {}),
|
|
41
|
+
};
|
|
42
|
+
const prepared = prepareInlineExecution({
|
|
43
|
+
content: "",
|
|
44
|
+
config,
|
|
45
|
+
invocationKind: "direct",
|
|
46
|
+
invocationDefaults,
|
|
47
|
+
current: indexExecutionDefaults(pass),
|
|
48
|
+
});
|
|
49
|
+
const lowered = lowerResolvedExecutionRequest(prepared.request, prepared.config);
|
|
50
|
+
if (lowered.runner.kind !== "llm") {
|
|
51
|
+
throw new ConfigError(`Index pass ${JSON.stringify(passName)} requires an LLM engine; ${JSON.stringify(selectedEngine)} is not one.`, "INVALID_CONFIG_FILE");
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze({ runner: lowered.runner, notices: lowered.notices });
|
|
16
54
|
}
|
package/dist/llm/memory-infer.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* This module is intentionally tiny and stateless so tests can stub it via
|
|
14
14
|
* `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
|
|
15
15
|
*
|
|
16
|
-
* The LLM
|
|
17
|
-
*
|
|
16
|
+
* The symbolic LLM runner comes from the current index-pass execution
|
|
17
|
+
* resolution and is passed straight through.
|
|
18
18
|
*/
|
|
19
19
|
import memoryInferSystemPrompt from "../assets/prompts/memory-infer-system.md" with { type: "text" };
|
|
20
20
|
import memoryInferUserPrompt from "../assets/prompts/memory-infer-user.md" with { type: "text" };
|
|
@@ -37,7 +37,7 @@ const PROMPT_PLACEHOLDERS = new Set([
|
|
|
37
37
|
]);
|
|
38
38
|
/**
|
|
39
39
|
* Strict JSON Schema for the derived-memory payload. Sent to providers that
|
|
40
|
-
* opt in via `
|
|
40
|
+
* opt in via `runner.connection.supportsJsonSchema = true`; the client
|
|
41
41
|
* silently drops the schema for providers that don't.
|
|
42
42
|
*
|
|
43
43
|
* Extends the responseSchema lift (PR 1, asset-writers-investigation §5) to
|
|
@@ -70,7 +70,7 @@ const DERIVED_MEMORY_JSON_SCHEMA = {
|
|
|
70
70
|
* feature gate, error classification, and onFallback hook are honoured uniformly
|
|
71
71
|
* (Fix C5).
|
|
72
72
|
*/
|
|
73
|
-
export async function compressMemoryToDerivedMemory(
|
|
73
|
+
export async function compressMemoryToDerivedMemory(llmRunner, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt, onNotices, lease) {
|
|
74
74
|
const trimmedBody = body.trim();
|
|
75
75
|
if (!trimmedBody)
|
|
76
76
|
return undefined;
|
|
@@ -84,18 +84,20 @@ export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akm
|
|
|
84
84
|
return callStructured({
|
|
85
85
|
feature: "memory_inference",
|
|
86
86
|
akmConfig,
|
|
87
|
-
|
|
87
|
+
runner: llmRunner,
|
|
88
|
+
...(lease ? { lease } : {}),
|
|
88
89
|
messages: [
|
|
89
90
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
90
91
|
{ role: "user", content: userPrompt },
|
|
91
92
|
],
|
|
92
93
|
request: {
|
|
93
94
|
temperature: 0.1,
|
|
94
|
-
timeoutMs:
|
|
95
|
+
timeoutMs: llmRunner.timeoutMs,
|
|
95
96
|
signal,
|
|
96
97
|
responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
|
|
97
98
|
onRetryAttempt,
|
|
98
99
|
},
|
|
100
|
+
onNotices,
|
|
99
101
|
parse: (raw) => {
|
|
100
102
|
if (!raw)
|
|
101
103
|
return undefined;
|
|
@@ -22,7 +22,7 @@ const SYSTEM_PROMPT = metadataEnhanceSystemPrompt;
|
|
|
22
22
|
* `akmConfig` is `undefined` the gate is bypassed entirely: the LLM call runs
|
|
23
23
|
* unconditionally and errors propagate to direct callers such as tests.
|
|
24
24
|
*/
|
|
25
|
-
export async function enhanceMetadata(
|
|
25
|
+
export async function enhanceMetadata(runner, entry, fileContent, signal, akmConfig, onNotices, lease) {
|
|
26
26
|
const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
|
|
27
27
|
if (entry.description)
|
|
28
28
|
contextParts.push(`Current description: ${entry.description}`);
|
|
@@ -51,12 +51,14 @@ Return ONLY the JSON object, no explanation.`;
|
|
|
51
51
|
const outcome = await callStructured({
|
|
52
52
|
feature: "metadata_enhance",
|
|
53
53
|
akmConfig,
|
|
54
|
-
|
|
54
|
+
runner,
|
|
55
|
+
...(lease ? { lease } : {}),
|
|
55
56
|
messages: [
|
|
56
57
|
{ role: "system", content: SYSTEM_PROMPT },
|
|
57
58
|
{ role: "user", content: userPrompt },
|
|
58
59
|
],
|
|
59
|
-
request: { signal, timeoutMs:
|
|
60
|
+
request: { signal, timeoutMs: runner.timeoutMs },
|
|
61
|
+
onNotices,
|
|
60
62
|
parse: (raw) => {
|
|
61
63
|
const parsed = raw ? parseJsonResponse(raw) : undefined;
|
|
62
64
|
const metadata = {};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ConfigError } from "../core/errors.js";
|
|
5
|
+
import { acquireLoweredExecutionDispatchLease, dispatchLoweredExecutionRequest, lowerResolvedExecutionRequestWithRunner, } from "../integrations/agent/execution-lowering.js";
|
|
6
|
+
import { prepareInlineExecutionWithRunner } from "../integrations/agent/inline-execution.js";
|
|
7
|
+
import { isContextSizeError, LlmCallError } from "./client.js";
|
|
8
|
+
import { isLlmFeatureEnabled, tryLlmFeature, } from "./feature-gate.js";
|
|
6
9
|
/**
|
|
7
10
|
* Classify a thrown LLM error into one of the three buckets. This is the single
|
|
8
11
|
* home for the `isContextSizeError -> html -> other` ladder that was previously
|
|
@@ -16,42 +19,136 @@ export function classifyLlmError(err) {
|
|
|
16
19
|
return "html";
|
|
17
20
|
return "other";
|
|
18
21
|
}
|
|
22
|
+
function own(value, key) {
|
|
23
|
+
return value !== undefined && Object.hasOwn(value, key);
|
|
24
|
+
}
|
|
25
|
+
/** @internal Exact request-to-cascade projection, exported for presence-semantics contracts. */
|
|
26
|
+
export function resolveStructuredCurrent(current, request) {
|
|
27
|
+
const out = current ? { ...current } : {};
|
|
28
|
+
const requestHasInference = own(request, "temperature") || own(request, "maxTokens") || own(request, "enableThinking");
|
|
29
|
+
const baseInference = current?.inference && typeof current.inference === "object" && !Array.isArray(current.inference)
|
|
30
|
+
? { ...current.inference }
|
|
31
|
+
: {};
|
|
32
|
+
const inference = { ...baseInference };
|
|
33
|
+
if (own(request, "temperature") && request?.temperature !== undefined)
|
|
34
|
+
inference.temperature = request.temperature;
|
|
35
|
+
if (own(request, "maxTokens") && request?.maxTokens !== undefined)
|
|
36
|
+
inference.maxTokens = request.maxTokens;
|
|
37
|
+
if (own(request, "enableThinking") && request?.enableThinking !== undefined) {
|
|
38
|
+
inference.enableThinking = request.enableThinking;
|
|
39
|
+
}
|
|
40
|
+
if (Object.keys(inference).length > 0 || requestHasInference)
|
|
41
|
+
out.inference = inference;
|
|
42
|
+
else if (current?.inference === null)
|
|
43
|
+
out.inference = null;
|
|
44
|
+
if (own(request, "responseSchema") && request?.responseSchema !== undefined) {
|
|
45
|
+
out.outputSchema = request.responseSchema;
|
|
46
|
+
}
|
|
47
|
+
if (own(request, "timeoutMs"))
|
|
48
|
+
out.timeout = request?.timeoutMs ?? null;
|
|
49
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
50
|
+
}
|
|
51
|
+
function requireTerminalUserMessage(messages) {
|
|
52
|
+
const terminal = messages.at(-1);
|
|
53
|
+
if (!terminal || terminal.role !== "user") {
|
|
54
|
+
throw new TypeError("callStructured messages must end with the terminal user command");
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
content: terminal.content,
|
|
58
|
+
conversation: messages.slice(0, -1).map((message) => ({ role: message.role, content: message.content })),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function dispatchFailure(result) {
|
|
62
|
+
const message = result.error ?? result.stderr ?? result.reason ?? "LLM dispatch failed";
|
|
63
|
+
return result.llmErrorCode ? new LlmCallError(message, result.llmErrorCode) : new Error(message);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validate one already-selected symbolic LLM runner before an operation makes
|
|
67
|
+
* any durable mutation. Callers must apply their feature/authorization gates
|
|
68
|
+
* first. Acquisition crosses the canonical prepare -> lower -> lease boundary,
|
|
69
|
+
* so required credentials are snapshotted by the same central authority as a
|
|
70
|
+
* real call without contacting the provider.
|
|
71
|
+
*/
|
|
72
|
+
export async function preflightStructuredLlmRunner(runner) {
|
|
73
|
+
const prepared = prepareInlineExecutionWithRunner({
|
|
74
|
+
content: "Validate the selected LLM runner before operation dispatch.",
|
|
75
|
+
runner,
|
|
76
|
+
invocationKind: "direct",
|
|
77
|
+
});
|
|
78
|
+
const lowered = lowerResolvedExecutionRequestWithRunner(prepared.request, prepared.runner);
|
|
79
|
+
return acquireLoweredExecutionDispatchLease(lowered);
|
|
80
|
+
}
|
|
19
81
|
export async function callStructured(opts) {
|
|
20
|
-
const { feature, akmConfig, enabled,
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
82
|
+
const { feature, akmConfig, enabled, messages, request, parse, onError, fallback, onFallback } = opts;
|
|
83
|
+
// A disabled feature owns a true no-work path: it does not need a runner,
|
|
84
|
+
// messages, authorization, lowering, or provider state. Some commands keep
|
|
85
|
+
// their runner optional precisely because a disabled feature must fall back
|
|
86
|
+
// before execution planning begins.
|
|
87
|
+
if (akmConfig !== undefined && !isLlmFeatureEnabled(akmConfig, feature, enabled)) {
|
|
88
|
+
return tryLlmFeature(feature, akmConfig, async () => fallback, fallback, {
|
|
89
|
+
...(own(request, "timeoutMs") ? { timeoutMs: request?.timeoutMs } : {}),
|
|
90
|
+
...(enabled !== undefined ? { enabled } : {}),
|
|
91
|
+
onFallback,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const runner = opts.runner;
|
|
95
|
+
if (!runner)
|
|
96
|
+
throw new TypeError("callStructured requires a resolved LLM runner");
|
|
97
|
+
const terminal = requireTerminalUserMessage(messages);
|
|
98
|
+
const prepareInvocation = () => {
|
|
99
|
+
const current = resolveStructuredCurrent(opts.current, request);
|
|
100
|
+
const prepared = prepareInlineExecutionWithRunner({
|
|
101
|
+
content: terminal.content,
|
|
102
|
+
conversation: terminal.conversation,
|
|
103
|
+
runner,
|
|
104
|
+
invocationKind: "direct",
|
|
105
|
+
...(current ? { current } : {}),
|
|
106
|
+
...(opts.authorizeTools ? { authorizeTools: opts.authorizeTools } : {}),
|
|
107
|
+
});
|
|
108
|
+
const lowered = lowerResolvedExecutionRequestWithRunner(prepared.request, prepared.runner);
|
|
109
|
+
opts.onNotices?.(lowered.notices);
|
|
110
|
+
return async () => {
|
|
111
|
+
const result = await dispatchLoweredExecutionRequest(lowered, {
|
|
112
|
+
...(opts.lease ? { lease: opts.lease } : {}),
|
|
113
|
+
...(request?.chat ? { chat: request.chat } : {}),
|
|
114
|
+
...(request?.onRetryAttempt ? { onRetryAttempt: request.onRetryAttempt } : {}),
|
|
115
|
+
...(own(request, "signal") ? { runOptions: { signal: request?.signal } } : {}),
|
|
116
|
+
});
|
|
117
|
+
if (!result.ok)
|
|
118
|
+
throw dispatchFailure(result);
|
|
119
|
+
return parse(result.stdout);
|
|
120
|
+
};
|
|
35
121
|
};
|
|
36
122
|
// UNGATED: run the chat+parse directly. Errors propagate — no `onError`
|
|
37
123
|
// funnel — matching the pre-gate behaviour of direct callers.
|
|
38
124
|
if (akmConfig === undefined) {
|
|
39
|
-
|
|
40
|
-
return parse(raw);
|
|
125
|
+
return prepareInvocation()();
|
|
41
126
|
}
|
|
127
|
+
// On an enabled path, preparation/lowering happen OUTSIDE tryLlmFeature so
|
|
128
|
+
// authorization and invalid-config failures remain hard failures instead of
|
|
129
|
+
// being mistaken for provider fallbacks.
|
|
130
|
+
const invoke = prepareInvocation();
|
|
42
131
|
// GATED: run through `tryLlmFeature`. A throw inside is classified ONCE and
|
|
43
132
|
// routed to `onError`; `tryLlmFeature` returns `fallback` on disablement/timeout.
|
|
44
|
-
|
|
133
|
+
const outcome = await tryLlmFeature(feature, akmConfig, async () => {
|
|
45
134
|
try {
|
|
46
|
-
|
|
47
|
-
return parse(raw);
|
|
135
|
+
return { kind: "value", value: await invoke() };
|
|
48
136
|
}
|
|
49
137
|
catch (err) {
|
|
50
|
-
|
|
138
|
+
// Credential materialization remains dispatch-owned, so a missing
|
|
139
|
+
// required symbolic credential can surface here. Preserve config
|
|
140
|
+
// failures as hard pre-provider errors instead of sending them through
|
|
141
|
+
// a leaf's provider/runtime fallback policy.
|
|
142
|
+
if (err instanceof ConfigError)
|
|
143
|
+
return { kind: "config-error", error: err };
|
|
144
|
+
return { kind: "value", value: onError(classifyLlmError(err), err) };
|
|
51
145
|
}
|
|
52
|
-
}, fallback, {
|
|
53
|
-
...(
|
|
146
|
+
}, { kind: "value", value: fallback }, {
|
|
147
|
+
...(own(request, "timeoutMs") ? { timeoutMs: request?.timeoutMs } : {}),
|
|
54
148
|
...(enabled !== undefined ? { enabled } : {}),
|
|
55
149
|
onFallback,
|
|
56
150
|
});
|
|
151
|
+
if (outcome.kind === "config-error")
|
|
152
|
+
throw outcome.error;
|
|
153
|
+
return outcome.value;
|
|
57
154
|
}
|
|
@@ -25,7 +25,7 @@ const EXEMPT_COMMANDS = new Set([
|
|
|
25
25
|
// Emits shell completion script source for eval.
|
|
26
26
|
"completions",
|
|
27
27
|
// `migrate status`/`apply` used to be exempt here too: `runMigrationTool`
|
|
28
|
-
// (src/commands/migration-tool.ts) spawns the standalone
|
|
28
|
+
// (src/commands/migration-tool.ts) spawns the task-only standalone
|
|
29
29
|
// `scripts/akm-migrate.ts` tool, which always emitted its own fixed JSON
|
|
30
30
|
// shape and never consulted `--format`. `src/commands/migrate-cli.ts` now
|
|
31
31
|
// parses that child's final result line and renders it through the normal
|
|
@@ -27,31 +27,15 @@ const HTML_RENDERER_REGISTRY = createCommandRegistry();
|
|
|
27
27
|
export function registerMdRenderer(command, handler) {
|
|
28
28
|
MD_RENDERER_REGISTRY.register(command, handler);
|
|
29
29
|
}
|
|
30
|
-
/** Register a batch of Markdown renderers in iteration order. */
|
|
31
|
-
export function registerMdRenderers(entries) {
|
|
32
|
-
MD_RENDERER_REGISTRY.registerAll(entries);
|
|
33
|
-
}
|
|
34
30
|
/** Look up a registered Markdown renderer, or `undefined` when unregistered. */
|
|
35
31
|
export function getMdRendererHandler(command) {
|
|
36
32
|
return MD_RENDERER_REGISTRY.get(command);
|
|
37
33
|
}
|
|
38
|
-
/** Remove a previously-registered Markdown renderer. Test-only utility. */
|
|
39
|
-
export function deregisterMdRenderer(command) {
|
|
40
|
-
MD_RENDERER_REGISTRY.deregister(command);
|
|
41
|
-
}
|
|
42
34
|
/** Register an HTML renderer for a command name. */
|
|
43
35
|
export function registerHtmlRenderer(command, handler) {
|
|
44
36
|
HTML_RENDERER_REGISTRY.register(command, handler);
|
|
45
37
|
}
|
|
46
|
-
/** Register a batch of HTML renderers in iteration order. */
|
|
47
|
-
export function registerHtmlRenderers(entries) {
|
|
48
|
-
HTML_RENDERER_REGISTRY.registerAll(entries);
|
|
49
|
-
}
|
|
50
38
|
/** Look up a registered HTML renderer, or `undefined` when unregistered. */
|
|
51
39
|
export function getHtmlRendererHandler(command) {
|
|
52
40
|
return HTML_RENDERER_REGISTRY.get(command);
|
|
53
41
|
}
|
|
54
|
-
/** Remove a previously-registered HTML renderer. Test-only utility. */
|
|
55
|
-
export function deregisterHtmlRenderer(command) {
|
|
56
|
-
HTML_RENDERER_REGISTRY.deregister(command);
|
|
57
|
-
}
|