nlm-memory 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/dist/cli/digest.js +21 -0
- package/dist/cli/digest.js.map +1 -1
- package/dist/cli/init.d.ts +12 -0
- package/dist/cli/init.js +49 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/nlm.d.ts +13 -1
- package/dist/cli/nlm.js +766 -55
- package/dist/cli/nlm.js.map +1 -1
- package/dist/core/actions/overlay.d.ts +20 -6
- package/dist/core/actions/overlay.js +32 -18
- package/dist/core/actions/overlay.js.map +1 -1
- package/dist/core/adapters/claude-code.js +6 -0
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/classifier/hierarchical-classify.d.ts +6 -2
- package/dist/core/classifier/hierarchical-classify.js +10 -6
- package/dist/core/classifier/hierarchical-classify.js.map +1 -1
- package/dist/core/classifier/prompt.js +3 -3
- package/dist/core/digest/compose.d.ts +10 -0
- package/dist/core/digest/compose.js +7 -1
- package/dist/core/digest/compose.js.map +1 -1
- package/dist/core/embedding/embed-backfill.d.ts +13 -2
- package/dist/core/embedding/embed-backfill.js +181 -43
- package/dist/core/embedding/embed-backfill.js.map +1 -1
- package/dist/core/embedding/embedding-config.d.ts +17 -0
- package/dist/core/embedding/embedding-config.js +12 -0
- package/dist/core/embedding/embedding-config.js.map +1 -0
- package/dist/core/embedding/pg-embed-backfill.d.ts +31 -0
- package/dist/core/embedding/pg-embed-backfill.js +264 -0
- package/dist/core/embedding/pg-embed-backfill.js.map +1 -0
- package/dist/core/entities/dedup-suggest.d.ts +25 -0
- package/dist/core/entities/dedup-suggest.js +107 -0
- package/dist/core/entities/dedup-suggest.js.map +1 -0
- package/dist/core/eval/classifier-fixture-eval.d.ts +29 -0
- package/dist/core/eval/classifier-fixture-eval.js +217 -0
- package/dist/core/eval/classifier-fixture-eval.js.map +1 -0
- package/dist/core/eval/extraction-scoring.d.ts +63 -0
- package/dist/core/eval/extraction-scoring.js +79 -0
- package/dist/core/eval/extraction-scoring.js.map +1 -0
- package/dist/core/eval/run-eval.d.ts +40 -0
- package/dist/core/eval/run-eval.js +34 -0
- package/dist/core/eval/run-eval.js.map +1 -0
- package/dist/core/exemplars/capture-from-session.d.ts +1 -0
- package/dist/core/exemplars/capture-from-session.js +6 -2
- package/dist/core/exemplars/capture-from-session.js.map +1 -1
- package/dist/core/exemplars/diff-parse.d.ts +23 -0
- package/dist/core/exemplars/diff-parse.js +73 -0
- package/dist/core/exemplars/diff-parse.js.map +1 -0
- package/dist/core/exemplars/embed-backfill.d.ts +36 -0
- package/dist/core/exemplars/embed-backfill.js +85 -0
- package/dist/core/exemplars/embed-backfill.js.map +1 -0
- package/dist/core/exemplars/embed-text.d.ts +15 -0
- package/dist/core/exemplars/embed-text.js +18 -0
- package/dist/core/exemplars/embed-text.js.map +1 -0
- package/dist/core/exemplars/extract-exemplar.js +2 -65
- package/dist/core/exemplars/extract-exemplar.js.map +1 -1
- package/dist/core/exemplars/recall-code.js +4 -0
- package/dist/core/exemplars/recall-code.js.map +1 -1
- package/dist/core/exemplars/warm-embedder.d.ts +14 -0
- package/dist/core/exemplars/warm-embedder.js +18 -0
- package/dist/core/exemplars/warm-embedder.js.map +1 -0
- package/dist/core/health/corpus-state.d.ts +8 -0
- package/dist/core/health/corpus-state.js +11 -0
- package/dist/core/health/corpus-state.js.map +1 -0
- package/dist/core/health/embed-inflight.d.ts +9 -0
- package/dist/core/health/embed-inflight.js +30 -0
- package/dist/core/health/embed-inflight.js.map +1 -0
- package/dist/core/health/embedding-lane-state.d.ts +6 -0
- package/dist/core/health/embedding-lane-state.js +18 -0
- package/dist/core/health/embedding-lane-state.js.map +1 -0
- package/dist/core/health/warmup-state.d.ts +12 -0
- package/dist/core/health/warmup-state.js +12 -0
- package/dist/core/health/warmup-state.js.map +1 -0
- package/dist/core/hook/gate.d.ts +13 -6
- package/dist/core/hook/gate.js +47 -6
- package/dist/core/hook/gate.js.map +1 -1
- package/dist/core/hook/hook-log.d.ts +5 -0
- package/dist/core/hook/hook-log.js.map +1 -1
- package/dist/core/hook/memo.d.ts +12 -0
- package/dist/core/hook/memo.js +41 -1
- package/dist/core/hook/memo.js.map +1 -1
- package/dist/core/hook/pointer-block.d.ts +1 -0
- package/dist/core/hook/pointer-block.js +23 -2
- package/dist/core/hook/pointer-block.js.map +1 -1
- package/dist/core/hook/query-extract.d.ts +2 -1
- package/dist/core/hook/query-extract.js +10 -1
- package/dist/core/hook/query-extract.js.map +1 -1
- package/dist/core/hook/resolve-conversation-by-query.d.ts +28 -0
- package/dist/core/hook/resolve-conversation-by-query.js +98 -0
- package/dist/core/hook/resolve-conversation-by-query.js.map +1 -0
- package/dist/core/hook/select.d.ts +8 -0
- package/dist/core/hook/select.js +9 -2
- package/dist/core/hook/select.js.map +1 -1
- package/dist/core/hook/transcript.js +16 -2
- package/dist/core/hook/transcript.js.map +1 -1
- package/dist/core/ingest/ingest-session.d.ts +5 -0
- package/dist/core/ingest/ingest-session.js +3 -0
- package/dist/core/ingest/ingest-session.js.map +1 -1
- package/dist/core/ingest/reclassify-oversized.d.ts +5 -0
- package/dist/core/ingest/reclassify-oversized.js +3 -0
- package/dist/core/ingest/reclassify-oversized.js.map +1 -1
- package/dist/core/ingest/reprocess.d.ts +96 -0
- package/dist/core/ingest/reprocess.js +275 -0
- package/dist/core/ingest/reprocess.js.map +1 -0
- package/dist/core/integrity/check-invariants.d.ts +6 -1
- package/dist/core/integrity/check-invariants.js +209 -3
- package/dist/core/integrity/check-invariants.js.map +1 -1
- package/dist/core/metrics/corpus-stats.d.ts +49 -0
- package/dist/core/metrics/corpus-stats.js +111 -0
- package/dist/core/metrics/corpus-stats.js.map +1 -0
- package/dist/core/metrics/re-derivation.d.ts +48 -0
- package/dist/core/metrics/re-derivation.js +87 -0
- package/dist/core/metrics/re-derivation.js.map +1 -0
- package/dist/core/recall/citation-log.d.ts +8 -0
- package/dist/core/recall/citation-log.js +17 -0
- package/dist/core/recall/citation-log.js.map +1 -1
- package/dist/core/recall/query-intent.d.ts +7 -0
- package/dist/core/recall/query-intent.js +25 -0
- package/dist/core/recall/query-intent.js.map +1 -0
- package/dist/core/recall/query-log.d.ts +4 -0
- package/dist/core/recall/query-log.js +4 -0
- package/dist/core/recall/query-log.js.map +1 -1
- package/dist/core/recall/recall-service.d.ts +37 -1
- package/dist/core/recall/recall-service.js +106 -63
- package/dist/core/recall/recall-service.js.map +1 -1
- package/dist/core/recall/related-exemplars.d.ts +1 -0
- package/dist/core/recall/related-exemplars.js +4 -1
- package/dist/core/recall/related-exemplars.js.map +1 -1
- package/dist/core/recall/related-facts.js +2 -2
- package/dist/core/recall/related-facts.js.map +1 -1
- package/dist/core/recall/reranker.d.ts +2 -1
- package/dist/core/recall/reranker.js +3 -3
- package/dist/core/recall/reranker.js.map +1 -1
- package/dist/core/recall-facts/fact-query-log.d.ts +7 -0
- package/dist/core/recall-facts/fact-query-log.js +2 -0
- package/dist/core/recall-facts/fact-query-log.js.map +1 -1
- package/dist/core/recall-facts/fact-recall-service.d.ts +9 -1
- package/dist/core/recall-facts/fact-recall-service.js +21 -8
- package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
- package/dist/core/scheduler/scan-once.d.ts +6 -6
- package/dist/core/scheduler/scan-once.js +13 -49
- package/dist/core/scheduler/scan-once.js.map +1 -1
- package/dist/core/scheduler/scheduler.d.ts +10 -0
- package/dist/core/scheduler/scheduler.js +80 -28
- package/dist/core/scheduler/scheduler.js.map +1 -1
- package/dist/core/signals/code-signal.d.ts +49 -0
- package/dist/core/signals/code-signal.js +53 -0
- package/dist/core/signals/code-signal.js.map +1 -0
- package/dist/core/storage/db-restore.js +3 -1
- package/dist/core/storage/db-restore.js.map +1 -1
- package/dist/core/storage/fact-batch.d.ts +9 -0
- package/dist/core/storage/fact-batch.js +14 -0
- package/dist/core/storage/fact-batch.js.map +1 -0
- package/dist/core/storage/pg-code-exemplar-store.d.ts +1 -0
- package/dist/core/storage/pg-code-exemplar-store.js +7 -0
- package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/pg-embedding-config.d.ts +29 -0
- package/dist/core/storage/pg-embedding-config.js +53 -0
- package/dist/core/storage/pg-embedding-config.js.map +1 -0
- package/dist/core/storage/pg-entity-store.d.ts +12 -0
- package/dist/core/storage/pg-entity-store.js +79 -0
- package/dist/core/storage/pg-entity-store.js.map +1 -0
- package/dist/core/storage/pg-fact-ingest.d.ts +15 -0
- package/dist/core/storage/pg-fact-ingest.js +34 -0
- package/dist/core/storage/pg-fact-ingest.js.map +1 -0
- package/dist/core/storage/pg-fact-store.d.ts +3 -0
- package/dist/core/storage/pg-fact-store.js +26 -30
- package/dist/core/storage/pg-fact-store.js.map +1 -1
- package/dist/core/storage/pg-migrate.d.ts +18 -0
- package/dist/core/storage/pg-migrate.js +73 -0
- package/dist/core/storage/pg-migrate.js.map +1 -0
- package/dist/core/storage/pg-session-store.d.ts +9 -0
- package/dist/core/storage/pg-session-store.js +204 -76
- package/dist/core/storage/pg-session-store.js.map +1 -1
- package/dist/core/storage/pg-storage.d.ts +9 -7
- package/dist/core/storage/pg-storage.js +20 -50
- package/dist/core/storage/pg-storage.js.map +1 -1
- package/dist/core/storage/pg-workstream-store.d.ts +24 -0
- package/dist/core/storage/pg-workstream-store.js +96 -0
- package/dist/core/storage/pg-workstream-store.js.map +1 -0
- package/dist/core/storage/sqlite-code-exemplar-store.d.ts +1 -0
- package/dist/core/storage/sqlite-code-exemplar-store.js +11 -0
- package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/sqlite-embedding-config.d.ts +10 -0
- package/dist/core/storage/sqlite-embedding-config.js +27 -0
- package/dist/core/storage/sqlite-embedding-config.js.map +1 -0
- package/dist/core/storage/sqlite-entity-store.d.ts +13 -0
- package/dist/core/storage/sqlite-entity-store.js +96 -0
- package/dist/core/storage/sqlite-entity-store.js.map +1 -0
- package/dist/core/storage/sqlite-fact-store.d.ts +15 -7
- package/dist/core/storage/sqlite-fact-store.js +76 -25
- package/dist/core/storage/sqlite-fact-store.js.map +1 -1
- package/dist/core/storage/sqlite-session-store.d.ts +20 -5
- package/dist/core/storage/sqlite-session-store.js +183 -68
- package/dist/core/storage/sqlite-session-store.js.map +1 -1
- package/dist/core/storage/sqlite-storage.d.ts +8 -8
- package/dist/core/storage/sqlite-storage.js +21 -32
- package/dist/core/storage/sqlite-storage.js.map +1 -1
- package/dist/core/storage/sqlite-workstream-store.d.ts +24 -0
- package/dist/core/storage/sqlite-workstream-store.js +98 -0
- package/dist/core/storage/sqlite-workstream-store.js.map +1 -0
- package/dist/core/util/with-timeout.d.ts +5 -0
- package/dist/core/util/with-timeout.js +20 -0
- package/dist/core/util/with-timeout.js.map +1 -0
- package/dist/core/work-digest/active-spans.d.ts +8 -0
- package/dist/core/work-digest/active-spans.js +29 -0
- package/dist/core/work-digest/active-spans.js.map +1 -0
- package/dist/core/work-digest/attribute.d.ts +15 -0
- package/dist/core/work-digest/attribute.js +76 -0
- package/dist/core/work-digest/attribute.js.map +1 -0
- package/dist/core/work-digest/build-work-digest.d.ts +12 -0
- package/dist/core/work-digest/build-work-digest.js +69 -0
- package/dist/core/work-digest/build-work-digest.js.map +1 -0
- package/dist/core/work-digest/compose-work-digest.d.ts +3 -0
- package/dist/core/work-digest/compose-work-digest.js +52 -0
- package/dist/core/work-digest/compose-work-digest.js.map +1 -0
- package/dist/core/work-digest/merge-active.d.ts +10 -0
- package/dist/core/work-digest/merge-active.js +22 -0
- package/dist/core/work-digest/merge-active.js.map +1 -0
- package/dist/core/work-digest/read-transcript-timestamps.d.ts +7 -0
- package/dist/core/work-digest/read-transcript-timestamps.js +40 -0
- package/dist/core/work-digest/read-transcript-timestamps.js.map +1 -0
- package/dist/core/work-digest/topics.d.ts +18 -0
- package/dist/core/work-digest/topics.js +26 -0
- package/dist/core/work-digest/topics.js.map +1 -0
- package/dist/core/work-digest/types.d.ts +45 -0
- package/dist/core/work-digest/types.js +2 -0
- package/dist/core/work-digest/types.js.map +1 -0
- package/dist/core/workstream/bind.d.ts +25 -0
- package/dist/core/workstream/bind.js +23 -0
- package/dist/core/workstream/bind.js.map +1 -0
- package/dist/core/workstream/compose-recall.d.ts +2 -0
- package/dist/core/workstream/compose-recall.js +39 -0
- package/dist/core/workstream/compose-recall.js.map +1 -0
- package/dist/core/workstream/merge-suggest.d.ts +19 -0
- package/dist/core/workstream/merge-suggest.js +61 -0
- package/dist/core/workstream/merge-suggest.js.map +1 -0
- package/dist/core/workstream/model.d.ts +20 -0
- package/dist/core/workstream/model.js +9 -0
- package/dist/core/workstream/model.js.map +1 -0
- package/dist/core/workstream/name-match.d.ts +8 -0
- package/dist/core/workstream/name-match.js +18 -0
- package/dist/core/workstream/name-match.js.map +1 -0
- package/dist/core/workstream/resolve.d.ts +10 -0
- package/dist/core/workstream/resolve.js +21 -0
- package/dist/core/workstream/resolve.js.map +1 -0
- package/dist/core/workstream/rollup.d.ts +12 -0
- package/dist/core/workstream/rollup.js +17 -0
- package/dist/core/workstream/rollup.js.map +1 -0
- package/dist/core/workstream/work-topics.d.ts +10 -0
- package/dist/core/workstream/work-topics.js +55 -0
- package/dist/core/workstream/work-topics.js.map +1 -0
- package/dist/hook/hook-helpers.d.ts +4 -0
- package/dist/hook/hook-helpers.js +36 -0
- package/dist/hook/hook-helpers.js.map +1 -0
- package/dist/hook/pre-compact-hook.d.ts +23 -0
- package/dist/hook/pre-compact-hook.js +66 -0
- package/dist/hook/pre-compact-hook.js.map +1 -0
- package/dist/hook/prompt-recall-hook.d.ts +37 -0
- package/dist/hook/prompt-recall-hook.js +120 -20
- package/dist/hook/prompt-recall-hook.js.map +1 -1
- package/dist/hook/recall-gate.d.ts +29 -0
- package/dist/hook/recall-gate.js +50 -0
- package/dist/hook/recall-gate.js.map +1 -0
- package/dist/hook/recall-over-http.d.ts +1 -1
- package/dist/hook/recall-over-http.js +11 -11
- package/dist/hook/recall-over-http.js.map +1 -1
- package/dist/hook/recent-context.d.ts +25 -0
- package/dist/hook/recent-context.js +104 -0
- package/dist/hook/recent-context.js.map +1 -0
- package/dist/hook/score-floor.d.ts +27 -0
- package/dist/hook/score-floor.js +42 -0
- package/dist/hook/score-floor.js.map +1 -0
- package/dist/hook/session-end-hook.js +8 -29
- package/dist/hook/session-end-hook.js.map +1 -1
- package/dist/hook/session-start-hook.js +24 -59
- package/dist/hook/session-start-hook.js.map +1 -1
- package/dist/hook/stop-hook.js +22 -59
- package/dist/hook/stop-hook.js.map +1 -1
- package/dist/hook/subagent-start-hook.d.ts +28 -0
- package/dist/hook/subagent-start-hook.js +81 -0
- package/dist/hook/subagent-start-hook.js.map +1 -0
- package/dist/http/app.js +28 -8
- package/dist/http/app.js.map +1 -1
- package/dist/install/setup.js +2 -0
- package/dist/install/setup.js.map +1 -1
- package/dist/llm/build-classifier.d.ts +22 -0
- package/dist/llm/build-classifier.js +42 -0
- package/dist/llm/build-classifier.js.map +1 -0
- package/dist/llm/build-embedder.d.ts +11 -0
- package/dist/llm/build-embedder.js +42 -0
- package/dist/llm/build-embedder.js.map +1 -0
- package/dist/llm/bundled-embedder-client.d.ts +37 -0
- package/dist/llm/bundled-embedder-client.js +90 -0
- package/dist/llm/bundled-embedder-client.js.map +1 -0
- package/dist/llm/classifier-box.d.ts +13 -5
- package/dist/llm/classifier-box.js +28 -6
- package/dist/llm/classifier-box.js.map +1 -1
- package/dist/llm/classifier-egress.d.ts +18 -0
- package/dist/llm/classifier-egress.js +59 -0
- package/dist/llm/classifier-egress.js.map +1 -0
- package/dist/llm/client-shared.d.ts +4 -0
- package/dist/llm/client-shared.js +39 -0
- package/dist/llm/client-shared.js.map +1 -0
- package/dist/llm/deepseek-client.d.ts +20 -0
- package/dist/llm/deepseek-client.js +59 -45
- package/dist/llm/deepseek-client.js.map +1 -1
- package/dist/llm/embedder-info.d.ts +15 -0
- package/dist/llm/embedder-info.js +27 -0
- package/dist/llm/embedder-info.js.map +1 -0
- package/dist/llm/model-quirks.d.ts +4 -0
- package/dist/llm/model-quirks.js +7 -0
- package/dist/llm/model-quirks.js.map +1 -0
- package/dist/llm/naming.d.ts +5 -0
- package/dist/llm/naming.js +20 -0
- package/dist/llm/naming.js.map +1 -0
- package/dist/llm/ollama-client.d.ts +7 -8
- package/dist/llm/ollama-client.js +59 -51
- package/dist/llm/ollama-client.js.map +1 -1
- package/dist/llm/ollama-code-embedder.d.ts +8 -3
- package/dist/llm/ollama-code-embedder.js +14 -24
- package/dist/llm/ollama-code-embedder.js.map +1 -1
- package/dist/llm/openai-code-embedder-client.d.ts +25 -0
- package/dist/llm/openai-code-embedder-client.js +52 -0
- package/dist/llm/openai-code-embedder-client.js.map +1 -0
- package/dist/llm/openai-embedder-client.d.ts +41 -0
- package/dist/llm/openai-embedder-client.js +83 -0
- package/dist/llm/openai-embedder-client.js.map +1 -0
- package/dist/mcp/server.d.ts +47 -2
- package/dist/mcp/server.js +303 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/ports/code-embedder.d.ts +6 -1
- package/dist/ports/code-exemplar-store.d.ts +5 -0
- package/dist/ports/entity-store.d.ts +35 -0
- package/dist/ports/entity-store.js +7 -0
- package/dist/ports/entity-store.js.map +1 -0
- package/dist/ports/fact-store.d.ts +0 -0
- package/dist/ports/fact-store.js.map +1 -1
- package/dist/ports/llm-client.d.ts +9 -1
- package/dist/ports/session-store.d.ts +12 -0
- package/dist/ports/storage.d.ts +7 -30
- package/dist/ports/storage.js +3 -7
- package/dist/ports/storage.js.map +1 -1
- package/dist/ports/transcript-adapter.d.ts +2 -0
- package/dist/ports/workstream-store.d.ts +21 -0
- package/dist/ports/workstream-store.js +2 -0
- package/dist/ports/workstream-store.js.map +1 -0
- package/dist/shared/types.d.ts +23 -2
- package/fixtures/classifier-gold/README.md +94 -0
- package/fixtures/classifier-gold/reference.json +318 -0
- package/fixtures/classifier-gold/transcripts/01-bug-fix-null-pointer-api.txt +75 -0
- package/fixtures/classifier-gold/transcripts/02-bug-fix-race-condition-queue.txt +77 -0
- package/fixtures/classifier-gold/transcripts/03-bug-fix-jwt-clock-skew.txt +86 -0
- package/fixtures/classifier-gold/transcripts/04-feature-webhook-retry.txt +95 -0
- package/fixtures/classifier-gold/transcripts/05-feature-csv-export.txt +86 -0
- package/fixtures/classifier-gold/transcripts/06-feature-rate-limiting.txt +61 -0
- package/fixtures/classifier-gold/transcripts/07-refactor-db-abstraction.txt +72 -0
- package/fixtures/classifier-gold/transcripts/08-refactor-config-loading.txt +89 -0
- package/fixtures/classifier-gold/transcripts/09-ops-docker-staging.txt +94 -0
- package/fixtures/classifier-gold/transcripts/10-ops-nginx-ssl.txt +121 -0
- package/fixtures/classifier-gold/transcripts/11-ops-postgres-migration.txt +74 -0
- package/fixtures/classifier-gold/transcripts/12-ops-cron-debugging.txt +73 -0
- package/fixtures/classifier-gold/transcripts/13-research-vector-db-comparison.txt +59 -0
- package/fixtures/classifier-gold/transcripts/14-research-oauth-provider.txt +74 -0
- package/fixtures/classifier-gold/transcripts/15-writing-api-docs.txt +154 -0
- package/fixtures/classifier-gold/transcripts/16-meeting-sprint-planning.txt +69 -0
- package/fixtures/classifier-gold/transcripts/17-trivial-list-files.txt +14 -0
- package/fixtures/classifier-gold/transcripts/18-trivial-version-check.txt +21 -0
- package/fixtures/classifier-gold/transcripts/19-trivial-syntax-help.txt +29 -0
- package/fixtures/classifier-gold/transcripts/20-trivial-read-package-json.txt +41 -0
- package/migrations/000_initial_schema.sql +1 -1
- package/migrations/024_backfill_exemplar_repo_basename.sql +32 -0
- package/migrations/025_workstreams.sql +30 -0
- package/migrations/026_embedding_config.sql +12 -0
- package/migrations/027_drop_legacy_session_embeddings.sql +7 -0
- package/migrations/028_classifier_provenance.sql +5 -0
- package/migrations/pg/001_initial.sql +34 -0
- package/migrations/pg/019_split_replaces.sql +16 -7
- package/migrations/pg/025_workstreams.sql +34 -0
- package/migrations/pg/026_embedding_config.sql +12 -0
- package/migrations/pg/028_classifier_provenance.sql +5 -0
- package/migrations/pg/029_entity_variants.sql +9 -0
- package/nlm/index.js +395 -37
- package/package.json +12 -2
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/scripts/prompt-recall-hook.mjs +402 -41
- package/plugin/scripts/session-start-hook.mjs +286 -60
- package/plugin/scripts/stop-hook.mjs +85 -65
- package/plugin-hermes-agent/__init__.py +2 -1
- package/templates/agent-contract/README.md +24 -0
- package/templates/agent-contract/claude-code.md +40 -0
- package/templates/agent-contract/generic.md +38 -0
- package/dist/core/embedding/embed-normalize.d.ts +0 -28
- package/dist/core/embedding/embed-normalize.js +0 -98
- package/dist/core/embedding/embed-normalize.js.map +0 -1
- package/dist/core/storage/pg-tx-context.d.ts +0 -52
- package/dist/core/storage/pg-tx-context.js +0 -149
- package/dist/core/storage/pg-tx-context.js.map +0 -1
|
@@ -13,60 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { execFileSync } from "node:child_process";
|
|
15
15
|
import { codeHash, normalizeExemplar } from "./ingest-exemplar.js";
|
|
16
|
-
|
|
17
|
-
function parseFuncname(hunkHeader) {
|
|
18
|
-
const m = hunkHeader.match(/@@ [^@]+ @@ (.+)/);
|
|
19
|
-
return m?.[1]?.trim() ?? null;
|
|
20
|
-
}
|
|
21
|
-
/** Detect likely language from file extension. */
|
|
22
|
-
function detectLang(filePath) {
|
|
23
|
-
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
24
|
-
const map = {
|
|
25
|
-
ts: "ts", tsx: "ts", js: "js", jsx: "js", mjs: "js",
|
|
26
|
-
py: "py", go: "go", rb: "rb", rs: "rs", java: "java",
|
|
27
|
-
c: "c", cpp: "cpp", cs: "cs", swift: "swift", kt: "kt",
|
|
28
|
-
sh: "sh", bash: "sh", zsh: "sh",
|
|
29
|
-
};
|
|
30
|
-
return map[ext] ?? null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Parse a unified diff into individual hunks. Returns (file, hunkHeader, body) triples.
|
|
34
|
-
*/
|
|
35
|
-
function parseHunks(diff) {
|
|
36
|
-
const result = [];
|
|
37
|
-
let currentFile = "";
|
|
38
|
-
let currentHeader = "";
|
|
39
|
-
let bodyLines = [];
|
|
40
|
-
for (const line of diff.split("\n")) {
|
|
41
|
-
if (line.startsWith("+++ b/")) {
|
|
42
|
-
currentFile = line.slice(6);
|
|
43
|
-
}
|
|
44
|
-
else if (line.startsWith("@@ ")) {
|
|
45
|
-
if (currentHeader && bodyLines.length > 0) {
|
|
46
|
-
result.push({ file: currentFile, hunkHeader: currentHeader, body: bodyLines.join("\n") });
|
|
47
|
-
}
|
|
48
|
-
currentHeader = line;
|
|
49
|
-
bodyLines = [];
|
|
50
|
-
}
|
|
51
|
-
else if (currentHeader && (line.startsWith("+") || line.startsWith("-") || line.startsWith(" "))) {
|
|
52
|
-
bodyLines.push(line);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (currentHeader && bodyLines.length > 0) {
|
|
56
|
-
result.push({ file: currentFile, hunkHeader: currentHeader, body: bodyLines.join("\n") });
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Extract the added/changed lines from a hunk body (strip context and removed lines).
|
|
62
|
-
*/
|
|
63
|
-
function extractAddedLines(hunkBody) {
|
|
64
|
-
return hunkBody
|
|
65
|
-
.split("\n")
|
|
66
|
-
.filter((l) => l.startsWith("+"))
|
|
67
|
-
.map((l) => l.slice(1))
|
|
68
|
-
.join("\n");
|
|
69
|
-
}
|
|
16
|
+
import { detectLang, extractAddedLines, parseFuncname, parseHunks, selectLargestHunk } from "./diff-parse.js";
|
|
70
17
|
/**
|
|
71
18
|
* Path (a): extract exemplar from a git commit's diff.
|
|
72
19
|
* Returns null if the git command fails or no valid hunks are found.
|
|
@@ -83,17 +30,7 @@ export function extractFromGitSha(params) {
|
|
|
83
30
|
const hunks = parseHunks(diff);
|
|
84
31
|
if (hunks.length === 0)
|
|
85
32
|
return null;
|
|
86
|
-
|
|
87
|
-
let best;
|
|
88
|
-
let bestCount = 0;
|
|
89
|
-
for (const h of hunks) {
|
|
90
|
-
const added = extractAddedLines(h.body);
|
|
91
|
-
const count = added.split("\n").filter((l) => l.trim()).length;
|
|
92
|
-
if (count > bestCount) {
|
|
93
|
-
bestCount = count;
|
|
94
|
-
best = h;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
33
|
+
const best = selectLargestHunk(hunks);
|
|
97
34
|
if (!best)
|
|
98
35
|
return null;
|
|
99
36
|
const code = extractAddedLines(best.body);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-exemplar.js","sourceRoot":"","sources":["../../../src/core/exemplars/extract-exemplar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"extract-exemplar.js","sourceRoot":"","sources":["../../../src/core/exemplars/extract-exemplar.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
* Negatives are returned clearly labeled — "code that failed the gate" is
|
|
7
7
|
* as useful to the caller as the positive.
|
|
8
8
|
*/
|
|
9
|
+
import { laneHealth } from "../health/embedding-lane-state.js";
|
|
9
10
|
/**
|
|
10
11
|
* Recall code exemplars. Returns positives (pass/fix) and negatives (fail/exhausted)
|
|
11
12
|
* as separate lists so the caller can surface them with distinct framing.
|
|
12
13
|
*/
|
|
13
14
|
export async function recallCode(opts, store, codeEmbedder, proseFallback) {
|
|
15
|
+
if (laneHealth("code") === "stale") {
|
|
16
|
+
return { positives: [], negatives: [] };
|
|
17
|
+
}
|
|
14
18
|
let queryVector = null;
|
|
15
19
|
if (codeEmbedder) {
|
|
16
20
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recall-code.js","sourceRoot":"","sources":["../../../src/core/exemplars/recall-code.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"recall-code.js","sourceRoot":"","sources":["../../../src/core/exemplars/recall-code.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAYlE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAuB,EACvB,KAAwB,EACxB,YAAiC,EACjC,aAA+B;IAE/B,IAAI,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC;QACnC,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,WAAW,GAAwB,IAAI,CAAC;IAE5C,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC7D,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;IAED,IAAI,CAAC,WAAW,IAAI,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9D,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED,MAAM,MAAM,GAA6B;QACvC,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACtD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,IAAI;QAC/C,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;KACf,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAE7D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC;IAExF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* warm-on-start for the code embedder.
|
|
3
|
+
*
|
|
4
|
+
* recall_code is vector-only and the capture path embeds fire-and-forget, so
|
|
5
|
+
* the very first real capture against a cold CodeRankEmbed model can time out
|
|
6
|
+
* and silently drop the vector. Warming the model once at daemon boot pays
|
|
7
|
+
* that load cost up front, off the request path.
|
|
8
|
+
*
|
|
9
|
+
* Gated on NLM_CODE_EXEMPLARS_ENABLED=1 (the same flag that gates capture).
|
|
10
|
+
* Best-effort and non-blocking: the embed is a throwaway, and any failure is
|
|
11
|
+
* swallowed — a cold embedder must never block or crash daemon startup.
|
|
12
|
+
*/
|
|
13
|
+
import type { CodeEmbedder } from "../../ports/code-embedder.js";
|
|
14
|
+
export declare function warmCodeEmbedder(embedder: CodeEmbedder): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* warm-on-start for the code embedder.
|
|
3
|
+
*
|
|
4
|
+
* recall_code is vector-only and the capture path embeds fire-and-forget, so
|
|
5
|
+
* the very first real capture against a cold CodeRankEmbed model can time out
|
|
6
|
+
* and silently drop the vector. Warming the model once at daemon boot pays
|
|
7
|
+
* that load cost up front, off the request path.
|
|
8
|
+
*
|
|
9
|
+
* Gated on NLM_CODE_EXEMPLARS_ENABLED=1 (the same flag that gates capture).
|
|
10
|
+
* Best-effort and non-blocking: the embed is a throwaway, and any failure is
|
|
11
|
+
* swallowed — a cold embedder must never block or crash daemon startup.
|
|
12
|
+
*/
|
|
13
|
+
export function warmCodeEmbedder(embedder) {
|
|
14
|
+
if (process.env["NLM_CODE_EXEMPLARS_ENABLED"] !== "1")
|
|
15
|
+
return;
|
|
16
|
+
void embedder.embed("warm", "query").catch(() => { });
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=warm-embedder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warm-embedder.js","sourceRoot":"","sources":["../../../src/core/exemplars/warm-embedder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACrD,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,KAAK,GAAG;QAAE,OAAO;IAC9D,KAAK,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAA0C,CAAC,CAAC,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CorpusStats } from "../metrics/corpus-stats.js";
|
|
2
|
+
export interface CorpusSnapshot extends CorpusStats {
|
|
3
|
+
readonly state: "ok" | "warn" | "alert";
|
|
4
|
+
readonly lastComputedAt: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function setCorpusSnapshot(s: CorpusSnapshot): void;
|
|
7
|
+
export declare function corpusSnapshot(): CorpusSnapshot | null;
|
|
8
|
+
export declare function resetForTests(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus-state.js","sourceRoot":"","sources":["../../../src/core/health/corpus-state.ts"],"names":[],"mappings":"AAOA,IAAI,QAAQ,GAA0B,IAAI,CAAC;AAE3C,MAAM,UAAU,iBAAiB,CAAC,CAAiB;IACjD,QAAQ,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function tryAcquire(): boolean;
|
|
2
|
+
export declare function release(): void;
|
|
3
|
+
export interface EmbedInflightSnapshot {
|
|
4
|
+
readonly current: number;
|
|
5
|
+
readonly cap: number;
|
|
6
|
+
readonly shedTotal: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function inflightSnapshot(): EmbedInflightSnapshot;
|
|
9
|
+
export declare function resetForTests(): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const DEFAULT_CAP = 4;
|
|
2
|
+
function parseCap(raw) {
|
|
3
|
+
if (raw === undefined)
|
|
4
|
+
return DEFAULT_CAP;
|
|
5
|
+
const n = parseInt(raw, 10);
|
|
6
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_CAP;
|
|
7
|
+
}
|
|
8
|
+
const cap = parseCap(process.env["NLM_RECALL_MAX_INFLIGHT_EMBEDS"]);
|
|
9
|
+
let current = 0;
|
|
10
|
+
let shedTotal = 0;
|
|
11
|
+
export function tryAcquire() {
|
|
12
|
+
if (current >= cap) {
|
|
13
|
+
shedTotal++;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
current++;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
export function release() {
|
|
20
|
+
if (current > 0)
|
|
21
|
+
current--;
|
|
22
|
+
}
|
|
23
|
+
export function inflightSnapshot() {
|
|
24
|
+
return { current, cap, shedTotal };
|
|
25
|
+
}
|
|
26
|
+
export function resetForTests() {
|
|
27
|
+
current = 0;
|
|
28
|
+
shedTotal = 0;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=embed-inflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed-inflight.js","sourceRoot":"","sources":["../../../src/core/health/embed-inflight.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,SAAS,QAAQ,CAAC,GAAuB;IACvC,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC1C,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5B,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AACvD,CAAC;AAED,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC,CAAC;AAEpE,IAAI,OAAO,GAAG,CAAC,CAAC;AAChB,IAAI,SAAS,GAAG,CAAC,CAAC;AAElB,MAAM,UAAU,UAAU;IACxB,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;QACnB,SAAS,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,EAAE,CAAC;IACV,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;AAC7B,CAAC;AAQD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,GAAG,CAAC,CAAC;IACZ,SAAS,GAAG,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EmbeddingLane } from "../embedding/embedding-config.js";
|
|
2
|
+
export type EmbeddingLaneHealth = "unknown" | "ok" | "stale";
|
|
3
|
+
export declare function setLaneHealth(lane: EmbeddingLane, health: EmbeddingLaneHealth): void;
|
|
4
|
+
export declare function laneHealth(lane: EmbeddingLane): EmbeddingLaneHealth;
|
|
5
|
+
export declare function laneHealthSnapshot(): Readonly<Record<EmbeddingLane, EmbeddingLaneHealth>>;
|
|
6
|
+
export declare function resetLaneHealthForTests(): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const state = {
|
|
2
|
+
prose: "unknown",
|
|
3
|
+
code: "unknown",
|
|
4
|
+
};
|
|
5
|
+
export function setLaneHealth(lane, health) {
|
|
6
|
+
state[lane] = health;
|
|
7
|
+
}
|
|
8
|
+
export function laneHealth(lane) {
|
|
9
|
+
return state[lane];
|
|
10
|
+
}
|
|
11
|
+
export function laneHealthSnapshot() {
|
|
12
|
+
return Object.freeze({ ...state });
|
|
13
|
+
}
|
|
14
|
+
export function resetLaneHealthForTests() {
|
|
15
|
+
state.prose = "unknown";
|
|
16
|
+
state.code = "unknown";
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=embedding-lane-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-lane-state.js","sourceRoot":"","sources":["../../../src/core/health/embedding-lane-state.ts"],"names":[],"mappings":"AAIA,MAAM,KAAK,GAA+C;IACxD,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,IAAmB,EAAE,MAA2B;IAC5E,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAmB;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface WarmupState {
|
|
2
|
+
fts5: boolean;
|
|
3
|
+
textEmbedder: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function markWarm(stage: keyof WarmupState): void;
|
|
6
|
+
export declare function warmupSnapshot(): {
|
|
7
|
+
fts5: boolean;
|
|
8
|
+
textEmbedder: boolean;
|
|
9
|
+
ready: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function resetWarmupState(): void;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const state = { fts5: false, textEmbedder: false };
|
|
2
|
+
export function markWarm(stage) {
|
|
3
|
+
state[stage] = true;
|
|
4
|
+
}
|
|
5
|
+
export function warmupSnapshot() {
|
|
6
|
+
return { fts5: state.fts5, textEmbedder: state.textEmbedder, ready: state.fts5 && state.textEmbedder };
|
|
7
|
+
}
|
|
8
|
+
export function resetWarmupState() {
|
|
9
|
+
state.fts5 = false;
|
|
10
|
+
state.textEmbedder = false;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=warmup-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warmup-state.js","sourceRoot":"","sources":["../../../src/core/health/warmup-state.ts"],"names":[],"mappings":"AAKA,MAAM,KAAK,GAAgB,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AAEhE,MAAM,UAAU,QAAQ,CAAC,KAAwB;IAC/C,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;AACzG,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;IACnB,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;AAC7B,CAAC"}
|
package/dist/core/hook/gate.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompt gate for the recall hook. Pure — no I/O.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two excluders, both conservative (a wrong exclusion skips useful recall — the
|
|
5
|
+
* failure this feature exists to prevent — so both sets are deliberately tight):
|
|
6
|
+
*
|
|
7
|
+
* - "skip": no user query to serve. Harness-injected events (IDE selections,
|
|
8
|
+
* task notifications, system reminders), bare acknowledgements ("ok", "do
|
|
9
|
+
* it"), and empty/punctuation prompts. Measured: ~41% of historical hook
|
|
10
|
+
* injections fired on these — pure context flooding that can never be cited.
|
|
11
|
+
* SAFE by construction: any prompt that still carries real user content after
|
|
12
|
+
* stripping harness boilerplate falls through and still fires.
|
|
13
|
+
* - "generative": high-precision generative openers ("write", "draft", …).
|
|
14
|
+
*
|
|
15
|
+
* Everything else defaults to "evaluate" (run recall).
|
|
9
16
|
*/
|
|
10
|
-
export type PromptClass = "generative" | "evaluate";
|
|
17
|
+
export type PromptClass = "generative" | "evaluate" | "skip";
|
|
11
18
|
export declare function classifyPrompt(prompt: string): PromptClass;
|
package/dist/core/hook/gate.js
CHANGED
|
@@ -1,19 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompt gate for the recall hook. Pure — no I/O.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two excluders, both conservative (a wrong exclusion skips useful recall — the
|
|
5
|
+
* failure this feature exists to prevent — so both sets are deliberately tight):
|
|
6
|
+
*
|
|
7
|
+
* - "skip": no user query to serve. Harness-injected events (IDE selections,
|
|
8
|
+
* task notifications, system reminders), bare acknowledgements ("ok", "do
|
|
9
|
+
* it"), and empty/punctuation prompts. Measured: ~41% of historical hook
|
|
10
|
+
* injections fired on these — pure context flooding that can never be cited.
|
|
11
|
+
* SAFE by construction: any prompt that still carries real user content after
|
|
12
|
+
* stripping harness boilerplate falls through and still fires.
|
|
13
|
+
* - "generative": high-precision generative openers ("write", "draft", …).
|
|
14
|
+
*
|
|
15
|
+
* Everything else defaults to "evaluate" (run recall).
|
|
9
16
|
*/
|
|
10
17
|
const LEADING_FILLER = /^(please|can you|could you|would you|will you|i need you to|i'd like you to|i want you to|i would like you to|help me|let's|lets|hey|ok|okay)\b[\s,]*/i;
|
|
11
18
|
const GENERATIVE_OPENER = /^(write|draft|create|compose|generate|brainstorm|design|outline|sketch|invent|rename|come up with)\b/i;
|
|
19
|
+
// Harness-injected tags. These are emitted by the agent runtime, not typed by
|
|
20
|
+
// the user, so they carry no query intent on their own. We strip the whole
|
|
21
|
+
// block (well-formed `<tag>…</tag>`, or to end-of-string if unclosed) — a real
|
|
22
|
+
// user query appended after the block survives and still triggers recall.
|
|
23
|
+
const HARNESS_TAGS = [
|
|
24
|
+
"ide_selection",
|
|
25
|
+
"ide_opened_file",
|
|
26
|
+
"ide_closed_file",
|
|
27
|
+
"ide_diagnostics",
|
|
28
|
+
"ide_recently_modified_file",
|
|
29
|
+
"task-notification",
|
|
30
|
+
"system-reminder",
|
|
31
|
+
"command-name",
|
|
32
|
+
"command-message",
|
|
33
|
+
"command-args",
|
|
34
|
+
"local-command-stdout",
|
|
35
|
+
"local-command-stderr",
|
|
36
|
+
];
|
|
37
|
+
const HARNESS_BLOCK = new RegExp(`<(${HARNESS_TAGS.join("|")})\\b[^>]*>[\\s\\S]*?(?:</\\1>|$)`, "gi");
|
|
38
|
+
// Whole-prompt acknowledgements — matched against the entire residual, so a
|
|
39
|
+
// real query that merely opens with an ack word ("ok so why does X…") is NOT
|
|
40
|
+
// caught. A bare ack has no recall value.
|
|
41
|
+
const ACK_ONLY = /^(yes|yep|yeah|sure|ok|okay|k|thanks|thank you|ty|done|nice|cool|great|perfect|continue|next|go|go ahead|do it|yes please|sounds good|good|got it|right|correct)\W*$/i;
|
|
42
|
+
function contentWordCount(s) {
|
|
43
|
+
const m = s.match(/[A-Za-z0-9]{2,}/g);
|
|
44
|
+
return m ? m.length : 0;
|
|
45
|
+
}
|
|
12
46
|
export function classifyPrompt(prompt) {
|
|
13
|
-
let p = prompt.trim();
|
|
47
|
+
let p = prompt.replace(HARNESS_BLOCK, " ").replace(/\s+/g, " ").trim();
|
|
48
|
+
if (contentWordCount(p) === 0)
|
|
49
|
+
return "skip"; // harness-only / empty / punctuation
|
|
50
|
+
if (ACK_ONLY.test(p))
|
|
51
|
+
return "skip"; // bare acknowledgement
|
|
14
52
|
for (let i = 0; i < 3 && LEADING_FILLER.test(p); i++) {
|
|
15
53
|
p = p.replace(LEADING_FILLER, "");
|
|
16
54
|
}
|
|
55
|
+
p = p.trim();
|
|
56
|
+
if (contentWordCount(p) === 0)
|
|
57
|
+
return "skip"; // was pure leading filler ("ok", "lets")
|
|
17
58
|
return GENERATIVE_OPENER.test(p) ? "generative" : "evaluate";
|
|
18
59
|
}
|
|
19
60
|
//# sourceMappingURL=gate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/core/hook/gate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../../src/core/hook/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,cAAc,GAClB,wJAAwJ,CAAC;AAE3J,MAAM,iBAAiB,GACrB,uGAAuG,CAAC;AAE1G,8EAA8E;AAC9E,2EAA2E;AAC3E,+EAA+E;AAC/E,0EAA0E;AAC1E,MAAM,YAAY,GAAG;IACnB,eAAe;IACf,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,4BAA4B;IAC5B,mBAAmB;IACnB,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,cAAc;IACd,sBAAsB;IACtB,sBAAsB;CACvB,CAAC;AACF,MAAM,aAAa,GAAG,IAAI,MAAM,CAC9B,KAAK,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,kCAAkC,EAC7D,IAAI,CACL,CAAC;AAEF,4EAA4E;AAC5E,6EAA6E;AAC7E,0CAA0C;AAC1C,MAAM,QAAQ,GACZ,uKAAuK,CAAC;AAE1K,SAAS,gBAAgB,CAAC,CAAS;IACjC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACvE,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,qCAAqC;IACnF,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,uBAAuB;IAE5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACrD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACb,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,yCAAyC;IAEvF,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC;AAC/D,CAAC"}
|
|
@@ -21,5 +21,10 @@ export interface HookLogEntry {
|
|
|
21
21
|
readonly wouldInject: ReadonlyArray<string>;
|
|
22
22
|
readonly estTokens: number;
|
|
23
23
|
readonly mode: "shadow" | "live";
|
|
24
|
+
/** Per-candidate relevance-gate decisions, when the recall gate ran. */
|
|
25
|
+
readonly gateDecisions?: ReadonlyArray<{
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly gate: "relevant" | "irrelevant";
|
|
28
|
+
}>;
|
|
24
29
|
}
|
|
25
30
|
export declare function appendHookLog(entry: HookLogEntry): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-log.js","sourceRoot":"","sources":["../../../src/core/hook/hook-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-log.js","sourceRoot":"","sources":["../../../src/core/hook/hook-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgB1C,SAAS,OAAO;IACd,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,+EAA+E;QAC/E,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;AACH,CAAC"}
|
package/dist/core/hook/memo.d.ts
CHANGED
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function loadSurfaced(conversationId: string): Set<string>;
|
|
14
14
|
export declare function recordSurfaced(conversationId: string, ids: ReadonlyArray<string>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Reverse lookup: which conversation most recently surfaced `sessionId`?
|
|
17
|
+
*
|
|
18
|
+
* cite_session callers rarely pass conversation_id, so the citation would be
|
|
19
|
+
* logged as the unjoinable "mcp_tool" placeholder (NLM #345). The hook already
|
|
20
|
+
* recorded a per-conversation memo of what it surfaced; scan those (few, since
|
|
21
|
+
* they're cleared on SessionEnd) for the session and return the most-recently-
|
|
22
|
+
* written conversation. Returns null when no live conversation surfaced it.
|
|
23
|
+
*
|
|
24
|
+
* Defensive: any I/O error yields null so cite_session never breaks.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveConversationForSession(sessionId: string): string | null;
|
|
15
27
|
/**
|
|
16
28
|
* Delete the memo file for a closed conversation. Called by the SessionEnd
|
|
17
29
|
* hook so memo files don't accumulate forever. Returns true if a file was
|
package/dist/core/hook/memo.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* memo, and a write failure is swallowed. The hook must never break on memo
|
|
11
11
|
* I/O.
|
|
12
12
|
*/
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
function stateDir() {
|
|
@@ -46,6 +46,46 @@ export function recordSurfaced(conversationId, ids) {
|
|
|
46
46
|
// Memo write failure must never break the hook.
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Reverse lookup: which conversation most recently surfaced `sessionId`?
|
|
51
|
+
*
|
|
52
|
+
* cite_session callers rarely pass conversation_id, so the citation would be
|
|
53
|
+
* logged as the unjoinable "mcp_tool" placeholder (NLM #345). The hook already
|
|
54
|
+
* recorded a per-conversation memo of what it surfaced; scan those (few, since
|
|
55
|
+
* they're cleared on SessionEnd) for the session and return the most-recently-
|
|
56
|
+
* written conversation. Returns null when no live conversation surfaced it.
|
|
57
|
+
*
|
|
58
|
+
* Defensive: any I/O error yields null so cite_session never breaks.
|
|
59
|
+
*/
|
|
60
|
+
export function resolveConversationForSession(sessionId) {
|
|
61
|
+
try {
|
|
62
|
+
const dir = stateDir();
|
|
63
|
+
if (!existsSync(dir))
|
|
64
|
+
return null;
|
|
65
|
+
let best = null;
|
|
66
|
+
for (const file of readdirSync(dir)) {
|
|
67
|
+
if (!file.endsWith(".json"))
|
|
68
|
+
continue;
|
|
69
|
+
const path = join(dir, file);
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
72
|
+
if (!Array.isArray(parsed) || !parsed.includes(sessionId))
|
|
73
|
+
continue;
|
|
74
|
+
const mtimeMs = statSync(path).mtimeMs;
|
|
75
|
+
if (best === null || mtimeMs > best.mtimeMs) {
|
|
76
|
+
best = { conversationId: file.slice(0, -".json".length), mtimeMs };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Skip a corrupt or vanished memo file.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return best?.conversationId ?? null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
49
89
|
/**
|
|
50
90
|
* Delete the memo file for a closed conversation. Called by the SessionEnd
|
|
51
91
|
* hook so memo files don't accumulate forever. Returns true if a file was
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memo.js","sourceRoot":"","sources":["../../../src/core/hook/memo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"memo.js","sourceRoot":"","sources":["../../../src/core/hook/memo.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,4 +29,5 @@ export interface PointerExemplar {
|
|
|
29
29
|
readonly repo: string;
|
|
30
30
|
readonly taskContext: string;
|
|
31
31
|
}
|
|
32
|
+
export declare function truncateSummary(s: string, max?: number): string;
|
|
32
33
|
export declare function formatPointerBlock(hits: ReadonlyArray<PointerHit>, facts?: ReadonlyArray<PointerFact>, exemplars?: ReadonlyArray<PointerExemplar>): string;
|
|
@@ -11,6 +11,27 @@
|
|
|
11
11
|
* `<subject> <predicate>: <value> [N sessions]` so the agent has structured
|
|
12
12
|
* context alongside the session pointers.
|
|
13
13
|
*/
|
|
14
|
+
export function truncateSummary(s, max = 200) {
|
|
15
|
+
if (s.length <= max)
|
|
16
|
+
return s;
|
|
17
|
+
const window = s.slice(0, max);
|
|
18
|
+
const last = window[window.length - 1];
|
|
19
|
+
if ((last === "." || last === "!" || last === "?") && window.length - 1 >= 60) {
|
|
20
|
+
return window;
|
|
21
|
+
}
|
|
22
|
+
for (let i = window.length - 2; i >= 60; i--) {
|
|
23
|
+
const c = window[i];
|
|
24
|
+
if ((c === "." || c === "!" || c === "?") && window[i + 1] === " ") {
|
|
25
|
+
return s.slice(0, i + 1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (let i = window.length - 1; i >= 60; i--) {
|
|
29
|
+
if (window[i] === " ") {
|
|
30
|
+
return s.slice(0, i) + " ...";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return window + " ...";
|
|
34
|
+
}
|
|
14
35
|
export function formatPointerBlock(hits, facts = [], exemplars = []) {
|
|
15
36
|
if (hits.length === 0 && facts.length === 0 && exemplars.length === 0)
|
|
16
37
|
return "";
|
|
@@ -20,7 +41,7 @@ export function formatPointerBlock(hits, facts = [], exemplars = []) {
|
|
|
20
41
|
for (const h of hits) {
|
|
21
42
|
const datePart = h.startedAt.slice(0, 10);
|
|
22
43
|
if (h.summary) {
|
|
23
|
-
out.push(`- ${h.id} · ${h.label} (${datePart}) — ${h.summary
|
|
44
|
+
out.push(`- ${h.id} · ${h.label} (${datePart}) — ${truncateSummary(h.summary)}`);
|
|
24
45
|
}
|
|
25
46
|
else {
|
|
26
47
|
out.push(`- ${h.id} · ${h.label} (${datePart})`);
|
|
@@ -42,7 +63,7 @@ export function formatPointerBlock(hits, facts = [], exemplars = []) {
|
|
|
42
63
|
out.push("## Related code exemplars (nlm-memory)");
|
|
43
64
|
for (const e of exemplars) {
|
|
44
65
|
const langPart = e.lang ? `${e.lang} · ` : "";
|
|
45
|
-
out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext
|
|
66
|
+
out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${truncateSummary(e.taskContext)}`);
|
|
46
67
|
}
|
|
47
68
|
}
|
|
48
69
|
const tools = exemplars.length > 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pointer-block.js","sourceRoot":"","sources":["../../../src/core/hook/pointer-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAuBH,MAAM,UAAU,kBAAkB,CAChC,IAA+B,EAC/B,QAAoC,EAAE,EACtC,YAA4C,EAAE;IAE9C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjF,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBACd,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,KAAK,QAAQ,OAAO,
|
|
1
|
+
{"version":3,"file":"pointer-block.js","sourceRoot":"","sources":["../../../src/core/hook/pointer-block.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns null when the message is too conversational to produce a useful
|
|
3
|
-
* query — the caller should skip
|
|
3
|
+
* query, or is a harness-injected system message — the caller should skip
|
|
4
|
+
* recall entirely in that case.
|
|
4
5
|
*/
|
|
5
6
|
export declare function extractRecallQuery(prompt: string): string | null;
|
|
@@ -18,11 +18,20 @@ const STOPWORDS = new Set([
|
|
|
18
18
|
]);
|
|
19
19
|
const MIN_CONTENT_WORDS = 2;
|
|
20
20
|
const MIN_WORD_LEN = 3;
|
|
21
|
+
// Harness-injected turns (task-completion notifications, slash-command wrappers,
|
|
22
|
+
// local-command output, background context) arrive on the prompt surface but are
|
|
23
|
+
// never user queries. Recalling against them floods context with irrelevant
|
|
24
|
+
// sessions (measured: 7.2% of historical fires). Skip any message that opens
|
|
25
|
+
// with one of these tags.
|
|
26
|
+
const SYSTEM_MESSAGE_PREFIX = /^<(task-notification|command-name|command-message|command-args|local-command-stdout|local-command-caveat|output-file|system-reminder)\b/;
|
|
21
27
|
/**
|
|
22
28
|
* Returns null when the message is too conversational to produce a useful
|
|
23
|
-
* query — the caller should skip
|
|
29
|
+
* query, or is a harness-injected system message — the caller should skip
|
|
30
|
+
* recall entirely in that case.
|
|
24
31
|
*/
|
|
25
32
|
export function extractRecallQuery(prompt) {
|
|
33
|
+
if (SYSTEM_MESSAGE_PREFIX.test(prompt.trim()))
|
|
34
|
+
return null;
|
|
26
35
|
const tokens = prompt
|
|
27
36
|
.trim()
|
|
28
37
|
.split(/\s+/)
|