@remnic/core 1.0.3 → 1.1.0

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 (148) hide show
  1. package/dist/abort-error.d.ts +32 -0
  2. package/dist/abort-error.js +11 -0
  3. package/dist/access-cli.js +41 -40
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +3 -2
  6. package/dist/access-http.js +11 -11
  7. package/dist/access-mcp.d.ts +3 -2
  8. package/dist/access-mcp.js +7 -7
  9. package/dist/access-schema.d.ts +16 -16
  10. package/dist/access-service-HmO1Trrx.d.ts +732 -0
  11. package/dist/access-service.d.ts +11 -658
  12. package/dist/access-service.js +9 -9
  13. package/dist/bootstrap.d.ts +1 -1
  14. package/dist/briefing.js +6 -6
  15. package/dist/calibration.js +3 -3
  16. package/dist/causal-consolidation.js +10 -10
  17. package/dist/{chunk-PMB3WGDL.js → chunk-37UIFYWO.js} +64 -3
  18. package/dist/chunk-37UIFYWO.js.map +1 -0
  19. package/dist/{chunk-X4WESCKA.js → chunk-3QHL5ABG.js} +5 -5
  20. package/dist/{chunk-2VFW5K5U.js → chunk-3SV6CQHO.js} +10 -8
  21. package/dist/chunk-3SV6CQHO.js.map +1 -0
  22. package/dist/{chunk-U2IQTSBY.js → chunk-3WHVNEN7.js} +1 -1
  23. package/dist/chunk-3WHVNEN7.js.map +1 -0
  24. package/dist/{chunk-QKAH5B6E.js → chunk-44ICJRF3.js} +97 -9
  25. package/dist/chunk-44ICJRF3.js.map +1 -0
  26. package/dist/{chunk-S4LX5EBI.js → chunk-47UU5PU2.js} +48 -9
  27. package/dist/chunk-47UU5PU2.js.map +1 -0
  28. package/dist/{chunk-ECKDIK5F.js → chunk-4LACOVZX.js} +2 -2
  29. package/dist/{chunk-AYPYCLR7.js → chunk-6LX5ORAS.js} +2 -2
  30. package/dist/{chunk-GJQPH5G3.js → chunk-6UJ47TVX.js} +2 -2
  31. package/dist/{chunk-KWP7T3DP.js → chunk-7ECD5ATE.js} +2 -2
  32. package/dist/{chunk-74JR4N5J.js → chunk-7WQ6SLIE.js} +2 -2
  33. package/dist/{chunk-7PA4OZEU.js → chunk-BLKTA7MM.js} +6 -14
  34. package/dist/chunk-BLKTA7MM.js.map +1 -0
  35. package/dist/{chunk-XMGSSBFX.js → chunk-DEPL3635.js} +172 -100
  36. package/dist/chunk-DEPL3635.js.map +1 -0
  37. package/dist/{chunk-JROGC36Y.js → chunk-DHHP2Z4X.js} +2 -2
  38. package/dist/{chunk-BKQJBXXX.js → chunk-GGD5W7TB.js} +2 -2
  39. package/dist/chunk-GGD5W7TB.js.map +1 -0
  40. package/dist/{chunk-POMSFKTB.js → chunk-GV6NLQ4X.js} +14 -14
  41. package/dist/{chunk-AAI7JARD.js → chunk-HMDCOMYU.js} +8 -11
  42. package/dist/chunk-HMDCOMYU.js.map +1 -0
  43. package/dist/{chunk-POBPGDWI.js → chunk-ITRLGI2T.js} +2 -2
  44. package/dist/{chunk-MYQWXITD.js → chunk-JIU55F3X.js} +2 -2
  45. package/dist/{chunk-UPMD5XND.js → chunk-JL2PU6AI.js} +16 -5
  46. package/dist/chunk-JL2PU6AI.js.map +1 -0
  47. package/dist/{chunk-OJFGVJS6.js → chunk-MBJHSA7F.js} +65 -9
  48. package/dist/chunk-MBJHSA7F.js.map +1 -0
  49. package/dist/{chunk-V7XCAHIB.js → chunk-MVTHXUBX.js} +46 -23
  50. package/dist/chunk-MVTHXUBX.js.map +1 -0
  51. package/dist/{chunk-BTY5RRRF.js → chunk-N42IWANG.js} +5 -5
  52. package/dist/{chunk-NSB3WSYS.js → chunk-NQEVYWX6.js} +74 -3
  53. package/dist/chunk-NQEVYWX6.js.map +1 -0
  54. package/dist/chunk-OIT5QGG4.js +80 -0
  55. package/dist/chunk-OIT5QGG4.js.map +1 -0
  56. package/dist/chunk-PVGDJXVK.js +21 -0
  57. package/dist/chunk-PVGDJXVK.js.map +1 -0
  58. package/dist/{chunk-RCICHSHL.js → chunk-SYUK3VLY.js} +2 -2
  59. package/dist/{chunk-YFYL2SIJ.js → chunk-WBSAYXVI.js} +127 -39
  60. package/dist/chunk-WBSAYXVI.js.map +1 -0
  61. package/dist/{chunk-KEG4GNGI.js → chunk-XZ2TIKGC.js} +38 -8
  62. package/dist/chunk-XZ2TIKGC.js.map +1 -0
  63. package/dist/chunk-Y4FHOFJ2.js +140 -0
  64. package/dist/chunk-Y4FHOFJ2.js.map +1 -0
  65. package/dist/chunk-YNB73F22.js +137 -0
  66. package/dist/chunk-YNB73F22.js.map +1 -0
  67. package/dist/{chunk-HITJFT7E.js → chunk-ZVBB3T7V.js} +10 -5
  68. package/dist/chunk-ZVBB3T7V.js.map +1 -0
  69. package/dist/{cli-DwIBnp2g.d.ts → cli-BneVIEvh.d.ts} +2 -2
  70. package/dist/cli.d.ts +4 -3
  71. package/dist/cli.js +25 -24
  72. package/dist/config.js +1 -1
  73. package/dist/contradiction-review-WIUBAR52.js +21 -0
  74. package/dist/contradiction-review-WIUBAR52.js.map +1 -0
  75. package/dist/contradiction-scan-GR33PONM.js +376 -0
  76. package/dist/contradiction-scan-GR33PONM.js.map +1 -0
  77. package/dist/direct-answer-wiring.d.ts +77 -0
  78. package/dist/direct-answer-wiring.js +75 -0
  79. package/dist/direct-answer-wiring.js.map +1 -0
  80. package/dist/direct-answer.d.ts +106 -0
  81. package/dist/direct-answer.js +10 -0
  82. package/dist/direct-answer.js.map +1 -0
  83. package/dist/{engine-X7X3AAG3.js → engine-5TIQBYZR.js} +7 -7
  84. package/dist/engine-5TIQBYZR.js.map +1 -0
  85. package/dist/entity-retrieval.js +6 -6
  86. package/dist/explicit-capture.d.ts +1 -1
  87. package/dist/extraction.js +6 -6
  88. package/dist/fallback-llm.d.ts +11 -2
  89. package/dist/fallback-llm.js +3 -3
  90. package/dist/harmonic-retrieval.js +2 -1
  91. package/dist/index.d.ts +10 -124
  92. package/dist/index.js +74 -137
  93. package/dist/index.js.map +1 -1
  94. package/dist/intent.js +1 -1
  95. package/dist/local-llm.d.ts +10 -3
  96. package/dist/local-llm.js +1 -1
  97. package/dist/operator-toolkit.js +12 -11
  98. package/dist/{orchestrator-B9kwlCep.d.ts → orchestrator-DRYA6_lW.d.ts} +21 -2
  99. package/dist/orchestrator.d.ts +1 -1
  100. package/dist/orchestrator.js +36 -35
  101. package/dist/qmd.js +2 -1
  102. package/dist/recall-state.d.ts +28 -1
  103. package/dist/recall-state.js +1 -1
  104. package/dist/resolution-QBTDHTG7.js +100 -0
  105. package/dist/resolution-QBTDHTG7.js.map +1 -0
  106. package/dist/resolve-provider-secret.d.ts +24 -1
  107. package/dist/resolve-provider-secret.js +3 -1
  108. package/dist/resume-bundles.js +3 -3
  109. package/dist/schemas.d.ts +14 -14
  110. package/dist/semantic-consolidation.js +6 -6
  111. package/dist/semantic-rule-promotion.js +6 -6
  112. package/dist/semantic-rule-verifier.js +6 -6
  113. package/dist/storage.js +5 -5
  114. package/dist/summarizer.js +5 -5
  115. package/dist/types-DJhqDJUV.d.ts +50 -0
  116. package/dist/types.d.ts +34 -2
  117. package/dist/types.js +1 -1
  118. package/dist/verified-recall.js +6 -6
  119. package/package.json +1 -1
  120. package/dist/chunk-2VFW5K5U.js.map +0 -1
  121. package/dist/chunk-7PA4OZEU.js.map +0 -1
  122. package/dist/chunk-AAI7JARD.js.map +0 -1
  123. package/dist/chunk-BKQJBXXX.js.map +0 -1
  124. package/dist/chunk-HITJFT7E.js.map +0 -1
  125. package/dist/chunk-KEG4GNGI.js.map +0 -1
  126. package/dist/chunk-NSB3WSYS.js.map +0 -1
  127. package/dist/chunk-OJFGVJS6.js.map +0 -1
  128. package/dist/chunk-PMB3WGDL.js.map +0 -1
  129. package/dist/chunk-QKAH5B6E.js.map +0 -1
  130. package/dist/chunk-S4LX5EBI.js.map +0 -1
  131. package/dist/chunk-U2IQTSBY.js.map +0 -1
  132. package/dist/chunk-UPMD5XND.js.map +0 -1
  133. package/dist/chunk-V7XCAHIB.js.map +0 -1
  134. package/dist/chunk-XMGSSBFX.js.map +0 -1
  135. package/dist/chunk-YFYL2SIJ.js.map +0 -1
  136. /package/dist/{engine-X7X3AAG3.js.map → abort-error.js.map} +0 -0
  137. /package/dist/{chunk-X4WESCKA.js.map → chunk-3QHL5ABG.js.map} +0 -0
  138. /package/dist/{chunk-ECKDIK5F.js.map → chunk-4LACOVZX.js.map} +0 -0
  139. /package/dist/{chunk-AYPYCLR7.js.map → chunk-6LX5ORAS.js.map} +0 -0
  140. /package/dist/{chunk-GJQPH5G3.js.map → chunk-6UJ47TVX.js.map} +0 -0
  141. /package/dist/{chunk-KWP7T3DP.js.map → chunk-7ECD5ATE.js.map} +0 -0
  142. /package/dist/{chunk-74JR4N5J.js.map → chunk-7WQ6SLIE.js.map} +0 -0
  143. /package/dist/{chunk-JROGC36Y.js.map → chunk-DHHP2Z4X.js.map} +0 -0
  144. /package/dist/{chunk-POMSFKTB.js.map → chunk-GV6NLQ4X.js.map} +0 -0
  145. /package/dist/{chunk-POBPGDWI.js.map → chunk-ITRLGI2T.js.map} +0 -0
  146. /package/dist/{chunk-MYQWXITD.js.map → chunk-JIU55F3X.js.map} +0 -0
  147. /package/dist/{chunk-BTY5RRRF.js.map → chunk-N42IWANG.js.map} +0 -0
  148. /package/dist/{chunk-RCICHSHL.js.map → chunk-SYUK3VLY.js.map} +0 -0
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  getGatewayRuntimeAuthForModel,
3
3
  resolveProviderApiKey
