akm-cli 0.9.23 → 0.9.25-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +339 -0
  2. package/dist/commands/health/checks.js +10 -11
  3. package/dist/commands/improve/consolidate/pair-pass.js +3 -2
  4. package/dist/commands/improve/consolidate.js +3 -2
  5. package/dist/commands/improve/execution.js +4 -10
  6. package/dist/commands/improve/extract-prompt.js +4 -4
  7. package/dist/commands/improve/extract.js +10 -13
  8. package/dist/commands/improve/improve-cli.js +32 -33
  9. package/dist/commands/improve/improve-strategies.js +49 -43
  10. package/dist/commands/improve/improve-usage-report.js +8 -17
  11. package/dist/commands/improve/preparation.js +3 -1
  12. package/dist/commands/improve/reflect.js +132 -177
  13. package/dist/commands/improve/stage.js +69 -18
  14. package/dist/commands/proposal/drain.js +19 -7
  15. package/dist/commands/proposal/proposal-cli.js +1 -5
  16. package/dist/commands/proposal/propose-cli.js +2 -2
  17. package/dist/commands/proposal/propose.js +80 -83
  18. package/dist/commands/remember.js +3 -3
  19. package/dist/commands/sources/schema-repair.js +1 -1
  20. package/dist/core/config/config-schema.js +37 -60
  21. package/dist/core/config/engine-semantics.js +15 -11
  22. package/dist/core/config/schema/engines.js +33 -15
  23. package/dist/core/config/schema/improve-processes.js +2 -2
  24. package/dist/core/improve-result.js +3 -3
  25. package/dist/core/structured.js +10 -0
  26. package/dist/execution/source.js +14 -0
  27. package/dist/indexer/passes/memory-inference.js +2 -1
  28. package/dist/integrations/agent/builder-shared.js +15 -0
  29. package/dist/integrations/agent/config.js +2 -0
  30. package/dist/integrations/agent/engine-resolution.js +16 -31
  31. package/dist/integrations/agent/execution.js +46 -21
  32. package/dist/integrations/agent/index.js +1 -1
  33. package/dist/integrations/agent/model-map.js +16 -15
  34. package/dist/integrations/agent/prompts.js +53 -76
  35. package/dist/integrations/agent/request-lowering.js +20 -9
  36. package/dist/integrations/agent/runner-dispatch.js +103 -4
  37. package/dist/integrations/agent/runner.js +8 -2
  38. package/dist/integrations/harnesses/aider/agent-builder.js +11 -23
  39. package/dist/integrations/harnesses/aider/index.js +0 -5
  40. package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -21
  41. package/dist/integrations/harnesses/amazonq/index.js +0 -5
  42. package/dist/integrations/harnesses/claude/agent-builder.js +61 -38
  43. package/dist/integrations/harnesses/claude/index.js +0 -14
  44. package/dist/integrations/harnesses/codex/agent-builder.js +4 -4
  45. package/dist/integrations/harnesses/codex/index.js +0 -4
  46. package/dist/integrations/harnesses/copilot/agent-builder.js +4 -13
  47. package/dist/integrations/harnesses/copilot/index.js +2 -7
  48. package/dist/integrations/harnesses/gemini/agent-builder.js +4 -13
  49. package/dist/integrations/harnesses/gemini/index.js +0 -5
  50. package/dist/integrations/harnesses/ids.js +18 -10
  51. package/dist/integrations/harnesses/opencode/agent-builder.js +52 -10
  52. package/dist/integrations/harnesses/opencode/index.js +0 -8
  53. package/dist/integrations/harnesses/opencode/model-config.js +80 -0
  54. package/dist/integrations/harnesses/opencode/model-work-agent.js +80 -0
  55. package/dist/integrations/harnesses/opencode-sdk/harness.js +6 -7
  56. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +168 -24
  57. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -21
  58. package/dist/integrations/harnesses/openhands/index.js +0 -5
  59. package/dist/integrations/harnesses/pi/agent-builder.js +8 -21
  60. package/dist/integrations/harnesses/pi/index.js +0 -5
  61. package/dist/llm/client.js +5 -0
  62. package/dist/llm/feature-gate.js +10 -4
  63. package/dist/llm/index-passes.js +3 -6
  64. package/dist/llm/memory-infer.js +6 -5
  65. package/dist/llm/structured-call.js +33 -11
  66. package/dist/scripts/akm-migrate-node.js +298 -204
  67. package/dist/scripts/akm-migrate.js +298 -204
  68. package/dist/workflows/exec/step-work.js +6 -5
  69. package/dist/workflows/freeze/step-values.js +1 -1
  70. package/docs/reference/cli.md +34 -14
  71. package/docs/reference/configuration.md +171 -12
  72. package/docs/reference/workflow-schema.md +13 -9
  73. package/package.json +1 -1
  74. package/schemas/akm-config.json +36 -0
