nlm-memory 0.20.1 → 0.21.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 (168) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/digest.js +39 -1
  3. package/dist/cli/digest.js.map +1 -1
  4. package/dist/cli/nlm.js +106 -6
  5. package/dist/cli/nlm.js.map +1 -1
  6. package/dist/cli/scope-backfill.d.ts +34 -0
  7. package/dist/cli/scope-backfill.js +237 -0
  8. package/dist/cli/scope-backfill.js.map +1 -0
  9. package/dist/cli/scope-coverage.d.ts +44 -0
  10. package/dist/cli/scope-coverage.js +205 -0
  11. package/dist/cli/scope-coverage.js.map +1 -0
  12. package/dist/core/adapters/claude-code.d.ts +14 -0
  13. package/dist/core/adapters/claude-code.js +30 -0
  14. package/dist/core/adapters/claude-code.js.map +1 -1
  15. package/dist/core/dataset/build-dataset.js +1 -0
  16. package/dist/core/dataset/build-dataset.js.map +1 -1
  17. package/dist/core/digest/compose.d.ts +3 -3
  18. package/dist/core/digest/compose.js +16 -17
  19. package/dist/core/digest/compose.js.map +1 -1
  20. package/dist/core/digest/hook-injection.d.ts +43 -0
  21. package/dist/core/digest/hook-injection.js +79 -0
  22. package/dist/core/digest/hook-injection.js.map +1 -0
  23. package/dist/core/exemplars/capture-from-session.d.ts +1 -0
  24. package/dist/core/exemplars/capture-from-session.js +1 -1
  25. package/dist/core/exemplars/capture-from-session.js.map +1 -1
  26. package/dist/core/exemplars/extract-exemplar.d.ts +2 -0
  27. package/dist/core/exemplars/extract-exemplar.js +7 -1
  28. package/dist/core/exemplars/extract-exemplar.js.map +1 -1
  29. package/dist/core/exemplars/ingest-exemplar.d.ts +1 -0
  30. package/dist/core/exemplars/ingest-exemplar.js +1 -0
  31. package/dist/core/exemplars/ingest-exemplar.js.map +1 -1
  32. package/dist/core/hook/claude-settings.d.ts +1 -1
  33. package/dist/core/hook/claude-settings.js +0 -1
  34. package/dist/core/hook/claude-settings.js.map +1 -1
  35. package/dist/core/ingest/backfill-derivables.d.ts +92 -0
  36. package/dist/core/ingest/backfill-derivables.js +175 -0
  37. package/dist/core/ingest/backfill-derivables.js.map +1 -0
  38. package/dist/core/ingest/ingest-session.js +20 -0
  39. package/dist/core/ingest/ingest-session.js.map +1 -1
  40. package/dist/core/ingest/reclassify-oversized.js +6 -0
  41. package/dist/core/ingest/reclassify-oversized.js.map +1 -1
  42. package/dist/core/ingest/reprocess.js +5 -0
  43. package/dist/core/ingest/reprocess.js.map +1 -1
  44. package/dist/core/ingest/transcript-derivables.d.ts +27 -0
  45. package/dist/core/ingest/transcript-derivables.js +138 -0
  46. package/dist/core/ingest/transcript-derivables.js.map +1 -0
  47. package/dist/core/outcome/coverage.d.ts +33 -0
  48. package/dist/core/outcome/coverage.js +46 -0
  49. package/dist/core/outcome/coverage.js.map +1 -0
  50. package/dist/core/outcome/rollup.d.ts +33 -0
  51. package/dist/core/outcome/rollup.js +113 -0
  52. package/dist/core/outcome/rollup.js.map +1 -0
  53. package/dist/core/recall/hook-recall-log.d.ts +2 -2
  54. package/dist/core/recall/hook-recall-log.js +2 -2
  55. package/dist/core/recall-facts/fact-recall-service.js +1 -0
  56. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  57. package/dist/core/scheduler/scan-once.js +2 -0
  58. package/dist/core/scheduler/scan-once.js.map +1 -1
  59. package/dist/core/scheduler/scheduler.js +34 -3
  60. package/dist/core/scheduler/scheduler.js.map +1 -1
  61. package/dist/core/scope/alias-map.d.ts +10 -0
  62. package/dist/core/scope/alias-map.js +41 -0
  63. package/dist/core/scope/alias-map.js.map +1 -0
  64. package/dist/core/scope/derive-scope.d.ts +8 -0
  65. package/dist/core/scope/derive-scope.js +54 -0
  66. package/dist/core/scope/derive-scope.js.map +1 -0
  67. package/dist/core/scope/scope-clause.d.ts +16 -0
  68. package/dist/core/scope/scope-clause.js +21 -0
  69. package/dist/core/scope/scope-clause.js.map +1 -0
  70. package/dist/core/scope/stamp-flag.d.ts +1 -0
  71. package/dist/core/scope/stamp-flag.js +4 -0
  72. package/dist/core/scope/stamp-flag.js.map +1 -0
  73. package/dist/core/signals/code-signal.d.ts +1 -0
  74. package/dist/core/signals/code-signal.js +1 -0
  75. package/dist/core/signals/code-signal.js.map +1 -1
  76. package/dist/core/signals/ingest-signal.d.ts +1 -1
  77. package/dist/core/signals/ingest-signal.js +3 -1
  78. package/dist/core/signals/ingest-signal.js.map +1 -1
  79. package/dist/core/signals/stamp-scope.d.ts +18 -0
  80. package/dist/core/signals/stamp-scope.js +28 -0
  81. package/dist/core/signals/stamp-scope.js.map +1 -0
  82. package/dist/core/storage/pg-code-exemplar-store.js +6 -2
  83. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  84. package/dist/core/storage/pg-fact-ingest.d.ts +1 -1
  85. package/dist/core/storage/pg-fact-ingest.js +4 -4
  86. package/dist/core/storage/pg-fact-ingest.js.map +1 -1
  87. package/dist/core/storage/pg-session-store.d.ts +2 -2
  88. package/dist/core/storage/pg-session-store.js +31 -31
  89. package/dist/core/storage/pg-session-store.js.map +1 -1
  90. package/dist/core/storage/pg-signal-store.js +4 -3
  91. package/dist/core/storage/pg-signal-store.js.map +1 -1
  92. package/dist/core/storage/pg-workstream-store.d.ts +1 -0
  93. package/dist/core/storage/pg-workstream-store.js +2 -1
  94. package/dist/core/storage/pg-workstream-store.js.map +1 -1
  95. package/dist/core/storage/sqlite-code-exemplar-store.js +4 -2
  96. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  97. package/dist/core/storage/sqlite-fact-store.d.ts +1 -1
  98. package/dist/core/storage/sqlite-fact-store.js +8 -5
  99. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  100. package/dist/core/storage/sqlite-outcome-store.d.ts +89 -0
  101. package/dist/core/storage/sqlite-outcome-store.js +158 -0
  102. package/dist/core/storage/sqlite-outcome-store.js.map +1 -0
  103. package/dist/core/storage/sqlite-session-store.d.ts +22 -2
  104. package/dist/core/storage/sqlite-session-store.js +33 -35
  105. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  106. package/dist/core/storage/sqlite-signal-store.js +4 -2
  107. package/dist/core/storage/sqlite-signal-store.js.map +1 -1
  108. package/dist/core/storage/sqlite-workstream-store.d.ts +1 -0
  109. package/dist/core/storage/sqlite-workstream-store.js +2 -1
  110. package/dist/core/storage/sqlite-workstream-store.js.map +1 -1
  111. package/dist/core/storage/supersedence-log.js +2 -0
  112. package/dist/core/storage/supersedence-log.js.map +1 -1
  113. package/dist/core/telemetry/probe-filter.d.ts +35 -0
  114. package/dist/core/telemetry/probe-filter.js +56 -0
  115. package/dist/core/telemetry/probe-filter.js.map +1 -0
  116. package/dist/core/update-check/check.d.ts +6 -0
  117. package/dist/core/update-check/check.js +35 -0
  118. package/dist/core/update-check/check.js.map +1 -1
  119. package/dist/core/workstream/bind.d.ts +2 -1
  120. package/dist/core/workstream/bind.js +28 -9
  121. package/dist/core/workstream/bind.js.map +1 -1
  122. package/dist/core/workstream/model.d.ts +1 -0
  123. package/dist/core/workstream/model.js.map +1 -1
  124. package/dist/hook/pre-compact-hook.js +2 -1
  125. package/dist/hook/pre-compact-hook.js.map +1 -1
  126. package/dist/hook/prompt-recall-hook.d.ts +3 -2
  127. package/dist/hook/prompt-recall-hook.js +8 -4
  128. package/dist/hook/prompt-recall-hook.js.map +1 -1
  129. package/dist/hook/recall-over-http.d.ts +2 -1
  130. package/dist/hook/recall-over-http.js +15 -2
  131. package/dist/hook/recall-over-http.js.map +1 -1
  132. package/dist/hook/session-start-hook.js +2 -1
  133. package/dist/hook/session-start-hook.js.map +1 -1
  134. package/dist/hook/stop-hook.js +2 -1
  135. package/dist/hook/stop-hook.js.map +1 -1
  136. package/dist/http/app.d.ts +6 -0
  137. package/dist/http/app.js +26 -38
  138. package/dist/http/app.js.map +1 -1
  139. package/dist/mcp/server.d.ts +22 -0
  140. package/dist/mcp/server.js +101 -1
  141. package/dist/mcp/server.js.map +1 -1
  142. package/dist/ports/outcome.d.ts +85 -0
  143. package/dist/ports/outcome.js +11 -0
  144. package/dist/ports/outcome.js.map +1 -0
  145. package/dist/ports/session-store.d.ts +0 -6
  146. package/dist/ports/workstream-store.d.ts +1 -0
  147. package/dist/shared/net.d.ts +3 -0
  148. package/dist/shared/net.js +4 -0
  149. package/dist/shared/net.js.map +1 -0
  150. package/dist/shared/types.d.ts +33 -0
  151. package/dist/shared/types.js +4 -1
  152. package/dist/shared/types.js.map +1 -1
  153. package/dist/ui/assets/{index-CMpQkbQy.js → index-CSaBtgXD.js} +1 -1
  154. package/dist/ui/index.html +1 -1
  155. package/migrations/029_project_scope.sql +21 -0
  156. package/migrations/032_session_derivables.sql +11 -0
  157. package/migrations/pg/030_project_scope.sql +23 -0
  158. package/migrations/pg/031_widen_session_edges_kind.sql +11 -0
  159. package/migrations/pg/032_session_derivables.sql +17 -0
  160. package/nlm/index.js +14 -6
  161. package/package.json +1 -1
  162. package/plugin/.codex-plugin/plugin.json +2 -2
  163. package/plugin/scripts/prompt-recall-hook.mjs +15 -6
  164. package/plugin/scripts/session-start-hook.mjs +11 -3
  165. package/plugin/scripts/stop-hook.mjs +4 -1
  166. package/dist/hook/subagent-start-hook.d.ts +0 -28
  167. package/dist/hook/subagent-start-hook.js +0 -81
  168. package/dist/hook/subagent-start-hook.js.map +0 -1
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>nlm memory</title>
7
- <script type="module" crossorigin src="/ui/assets/index-CMpQkbQy.js"></script>
7
+ <script type="module" crossorigin src="/ui/assets/index-CSaBtgXD.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/ui/assets/index-DSzo1eTD.css">
9
9
  </head>
