akm-cli 0.9.14 → 0.9.15-beta.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 +559 -0
- package/STABILITY.md +6 -3
- package/dist/akm +54 -1
- package/dist/akm-migrate +34 -1
- package/dist/assets/prompts/reflect-feedback-framing.md +1 -0
- package/dist/assets/prompts/reflect-llm-framed-contract.md +2 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +2 -0
- package/dist/assets/tasks/core/improve.yml +1 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +1 -1
- package/dist/cli/retired-commands.js +0 -1
- package/dist/cli/shared.js +9 -0
- package/dist/cli/unknown-flags.js +1 -0
- package/dist/cli.js +40 -3
- package/dist/commands/config-cli.js +85 -3
- package/dist/commands/env/env-cli.js +1 -42
- package/dist/commands/env/env.js +1 -1
- package/dist/commands/env/secret-cli.js +1 -2
- package/dist/commands/health/checks.js +357 -63
- package/dist/commands/health/engine-usage.js +45 -0
- package/dist/commands/health/improve-metrics.js +18 -0
- package/dist/commands/health/llm-usage.js +41 -1
- package/dist/commands/health/plugin-staleness.js +7 -3
- package/dist/commands/health/version-drift.js +93 -0
- package/dist/commands/health/windows.js +3 -1
- package/dist/commands/health.js +44 -9
- package/dist/commands/improve/consolidate/chunking.js +4 -2
- package/dist/commands/improve/improve-cli.js +99 -5
- package/dist/commands/improve/improve-report.js +154 -0
- package/dist/commands/improve/improve-result-file.js +45 -33
- package/dist/commands/improve/improve-strategies.js +133 -3
- package/dist/commands/improve/improve-usage-report.js +182 -0
- package/dist/commands/improve/improve.js +40 -3
- package/dist/commands/improve/locks.js +28 -78
- package/dist/commands/improve/planner.js +1 -0
- package/dist/commands/improve/preparation.js +9 -1
- package/dist/commands/improve/reflect.js +44 -4
- package/dist/commands/models-cli.js +50 -1
- package/dist/commands/proposal/repository.js +8 -3
- package/dist/commands/proposal/validators/proposal-quality-validators.js +41 -6
- package/dist/commands/proposal/validators/proposal-validators.js +24 -0
- package/dist/commands/read/search-cli.js +38 -2
- package/dist/commands/read/show.js +103 -4
- package/dist/commands/sources/info.js +5 -1
- package/dist/commands/sources/installed-stashes.js +58 -16
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/stash-cli.js +48 -0
- package/dist/commands/tasks/tasks-cli.js +49 -2
- package/dist/commands/workflow-cli.js +86 -12
- package/dist/core/asset/markdown-fragments.js +35 -0
- package/dist/core/config/config-schema.js +14 -0
- package/dist/core/config/config.js +302 -24
- package/dist/core/config/schema/embedding.js +41 -0
- package/dist/core/env-secret-ref.js +58 -5
- package/dist/core/errors.js +30 -0
- package/dist/core/file-lock.js +49 -15
- package/dist/core/improve-result.js +51 -0
- package/dist/core/loopback.js +17 -0
- package/dist/core/parent-watchdog.js +64 -0
- package/dist/core/paths.js +11 -0
- package/dist/core/run-lock.js +107 -0
- package/dist/core/sensitive-marker-path.js +19 -0
- package/dist/core/state-db.js +74 -14
- package/dist/indexer/index-rebuild-lock.js +73 -0
- package/dist/indexer/index-writer-lock.js +40 -1
- package/dist/indexer/index-written-assets.js +29 -1
- package/dist/indexer/indexer.js +93 -29
- package/dist/indexer/materialize-embeddings.js +564 -48
- package/dist/indexer/search/db-search.js +49 -2
- package/dist/indexer/search/search-source.js +23 -1
- package/dist/integrations/agent/engine-resolution.js +96 -6
- package/dist/integrations/agent/execution-definitions.js +6 -15
- package/dist/integrations/agent/execution-lowering.js +6 -1
- package/dist/integrations/agent/execution-preparation.js +1 -1
- package/dist/integrations/agent/model-map.js +123 -20
- package/dist/integrations/agent/prompts.js +40 -8
- package/dist/integrations/agent/runner-dispatch.js +9 -3
- package/dist/integrations/agent/runner.js +2 -0
- package/dist/llm/client.js +8 -3
- package/dist/llm/embedder.js +20 -8
- package/dist/llm/embedders/local.js +10 -2
- package/dist/llm/embedders/remote.js +497 -32
- package/dist/output/shapes/helpers.js +38 -2
- package/dist/output/shapes/models-list.js +16 -0
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/shapes.js +4 -0
- package/dist/output/text/command-format.js +29 -0
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/improve-report.js +27 -0
- package/dist/{commands/env/marker-path.js → output/text/models.js} +4 -3
- package/dist/output/text/show-format.js +4 -0
- package/dist/output/text.js +4 -0
- package/dist/scripts/akm-migrate-node.js +25146 -21759
- package/dist/scripts/akm-migrate.js +24271 -20885
- package/dist/storage/repositories/embedding-salvage-repository.js +184 -0
- package/dist/storage/repositories/improve-runs-repository.js +34 -0
- package/dist/storage/repositories/index-fts-repository.js +49 -6
- package/dist/storage/repositories/index-schema.js +16 -0
- package/dist/storage/repositories/index-vec-repository.js +30 -0
- package/dist/storage/repositories/workflow-runs-repository.js +55 -18
- package/dist/tasks/backends/cron.js +14 -7
- package/dist/tasks/run/run-native-task.js +23 -1
- package/dist/tasks/run/run-workflow-task.js +16 -0
- package/dist/workflows/exec/child-workflow.js +2 -2
- package/dist/workflows/exec/dispatch-redaction.js +21 -9
- package/dist/workflows/exec/run-workflow.js +6 -5
- package/dist/workflows/runtime/runs.js +33 -5
- package/docs/migration/release-notes/0.9.15.md +133 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/reference/cli.md +271 -30
- package/docs/reference/configuration.md +234 -21
- package/docs/reference/data-and-telemetry.md +8 -0
- package/docs/reference/tasks.md +16 -1
- package/docs/reference/workflow-schema.md +5 -1
- package/package.json +1 -1
- package/schemas/akm-config.json +47 -0
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
* `driveRun` itself is never exported (B-N5's "no second executor" holds).
|
|
54
54
|
*/
|
|
55
55
|
import { randomUUID } from "node:crypto";
|
|
56
|
-
import {
|
|
56
|
+
import { TransientError } from "../../core/errors.js";
|
|
57
57
|
import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
58
58
|
import { validateWorkflowParams } from "../ir/params.js";
|
|
59
59
|
import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
|
|
@@ -76,7 +76,7 @@ function errorMessage(err) {
|
|
|
76
76
|
}
|
|
77
77
|
/** `acquireRunLease`'s exact refusal shape (run-workflow.ts) — matched by text, since this module cannot import that private helper. */
|
|
78
78
|
function isLeaseBusyError(err) {
|
|
79
|
-
return err instanceof
|
|
79
|
+
return err instanceof TransientError && err.message.includes("is already being driven by engine");
|
|
80
80
|
}
|
|
81
81
|
/** §3.4's exact `child_workflow_failed` message. */
|
|
82
82
|
function childWorkflowFailedMessage(input) {
|
|
@@ -15,23 +15,24 @@
|
|
|
15
15
|
* @module workflows/exec/dispatch-redaction
|
|
16
16
|
*/
|
|
17
17
|
import { collectSensitiveValues, isEnvPassthroughValueSafeToExpose, redactSensitiveText, redactSensitiveValue, } from "../../core/redaction.js";
|
|
18
|
-
import { lookupApiKeyFileValue } from "../../integrations/agent/engine-resolution.js";
|
|
18
|
+
import { lookupApiKeyFileValue, lookupApiKeySecretRefValue } from "../../integrations/agent/engine-resolution.js";
|
|
19
19
|
/**
|
|
20
20
|
* Every exact value that must never survive into the journal from ONE frozen
|
|
21
21
|
* dispatch: the resolved `env` bindings injected into the child, the selected
|
|
22
|
-
* engine's (and its SDK fallback's) credential — an env value,
|
|
23
|
-
*
|
|
24
|
-
* policy does not consider safe to expose.
|
|
22
|
+
* engine's (and its SDK fallback's) credential — an env value, a file-backed
|
|
23
|
+
* one (#905), or a secret-store-backed one (#953) — and any `envPassthrough`
|
|
24
|
+
* value the redaction policy does not consider safe to expose.
|
|
25
25
|
*
|
|
26
26
|
* Shared by the unit path and the gate-judge path. There is deliberately ONE
|
|
27
27
|
* collector: a second, parallel implementation is exactly how a dispatch path
|
|
28
28
|
* silently loses the scrub.
|
|
29
29
|
*
|
|
30
|
-
* The credential values are read from `process.env` (or disk
|
|
31
|
-
* backed one) AT CALL TIME, so a caller must
|
|
32
|
-
* dispatch whose outcome it scrubs. A snapshot
|
|
33
|
-
* merely *planned* can predate a credential the
|
|
34
|
-
* leaving the exact value it must remove out of
|
|
30
|
+
* The credential values are read from `process.env` (or disk / the secret
|
|
31
|
+
* store, for a file- or store-backed one) AT CALL TIME, so a caller must
|
|
32
|
+
* collect no earlier than the dispatch whose outcome it scrubs. A snapshot
|
|
33
|
+
* taken when the dispatch was merely *planned* can predate a credential the
|
|
34
|
+
* dispatch then resolves live, leaving the exact value it must remove out of
|
|
35
|
+
* the set.
|
|
35
36
|
*/
|
|
36
37
|
export function collectWorkflowDispatchSensitiveValues(dispatch, env) {
|
|
37
38
|
const values = new Set([...Object.values(env ?? {}), ...(dispatch.sensitiveValues ?? [])]);
|
|
@@ -51,6 +52,12 @@ export function collectWorkflowDispatchSensitiveValues(dispatch, env) {
|
|
|
51
52
|
if (value)
|
|
52
53
|
values.add(value);
|
|
53
54
|
}
|
|
55
|
+
// #953: secret-store-backed credential — same best-effort rationale.
|
|
56
|
+
if (runner.apiKeySecretRef) {
|
|
57
|
+
const value = lookupApiKeySecretRefValue(runner.apiKeySecretRef);
|
|
58
|
+
if (value)
|
|
59
|
+
values.add(value);
|
|
60
|
+
}
|
|
54
61
|
return;
|
|
55
62
|
}
|
|
56
63
|
for (const name of runner.profile.envPassthrough ?? []) {
|
|
@@ -69,6 +76,11 @@ export function collectWorkflowDispatchSensitiveValues(dispatch, env) {
|
|
|
69
76
|
if (value)
|
|
70
77
|
values.add(value);
|
|
71
78
|
}
|
|
79
|
+
if (runner.fallbackApiKeySecretRef) {
|
|
80
|
+
const value = lookupApiKeySecretRefValue(runner.fallbackApiKeySecretRef);
|
|
81
|
+
if (value)
|
|
82
|
+
values.add(value);
|
|
83
|
+
}
|
|
72
84
|
}
|
|
73
85
|
};
|
|
74
86
|
addCredential(dispatch.runner);
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* full design history behind each of these invariants.
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from "node:crypto";
|
|
18
|
-
import { UsageError } from "../../core/errors.js";
|
|
18
|
+
import { TransientError, UsageError } from "../../core/errors.js";
|
|
19
19
|
import { withMaintenanceStartBarrierAsync } from "../../core/maintenance-barrier.js";
|
|
20
20
|
import { disposeDispatchResources } from "../../integrations/agent/runner-dispatch.js";
|
|
21
21
|
import { withWorkflowRunsConnection, withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
|
|
@@ -183,16 +183,17 @@ function leaseExpiry() {
|
|
|
183
183
|
return new Date(Date.now() + RUN_LEASE_TTL_MS).toISOString();
|
|
184
184
|
}
|
|
185
185
|
/**
|
|
186
|
-
* Atomically claim the run lease or refuse with a
|
|
187
|
-
* current holder + expiry
|
|
188
|
-
* arbiter — two racing
|
|
186
|
+
* Atomically claim the run lease or refuse with a TransientError naming the
|
|
187
|
+
* current holder + expiry (#948 addendum — moved off UsageError, exit 75).
|
|
188
|
+
* The single-UPDATE claim in the repository is the arbiter — two racing
|
|
189
|
+
* invocations cannot both win.
|
|
189
190
|
*/
|
|
190
191
|
async function acquireRunLease(runId, holder) {
|
|
191
192
|
await withMaintenanceStartBarrierAsync(() => withWorkflowRunsRepo((repo) => {
|
|
192
193
|
if (repo.acquireEngineLease(runId, holder, leaseExpiry(), new Date().toISOString()))
|
|
193
194
|
return;
|
|
194
195
|
const row = repo.getRunById(runId);
|
|
195
|
-
throw new
|
|
196
|
+
throw new TransientError(`Workflow run ${runId} is already being driven by engine ${row?.engine_lease_holder ?? "(unknown)"} ` +
|
|
196
197
|
`(run lease expires ${row?.engine_lease_until ?? "(unknown)"}). A second \`akm workflow run\` would race it — ` +
|
|
197
198
|
`wait for that invocation to finish or for the lease to expire.`, "RUN_LEASE_HELD");
|
|
198
199
|
}));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
6
6
|
import { loadConfig } from "../../core/config/config.js";
|
|
7
|
-
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
7
|
+
import { ConfigError, NotFoundError, TransientError, UsageError } from "../../core/errors.js";
|
|
8
8
|
import { appendEvent } from "../../core/events.js";
|
|
9
9
|
import { warn } from "../../core/warn.js";
|
|
10
10
|
import { insertEventOnce } from "../../storage/repositories/events-repository.js";
|
|
@@ -127,6 +127,25 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
127
127
|
// per the workflow-agent check-in ADR) so a stalled run can be
|
|
128
128
|
// re-targeted with a `continue` directive. The agent harness + session id
|
|
129
129
|
// are already resolved above (agentHarness/agentSessionId, from #501).
|
|
130
|
+
// #942: an active run of this ref may already exist in a DIFFERENT
|
|
131
|
+
// scope — the incident this issue reports (a scheduled task's cwd and a
|
|
132
|
+
// human's shell hash to different scope keys, so each believed it held
|
|
133
|
+
// no active run and each started one). The scope-local uniqueness guard
|
|
134
|
+
// stays scope-local (a documented, deliberate per-project partition —
|
|
135
|
+
// see storage-locations.md); this only warns, once, so the operator can
|
|
136
|
+
// resume or abandon the other run instead of silently accumulating a
|
|
137
|
+
// second one. `findActiveRunOutsideScope` excludes the caller's own
|
|
138
|
+
// scope IN SQL (never merely post-filtered) so the caller's own active
|
|
139
|
+
// run can never sort first under `LIMIT 1` and mask a genuinely different
|
|
140
|
+
// scope's run — the failure mode a same-scope-inclusive query plus a
|
|
141
|
+
// post-filter has with `--new`/`--force`.
|
|
142
|
+
const crossScopeActive = repo.findActiveRunOutsideScope(workflowRefs, scopeKey);
|
|
143
|
+
const crossScopeWarning = crossScopeActive
|
|
144
|
+
? `Workflow ${asset.ref} already has an active run in another scope ` +
|
|
145
|
+
`(id ${crossScopeActive.id}, started ${crossScopeActive.created_at}, scope ${crossScopeActive.scope_key ?? "unknown"}); ` +
|
|
146
|
+
`starting a separate run here. Resume it from anywhere with "akm workflow run ${crossScopeActive.id}" ` +
|
|
147
|
+
`or free it with "akm workflow abandon ${crossScopeActive.id}".`
|
|
148
|
+
: undefined;
|
|
130
149
|
repo.publishWorkflowRunV4({
|
|
131
150
|
workflowRefs,
|
|
132
151
|
...(options?.force ? { force: true } : {}),
|
|
@@ -157,6 +176,8 @@ export async function startWorkflowRun(ref, params = {}, options) {
|
|
|
157
176
|
revalidateSources: () => frozen.sourceCollector.revalidate(),
|
|
158
177
|
});
|
|
159
178
|
const result = await getWorkflowStatus(runId);
|
|
179
|
+
if (crossScopeWarning)
|
|
180
|
+
result.warnings = [...(result.warnings ?? []), crossScopeWarning];
|
|
160
181
|
// #13: params are declared non-secret (they are copied verbatim into every
|
|
161
182
|
// unit prompt and hashed into the unit identity, so they cannot be redacted
|
|
162
183
|
// without breaking replay determinism). Surface a loud, best-effort warning
|
|
@@ -193,7 +214,7 @@ export async function getWorkflowStatus(runId, opts) {
|
|
|
193
214
|
});
|
|
194
215
|
}
|
|
195
216
|
export async function listWorkflowRuns(input) {
|
|
196
|
-
const scopeKey = getCurrentWorkflowScopeKey();
|
|
217
|
+
const scopeKey = input?.allScopes === true ? null : getCurrentWorkflowScopeKey();
|
|
197
218
|
const activeOnly = input?.activeOnly === true;
|
|
198
219
|
const includeChildren = input?.includeChildren === true;
|
|
199
220
|
if (input?.workflowRef === undefined) {
|
|
@@ -205,6 +226,7 @@ export async function listWorkflowRuns(input) {
|
|
|
205
226
|
...(includeChildren ? { includeChildren: true } : {}),
|
|
206
227
|
})
|
|
207
228
|
.map(toWorkflowRunSummary),
|
|
229
|
+
scopeKey,
|
|
208
230
|
}));
|
|
209
231
|
}
|
|
210
232
|
const exactRef = input.workflowRef.trim();
|
|
@@ -227,6 +249,7 @@ export async function listWorkflowRuns(input) {
|
|
|
227
249
|
throw error;
|
|
228
250
|
return {
|
|
229
251
|
runs: exactRows.filter((row) => !activeOnly || row.status === "active").map(toWorkflowRunSummary),
|
|
252
|
+
scopeKey,
|
|
230
253
|
};
|
|
231
254
|
}
|
|
232
255
|
if (!(error instanceof NotFoundError))
|
|
@@ -241,6 +264,7 @@ export async function listWorkflowRuns(input) {
|
|
|
241
264
|
...(includeChildren ? { includeChildren: true } : {}),
|
|
242
265
|
})
|
|
243
266
|
.map(toWorkflowRunSummary),
|
|
267
|
+
scopeKey,
|
|
244
268
|
}));
|
|
245
269
|
}
|
|
246
270
|
export async function getNextWorkflowStep(specifier, params, options) {
|
|
@@ -558,7 +582,8 @@ async function resolveRunSpecifier(repo, specifier, params, parameterFlags, forc
|
|
|
558
582
|
catch (error) {
|
|
559
583
|
if (detached) {
|
|
560
584
|
if (hasParameters) {
|
|
561
|
-
throw new UsageError(`Workflow parameter flags can only be set on a new run; ${specifier} is already active
|
|
585
|
+
throw new UsageError(`Workflow parameter flags can only be set on a new run; ${specifier} is already active ` +
|
|
586
|
+
`(id ${detached.id}, scope ${scopeKey}).`, "INVALID_FLAG_VALUE", `Pass --new to start a separate run, or run "akm workflow abandon ${detached.id}" to free up ${specifier} first.`);
|
|
562
587
|
}
|
|
563
588
|
return { run: detached, autoStarted: false, resumed: true };
|
|
564
589
|
}
|
|
@@ -570,7 +595,8 @@ async function resolveRunSpecifier(repo, specifier, params, parameterFlags, forc
|
|
|
570
595
|
const active = forceNew ? undefined : repo.getActiveRunRowForScope(await workflowRunRefSet(ref, exactRef), scopeKey);
|
|
571
596
|
if (active) {
|
|
572
597
|
if (hasParameters) {
|
|
573
|
-
throw new UsageError(`Workflow parameter flags can only be set on a new run; ${ref} is already active
|
|
598
|
+
throw new UsageError(`Workflow parameter flags can only be set on a new run; ${ref} is already active ` +
|
|
599
|
+
`(id ${active.id}, scope ${scopeKey}).`, "INVALID_FLAG_VALUE", `Pass --new to start a separate run, or run "akm workflow abandon ${active.id}" to free up ${ref} first.`);
|
|
574
600
|
}
|
|
575
601
|
return { run: active, autoStarted: false, resumed: true };
|
|
576
602
|
}
|
|
@@ -808,7 +834,9 @@ function assertLeaseAllowsSpineAdvance(run, leaseHolder) {
|
|
|
808
834
|
return;
|
|
809
835
|
if (run.engine_lease_until < new Date().toISOString())
|
|
810
836
|
return; // expired ⇒ claimable, not live
|
|
811
|
-
|
|
837
|
+
// #948 addendum: moved off UsageError (exit 75, not exit 2) — a held lease
|
|
838
|
+
// is ordinary contention, not a bad command line.
|
|
839
|
+
throw new TransientError(`Workflow run ${run.id} is being driven by engine ${run.engine_lease_holder} ` +
|
|
812
840
|
`(run lease expires ${run.engine_lease_until}). The engine owns the step spine while it runs — ` +
|
|
813
841
|
`wait for it to finish or for the lease to expire before advancing steps manually.`, "RUN_LEASE_HELD");
|
|
814
842
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Migration notes for akm v0.9.15
|
|
2
|
+
|
|
3
|
+
`RUN_LEASE_HELD` (a workflow run-lease refusal) and the new
|
|
4
|
+
`STATE_DB_CONTENDED` (state.db write contention) now exit 75 (`EX_TEMPFAIL`)
|
|
5
|
+
instead of exit 2 and exit 70 respectively. Both are unrelated to a bad command
|
|
6
|
+
line — they mean "another akm process is using this database or lease right
|
|
7
|
+
now, retry shortly." If a script or scheduler wrapper special-cases exit 2 to
|
|
8
|
+
detect a held lease, switch it to exit 75, or read the JSON envelope's `code`
|
|
9
|
+
field instead.
|
|
10
|
+
|
|
11
|
+
The six shipped scheduled `improve` task templates now run with
|
|
12
|
+
`--require-engines`, which aborts (exit 78) before any index work when a
|
|
13
|
+
process's engine or credential cannot be resolved in the task's own
|
|
14
|
+
environment, instead of silently skipping that process. This only affects new
|
|
15
|
+
installs and new `akm setup` task seeding — an existing install's
|
|
16
|
+
already-materialized task files are not rewritten. To get the same protection
|
|
17
|
+
on an existing scheduled task, add `--require-engines` to its `run:` command
|
|
18
|
+
yourself, then run `akm task sync`.
|
|
19
|
+
|
|
20
|
+
`akm health --no-probe` now also skips the `cli-version` update check (a GitHub
|
|
21
|
+
release lookup), alongside the engine-reachability checks it already skipped.
|
|
22
|
+
An air-gapped or offline host's existing `--no-probe` habit now suppresses both
|
|
23
|
+
network calls with no config change needed.
|
|
24
|
+
|
|
25
|
+
Thinking-control wire forms (`chat_template_kwargs.enable_thinking` and
|
|
26
|
+
`enable_thinking`) are now sent for every provider whenever `enableThinking`
|
|
27
|
+
resolves to a value, not only when `provider: "vllm"` is set. If your engine
|
|
28
|
+
sits behind Bifrost and the gateway does not honor either form, also set
|
|
29
|
+
`reasoningEffort: "none"` on that engine. If your engine talks to a strict
|
|
30
|
+
hosted API that rejects unknown request keys, leave `enableThinking` unset on
|
|
31
|
+
that engine so neither wire form is sent.
|
|
32
|
+
|
|
33
|
+
An `embedding.model` rename no longer forces a full re-embed by itself: `akm
|
|
34
|
+
index` re-embeds a small sample first and keeps the existing vectors when they
|
|
35
|
+
still verify against the endpoint. `akm index --reembed` forces a full re-embed
|
|
36
|
+
when you don't trust that verdict. `akm index --skip-if-locked` lets a
|
|
37
|
+
scheduled or opportunistic run step aside instead of contending with a rebuild
|
|
38
|
+
already in progress; the shipped `index-refresh` scheduled task already passes
|
|
39
|
+
it.
|
|
40
|
+
|
|
41
|
+
`embedding.concurrency` (positive integer, 1-16) overrides the number of
|
|
42
|
+
embedding requests kept in flight at once, which otherwise defaults to 1 for
|
|
43
|
+
a loopback endpoint and 2 for a remote one. Set it only for an endpoint that
|
|
44
|
+
genuinely serves parallel requests — a local model server started with a
|
|
45
|
+
multi-slot flag (llama.cpp's `--parallel N`, vLLM) — since the default
|
|
46
|
+
already protects an ordinary single-slot server from reload-thrash.
|
|
47
|
+
Embedding throughput is still tuned first by `embedding.batchSize`
|
|
48
|
+
(documents per request) and `embedding.maxTokens` (token
|
|
49
|
+
budget per request); the concurrency override is a second lever for a
|
|
50
|
+
server that can actually use it.
|
|
51
|
+
|
|
52
|
+
`embedding.timeoutMs` bounds each embedding request (default 120s, up from a
|
|
53
|
+
prior fixed 30s that cut off a slow local model server mid-response). It is
|
|
54
|
+
the budget for a request at the full token budget — a smaller request gets a
|
|
55
|
+
proportionally smaller timeout, so a dead endpoint is still detected in
|
|
56
|
+
seconds on the common case of small documents. A request TIMEOUT no longer
|
|
57
|
+
drops its batch immediately: akm now backs off (5s, doubling, capped at 60s)
|
|
58
|
+
and retries the same request once, since field evidence showed the endpoint
|
|
59
|
+
keeps computing an abandoned request regardless of the client giving up; a
|
|
60
|
+
second timeout splits the batch in half and retries each half the same way,
|
|
61
|
+
down to individual documents, and a single document that still times out is
|
|
62
|
+
finally skipped. After 3 consecutive failures at single-document size
|
|
63
|
+
(timeout or network error), or 3 consecutive network errors at any size —
|
|
64
|
+
never a batch rejected only for exceeding the endpoint's context window —
|
|
65
|
+
`akm index`'s embedding phase stops dispatching further requests and reports
|
|
66
|
+
failure instead of grinding through every remaining batch against a dead
|
|
67
|
+
endpoint — batches already committed are kept, and a rerun picks up where it
|
|
68
|
+
left off.
|
|
69
|
+
|
|
70
|
+
`akm bundle update` now durably commits its embedding pass instead of
|
|
71
|
+
nesting it inside its own transaction: earlier releases ran the embedding
|
|
72
|
+
phase inside the same transaction as content/lock/index/state, so every
|
|
73
|
+
per-batch commit landed as an unobservable SAVEPOINT and a SIGKILL mid-run
|
|
74
|
+
lost every embedding of the update, not just the one in flight. The
|
|
75
|
+
embedding phase now runs on its own connection after the update's own
|
|
76
|
+
commit; a failing pass (provider down) still leaves the update itself
|
|
77
|
+
successful, with the new `index.semanticStatus` field on `akm bundle
|
|
78
|
+
update`'s response the only sign semantic search fell behind.
|
|
79
|
+
|
|
80
|
+
The published `akm`/`akm-migrate` launchers now forward SIGTERM/SIGINT/
|
|
81
|
+
SIGHUP to their child and exit alongside it, instead of leaving the child
|
|
82
|
+
running as an orphan when only the launcher is signaled. No action needed —
|
|
83
|
+
this is a drop-in fix for anyone running `akm` under a scheduler,
|
|
84
|
+
supervisor, or hook that can time out or kill the launcher process
|
|
85
|
+
directly.
|
|
86
|
+
|
|
87
|
+
`embedding.maxInputTokens` (default 512) now caps how much of a single
|
|
88
|
+
document's text is sent to the embedding provider, truncating to the head
|
|
89
|
+
instead of ever failing a whole batch over one oversized document.
|
|
90
|
+
|
|
91
|
+
- An existing install's already-stored vectors are untouched and stay
|
|
92
|
+
valid — this only changes what happens for entries embedded *after*
|
|
93
|
+
upgrading.
|
|
94
|
+
- New embeddings (any entry indexed for the first time, or re-indexed after
|
|
95
|
+
a content change) go through the new 512-token cap by default. If you
|
|
96
|
+
were relying on documents longer than ~2000 characters being embedded in
|
|
97
|
+
full, set `embedding.maxInputTokens` higher in `config.json`.
|
|
98
|
+
- `akm index --reembed` re-embeds every entry under the new cap — run it if
|
|
99
|
+
you want your entire existing index rebuilt against the new default (or a
|
|
100
|
+
custom `embedding.maxInputTokens` you've set).
|
|
101
|
+
- `embedding.contextLength` is Ollama's `num_ctx` only now; it no longer
|
|
102
|
+
also sets the per-request token budget (`embedding.maxTokens`). If you had
|
|
103
|
+
set `contextLength` specifically to control request batching (not your
|
|
104
|
+
Ollama server's context window), set `embedding.maxTokens` instead.
|
|
105
|
+
|
|
106
|
+
`akm index --full` and an index-generation bump no longer re-embed
|
|
107
|
+
unchanged content: vectors about to be discarded are salvaged and handed
|
|
108
|
+
back to unchanged entries at the start of the next embedding pass instead
|
|
109
|
+
of every upgrade re-embedding the whole corpus once. No action needed —
|
|
110
|
+
this is automatic; `akm index --reembed` still forces a full re-embed when
|
|
111
|
+
you don't trust the salvaged vectors.
|
|
112
|
+
|
|
113
|
+
A field report suspected `akm index` was sending embedding requests with no
|
|
114
|
+
`Authorization` header despite `embedding.apiKey` being set to a
|
|
115
|
+
`secret://` reference. Auditing every path that builds an embedding request
|
|
116
|
+
found all of them already resolve `secret://` through the same store
|
|
117
|
+
lookup, now pinned by integration and contract tests — this was not a bug
|
|
118
|
+
in the code as it stands. `akm index` now prints one line before its first
|
|
119
|
+
provider request naming the endpoint, model, and credential SOURCE (never
|
|
120
|
+
the value), e.g. `[embed] endpoint http://.../v1/embeddings, model
|
|
121
|
+
nomic-embed; credential: secret://lab-api-key (store)`. If you run an
|
|
122
|
+
embedding gateway that enforces auth and still see unauthenticated requests
|
|
123
|
+
after upgrading, compare this line's endpoint and credential source against
|
|
124
|
+
what the gateway's own request log shows for the same request — a
|
|
125
|
+
mismatch there (not in this line) is the next place to look.
|
|
126
|
+
|
|
127
|
+
A config file can now inherit a shared base via `extends: <path|bundle//path>`,
|
|
128
|
+
deep-merging under the local file so local keys always win. `akm config diff
|
|
129
|
+
<path|bundle//path>` prints every leaf that differs between this instance's
|
|
130
|
+
effective config and another config file or bundle-relative file. `akm config
|
|
131
|
+
unset` now refuses to unset a key whose value comes only from an
|
|
132
|
+
`extends`-inherited base, naming the source, since there would be nothing local
|
|
133
|
+
to remove.
|
|
@@ -7,6 +7,11 @@ live one level up in `docs/migration/`.
|
|
|
7
7
|
|
|
8
8
|
## Available notes
|
|
9
9
|
|
|
10
|
+
- [0.9.15](0.9.15.md) — exit-code 75 for lease/state.db contention,
|
|
11
|
+
`--require-engines` scheduled task templates, `--no-probe` cli-version
|
|
12
|
+
skip, thinking-control wire forms, embedding re-embed safety and
|
|
13
|
+
cross-rebuild vector salvage, launcher signal forwarding, a default
|
|
14
|
+
per-document embedding cap, and `extends` config inheritance
|
|
10
15
|
- [0.9.14](0.9.14.md) — index v22-to-v23 derived-cache rebuild, lexical
|
|
11
16
|
fragments, and collapse-detector canary re-minting
|
|
12
17
|
- [0.9.2](0.9.2.md) — task source v4 migration, workflow source IR v1 and
|