nlm-memory 0.5.21 → 0.6.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 (164) hide show
  1. package/README.md +129 -19
  2. package/dist/cli/classify-parity.js +1 -1
  3. package/dist/cli/classify-parity.js.map +1 -1
  4. package/dist/cli/nlm.js +152 -12
  5. package/dist/cli/nlm.js.map +1 -1
  6. package/dist/core/actions/overlay.d.ts +22 -0
  7. package/dist/core/actions/overlay.js +56 -0
  8. package/dist/core/actions/overlay.js.map +1 -1
  9. package/dist/core/adapters/pi.js +13 -3
  10. package/dist/core/adapters/pi.js.map +1 -1
  11. package/dist/core/classifier/prompt.d.ts +1 -1
  12. package/dist/core/classifier/prompt.js +1 -1
  13. package/dist/core/dataset/build-dataset.d.ts +14 -1
  14. package/dist/core/dataset/build-dataset.js +68 -11
  15. package/dist/core/dataset/build-dataset.js.map +1 -1
  16. package/dist/core/hook/miss-detect.d.ts +32 -0
  17. package/dist/core/hook/miss-detect.js +67 -0
  18. package/dist/core/hook/miss-detect.js.map +1 -0
  19. package/dist/core/hook/pointer-block.d.ts +12 -1
  20. package/dist/core/hook/pointer-block.js +25 -8
  21. package/dist/core/hook/pointer-block.js.map +1 -1
  22. package/dist/core/providers/provider-registry.js +1 -1
  23. package/dist/core/providers/provider-registry.js.map +1 -1
  24. package/dist/core/recall/miss-log.d.ts +47 -0
  25. package/dist/core/recall/miss-log.js +112 -0
  26. package/dist/core/recall/miss-log.js.map +1 -0
  27. package/dist/core/recall/query-log.d.ts +3 -0
  28. package/dist/core/recall/query-log.js +1 -0
  29. package/dist/core/recall/query-log.js.map +1 -1
  30. package/dist/core/recall/recall-service.d.ts +9 -0
  31. package/dist/core/recall/recall-service.js +81 -18
  32. package/dist/core/recall/recall-service.js.map +1 -1
  33. package/dist/core/recall/recency.d.ts +52 -0
  34. package/dist/core/recall/recency.js +98 -0
  35. package/dist/core/recall/recency.js.map +1 -0
  36. package/dist/core/recall/recent-log.d.ts +1 -0
  37. package/dist/core/recall/recent-log.js +1 -0
  38. package/dist/core/recall/recent-log.js.map +1 -1
  39. package/dist/core/recall/related-facts.d.ts +30 -0
  40. package/dist/core/recall/related-facts.js +134 -0
  41. package/dist/core/recall/related-facts.js.map +1 -0
  42. package/dist/core/recall/rewrite-cache.d.ts +25 -0
  43. package/dist/core/recall/rewrite-cache.js +60 -0
  44. package/dist/core/recall/rewrite-cache.js.map +1 -0
  45. package/dist/core/recall/rewrite-prompt.d.ts +23 -0
  46. package/dist/core/recall/rewrite-prompt.js +81 -0
  47. package/dist/core/recall/rewrite-prompt.js.map +1 -0
  48. package/dist/core/recall-facts/fact-recall-service.d.ts +11 -0
  49. package/dist/core/recall-facts/fact-recall-service.js +69 -4
  50. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  51. package/dist/core/scheduler/scheduler.d.ts +7 -0
  52. package/dist/core/scheduler/scheduler.js +26 -0
  53. package/dist/core/scheduler/scheduler.js.map +1 -1
  54. package/dist/core/signals/aggregate.d.ts +21 -0
  55. package/dist/core/signals/aggregate.js +34 -0
  56. package/dist/core/signals/aggregate.js.map +1 -0
  57. package/dist/core/signals/failure-mode-recall.d.ts +18 -0
  58. package/dist/core/signals/failure-mode-recall.js +28 -0
  59. package/dist/core/signals/failure-mode-recall.js.map +1 -0
  60. package/dist/core/signals/ingest-signal.d.ts +19 -0
  61. package/dist/core/signals/ingest-signal.js +63 -0
  62. package/dist/core/signals/ingest-signal.js.map +1 -0
  63. package/dist/core/signals/install-scope.d.ts +8 -0
  64. package/dist/core/signals/install-scope.js +41 -0
  65. package/dist/core/signals/install-scope.js.map +1 -0
  66. package/dist/core/signals/recommend.d.ts +13 -0
  67. package/dist/core/signals/recommend.js +24 -0
  68. package/dist/core/signals/recommend.js.map +1 -0
  69. package/dist/core/storage/pg-fact-store.d.ts +5 -0
  70. package/dist/core/storage/pg-fact-store.js +29 -0
  71. package/dist/core/storage/pg-fact-store.js.map +1 -1
  72. package/dist/core/storage/pg-session-store.js +16 -1
  73. package/dist/core/storage/pg-session-store.js.map +1 -1
  74. package/dist/core/storage/pg-signal-store.d.ts +16 -0
  75. package/dist/core/storage/pg-signal-store.js +100 -0
  76. package/dist/core/storage/pg-signal-store.js.map +1 -0
  77. package/dist/core/storage/pg-storage.d.ts +2 -0
  78. package/dist/core/storage/pg-storage.js +3 -0
  79. package/dist/core/storage/pg-storage.js.map +1 -1
  80. package/dist/core/storage/pg-tx-context.d.ts +5 -0
  81. package/dist/core/storage/pg-tx-context.js +3 -0
  82. package/dist/core/storage/pg-tx-context.js.map +1 -1
  83. package/dist/core/storage/sqlite-fact-store.d.ts +5 -0
  84. package/dist/core/storage/sqlite-fact-store.js +30 -0
  85. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  86. package/dist/core/storage/sqlite-session-store.d.ts +3 -0
  87. package/dist/core/storage/sqlite-session-store.js +21 -1
  88. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  89. package/dist/core/storage/sqlite-signal-store.d.ts +20 -0
  90. package/dist/core/storage/sqlite-signal-store.js +112 -0
  91. package/dist/core/storage/sqlite-signal-store.js.map +1 -0
  92. package/dist/core/storage/sqlite-storage.d.ts +2 -0
  93. package/dist/core/storage/sqlite-storage.js +6 -2
  94. package/dist/core/storage/sqlite-storage.js.map +1 -1
  95. package/dist/hook/pi-extension.d.ts +3 -1
  96. package/dist/hook/pi-extension.js +4 -2
  97. package/dist/hook/pi-extension.js.map +1 -1
  98. package/dist/hook/prompt-recall-hook.d.ts +10 -1
  99. package/dist/hook/prompt-recall-hook.js +11 -5
  100. package/dist/hook/prompt-recall-hook.js.map +1 -1
  101. package/dist/hook/recall-over-http.d.ts +11 -1
  102. package/dist/hook/recall-over-http.js +21 -6
  103. package/dist/hook/recall-over-http.js.map +1 -1
  104. package/dist/hook/session-start-hook.d.ts +2 -0
  105. package/dist/hook/session-start-hook.js +36 -3
  106. package/dist/hook/session-start-hook.js.map +1 -1
  107. package/dist/hook/stop-hook.js +16 -0
  108. package/dist/hook/stop-hook.js.map +1 -1
  109. package/dist/http/app.d.ts +9 -0
  110. package/dist/http/app.js +125 -10
  111. package/dist/http/app.js.map +1 -1
  112. package/dist/install/codex.d.ts +1 -1
  113. package/dist/install/codex.js +4 -4
  114. package/dist/install/codex.js.map +1 -1
  115. package/dist/install/ollama.d.ts +10 -0
  116. package/dist/install/ollama.js +19 -3
  117. package/dist/install/ollama.js.map +1 -1
  118. package/dist/install/pi.d.ts +1 -1
  119. package/dist/install/pi.js +1 -1
  120. package/dist/install/rules-content.d.ts +51 -0
  121. package/dist/install/rules-content.js +140 -0
  122. package/dist/install/rules-content.js.map +1 -0
  123. package/dist/install/rules-install.d.ts +35 -0
  124. package/dist/install/rules-install.js +79 -0
  125. package/dist/install/rules-install.js.map +1 -0
  126. package/dist/install/setup.js +63 -10
  127. package/dist/install/setup.js.map +1 -1
  128. package/dist/llm/classifier-box.d.ts +2 -1
  129. package/dist/llm/classifier-box.js +3 -0
  130. package/dist/llm/classifier-box.js.map +1 -1
  131. package/dist/llm/deepseek-client.d.ts +5 -2
  132. package/dist/llm/deepseek-client.js +51 -1
  133. package/dist/llm/deepseek-client.js.map +1 -1
  134. package/dist/llm/ollama-client.d.ts +3 -2
  135. package/dist/llm/ollama-client.js +45 -2
  136. package/dist/llm/ollama-client.js.map +1 -1
  137. package/dist/mcp/server.d.ts +1 -0
  138. package/dist/mcp/server.js +23 -3
  139. package/dist/mcp/server.js.map +1 -1
  140. package/dist/ports/fact-store.d.ts +0 -0
  141. package/dist/ports/fact-store.js +0 -0
  142. package/dist/ports/fact-store.js.map +1 -1
  143. package/dist/ports/llm-client.d.ts +24 -0
  144. package/dist/ports/signal-store.d.ts +30 -0
  145. package/dist/ports/signal-store.js +9 -0
  146. package/dist/ports/signal-store.js.map +1 -0
  147. package/dist/ports/storage.d.ts +2 -0
  148. package/dist/ports/transcript-adapter.d.ts +6 -0
  149. package/dist/shared/types.d.ts +76 -0
  150. package/dist/ui/assets/index-CgHq6xeZ.css +1 -0
  151. package/dist/ui/assets/index-DLDd-OM_.js +69 -0
  152. package/dist/ui/index.html +2 -2
  153. package/nlm/README.md +1 -1
  154. package/nlm/index.js +46 -21
  155. package/package.json +7 -5
  156. package/plugin/.codex-plugin/plugin.json +2 -2
  157. package/plugin/README.md +3 -3
  158. package/plugin/scripts/prompt-recall-hook.mjs +45 -20
  159. package/plugin/scripts/stop-hook.mjs +80 -6
  160. package/plugin-hermes-agent/README.md +49 -0
  161. package/plugin-hermes-agent/__init__.py +75 -0
  162. package/plugin-hermes-agent/plugin.yaml +15 -0
  163. package/dist/ui/assets/index-B7j5U42U.css +0 -1
  164. package/dist/ui/assets/index-BO0dvNHn.js +0 -69