4
- } from "./chunk-KEG4GNGI.js";
5
- import {
6
- loadModelsJsonProviders
7
- } from "./chunk-ODWDQNRE.js";
4
+ } from "./chunk-XZ2TIKGC.js";
8
5
  import {
9
6
  buildChatCompletionTokenLimit,
10
7
  shouldAssumeOpenAiChatCompletions
11
8
  } from "./chunk-Y27UJK6V.js";
9
+ import {
10
+ loadModelsJsonProviders
11
+ } from "./chunk-ODWDQNRE.js";
12
12
  import {
13
13
  extractJsonCandidates
14
14
  } from "./chunk-UZB5KHKX.js";
@@ -19,8 +19,10 @@ import {
19
19
  // src/fallback-llm.ts
20
20
  var FallbackLlmClient = class {
21
21
  gatewayConfig;
22
- constructor(gatewayConfig) {
22
+ runtimeContext;
23
+ constructor(gatewayConfig, runtimeContext = {}) {
23
24
  this.gatewayConfig = gatewayConfig;
25
+ this.runtimeContext = runtimeContext;
24
26
  }
25
27
  /**
26
28
  * Check if fallback is available (gateway config has at least one model).
@@ -222,6 +224,14 @@ var FallbackLlmClient = class {
222
224
  if (model.providerConfig.api === "anthropic-messages") {
223
225
  return await this.callAnthropic(effectiveConfig, model.modelId, messages, options);
224
226
  }
227
+ if (model.providerConfig.api === "openai-responses" || model.providerConfig.api === "openai-codex-responses" || model.providerConfig.api === "azure-openai-responses") {
228
+ return await this.callOpenAIResponses(
229
+ effectiveConfig,
230
+ model.modelId,
231
+ messages,
232
+ options
233
+ );
234
+ }
225
235
  return await this.callOpenAI(
226
236
  effectiveConfig,
227
237
  model.modelId,
@@ -246,7 +256,8 @@ var FallbackLlmClient = class {
246
256
  api: model.providerConfig.api,
247
257
  baseUrl: model.providerConfig.baseUrl
248
258
  },
249
- cfg: this.gatewayConfig
259
+ cfg: this.gatewayConfig,
260
+ workspaceDir: this.runtimeContext.workspaceDir
250
261
  });
251
262
  if (result?.apiKey || result?.baseUrl) {
252
263
  log.debug(
@@ -266,7 +277,12 @@ var FallbackLlmClient = class {
266
277
  * secret refs, etc.). Used as fallback when gateway runtime auth isn't available.
267
278
  */
268
279
  async resolveFallbackApiKey(model) {
269
- return resolveProviderApiKey(model.providerId, model.providerConfig.apiKey, this.gatewayConfig);
280
+ return resolveProviderApiKey(
281
+ model.providerId,
282
+ model.providerConfig.apiKey,
283
+ this.gatewayConfig,
284
+ this.runtimeContext.agentDir
285
+ );
270
286
  }
271
287
  /**
272
288
  * Call OpenAI-compatible API.
@@ -314,11 +330,65 @@ var FallbackLlmClient = class {
314
330
  } : void 0
315
331
  };
316
332
  }
333
+ /**
334
+ * Call an OpenAI-compatible Responses API.
335
+ */
336
+ async callOpenAIResponses(config, modelId, messages, options) {
337
+ const base = config.baseUrl.replace(/\/$/, "");
338
+ const url = base.endsWith("/v1") ? `${base}/responses` : `${base}/v1/responses`;
339
+ const headers = {
340
+ "Content-Type": "application/json",
341
+ ...config.headers
342
+ };
343
+ if (config.apiKey && typeof config.apiKey === "string" && config.authHeader !== false) {
344
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
345
+ }
346
+ const instructions = messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
347
+ const input = messages.filter((message) => message.role !== "system").map((message) => ({
348
+ role: message.role,
349
+ content: [{
350
+ type: message.role === "assistant" ? "output_text" : "input_text",
351
+ text: message.content
352
+ }]
353
+ }));
354
+ const body = {
355
+ model: modelId,
356
+ input,
357
+ max_output_tokens: Math.max(0, Math.floor(options.maxTokens ?? 4096)),
358
+ temperature: options.temperature ?? 0.3
359
+ };
360
+ if (instructions.length > 0) {
361
+ body.instructions = instructions;
362
+ }
363
+ const response = await fetch(url, {
364
+ method: "POST",
365
+ headers,
366
+ body: JSON.stringify(body)
367
+ });
368
+ if (!response.ok) {
369
+ const error = await response.text();
370
+ throw new Error(`OpenAI Responses API error: ${response.status} ${error}`);
371
+ }
372
+ const data = await response.json();
373
+ const outputText = extractResponsesOutputText(data);
374
+ if (!outputText) {
375
+ throw new Error("Empty response from OpenAI Responses API");
376
+ }
377
+ return {
378
+ content: outputText,
379
+ usage: data.usage ? {
380
+ inputTokens: data.usage.input_tokens,
381
+ outputTokens: data.usage.output_tokens,
382
+ totalTokens: data.usage.total_tokens
383
+ } : void 0
384
+ };
385
+ }
317
386
  /**
318
387
  * Call Anthropic Messages API.
319
388
  */
320
389
  async callAnthropic(config, modelId, messages, options) {
321
- const url = `${config.baseUrl.replace(/\/$/, "")}/messages`;
390
+ const base = config.baseUrl.replace(/\/$/, "");
391
+ const url = base.endsWith("/v1") ? `${base}/messages` : `${base}/v1/messages`;
322
392
  const headers = {
323
393
  "Content-Type": "application/json",
324
394
  "anthropic-version": "2023-06-01",
@@ -366,8 +436,26 @@ var FallbackLlmClient = class {
366
436
  };
367
437
  }
368
438
  };
439
+ function extractResponsesOutputText(data) {
440
+ if (typeof data.output_text === "string" && data.output_text.trim().length > 0) {
441
+ return data.output_text;
442
+ }
443
+ const chunks = [];
444
+ for (const item of data.output ?? []) {
445
+ if (typeof item.text === "string" && item.text.trim().length > 0) {
446
+ chunks.push(item.text);
447
+ }
448
+ for (const part of item.content ?? []) {
449
+ if ((part.type === "output_text" || part.type === "text") && typeof part.text === "string" && part.text.trim().length > 0) {
450
+ chunks.push(part.text);
451
+ }
452
+ }
453
+ }
454
+ const joined = chunks.join("\n").trim();
455
+ return joined.length > 0 ? joined : null;
456
+ }
369
457
 
370
458
  export {
371
459
  FallbackLlmClient
372
460
  };
373
- //# sourceMappingURL=chunk-QKAH5B6E.js.map
461
+ //# sourceMappingURL=chunk-44ICJRF3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fallback-llm.ts"],"sourcesContent":["import { log } from \"./logger.js\";\nimport type { GatewayConfig, ModelProviderConfig, AgentPersona } from \"./types.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport {\n buildChatCompletionTokenLimit,\n shouldAssumeOpenAiChatCompletions,\n} from \"./openai-chat-compat.js\";\nimport { resolveProviderApiKey, getGatewayRuntimeAuthForModel } from \"./resolve-provider-secret.js\";\nimport { loadModelsJsonProviders } from \"./models-json.js\";\n\nexport interface FallbackLlmOptions {\n temperature?: number;\n maxTokens?: number;\n timeoutMs?: number;\n /** Override which agent persona's model chain to use (by ID from agents.list[]). */\n agentId?: string;\n}\n\nexport interface FallbackLlmResponse {\n content: string;\n modelUsed: string;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n}\n\nexport interface FallbackLlmRuntimeContext {\n agentDir?: string;\n workspaceDir?: string;\n}\n\ninterface ModelRef {\n providerId: string;\n modelId: string;\n providerConfig: ModelProviderConfig;\n modelString: string;\n}\n\n/**\n * Generic fallback LLM client that uses the gateway's default AI configuration\n * and walks through the full fallback chain (primary + fallbacks).\n * Supports OpenAI and Anthropic API formats.\n */\nexport class FallbackLlmClient {\n private gatewayConfig: GatewayConfig | undefined;\n private runtimeContext: FallbackLlmRuntimeContext;\n\n constructor(\n gatewayConfig?: GatewayConfig,\n runtimeContext: FallbackLlmRuntimeContext = {},\n ) {\n this.gatewayConfig = gatewayConfig;\n this.runtimeContext = runtimeContext;\n }\n\n /**\n * Check if fallback is available (gateway config has at least one model).\n */\n isAvailable(agentId?: string): boolean {\n const models = this.getModelChain(agentId);\n return models.length > 0;\n }\n\n /**\n * Make a chat completion request using the gateway's default AI chain.\n * Tries primary first, then each fallback in order.\n * When agentId is provided, uses that agent persona's model chain instead of defaults.\n */\n async chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions = {},\n ): Promise<FallbackLlmResponse | null> {\n const models = this.getModelChain(options.agentId);\n if (models.length === 0) {\n log.warn(\"fallback LLM: no models configured in gateway\");\n return null;\n }\n\n const runChain = async (): Promise<FallbackLlmResponse | null> => {\n // Try each model in the chain\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isFallback = i > 0;\n\n try {\n const result = await this.tryModel(model, messages, options);\n if (result) {\n if (isFallback) {\n log.debug(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);\n }\n return {\n content: result.content,\n modelUsed: model.modelString,\n usage: result.usage,\n };\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);\n // Continue to next model in chain\n }\n }\n\n log.warn(`fallback LLM: all ${models.length} models in chain failed`);\n return null;\n };\n\n if (typeof options.timeoutMs === \"number\") {\n if (options.timeoutMs <= 0) {\n log.warn(\"fallback LLM: timed out before request started\");\n return null;\n }\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runChain(),\n new Promise<null>((resolve) => {\n timeoutHandle = setTimeout(() => {\n log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);\n resolve(null);\n }, options.timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n }\n\n return await runChain();\n }\n\n /**\n * Make a request with structured output (Zod schema).\n * Returns parsed JSON or null on failure.\n */\n async parseWithSchema<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<T | null> {\n const detailed = await this.parseWithSchemaDetailed(messages, schema, options);\n return detailed?.result ?? null;\n }\n\n /**\n * Like parseWithSchema but also returns the model that was used,\n * so callers can emit accurate trace events.\n */\n async parseWithSchemaDetailed<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<{ result: T; modelUsed: string } | null> {\n const response = await this.chatCompletion(messages, options);\n if (!response?.content) return null;\n\n try {\n const candidates = extractJsonCandidates(response.content);\n for (const c of candidates) {\n try {\n const parsed = JSON.parse(c);\n return { result: schema.parse(parsed), modelUsed: response.modelUsed };\n } catch {\n // keep trying other candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"fallback LLM: failed to parse structured output:\", err);\n return null;\n }\n }\n\n /**\n * Get the full model chain from gateway config.\n * Returns array of models in order: [primary, fallback1, fallback2, ...]\n *\n * When agentId is provided, looks up the matching entry in agents.list[]\n * and uses that persona's model chain. Falls back to agents.defaults.model\n * if agentId is not found or not provided.\n */\n private getModelChain(agentId?: string): ModelRef[] {\n const chain: ModelRef[] = [];\n const providers = this.gatewayConfig?.models?.providers ?? {};\n\n // Resolve the model config: agent persona chain or global defaults\n let modelConfig: { primary?: string; fallbacks?: string[] } | undefined;\n\n if (agentId) {\n const persona = this.gatewayConfig?.agents?.list?.find(\n (a) => a.id === agentId,\n );\n if (persona?.model) {\n modelConfig = persona.model;\n log.debug(`fallback LLM: using agent persona \"${agentId}\" model chain`);\n } else {\n log.warn(\n `fallback LLM: agent persona \"${agentId}\" not found or has no model config, falling back to defaults`,\n );\n }\n }\n\n if (!modelConfig) {\n modelConfig = this.gatewayConfig?.agents?.defaults?.model;\n }\n\n // Build list of model strings: primary + fallbacks\n const modelStrings: string[] = [];\n\n if (modelConfig?.primary) {\n modelStrings.push(modelConfig.primary);\n }\n\n if (Array.isArray(modelConfig?.fallbacks)) {\n for (const fb of modelConfig.fallbacks) {\n if (typeof fb === \"string\" && !modelStrings.includes(fb)) {\n modelStrings.push(fb);\n }\n }\n }\n\n // Parse each model string and look up provider config\n for (const modelString of modelStrings) {\n const modelRef = this.parseModelString(modelString, providers);\n if (modelRef) {\n chain.push(modelRef);\n }\n }\n\n return chain;\n }\n\n /**\n * Parse a \"provider/model\" string and look up its config.\n */\n private parseModelString(\n modelString: string,\n providers: Record<string, ModelProviderConfig>,\n ): ModelRef | null {\n // Parse \"provider/model\" format (e.g., \"openai/gpt-5.2\", \"anthropic/claude-opus-4-6\")\n const parts = modelString.split(\"/\");\n if (parts.length < 2) {\n log.warn(`fallback LLM: invalid model format: ${modelString}`);\n return null;\n }\n\n const providerId = parts[0];\n const modelId = parts.slice(1).join(\"/\"); // Handle cases like \"openai/gpt-5.2-turbo\"\n\n // Respect the active gateway config first so profile-local overrides and\n // credentials win. Fall back to the materialized models.json only when\n // the provider is absent from the loaded config (for built-in providers\n // registered by the gateway at runtime).\n const providerConfig = providers[providerId] ?? this.resolveFromModelsJson(providerId);\n if (!providerConfig) {\n log.warn(`fallback LLM: provider not found: ${providerId}`);\n return null;\n }\n\n return { providerId, modelId, providerConfig, modelString };\n }\n\n /**\n * Look up a provider from the gateway's materialized models.json, which\n * contains all providers including built-in ones (openai-codex, google-vertex,\n * etc.) that aren't in the user's openclaw.json but are registered by\n * gateway plugins. Returns null if the provider isn't found there either.\n */\n private resolveFromModelsJson(providerId: string): ModelProviderConfig | null {\n const allProviders = loadModelsJsonProviders();\n const config = allProviders[providerId];\n if (config) {\n log.debug(`fallback LLM: resolved provider \"${providerId}\" from models.json (api: ${config.api ?? \"default\"})`);\n return config;\n }\n return null;\n }\n\n /**\n * Try to call a single model.\n *\n * Uses the gateway's native getRuntimeAuthForModel when available — this\n * handles all provider-specific auth transforms (OAuth token exchange,\n * base URL overrides for codex/copilot/etc.) through the same codepath\n * the gateway itself uses. Falls back to resolveProviderApiKey for\n * simpler providers or when the runtime module isn't loaded.\n */\n private async tryModel(\n model: ModelRef,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n // Try the gateway's native runtime auth first — it handles all provider-\n // specific transforms (OAuth exchange, base URL rewrite, etc.)\n const runtimeAuth = await this.resolveRuntimeAuth(model);\n const effectiveBaseUrl = runtimeAuth?.baseUrl ?? model.providerConfig.baseUrl;\n const resolvedApiKey = runtimeAuth?.apiKey ?? await this.resolveFallbackApiKey(model);\n\n // If the raw key looks like an unresolved secret ref and resolution fails,\n // skip this provider entirely so the chain falls through to the next.\n const rawKey = model.providerConfig.apiKey;\n const needsResolution = rawKey === \"secretref-managed\"\n || (typeof rawKey === \"object\" && rawKey !== null);\n if (needsResolution && !resolvedApiKey) {\n throw new Error(`API key for provider \"${model.providerId}\" could not be resolved from secret ref`);\n }\n\n const effectiveConfig: ModelProviderConfig = {\n ...model.providerConfig,\n baseUrl: effectiveBaseUrl,\n ...(resolvedApiKey ? { apiKey: resolvedApiKey } : {}),\n };\n\n if (model.providerConfig.api === \"anthropic-messages\") {\n return await this.callAnthropic(effectiveConfig, model.modelId, messages, options);\n }\n\n if (\n model.providerConfig.api === \"openai-responses\" ||\n model.providerConfig.api === \"openai-codex-responses\" ||\n model.providerConfig.api === \"azure-openai-responses\"\n ) {\n return await this.callOpenAIResponses(\n effectiveConfig,\n model.modelId,\n messages,\n options,\n );\n }\n\n // For OpenAI-compatible chat-completions APIs (openai-completions,\n // ollama, etc.) and unknown formats, use chat completions — the gateway's\n // runtime auth resolver returns request-ready base URL and credentials for\n // most providers.\n return await this.callOpenAI(\n effectiveConfig,\n model.modelId,\n messages,\n options,\n shouldAssumeOpenAiChatCompletions(effectiveConfig.baseUrl),\n );\n }\n\n /**\n * Resolve request-ready auth through the gateway's native runtime, which\n * handles provider-specific transforms (OAuth token exchange for codex/copilot,\n * base URL rewrite, etc.). Returns null if the runtime isn't available.\n */\n private async resolveRuntimeAuth(\n model: ModelRef,\n ): Promise<{ apiKey?: string; baseUrl?: string } | null> {\n try {\n const getRuntimeAuth = await getGatewayRuntimeAuthForModel();\n if (!getRuntimeAuth) return null;\n\n const result = await getRuntimeAuth({\n model: {\n provider: model.providerId,\n id: model.modelId,\n api: model.providerConfig.api,\n baseUrl: model.providerConfig.baseUrl,\n },\n cfg: this.gatewayConfig,\n workspaceDir: this.runtimeContext.workspaceDir,\n });\n\n if (result?.apiKey || result?.baseUrl) {\n log.debug(\n `fallback LLM: resolved runtime auth for \"${model.modelString}\" (source: ${result.source ?? \"unknown\"}, mode: ${result.mode ?? \"unknown\"})`,\n );\n return { apiKey: result.apiKey, baseUrl: result.baseUrl };\n }\n } catch (err) {\n log.debug(\n `fallback LLM: gateway runtime auth failed for \"${model.modelString}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n return null;\n }\n\n /**\n * Resolve API key through the existing provider-level resolution (env vars,\n * secret refs, etc.). Used as fallback when gateway runtime auth isn't available.\n */\n private async resolveFallbackApiKey(model: ModelRef): Promise<string | undefined> {\n return resolveProviderApiKey(\n model.providerId,\n model.providerConfig.apiKey,\n this.gatewayConfig,\n this.runtimeContext.agentDir,\n );\n }\n\n /**\n * Call OpenAI-compatible API.\n */\n private async callOpenAI(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n assumeOpenAI: boolean,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/chat/completions`\n : `${base}/v1/chat/completions`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n\n // Handle auth — apiKey is already resolved to a string by tryModel()\n if (config.apiKey && typeof config.apiKey === \"string\") {\n if (config.authHeader !== false) {\n headers[\"Authorization\"] = `Bearer ${config.apiKey}`;\n }\n }\n\n const body = {\n model: modelId,\n messages,\n temperature: options.temperature ?? 0.3,\n ...buildChatCompletionTokenLimit(modelId, options.maxTokens ?? 4096, {\n assumeOpenAI,\n }),\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error(\"Empty response from OpenAI API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Call an OpenAI-compatible Responses API.\n */\n private async callOpenAIResponses(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/responses`\n : `${base}/v1/responses`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n\n if (config.apiKey && typeof config.apiKey === \"string\" && config.authHeader !== false) {\n headers[\"Authorization\"] = `Bearer ${config.apiKey}`;\n }\n\n const instructions = messages\n .filter((message) => message.role === \"system\")\n .map((message) => message.content)\n .join(\"\\n\\n\")\n .trim();\n const input = messages\n .filter((message) => message.role !== \"system\")\n .map((message) => ({\n role: message.role,\n content: [{\n type: message.role === \"assistant\" ? \"output_text\" : \"input_text\",\n text: message.content,\n }],\n }));\n\n const body: Record<string, unknown> = {\n model: modelId,\n input,\n max_output_tokens: Math.max(0, Math.floor(options.maxTokens ?? 4096)),\n temperature: options.temperature ?? 0.3,\n };\n if (instructions.length > 0) {\n body.instructions = instructions;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI Responses API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n output_text?: string;\n output?: Array<{\n type?: string;\n text?: string;\n content?: Array<{\n type?: string;\n text?: string;\n }>;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n };\n };\n\n const outputText = extractResponsesOutputText(data);\n if (!outputText) {\n throw new Error(\"Empty response from OpenAI Responses API\");\n }\n\n return {\n content: outputText,\n usage: data.usage\n ? {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Call Anthropic Messages API.\n */\n private async callAnthropic(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/messages`\n : `${base}/v1/messages`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n ...config.headers,\n };\n\n // Handle auth - Anthropic uses x-api-key header (apiKey resolved by tryModel)\n if (config.apiKey && typeof config.apiKey === \"string\") {\n headers[\"x-api-key\"] = config.apiKey;\n }\n\n // Extract system message (Anthropic handles it separately)\n const systemMessage = messages.find((m) => m.role === \"system\")?.content;\n const nonSystemMessages = messages.filter((m) => m.role !== \"system\");\n\n // Convert messages to Anthropic format\n const anthropicMessages = nonSystemMessages.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const body: Record<string, unknown> = {\n model: modelId,\n messages: anthropicMessages,\n max_tokens: options.maxTokens ?? 4096,\n temperature: options.temperature ?? 0.3,\n };\n\n if (systemMessage) {\n body.system = systemMessage;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n content: Array<{\n type: string;\n text: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const content = data.content?.[0]?.text;\n if (!content) {\n throw new Error(\"Empty response from Anthropic API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: (data.usage.input_tokens ?? 0) + (data.usage.output_tokens ?? 0),\n }\n : undefined,\n };\n }\n}\n\nfunction extractResponsesOutputText(data: {\n output_text?: string;\n output?: Array<{\n type?: string;\n text?: string;\n content?: Array<{\n type?: string;\n text?: string;\n }>;\n }>;\n}): string | null {\n if (typeof data.output_text === \"string\" && data.output_text.trim().length > 0) {\n return data.output_text;\n }\n\n const chunks: string[] = [];\n for (const item of data.output ?? []) {\n if (typeof item.text === \"string\" && item.text.trim().length > 0) {\n chunks.push(item.text);\n }\n for (const part of item.content ?? []) {\n if (\n (part.type === \"output_text\" || part.type === \"text\") &&\n typeof part.text === \"string\" &&\n part.text.trim().length > 0\n ) {\n chunks.push(part.text);\n }\n }\n }\n\n const joined = chunks.join(\"\\n\").trim();\n return joined.length > 0 ? joined : null;\n}\n"],"mappings":"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","names":[]}
@@ -22,6 +22,14 @@ function clampGraphRecallExpandedEntries(entries, maxEntries = 64) {
22
22
  };
23
23
  }).filter((item) => item.path.length > 0 && item.namespace.length > 0).slice(0, limit);
24
24
  }
