akm-cli 0.9.5 → 0.9.6
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 +105 -0
- package/dist/commands/env/env-cli.js +1 -2
- package/dist/commands/env/secret-cli.js +1 -5
- package/dist/commands/feedback-cli.js +0 -4
- package/dist/commands/health/checks.js +0 -32
- package/dist/commands/health/surfaces.js +2 -2
- package/dist/commands/health.js +6 -15
- package/dist/commands/improve/autonomy-gate.js +1 -1
- package/dist/commands/improve/consolidate.js +25 -26
- package/dist/commands/improve/distill.js +2 -2
- package/dist/commands/improve/extract.js +8 -1
- package/dist/commands/improve/improve.js +3 -5
- package/dist/commands/improve/locks.js +13 -5
- package/dist/commands/improve/loop-stages.js +8 -9
- package/dist/commands/improve/memory/memory-contradiction-detect.js +1 -19
- package/dist/commands/improve/outcome-loop.js +0 -6
- package/dist/commands/improve/reflect.js +20 -9
- package/dist/commands/lint/index.js +4 -6
- package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -7
- package/dist/commands/sources/bundle-config-ops.js +9 -4
- package/dist/commands/sources/info.js +14 -10
- package/dist/commands/tasks/tasks.js +12 -16
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/common.js +61 -24
- package/dist/core/config/config-io.js +2 -2
- package/dist/core/config/config-sources.js +32 -2
- package/dist/core/config/config.js +1 -1
- package/dist/core/config/schema/engines.js +9 -7
- package/dist/core/config/schema/primitives.js +0 -5
- package/dist/core/config/schema/search.js +2 -1
- package/dist/core/file-lock.js +2 -1
- package/dist/core/maintenance-barrier.js +2 -14
- package/dist/core/paths.js +0 -3
- package/dist/core/redaction.js +2 -2
- package/dist/core/spawn-env.js +8 -12
- package/dist/core/state/migrations.js +1 -12
- package/dist/core/state-db.js +9 -27
- package/dist/core/write-source.js +12 -19
- package/dist/execution/directory-identity.js +36 -10
- package/dist/indexer/graph/graph-boost.js +0 -4
- package/dist/indexer/index-writer-lock.js +43 -24
- package/dist/indexer/index-written-assets.js +5 -6
- package/dist/indexer/indexer.js +2 -39
- package/dist/indexer/materialize-embeddings.js +85 -41
- package/dist/indexer/search/db-search.js +15 -48
- package/dist/indexer/search/ranking-contributors.js +0 -25
- package/dist/indexer/search/ranking.js +3 -13
- package/dist/integrations/agent/builder-shared.js +0 -25
- package/dist/integrations/agent/model-map.js +2 -60
- package/dist/integrations/harnesses/aider/agent-builder.js +1 -3
- package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -7
- package/dist/integrations/harnesses/claude/agent-builder.js +1 -4
- package/dist/integrations/harnesses/codex/agent-builder.js +1 -4
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -6
- package/dist/integrations/harnesses/gemini/agent-builder.js +2 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -4
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +2 -16
- package/dist/integrations/harnesses/openhands/agent-builder.js +1 -3
- package/dist/integrations/harnesses/pi/agent-builder.js +2 -4
- package/dist/integrations/session-logs/index.js +0 -9
- package/dist/llm/client.js +75 -42
- package/dist/llm/embedder.js +7 -3
- package/dist/llm/embedders/remote.js +141 -42
- package/dist/registry/network.js +5 -37
- package/dist/runtime.js +2 -10
- package/dist/scripts/akm-migrate-node.js +60 -126
- package/dist/scripts/akm-migrate.js +60 -126
- package/dist/setup/engine-config.js +2 -5
- package/dist/setup/registry-stash-loader.js +0 -8
- package/dist/setup/setup.js +9 -46
- package/dist/setup/steps/connection-shared.js +10 -13
- package/dist/sources/providers/git-install.js +1 -1
- package/dist/storage/engines/sqlite-migrations.js +20 -1
- package/dist/storage/repositories/index-entries-repository.js +0 -15
- package/dist/tasks/backends/launchd.js +15 -20
- package/dist/tasks/backends/schtasks.js +18 -8
- package/dist/tasks/run/run-native-task.js +8 -6
- package/dist/tasks/source/bounded-document.js +2 -4
- package/dist/tasks/source/task-source-v3-frozen.js +5 -7
- package/dist/tasks/source/task-source-v4.js +5 -10
- package/dist/tasks/source/task-to-v3.js +5 -10
- package/dist/tasks/source/task-to-v4.js +1 -4
- package/dist/tasks/source-v3.js +6 -6
- package/dist/workflows/exec/native-executor.js +21 -31
- package/dist/workflows/exec/run-workflow.js +5 -6
- package/dist/workflows/exec/scheduler.js +3 -19
- package/dist/workflows/exec/step-work.js +1 -4
- package/dist/workflows/exec/unit-dispatch.js +2 -2
- package/dist/workflows/exec/worktree.js +1 -13
- package/dist/workflows/freeze/targets/child-workflow.js +2 -10
- package/dist/workflows/ir/plan-hash.js +4 -6
- package/dist/workflows/ir/schema-v4.js +0 -12
- package/dist/workflows/ir/schema.js +20 -31
- package/dist/workflows/parser.js +11 -52
- package/dist/workflows/renderer.js +2 -3
- package/dist/workflows/resource-limits.js +11 -41
- package/dist/workflows/runtime/runs.js +3 -4
- package/dist/workflows/source-ir/schema.js +14 -30
- package/dist/workflows/validator.js +1 -7
- package/docs/reference/configuration.md +1 -1
- package/package.json +1 -1
- package/schemas/akm-config.json +4 -23
- package/schemas/akm-task.json +1 -2
- package/schemas/akm-workflow.json +1 -13
- package/dist/indexer/search/semantic-status.js +0 -142
package/dist/indexer/indexer.js
CHANGED
|
@@ -27,7 +27,6 @@ import { upsertUtilityScore } from "../storage/repositories/index-utility-reposi
|
|
|
27
27
|
import { getEmbeddingCount, isVecAvailable, isVecFastPathReady, warnIfVecMissing, } from "../storage/repositories/index-vec-repository.js";
|
|
28
28
|
import { assertIndexedWorkflowSourceIdentity, WorkflowSourceIdentityError } from "../workflows/source-files.js";
|
|
29
29
|
import { deleteStoredGraph } from "./db/graph-db.js";
|
|
30
|
-
import { withIndexWriterLease } from "./index-writer-lock.js";
|
|
31
30
|
import { deriveEntryProvenance, deriveInstallations } from "./installations.js";
|
|
32
31
|
import { indexedPathMatchesOwner, resolveAdapterConceptOwner, } from "./lookup/adapter-concept-owner.js";
|
|
33
32
|
import { generateEmbeddingsForDb } from "./materialize-embeddings.js";
|
|
@@ -35,7 +34,6 @@ import { canUseIncrementalSkip, computeDirFingerprint, getCachedZeroRowDirState,
|
|
|
35
34
|
import { isEnrichmentComplete, isWorkflowSkipWarning } from "./passes/metadata.js";
|
|
36
35
|
import { drainDirDocuments } from "./scan/drain-dir.js";
|
|
37
36
|
import { buildSearchText } from "./search/search-fields.js";
|
|
38
|
-
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus } from "./search/semantic-status.js";
|
|
39
37
|
import { purgeOldUsageEvents } from "./usage/usage-events.js";
|
|
40
38
|
import { walkStashFlatWithStatus } from "./walk/walker.js";
|
|
41
39
|
function collectLoweringNotices(target, notices) {
|
|
@@ -265,27 +263,6 @@ async function runFinalizePhase(ctx, deferredUpdateTransaction) {
|
|
|
265
263
|
const semanticEntryCount = getEmbeddableEntryCount(db);
|
|
266
264
|
onProgress({ phase: "finalize", message: "Verifying semantic search state." });
|
|
267
265
|
const verification = verifyIndexState(db, config, semanticEntryCount, embeddingResult);
|
|
268
|
-
const persistSemanticStatus = () => {
|
|
269
|
-
if (config.semanticSearchMode === "off") {
|
|
270
|
-
clearSemanticStatus();
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
writeSemanticStatus({
|
|
274
|
-
status: verification.semanticStatus === "disabled" ? "pending" : verification.semanticStatus,
|
|
275
|
-
...(embeddingResult.reason ? { reason: embeddingResult.reason } : {}),
|
|
276
|
-
...(embeddingResult.message ? { message: embeddingResult.message } : {}),
|
|
277
|
-
providerFingerprint: deriveSemanticProviderFingerprint(config.embedding),
|
|
278
|
-
lastCheckedAt: new Date().toISOString(),
|
|
279
|
-
entryCount: verification.entryCount,
|
|
280
|
-
embeddingCount: verification.embeddingCount,
|
|
281
|
-
});
|
|
282
|
-
};
|
|
283
|
-
if (deferredUpdateTransaction) {
|
|
284
|
-
deferredUpdateTransaction.afterCommit = persistSemanticStatus;
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
persistSemanticStatus();
|
|
288
|
-
}
|
|
289
266
|
onProgress({ phase: "verify", message: verification.message });
|
|
290
267
|
// Store verification result and totalEntries on ctx for the caller to use
|
|
291
268
|
ctx.verification = verification;
|
|
@@ -492,20 +469,7 @@ async function akmIndexReal(options) {
|
|
|
492
469
|
"Pass `akm index --clean --dry-run`, or drop `--dry-run` to run a real index.", "INVALID_FLAG_VALUE", "Run `akm index --clean --dry-run` to preview, or `akm index --clean` to apply.");
|
|
493
470
|
}
|
|
494
471
|
const requestedAt = Date.now();
|
|
495
|
-
|
|
496
|
-
return withIndexWriterLease({
|
|
497
|
-
purpose: "akm-index",
|
|
498
|
-
signal: options?.signal,
|
|
499
|
-
onWait: ({ waitedMs }) => {
|
|
500
|
-
options?.onProgress?.({
|
|
501
|
-
phase: "preflight",
|
|
502
|
-
message: `Waiting for index writer lease (${Math.round(waitedMs / 1000)}s elapsed).`,
|
|
503
|
-
});
|
|
504
|
-
},
|
|
505
|
-
onAcquired: ({ waitedMs }) => {
|
|
506
|
-
acquiredAt = requestedAt + waitedMs;
|
|
507
|
-
},
|
|
508
|
-
}, async () => {
|
|
472
|
+
return (async () => {
|
|
509
473
|
const stashDir = options.stashDir;
|
|
510
474
|
const onProgress = options?.onProgress ?? (() => { });
|
|
511
475
|
const signal = options?.signal;
|
|
@@ -644,7 +608,6 @@ async function akmIndexReal(options) {
|
|
|
644
608
|
finalizeMs: timing.tFinalizeEnd - timing.tFinalizeStart,
|
|
645
609
|
cleanMs: clean ? cleanEnd - cleanStart : 0,
|
|
646
610
|
preflightMs: timing.t0 - requestedAt,
|
|
647
|
-
leaseWaitMs: acquiredAt - requestedAt,
|
|
648
611
|
sourceCacheMs: sourceCacheEnd - sourceCacheStart,
|
|
649
612
|
endToEndMs: Date.now() - requestedAt,
|
|
650
613
|
},
|
|
@@ -658,7 +621,7 @@ async function akmIndexReal(options) {
|
|
|
658
621
|
if (!borrowedUpdateDb)
|
|
659
622
|
closeDatabase(db);
|
|
660
623
|
}
|
|
661
|
-
});
|
|
624
|
+
})();
|
|
662
625
|
}
|
|
663
626
|
function buildIndexedSourceOwners(sources) {
|
|
664
627
|
const installations = deriveInstallations([...sources]);
|
|
@@ -3,11 +3,25 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { isVerbose, warn, warnVerbose } from "../core/warn.js";
|
|
5
5
|
import { embedBatch } from "../llm/embedder.js";
|
|
6
|
-
import {
|
|
6
|
+
import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../llm/embedders/deterministic.js";
|
|
7
|
+
import { DEFAULT_LOCAL_MODEL } from "../llm/embedders/local.js";
|
|
8
|
+
import { buildTokenBoundedBatches, DEFAULT_REMOTE_BATCH_SIZE, DEFAULT_TOKEN_BUDGET, estimateTokenCount, hasRemoteEndpoint, } from "../llm/embedders/remote.js";
|
|
7
9
|
import { getEmbeddableEntryCount } from "../storage/repositories/index-entries-repository.js";
|
|
8
10
|
import { deleteMeta, getMeta, setMeta } from "../storage/repositories/index-meta-repository.js";
|
|
9
|
-
import { getAllEntriesForEmbedding, getEmbeddingCount,
|
|
10
|
-
|
|
11
|
+
import { getAllEntriesForEmbedding, getEmbeddingCount, isVecFastPathComplete, isVecFastPathReady, purgeEmbeddings, setVecFastPathReady, upsertEmbedding, } from "../storage/repositories/index-vec-repository.js";
|
|
12
|
+
/** Identifies the embedding provider+model+dimension a stored vector was generated with. */
|
|
13
|
+
export function deriveSemanticProviderFingerprint(embedding) {
|
|
14
|
+
if (isDeterministicEmbedEnabled()) {
|
|
15
|
+
return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
|
|
16
|
+
}
|
|
17
|
+
if (embedding?.endpoint) {
|
|
18
|
+
// Fingerprint keys on vector identity only (model + dimension). The endpoint
|
|
19
|
+
// is transport/routing and has no bearing on vector compatibility, so moving
|
|
20
|
+
// the same model+dimension to a different host must not force a full re-embed.
|
|
21
|
+
return `remote:${embedding.model}|${embedding.dimension ?? "default"}`;
|
|
22
|
+
}
|
|
23
|
+
return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}`;
|
|
24
|
+
}
|
|
11
25
|
function throwIfAborted(signal) {
|
|
12
26
|
if (signal?.aborted) {
|
|
13
27
|
throw signal.reason instanceof Error ? signal.reason : new Error("index interrupted");
|
|
@@ -17,7 +31,7 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
17
31
|
throwIfAborted(signal);
|
|
18
32
|
if (config.semanticSearchMode === "off") {
|
|
19
33
|
onProgress({ phase: "embeddings", message: "Semantic search disabled; skipping embeddings." });
|
|
20
|
-
return { success: false,
|
|
34
|
+
return { success: false, message: "Semantic search is disabled." };
|
|
21
35
|
}
|
|
22
36
|
// A targeted call starts from an already-published generation. Preserve its
|
|
23
37
|
// trust decision in O(1): successful writes for the changed IDs keep a
|
|
@@ -48,14 +62,35 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
48
62
|
});
|
|
49
63
|
const texts = allEntries.map((entry) => entry.searchText);
|
|
50
64
|
if (isVerbose()) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
// Mirror RemoteEmbedder's actual token-bounded batching (#874) so this
|
|
66
|
+
// log reflects the real request grouping rather than a fixed count of
|
|
67
|
+
// 100 that no longer matches what gets sent over the wire. Local runs
|
|
68
|
+
// don't batch by size at all (LocalEmbedder chunks by a fixed count
|
|
69
|
+
// for inference throughput only, never fails/skips), so there's
|
|
70
|
+
// nothing meaningful to report per-batch for them.
|
|
71
|
+
if (hasRemoteEndpoint(config.embedding ?? {})) {
|
|
72
|
+
const tokenBudget = config.embedding?.maxTokens ?? config.embedding?.contextLength ?? DEFAULT_TOKEN_BUDGET;
|
|
73
|
+
const maxCount = config.embedding?.batchSize ?? DEFAULT_REMOTE_BATCH_SIZE;
|
|
74
|
+
const batches = buildTokenBoundedBatches(texts, tokenBudget, maxCount);
|
|
75
|
+
const batchNumberByIndex = new Map();
|
|
76
|
+
batches.forEach((batch, batchIdx) => {
|
|
77
|
+
for (const i of batch.indices)
|
|
78
|
+
batchNumberByIndex.set(i, batchIdx + 1);
|
|
79
|
+
});
|
|
80
|
+
for (const [i, entry] of allEntries.entries()) {
|
|
81
|
+
const chars = entry.searchText.length;
|
|
82
|
+
const tokens = estimateTokenCount(entry.searchText);
|
|
83
|
+
const batch = batches[batchNumberByIndex.get(i) - 1];
|
|
84
|
+
const label = batch?.oversized
|
|
85
|
+
? "oversized (skipped)"
|
|
86
|
+
: `batch ${batchNumberByIndex.get(i)}/${batches.length}`;
|
|
87
|
+
warnVerbose(`[embed] ${entry.itemRef} (${chars} chars, est. ${tokens} tokens) → ${label}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
for (const entry of allEntries) {
|
|
92
|
+
warnVerbose(`[embed] ${entry.itemRef} (${entry.searchText.length} chars, est. ${estimateTokenCount(entry.searchText)} tokens)`);
|
|
93
|
+
}
|
|
59
94
|
}
|
|
60
95
|
}
|
|
61
96
|
let heartbeatTimer;
|
|
@@ -66,17 +101,24 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
66
101
|
message: `Still generating embeddings for ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"}; waiting on embedding provider.`,
|
|
67
102
|
});
|
|
68
103
|
}, 15000);
|
|
69
|
-
|
|
104
|
+
// A failing sub-batch or an oversized document is SKIPPED by embedBatch,
|
|
105
|
+
// not thrown (#874) — collect what couldn't be embedded and why, so a
|
|
106
|
+
// few bad documents don't discard every other entry's embedding.
|
|
107
|
+
const skips = [];
|
|
108
|
+
const embeddings = await embedBatch(texts, config.embedding, signal, (skip) => skips.push(skip));
|
|
70
109
|
throwIfAborted(signal);
|
|
71
110
|
let storedCount = 0;
|
|
72
111
|
let skippedCount = 0;
|
|
112
|
+
let embedFailedCount = 0;
|
|
73
113
|
let vecFailedCount = 0;
|
|
74
114
|
let vecUnavailableCount = 0;
|
|
75
115
|
db.transaction(() => {
|
|
76
116
|
for (const [i, entry] of allEntries.entries()) {
|
|
77
117
|
const embedding = embeddings[i];
|
|
78
|
-
if (!embedding)
|
|
79
|
-
|
|
118
|
+
if (!embedding) {
|
|
119
|
+
embedFailedCount++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
80
122
|
const result = upsertEmbedding(db, entry.id, embedding);
|
|
81
123
|
if (result.stored)
|
|
82
124
|
storedCount++;
|
|
@@ -91,6 +133,14 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
91
133
|
if (skippedCount > 0) {
|
|
92
134
|
warn(`[embed] ${skippedCount} embedding${skippedCount === 1 ? "" : "s"} skipped (entry deleted between queue and write)`);
|
|
93
135
|
}
|
|
136
|
+
if (embedFailedCount > 0) {
|
|
137
|
+
const detail = skips
|
|
138
|
+
.slice(0, 20)
|
|
139
|
+
.map((skip) => ` - ${allEntries[skip.index]?.itemRef ?? skip.index} (${skip.reason}): ${skip.message}`)
|
|
140
|
+
.join("\n");
|
|
141
|
+
const more = skips.length > 20 ? `\n ...and ${skips.length - 20} more` : "";
|
|
142
|
+
warn(`[embed] ${embedFailedCount} embedding${embedFailedCount === 1 ? "" : "s"} could not be generated and ${embedFailedCount === 1 ? "was" : "were"} skipped:\n${detail}${more}`);
|
|
143
|
+
}
|
|
94
144
|
const vecGenerationComplete = targetEntryIds === undefined ? isVecFastPathComplete(db) : vecFastPathWasReady;
|
|
95
145
|
setVecFastPathReady(db, vecFailedCount === 0 && vecUnavailableCount === 0 && vecGenerationComplete);
|
|
96
146
|
if (vecFailedCount > 0) {
|
|
@@ -103,6 +153,19 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
103
153
|
message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"}.`,
|
|
104
154
|
});
|
|
105
155
|
setMeta(db, "embeddingFingerprint", currentFingerprint);
|
|
156
|
+
// Only a total failure (nothing at all embedded, despite having entries
|
|
157
|
+
// to embed) turns into a phase failure. Any partial success — the vast
|
|
158
|
+
// majority of a large bundle embedding fine around a handful of skips —
|
|
159
|
+
// must not discard what DID get stored (#874).
|
|
160
|
+
if (storedCount === 0 && embedFailedCount > 0) {
|
|
161
|
+
const firstMessage = skips[0]?.message ?? "All embeddings failed.";
|
|
162
|
+
// #873 removed the persisted semantic verdict, so there is no failure
|
|
163
|
+
// class to record — just report what happened on this run.
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
message: `All ${embedFailedCount} embedding batch(es) failed: ${firstMessage}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
106
169
|
return { success: true, vecInsertFailures: vecFailedCount };
|
|
107
170
|
}
|
|
108
171
|
finally {
|
|
@@ -116,40 +179,21 @@ export async function generateEmbeddingsForDb(db, config, onProgress, signal, en
|
|
|
116
179
|
onProgress({ phase: "embeddings", message: `Embedding generation failed: ${message}` });
|
|
117
180
|
return {
|
|
118
181
|
success: false,
|
|
119
|
-
reason: classifySemanticFailure(message),
|
|
120
182
|
message: `Semantic search verification failed: ${message}`,
|
|
121
183
|
};
|
|
122
184
|
}
|
|
123
185
|
}
|
|
124
|
-
/**
|
|
125
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Update the `hasEmbeddings` DB fact after a targeted mutation, from the
|
|
188
|
+
* index's actual current embedding coverage — read fresh, not cached.
|
|
189
|
+
*/
|
|
190
|
+
export function publishTargetedEmbeddingMeta(db, config) {
|
|
126
191
|
if (config.semanticSearchMode === "off") {
|
|
127
|
-
clearSemanticStatus();
|
|
128
192
|
setMeta(db, "hasEmbeddings", "0");
|
|
129
193
|
return;
|
|
130
194
|
}
|
|
131
195
|
const entryCount = getEmbeddableEntryCount(db);
|
|
132
196
|
const embeddingCount = getEmbeddingCount(db);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
status = "pending";
|
|
136
|
-
else if (embeddingCount >= entryCount)
|
|
137
|
-
status = isVecAvailable(db) && isVecFastPathReady(db) ? "ready-vec" : "ready-js";
|
|
138
|
-
else
|
|
139
|
-
status = "blocked";
|
|
140
|
-
setMeta(db, "hasEmbeddings", status === "ready-js" || status === "ready-vec" ? "1" : "0");
|
|
141
|
-
writeSemanticStatus({
|
|
142
|
-
status,
|
|
143
|
-
...(status === "blocked" ? { reason: result.reason ?? "index-failed" } : {}),
|
|
144
|
-
...(status === "blocked"
|
|
145
|
-
? {
|
|
146
|
-
message: result.message ??
|
|
147
|
-
`Semantic search verification failed (${embeddingCount}/${entryCount} embeddings available).`,
|
|
148
|
-
}
|
|
149
|
-
: {}),
|
|
150
|
-
providerFingerprint: deriveSemanticProviderFingerprint(config.embedding),
|
|
151
|
-
lastCheckedAt: new Date().toISOString(),
|
|
152
|
-
entryCount,
|
|
153
|
-
embeddingCount,
|
|
154
|
-
});
|
|
197
|
+
const ready = entryCount > 0 && embeddingCount >= entryCount;
|
|
198
|
+
setMeta(db, "hasEmbeddings", ready ? "1" : "0");
|
|
155
199
|
}
|
|
@@ -26,7 +26,7 @@ import { assertIndexPathReadable, closeDatabase, openExistingDatabase, } from ".
|
|
|
26
26
|
import { getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getPositiveFeedbackCountsByIds, } from "../../storage/repositories/index-entries-repository.js";
|
|
27
27
|
import { searchFts } from "../../storage/repositories/index-fts-repository.js";
|
|
28
28
|
import { getMeta } from "../../storage/repositories/index-meta-repository.js";
|
|
29
|
-
import { searchVec } from "../../storage/repositories/index-vec-repository.js";
|
|
29
|
+
import { getEmbeddingCount, searchVec } from "../../storage/repositories/index-vec-repository.js";
|
|
30
30
|
import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
|
|
31
31
|
import { ensureIndex } from "../ensure-index.js";
|
|
32
32
|
import { collectGraphRelatedHit, loadGraphBoostContext } from "../graph/graph-boost.js";
|
|
@@ -38,7 +38,6 @@ import { typeBoostFor } from "./ranking-contributors.js";
|
|
|
38
38
|
import { attachSearchHitAttribution, copySearchHitAttribution, getSearchHitAttribution } from "./search-attribution.js";
|
|
39
39
|
import { enrichSearchHit } from "./search-hit-enrichers.js";
|
|
40
40
|
import { buildEditHint, findSourceForPath, isEditable } from "./search-source.js";
|
|
41
|
-
import { deriveSemanticProviderFingerprint, getEffectiveSemanticStatus, isSemanticRuntimeReady, readSemanticStatus, } from "./semantic-status.js";
|
|
42
41
|
/**
|
|
43
42
|
* Age past which search surfaces a "run akm index" hint. Reads serve the
|
|
44
43
|
* existing index as-is (freshness is the writers' job — `indexWrittenAssets`
|
|
@@ -126,49 +125,14 @@ export async function searchLocal(input) {
|
|
|
126
125
|
const disableScopedUtility = input.disableScopedUtility === true;
|
|
127
126
|
const rendererRegistry = input.rendererRegistry ?? defaultRendererRegistry;
|
|
128
127
|
const allSourceDirs = sources.map((s) => s.path);
|
|
129
|
-
const rawStatus = readSemanticStatus();
|
|
130
|
-
const semanticStatus = getEffectiveSemanticStatus(config, rawStatus);
|
|
131
128
|
const warnings = [];
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// configured (e.g. `akm setup --yes` ran without picking one), telling
|
|
140
|
-
// the user to "run akm setup" is misleading — they just did. Surface
|
|
141
|
-
// the actual remediation: configure an embedding endpoint OR switch
|
|
142
|
-
// semanticSearchMode to `off` to silence the warning.
|
|
143
|
-
warnings.push("Semantic search is enabled (semanticSearchMode='auto') but no embedding provider is configured. " +
|
|
144
|
-
'Either: (a) `akm config set embedding \'{"endpoint":"...","model":"..."}\'`, or ' +
|
|
145
|
-
"(b) `akm config set semanticSearchMode off` to use keyword-only search.");
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
warnings.push("Semantic search is pending verification. Run 'akm index --full' to build the semantic index now, or wait for the next background index pass.");
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (config.semanticSearchMode === "auto" && semanticStatus === "blocked") {
|
|
152
|
-
if (!hasConfiguredEmbeddingProvider(config)) {
|
|
153
|
-
// F7/A2: same predicate as the `pending` branch above (#480) — a
|
|
154
|
-
// `blocked` status can outlive the provider config that produced it
|
|
155
|
-
// (e.g. the embedding config was later unset). This is not a fault;
|
|
156
|
-
// there is simply nothing configured to use.
|
|
157
|
-
warnings.push("Semantic search is enabled (semanticSearchMode='auto') but no embedding provider is configured. " +
|
|
158
|
-
'Either: (a) `akm config set embedding \'{"endpoint":"...","model":"..."}\'`, or ' +
|
|
159
|
-
"(b) `akm config set semanticSearchMode off` to use keyword-only search.");
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
// F7/A2: surface the ACTUAL diagnostic instead of one fixed generic
|
|
163
|
-
// string. `rawStatus.reason`/`rawStatus.message` record why the
|
|
164
|
-
// semantic backend failed (auth, network, a stuck local-model
|
|
165
|
-
// download, …) — read at `readSemanticStatus()` above and, before
|
|
166
|
-
// this fix, discarded here in favor of a message that never varied.
|
|
167
|
-
const detail = rawStatus?.message ?? (rawStatus?.reason ? `reason: ${rawStatus.reason}` : undefined);
|
|
168
|
-
warnings.push(`Semantic search is blocked${detail ? ` (${detail})` : ""}. Using keyword search until the semantic ` +
|
|
169
|
-
"backend is healthy again. Run 'akm index --full' to retry, or " +
|
|
170
|
-
"`akm config set semanticSearchMode off` to silence this warning.");
|
|
171
|
-
}
|
|
129
|
+
// Semantic search is attempted fresh on every query (see `tryVecScores`);
|
|
130
|
+
// there is no cached readiness verdict to consult here. The only thing
|
|
131
|
+
// worth flagging ahead of the attempt is a config that can never succeed.
|
|
132
|
+
if (config.semanticSearchMode === "auto" && !hasConfiguredEmbeddingProvider(config)) {
|
|
133
|
+
warnings.push("Semantic search is enabled (semanticSearchMode='auto') but no embedding provider is configured. " +
|
|
134
|
+
'Either: (a) `akm config set embedding \'{"endpoint":"...","model":"..."}\'`, or ' +
|
|
135
|
+
"(b) `akm config set semanticSearchMode off` to use keyword-only search.");
|
|
172
136
|
}
|
|
173
137
|
// Bootstrap-only: builds the index inline when it cannot serve this stash.
|
|
174
138
|
// Content freshness is the writers' job (indexWrittenAssets + full runs);
|
|
@@ -662,11 +626,14 @@ function matchBeliefFilter(beliefState, filter) {
|
|
|
662
626
|
}
|
|
663
627
|
// ── Vector scorer ───────────────────────────────────────────────────────────
|
|
664
628
|
async function tryVecScores(db, query, k, config) {
|
|
665
|
-
|
|
666
|
-
if (!isSemanticRuntimeReady(semanticStatus))
|
|
629
|
+
if (config.semanticSearchMode === "off")
|
|
667
630
|
return { scores: null };
|
|
668
|
-
|
|
669
|
-
|
|
631
|
+
// A real-time completeness fact, not a cached verdict: skip the network
|
|
632
|
+
// round trip only when the index has never embedded anything. A PARTIAL
|
|
633
|
+
// failure (some entries embedded, one write degraded) still attempts —
|
|
634
|
+
// and if the endpoint is genuinely down, the failure surfaces as a live
|
|
635
|
+
// `semanticWarning` below instead of silently skipping with no signal.
|
|
636
|
+
if (getEmbeddingCount(db) === 0)
|
|
670
637
|
return { scores: null };
|
|
671
638
|
try {
|
|
672
639
|
const { embed } = await import("../../llm/embedder.js");
|
|
@@ -490,31 +490,6 @@ export const salienceRankingContributor = {
|
|
|
490
490
|
// comment. Do not add it back here without a config gate + a measured
|
|
491
491
|
// curate-golden-bench delta justifying it.
|
|
492
492
|
export const defaultUtilityRankingContributors = [utilityRankingContributor];
|
|
493
|
-
/**
|
|
494
|
-
* EVAL/DEBUG ONLY — remove named ranking contributors from a list.
|
|
495
|
-
*
|
|
496
|
-
* Driven by the `AKM_ABLATE_CONTRIBUTORS` env var (comma-separated contributor
|
|
497
|
-
* `name`s). A no-op — returns the input list unchanged (same reference) — when
|
|
498
|
-
* the env value is unset/empty, so production ranking is never affected unless
|
|
499
|
-
* the operator opts in. Its sole purpose is per-contributor ablation for the
|
|
500
|
-
* curate ablation harness (`scripts/akm-eval/`): run the same fixture with and without a contributor
|
|
501
|
-
* and diff the ranked results to measure whether that contributor is load-bearing.
|
|
502
|
-
*
|
|
503
|
-
* NOTE (from the 2026-06 ablation analysis): a contributor's ablation delta is only observable
|
|
504
|
-
* in the UNSATURATED score regime — once entries saturate at the `displayScore`
|
|
505
|
-
* ceiling their contributor deltas are absorbed and ablation reads Δ=0.
|
|
506
|
-
*/
|
|
507
|
-
export function applyContributorAblation(contributors, ablateEnv) {
|
|
508
|
-
if (!ablateEnv)
|
|
509
|
-
return contributors;
|
|
510
|
-
const ablated = new Set(ablateEnv
|
|
511
|
-
.split(",")
|
|
512
|
-
.map((s) => s.trim())
|
|
513
|
-
.filter(Boolean));
|
|
514
|
-
if (ablated.size === 0)
|
|
515
|
-
return contributors;
|
|
516
|
-
return contributors.filter((c) => !ablated.has(c.name));
|
|
517
|
-
}
|
|
518
493
|
export function applyScoreContributors(item, ctx, contributors = defaultRankingContributors) {
|
|
519
494
|
let boostSum = 0;
|
|
520
495
|
for (const contributor of contributors) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { getUtilityScoresByIds } from "../../storage/repositories/index-utility-repository.js";
|
|
5
5
|
import { buildLexicalQueryPlan } from "./fts-query.js";
|
|
6
|
-
import { applyBeliefStateScoreCeiling,
|
|
6
|
+
import { applyBeliefStateScoreCeiling, applyScoreContributors, applyUtilityContributors, defaultRankingContributors, defaultUtilityRankingContributors, } from "./ranking-contributors.js";
|
|
7
7
|
export function normalizeFtsScores(results) {
|
|
8
8
|
const ftsScoreMap = new Map();
|
|
9
9
|
if (results.length === 0)
|
|
@@ -75,18 +75,8 @@ export function applyRankingRules(options) {
|
|
|
75
75
|
graphContext: options.graphContext,
|
|
76
76
|
projectContext: options.projectContext,
|
|
77
77
|
};
|
|
78
|
-
// Eval/debug only: lets the ablation harness drop named contributors to
|
|
79
|
-
// measure their effect. Resolved once per query; a no-op (full lists) when
|
|
80
|
-
// unset — see applyContributorAblation. C9: DI via
|
|
81
|
-
// `options.ablateContributors` is now the primary path (see the field's
|
|
82
|
-
// doc comment on RankEntriesOptions); AKM_ABLATE_CONTRIBUTORS is a
|
|
83
|
-
// temporary fallback for the one caller that cannot yet thread the option
|
|
84
|
-
// through (see that doc comment for why).
|
|
85
|
-
const ablateEnv = options.ablateContributors ?? process.env.AKM_ABLATE_CONTRIBUTORS;
|
|
86
|
-
const activeScoreContributors = applyContributorAblation(defaultRankingContributors, ablateEnv);
|
|
87
|
-
const activeUtilityContributors = applyContributorAblation(defaultUtilityRankingContributors, ablateEnv);
|
|
88
78
|
for (const item of options.items) {
|
|
89
|
-
applyScoreContributors(item, rankingContext,
|
|
79
|
+
applyScoreContributors(item, rankingContext, defaultRankingContributors);
|
|
90
80
|
}
|
|
91
81
|
const { global: utilScoresMap, scoped: scopedUtilScoresMap } = getUtilityScoresByIds(options.db, options.items.map((item) => item.id), options.scopeKey);
|
|
92
82
|
// R2 / #692 — salience-ranking is not in defaultUtilityRankingContributors
|
|
@@ -104,7 +94,7 @@ export function applyRankingRules(options) {
|
|
|
104
94
|
salienceRankScores,
|
|
105
95
|
};
|
|
106
96
|
for (const item of options.items) {
|
|
107
|
-
applyUtilityContributors(item, utilityContext,
|
|
97
|
+
applyUtilityContributors(item, utilityContext, defaultUtilityRankingContributors);
|
|
108
98
|
applyRelaxedLexicalScoreCeiling(item, queryTokens);
|
|
109
99
|
// SPEC-5: demoting belief states (superseded/contradicted/archived/
|
|
110
100
|
// deprecated) cap the FINAL score. The additive belief penalty inside the
|
|
@@ -1,35 +1,10 @@
|
|
|
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
|
-
* Shared agent-command-builder types and helpers (#563).
|
|
6
|
-
*
|
|
7
|
-
* Extracted from `agent/builders.ts` so per-harness builders (e.g.
|
|
8
|
-
* `harnesses/claude/agent-builder.ts`) can depend on the common
|
|
9
|
-
* types/validation WITHOUT creating an import cycle back through
|
|
10
|
-
* `builders.ts` (which imports the per-harness builders into its registry).
|
|
11
|
-
* This module is a dependency-graph LEAF for the builder subsystem.
|
|
12
|
-
*
|
|
13
|
-
* Behaviour-preserving: the type shapes and helper bodies are unchanged from
|
|
14
|
-
* their previous home in `builders.ts`.
|
|
15
|
-
*/
|
|
16
|
-
import { UsageError } from "../../core/errors.js";
|
|
17
4
|
/** Models reaching a harness have already crossed the single model-map boundary. */
|
|
18
5
|
export function resolveDispatchModel(request, _profile, _platform) {
|
|
19
6
|
return request.model;
|
|
20
7
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Guard against values that start with `--`, which would be mis-interpreted as
|
|
23
|
-
* CLI flags by the spawned process when used as flag values (model, systemPrompt).
|
|
24
|
-
* Bun.spawn uses array argv so there is no shell injection, but a `--`-prefixed
|
|
25
|
-
* value passed as the argument to `--model` or `--system-prompt` can still
|
|
26
|
-
* confuse the CLI parser of the target process.
|
|
27
|
-
*/
|
|
28
|
-
export function assertNotFlag(value, field) {
|
|
29
|
-
if (value?.trimStart().startsWith("--")) {
|
|
30
|
-
throw new UsageError(`${field} must not start with "--": ${JSON.stringify(value.slice(0, 60))}`, "INVALID_FLAG_VALUE");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
8
|
/**
|
|
34
9
|
* Normalize a toolPolicy value to a comma-separated string suitable for a
|
|
35
10
|
* CLI flag. Structured policy objects are JSON-serialized.
|
|
@@ -5,7 +5,7 @@ import { randomBytes } from "node:crypto";
|
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import {
|
|
8
|
+
import { writeFileAtomic } from "../../core/common.js";
|
|
9
9
|
import { ENGINE_NAME_PATTERN_SOURCE } from "../../core/config/engine-semantics.js";
|
|
10
10
|
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
11
11
|
import { getConfigDir } from "../../core/paths.js";
|
|
@@ -21,9 +21,6 @@ import { cloneExecutionJsonObject } from "../../execution/json.js";
|
|
|
21
21
|
* provider mappings.
|
|
22
22
|
*/
|
|
23
23
|
export const MODEL_MAP_VERSION = 1;
|
|
24
|
-
export const MODEL_MAP_MAX_BYTES = MAX_CONFIG_FILE_BYTES;
|
|
25
|
-
export const MODEL_MAP_MAX_DEPTH = 64;
|
|
26
|
-
export const MODEL_MAP_MAX_NODES = 100_000;
|
|
27
24
|
let standaloneInstalledModelMapText;
|
|
28
25
|
const ENGINE_KEY_PATTERN = new RegExp(ENGINE_NAME_PATTERN_SOURCE);
|
|
29
26
|
const ALIAS_KEY_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
@@ -92,62 +89,8 @@ function parseProfileLayer(value, source, jsonPath) {
|
|
|
92
89
|
}
|
|
93
90
|
return Object.freeze(out);
|
|
94
91
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Bound nesting before JSON.parse or any recursive durable-JSON clone runs.
|
|
97
|
-
* Brackets inside strings are ignored, including escaped quote sequences.
|
|
98
|
-
*/
|
|
99
|
-
function assertJsonDepthBudget(text, source) {
|
|
100
|
-
let depth = 0;
|
|
101
|
-
let inString = false;
|
|
102
|
-
let escaped = false;
|
|
103
|
-
for (const character of text) {
|
|
104
|
-
if (inString) {
|
|
105
|
-
if (escaped)
|
|
106
|
-
escaped = false;
|
|
107
|
-
else if (character === "\\")
|
|
108
|
-
escaped = true;
|
|
109
|
-
else if (character === '"')
|
|
110
|
-
inString = false;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (character === '"') {
|
|
114
|
-
inString = true;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (character === "{" || character === "[") {
|
|
118
|
-
depth += 1;
|
|
119
|
-
if (depth > MODEL_MAP_MAX_DEPTH) {
|
|
120
|
-
invalid(source, "$", `JSON nesting exceeds the ${MODEL_MAP_MAX_DEPTH}-level limit`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
else if (character === "}" || character === "]") {
|
|
124
|
-
depth -= 1;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/** Iterative node budget, deliberately completed before recursive inference cloning. */
|
|
129
|
-
function assertJsonNodeBudget(value, source) {
|
|
130
|
-
const pending = [value];
|
|
131
|
-
let count = 0;
|
|
132
|
-
while (pending.length > 0) {
|
|
133
|
-
const current = pending.pop();
|
|
134
|
-
count += 1;
|
|
135
|
-
if (count > MODEL_MAP_MAX_NODES) {
|
|
136
|
-
invalid(source, "$", `JSON value exceeds the ${MODEL_MAP_MAX_NODES}-node limit`);
|
|
137
|
-
}
|
|
138
|
-
if (current !== null && typeof current === "object") {
|
|
139
|
-
const children = Array.isArray(current) ? current : Object.values(current);
|
|
140
|
-
for (const child of children)
|
|
141
|
-
pending.push(child);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
92
|
/** Parse one installed or user layer. Partial structured profiles are valid at this stage. */
|
|
146
93
|
export function parseModelMapLayer(text, source) {
|
|
147
|
-
if (Buffer.byteLength(text, "utf8") > MODEL_MAP_MAX_BYTES) {
|
|
148
|
-
invalid(source, "$", `document exceeds the ${MODEL_MAP_MAX_BYTES}-byte limit`);
|
|
149
|
-
}
|
|
150
|
-
assertJsonDepthBudget(text, source);
|
|
151
94
|
let parsed;
|
|
152
95
|
try {
|
|
153
96
|
parsed = JSON.parse(text);
|
|
@@ -155,7 +98,6 @@ export function parseModelMapLayer(text, source) {
|
|
|
155
98
|
catch {
|
|
156
99
|
invalid(source, "$", "invalid JSON syntax");
|
|
157
100
|
}
|
|
158
|
-
assertJsonNodeBudget(parsed, source);
|
|
159
101
|
const root = requireRecord(parsed, source, "$");
|
|
160
102
|
assertOnlyKeys(root, ["version", "aliases"], source, "$");
|
|
161
103
|
if (root.version !== MODEL_MAP_VERSION) {
|
|
@@ -314,7 +256,7 @@ function readModelMapFile(filePath, label, optional) {
|
|
|
314
256
|
if (!sameFileIdentity(linkStat, openedStat)) {
|
|
315
257
|
throw new ConfigError(`${label} changed while it was being opened: ${filePath}.`, "INVALID_CONFIG_FILE", "Retry after ensuring no other process is replacing models.json.");
|
|
316
258
|
}
|
|
317
|
-
const text =
|
|
259
|
+
const text = fs.readFileSync(fd, "utf8");
|
|
318
260
|
fs.closeSync(fd);
|
|
319
261
|
fd = undefined;
|
|
320
262
|
return text;
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
* name without any further wiring. The registry entry also declares
|
|
68
68
|
* `structuredOutput: "none"` alongside it (`./index.ts`).
|
|
69
69
|
*/
|
|
70
|
-
import {
|
|
70
|
+
import { resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
71
71
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
72
72
|
/** Canonical harness/platform id used for model-alias resolution. */
|
|
73
73
|
export const AIDER_PLATFORM = "aider";
|
|
@@ -103,8 +103,6 @@ export const aiderBuilder = {
|
|
|
103
103
|
outputSchema: true,
|
|
104
104
|
}),
|
|
105
105
|
build(profile, req) {
|
|
106
|
-
assertNotFlag(req.systemPrompt, "systemPrompt");
|
|
107
|
-
assertNotFlag(req.model, "model");
|
|
108
106
|
const args = [...profile.args];
|
|
109
107
|
if (req.model) {
|
|
110
108
|
const resolved = resolveDispatchModel(req, profile, AIDER_PLATFORM);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* - **systemPrompt** — `q chat` has no system-prompt flag (persona/context
|
|
33
33
|
* comes from Q's own agent config files), so the system prompt is folded
|
|
34
34
|
* into the positional payload ahead of the task prompt, separated by a
|
|
35
|
-
* blank line.
|
|
35
|
+
* blank line.
|
|
36
36
|
* - **schema** — the matrix places Q in the NO-structured-output tier
|
|
37
37
|
* ("via prompt+validate": *(none documented)* — there is no `--json` or
|
|
38
38
|
* `--output-format` to ask for). The JSON Schema is therefore passed
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
* pattern `local-runner`, structuredOutput `none` — is declared alongside it
|
|
63
63
|
* (`./index.ts`).
|
|
64
64
|
*/
|
|
65
|
-
import {
|
|
65
|
+
import { resolveDispatchModel } from "../../agent/builder-shared.js";
|
|
66
66
|
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
|
|
67
67
|
/** Canonical harness/platform id used for model-alias resolution. */
|
|
68
68
|
export const AMAZONQ_PLATFORM = "amazonq";
|
|
@@ -116,8 +116,6 @@ export const amazonqBuilder = {
|
|
|
116
116
|
outputSchema: true,
|
|
117
117
|
}),
|
|
118
118
|
build(profile, req) {
|
|
119
|
-
assertNotFlag(req.systemPrompt, "systemPrompt");
|
|
120
|
-
assertNotFlag(req.model, "model");
|
|
121
119
|
// Built-in q profiles would ship `args: []`; headless dispatch is the
|
|
122
120
|
// `chat` subcommand. Don't double it when a user profile already pins it.
|
|
123
121
|
const extra = profile.args[0] === "chat" ? profile.args.slice(1) : [...profile.args];
|
|
@@ -129,9 +127,6 @@ export const amazonqBuilder = {
|
|
|
129
127
|
// flags: dropping a restriction must never widen to --trust-all-tools.
|
|
130
128
|
const entries = toolPolicyEntries(req.tools);
|
|
131
129
|
if (entries !== undefined) {
|
|
132
|
-
for (const tool of entries) {
|
|
133
|
-
assertNotFlag(tool, "tools entry");
|
|
134
|
-
}
|
|
135
130
|
// Q's documented allowlist form is equals-joined and comma-separated
|
|
136
131
|
// (`--trust-tools=fs_read,fs_write`); an empty list trusts no tools.
|
|
137
132
|
args.push(`--trust-tools=${entries.join(",")}`);
|