@@ -58,6 +58,34 @@ export async function runHook(input, deps) {
58
58
  }
59
59
  return "";
60
60
  }
61
+ /** Join the failure-mode block (if any) above the session-recall block. */
62
+ export function composeSessionStartOutput(failureModeBlock, recallBlock) {
63
+ return [failureModeBlock, recallBlock].filter((s) => s.length > 0).join("\n\n");
64
+ }
65
+ async function fetchFailureModeBlock(repo) {
66
+ if (!repo)
67
+ return "";
68
+ const portValue = process.env["NLM_PORT"] ?? "3940";
69
+ const url = `http://localhost:${portValue}/api/signals/failure-modes?repo=${encodeURIComponent(repo)}`;
70
+ const controller = new AbortController();
71
+ const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
72
+ try {
73
+ const res = await fetch(url, {
74
+ headers: hookAuthHeaders({ "x-recall-source": "session-start-hook" }),
75
+ signal: controller.signal,
76
+ });
77
+ if (!res.ok)
78
+ return "";
79
+ const body = (await res.json());
80
+ return typeof body.block === "string" ? body.block : "";
81
+ }
82
+ catch {
83
+ return "";
84
+ }
85
+ finally {
86
+ clearTimeout(timer);
87
+ }
88
+ }
61
89
  /** Derive a best-effort query from SessionStart payload fields. */