25
+ function cloneTierExplain(tierExplain) {
26
+ if (!tierExplain) return void 0;
27
+ return structuredClone(tierExplain);
28
+ }
29
+ function cloneLastRecallSnapshot(snapshot) {
30
+ if (!snapshot) return null;
31
+ return structuredClone(snapshot);
32
+ }
25
33
  var DEFAULT_TIER_MIGRATION_STATUS = {
26
34
  updatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
27
35
  lastCycle: null,
@@ -52,13 +60,17 @@ var LastRecallStore = class {
52
60
  }
53
61
  }
54
62
  get(sessionKey) {
55
- return this.state[sessionKey] ?? null;
63
+ return cloneLastRecallSnapshot(this.state[sessionKey] ?? null);
56
64
  }
57
65
  getMostRecent() {
58
66
  const snapshots = Object.values(this.state);
59
67
  if (snapshots.length === 0) return null;
60
- snapshots.sort((a, b) => b.recordedAt.localeCompare(a.recordedAt));
61
- return snapshots[0] ?? null;
68
+ snapshots.sort((a, b) => {
69
+ const byTime = b.recordedAt.localeCompare(a.recordedAt);
70
+ if (byTime !== 0) return byTime;
71
+ return a.sessionKey.localeCompare(b.sessionKey);
72
+ });
73
+ return cloneLastRecallSnapshot(snapshots[0] ?? null);
62
74
  }
