nlm-memory 0.20.0 → 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 +19 -10
- package/dist/cli/digest.js +60 -1
- 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 +872 -61
- 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/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.d.ts +14 -0
- package/dist/core/adapters/claude-code.js +36 -0
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/classifier/prompt.js +3 -3
- 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 +13 -3
- package/dist/core/digest/compose.js +23 -18
- 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/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 +2 -0
- package/dist/core/exemplars/capture-from-session.js +7 -3
- 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.d.ts +2 -0
- package/dist/core/exemplars/extract-exemplar.js +9 -66
- 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/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/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/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/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.d.ts +5 -0
- package/dist/core/ingest/ingest-session.js +23 -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 +9 -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 +280 -0
- package/dist/core/ingest/reprocess.js.map +1 -0
- 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/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/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/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/hook-recall-log.d.ts +2 -2
- package/dist/core/recall/hook-recall-log.js +2 -2
- 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 +22 -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 +15 -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 +109 -13
- 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 +50 -0
- package/dist/core/signals/code-signal.js +54 -0
- package/dist/core/signals/code-signal.js.map +1 -0
- 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/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 +13 -2
- 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 +11 -2
- package/dist/core/storage/pg-session-store.js +224 -96
- 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-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 +25 -0
- package/dist/core/storage/pg-workstream-store.js +97 -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 +15 -2
- 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 +81 -27
- 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 +42 -7
- package/dist/core/storage/sqlite-session-store.js +208 -95
- 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-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 +25 -0
- package/dist/core/storage/sqlite-workstream-store.js +99 -0
- package/dist/core/storage/sqlite-workstream-store.js.map +1 -0
- 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/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 +26 -0
- package/dist/core/workstream/bind.js +42 -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 +21 -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 +67 -0
- package/dist/hook/pre-compact-hook.js.map +1 -0
- package/dist/hook/prompt-recall-hook.d.ts +38 -0
- package/dist/hook/prompt-recall-hook.js +124 -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 +3 -2
- package/dist/hook/recall-over-http.js +26 -13
- 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 +26 -60
- package/dist/hook/session-start-hook.js.map +1 -1
- package/dist/hook/stop-hook.js +24 -60
- package/dist/hook/stop-hook.js.map +1 -1
- package/dist/http/app.d.ts +6 -0
- package/dist/http/app.js +52 -44
- 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 +69 -2
- package/dist/mcp/server.js +404 -19
- 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/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 +12 -6
- 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 +22 -0
- package/dist/ports/workstream-store.js +2 -0
- package/dist/ports/workstream-store.js.map +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 +56 -2
- 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/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/029_project_scope.sql +21 -0
- package/migrations/032_session_derivables.sql +11 -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/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 +405 -39
- package/package.json +12 -2
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/scripts/prompt-recall-hook.mjs +413 -43
- package/plugin/scripts/session-start-hook.mjs +295 -61
- package/plugin/scripts/stop-hook.mjs +89 -66
- 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
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,94 @@
|
|
|
1
|
+
# Classifier Gold Fixtures
|
|
2
|
+
|
|
3
|
+
A hand-authored evaluation set for the NLM session classifier. These fixtures
|
|
4
|
+
are part of the shipped product; they run as part of the deterministic fixture
|
|
5
|
+
eval in CI and are public.
|
|
6
|
+
|
|
7
|
+
## What is in this set
|
|
8
|
+
|
|
9
|
+
20 synthetic agent-session transcripts covering the range of sessions the
|
|
10
|
+
classifier encounters in practice:
|
|
11
|
+
|
|
12
|
+
| Category | IDs | Count |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Coding: bug fix | 01, 02, 03 | 3 |
|
|
15
|
+
| Coding: feature | 04, 05, 06 | 3 |
|
|
16
|
+
| Coding: refactor | 07, 08 | 2 |
|
|
17
|
+
| Ops / infra | 09, 10, 11, 12 | 4 |
|
|
18
|
+
| Research / writing | 13, 14, 15 | 3 |
|
|
19
|
+
| Meeting notes | 16 | 1 |
|
|
20
|
+
| Low-signal / trivial | 17, 18, 19, 20 | 4 |
|
|
21
|
+
|
|
22
|
+
Low-signal sessions (17-20) contain no project context, no decisions, and no
|
|
23
|
+
meaningful entities. The classifier prompt instructs models to use confidence
|
|
24
|
+
<= 0.4 for routine/trivial sessions. The fixture eval asserts this calibration
|
|
25
|
+
in both directions (low-signal sessions must score <= 0.4; substantive sessions
|
|
26
|
+
must score > 0.4).
|
|
27
|
+
|
|
28
|
+
## Authorship rules
|
|
29
|
+
|
|
30
|
+
All content is fully synthetic. No real projects, people, companies, or
|
|
31
|
+
infrastructure appear anywhere in the transcripts or references. Invented
|
|
32
|
+
names follow the pattern: single lowercase word (corvid-api, taskvine, lumens,
|
|
33
|
+
pulsearch, irongate, gridhost, droneflow, bridgeauth, flarepath, vaultdb,
|
|
34
|
+
axiom-worker, crestapi, orbitflow, cobalt-api). Domains are example.com.
|
|
35
|
+
Paths are /opt/... or /home/dev/... style.
|
|
36
|
+
|
|
37
|
+
No em dashes appear in any file in this directory. This is a hard rule for
|
|
38
|
+
all text in this repository, fixtures included.
|
|
39
|
+
|
|
40
|
+
## reference.json schema
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
[
|
|
44
|
+
{
|
|
45
|
+
"id": "<transcript filename without .txt>",
|
|
46
|
+
"label": "Expected label string (4-10 words)",
|
|
47
|
+
"labelAlternates": ["alternate phrasings that also count as correct"],
|
|
48
|
+
"entities": ["exact named things the classifier must surface"],
|
|
49
|
+
"decisions": ["key phrase that must appear in a matched extracted decision"],
|
|
50
|
+
"expectLowConfidence": false
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Field notes:
|
|
56
|
+
|
|
57
|
+
- `entities`: only unambiguous named things (project names, specific
|
|
58
|
+
technologies, specific error types). Do not include generic nouns. An entity
|
|
59
|
+
is matched if the reference string appears (case-insensitive) as a substring
|
|
60
|
+
in any extracted entity, or vice versa.
|
|
61
|
+
- `decisions`: concrete enough to match by key-phrase overlap. The scorer
|
|
62
|
+
checks whether a reference decision's key tokens appear in at least one
|
|
63
|
+
extracted decision. Author reference decisions around the unique commitment
|
|
64
|
+
(e.g. "SELECT FOR UPDATE SKIP LOCKED") rather than generic phrasing.
|
|
65
|
+
- `expectLowConfidence`: true for sessions 17-20. The classifier must return
|
|
66
|
+
confidence <= 0.4 for these or the calibration check fails.
|
|
67
|
+
|
|
68
|
+
## Frozen prompt dependency
|
|
69
|
+
|
|
70
|
+
The reference values are authored against the classifier prompt in:
|
|
71
|
+
|
|
72
|
+
src/core/classifier/prompt.ts (CLASSIFIER_SYSTEM_PROMPT)
|
|
73
|
+
|
|
74
|
+
Specifically:
|
|
75
|
+
- The label style guidance ("4-10 word string title")
|
|
76
|
+
- The closed PREDICATE_VOCABULARY (for any expected facts, though facts are
|
|
77
|
+
not included in this fixture set's reference shape)
|
|
78
|
+
- The confidence instruction ("Use 0.4 or below for routine/trivial sessions")
|
|
79
|
+
|
|
80
|
+
If CLASSIFIER_SYSTEM_PROMPT changes materially, review all reference.json
|
|
81
|
+
entries and update expectLowConfidence thresholds if the confidence instruction
|
|
82
|
+
changes.
|
|
83
|
+
|
|
84
|
+
## How to add a fixture
|
|
85
|
+
|
|
86
|
+
1. Write a .txt transcript in transcripts/. File name: NN-category-short-slug.txt.
|
|
87
|
+
Keep it 1-6K characters. Use only invented names. No em dashes.
|
|
88
|
+
2. Add an entry to reference.json. Choose entities and decisions that are
|
|
89
|
+
unambiguous: a competent extraction must find them; borderline cases must
|
|
90
|
+
not be included.
|
|
91
|
+
3. Run the fixture eval to confirm your reference scores as expected:
|
|
92
|
+
`npx tsx scripts/eval/classifier-eval.ts --fixtures tests/fixtures/classifier-gold`
|
|
93
|
+
4. Increment the fixture count comment at the top of
|
|
94
|
+
src/core/eval/classifier-fixture-eval.ts if it exists.
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "01-bug-fix-null-pointer-api",
|
|
4
|
+
"label": "Fix null pointer bug in corvid-api events route",
|
|
5
|
+
"labelAlternates": [
|
|
6
|
+
"corvid-api POST events null source fix",
|
|
7
|
+
"Fix missing source field TypeError in events handler"
|
|
8
|
+
],
|
|
9
|
+
"entities": [
|
|
10
|
+
"corvid-api",
|
|
11
|
+
"Express",
|
|
12
|
+
"Postgres"
|
|
13
|
+
],
|
|
14
|
+
"decisions": [
|
|
15
|
+
"Reject requests missing source field with 400 instead of defaulting"
|
|
16
|
+
],
|
|
17
|
+
"expectLowConfidence": false
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "02-bug-fix-race-condition-queue",
|
|
21
|
+
"label": "Fix duplicate job execution race condition in taskvine",
|
|
22
|
+
"labelAlternates": [
|
|
23
|
+
"taskvine worker race condition fix with SELECT FOR UPDATE",
|
|
24
|
+
"Fix concurrent worker job claim race in taskvine"
|
|
25
|
+
],
|
|
26
|
+
"entities": [
|
|
27
|
+
"taskvine",
|
|
28
|
+
"PostgreSQL",
|
|
29
|
+
"SQLAlchemy"
|
|
30
|
+
],
|
|
31
|
+
"decisions": [
|
|
32
|
+
"Replace unlocked SELECT with SELECT FOR UPDATE SKIP LOCKED to eliminate duplicate job claims"
|
|
33
|
+
],
|
|
34
|
+
"expectLowConfidence": false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "03-bug-fix-jwt-clock-skew",
|
|
38
|
+
"label": "Fix JWT clock skew validation failures in bridgeauth",
|
|
39
|
+
"labelAlternates": [
|
|
40
|
+
"bridgeauth JWT expiry leeway fix",
|
|
41
|
+
"Add clock skew leeway to PyJWT token validation"
|
|
42
|
+
],
|
|
43
|
+
"entities": [
|
|
44
|
+
"bridgeauth",
|
|
45
|
+
"PyJWT",
|
|
46
|
+
"HS256"
|
|
47
|
+
],
|
|
48
|
+
"decisions": [
|
|
49
|
+
"Add clock-skew leeway to JWT validation"
|
|
50
|
+
],
|
|
51
|
+
"expectLowConfidence": false
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "04-feature-webhook-retry",
|
|
55
|
+
"label": "Add exponential backoff webhook retry queue to taskstream",
|
|
56
|
+
"labelAlternates": [
|
|
57
|
+
"taskstream webhook retry with Redis sorted set",
|
|
58
|
+
"Implement webhook retry with exponential backoff in taskstream"
|
|
59
|
+
],
|
|
60
|
+
"entities": [
|
|
61
|
+
"taskstream",
|
|
62
|
+
"Redis"
|
|
63
|
+
],
|
|
64
|
+
"decisions": [
|
|
65
|
+
"Use Redis sorted set with score as next-attempt timestamp for retry queue",
|
|
66
|
+
"Dead-letter failed jobs after 5 attempts with exponential backoff"
|
|
67
|
+
],
|
|
68
|
+
"expectLowConfidence": false
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "05-feature-csv-export",
|
|
72
|
+
"label": "Add streaming CSV export endpoint to lumens",
|
|
73
|
+
"labelAlternates": [
|
|
74
|
+
"lumens GET /reports/export CSV streaming",
|
|
75
|
+
"Implement CSV export with streaming response in lumens"
|
|
76
|
+
],
|
|
77
|
+
"entities": [
|
|
78
|
+
"lumens",
|
|
79
|
+
"encoding/csv"
|
|
80
|
+
],
|
|
81
|
+
"decisions": [
|
|
82
|
+
"Stream CSV directly to http.ResponseWriter instead of buffering in memory"
|
|
83
|
+
],
|
|
84
|
+
"expectLowConfidence": false
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "06-feature-rate-limiting",
|
|
88
|
+
"label": "Add Redis-backed rate limiting to crestapi search endpoint",
|
|
89
|
+
"labelAlternates": [
|
|
90
|
+
"crestapi API rate limiting with slowapi and Redis",
|
|
91
|
+
"Implement per-API-key rate limiting in crestapi"
|
|
92
|
+
],
|
|
93
|
+
"entities": [
|
|
94
|
+
"crestapi",
|
|
95
|
+
"FastAPI",
|
|
96
|
+
"Redis",
|
|
97
|
+
"slowapi"
|
|
98
|
+
],
|
|
99
|
+
"decisions": [
|
|
100
|
+
"Use slowapi with Redis backend for 100 requests per minute per API key"
|
|
101
|
+
],
|
|
102
|
+
"expectLowConfidence": false
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "07-refactor-db-abstraction",
|
|
106
|
+
"label": "Extract DbAdapter interface to ports layer in vaultdb",
|
|
107
|
+
"labelAlternates": [
|
|
108
|
+
"vaultdb database adapter abstraction refactor",
|
|
109
|
+
"Refactor vaultdb to depend on DbAdapter interface not Postgres directly"
|
|
110
|
+
],
|
|
111
|
+
"entities": [
|
|
112
|
+
"vaultdb",
|
|
113
|
+
"TypeScript",
|
|
114
|
+
"Postgres",
|
|
115
|
+
"SQLite"
|
|
116
|
+
],
|
|
117
|
+
"decisions": [
|
|
118
|
+
"Extract DbAdapter interface to ports layer so core modules do not import Postgres directly"
|
|
119
|
+
],
|
|
120
|
+
"expectLowConfidence": false
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "08-refactor-config-loading",
|
|
124
|
+
"label": "Centralize config into Pydantic Settings class in axiom-worker",
|
|
125
|
+
"labelAlternates": [
|
|
126
|
+
"axiom-worker config refactor using Pydantic Settings",
|
|
127
|
+
"Replace scattered os.getenv calls with Pydantic Settings in axiom-worker"
|
|
128
|
+
],
|
|
129
|
+
"entities": [
|
|
130
|
+
"axiom-worker",
|
|
131
|
+
"Pydantic",
|
|
132
|
+
"pydantic-settings"
|
|
133
|
+
],
|
|
134
|
+
"decisions": [
|
|
135
|
+
"Replace scattered os.getenv calls with a single Pydantic Settings class loaded at startup"
|
|
136
|
+
],
|
|
137
|
+
"expectLowConfidence": false
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "09-ops-docker-staging",
|
|
141
|
+
"label": "Set up Docker Compose staging environment with Traefik for droneflow",
|
|
142
|
+
"labelAlternates": [
|
|
143
|
+
"droneflow staging docker-compose with Traefik and TLS",
|
|
144
|
+
"Create staging Docker Compose stack for droneflow"
|
|
145
|
+
],
|
|
146
|
+
"entities": [
|
|
147
|
+
"droneflow",
|
|
148
|
+
"Docker Compose",
|
|
149
|
+
"Traefik"
|
|
150
|
+
],
|
|
151
|
+
"decisions": [
|
|
152
|
+
"Use Traefik for TLS termination and routing in staging",
|
|
153
|
+
"Keep staging deploys explicit via deploy script rather than auto-pull"
|
|
154
|
+
],
|
|
155
|
+
"expectLowConfidence": false
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "10-ops-nginx-ssl",
|
|
159
|
+
"label": "Configure nginx reverse proxy with TLS for gridhost",
|
|
160
|
+
"labelAlternates": [
|
|
161
|
+
"gridhost nginx TLS setup with certbot and Let's Encrypt",
|
|
162
|
+
"Set up nginx with Let's Encrypt certificate for gridhost"
|
|
163
|
+
],
|
|
164
|
+
"entities": [
|
|
165
|
+
"gridhost",
|
|
166
|
+
"nginx",
|
|
167
|
+
"certbot"
|
|
168
|
+
],
|
|
169
|
+
"decisions": [
|
|
170
|
+
"Terminate TLS at nginx and proxy to app on port 8080",
|
|
171
|
+
"Add cron fallback for certbot renewal in case systemd timer is disabled"
|
|
172
|
+
],
|
|
173
|
+
"expectLowConfidence": false
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"id": "11-ops-postgres-migration",
|
|
177
|
+
"label": "Run concurrent index migration on Postgres sessions table",
|
|
178
|
+
"labelAlternates": [
|
|
179
|
+
"Add CONCURRENTLY indexes to sessions table via Alembic",
|
|
180
|
+
"Postgres sessions table index migration without table lock"
|
|
181
|
+
],
|
|
182
|
+
"entities": [
|
|
183
|
+
"PostgreSQL",
|
|
184
|
+
"Alembic"
|
|
185
|
+
],
|
|
186
|
+
"decisions": [
|
|
187
|
+
"Use CREATE INDEX CONCURRENTLY to avoid locking the 8-million-row sessions table",
|
|
188
|
+
"Set statement_timeout to 0 for the migration connection to prevent timeout killing the index build"
|
|
189
|
+
],
|
|
190
|
+
"expectLowConfidence": false
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "12-ops-cron-debugging",
|
|
194
|
+
"label": "Debug and fix flarepath nightly cron job not running",
|
|
195
|
+
"labelAlternates": [
|
|
196
|
+
"flarepath cron exit 126 permission denied fix",
|
|
197
|
+
"Fix missing execute bit on flarepath nightly report script"
|
|
198
|
+
],
|
|
199
|
+
"entities": [
|
|
200
|
+
"flarepath"
|
|
201
|
+
],
|
|
202
|
+
"decisions": [
|
|
203
|
+
"Add chmod to deploy script to re-apply execute bit after git checkout"
|
|
204
|
+
],
|
|
205
|
+
"expectLowConfidence": false
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "13-research-vector-db-comparison",
|
|
209
|
+
"label": "Vector database comparison and Qdrant selection for pulsearch",
|
|
210
|
+
"labelAlternates": [
|
|
211
|
+
"pulsearch vector DB evaluation: pgvector vs Qdrant vs Weaviate",
|
|
212
|
+
"Choose Qdrant for pulsearch vector search"
|
|
213
|
+
],
|
|
214
|
+
"entities": [
|
|
215
|
+
"pulsearch",
|
|
216
|
+
"Qdrant",
|
|
217
|
+
"pgvector",
|
|
218
|
+
"Weaviate"
|
|
219
|
+
],
|
|
220
|
+
"decisions": [
|
|
221
|
+
"Use Qdrant for pulsearch instead of pgvector due to stronger ANN performance and pre-filter payload filtering"
|
|
222
|
+
],
|
|
223
|
+
"expectLowConfidence": false
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": "14-research-oauth-provider",
|
|
227
|
+
"label": "OAuth provider evaluation and Keycloak selection for irongate",
|
|
228
|
+
"labelAlternates": [
|
|
229
|
+
"irongate auth provider comparison: Keycloak vs Auth0 vs SuperTokens",
|
|
230
|
+
"Choose Keycloak self-hosted for irongate customer auth"
|
|
231
|
+
],
|
|
232
|
+
"entities": [
|
|
233
|
+
"irongate",
|
|
234
|
+
"Keycloak",
|
|
235
|
+
"Auth0",
|
|
236
|
+
"SuperTokens"
|
|
237
|
+
],
|
|
238
|
+
"decisions": [
|
|
239
|
+
"Use Keycloak self-hosted backed by Postgres for irongate auth because it covers LDAP federation without additional cost"
|
|
240
|
+
],
|
|
241
|
+
"expectLowConfidence": false
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "15-writing-api-docs",
|
|
245
|
+
"label": "Draft OpenAPI 3.0 documentation for cobalt-api with Swagger UI",
|
|
246
|
+
"labelAlternates": [
|
|
247
|
+
"cobalt-api OpenAPI spec and Swagger UI setup",
|
|
248
|
+
"Set up OpenAPI docs for cobalt-api served at /api-docs"
|
|
249
|
+
],
|
|
250
|
+
"entities": [
|
|
251
|
+
"cobalt-api",
|
|
252
|
+
"OpenAPI",
|
|
253
|
+
"Swagger UI"
|
|
254
|
+
],
|
|
255
|
+
"decisions": [
|
|
256
|
+
"Use OpenAPI 3.0 format and host docs publicly at /api-docs via swagger-ui-express"
|
|
257
|
+
],
|
|
258
|
+
"expectLowConfidence": false
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"id": "16-meeting-sprint-planning",
|
|
262
|
+
"label": "Sprint 5 planning notes for orbitflow team",
|
|
263
|
+
"labelAlternates": [
|
|
264
|
+
"orbitflow sprint 5 planning: webhook retry, rate limiting, deferrals",
|
|
265
|
+
"orbitflow sprint 5 commitments and deferrals"
|
|
266
|
+
],
|
|
267
|
+
"entities": [
|
|
268
|
+
"orbitflow"
|
|
269
|
+
],
|
|
270
|
+
"decisions": [
|
|
271
|
+
"Webhook retry is top backend priority for sprint 5",
|
|
272
|
+
"Analytics dashboard deferred to sprint 6",
|
|
273
|
+
"Rate limiting assigned to backend as secondary sprint 5 item",
|
|
274
|
+
"Mobile push notifications deferred to sprint 6 backlog"
|
|
275
|
+
],
|
|
276
|
+
"expectLowConfidence": false
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "17-trivial-list-files",
|
|
280
|
+
"label": "List files in src directory",
|
|
281
|
+
"labelAlternates": [
|
|
282
|
+
"Directory listing of src folder"
|
|
283
|
+
],
|
|
284
|
+
"entities": [],
|
|
285
|
+
"decisions": [],
|
|
286
|
+
"expectLowConfidence": true
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "18-trivial-version-check",
|
|
290
|
+
"label": "Check Node.js and npm versions",
|
|
291
|
+
"labelAlternates": [
|
|
292
|
+
"Node.js npm version query"
|
|
293
|
+
],
|
|
294
|
+
"entities": [],
|
|
295
|
+
"decisions": [],
|
|
296
|
+
"expectLowConfidence": true
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"id": "19-trivial-syntax-help",
|
|
300
|
+
"label": "TypeScript array iteration syntax question",
|
|
301
|
+
"labelAlternates": [
|
|
302
|
+
"How to iterate array with index in TypeScript"
|
|
303
|
+
],
|
|
304
|
+
"entities": [],
|
|
305
|
+
"decisions": [],
|
|
306
|
+
"expectLowConfidence": true
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"id": "20-trivial-read-package-json",
|
|
310
|
+
"label": "Read package.json and confirm dependencies installed",
|
|
311
|
+
"labelAlternates": [
|
|
312
|
+
"Check installed npm dependencies from package.json"
|
|
313
|
+
],
|
|
314
|
+
"entities": [],
|
|
315
|
+
"decisions": [],
|
|
316
|
+
"expectLowConfidence": true
|
|
317
|
+
}
|
|
318
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
User: The POST /events endpoint in corvid-api is throwing a TypeError in production. Stack trace says "Cannot read properties of undefined (reading 'source')" on line 47 of src/routes/events.ts. Can you find the bug?
|
|
2
|
+
|
|
3
|
+
Agent: Let me read the route handler.
|
|
4
|
+
|
|
5
|
+
[Read: src/routes/events.ts]
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export async function postEvent(req: Request, res: Response) {
|
|
9
|
+
const { source, payload } = req.body;
|
|
10
|
+
// line 47
|
|
11
|
+
const normalized = source.toLowerCase();
|
|
12
|
+
await db.events.insert({ source: normalized, payload, createdAt: new Date() });
|
|
13
|
+
res.status(201).json({ ok: true });
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The bug is on line 47. When the request body omits the `source` field, `source` is `undefined` and calling `.toLowerCase()` throws. There is no guard before the property access.
|
|
18
|
+
|
|
19
|
+
User: Right. What's the fix?
|
|
20
|
+
|
|
21
|
+
Agent: Two options: reject requests missing `source` with a 400, or default it to "unknown". Given this is an event ingestion endpoint where source identity matters for routing downstream, rejecting is safer.
|
|
22
|
+
|
|
23
|
+
User: Agreed. Reject with 400.
|
|
24
|
+
|
|
25
|
+
Agent: Updating the handler now.
|
|
26
|
+
|
|
27
|
+
[Edit: src/routes/events.ts]
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
export async function postEvent(req: Request, res: Response) {
|
|
31
|
+
const { source, payload } = req.body;
|
|
32
|
+
if (typeof source !== "string" || source.trim().length === 0) {
|
|
33
|
+
res.status(400).json({ error: "source is required" });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const normalized = source.toLowerCase().trim();
|
|
37
|
+
await db.events.insert({ source: normalized, payload, createdAt: new Date() });
|
|
38
|
+
res.status(201).json({ ok: true });
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
User: Good. Add a test for the missing-source case.
|
|
43
|
+
|
|
44
|
+
Agent: Adding to tests/routes/events.test.ts.
|
|
45
|
+
|
|
46
|
+
[Read: tests/routes/events.test.ts]
|
|
47
|
+
|
|
48
|
+
[Edit: tests/routes/events.test.ts]
|
|
49
|
+
|
|
50
|
+
Added a test case:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
it("returns 400 when source is missing", async () => {
|
|
54
|
+
const res = await request(app).post("/events").send({ payload: { x: 1 } });
|
|
55
|
+
expect(res.status).toBe(400);
|
|
56
|
+
expect(res.body.error).toBe("source is required");
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
User: Run the tests to confirm.
|
|
61
|
+
|
|
62
|
+
Agent: [Bash: npm test -- --testPathPattern=events]
|
|
63
|
+
|
|
64
|
+
Output:
|
|
65
|
+
```
|
|
66
|
+
PASS tests/routes/events.test.ts
|
|
67
|
+
POST /events
|
|
68
|
+
✓ returns 201 for valid event (42ms)
|
|
69
|
+
✓ returns 400 when source is missing (8ms)
|
|
70
|
+
✓ returns 400 when source is empty string (6ms)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
All passing. The fix guards on both undefined and empty-string cases since `trim().length === 0` catches whitespace-only inputs too.
|
|
74
|
+
|
|
75
|
+
User: Perfect. Ship it.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
User: We're seeing duplicate job execution in taskvine. Two workers occasionally pick up the same job from the queue table. The claim logic is in src/worker/claim.py.
|
|
2
|
+
|
|
3
|
+
Agent: [Read: src/worker/claim.py]
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
def claim_next_job(conn):
|
|
7
|
+
with conn.transaction():
|
|
8
|
+
row = conn.execute(
|
|
9
|
+
"SELECT id FROM jobs WHERE status = 'pending' LIMIT 1"
|
|
10
|
+
).fetchone()
|
|
11
|
+
if row is None:
|
|
12
|
+
return None
|
|
13
|
+
conn.execute(
|
|
14
|
+
"UPDATE jobs SET status = 'running', worker_id = %s WHERE id = %s",
|
|
15
|
+
(WORKER_ID, row["id"]),
|
|
16
|
+
)
|
|
17
|
+
return row["id"]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This is a classic race condition. The SELECT and UPDATE are in the same transaction, but between the SELECT and UPDATE another worker can read the same row before either has committed. The advisory lock approach you're using doesn't appear here, so there's no row-level locking.
|
|
21
|
+
|
|
22
|
+
The fix is to use SELECT FOR UPDATE SKIP LOCKED. This atomically locks the selected row for the duration of the transaction and skips rows already locked by another worker.
|
|
23
|
+
|
|
24
|
+
User: Yes, let's switch to SELECT FOR UPDATE SKIP LOCKED.
|
|
25
|
+
|
|
26
|
+
Agent: [Edit: src/worker/claim.py]
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
def claim_next_job(conn):
|
|
30
|
+
with conn.transaction():
|
|
31
|
+
row = conn.execute(
|
|
32
|
+
"""
|
|
33
|
+
SELECT id FROM jobs
|
|
34
|
+
WHERE status = 'pending'
|
|
35
|
+
ORDER BY created_at
|
|
36
|
+
LIMIT 1
|
|
37
|
+
FOR UPDATE SKIP LOCKED
|
|
38
|
+
"""
|
|
39
|
+
).fetchone()
|
|
40
|
+
if row is None:
|
|
41
|
+
return None
|
|
42
|
+
conn.execute(
|
|
43
|
+
"UPDATE jobs SET status = 'running', worker_id = %s WHERE id = %s",
|
|
44
|
+
(WORKER_ID, row["id"]),
|
|
45
|
+
)
|
|
46
|
+
return row["id"]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Added ORDER BY created_at for FIFO fairness while I was in there.
|
|
50
|
+
|
|
51
|
+
User: Does PostgreSQL actually guarantee no duplicates with this pattern?
|
|
52
|
+
|
|
53
|
+
Agent: Yes. FOR UPDATE acquires a row-level lock at SELECT time. SKIP LOCKED means any row already locked by another transaction is skipped entirely rather than waited on. Within a single transaction, any row your SELECT returns is guaranteed not to be selected by another concurrent transaction using the same pattern. This is the canonical pattern for Postgres-backed job queues.
|
|
54
|
+
|
|
55
|
+
User: What about the existing advisory lock code I thought we had?
|
|
56
|
+
|
|
57
|
+
Agent: [Bash: grep -r "advisory\|pg_try_advisory" src/]
|
|
58
|
+
|
|
59
|
+
No results. There is no advisory lock code in the codebase. The previous SELECT without FOR UPDATE had no locking at all, which is why duplicates occurred.
|
|
60
|
+
|
|
61
|
+
User: Got it. Let's also add a test to confirm two concurrent workers cannot claim the same job.
|
|
62
|
+
|
|
63
|
+
Agent: [Read: tests/worker/test_claim.py]
|
|
64
|
+
|
|
65
|
+
[Edit: tests/worker/test_claim.py]
|
|
66
|
+
|
|
67
|
+
Added a test that spawns two threads, each calling claim_next_job with a shared connection pool containing a single pending job, and asserts only one gets a non-None return and the other gets None.
|
|
68
|
+
|
|
69
|
+
User: Run it.
|
|
70
|
+
|
|
71
|
+
Agent: [Bash: pytest tests/worker/test_claim.py -v]
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
tests/worker/test_claim.py::test_concurrent_claim_no_duplicate PASSED
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Fixed. The SELECT FOR UPDATE SKIP LOCKED pattern eliminates the race.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
User: Users are reporting random login failures in bridgeauth. The error in logs is "Token is not yet valid" and "Signature has expired". It's intermittent and seems worse around the token boundary. Here's the validation code in src/auth/token.py.
|
|
2
|
+
|
|
3
|
+
Agent: [Read: src/auth/token.py]
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import jwt
|
|
7
|
+
|
|
8
|
+
SECRET = os.environ["JWT_SECRET"]
|
|
9
|
+
ALGORITHM = "HS256"
|
|
10
|
+
|
|
11
|
+
def validate_token(token: str) -> dict:
|
|
12
|
+
return jwt.decode(token, SECRET, algorithms=[ALGORITHM])
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The validation call has no leeway parameter. JWT tokens have an `exp` claim and PyJWT validates it against the current time. When there is a small clock difference between the server that issued the token and the server validating it, tokens can appear expired seconds before their actual expiry from the issuer's perspective.
|
|
16
|
+
|
|
17
|
+
The standard fix is to add a leeway value, typically 10-30 seconds, which tells PyJWT to accept tokens that expired within that window.
|
|
18
|
+
|
|
19
|
+
User: How big is the clock drift between our services likely to be?
|
|
20
|
+
|
|
21
|
+
Agent: For services within the same datacenter using NTP, drift is usually under 1 second. But network-synchronized clocks under load can drift up to 5 seconds. A 10-second leeway covers realistic cases without meaningfully extending token validity from a security perspective.
|
|
22
|
+
|
|
23
|
+
User: Let's add 10 seconds of leeway.
|
|
24
|
+
|
|
25
|
+
Agent: [Edit: src/auth/token.py]
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import jwt
|
|
29
|
+
from datetime import timedelta
|
|
30
|
+
|
|
31
|
+
SECRET = os.environ["JWT_SECRET"]
|
|
32
|
+
ALGORITHM = "HS256"
|
|
33
|
+
CLOCK_SKEW_LEEWAY = timedelta(seconds=10)
|
|
34
|
+
|
|
35
|
+
def validate_token(token: str) -> dict:
|
|
36
|
+
return jwt.decode(
|
|
37
|
+
token,
|
|
38
|
+
SECRET,
|
|
39
|
+
algorithms=[ALGORITHM],
|
|
40
|
+
leeway=CLOCK_SKEW_LEEWAY,
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
User: Are there tests for the expiry behavior?
|
|
45
|
+
|
|
46
|
+
Agent: [Bash: grep -n "expir\|leeway\|exp" tests/auth/test_token.py]
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
12: def make_token(exp_offset_seconds=300):
|
|
50
|
+
14: payload = {"sub": "user-1", "exp": now + timedelta(seconds=exp_offset_seconds)}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
No test covers the boundary case. I'll add one that creates a token with `exp` set 5 seconds in the past and confirms it is now accepted with leeway.
|
|
54
|
+
|
|
55
|
+
[Edit: tests/auth/test_token.py]
|
|
56
|
+
|
|
57
|
+
Added:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
def test_validates_token_within_leeway():
|
|
61
|
+
# Token expired 5 seconds ago; should pass with 10s leeway
|
|
62
|
+
payload = {"sub": "user-1", "exp": datetime.utcnow() - timedelta(seconds=5)}
|
|
63
|
+
token = jwt.encode(payload, SECRET, algorithm="HS256")
|
|
64
|
+
result = validate_token(token)
|
|
65
|
+
assert result["sub"] == "user-1"
|
|
66
|
+
|
|
67
|
+
def test_rejects_token_outside_leeway():
|
|
68
|
+
# Token expired 60 seconds ago; must fail even with leeway
|
|
69
|
+
payload = {"sub": "user-1", "exp": datetime.utcnow() - timedelta(seconds=60)}
|
|
70
|
+
token = jwt.encode(payload, SECRET, algorithm="HS256")
|
|
71
|
+
with pytest.raises(jwt.ExpiredSignatureError):
|
|
72
|
+
validate_token(token)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
User: Run the tests.
|
|
76
|
+
|
|
77
|
+
Agent: [Bash: pytest tests/auth/test_token.py -v]
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
tests/auth/test_token.py::test_validates_valid_token PASSED
|
|
81
|
+
tests/auth/test_token.py::test_rejects_expired_token PASSED
|
|
82
|
+
tests/auth/test_token.py::test_validates_token_within_leeway PASSED
|
|
83
|
+
tests/auth/test_token.py::test_rejects_token_outside_leeway PASSED
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
All green. The 10-second leeway resolves the clock-skew login failures.
|