62
90
  function buildQuery(workingDirectory, projectName) {
63
91
  const dirTail = workingDirectory.split("/").filter(Boolean).at(-1) ?? "";
@@ -81,7 +109,10 @@ async function recallOverHttp(query) {
81
109
  const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
82
110
  try {
83
111
  const res = await fetch(url, {
84
- headers: hookAuthHeaders({ "x-recall-source": "session-start-hook" }),
112
+ headers: hookAuthHeaders({
113
+ "x-recall-source": "session-start-hook",
114
+ "x-recall-runtime": "claude-code",
115
+ }),
85
116
  signal: controller.signal,
86
117
  });
87
118
  if (!res.ok)
@@ -119,8 +150,10 @@ async function main() {
119
150
  const query = buildQuery(workingDirectory, projectName);
120
151
  const mode = process.env["NLM_HOOK_MODE"] === "live" ? "live" : "shadow";
121
152
  const out = await runHook({ conversationId, query }, { mode, recall: recallOverHttp });
122
- if (out)
123
- process.stdout.write(out);
153
+ const failureModes = mode === "live" ? await fetchFailureModeBlock(workingDirectory) : "";
154
+ const combined = composeSessionStartOutput(failureModes, out);
155
+ if (combined)
156
+ process.stdout.write(combined);
124
157
  }
125
158
  catch {
126
159
  // Fail open — never block or fail a session start.
@@ -1 +1 @@
1
- {"version":3,"file":"session-start-hook.js","sourceRoot":"","sources":["../../src/hook/session-start-hook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-start-hook.js","sourceRoot":"","sources":["../../src/hook/session-start-hook.ts"],"names":[],"mappings":"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"}
@@ -16,8 +16,10 @@ import { appendFileSync, mkdirSync } from "node:fs";
16
16
  import { homedir } from "node:os";
17
17
  import { dirname, join } from "node:path";
18
18
  import { detectCitations, } from "../core/hook/citation-detect.js";
19
+ import { detectMisses } from "../core/hook/miss-detect.js";
19
20
  import { loadSurfaced } from "../core/hook/memo.js";
20
21
  import { loadCited, recordCited } from "../core/hook/cite-memo.js";
22
+ import { appendMisses } from "../core/recall/miss-log.js";
21
23
  import { readAllAssistantTurns, } from "../core/hook/transcript.js";
22
24
  import { autoloadEnv } from "../llm/env-autoload.js";
23
25
  import { hookAuthHeaders } from "./hook-auth.js";
@@ -71,6 +73,20 @@ export async function runStopHook(input, deps) {
71
73
  });
72
74
  const alreadyCited = loadCited(input.conversationId);
73
75
  const fresh = detected.filter((c) => !alreadyCited.has(c.id));
76
+ // Spec E: passive miss detection. Any session the agent explicitly
77
+ // fetched via get_session / cite_session that was NOT in the surfaced
78
+ // set is a hook-side miss — the recall gate failed to fire for a
79
+ // session the model later cared about. Fire-and-forget: never blocks
80
+ // the Stop hook return.
81
+ const misses = detectMisses({ toolUses: allToolUses, surfacedIds: surfaced });
82
+ if (misses.length > 0) {
83
+ void appendMisses(misses.map((m) => ({
84
+ conversationId: input.conversationId,
85
+ missedId: m.id,
86
+ kind: m.kind,
87
+ surfacedCount: surfaced.size,
88
+ })));
89
+ }
74
90
  // Preview is the LAST turn's prose — that's what Edward saw when Stop
75
91
  // fired. Stable substrate for the citation log even when detection
76
92
  // ranges across earlier turns.
@@ -1 +1 @@
1
- {"version":3,"file":"stop-hook.js","sourceRoot":"","sources":["../../src/hook/stop-hook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stop-hook.js","sourceRoot":"","sources":["../../src/hook/stop-hook.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,7 @@ import type { SessionStore } from "../ports/session-store.js";
29
29
  import type { SqliteSessionStore } from "../core/storage/sqlite-session-store.js";
30
30
  import type { PgSessionStore } from "../core/storage/pg-session-store.js";
31
31
  import type { McpDeps } from "../mcp/server.js";
32
+ import type { SignalStore } from "../ports/signal-store.js";
32
33
  export interface HttpDeps {
33
34
  readonly recall: RecallService;
34
35
  readonly store: SessionStore;
@@ -66,7 +67,15 @@ export interface HttpDeps {
66
67
  * Omitting this keeps the route absent — no auth surface, no risk.
67
68
  */
68
69
  readonly mcpDeps?: McpDeps;
70
+ /** Signal store - wire to enable POST /api/signal + GET /api/signals/*. */
71
+ readonly signalStore?: SignalStore;
72
+ /** Per-install scope stamped on every ingested signal. */
73
+ readonly installScope?: string;
69
74
  }
70
75
  export declare function isLoopbackHost(host: string | undefined, port: number): boolean;
71
76
  export declare function isLoopbackOrigin(origin: string | undefined, port: number): boolean;
77
+ export declare function parseAllowedOrigins(raw: string): {
78
+ origins: Set<string>;
79
+ hosts: Set<string>;
80
+ };
72
81
  export declare function createApp(deps: HttpDeps): Hono;
package/dist/http/app.js CHANGED
@@ -46,6 +46,9 @@ import { ProviderRegistry, PgProviderRegistry, } from "../core/providers/provide
46
46
  import { listModels } from "../core/providers/provider-models.js";
47
47
  import { ingestSession, deriveSessionId } from "../core/ingest/ingest-session.js";
48
48
  import { listActions, undoAction, writeAction, writeActionsBatch, } from "../core/actions/actions-log.js";
49
+ import { normalizeSignal } from "../core/signals/ingest-signal.js";
50
+ import { buildFailureModeBlock } from "../core/signals/failure-mode-recall.js";
51
+ import { aggregateFailureModes } from "../core/signals/aggregate.js";
49
52
  const MIME_TYPES = {
50
53
  ".html": "text/html; charset=utf-8",
51
54
  ".js": "application/javascript; charset=utf-8",
@@ -104,6 +107,23 @@ export function isLoopbackOrigin(origin, port) {
104
107
  lower === `http://127.0.0.1:${port}` ||
105
108
  lower === `http://[::1]:${port}`);
106
109
  }
110
+ // Parses NLM_ALLOWED_ORIGINS (comma-separated origin URLs) into a set of
111
+ // trusted origins and the hosts derived from them. Called once at middleware
112
+ // install time so env is read once, not per request.
113
+ export function parseAllowedOrigins(raw) {
114
+ const origins = new Set();
115
+ const hosts = new Set();
116
+ for (const entry of raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)) {
117
+ origins.add(entry);
118
+ try {
119
+ hosts.add(new URL(entry).host);
120
+ }
121
+ catch {
122
+ // ignore malformed entries
123
+ }
124
+ }
125
+ return { origins, hosts };
126
+ }
107
127
  const VALID_MODES = ["keyword", "semantic", "hybrid"];
108
128
  const VALID_KINDS = ["decision", "open"];
109
129
  const VALID_FACT_KINDS = ["decision", "open", "attribute"];
@@ -126,6 +146,7 @@ export function createApp(deps) {
126
146
  registerIngestRoute(app, deps);
127
147
  registerProviderRoutes(app, deps);
128
148
  registerSessionRoute(app, deps);
149
+ registerSignalRoutes(app, deps);
129
150
  const nonceStore = createNonceStore();
130
151
  registerNonceRoute(app, nonceStore);
131
152
  if (deps.uiDist) {
@@ -162,18 +183,19 @@ function uiAuthMode() {
162
183
  }
163
184
  function installLocalOnlyMiddleware(app, boundPort) {
164
185
  const skipLocalGate = !!process.env["VITEST"] || process.env["NODE_ENV"] === "test";
186
+ const extras = parseAllowedOrigins(process.env["NLM_ALLOWED_ORIGINS"] ?? "");
165
187
  app.use("/api/*", async (c, next) => {
166
188
  if (skipLocalGate)
167
189
  return next();
168
190
  const host = c.req.header("host");
169
- if (!isLoopbackHost(host, boundPort)) {
191
+ if (!isLoopbackHost(host, boundPort) && !extras.hosts.has(host?.toLowerCase() ?? "")) {
170
192
  return c.json({ error: "host header not allowed" }, 403);
171
193
  }
172
194
  if (c.req.path === "/api/health") {
173
195
  return next();
174
196
  }
175
197
  const origin = c.req.header("origin");
176
- if (origin !== undefined && !isLoopbackOrigin(origin, boundPort)) {
198
+ if (origin !== undefined && !isLoopbackOrigin(origin, boundPort) && !extras.origins.has(origin.toLowerCase())) {
177
199
  return c.json({ error: "origin not allowed" }, 403);
178
200
  }
179
201
  if (uiAuthMode() === "none") {
@@ -344,18 +366,47 @@ function registerRecallRoutes(app, deps) {
344
366
  if (!Number.isFinite(limit) || limit < 1 || limit > 100) {
345
367
  return c.json({ error: "limit must be 1..100" }, 400);
346
368
  }
369
+ // Rewrite resolution: the hot-path hook caller always sets
370
+ // `x-recall-source: hook`, and that header force-disables the LLM
371
+ // rewrite step regardless of the `?rewrite=...` query string. This
372
+ // protects the ~400ms hook latency budget even if a misconfigured
373
+ // hook caller passes rewrite=true. MCP and direct HTTP callers honor
374
+ // the query param; default is false at the HTTP layer.
375
+ const source = c.req.header("x-recall-source") ?? "http";
376
+ const runtime = c.req.header("x-recall-runtime") ?? null;
377
+ const rewriteParam = c.req.query("rewrite");
378
+ let rewrite;
379
+ if (source === "hook") {
380
+ rewrite = false;
381
+ }
382
+ else if (rewriteParam !== undefined) {
383
+ rewrite = rewriteParam === "true" || rewriteParam === "1";
384
+ }
385
+ // Spec G.2: `?withFacts=true` (or hook source = automatic) attaches
386
+ // a relatedFacts array. Hooks get facts by default; other callers
387
+ // opt in via the query param.
388
+ const factsParam = c.req.query("withFacts");
389
+ let withRelatedFacts;
390
+ if (factsParam !== undefined) {
391
+ withRelatedFacts = factsParam === "true" || factsParam === "1";
392
+ }
393
+ else if (source === "hook") {
394
+ withRelatedFacts = true;
395
+ }
347
396
  const query = {
348
397
  query: q,
349
398
  mode: mode,
350
399
  limit,
351
400
  ...(entity !== undefined ? { entity } : {}),
352
401
  ...(kind !== undefined ? { kind: kind } : {}),
402
+ ...(rewrite !== undefined ? { rewrite } : {}),
403
+ ...(withRelatedFacts !== undefined ? { withRelatedFacts } : {}),
353
404
  };
354
405
  const result = await deps.recall.search(query);
355
406
  // Fire-and-forget telemetry — never blocks the response.
356
- const source = c.req.header("x-recall-source") ?? "http";
357
407
  void logQuery({
358
408
  source,
409
+ runtime,
359
410
  query: q || null,
360
411
  entity: entity ?? null,
361
412
  kind: kind ?? null,
@@ -541,7 +592,12 @@ function registerHermesAgentHookRoutes(app, deps) {
541
592
  return c.json({ context: null });
542
593
  }
543
594
  try {
544
- const result = await deps.recall.search({ query: userMessage, mode: "keyword", limit: 5 });
595
+ const result = await deps.recall.search({
596
+ query: userMessage,
597
+ mode: "keyword",
598
+ limit: 5,
599
+ withRelatedFacts: true,
600
+ });
545
601
  const hits = result.results.map((r) => ({
546
602
  id: r.id,
547
603
  label: r.label,
@@ -550,10 +606,13 @@ function registerHermesAgentHookRoutes(app, deps) {
550
606
  }));
551
607
  const surfaced = loadSurfaced(sessionId);
552
608
  const selected = selectHits({ hits, surfaced, scoreThreshold: 0, perFireCap: 3, perConversationCap: 10 });
553
- if (selected.length === 0)
609
+ if (selected.length === 0 && (result.relatedFacts ?? []).length === 0) {
554
610
  return c.json({ context: null });
555
- recordSurfaced(sessionId, selected.map((h) => h.id));
556
- return c.json({ context: formatPointerBlock(selected) });
611
+ }
612
+ if (selected.length > 0) {
613
+ recordSurfaced(sessionId, selected.map((h) => h.id));
614
+ }
615
+ return c.json({ context: formatPointerBlock(selected, result.relatedFacts ?? []) });
557
616
  }
558
617
  catch {
559
618
  return c.json({ context: null });
@@ -888,8 +947,8 @@ function registerActionRoutes(app, deps) {
888
947
  }
889
948
  function registerClassifierRoutes(app, deps) {
890
949
  app.get("/api/classifier/info", (c) => {
891
- const provider = deps.classifier?.provider ?? "deepseek";
892
- const model = deps.classifier?.model ?? "deepseek-v4-flash";
950
+ const provider = deps.classifier?.provider ?? "ollama";
951
+ const model = deps.classifier?.model ?? "qwen3:4b-instruct-2507-q4_K_M";
893
952
  return c.json({
894
953
  provider,
895
954
  model,
@@ -900,7 +959,7 @@ function registerClassifierRoutes(app, deps) {
900
959
  },
901
960
  default_models: {
902
961
  deepseek: ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat"],
903
- ollama: ["phi4-mini:latest", "qwen2.5:3b-instruct", "llama3.2:3b", "mistral:7b"],
962
+ ollama: ["qwen3:4b-instruct-2507-q4_K_M", "qwen2.5:3b-instruct", "llama3.2:3b", "mistral:7b"],
904
963
  },
905
964
  embedder: deps.embedderInfo ?? { provider: "ollama", model: "nomic-embed-text", dims: 768 },
906
965
  });
@@ -1311,6 +1370,62 @@ function parseProviderUpdate(raw) {
1311
1370
  return null;
1312
1371
  return patch;
1313
1372
  }
1373
+ function registerSignalRoutes(app, deps) {
1374
+ app.post("/api/signal", async (c) => {
1375
+ if (!deps.signalStore || deps.installScope === undefined) {
1376
+ return c.json({ error: "signal store not wired in this deployment" }, 503);
1377
+ }
1378
+ if (process.env["NLM_SIGNALS_ENABLED"] === "0") {
1379
+ return c.json({ error: "signals disabled" }, 403);
1380
+ }
1381
+ let body;
1382
+ try {
1383
+ body = await c.req.json();
1384
+ }
1385
+ catch {
1386
+ return c.json({ error: "body must be JSON" }, 400);
1387
+ }
1388
+ let signal;
1389
+ try {
1390
+ signal = normalizeSignal(body, deps.installScope);
1391
+ }
1392
+ catch (e) {
1393
+ return c.json({ error: e instanceof Error ? e.message : "invalid signal" }, 400);
1394
+ }
1395
+ try {
1396
+ await deps.signalStore.insert(signal);
1397
+ }
1398
+ catch {
1399
+ return c.json({ error: "signal insert failed" }, 500);
1400
+ }
1401
+ return c.json({ id: signal.id, status: "accepted" }, 202);
1402
+ });
1403
+ app.get("/api/signals/failure-modes", async (c) => {
1404
+ if (!deps.signalStore || deps.installScope === undefined) {
1405
+ return c.json({ error: "signal store not wired in this deployment" }, 503);
1406
+ }
1407
+ const repo = c.req.query("repo");
1408
+ if (!repo)
1409
+ return c.json({ error: "repo is required" }, 400);
1410
+ const model = c.req.query("model");
1411
+ const block = await buildFailureModeBlock(deps.signalStore, { installScope: deps.installScope, repo, ...(model ? { model } : {}) });
1412
+ return c.json({ repo, model: model ?? null, block });
1413
+ });
1414
+ app.get("/api/signals/stats", async (c) => {
1415
+ if (!deps.signalStore || deps.installScope === undefined) {
1416
+ return c.json({ error: "signal store not wired in this deployment" }, 503);
1417
+ }
1418
+ const daysStr = c.req.query("days") ?? "14";
1419
+ const days = Number.parseInt(daysStr, 10);
1420
+ if (!Number.isFinite(days) || days < 1 || days > 365) {
1421
+ return c.json({ error: "days must be 1..365" }, 400);
1422
+ }
1423
+ const sinceTs = new Date(Date.now() - days * 86_400_000).toISOString();
1424
+ const rows = await deps.signalStore.listForAggregation({ installScope: deps.installScope, sinceTs });
1425
+ const modes = aggregateFailureModes(rows);
1426
+ return c.json({ days, total: rows.length, modes });
1427
+ });
1428
+ }
1314
1429
  function mountSpa(app, dist) {
1315
1430
  const indexHtml = join(dist, "index.html");
1316
1431
  if (!existsSync(indexHtml))