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.
- package/README.md +2 -0
- package/dist/cli/digest.js +39 -1
- package/dist/cli/digest.js.map +1 -1
- package/dist/cli/nlm.js +106 -6
- package/dist/cli/nlm.js.map +1 -1
- package/dist/cli/scope-backfill.d.ts +34 -0
- package/dist/cli/scope-backfill.js +237 -0
- package/dist/cli/scope-backfill.js.map +1 -0
- package/dist/cli/scope-coverage.d.ts +44 -0
- package/dist/cli/scope-coverage.js +205 -0
- package/dist/cli/scope-coverage.js.map +1 -0
- package/dist/core/adapters/claude-code.d.ts +14 -0
- package/dist/core/adapters/claude-code.js +30 -0
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/dataset/build-dataset.js +1 -0
- package/dist/core/dataset/build-dataset.js.map +1 -1
- package/dist/core/digest/compose.d.ts +3 -3
- package/dist/core/digest/compose.js +16 -17
- package/dist/core/digest/compose.js.map +1 -1
- package/dist/core/digest/hook-injection.d.ts +43 -0
- package/dist/core/digest/hook-injection.js +79 -0
- package/dist/core/digest/hook-injection.js.map +1 -0
- package/dist/core/exemplars/capture-from-session.d.ts +1 -0
- package/dist/core/exemplars/capture-from-session.js +1 -1
- package/dist/core/exemplars/capture-from-session.js.map +1 -1
- package/dist/core/exemplars/extract-exemplar.d.ts +2 -0
- package/dist/core/exemplars/extract-exemplar.js +7 -1
- package/dist/core/exemplars/extract-exemplar.js.map +1 -1
- package/dist/core/exemplars/ingest-exemplar.d.ts +1 -0
- package/dist/core/exemplars/ingest-exemplar.js +1 -0
- package/dist/core/exemplars/ingest-exemplar.js.map +1 -1
- package/dist/core/hook/claude-settings.d.ts +1 -1
- package/dist/core/hook/claude-settings.js +0 -1
- package/dist/core/hook/claude-settings.js.map +1 -1
- package/dist/core/ingest/backfill-derivables.d.ts +92 -0
- package/dist/core/ingest/backfill-derivables.js +175 -0
- package/dist/core/ingest/backfill-derivables.js.map +1 -0
- package/dist/core/ingest/ingest-session.js +20 -0
- package/dist/core/ingest/ingest-session.js.map +1 -1
- package/dist/core/ingest/reclassify-oversized.js +6 -0
- package/dist/core/ingest/reclassify-oversized.js.map +1 -1
- package/dist/core/ingest/reprocess.js +5 -0
- package/dist/core/ingest/reprocess.js.map +1 -1
- package/dist/core/ingest/transcript-derivables.d.ts +27 -0
- package/dist/core/ingest/transcript-derivables.js +138 -0
- package/dist/core/ingest/transcript-derivables.js.map +1 -0
- package/dist/core/outcome/coverage.d.ts +33 -0
- package/dist/core/outcome/coverage.js +46 -0
- package/dist/core/outcome/coverage.js.map +1 -0
- package/dist/core/outcome/rollup.d.ts +33 -0
- package/dist/core/outcome/rollup.js +113 -0
- package/dist/core/outcome/rollup.js.map +1 -0
- package/dist/core/recall/hook-recall-log.d.ts +2 -2
- package/dist/core/recall/hook-recall-log.js +2 -2
- package/dist/core/recall-facts/fact-recall-service.js +1 -0
- package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
- package/dist/core/scheduler/scan-once.js +2 -0
- package/dist/core/scheduler/scan-once.js.map +1 -1
- package/dist/core/scheduler/scheduler.js +34 -3
- package/dist/core/scheduler/scheduler.js.map +1 -1
- package/dist/core/scope/alias-map.d.ts +10 -0
- package/dist/core/scope/alias-map.js +41 -0
- package/dist/core/scope/alias-map.js.map +1 -0
- package/dist/core/scope/derive-scope.d.ts +8 -0
- package/dist/core/scope/derive-scope.js +54 -0
- package/dist/core/scope/derive-scope.js.map +1 -0
- package/dist/core/scope/scope-clause.d.ts +16 -0
- package/dist/core/scope/scope-clause.js +21 -0
- package/dist/core/scope/scope-clause.js.map +1 -0
- package/dist/core/scope/stamp-flag.d.ts +1 -0
- package/dist/core/scope/stamp-flag.js +4 -0
- package/dist/core/scope/stamp-flag.js.map +1 -0
- package/dist/core/signals/code-signal.d.ts +1 -0
- package/dist/core/signals/code-signal.js +1 -0
- package/dist/core/signals/code-signal.js.map +1 -1
- package/dist/core/signals/ingest-signal.d.ts +1 -1
- package/dist/core/signals/ingest-signal.js +3 -1
- package/dist/core/signals/ingest-signal.js.map +1 -1
- package/dist/core/signals/stamp-scope.d.ts +18 -0
- package/dist/core/signals/stamp-scope.js +28 -0
- package/dist/core/signals/stamp-scope.js.map +1 -0
- package/dist/core/storage/pg-code-exemplar-store.js +6 -2
- package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/pg-fact-ingest.d.ts +1 -1
- package/dist/core/storage/pg-fact-ingest.js +4 -4
- package/dist/core/storage/pg-fact-ingest.js.map +1 -1
- package/dist/core/storage/pg-session-store.d.ts +2 -2
- package/dist/core/storage/pg-session-store.js +31 -31
- package/dist/core/storage/pg-session-store.js.map +1 -1
- package/dist/core/storage/pg-signal-store.js +4 -3
- package/dist/core/storage/pg-signal-store.js.map +1 -1
- package/dist/core/storage/pg-workstream-store.d.ts +1 -0
- package/dist/core/storage/pg-workstream-store.js +2 -1
- package/dist/core/storage/pg-workstream-store.js.map +1 -1
- package/dist/core/storage/sqlite-code-exemplar-store.js +4 -2
- package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/sqlite-fact-store.d.ts +1 -1
- package/dist/core/storage/sqlite-fact-store.js +8 -5
- package/dist/core/storage/sqlite-fact-store.js.map +1 -1
- package/dist/core/storage/sqlite-outcome-store.d.ts +89 -0
- package/dist/core/storage/sqlite-outcome-store.js +158 -0
- package/dist/core/storage/sqlite-outcome-store.js.map +1 -0
- package/dist/core/storage/sqlite-session-store.d.ts +22 -2
- package/dist/core/storage/sqlite-session-store.js +33 -35
- package/dist/core/storage/sqlite-session-store.js.map +1 -1
- package/dist/core/storage/sqlite-signal-store.js +4 -2
- package/dist/core/storage/sqlite-signal-store.js.map +1 -1
- package/dist/core/storage/sqlite-workstream-store.d.ts +1 -0
- package/dist/core/storage/sqlite-workstream-store.js +2 -1
- package/dist/core/storage/sqlite-workstream-store.js.map +1 -1
- package/dist/core/storage/supersedence-log.js +2 -0
- package/dist/core/storage/supersedence-log.js.map +1 -1
- package/dist/core/telemetry/probe-filter.d.ts +35 -0
- package/dist/core/telemetry/probe-filter.js +56 -0
- package/dist/core/telemetry/probe-filter.js.map +1 -0
- package/dist/core/update-check/check.d.ts +6 -0
- package/dist/core/update-check/check.js +35 -0
- package/dist/core/update-check/check.js.map +1 -1
- package/dist/core/workstream/bind.d.ts +2 -1
- package/dist/core/workstream/bind.js +28 -9
- package/dist/core/workstream/bind.js.map +1 -1
- package/dist/core/workstream/model.d.ts +1 -0
- package/dist/core/workstream/model.js.map +1 -1
- package/dist/hook/pre-compact-hook.js +2 -1
- package/dist/hook/pre-compact-hook.js.map +1 -1
- package/dist/hook/prompt-recall-hook.d.ts +3 -2
- package/dist/hook/prompt-recall-hook.js +8 -4
- package/dist/hook/prompt-recall-hook.js.map +1 -1
- package/dist/hook/recall-over-http.d.ts +2 -1
- package/dist/hook/recall-over-http.js +15 -2
- package/dist/hook/recall-over-http.js.map +1 -1
- package/dist/hook/session-start-hook.js +2 -1
- package/dist/hook/session-start-hook.js.map +1 -1
- package/dist/hook/stop-hook.js +2 -1
- package/dist/hook/stop-hook.js.map +1 -1
- package/dist/http/app.d.ts +6 -0
- package/dist/http/app.js +26 -38
- package/dist/http/app.js.map +1 -1
- package/dist/mcp/server.d.ts +22 -0
- package/dist/mcp/server.js +101 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/ports/outcome.d.ts +85 -0
- package/dist/ports/outcome.js +11 -0
- package/dist/ports/outcome.js.map +1 -0
- package/dist/ports/session-store.d.ts +0 -6
- package/dist/ports/workstream-store.d.ts +1 -0
- package/dist/shared/net.d.ts +3 -0
- package/dist/shared/net.js +4 -0
- package/dist/shared/net.js.map +1 -0
- package/dist/shared/types.d.ts +33 -0
- package/dist/shared/types.js +4 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/ui/assets/{index-CMpQkbQy.js → index-CSaBtgXD.js} +1 -1
- package/dist/ui/index.html +1 -1
- package/migrations/029_project_scope.sql +21 -0
- package/migrations/032_session_derivables.sql +11 -0
- package/migrations/pg/030_project_scope.sql +23 -0
- package/migrations/pg/031_widen_session_edges_kind.sql +11 -0
- package/migrations/pg/032_session_derivables.sql +17 -0
- package/nlm/index.js +14 -6
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/scripts/prompt-recall-hook.mjs +15 -6
- package/plugin/scripts/session-start-hook.mjs +11 -3
- package/plugin/scripts/stop-hook.mjs +4 -1
- package/dist/hook/subagent-start-hook.d.ts +0 -28
- package/dist/hook/subagent-start-hook.js +0 -81
- package/dist/hook/subagent-start-hook.js.map +0 -1
package/dist/ui/index.html
CHANGED
|
@@ -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-
|
|
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
|
-
|
|
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"] ??
|
|
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
|
|
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 :
|
|
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.
|
|
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
|
-
|
|
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"] ??
|
|
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
|
|
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 :
|
|
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
|
-
|
|
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"] ??
|
|
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"] ??
|
|
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"] ??
|
|
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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"}
|