63
75
  /**
64
76
  * Persist last-recall snapshot and append an impression log entry.
@@ -67,7 +79,7 @@ var LastRecallStore = class {
67
79
  async record(opts) {
68
80
  const now = (/* @__PURE__ */ new Date()).toISOString();
69
81
  const queryHash = createHash("sha256").update(opts.query).digest("hex");
70
- const snapshot = {
82
+ const liveSnapshot = {
71
83
  sessionKey: opts.sessionKey,
72
84
  recordedAt: now,
73
85
  queryHash,
@@ -79,15 +91,17 @@ var LastRecallStore = class {
79
91
  requestedMode: opts.requestedMode,
80
92
  source: opts.source,
81
93
  fallbackUsed: opts.fallbackUsed,
82
- sourcesUsed: opts.sourcesUsed ? [...opts.sourcesUsed] : void 0,
83
- budgetsApplied: opts.budgetsApplied ? { ...opts.budgetsApplied } : void 0,
94
+ sourcesUsed: opts.sourcesUsed,
95
+ budgetsApplied: opts.budgetsApplied,
84
96
  latencyMs: opts.latencyMs,
85
- resultPaths: opts.resultPaths ? [...opts.resultPaths] : void 0,
97
+ resultPaths: opts.resultPaths,
86
98
  policyVersion: opts.policyVersion,
87
99
  identityInjectionMode: opts.identityInjection?.mode,
88
100
  identityInjectedChars: opts.identityInjection?.injectedChars,
89
- identityInjectionTruncated: opts.identityInjection?.truncated
101
+ identityInjectionTruncated: opts.identityInjection?.truncated,
102
+ tierExplain: opts.tierExplain
90
103
  };
104
+ const snapshot = cloneLastRecallSnapshot(liveSnapshot);
91
105
  this.state[opts.sessionKey] = snapshot;
92
106
  const keys = Object.keys(this.state);
93
107
  if (keys.length > 50) {
@@ -111,6 +125,31 @@ var LastRecallStore = class {
111
125
  }
112
126
  }
113
127
  }
128
+ /**
129
+ * Attach a RecallTierExplain block to the existing snapshot for a
130
+ * session without rewriting the entire snapshot. Used by the
131
+ * post-recall direct-answer annotation path (issue #518 slice 3c):
132
+ * recallInternal records the snapshot first, then the orchestrator
133
+ * fires the direct-answer tier in observation mode and annotates
134
+ * the stored snapshot with whichever tier served the query.
135
+ *
136
+ * No-op when no snapshot exists for the given session; callers do
137
+ * not need to guard on existence.
138
+ */
139
+ async annotateTierExplain(sessionKey, tierExplain) {
140
+ const current = this.state[sessionKey];
141
+ if (!current) return;
142
+ this.state[sessionKey] = {
143
+ ...current,
144
+ tierExplain: cloneTierExplain(tierExplain)
145
+ };
146
+ try {
147
+ await mkdir(path.dirname(this.statePath), { recursive: true });
148
+ await writeFile(this.statePath, JSON.stringify(this.state, null, 2), "utf-8");
149
+ } catch (err) {
150
+ log.debug(`last recall tier-explain annotate failed: ${err}`);
151
+ }
152
+ }
114
153
  };
