nlm-memory 0.20.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/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 +75 -10
- 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/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
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Owns the connection. Builds SqliteSessionStore and SqliteFactStore over
|
|
5
5
|
* that single connection so writes commit on one WAL writer (the SQLite
|
|
6
|
-
* atomicity model).
|
|
7
|
-
* `db.transaction()` API and re-runs it inside an async shell so callers
|
|
8
|
-
* can await async work inside the callback (e.g. an embedder call) — but
|
|
9
|
-
* note that the db txn itself is synchronous; do not call long-running
|
|
10
|
-
* async work inside withTransaction or the txn will hold its write lock.
|
|
6
|
+
* atomicity model).
|
|
11
7
|
*
|
|
12
8
|
* rawDb() is a deprecated escape hatch for callers that still use direct
|
|
13
9
|
* better-sqlite3 — scheduler, http actions endpoints, backfill-facts,
|
|
14
10
|
* source/provider registries. Tracked for removal in #215a.
|
|
15
11
|
*/
|
|
16
12
|
import { SqliteCodeExemplarStore } from "./sqlite-code-exemplar-store.js";
|
|
13
|
+
import { SqliteEmbeddingConfigStore } from "./sqlite-embedding-config.js";
|
|
14
|
+
import { SqliteEntityStore } from "./sqlite-entity-store.js";
|
|
17
15
|
import { SqliteFactStore } from "./sqlite-fact-store.js";
|
|
18
16
|
import { SqliteSessionStore } from "./sqlite-session-store.js";
|
|
19
17
|
import { SqliteSignalStore } from "./sqlite-signal-store.js";
|
|
18
|
+
import { SqliteWorkstreamStore } from "./sqlite-workstream-store.js";
|
|
20
19
|
import { SourceRegistry } from "../sources/source-registry.js";
|
|
21
20
|
import { ProviderRegistry } from "../providers/provider-registry.js";
|
|
22
21
|
export class SqliteStorage {
|
|
@@ -24,25 +23,34 @@ export class SqliteStorage {
|
|
|
24
23
|
facts;
|
|
25
24
|
signals;
|
|
26
25
|
exemplars;
|
|
26
|
+
workstreams;
|
|
27
|
+
entities;
|
|
28
|
+
embeddingConfig;
|
|
27
29
|
sources;
|
|
28
30
|
providers;
|
|
29
|
-
|
|
30
|
-
constructor(sessions, facts, signals, exemplars, sources, providers) {
|
|
31
|
+
constructor(sessions, facts, signals, exemplars, workstreams, entities, embeddingConfig, sources, providers) {
|
|
31
32
|
this.sessions = sessions;
|
|
32
33
|
this.facts = facts;
|
|
33
34
|
this.signals = signals;
|
|
34
35
|
this.exemplars = exemplars;
|
|
36
|
+
this.workstreams = workstreams;
|
|
37
|
+
this.entities = entities;
|
|
38
|
+
this.embeddingConfig = embeddingConfig;
|
|
35
39
|
this.sources = sources;
|
|
36
40
|
this.providers = providers;
|
|
37
41
|
}
|
|
38
42
|
static create(opts) {
|
|
39
43
|
const sessions = new SqliteSessionStore(opts);
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const db = sessions.rawDb();
|
|
45
|
+
const facts = new SqliteFactStore(db);
|
|
46
|
+
const signals = new SqliteSignalStore(db);
|
|
47
|
+
const exemplars = new SqliteCodeExemplarStore(db);
|
|
48
|
+
const workstreams = new SqliteWorkstreamStore(db);
|
|
49
|
+
const entities = new SqliteEntityStore(db);
|
|
50
|
+
const embeddingConfig = new SqliteEmbeddingConfigStore(db);
|
|
51
|
+
const sources = new SourceRegistry(db);
|
|
52
|
+
const providers = new ProviderRegistry(db);
|
|
53
|
+
return new SqliteStorage(sessions, facts, signals, exemplars, workstreams, entities, embeddingConfig, sources, providers);
|
|
46
54
|
}
|
|
47
55
|
async init() {
|
|
48
56
|
// SqliteSessionStore runs migrations in its constructor today; this is
|
|
@@ -52,25 +60,6 @@ export class SqliteStorage {
|
|
|
52
60
|
async close() {
|
|
53
61
|
this.sessions.close();
|
|
54
62
|
}
|
|
55
|
-
async withTransaction(fn) {
|
|
56
|
-
if (this.inTxn) {
|
|
57
|
-
throw new Error("SqliteStorage.withTransaction does not support nesting");
|
|
58
|
-
}
|
|
59
|
-
this.inTxn = true;
|
|
60
|
-
try {
|
|
61
|
-
let captured;
|
|
62
|
-
const txn = this.sessions.rawDb().transaction(() => {
|
|
63
|
-
const ctx = { facts: this.facts, sessions: this.sessions };
|
|
64
|
-
captured = fn(ctx);
|
|
65
|
-
});
|
|
66
|
-
txn();
|
|
67
|
-
// Cast is safe: a sync throw inside fn propagates out of txn() before we reach here, so the success path always assigned captured.
|
|
68
|
-
return captured;
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
this.inTxn = false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
63
|
/**
|
|
75
64
|
* @deprecated SQLite-only escape hatch for callers not yet ported to the
|
|
76
65
|
* Storage interface. Tracked for removal in #215a. Do not use in new code.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-storage.js","sourceRoot":"","sources":["../../../src/core/storage/sqlite-storage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sqlite-storage.js","sourceRoot":"","sources":["../../../src/core/storage/sqlite-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAOxE,MAAM,OAAO,aAAa;IACf,QAAQ,CAAqB;IAC7B,KAAK,CAAkB;IACvB,OAAO,CAAoB;IAC3B,SAAS,CAA0B;IACnC,WAAW,CAAwB;IACnC,QAAQ,CAAoB;IAC5B,eAAe,CAA6B;IAC5C,OAAO,CAAiB;IACxB,SAAS,CAAmB;IAErC,YACE,QAA4B,EAC5B,KAAsB,EACtB,OAA0B,EAC1B,SAAkC,EAClC,WAAkC,EAClC,QAA2B,EAC3B,eAA2C,EAC3C,OAAuB,EACvB,SAA2B;QAE3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,IAA0B;QACtC,MAAM,QAAQ,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,IAAI,qBAAqB,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,eAAe,GAAG,IAAI,0BAA0B,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAC3C,OAAO,IAAI,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAC5H,CAAC;IAED,KAAK,CAAC,IAAI;QACR,uEAAuE;QACvE,mEAAmE;QACnE,2BAA2B;IAC7B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { WorkstreamStore } from "../../ports/workstream-store.js";
|
|
3
|
+
import { type Workstream } from "../workstream/model.js";
|
|
4
|
+
export declare class SqliteWorkstreamStore implements WorkstreamStore {
|
|
5
|
+
private readonly db;
|
|
6
|
+
constructor(db: Database.Database);
|
|
7
|
+
create(input: {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}): Promise<Workstream>;
|
|
11
|
+
getById(id: string): Promise<Workstream | null>;
|
|
12
|
+
findByNormalizedLabel(normalizedLabel: string): Promise<Workstream | null>;
|
|
13
|
+
listAll(): Promise<ReadonlyArray<Workstream>>;
|
|
14
|
+
touchLastSession(id: string, atIso: string): Promise<void>;
|
|
15
|
+
setLabel(id: string, label: string): Promise<void>;
|
|
16
|
+
setStatus(id: string, status: Workstream["status"]): Promise<void>;
|
|
17
|
+
merge(fromId: string, intoId: string): Promise<void>;
|
|
18
|
+
upsertEntities(workstreamId: string, entities: ReadonlyArray<string>): Promise<void>;
|
|
19
|
+
entitiesFor(workstreamIds: ReadonlyArray<string>): Promise<Map<string, string[]>>;
|
|
20
|
+
candidatesByEntityOverlap(entities: ReadonlyArray<string>, limit: number): Promise<ReadonlyArray<{
|
|
21
|
+
workstreamId: string;
|
|
22
|
+
entities: string[];
|
|
23
|
+
}>>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { normalizeLabel } from "../workstream/model.js";
|
|
2
|
+
function rowToWorkstream(r) {
|
|
3
|
+
return {
|
|
4
|
+
id: r.id, label: r.label, status: r.status, mergedInto: r.merged_into,
|
|
5
|
+
createdAt: r.created_at, updatedAt: r.updated_at, lastSessionAt: r.last_session_at,
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export class SqliteWorkstreamStore {
|
|
9
|
+
db;
|
|
10
|
+
constructor(db) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
}
|
|
13
|
+
async create(input) {
|
|
14
|
+
this.db.prepare("INSERT INTO workstreams (id, label) VALUES (?, ?)").run(input.id, input.label);
|
|
15
|
+
return (await this.getById(input.id));
|
|
16
|
+
}
|
|
17
|
+
async getById(id) {
|
|
18
|
+
const r = this.db.prepare("SELECT * FROM workstreams WHERE id = ?").get(id);
|
|
19
|
+
return r ? rowToWorkstream(r) : null;
|
|
20
|
+
}
|
|
21
|
+
async findByNormalizedLabel(normalizedLabel) {
|
|
22
|
+
// Small set; normalize in JS to match normalizeLabel semantics exactly.
|
|
23
|
+
const rows = this.db.prepare("SELECT * FROM workstreams").all();
|
|
24
|
+
const hit = rows.find((r) => normalizeLabel(r.label) === normalizedLabel);
|
|
25
|
+
return hit ? rowToWorkstream(hit) : null;
|
|
26
|
+
}
|
|
27
|
+
async listAll() {
|
|
28
|
+
return this.db.prepare("SELECT * FROM workstreams").all().map(rowToWorkstream);
|
|
29
|
+
}
|
|
30
|
+
async touchLastSession(id, atIso) {
|
|
31
|
+
this.db.prepare("UPDATE workstreams SET last_session_at = ?, updated_at = datetime('now') WHERE id = ?").run(atIso, id);
|
|
32
|
+
}
|
|
33
|
+
async setLabel(id, label) {
|
|
34
|
+
this.db.prepare("UPDATE workstreams SET label = ?, updated_at = datetime('now') WHERE id = ?").run(label, id);
|
|
35
|
+
}
|
|
36
|
+
async setStatus(id, status) {
|
|
37
|
+
this.db.prepare("UPDATE workstreams SET status = ?, updated_at = datetime('now') WHERE id = ?").run(status, id);
|
|
38
|
+
}
|
|
39
|
+
async merge(fromId, intoId) {
|
|
40
|
+
const tx = this.db.transaction(() => {
|
|
41
|
+
this.db.prepare("UPDATE workstreams SET merged_into = ?, status = 'merged', updated_at = datetime('now') WHERE id = ?").run(intoId, fromId);
|
|
42
|
+
// Union from's entities into into (sum session_count on PK conflict).
|
|
43
|
+
this.db.prepare(`
|
|
44
|
+
INSERT INTO workstream_entities (workstream_id, entity_canonical, session_count)
|
|
45
|
+
SELECT ?, entity_canonical, session_count FROM workstream_entities WHERE workstream_id = ?
|
|
46
|
+
ON CONFLICT(workstream_id, entity_canonical)
|
|
47
|
+
DO UPDATE SET session_count = session_count + excluded.session_count
|
|
48
|
+
`).run(intoId, fromId);
|
|
49
|
+
this.db.prepare("DELETE FROM workstream_entities WHERE workstream_id = ?").run(fromId);
|
|
50
|
+
});
|
|
51
|
+
tx();
|
|
52
|
+
}
|
|
53
|
+
async upsertEntities(workstreamId, entities) {
|
|
54
|
+
const stmt = this.db.prepare(`
|
|
55
|
+
INSERT INTO workstream_entities (workstream_id, entity_canonical, session_count)
|
|
56
|
+
VALUES (?, ?, 1)
|
|
57
|
+
ON CONFLICT(workstream_id, entity_canonical)
|
|
58
|
+
DO UPDATE SET session_count = session_count + 1
|
|
59
|
+
`);
|
|
60
|
+
const tx = this.db.transaction((names) => {
|
|
61
|
+
for (const n of names) {
|
|
62
|
+
const e = n.trim();
|
|
63
|
+
if (e)
|
|
64
|
+
stmt.run(workstreamId, e);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
tx(entities);
|
|
68
|
+
}
|
|
69
|
+
async entitiesFor(workstreamIds) {
|
|
70
|
+
const out = new Map();
|
|
71
|
+
if (workstreamIds.length === 0)
|
|
72
|
+
return out;
|
|
73
|
+
const ph = workstreamIds.map(() => "?").join(",");
|
|
74
|
+
const rows = this.db.prepare(`SELECT workstream_id, entity_canonical FROM workstream_entities WHERE workstream_id IN (${ph})`).all(...workstreamIds);
|
|
75
|
+
for (const r of rows) {
|
|
76
|
+
const list = out.get(r.workstream_id);
|
|
77
|
+
if (list)
|
|
78
|
+
list.push(r.entity_canonical);
|
|
79
|
+
else
|
|
80
|
+
out.set(r.workstream_id, [r.entity_canonical]);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
async candidatesByEntityOverlap(entities, limit) {
|
|
85
|
+
const names = entities.map((e) => e.trim()).filter(Boolean);
|
|
86
|
+
if (names.length === 0)
|
|
87
|
+
return [];
|
|
88
|
+
const ph = names.map(() => "?").join(",");
|
|
89
|
+
const ids = this.db.prepare(`SELECT workstream_id, COUNT(*) AS overlap FROM workstream_entities
|
|
90
|
+
WHERE entity_canonical IN (${ph})
|
|
91
|
+
GROUP BY workstream_id ORDER BY overlap DESC LIMIT ?`).all(...names, limit).map((r) => r.workstream_id);
|
|
92
|
+
if (ids.length === 0)
|
|
93
|
+
return [];
|
|
94
|
+
const map = await this.entitiesFor(ids);
|
|
95
|
+
return ids.map((id) => ({ workstreamId: id, entities: map.get(id) ?? [] }));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=sqlite-workstream-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-workstream-store.js","sourceRoot":"","sources":["../../../src/core/storage/sqlite-workstream-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Interval } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Collapse a session's message timestamps into activity intervals. Consecutive
|
|
4
|
+
* messages no more than `idleThresholdMin` apart belong to the same span; a
|
|
5
|
+
* larger gap means the operator was away and starts a new span. A lone message
|
|
6
|
+
* yields a zero-length span (it marks activity but adds no minutes).
|
|
7
|
+
*/
|
|
8
|
+
export declare function activeSpans(timestampsMs: ReadonlyArray<number>, idleThresholdMin: number): Interval[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse a session's message timestamps into activity intervals. Consecutive
|
|
3
|
+
* messages no more than `idleThresholdMin` apart belong to the same span; a
|
|
4
|
+
* larger gap means the operator was away and starts a new span. A lone message
|
|
5
|
+
* yields a zero-length span (it marks activity but adds no minutes).
|
|
6
|
+
*/
|
|
7
|
+
export function activeSpans(timestampsMs, idleThresholdMin) {
|
|
8
|
+
const ts = [...timestampsMs].sort((a, b) => a - b);
|
|
9
|
+
if (ts.length === 0)
|
|
10
|
+
return [];
|
|
11
|
+
const gapMs = idleThresholdMin * 60_000;
|
|
12
|
+
const spans = [];
|
|
13
|
+
let start = ts[0];
|
|
14
|
+
let prev = ts[0];
|
|
15
|
+
for (let i = 1; i < ts.length; i++) {
|
|
16
|
+
const t = ts[i];
|
|
17
|
+
if (t - prev <= gapMs) {
|
|
18
|
+
prev = t;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
spans.push({ start, end: prev });
|
|
22
|
+
start = t;
|
|
23
|
+
prev = t;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
spans.push({ start, end: prev });
|
|
27
|
+
return spans;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=active-spans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-spans.js","sourceRoot":"","sources":["../../../src/core/work-digest/active-spans.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,YAAmC,EACnC,gBAAwB;IAExB,MAAM,EAAE,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,gBAAgB,GAAG,MAAM,CAAC;IACxC,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC;IACnB,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC;QACjB,IAAI,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACjC,KAAK,GAAG,CAAC,CAAC;YACV,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Interval, SessionActivity, TopicShare, FocusStats } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Attribute each merged active interval to a topic and derive attention +
|
|
4
|
+
* focus. v1 uses dominant-session attribution: the whole interval goes to the
|
|
5
|
+
* session with the most messages in it (the bounded simplification over
|
|
6
|
+
* fractional splitting documented in the design). Focus is derived from the
|
|
7
|
+
* ordered, attributed segments: adjacent same-topic segments collapse into
|
|
8
|
+
* blocks; a topic change between blocks is one context switch.
|
|
9
|
+
*/
|
|
10
|
+
export declare function attribute(merged: ReadonlyArray<Interval>, sessions: ReadonlyArray<SessionActivity>, opts: {
|
|
11
|
+
deepBlockMin: number;
|
|
12
|
+
}): {
|
|
13
|
+
byTopic: TopicShare[];
|
|
14
|
+
focus: FocusStats;
|
|
15
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const round1 = (n) => Math.round(n * 10) / 10;
|
|
2
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
3
|
+
function countIn(ts, iv) {
|
|
4
|
+
let n = 0;
|
|
5
|
+
for (const t of ts)
|
|
6
|
+
if (t >= iv.start && t <= iv.end)
|
|
7
|
+
n++;
|
|
8
|
+
return n;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Attribute each merged active interval to a topic and derive attention +
|
|
12
|
+
* focus. v1 uses dominant-session attribution: the whole interval goes to the
|
|
13
|
+
* session with the most messages in it (the bounded simplification over
|
|
14
|
+
* fractional splitting documented in the design). Focus is derived from the
|
|
15
|
+
* ordered, attributed segments: adjacent same-topic segments collapse into
|
|
16
|
+
* blocks; a topic change between blocks is one context switch.
|
|
17
|
+
*/
|
|
18
|
+
export function attribute(merged, sessions, opts) {
|
|
19
|
+
const segments = [];
|
|
20
|
+
for (const iv of merged) {
|
|
21
|
+
const minutes = (iv.end - iv.start) / 60_000;
|
|
22
|
+
let best;
|
|
23
|
+
let bestN = -1;
|
|
24
|
+
for (const s of sessions) {
|
|
25
|
+
const n = countIn(s.timestampsMs, iv);
|
|
26
|
+
if (n > bestN) {
|
|
27
|
+
bestN = n;
|
|
28
|
+
best = s;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
segments.push({ topic: best ? best.topic : "uncategorized", minutes, workstreamId: best?.workstreamId ?? null });
|
|
32
|
+
}
|
|
33
|
+
const totalMin = segments.reduce((t, s) => t + s.minutes, 0);
|
|
34
|
+
const byTopicMap = new Map();
|
|
35
|
+
for (const seg of segments)
|
|
36
|
+
byTopicMap.set(seg.topic, (byTopicMap.get(seg.topic) ?? 0) + seg.minutes);
|
|
37
|
+
const wsByTopic = new Map();
|
|
38
|
+
for (const seg of segments)
|
|
39
|
+
if (seg.workstreamId && !wsByTopic.has(seg.topic))
|
|
40
|
+
wsByTopic.set(seg.topic, seg.workstreamId);
|
|
41
|
+
const byTopic = [...byTopicMap.entries()]
|
|
42
|
+
.map(([topic, mins]) => {
|
|
43
|
+
const wsId = wsByTopic.get(topic);
|
|
44
|
+
return {
|
|
45
|
+
topic,
|
|
46
|
+
activeMinutes: round1(mins),
|
|
47
|
+
share: totalMin ? round2(mins / totalMin) : 0,
|
|
48
|
+
...(wsId ? { meta: { workstream_id: wsId } } : {}),
|
|
49
|
+
};
|
|
50
|
+
})
|
|
51
|
+
.sort((a, b) => b.activeMinutes - a.activeMinutes);
|
|
52
|
+
const blocks = [];
|
|
53
|
+
for (const seg of segments) {
|
|
54
|
+
const last = blocks[blocks.length - 1];
|
|
55
|
+
if (last && last.topic === seg.topic)
|
|
56
|
+
last.minutes += seg.minutes;
|
|
57
|
+
else
|
|
58
|
+
blocks.push({ topic: seg.topic, minutes: seg.minutes });
|
|
59
|
+
}
|
|
60
|
+
const contextSwitches = Math.max(0, blocks.length - 1);
|
|
61
|
+
const longestBlockMin = blocks.reduce((mx, b) => Math.max(mx, b.minutes), 0);
|
|
62
|
+
const deepMin = blocks
|
|
63
|
+
.filter((b) => b.minutes >= opts.deepBlockMin)
|
|
64
|
+
.reduce((t, b) => t + b.minutes, 0);
|
|
65
|
+
const deepWorkRatio = totalMin ? round2(deepMin / totalMin) : 0;
|
|
66
|
+
return {
|
|
67
|
+
byTopic,
|
|
68
|
+
focus: {
|
|
69
|
+
contextSwitches,
|
|
70
|
+
longestBlockMin: round1(longestBlockMin),
|
|
71
|
+
deepWorkRatio,
|
|
72
|
+
projectsTouched: byTopicMap.size,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=attribute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribute.js","sourceRoot":"","sources":["../../../src/core/work-digest/attribute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SessionStore } from "../../ports/session-store.js";
|
|
2
|
+
import { type TopicProvider } from "./topics.js";
|
|
3
|
+
import type { WorkDigest } from "./types.js";
|
|
4
|
+
export interface BuildWorkDigestDeps {
|
|
5
|
+
readonly store: Pick<SessionStore, "listByDateRange">;
|
|
6
|
+
readonly topicProvider?: TopicProvider;
|
|
7
|
+
readonly idleThresholdMin?: number;
|
|
8
|
+
readonly deepBlockMin?: number;
|
|
9
|
+
readonly readTimestamps?: (path: string, fromMs: number, toMs: number) => number[];
|
|
10
|
+
readonly workstreams?: Pick<import("../../ports/workstream-store.js").WorkstreamStore, "listAll">;
|
|
11
|
+
}
|
|
12
|
+
export declare function buildWorkDigest(deps: BuildWorkDigestDeps, date: string): Promise<WorkDigest>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { activeSpans } from "./active-spans.js";
|
|
2
|
+
import { mergeIntervals } from "./merge-active.js";
|
|
3
|
+
import { attribute } from "./attribute.js";
|
|
4
|
+
import { readTranscriptTimestamps } from "./read-transcript-timestamps.js";
|
|
5
|
+
import { defaultTopicProvider, workstreamTopicProvider } from "./topics.js";
|
|
6
|
+
import { resolveWorkstreamId } from "../workstream/resolve.js";
|
|
7
|
+
const SCOPE_NOTE = "Agent-assisted active time. Excludes meetings and work without an agent.";
|
|
8
|
+
/** Local-midnight window for `date` (YYYY-MM-DD) in the process timezone. */
|
|
9
|
+
function dayWindow(date) {
|
|
10
|
+
const from = new Date(`${date}T00:00:00`);
|
|
11
|
+
const to = new Date(from);
|
|
12
|
+
to.setDate(to.getDate() + 1);
|
|
13
|
+
return { fromMs: from.getTime(), toMs: to.getTime(), fromIso: from.toISOString(), toIso: to.toISOString() };
|
|
14
|
+
}
|
|
15
|
+
export async function buildWorkDigest(deps, date) {
|
|
16
|
+
const idleThresholdMin = deps.idleThresholdMin ?? 5;
|
|
17
|
+
const deepBlockMin = deps.deepBlockMin ?? 25;
|
|
18
|
+
const topicProvider = deps.topicProvider ?? defaultTopicProvider;
|
|
19
|
+
const readTs = deps.readTimestamps ?? readTranscriptTimestamps;
|
|
20
|
+
const provider = deps.workstreams ? workstreamTopicProvider(topicProvider) : topicProvider;
|
|
21
|
+
const wsList = deps.workstreams ? await deps.workstreams.listAll() : [];
|
|
22
|
+
const wsById = new Map(wsList.map((w) => [w.id, { id: w.id, mergedInto: w.mergedInto }]));
|
|
23
|
+
const wsLabel = new Map(wsList.map((w) => [w.id, w.label]));
|
|
24
|
+
function resolveWs(id) {
|
|
25
|
+
if (!id)
|
|
26
|
+
return null;
|
|
27
|
+
const live = resolveWorkstreamId(id, wsById);
|
|
28
|
+
const label = wsLabel.get(live);
|
|
29
|
+
return label ? { id: live, label } : null;
|
|
30
|
+
}
|
|
31
|
+
const { fromMs, toMs, fromIso, toIso } = dayWindow(date);
|
|
32
|
+
const sessions = await deps.store.listByDateRange(fromIso, toIso);
|
|
33
|
+
const activities = [];
|
|
34
|
+
const allSpans = [];
|
|
35
|
+
const decisions = new Set();
|
|
36
|
+
const openLoops = new Set();
|
|
37
|
+
let measured = 0;
|
|
38
|
+
let skipped = 0;
|
|
39
|
+
for (const s of sessions) {
|
|
40
|
+
for (const d of s.decisions)
|
|
41
|
+
decisions.add(d);
|
|
42
|
+
for (const o of s.open)
|
|
43
|
+
openLoops.add(o);
|
|
44
|
+
const timestampsMs = s.transcriptPath ? readTs(s.transcriptPath, fromMs, toMs) : [];
|
|
45
|
+
if (timestampsMs.length === 0) {
|
|
46
|
+
skipped++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
measured++;
|
|
50
|
+
const ws = resolveWs(s.workstreamId);
|
|
51
|
+
const topic = provider({ entities: s.entities, label: s.label, ...(ws ? { workstreamLabel: ws.label } : {}) });
|
|
52
|
+
activities.push({ sessionId: s.id, topic, timestampsMs, ...(ws ? { workstreamId: ws.id } : {}) });
|
|
53
|
+
for (const span of activeSpans(timestampsMs, idleThresholdMin))
|
|
54
|
+
allSpans.push(span);
|
|
55
|
+
}
|
|
56
|
+
const { merged, totalMinutes } = mergeIntervals(allSpans);
|
|
57
|
+
const { byTopic, focus } = attribute(merged, activities, { deepBlockMin });
|
|
58
|
+
return {
|
|
59
|
+
date,
|
|
60
|
+
idleThresholdMin,
|
|
61
|
+
scopeNote: SCOPE_NOTE,
|
|
62
|
+
coverage: { sessions: sessions.length, activeTimeMeasured: measured, activeTimeSkipped: skipped },
|
|
63
|
+
activeMinutes: Math.round(totalMinutes * 10) / 10,
|
|
64
|
+
byTopic,
|
|
65
|
+
focus,
|
|
66
|
+
progress: { decisions: [...decisions], openLoops: [...openLoops] },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=build-work-digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-work-digest.js","sourceRoot":"","sources":["../../../src/core/work-digest/build-work-digest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const PROGRESS_CAP = 8;
|
|
2
|
+
function hours(min) {
|
|
3
|
+
return (min / 60).toFixed(1) + "h";
|
|
4
|
+
}
|
|
5
|
+
function renderProgressList(items, emptyLine, label) {
|
|
6
|
+
const len = items.length;
|
|
7
|
+
if (len === 0) {
|
|
8
|
+
return [emptyLine];
|
|
9
|
+
}
|
|
10
|
+
if (len <= PROGRESS_CAP) {
|
|
11
|
+
return [` ${label} (${len}):`, ...items.map((item) => ` - ${item}`)];
|
|
12
|
+
}
|
|
13
|
+
const tail = items.slice(len - PROGRESS_CAP);
|
|
14
|
+
const overflow = len - PROGRESS_CAP;
|
|
15
|
+
return [
|
|
16
|
+
` ${label}: ${len} (showing last ${PROGRESS_CAP})`,
|
|
17
|
+
...tail.map((item) => ` - ${item}`),
|
|
18
|
+
` ... (+${overflow} more)`,
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
/** Render a WorkDigest as the shared operator-facing text. No em dashes. */
|
|
22
|
+
export function composeWorkDigest(d) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push(`DAILY WORK RECAP - ${d.date}`);
|
|
25
|
+
lines.push(`(${d.scopeNote})`);
|
|
26
|
+
lines.push("");
|
|
27
|
+
if (d.activeMinutes <= 0) {
|
|
28
|
+
lines.push("no agent-assisted work recorded for this day.");
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
const skipped = d.coverage.activeTimeSkipped > 0
|
|
32
|
+
? ` (${d.coverage.activeTimeSkipped} session(s): active-time not measured)`
|
|
33
|
+
: "";
|
|
34
|
+
lines.push(`~${hours(d.activeMinutes)} active across ${d.coverage.sessions} sessions, ${d.focus.projectsTouched} projects${skipped}`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
lines.push("ATTENTION");
|
|
37
|
+
for (const t of d.byTopic) {
|
|
38
|
+
const pct = Math.round(t.share * 100) + "%";
|
|
39
|
+
lines.push(` ${t.topic}: ${pct} (${hours(t.activeMinutes)})`);
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
lines.push("FOCUS");
|
|
43
|
+
lines.push(` longest block: ${Math.round(d.focus.longestBlockMin)} min ` +
|
|
44
|
+
`context switches: ${d.focus.contextSwitches} ` +
|
|
45
|
+
`deep-work: ${Math.round(d.focus.deepWorkRatio * 100)}%`);
|
|
46
|
+
lines.push("");
|
|
47
|
+
lines.push("PROGRESS");
|
|
48
|
+
lines.push(...renderProgressList(d.progress.decisions, " decided: none recorded", "decided"));
|
|
49
|
+
lines.push(...renderProgressList(d.progress.openLoops, " open loops: none", "open loops"));
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=compose-work-digest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose-work-digest.js","sourceRoot":"","sources":["../../../src/core/work-digest/compose-work-digest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Interval } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Union overlapping/touching activity intervals into a single wall-clock
|
|
4
|
+
* timeline so concurrently-supervised agents are not double-counted, and
|
|
5
|
+
* report the total active minutes of that timeline.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mergeIntervals(intervals: ReadonlyArray<Interval>): {
|
|
8
|
+
merged: Interval[];
|
|
9
|
+
totalMinutes: number;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Union overlapping/touching activity intervals into a single wall-clock
|
|
3
|
+
* timeline so concurrently-supervised agents are not double-counted, and
|
|
4
|
+
* report the total active minutes of that timeline.
|
|
5
|
+
*/
|
|
6
|
+
export function mergeIntervals(intervals) {
|
|
7
|
+
const sorted = [...intervals].sort((a, b) => a.start - b.start);
|
|
8
|
+
const merged = [];
|
|
9
|
+
for (const iv of sorted) {
|
|
10
|
+
const last = merged[merged.length - 1];
|
|
11
|
+
if (last && iv.start <= last.end) {
|
|
12
|
+
if (iv.end > last.end)
|
|
13
|
+
merged[merged.length - 1] = { start: last.start, end: iv.end };
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
merged.push({ start: iv.start, end: iv.end });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const totalMinutes = merged.reduce((t, iv) => t + (iv.end - iv.start) / 60_000, 0);
|
|
20
|
+
return { merged, totalMinutes };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=merge-active.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-active.js","sourceRoot":"","sources":["../../../src/core/work-digest/merge-active.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAkC;IAElC,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,IAAI,IAAI,IAAI,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACjC,IAAI,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG;gBAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC;QACxF,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;IACnF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract message timestamps (epoch ms) from a transcript JSONL file, keeping
|
|
3
|
+
* only those within [fromMs, toMs). Best-effort: a missing/unreadable file or
|
|
4
|
+
* an unparseable line yields no timestamps rather than throwing. Accepts the
|
|
5
|
+
* common timestamp fields across runtimes (claude-code/pi use `timestamp`).
|
|
6
|
+
*/
|
|
7
|
+
export declare function readTranscriptTimestamps(path: string, fromMs: number, toMs: number): number[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
/**
|
|
3
|
+
* Extract message timestamps (epoch ms) from a transcript JSONL file, keeping
|
|
4
|
+
* only those within [fromMs, toMs). Best-effort: a missing/unreadable file or
|
|
5
|
+
* an unparseable line yields no timestamps rather than throwing. Accepts the
|
|
6
|
+
* common timestamp fields across runtimes (claude-code/pi use `timestamp`).
|
|
7
|
+
*/
|
|
8
|
+
export function readTranscriptTimestamps(path, fromMs, toMs) {
|
|
9
|
+
let raw;
|
|
10
|
+
try {
|
|
11
|
+
raw = readFileSync(path, "utf8");
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const line of raw.split("\n")) {
|
|
18
|
+
const trimmed = line.trim();
|
|
19
|
+
if (!trimmed)
|
|
20
|
+
continue;
|
|
21
|
+
let obj;
|
|
22
|
+
try {
|
|
23
|
+
obj = JSON.parse(trimmed);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const tsRaw = obj["timestamp"] ?? obj["ts"] ?? obj["created_at"];
|
|
29
|
+
if (typeof tsRaw !== "string")
|
|
30
|
+
continue;
|
|
31
|
+
const ms = Date.parse(tsRaw);
|
|
32
|
+
if (!Number.isFinite(ms))
|
|
33
|
+
continue;
|
|
34
|
+
if (ms >= fromMs && ms < toMs)
|
|
35
|
+
out.push(ms);
|
|
36
|
+
}
|
|
37
|
+
out.sort((a, b) => a - b);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=read-transcript-timestamps.js.map
|