10
10
  <body>
@@ -0,0 +1,21 @@
1
+ -- migrations/029_project_scope.sql
2
+ -- Additive scope column on the five corpus tables (#348 Stage A).
3
+ -- No backfill; all existing rows are NULL. Enforcement is deferred behind
4
+ -- NLM_SCOPE_ENFORCE. See docs/superpowers/specs/2026-07-03-project-scoping-design.md.
5
+
6
+ ALTER TABLE sessions ADD COLUMN scope TEXT;
7
+ CREATE INDEX IF NOT EXISTS idx_sessions_scope ON sessions(scope);
8
+
9
+ ALTER TABLE facts ADD COLUMN scope TEXT;
10
+ CREATE INDEX IF NOT EXISTS idx_facts_scope ON facts(scope);
11
+
12
+ ALTER TABLE code_exemplars ADD COLUMN scope TEXT;
13
+ CREATE INDEX IF NOT EXISTS idx_code_exemplars_scope ON code_exemplars(scope);
14
+
15
+ ALTER TABLE signals ADD COLUMN scope TEXT;
16
+ CREATE INDEX IF NOT EXISTS idx_signals_scope ON signals(scope);
17
+
18
+ ALTER TABLE workstreams ADD COLUMN scope TEXT;
19
+ CREATE INDEX IF NOT EXISTS idx_workstreams_scope ON workstreams(scope);
20
+
21
+ INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (29, 'project_scope');
@@ -0,0 +1,11 @@
1
+ -- migrations/032_session_derivables.sql
2
+ -- #352 Phase 2: derivable metadata. All nullable: NULL means not-derivable
3
+ -- for that runtime, never faked.
4
+ ALTER TABLE sessions ADD COLUMN agent_persona TEXT;
5
+ ALTER TABLE sessions ADD COLUMN parent_session_id TEXT;
6
+ ALTER TABLE sessions ADD COLUMN primary_model TEXT;
7
+ ALTER TABLE sessions ADD COLUMN total_tokens INTEGER;
8
+ ALTER TABLE sessions ADD COLUMN skill TEXT;
9
+ CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
10
+
11
+ INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (32, 'session_derivables');
@@ -0,0 +1,23 @@
1
+ -- migrations/pg/030_project_scope.sql
2
+ -- PG parity for SQLite migration 029 (project scope). Auto-applied by the
3
+ -- version-gated runner (runMigrationsPg). Idempotent: every DDL uses
4
+ -- ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS. Mirrors migrations/029_project_scope.sql.
5
+
6
+ BEGIN;
7
+
8
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS scope TEXT;
9
+ CREATE INDEX IF NOT EXISTS idx_sessions_scope ON sessions(scope);
10
+
11
+ ALTER TABLE facts ADD COLUMN IF NOT EXISTS scope TEXT;
12
+ CREATE INDEX IF NOT EXISTS idx_facts_scope ON facts(scope);
13
+
14
+ ALTER TABLE code_exemplars ADD COLUMN IF NOT EXISTS scope TEXT;
15
+ CREATE INDEX IF NOT EXISTS idx_code_exemplars_scope ON code_exemplars(scope);
16
+
17
+ ALTER TABLE signals ADD COLUMN IF NOT EXISTS scope TEXT;
18
+ CREATE INDEX IF NOT EXISTS idx_signals_scope ON signals(scope);
19
+
20
+ ALTER TABLE workstreams ADD COLUMN IF NOT EXISTS scope TEXT;
21
+ CREATE INDEX IF NOT EXISTS idx_workstreams_scope ON workstreams(scope);
22
+
23
+ COMMIT;
@@ -0,0 +1,11 @@
1
+ -- Parity: SQLite migrations/019 allows five session_edges.kind values
2
+ -- (supersedes, replaces, continues, branched_from, merged_from); pg/019
3
+ -- only widened to three. This closes the remaining gap.
4
+
5
+ BEGIN;
6
+
7
+ ALTER TABLE session_edges DROP CONSTRAINT IF EXISTS session_edges_kind_check;
8
+ ALTER TABLE session_edges ADD CONSTRAINT session_edges_kind_check
9
+ CHECK (kind IN ('supersedes', 'replaces', 'continues', 'branched_from', 'merged_from'));
10
+
11
+ COMMIT;
@@ -0,0 +1,17 @@
1
+ -- migrations/pg/032_session_derivables.sql
2
+ -- PG parity for SQLite migration 032 (session derivables). Auto-applied by
3
+ -- the version-gated runner (runMigrationsPg). Idempotent: every DDL uses
4
+ -- ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS. Mirrors
5
+ -- migrations/032_session_derivables.sql. All nullable: NULL means
6
+ -- not-derivable for that runtime, never faked.
7
+
8
+ BEGIN;
9
+
10
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS agent_persona TEXT;
11
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS parent_session_id TEXT;
12
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS primary_model TEXT;
13
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS total_tokens INTEGER;
14
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS skill TEXT;
15
+ CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
16
+
17
+ COMMIT;
package/nlm/index.js CHANGED
@@ -213,13 +213,21 @@ function hookModeFromEnv() {
213
213
  return process.env["NLM_HOOK_MODE"] === "live" ? "live" : "shadow";
214
214
  }
215
215
 
216
+ // src/shared/net.ts
217
+ var DEFAULT_NLM_PORT = "3940";
218
+
216
219
  // src/hook/recall-over-http.ts
217
220
  var RECALL_LIMIT = 5;
218
- var RECALL_TIMEOUT_MS = 2e3;
221
+ function parseRecallTimeout(raw) {
222
+ if (raw === void 0) return 4e3;
223
+ const n = Number(raw);
224
+ return Number.isFinite(n) && n > 0 ? n : 4e3;
225
+ }
226
+ var RECALL_TIMEOUT_MS = parseRecallTimeout(process.env["NLM_HOOK_RECALL_TIMEOUT_MS"]);
219
227
  async function recallOverHttp(prompt, runtime, conversationId, mode = "keyword") {
220
228
  const query = extractRecallQuery(prompt);
221
229
  if (query === null) return { hits: [], facts: [], exemplars: [] };
222
- const portValue = process.env["NLM_PORT"] ?? "3940";
230
+ const portValue = process.env["NLM_PORT"] ?? DEFAULT_NLM_PORT;
223
231
  const url = (
224
232
  // 127.0.0.1, not localhost: each hook is a fresh process with no connection
225
233
  // reuse, and Node resolves localhost to IPv6 ::1 first — a measured ~50-300ms
@@ -652,12 +660,12 @@ var RELATIVE_FLOOR = parseRelativeFloor(process.env["NLM_RECALL_REL_FLOOR"], 0.9
652
660
  var PER_FIRE_CAP = 3;
653
661
  var PER_CONVERSATION_CAP = 10;
654
662
  var PROMPT_PREVIEW_CHARS = 200;
655
- function parseHookDeadline(raw) {
656
- if (raw === void 0) return 4e3;
663
+ function parseHookDeadline(raw, defaultMs = 4e3) {
664
+ if (raw === void 0) return defaultMs;
657
665
  const n = Number(raw);
658
- return Number.isFinite(n) && n > 0 ? n : 4e3;
666
+ return Number.isFinite(n) && n > 0 ? n : defaultMs;
659
667
  }
660
- var HOOK_DEADLINE_MS = parseHookDeadline(process.env["NLM_HOOK_DEADLINE_MS"]);
668
+ var HOOK_DEADLINE_MS = parseHookDeadline(process.env["NLM_HOOK_DEADLINE_MS"], 6e3);
661
669
  async function withDeadline(p, ms, fallback) {
662
670
  if (ms <= 0) return fallback;
663
671
  let timer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlm-memory",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "Local-first non-linear memory operating system for AI operators.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlm-memory",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "Local-first persistent memory for AI coding agents. Recalls relevant prior sessions on every prompt; learns which sessions actually get cited. Owner-edited timeline beats append-only memory layers.",
5
5
  "author": {
6
6
  "name": "Edward Chalupa",
@@ -19,4 +19,4 @@
19
19
  ],
20
20
  "mcpServers": "./.mcp.json",
21
21
  "hooks": "./hooks/hooks.json"
22
- }
22
+ }
@@ -537,13 +537,21 @@ function hookModeFromEnv() {
537
537
  return process.env["NLM_HOOK_MODE"] === "live" ? "live" : "shadow";
538
538
  }
539
539
 
540
+ // src/shared/net.ts
541
+ var DEFAULT_NLM_PORT = "3940";
542
+
540
543
  // src/hook/recall-over-http.ts
541
544
  var RECALL_LIMIT = 5;
542
- var RECALL_TIMEOUT_MS = 2e3;
545
+ function parseRecallTimeout(raw) {
546
+ if (raw === void 0) return 4e3;
547
+ const n = Number(raw);
548
+ return Number.isFinite(n) && n > 0 ? n : 4e3;
549
+ }
550
+ var RECALL_TIMEOUT_MS = parseRecallTimeout(process.env["NLM_HOOK_RECALL_TIMEOUT_MS"]);
543
551
  async function recallOverHttp(prompt, runtime, conversationId, mode = "keyword") {
544
552
  const query = extractRecallQuery(prompt);
545
553
  if (query === null) return { hits: [], facts: [], exemplars: [] };
546
- const portValue = process.env["NLM_PORT"] ?? "3940";
554
+ const portValue = process.env["NLM_PORT"] ?? DEFAULT_NLM_PORT;
547
555
  const url = (
548
556
  // 127.0.0.1, not localhost: each hook is a fresh process with no connection
549
557
  // reuse, and Node resolves localhost to IPv6 ::1 first — a measured ~50-300ms
@@ -654,12 +662,12 @@ var RELATIVE_FLOOR = parseRelativeFloor(process.env["NLM_RECALL_REL_FLOOR"], 0.9
654
662
  var PER_FIRE_CAP = 3;
655
663
  var PER_CONVERSATION_CAP = 10;
656
664
  var PROMPT_PREVIEW_CHARS = 200;
657
- function parseHookDeadline(raw) {
658
- if (raw === void 0) return 4e3;
665
+ function parseHookDeadline(raw, defaultMs = 4e3) {
666
+ if (raw === void 0) return defaultMs;
659
667
  const n = Number(raw);
660
- return Number.isFinite(n) && n > 0 ? n : 4e3;
668
+ return Number.isFinite(n) && n > 0 ? n : defaultMs;
661
669
  }
662
- var HOOK_DEADLINE_MS = parseHookDeadline(process.env["NLM_HOOK_DEADLINE_MS"]);
670
+ var HOOK_DEADLINE_MS = parseHookDeadline(process.env["NLM_HOOK_DEADLINE_MS"], 6e3);
663
671
  async function withDeadline(p, ms, fallback) {
664
672
  if (ms <= 0) return fallback;
665
673
  let timer;
@@ -800,6 +808,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
800
808
  void main();
801
809
  }
802
810
  export {
811
+ HOOK_DEADLINE_MS,
803
812
  buildRecallQuery,
804
813
  hookRuntimeFromEnv,
805
814
  parseHookDeadline,
@@ -362,13 +362,21 @@ function hookModeFromEnv() {
362
362
  return process.env["NLM_HOOK_MODE"] === "live" ? "live" : "shadow";
363
363
  }
364
364
 
365
+ // src/shared/net.ts
366
+ var DEFAULT_NLM_PORT = "3940";
367
+
365
368
  // src/hook/recall-over-http.ts
366
369
  var RECALL_LIMIT = 5;
367
- var RECALL_TIMEOUT_MS = 2e3;
370
+ function parseRecallTimeout(raw) {
371
+ if (raw === void 0) return 4e3;
372
+ const n = Number(raw);
373
+ return Number.isFinite(n) && n > 0 ? n : 4e3;
374
+ }
375
+ var RECALL_TIMEOUT_MS = parseRecallTimeout(process.env["NLM_HOOK_RECALL_TIMEOUT_MS"]);
368
376
  async function recallOverHttp(prompt, runtime, conversationId, mode = "keyword") {
369
377
  const query = extractRecallQuery(prompt);
370
378
  if (query === null) return { hits: [], facts: [], exemplars: [] };
371
- const portValue = process.env["NLM_PORT"] ?? "3940";
379
+ const portValue = process.env["NLM_PORT"] ?? DEFAULT_NLM_PORT;
372
380
  const url = (
373
381
  // 127.0.0.1, not localhost: each hook is a fresh process with no connection
374
382
  // reuse, and Node resolves localhost to IPv6 ::1 first — a measured ~50-300ms
@@ -456,7 +464,7 @@ function composeSessionStartOutput(failureModeBlock, recallBlock) {
456
464
  }
457
465
  async function fetchFailureModeBlock(repo) {
458
466
  if (!repo) return "";
459
- const portValue = process.env["NLM_PORT"] ?? "3940";
467
+ const portValue = process.env["NLM_PORT"] ?? DEFAULT_NLM_PORT;
460
468
  const url = `http://127.0.0.1:${portValue}/api/signals/failure-modes?repo=${encodeURIComponent(repo)}`;
461
469
  try {
462
470
  const res = await fetchWithTimeout(url, {
@@ -327,6 +327,9 @@ function appendHookEvent(data) {
327
327
  }
328
328
  }
329
329
 
330
+ // src/shared/net.ts
331
+ var DEFAULT_NLM_PORT = "3940";
332
+
330
333
  // src/hook/stop-hook.ts
331
334
  var RESPONSE_PREVIEW_CHARS = 200;
332
335
  var POST_TIMEOUT_MS = 1500;
@@ -414,7 +417,7 @@ function logStopResult(result) {
414
417
  });
415
418
  }
416
419
  async function postCitationOverHttp(conversationId, citedId, kind, responsePreview) {
417
- const port = process.env["NLM_PORT"] ?? "3940";
420
+ const port = process.env["NLM_PORT"] ?? DEFAULT_NLM_PORT;
418
421
  const url = `http://127.0.0.1:${port}/api/recall/cite-event`;
419
422
  await fetchWithTimeout(url, {
420
423
  method: "POST",
@@ -1,28 +0,0 @@
1
- /**
2
- * Claude Code SubagentStart hook entrypoint for NLM.
3
- *
4
- * Fires when Claude Code dispatches a subagent (Agent tool). Subagents have
5
- * their own session IDs but are invisible to NLM's session corpus today.
6
- * This hook captures the parent→subagent link so NLM can correlate subagent
7
- * transcripts back to the dispatching conversation when SessionEnd fires.
8
- *
9
- * Capture-only. No recall injection — subagents inherit context from their
10
- * dispatch prompt; additional recall would pollute their narrow task scope.
11
- *
12
- * Daemon endpoint: POST /api/hook/subagent-start
13
- *
14
- * Payload: { parent_conversation_id, subagent_session_id, subagent_description, ts }
15
- *
16
- * Fail-open by design: any error yields a clean exit with no output.
17
- */
18
- export interface SubagentStartInput {
19
- readonly parentConversationId: string;
20
- readonly subagentSessionId: string;
21
- readonly subagentDescription: string;
22
- }
23
- export interface SubagentStartResult {
24
- readonly parentConversationId: string;
25
- readonly subagentSessionId: string;
26
- readonly posted: boolean;
27
- }
28
- export declare function runSubagentStart(input: SubagentStartInput, portValue?: string): Promise<SubagentStartResult>;
@@ -1,81 +0,0 @@
1
- /**
2
- * Claude Code SubagentStart hook entrypoint for NLM.
3
- *
4
- * Fires when Claude Code dispatches a subagent (Agent tool). Subagents have
5
- * their own session IDs but are invisible to NLM's session corpus today.
6
- * This hook captures the parent→subagent link so NLM can correlate subagent
7
- * transcripts back to the dispatching conversation when SessionEnd fires.
8
- *
9
- * Capture-only. No recall injection — subagents inherit context from their
10
- * dispatch prompt; additional recall would pollute their narrow task scope.
11
- *
12
- * Daemon endpoint: POST /api/hook/subagent-start
13
- *
14
- * Payload: { parent_conversation_id, subagent_session_id, subagent_description, ts }
15
- *
16
- * Fail-open by design: any error yields a clean exit with no output.
17
- */
18
- import { pathToFileURL } from "node:url";
19
- import { autoloadEnv } from "../llm/env-autoload.js";
20
- import { hookAuthHeaders } from "./hook-auth.js";
21
- import { readStdin, fetchWithTimeout, appendHookEvent } from "./hook-helpers.js";
22
- const POST_TIMEOUT_MS = 1500;
23
- export async function runSubagentStart(input, portValue = process.env["NLM_PORT"] ?? "3940") {
24
- const payload = {
25
- parent_conversation_id: input.parentConversationId,
26
- subagent_session_id: input.subagentSessionId,
27
- subagent_description: input.subagentDescription,
28
- ts: new Date().toISOString(),
29
- };
30
- try {
31
- const res = await fetchWithTimeout(`http://127.0.0.1:${portValue}/api/hook/subagent-start`, {
32
- method: "POST",
33
- headers: hookAuthHeaders({ "content-type": "application/json" }),
34
- body: JSON.stringify(payload),
35
- }, POST_TIMEOUT_MS);
36
- return {
37
- parentConversationId: input.parentConversationId,
38
- subagentSessionId: input.subagentSessionId,
39
- posted: res.ok,
40
- };
41
- }
42
- catch {
43
- return {
44
- parentConversationId: input.parentConversationId,
45
- subagentSessionId: input.subagentSessionId,
46
- posted: false,
47
- };
48
- }
49
- }
50
- function logResult(result) {
51
- appendHookEvent({
52
- ts: new Date().toISOString(),
53
- kind: "subagent-start",
54
- parentConversationId: result.parentConversationId,
55
- subagentSessionId: result.subagentSessionId,
56
- posted: result.posted,
57
- });
58
- }
59
- async function main() {
60
- try {
61
- autoloadEnv();
62
- const raw = await readStdin();
63
- const payload = JSON.parse(raw);
64
- const subagentSessionId = typeof payload.session_id === "string" ? payload.session_id : "unknown";
65
- const parentConversationId = typeof payload.parent_session_id === "string" ? payload.parent_session_id : "unknown";
66
- const subagentDescription = typeof payload.description === "string" ? payload.description : "";
67
- const result = await runSubagentStart({
68
- parentConversationId,
69
- subagentSessionId,
70
- subagentDescription,
71
- });
72
- logResult(result);
73
- }
74
- catch {
75
- // Fail open — never block subagent dispatch.
76
- }
77
- }
78
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
79
- void main();
80
- }
81
- //# sourceMappingURL=subagent-start-hook.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"subagent-start-hook.js","sourceRoot":"","sources":["../../src/hook/subagent-start-hook.ts"],"names":[],"mappings":"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"}