115
154
  var TierMigrationStatusStore = class {
116
155
  statePath;
@@ -182,4 +221,4 @@ export {
182
221
  LastRecallStore,
183
222
  TierMigrationStatusStore
184
223
  };
185
- //# sourceMappingURL=chunk-S4LX5EBI.js.map
224
+ //# sourceMappingURL=chunk-47UU5PU2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recall-state.ts"],"sourcesContent":["import { appendFile, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { log } from \"./logger.js\";\nimport type {\n IdentityInjectionMode,\n RecallPlanMode,\n RecallTierExplain,\n} from \"./types.js\";\n\nexport interface LastRecallBudgetSummary {\n requestedTopK?: number;\n appliedTopK: number;\n recallBudgetChars: number;\n maxMemoryTokens: number;\n qmdFetchLimit?: number;\n qmdHybridFetchLimit?: number;\n finalContextChars?: number;\n truncated?: boolean;\n includedSections?: string[];\n omittedSections?: string[];\n}\n\nexport interface LastRecallSnapshot {\n sessionKey: string;\n recordedAt: string;\n queryHash: string;\n queryLen: number;\n memoryIds: string[];\n namespace?: string;\n traceId?: string;\n plannerMode?: RecallPlanMode;\n requestedMode?: RecallPlanMode;\n source?: string;\n fallbackUsed?: boolean;\n sourcesUsed?: string[];\n budgetsApplied?: LastRecallBudgetSummary;\n latencyMs?: number;\n resultPaths?: string[];\n policyVersion?: string;\n identityInjectionMode?: IdentityInjectionMode | \"none\";\n identityInjectedChars?: number;\n identityInjectionTruncated?: boolean;\n /**\n * Optional tier-level explanation of how recall was served\n * (issue #518). Populated by orchestrator call sites that can\n * identify a concrete tier; surfaces expose the block via\n * `engram query --explain`, the `?explain=1` HTTP flag, and the\n * `remnic_recall_explain` MCP tool. Orthogonal to the existing\n * graph-path `recallExplain` operation.\n */\n tierExplain?: RecallTierExplain;\n}\n\nexport interface GraphRecallExpandedEntry {\n path: string;\n score: number;\n namespace: string;\n seed: string;\n hopDepth: number;\n decayedWeight: number;\n graphType: \"entity\" | \"time\" | \"causal\";\n}\n\nexport function clampGraphRecallExpandedEntries(\n entries: unknown,\n maxEntries: number = 64,\n): GraphRecallExpandedEntry[] {\n const limit = Math.max(1, Math.floor(maxEntries));\n if (!Array.isArray(entries)) return [];\n return entries\n .filter((item): item is Record<string, unknown> => !!item && typeof item === \"object\")\n .map((item) => {\n const graphType: \"entity\" | \"time\" | \"causal\" =\n item.graphType === \"entity\" || item.graphType === \"time\" || item.graphType === \"causal\"\n ? item.graphType\n : \"entity\";\n return {\n path: typeof item.path === \"string\" ? item.path : \"\",\n score: typeof item.score === \"number\" && Number.isFinite(item.score) ? item.score : 0,\n namespace: typeof item.namespace === \"string\" ? item.namespace : \"\",\n seed: typeof item.seed === \"string\" ? item.seed : \"\",\n hopDepth:\n typeof item.hopDepth === \"number\" && Number.isFinite(item.hopDepth)\n ? Math.max(0, Math.floor(item.hopDepth))\n : 0,\n decayedWeight:\n typeof item.decayedWeight === \"number\" && Number.isFinite(item.decayedWeight)\n ? Math.max(0, item.decayedWeight)\n : 0,\n graphType,\n };\n })\n .filter((item) => item.path.length > 0 && item.namespace.length > 0)\n .slice(0, limit);\n}\n\ntype LastRecallState = Record<string, LastRecallSnapshot>;\n\n/**\n * Deep-copy a RecallTierExplain block. Used by both the write path\n * (so caller mutation after `record()` cannot tear the persisted\n * snapshot) and the read path (so caller mutation after `get()` /\n * `getMostRecent()` cannot tear the in-memory store).\n *\n * Uses structuredClone so future additions to RecallTierExplain do\n * not silently share references through hand-enumerated fields —\n * matching the pattern used elsewhere in the codebase (e.g.,\n * qmd-recall-cache.ts). The payload is pure JSON-shaped data, so\n * structuredClone is both safe and complete here.\n */\nfunction cloneTierExplain(\n tierExplain: RecallTierExplain | undefined,\n): RecallTierExplain | undefined {\n if (!tierExplain) return undefined;\n return structuredClone(tierExplain);\n}\n\n/**\n * Deep-copy a LastRecallSnapshot so callers that receive it cannot\n * mutate the store's internal state through mutable array/object\n * fields. Same structuredClone rationale as cloneTierExplain above.\n */\nfunction cloneLastRecallSnapshot(\n snapshot: LastRecallSnapshot | null,\n): LastRecallSnapshot | null {\n if (!snapshot) return null;\n return structuredClone(snapshot);\n}\n\nexport interface TierMigrationCycleSummary {\n trigger: \"extraction\" | \"maintenance\" | \"manual\";\n scanned: number;\n migrated: number;\n promoted: number;\n demoted: number;\n limit: number;\n dryRun: boolean;\n skipped?: string;\n errorCount?: number;\n}\n\nexport interface TierMigrationStatusSnapshot {\n updatedAt: string;\n lastCycle: TierMigrationCycleSummary | null;\n totals: {\n cycles: number;\n scanned: number;\n migrated: number;\n promoted: number;\n demoted: number;\n errors: number;\n };\n}\n\nconst DEFAULT_TIER_MIGRATION_STATUS: TierMigrationStatusSnapshot = {\n updatedAt: new Date(0).toISOString(),\n lastCycle: null,\n totals: {\n cycles: 0,\n scanned: 0,\n migrated: 0,\n promoted: 0,\n demoted: 0,\n errors: 0,\n },\n};\n\nexport class LastRecallStore {\n private readonly statePath: string;\n private readonly impressionsPath: string;\n private state: LastRecallState = {};\n\n constructor(memoryDir: string) {\n this.statePath = path.join(memoryDir, \"state\", \"last_recall.json\");\n this.impressionsPath = path.join(memoryDir, \"state\", \"recall_impressions.jsonl\");\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.statePath, \"utf-8\");\n const parsed = JSON.parse(raw) as LastRecallState;\n if (parsed && typeof parsed === \"object\") this.state = parsed;\n } catch {\n this.state = {};\n }\n }\n\n get(sessionKey: string): LastRecallSnapshot | null {\n // Defensive copy: callers must not be able to mutate internal state\n // by reaching into array/object fields on the returned snapshot.\n return cloneLastRecallSnapshot(this.state[sessionKey] ?? null);\n }\n\n getMostRecent(): LastRecallSnapshot | null {\n const snapshots = Object.values(this.state);\n if (snapshots.length === 0) return null;\n // Secondary key on sessionKey keeps the sort stable when two\n // snapshots share a recordedAt timestamp (CLAUDE.md rule 19).\n snapshots.sort((a, b) => {\n const byTime = b.recordedAt.localeCompare(a.recordedAt);\n if (byTime !== 0) return byTime;\n return a.sessionKey.localeCompare(b.sessionKey);\n });\n return cloneLastRecallSnapshot(snapshots[0] ?? null);\n }\n\n /**\n * Persist last-recall snapshot and append an impression log entry.\n * Does not store raw query text; uses a stable hash for correlation.\n */\n async record(opts: {\n sessionKey: string;\n query: string;\n memoryIds: string[];\n namespace?: string;\n traceId?: string;\n plannerMode?: RecallPlanMode;\n requestedMode?: RecallPlanMode;\n source?: string;\n fallbackUsed?: boolean;\n sourcesUsed?: string[];\n budgetsApplied?: LastRecallBudgetSummary;\n latencyMs?: number;\n resultPaths?: string[];\n policyVersion?: string;\n appendImpression?: boolean;\n identityInjection?: {\n mode: IdentityInjectionMode | \"none\";\n injectedChars: number;\n truncated: boolean;\n };\n /**\n * Per-tier explain annotation (issue #518). When supplied, the\n * snapshot carries it so downstream surfaces (CLI / HTTP / MCP)\n * can render which retrieval tier served the query.\n */\n tierExplain?: RecallTierExplain;\n }): Promise<void> {\n const now = new Date().toISOString();\n const queryHash = createHash(\"sha256\").update(opts.query).digest(\"hex\");\n\n // Build the snapshot from opts, then deep-copy it via\n // cloneLastRecallSnapshot so caller arrays/objects passed in\n // `opts` cannot retain a live reference to the persisted state\n // and tear it after record() returns.\n const liveSnapshot: LastRecallSnapshot = {\n sessionKey: opts.sessionKey,\n recordedAt: now,\n queryHash,\n queryLen: opts.query.length,\n memoryIds: opts.memoryIds,\n namespace: opts.namespace,\n traceId: opts.traceId,\n plannerMode: opts.plannerMode,\n requestedMode: opts.requestedMode,\n source: opts.source,\n fallbackUsed: opts.fallbackUsed,\n sourcesUsed: opts.sourcesUsed,\n budgetsApplied: opts.budgetsApplied,\n latencyMs: opts.latencyMs,\n resultPaths: opts.resultPaths,\n policyVersion: opts.policyVersion,\n identityInjectionMode: opts.identityInjection?.mode,\n identityInjectedChars: opts.identityInjection?.injectedChars,\n identityInjectionTruncated: opts.identityInjection?.truncated,\n tierExplain: opts.tierExplain,\n };\n // `cloneLastRecallSnapshot` handles `null` but that never applies\n // at this call site — the non-null assertion keeps the type\n // checker honest.\n const snapshot = cloneLastRecallSnapshot(liveSnapshot)!;\n\n this.state[opts.sessionKey] = snapshot;\n\n // Keep the state bounded; the impression log is append-only.\n const keys = Object.keys(this.state);\n if (keys.length > 50) {\n const ordered = keys\n .map((k) => ({ k, at: this.state[k]?.recordedAt ?? \"\" }))\n .sort((a, b) => b.at.localeCompare(a.at));\n for (const doomed of ordered.slice(50)) {\n delete this.state[doomed.k];\n }\n }\n\n try {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await writeFile(this.statePath, JSON.stringify(this.state, null, 2), \"utf-8\");\n } catch (err) {\n log.debug(`last recall store write failed: ${err}`);\n }\n\n if (opts.appendImpression !== false) {\n try {\n await mkdir(path.dirname(this.impressionsPath), { recursive: true });\n await appendFile(this.impressionsPath, JSON.stringify(snapshot) + \"\\n\", \"utf-8\");\n } catch (err) {\n log.debug(`recall impressions append failed: ${err}`);\n }\n }\n }\n\n /**\n * Attach a RecallTierExplain block to the existing snapshot for a\n * session without rewriting the entire snapshot. Used by the\n * post-recall direct-answer annotation path (issue #518 slice 3c):\n * recallInternal records the snapshot first, then the orchestrator\n * fires the direct-answer tier in observation mode and annotates\n * the stored snapshot with whichever tier served the query.\n *\n * No-op when no snapshot exists for the given session; callers do\n * not need to guard on existence.\n */\n async annotateTierExplain(\n sessionKey: string,\n tierExplain: RecallTierExplain,\n ): Promise<void> {\n const current = this.state[sessionKey];\n if (!current) return;\n this.state[sessionKey] = {\n ...current,\n tierExplain: cloneTierExplain(tierExplain),\n };\n try {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await writeFile(this.statePath, JSON.stringify(this.state, null, 2), \"utf-8\");\n } catch (err) {\n log.debug(`last recall tier-explain annotate failed: ${err}`);\n }\n }\n}\n\nexport class TierMigrationStatusStore {\n private readonly statePath: string;\n private state: TierMigrationStatusSnapshot = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n\n constructor(memoryDir: string) {\n this.statePath = path.join(memoryDir, \"state\", \"tier-migration-status.json\");\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.statePath, \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<TierMigrationStatusSnapshot> | null;\n if (!parsed || typeof parsed !== \"object\") {\n this.state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n return;\n }\n const totals = parsed.totals && typeof parsed.totals === \"object\"\n ? parsed.totals\n : DEFAULT_TIER_MIGRATION_STATUS.totals;\n this.state = {\n updatedAt:\n typeof parsed.updatedAt === \"string\" && parsed.updatedAt.length > 0\n ? parsed.updatedAt\n : DEFAULT_TIER_MIGRATION_STATUS.updatedAt,\n lastCycle:\n parsed.lastCycle && typeof parsed.lastCycle === \"object\"\n ? (parsed.lastCycle as TierMigrationCycleSummary)\n : null,\n totals: {\n cycles: typeof totals.cycles === \"number\" && Number.isFinite(totals.cycles) ? totals.cycles : 0,\n scanned: typeof totals.scanned === \"number\" && Number.isFinite(totals.scanned) ? totals.scanned : 0,\n migrated: typeof totals.migrated === \"number\" && Number.isFinite(totals.migrated) ? totals.migrated : 0,\n promoted: typeof totals.promoted === \"number\" && Number.isFinite(totals.promoted) ? totals.promoted : 0,\n demoted: typeof totals.demoted === \"number\" && Number.isFinite(totals.demoted) ? totals.demoted : 0,\n errors: typeof totals.errors === \"number\" && Number.isFinite(totals.errors) ? totals.errors : 0,\n },\n };\n } catch {\n this.state = structuredClone(DEFAULT_TIER_MIGRATION_STATUS);\n }\n }\n\n get(): TierMigrationStatusSnapshot {\n return {\n updatedAt: this.state.updatedAt,\n lastCycle: this.state.lastCycle ? { ...this.state.lastCycle } : null,\n totals: { ...this.state.totals },\n };\n }\n\n async recordCycle(summary: TierMigrationCycleSummary): Promise<void> {\n const now = new Date().toISOString();\n const migratedDelta = summary.dryRun ? 0 : Math.max(0, summary.migrated);\n const promotedDelta = summary.dryRun ? 0 : Math.max(0, summary.promoted);\n const demotedDelta = summary.dryRun ? 0 : Math.max(0, summary.demoted);\n const next: TierMigrationStatusSnapshot = {\n updatedAt: now,\n lastCycle: { ...summary },\n totals: {\n cycles: this.state.totals.cycles + 1,\n scanned: this.state.totals.scanned + Math.max(0, summary.scanned),\n migrated: this.state.totals.migrated + migratedDelta,\n promoted: this.state.totals.promoted + promotedDelta,\n demoted: this.state.totals.demoted + demotedDelta,\n errors: this.state.totals.errors + Math.max(0, summary.errorCount ?? 0),\n },\n };\n this.state = next;\n try {\n await mkdir(path.dirname(this.statePath), { recursive: true });\n await writeFile(this.statePath, JSON.stringify(next, null, 2), \"utf-8\");\n } catch (err) {\n log.debug(`tier migration status write failed: ${err}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-POMSFKTB.js";
3
+ } from "./chunk-GV6NLQ4X.js";
4
4
  import {
5
5
  log
6
6
  } from "./chunk-2ODBA7MQ.js";