@@ -15,6 +15,7 @@ import { redactSensitiveText } from "../../core/redaction.js";
15
15
  import { clearLogFile, setLogFile, warn } from "../../core/warn.js";
16
16
  import { resolveWriteTarget } from "../../core/write-source.js";
17
17
  import { DEFAULT_LLM_TIMEOUT_MS } from "../../integrations/agent/config.js";
18
+ import { defaultWhich } from "../../integrations/agent/detect.js";
18
19
  import { collectEngineCredentialValues } from "../../integrations/agent/engine-resolution.js";
19
20
  import { probeLlmReachable } from "../../llm/client.js";
20
21
  import { getOutputMode } from "../../output/context.js";
@@ -73,26 +74,19 @@ function assertRequiredEnginesAvailable(plan) {
73
74
  const lines = plan.engineUnavailable.map((item) => ` - ${item.process} (${item.configKey}): ${item.reason}`);
74
75
  throw new ConfigError(`--require-engines: ${plan.engineUnavailable.length} improve process${plan.engineUnavailable.length === 1 ? "" : "es"} cannot run because ${plan.engineUnavailable.length === 1 ? "its" : "their"} engine is unavailable:\n${lines.join("\n")}`, "LLM_NOT_CONFIGURED");
75
76
  }
76
- /** Every LLM connection the plan would dispatch to, triage's judgment engine included. */
77
+ /** Every engine the plan would dispatch to, triage's judgment engine included. */
77
78
  function collectRequiredEngineTargets(plan) {
78
- const targets = [];
79
- for (const [processName, process] of Object.entries(plan.processes)) {
80
- if (process.runner) {
81
- targets.push({
82
- process: processName,
83
- engine: process.runner.engine,
84
- connection: probeConnection(process.runner),
85
- });
86
- }
87
- }
88
- if (plan.triageJudgment?.kind === "llm") {
89
- targets.push({
90
- process: "triage.judgment",
91
- engine: plan.triageJudgment.engine,
92
- connection: probeConnection(plan.triageJudgment),
93
- });
94
- }
95
- return targets;
79
+ const runners = Object.entries(plan.processes)
80
+ .flatMap(([processName, process]) => (process.runner ? [[processName, process.runner]] : []))
81
+ .concat(plan.triageJudgment ? [["triage.judgment", plan.triageJudgment]] : []);
82
+ return runners.map(([processName, runner]) => ({
83
+ process: processName,
84
+ engine: runner.engine,
85
+ ...(runner.kind === "llm" ? { connection: probeConnection(runner) } : { bin: runner.profile.bin }),
86
+ ...(runner.kind === "sdk" && runner.fallbackConnection
87
+ ? { connection: probeConnection({ connection: runner.fallbackConnection, timeoutMs: runner.fallbackTimeoutMs }) }
88
+ : {}),
89
+ }));
96
90
  }
97
91
  /** The resolved engine keeps its request timeout beside the connection (the runtime merges it in); the probe needs it on the connection. */