@@ -810,4 +810,4 @@ export {
810
810
  resolveBriefingSaveDir,
811
811
  briefingFilename
812
812
  };
813
- //# sourceMappingURL=chunk-ECKDIK5F.js.map
813
+ //# sourceMappingURL=chunk-4LACOVZX.js.map
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-FYIYMQ5N.js";
16
16
  import {
17
17
  parseConfig
18
- } from "./chunk-OJFGVJS6.js";
18
+ } from "./chunk-MBJHSA7F.js";
19
19
  import {
20
20
  assertIsoRecordedAt,
21
21
  assertSafePathSegment,
@@ -268,4 +268,4 @@ export {
268
268
  recordResumeBundle,
269
269
  getResumeBundleStatus
270
270
  };
271
- //# sourceMappingURL=chunk-AYPYCLR7.js.map
271
+ //# sourceMappingURL=chunk-6LX5ORAS.js.map
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-DT5TVLJE.js";
8
8
  import {
9
9
  StorageManager
10
- } from "./chunk-POMSFKTB.js";
10
+ } from "./chunk-GV6NLQ4X.js";
11
11
  import {
12
12
  getCachedEpisodeMap,
13
13
  setCachedEpisodeMap
@@ -105,4 +105,4 @@ async function searchVerifiedEpisodes(options) {
105
105
  export {
106
106
  searchVerifiedEpisodes
107
107
  };
108
- //# sourceMappingURL=chunk-GJQPH5G3.js.map
108
+ //# sourceMappingURL=chunk-6UJ47TVX.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-POMSFKTB.js";
3
+ } from "./chunk-GV6NLQ4X.js";
4
4
 
5
5
  // src/semantic-rule-promotion.ts
6
6
  function normalizeRuleWhitespace(value) {
@@ -125,4 +125,4 @@ async function promoteSemanticRuleFromMemory(options) {
125
125
  export {
126
126
  promoteSemanticRuleFromMemory
127
127
  };
128
- //# sourceMappingURL=chunk-KWP7T3DP.js.map
128
+ //# sourceMappingURL=chunk-7ECD5ATE.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  compareEntityTimestamps,
6
6
  normalizeEntityName
7
- } from "./chunk-POMSFKTB.js";
7
+ } from "./chunk-GV6NLQ4X.js";
8
8
  import {
9
9
  sanitizeMemoryContent
10
10
  } from "./chunk-M62O4P4T.js";
@@ -553,4 +553,4 @@ export {
553
553
  entityIndexVersion,
554
554
  entityRecentTranscriptLookbackHours
555
555
  };
556
- //# sourceMappingURL=chunk-74JR4N5J.js.map
556
+ //# sourceMappingURL=chunk-7WQ6SLIE.js.map
@@ -1,6 +1,11 @@
1
1
  import {
2
2
  launchProcess
3
3
  } from "./chunk-LK6SGL53.js";
4
+ import {
5
+ abortError,
6
+ isAbortError,
7
+ throwIfAborted
8
+ } from "./chunk-PVGDJXVK.js";
4
9
  import {
5
10
  mergeEnv
6
11
  } from "./chunk-MARWOCVP.js";
@@ -46,14 +51,6 @@ function getGlobalQmdState() {
46
51
  }
47
52
  return g[QMD_GLOBAL_STATE_KEY];
48
53
  }
49
- function abortError(message) {
50
- const err = new Error(message);
51
- Object.defineProperty(err, "name", { value: "AbortError" });
52
- return err;
53
- }
54
- function isAbortError(err) {
55
- return err instanceof Error && err.name === "AbortError";
56
- }
57
54
  function errorMessage(err) {
58
55
  if (typeof err === "string") return err;
59
56
  if (err instanceof Error) return err.message;
@@ -74,11 +71,6 @@ function isCallerCancellation(err, signal) {
74
71
  function isDaemonTimeoutError(err) {
75
72
  return /timed out/i.test(errorMessage(err));
76
73
  }
77
- function throwIfAborted(signal, message = "operation aborted") {
78
- if (signal?.aborted) {
79
- throw abortError(message);
80
- }
81
- }
82
74
  function sleepWithSignal(ms, signal) {
83
75
  return new Promise((resolve, reject) => {
84
76
  throwIfAborted(signal);
@@ -1470,4 +1462,4 @@ export {
1470
1462
  parseQmdExplain,
1471
1463
  QmdClient
1472
1464
  };
1473
- //# sourceMappingURL=chunk-7PA4OZEU.js.map
1465
+ //# sourceMappingURL=chunk-BLKTA7MM.js.map