98
92
  function probeConnection(runner) {
@@ -112,37 +106,42 @@ const REQUIRED_ENGINE_PROBE_MAX_MS = 120_000;
112
106
  /**
113
107
  * `--require-engines`, live: probe each connection's real completion path
114
108
  * (a gateway can list a model whose completion route is dead, #980), once per
115
- * endpoint + model, within {@link requiredEngineProbeTimeoutMs}. Returns each
116
- * target's latency for the run result (R17); an unreachable one fails the run.
109
+ * endpoint + model, within {@link requiredEngineProbeTimeoutMs}, and look each
110
+ * agent harness's binary up on PATH. Returns each target's latency for the run
111
+ * result (R17); an unreachable one fails the run.
117
112
  */
118
- export async function assertRequiredEnginesReachable(plan, probeReachable = (connection) => probeLlmReachable(connection, requiredEngineProbeTimeoutMs(connection))) {
113
+ export async function assertRequiredEnginesReachable(plan, probeReachable = (connection) => probeLlmReachable(connection, requiredEngineProbeTimeoutMs(connection)), which = defaultWhich) {
119
114
  const targets = collectRequiredEngineTargets(plan);
120
115
  if (targets.length === 0)
121
116
  return [];
122
117
  const probesByConnection = new Map();
123
- const probed = await Promise.all(targets.map(async (target) => {
124
- const key = `${target.connection.endpoint.replace(/\/+$/, "")}|${target.connection.model}`;
118
+ const probeConnectionOnce = (connection) => {
119
+ const key = `${connection.endpoint.replace(/\/+$/, "")}|${connection.model}`;
125
120
  let pending = probesByConnection.get(key);
126
121
  if (!pending) {
127
122
  const probeStartedAt = Date.now();
128
- pending = probeReachable(target.connection).then((reach) => ({
129
- reach,
130
- latencyMs: Date.now() - probeStartedAt,
131
- }));
123
+ pending = probeReachable(connection).then((reach) => ({ reach, latencyMs: Date.now() - probeStartedAt }));
132
124
  probesByConnection.set(key, pending);
133
125
  }
134
- const { reach, latencyMs } = await pending;
135
- return { ...target, reach, latencyMs };
126
+ return pending;
127
+ };
128
+ const probed = await Promise.all(targets.map(async (target) => {
129
+ if (target.bin !== undefined && which(target.bin) === undefined) {
130
+ return { ...target, reach: { reachable: false, error: `${target.bin} is not on PATH` }, latencyMs: 0 };
131
+ }
132
+ if (!target.connection)
133
+ return { ...target, reach: { reachable: true }, latencyMs: 0 };
134
+ return { ...target, ...(await probeConnectionOnce(target.connection)) };
136
135
  }));
137
136
  const unreachable = probed.filter((item) => !item.reach.reachable);
138
137
  if (unreachable.length > 0) {
139
- const lines = unreachable.map((item) => ` - ${item.process} (engine "${item.engine}", ${item.connection.endpoint}): ${item.reach.error ?? "did not respond"}`);
140
- throw new ConfigError(`--require-engines: ${unreachable.length} improve process${unreachable.length === 1 ? "" : "es"} cannot run because ${unreachable.length === 1 ? "its" : "their"} engine completion path is not reachable:\n${lines.join("\n")}`, "LLM_NOT_CONFIGURED", "Check that each listed endpoint is up and serves its model. The probe is one short completion, bounded by the engine's timeoutMs (at most two minutes).");
138
+ const lines = unreachable.map((item) => ` - ${item.process} (engine "${item.engine}", ${item.connection?.endpoint ?? item.bin}): ${item.reach.error ?? "did not respond"}`);
139
+ throw new ConfigError(`--require-engines: ${unreachable.length} improve process${unreachable.length === 1 ? "" : "es"} cannot run because ${unreachable.length === 1 ? "its" : "their"} engine completion path is not reachable:\n${lines.join("\n")}`, "LLM_NOT_CONFIGURED", "Check that each listed endpoint is up and serves its model, and that each listed agent binary is installed. The endpoint probe is one short completion, bounded by the engine's timeoutMs (at most two minutes).");
141
140
  }
142
141
  return probed.map((item) => ({
143
142
  process: item.process,
144
143
  engine: item.engine,
145
- endpoint: item.connection.endpoint,
144
+ endpoint: item.connection?.endpoint ?? item.bin,
146
145
  reachable: item.reach.reachable,
147
146
  latencyMs: item.latencyMs,
148
147
  }));
@@ -11,11 +11,12 @@ import thorough from "../../assets/improve-strategies/thorough.json" with { type
11
11
  import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
12
12
  import { ImproveProfileConfigSchema } from "../../core/config/config-schema.js";
13
13
  import { deepMergeConfig } from "../../core/config/deep-merge.js";
14
- import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES, } from "../../core/config/engine-semantics.js";
14
+ import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_ENGINE_PROCESSES, IMPROVE_PROCESS_NAMES, } from "../../core/config/engine-semantics.js";
15
15
  import { ConfigError } from "../../core/errors.js";
16
- import { describeLlmCredentialAvailability } from "../../integrations/agent/engine-resolution.js";
16
+ import { describeLlmCredentialAvailability, } from "../../integrations/agent/engine-resolution.js";
17
+ import { runnerLlmConnection } from "../../integrations/agent/runner.js";
17
18
  import { applyAutonomyGate } from "./autonomy-gate.js";
18
- import { resolveImproveExecution, resolveImproveLlmExecution } from "./execution.js";
19
+ import { resolveImproveExecution } from "./execution.js";
19
20
  import { stripBundle } from "./ledger.js";
20
21
  export const DEFAULT_ALLOWED_TYPES = {
21
22
  reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "workflow"],
@@ -113,9 +114,14 @@ export function resolveImproveStrategy(name, config) {
113
114
  const resolved = deepMergeConfig(baseStrategy, (userStrategies[selectedName] ?? {}));
114
115
  return { name: selectedName, config: ImproveProfileConfigSchema.parse(resolved) };
115
116
  }
117
+ /**
118
+ * The processes that make their own model calls, so the plan resolves a runner
119
+ * for each: every engine process but triage, whose engine is its judgment's.
120
+ */
121
+ export const MODEL_CALLING_PROCESSES = new Set(IMPROVE_ENGINE_PROCESSES.filter((name) => name !== "triage"));
116
122
  /**
117
123
  * Project a resolved improve plan into one routing row per
118
- * {@link IMPROVE_PROCESS_ENGINE_CAPABILITIES} name, plus a `"triage.judgment"`
124
+ * {@link IMPROVE_PROCESS_NAMES} name, plus a `"triage.judgment"`
119
125
  * pseudo-row when the strategy configures a judgment engine. Pure — no I/O,
120
126
  * no re-resolution. Shared by `improve --dry-run`'s `plan.processes` (#947)
121
127
  * and `akm health`'s post-run reporting (#944); health's own
@@ -124,14 +130,18 @@ export function resolveImproveStrategy(name, config) {
124
130
  export function projectResolvedProcessRouting(plan) {
125
131
  const unavailableByProcess = new Map(plan.engineUnavailable.map((item) => [item.process, item]));
126
132
  const rows = [];
127
- for (const processName of Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES)) {
133
+ for (const processName of IMPROVE_PROCESS_NAMES) {
128
134
  const process = plan.processes[processName];
129
135
  const unavailable = unavailableByProcess.get(processName);
130
136
  rows.push({
131
137
  process: processName,
132
138
  enabled: process.enabled,
133
139
  ...(process.runner
134
- ? { engine: process.runner.engine, model: process.runner.connection.model, engineKind: process.runner.kind }
140
+ ? {
141
+ engine: process.runner.engine,
142
+ model: runnerLlmConnection(process.runner)?.model,
143
+ engineKind: process.runner.kind,
144
+ }
135
145
  : // #800/#957 round 3 — a credential-unavailable process never carries a
136
146
  // runner, but its structurally resolved engine/model is still worth
137
147
  // showing in the routing table (dry-run preview, health probe).
@@ -192,6 +202,22 @@ export function resolveImprovePlan(name, config, options = {}) {
192
202
  function credentialUnavailableReason(engineName, status) {
193
203
  return `requires a credential that is not available in this process's environment (engine "${engineName}": ${status.reason})`;
194
204
  }
205
+ /**
206
+ * Whether the credential akm reads for `runner` is reachable in `env`: an LLM
207
+ * engine's own, or an SDK engine's LLM fallback. An agent CLI reads its own.
208
+ */
209
+ function runnerCredentialStatus(runner, env) {
210
+ if (runner.kind === "llm")
211
+ return describeLlmCredentialAvailability(runner, env);
212
+ if (runner.kind === "sdk") {
213
+ return describeLlmCredentialAvailability({
214
+ credential: runner.fallbackCredential,
215
+ apiKeyFile: runner.fallbackApiKeyFile,
216
+ apiKeySecretRef: runner.fallbackApiKeySecretRef,
217
+ }, env);
218
+ }
219
+ return { available: true };
220
+ }
195
221
  function buildImprovePlan(strategy, config, options) {
196
222
  const env = options.env ?? process.env;
197
223
  const processes = {};
@@ -205,12 +231,12 @@ function buildImprovePlan(strategy, config, options) {
205
231
  // install is not the credential-in-the-wrong-environment case this option
206
232
  // exists for.
207
233
  let anyEngineNotConfigured = false;
208
- for (const processName of Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES)) {
234
+ for (const processName of IMPROVE_PROCESS_NAMES) {
209
235
  const sourceProcessConfig = strategy.config.processes?.[processName] ?? {};
210
236
  const enabled = sourceProcessConfig.enabled === true;
211
237
  let runner = null;
212
238
  let notices = [];
213
- if (IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName] !== "llm" || !enabled) {
239
+ if (!MODEL_CALLING_PROCESSES.has(processName) || !enabled) {
214
240
  processes[processName] = Object.freeze({ enabled, config: cloneAndFreeze(sourceProcessConfig), runner });
215
241
  continue;
216
242
  }
@@ -227,7 +253,7 @@ function buildImprovePlan(strategy, config, options) {
227
253
  // without the runner ever carrying a credential-less connection forward.
228
254
  let credentialUnavailableRouting;
229
255
  if (!skipsRepairEngine) {
230
- const resolved = resolveImproveLlmExecution({
256
+ const resolved = resolveImproveExecution({
231
257
  config,
232
258
  profile: strategy.config,
233
259
  process: sourceProcessConfig,
@@ -236,15 +262,14 @@ function buildImprovePlan(strategy, config, options) {
236
262
  runner = resolved?.runner ?? null;
237
263
  notices = resolved?.notices ?? [];
238
264
  if (runner) {
239
- const credentialStatus = describeLlmCredentialAvailability(runner, env);
265
+ const credentialStatus = runnerCredentialStatus(runner, env);
240
266
  if (!credentialStatus.available) {
267
+ const connection = runnerLlmConnection(runner);
241
268
  credentialUnavailableMessage = credentialUnavailableReason(runner.engine, credentialStatus);
242
269
  credentialUnavailableRouting = {
243
270
  engine: runner.engine,
244
- ...(runner.connection.model !== undefined ? { model: runner.connection.model } : {}),
245
- ...(runner.connection.contextLength !== undefined
246
- ? { contextLength: runner.connection.contextLength }
247
- : {}),
271
+ ...(connection?.model !== undefined ? { model: connection.model } : {}),
272
+ ...(connection?.contextLength !== undefined ? { contextLength: connection.contextLength } : {}),
248
273
  };
249
274
  runner = null;
250
275
  notices = [];
@@ -259,7 +284,7 @@ function buildImprovePlan(strategy, config, options) {
259
284
  process: processName,
260
285
  configKey,
261
286
  reason: credentialUnavailableMessage ??
262
- `requires an LLM engine that is not configured. Set defaults.llmEngine or ${configKey}`,
287
+ `requires an engine that is not configured. Set defaults.llmEngine or ${configKey}`,
263
288
  ...credentialUnavailableRouting,
264
289
  });
265
290
  processes[processName] = Object.freeze({
@@ -282,7 +307,7 @@ function buildImprovePlan(strategy, config, options) {
282
307
  !Object.values(processes).some((process) => process.enabled) &&
283
308
  (!options.allowAllDisabled || anyEngineNotConfigured)) {
284
309
  const names = engineUnavailable.map((item) => `"${item.process}"`).join(", ");
285
- throw new ConfigError(`No improve process can run: ${names} ${engineUnavailable.length === 1 ? "requires" : "require"} an LLM engine that is not configured. Set defaults.llmEngine, or the per-process engine key named for each.`, "LLM_NOT_CONFIGURED");
310
+ throw new ConfigError(`No improve process can run: ${names} ${engineUnavailable.length === 1 ? "requires" : "require"} an engine that is not configured. Set defaults.llmEngine, or the per-process engine key named for each.`, "LLM_NOT_CONFIGURED");
286
311
  }
287
312
  const triage = strategy.config.processes?.triage;
288
313
  const judgmentEnabled = triage?.judgment?.enabled === true;
@@ -296,10 +321,6 @@ function buildImprovePlan(strategy, config, options) {
296
321
  })
297
322
  : null;
298
323
  let triageJudgment = triageJudgmentResolution?.runner ?? null;
299
- const effectiveJudgmentLlm = triage?.judgment?.llm ?? triage?.llm ?? strategy.config.llm;
300
- if (triageJudgment && triageJudgment.kind !== "llm" && effectiveJudgmentLlm) {
301
- throw new ConfigError(`Triage judgment engine "${triageJudgment.engine ?? "unknown"}" is an agent engine and cannot receive llm overrides.`, "INVALID_CONFIG_FILE");
302
- }
303
324
  if (processes.triage.enabled && judgmentEnabled && !triageJudgment) {
304
325
  throw new ConfigError(`Enabled improve triage judgment requires an engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.triage.judgment.engine.`, "LLM_NOT_CONFIGURED");
305
326
  }
@@ -310,29 +331,14 @@ function buildImprovePlan(strategy, config, options) {
310
331
  // `engineUnavailable` under the reserved `"triage.judgment"` process name
311
332
  // instead of silently reaching dispatch with a doomed credential.
312
333
  if (triageJudgment) {
313
- const judgmentCredentialFields = triageJudgment.kind === "llm"
314
- ? {
315
- credential: triageJudgment.credential,
316
- apiKeyFile: triageJudgment.apiKeyFile,
317
- apiKeySecretRef: triageJudgment.apiKeySecretRef,
318
- }
319
- : triageJudgment.kind === "sdk"
320
- ? {
321
- credential: triageJudgment.fallbackCredential,
322
- apiKeyFile: triageJudgment.fallbackApiKeyFile,
323
- apiKeySecretRef: triageJudgment.fallbackApiKeySecretRef,
324
- }
325
- : undefined;
326
- if (judgmentCredentialFields) {
327
- const credentialStatus = describeLlmCredentialAvailability(judgmentCredentialFields, env);
328
- if (!credentialStatus.available) {
329
- engineUnavailable.push({
330
- process: "triage.judgment",
331
- configKey: `improve.strategies.${strategy.name}.processes.triage.judgment.engine`,
332
- reason: credentialUnavailableReason(triageJudgment.engine, credentialStatus),
333
- });
334
- triageJudgment = null;
335
- }
334
+ const credentialStatus = runnerCredentialStatus(triageJudgment, env);
335
+ if (!credentialStatus.available) {
336
+ engineUnavailable.push({
337
+ process: "triage.judgment",
338
+ configKey: `improve.strategies.${strategy.name}.processes.triage.judgment.engine`,
339
+ reason: credentialUnavailableReason(triageJudgment.engine, credentialStatus),
340
+ });
341
+ triageJudgment = null;
336
342
  }
337
343
  }
338
344
  const frozenProcesses = Object.freeze(processes);
@@ -1,20 +1,8 @@
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
- * The per-run LLM usage report (#944): the process × engine × model cross-tab
6
- * plus the enabled processes that made no call and why, rendered as one table
7
- * for the end-of-run stderr summary and `akm improve report`.
8
- */
9
- import { IMPROVE_PROCESS_ENGINE_CAPABILITIES } from "../../core/config/engine-semantics.js";
10
- import { eligibleRefCount, projectResolvedProcessRouting, } from "./improve-strategies.js";
11
- /**
12
- * Only processes that call an LLM themselves: triage (a runner, attributed to
13
- * its judgment engine) and proactive maintenance (no engine) would always read
14
- * as "zero calls".
15
- */
16
- const LLM_BACKED_PROCESSES = new Set(Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES).filter((name) => IMPROVE_PROCESS_ENGINE_CAPABILITIES[name] === "llm"));
17
- /** The autonomy lane gating each LLM-backed process, if any. */
4
+ import { eligibleRefCount, MODEL_CALLING_PROCESSES, projectResolvedProcessRouting, } from "./improve-strategies.js";
5
+ /** The autonomy lane gating each model-calling process, if any. */
18
6
  const AUTONOMY_LANE_BY_PROCESS = {
19
7
  memoryInference: "memoryInference",
20
8
  };
@@ -30,7 +18,7 @@ function dominantReason(counts) {
30
18
  return best;
31
19
  }
32
20
  /**
33
- * Why an LLM-backed process made no call, in priority order: its engine is
21
+ * Why a model-calling process made no call, in priority order: its engine is
34
22
  * unavailable; disabled — `autonomy_gated` when its lane was gated, else
35
23
  * nothing to report; every ref strategy-filtered; its dominant skip reason;
36
24
  * else `no_signal`. The reasons are the ones the events and results already
@@ -65,8 +53,11 @@ function countReflectSkipReasons(actions) {
65
53
  }
66
54
  /** This run's `usageReport`, or `undefined` when both halves are empty. */
67
55
  export function buildImproveUsageReport(args) {
68
- // The "triage.judgment" row is dropped too: its calls are never attributed to it (#947).
69
- const routing = projectResolvedProcessRouting(args.resolvedPlan).filter((row) => row.process !== "triage.judgment" && LLM_BACKED_PROCESSES.has(row.process));
56
+ // Only the processes that call a model themselves, on any engine kind: triage
57
+ // (attributed to its judgment) and proactive maintenance (no engine) would
58
+ // always read as "zero calls". The "triage.judgment" row is dropped too: its
59
+ // calls are never attributed to it (#947).
60
+ const routing = projectResolvedProcessRouting(args.resolvedPlan).filter((row) => row.process !== "triage.judgment" && MODEL_CALLING_PROCESSES.has(row.process));
70
61
  const calledProcesses = new Set(args.byProcessEngineModel.filter((row) => row.calls > 0).map((row) => row.process));
71
62
  const reflectSkipCounts = countReflectSkipReasons(args.persistedActions);
72
63
  const noCalls = [];
@@ -24,6 +24,7 @@ import { appendEvent, readEvents } from "../../core/events.js";
24
24
  import { withStateDb } from "../../core/state-db.js";
25
25
  import { info, warn } from "../../core/warn.js";
26
26
  import { countUsageEventsByType, USAGE_EVENT_RETENTION_DAYS } from "../../indexer/usage/usage-events.js";
27
+ import { runnerLlmConnection } from "../../integrations/agent/runner.js";
27
28
  import { getAvailableHarnesses } from "../../integrations/session-logs/index.js";
28
29
  import { getZeroResultSearches } from "../../storage/repositories/index-entries-repository.js";
29
30
  import { getRetrievalCounts } from "../../storage/repositories/index-utility-repository.js";
@@ -119,7 +120,8 @@ function planConsolidationPass(args) {
119
120
  : { poolSize: 0, candidatePoolSize: 0, judgedUnchanged: 0 };
120
121
  // A credential-unavailable engine still resolved its context length.
121
122
  const unavailable = resolvedPlan.engineUnavailable.find((item) => item.process === "consolidate");
122
- const chunkSize = computeSafeChunkSize(resolvedPlan.processes.consolidate.runner?.connection.contextLength ??
123
+ const chunkSize = computeSafeChunkSize((resolvedPlan.processes.consolidate.runner &&
124
+ runnerLlmConnection(resolvedPlan.processes.consolidate.runner)?.contextLength) ??
123
125
  unavailable?.contextLength ??
124
126
  DEFAULT_CONTEXT_LENGTH_TOKENS, 500, processConfig?.maxChunkSize);
125
127
  const profilePassed = processConfig?.enabled !== false;