nlm-memory 0.20.1 → 0.21.12
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 +69 -12
- package/assets/replay-eval-dark.svg +26 -0
- package/assets/replay-eval-light.svg +26 -0
- package/dist/cli/digest.js +69 -4
- package/dist/cli/digest.js.map +1 -1
- package/dist/cli/nlm.d.ts +14 -0
- package/dist/cli/nlm.js +556 -71
- package/dist/cli/nlm.js.map +1 -1
- package/dist/cli/reprocess-job-adapters.d.ts +35 -0
- package/dist/cli/reprocess-job-adapters.js +80 -0
- package/dist/cli/reprocess-job-adapters.js.map +1 -0
- 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 +208 -0
- package/dist/cli/scope-coverage.js.map +1 -0
- package/dist/cli/supersede.js +8 -7
- package/dist/cli/supersede.js.map +1 -1
- package/dist/core/actions/actions-log.d.ts +65 -8
- package/dist/core/actions/actions-log.js +134 -17
- package/dist/core/actions/actions-log.js.map +1 -1
- package/dist/core/actions/overlay.d.ts +11 -2
- package/dist/core/actions/overlay.js +18 -6
- 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 +30 -0
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/adapters/from-source.js +11 -3
- package/dist/core/adapters/from-source.js.map +1 -1
- package/dist/core/adapters/fs-wiki-writer.d.ts +44 -0
- package/dist/core/adapters/fs-wiki-writer.js +132 -0
- package/dist/core/adapters/fs-wiki-writer.js.map +1 -0
- package/dist/core/adapters/hermes-agent.d.ts +15 -2
- package/dist/core/adapters/hermes-agent.js +57 -11
- package/dist/core/adapters/hermes-agent.js.map +1 -1
- package/dist/core/adapters/memory-wiki-writer.d.ts +8 -0
- package/dist/core/adapters/memory-wiki-writer.js +16 -0
- package/dist/core/adapters/memory-wiki-writer.js.map +1 -0
- package/dist/core/adapters/muse.d.ts +51 -0
- package/dist/core/adapters/muse.js +271 -0
- package/dist/core/adapters/muse.js.map +1 -0
- package/dist/core/adapters/opencode.d.ts +11 -4
- package/dist/core/adapters/opencode.js +41 -7
- package/dist/core/adapters/opencode.js.map +1 -1
- package/dist/core/alerts/alert-state.d.ts +17 -0
- package/dist/core/alerts/alert-state.js +45 -0
- package/dist/core/alerts/alert-state.js.map +1 -0
- package/dist/core/alerts/check-and-alert.d.ts +28 -0
- package/dist/core/alerts/check-and-alert.js +53 -0
- package/dist/core/alerts/check-and-alert.js.map +1 -0
- package/dist/core/alerts/drift-transition.d.ts +27 -0
- package/dist/core/alerts/drift-transition.js +56 -0
- package/dist/core/alerts/drift-transition.js.map +1 -0
- package/dist/core/alerts/embedder-transition.d.ts +23 -0
- package/dist/core/alerts/embedder-transition.js +40 -0
- package/dist/core/alerts/embedder-transition.js.map +1 -0
- package/dist/core/alerts/fire-alert.d.ts +24 -0
- package/dist/core/alerts/fire-alert.js +72 -0
- package/dist/core/alerts/fire-alert.js.map +1 -0
- package/dist/core/alerts/job-alert.d.ts +12 -0
- package/dist/core/alerts/job-alert.js +37 -0
- package/dist/core/alerts/job-alert.js.map +1 -0
- package/dist/core/alerts/types.d.ts +104 -0
- package/dist/core/alerts/types.js +11 -0
- package/dist/core/alerts/types.js.map +1 -0
- package/dist/core/alerts/wiki-alert.d.ts +15 -0
- package/dist/core/alerts/wiki-alert.js +31 -0
- package/dist/core/alerts/wiki-alert.js.map +1 -0
- package/dist/core/classifier/prompt.d.ts +13 -0
- package/dist/core/classifier/prompt.js +52 -2
- package/dist/core/classifier/prompt.js.map +1 -1
- package/dist/core/dataset/build-dataset.d.ts +16 -1
- package/dist/core/dataset/build-dataset.js +46 -13
- package/dist/core/dataset/build-dataset.js.map +1 -1
- package/dist/core/digest/compose.d.ts +11 -3
- package/dist/core/digest/compose.js +70 -17
- package/dist/core/digest/compose.js.map +1 -1
- package/dist/core/digest/hook-injection.d.ts +43 -0
- package/dist/core/digest/hook-injection.js +87 -0
- package/dist/core/digest/hook-injection.js.map +1 -0
- package/dist/core/digest/hook-version-parity.d.ts +31 -0
- package/dist/core/digest/hook-version-parity.js +56 -0
- package/dist/core/digest/hook-version-parity.js.map +1 -0
- package/dist/core/embedding/pg-embed-backfill.js +1 -1
- package/dist/core/embedding/pg-embed-backfill.js.map +1 -1
- package/dist/core/exemplars/capture-from-session.d.ts +2 -1
- package/dist/core/exemplars/capture-from-session.js +4 -4
- package/dist/core/exemplars/capture-from-session.js.map +1 -1
- package/dist/core/exemplars/embed-backfill.d.ts +9 -0
- package/dist/core/exemplars/embed-backfill.js +9 -1
- package/dist/core/exemplars/embed-backfill.js.map +1 -1
- package/dist/core/exemplars/extract-exemplar.d.ts +2 -0
- package/dist/core/exemplars/extract-exemplar.js +7 -1
- package/dist/core/exemplars/extract-exemplar.js.map +1 -1
- package/dist/core/exemplars/ingest-exemplar.d.ts +1 -0
- package/dist/core/exemplars/ingest-exemplar.js +1 -0
- package/dist/core/exemplars/ingest-exemplar.js.map +1 -1
- package/dist/core/exemplars/recall-code.d.ts +1 -1
- package/dist/core/exemplars/recall-code.js +2 -2
- package/dist/core/exemplars/recall-code.js.map +1 -1
- package/dist/core/facts/backfill-facts.d.ts +1 -1
- package/dist/core/facts/backfill-facts.js +4 -4
- package/dist/core/facts/backfill-facts.js.map +1 -1
- package/dist/core/facts/embed-backfill.d.ts +54 -0
- package/dist/core/facts/embed-backfill.js +109 -0
- package/dist/core/facts/embed-backfill.js.map +1 -0
- package/dist/core/facts/reconcile-facts.d.ts +42 -0
- package/dist/core/facts/reconcile-facts.js +133 -0
- package/dist/core/facts/reconcile-facts.js.map +1 -0
- package/dist/core/health/embed-failure-state.d.ts +4 -0
- package/dist/core/health/embed-failure-state.js +12 -0
- package/dist/core/health/embed-failure-state.js.map +1 -0
- package/dist/core/health/warmup-retry.d.ts +27 -0
- package/dist/core/health/warmup-retry.js +53 -0
- package/dist/core/health/warmup-retry.js.map +1 -0
- package/dist/core/health/warmup-state.d.ts +15 -0
- package/dist/core/health/warmup-state.js +15 -1
- package/dist/core/health/warmup-state.js.map +1 -1
- package/dist/core/hook/cite-memo.d.ts +11 -7
- package/dist/core/hook/cite-memo.js +25 -17
- package/dist/core/hook/cite-memo.js.map +1 -1
- package/dist/core/hook/claude-settings.d.ts +1 -1
- package/dist/core/hook/claude-settings.js +0 -1
- package/dist/core/hook/claude-settings.js.map +1 -1
- package/dist/core/hook/hook-log.d.ts +13 -2
- package/dist/core/hook/hook-log.js +16 -8
- package/dist/core/hook/hook-log.js.map +1 -1
- package/dist/core/hook/memo.d.ts +10 -6
- package/dist/core/hook/memo.js +25 -17
- package/dist/core/hook/memo.js.map +1 -1
- package/dist/core/hook/pointer-block.d.ts +8 -1
- package/dist/core/hook/pointer-block.js +17 -4
- package/dist/core/hook/pointer-block.js.map +1 -1
- package/dist/core/hook/resolve-conversation-by-query.d.ts +6 -5
- package/dist/core/hook/resolve-conversation-by-query.js +24 -12
- package/dist/core/hook/resolve-conversation-by-query.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 +1 -1
- package/dist/core/ingest/ingest-session.js +23 -3
- package/dist/core/ingest/ingest-session.js.map +1 -1
- package/dist/core/ingest/reclassify-oversized.d.ts +1 -1
- package/dist/core/ingest/reclassify-oversized.js +8 -2
- package/dist/core/ingest/reclassify-oversized.js.map +1 -1
- package/dist/core/ingest/reprocess.d.ts +1 -1
- package/dist/core/ingest/reprocess.js +7 -2
- package/dist/core/ingest/reprocess.js.map +1 -1
- package/dist/core/ingest/transcript-derivables.d.ts +27 -0
- package/dist/core/ingest/transcript-derivables.js +138 -0
- package/dist/core/ingest/transcript-derivables.js.map +1 -0
- package/dist/core/jobs/job-supervisor.d.ts +168 -0
- package/dist/core/jobs/job-supervisor.js +335 -0
- package/dist/core/jobs/job-supervisor.js.map +1 -0
- package/dist/core/learner/model.d.ts +63 -0
- package/dist/core/learner/model.js +30 -0
- package/dist/core/learner/model.js.map +1 -0
- package/dist/core/learner/service.d.ts +42 -0
- package/dist/core/learner/service.js +74 -0
- package/dist/core/learner/service.js.map +1 -0
- package/dist/core/learner/summary.d.ts +28 -0
- package/dist/core/learner/summary.js +70 -0
- package/dist/core/learner/summary.js.map +1 -0
- package/dist/core/learner/validate.d.ts +18 -0
- package/dist/core/learner/validate.js +95 -0
- package/dist/core/learner/validate.js.map +1 -0
- package/dist/core/metrics/re-derivation.d.ts +31 -2
- package/dist/core/metrics/re-derivation.js +36 -4
- package/dist/core/metrics/re-derivation.js.map +1 -1
- 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/providers/provider-registry.d.ts +40 -23
- package/dist/core/providers/provider-registry.js +99 -81
- package/dist/core/providers/provider-registry.js.map +1 -1
- package/dist/core/recall/citation-log.d.ts +10 -5
- package/dist/core/recall/citation-log.js +21 -10
- package/dist/core/recall/citation-log.js.map +1 -1
- package/dist/core/recall/hook-recall-log.d.ts +7 -4
- package/dist/core/recall/hook-recall-log.js +14 -8
- package/dist/core/recall/hook-recall-log.js.map +1 -1
- package/dist/core/recall/miss-log.d.ts +7 -4
- package/dist/core/recall/miss-log.js +18 -11
- package/dist/core/recall/miss-log.js.map +1 -1
- package/dist/core/recall/query-log.d.ts +8 -5
- package/dist/core/recall/query-log.js +16 -9
- package/dist/core/recall/query-log.js.map +1 -1
- package/dist/core/recall/recall-service.d.ts +10 -1
- package/dist/core/recall/recall-service.js +31 -12
- package/dist/core/recall/recall-service.js.map +1 -1
- package/dist/core/recall/recent-log.d.ts +6 -1
- package/dist/core/recall/recent-log.js +13 -5
- package/dist/core/recall/recent-log.js.map +1 -1
- package/dist/core/recall/related-concepts.d.ts +29 -0
- package/dist/core/recall/related-concepts.js +67 -0
- package/dist/core/recall/related-concepts.js.map +1 -0
- package/dist/core/recall/related-exemplars.d.ts +1 -1
- package/dist/core/recall/related-exemplars.js +2 -2
- package/dist/core/recall/related-exemplars.js.map +1 -1
- package/dist/core/recall/related-facts.d.ts +1 -1
- package/dist/core/recall/related-facts.js +3 -3
- package/dist/core/recall/related-facts.js.map +1 -1
- package/dist/core/recall-facts/fact-query-log.d.ts +8 -4
- package/dist/core/recall-facts/fact-query-log.js +16 -8
- package/dist/core/recall-facts/fact-query-log.js.map +1 -1
- package/dist/core/recall-facts/fact-recall-service.d.ts +2 -2
- package/dist/core/recall-facts/fact-recall-service.js +13 -12
- package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
- package/dist/core/scheduler/scan-once.js +15 -12
- package/dist/core/scheduler/scan-once.js.map +1 -1
- package/dist/core/scheduler/scheduler.d.ts +3 -0
- package/dist/core/scheduler/scheduler.js +45 -10
- package/dist/core/scheduler/scheduler.js.map +1 -1
- package/dist/core/scope/alias-map.d.ts +10 -0
- package/dist/core/scope/alias-map.js +41 -0
- package/dist/core/scope/alias-map.js.map +1 -0
- package/dist/core/scope/derive-scope.d.ts +8 -0
- package/dist/core/scope/derive-scope.js +54 -0
- package/dist/core/scope/derive-scope.js.map +1 -0
- package/dist/core/scope/scope-clause.d.ts +16 -0
- package/dist/core/scope/scope-clause.js +21 -0
- package/dist/core/scope/scope-clause.js.map +1 -0
- package/dist/core/scope/stamp-flag.d.ts +1 -0
- package/dist/core/scope/stamp-flag.js +4 -0
- package/dist/core/scope/stamp-flag.js.map +1 -0
- package/dist/core/signals/code-signal.d.ts +1 -0
- package/dist/core/signals/code-signal.js +1 -0
- package/dist/core/signals/code-signal.js.map +1 -1
- package/dist/core/signals/failure-mode-recall.d.ts +1 -1
- package/dist/core/signals/failure-mode-recall.js +2 -2
- package/dist/core/signals/failure-mode-recall.js.map +1 -1
- package/dist/core/signals/ingest-signal.d.ts +1 -1
- package/dist/core/signals/ingest-signal.js +3 -1
- package/dist/core/signals/ingest-signal.js.map +1 -1
- package/dist/core/signals/stamp-scope.d.ts +18 -0
- package/dist/core/signals/stamp-scope.js +28 -0
- package/dist/core/signals/stamp-scope.js.map +1 -0
- package/dist/core/sources/source-registry.d.ts +49 -28
- package/dist/core/sources/source-registry.js +113 -70
- package/dist/core/sources/source-registry.js.map +1 -1
- package/dist/core/storage/pg-code-exemplar-store.d.ts +19 -10
- package/dist/core/storage/pg-code-exemplar-store.js +62 -27
- package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/pg-entity-store.d.ts +6 -1
- package/dist/core/storage/pg-entity-store.js +33 -18
- package/dist/core/storage/pg-entity-store.js.map +1 -1
- package/dist/core/storage/pg-fact-ingest.d.ts +1 -1
- package/dist/core/storage/pg-fact-ingest.js +10 -7
- package/dist/core/storage/pg-fact-ingest.js.map +1 -1
- package/dist/core/storage/pg-fact-store.d.ts +29 -17
- package/dist/core/storage/pg-fact-store.js +99 -46
- package/dist/core/storage/pg-fact-store.js.map +1 -1
- package/dist/core/storage/pg-learner-store.d.ts +27 -0
- package/dist/core/storage/pg-learner-store.js +174 -0
- package/dist/core/storage/pg-learner-store.js.map +1 -0
- package/dist/core/storage/pg-session-store.d.ts +25 -21
- package/dist/core/storage/pg-session-store.js +186 -114
- package/dist/core/storage/pg-session-store.js.map +1 -1
- package/dist/core/storage/pg-signal-store.d.ts +10 -5
- package/dist/core/storage/pg-signal-store.js +26 -13
- package/dist/core/storage/pg-signal-store.js.map +1 -1
- package/dist/core/storage/pg-storage.d.ts +4 -0
- package/dist/core/storage/pg-storage.js +6 -0
- package/dist/core/storage/pg-storage.js.map +1 -1
- package/dist/core/storage/pg-workstream-store.d.ts +17 -11
- package/dist/core/storage/pg-workstream-store.js +47 -29
- package/dist/core/storage/pg-workstream-store.js.map +1 -1
- package/dist/core/storage/sqlite-code-exemplar-store.d.ts +24 -10
- package/dist/core/storage/sqlite-code-exemplar-store.js +86 -40
- package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
- package/dist/core/storage/sqlite-entity-store.d.ts +8 -1
- package/dist/core/storage/sqlite-entity-store.js +46 -27
- package/dist/core/storage/sqlite-entity-store.js.map +1 -1
- package/dist/core/storage/sqlite-fact-store.d.ts +45 -19
- package/dist/core/storage/sqlite-fact-store.js +175 -64
- package/dist/core/storage/sqlite-fact-store.js.map +1 -1
- package/dist/core/storage/sqlite-learner-store.d.ts +33 -0
- package/dist/core/storage/sqlite-learner-store.js +178 -0
- package/dist/core/storage/sqlite-learner-store.js.map +1 -0
- package/dist/core/storage/sqlite-outcome-store.d.ts +104 -0
- package/dist/core/storage/sqlite-outcome-store.js +211 -0
- package/dist/core/storage/sqlite-outcome-store.js.map +1 -0
- package/dist/core/storage/sqlite-session-store.d.ts +50 -21
- package/dist/core/storage/sqlite-session-store.js +233 -149
- package/dist/core/storage/sqlite-session-store.js.map +1 -1
- package/dist/core/storage/sqlite-signal-store.d.ts +12 -5
- package/dist/core/storage/sqlite-signal-store.js +29 -15
- package/dist/core/storage/sqlite-signal-store.js.map +1 -1
- package/dist/core/storage/sqlite-storage.d.ts +4 -0
- package/dist/core/storage/sqlite-storage.js +10 -2
- package/dist/core/storage/sqlite-storage.js.map +1 -1
- package/dist/core/storage/sqlite-workstream-store.d.ts +17 -11
- package/dist/core/storage/sqlite-workstream-store.js +53 -31
- package/dist/core/storage/sqlite-workstream-store.js.map +1 -1
- package/dist/core/storage/supersedence-log.d.ts +10 -7
- package/dist/core/storage/supersedence-log.js +21 -14
- 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/tenancy/default-team.d.ts +6 -0
- package/dist/core/tenancy/default-team.js +7 -0
- package/dist/core/tenancy/default-team.js.map +1 -0
- package/dist/core/tenancy/hosted-mode.d.ts +9 -0
- package/dist/core/tenancy/hosted-mode.js +12 -0
- package/dist/core/tenancy/hosted-mode.js.map +1 -0
- package/dist/core/tenancy/team-auth.d.ts +16 -0
- package/dist/core/tenancy/team-auth.js +22 -0
- package/dist/core/tenancy/team-auth.js.map +1 -0
- package/dist/core/tenancy/team-token-store.d.ts +45 -0
- package/dist/core/tenancy/team-token-store.js +52 -0
- package/dist/core/tenancy/team-token-store.js.map +1 -0
- package/dist/core/tenancy/tenant-clause.d.ts +37 -0
- package/dist/core/tenancy/tenant-clause.js +11 -0
- package/dist/core/tenancy/tenant-clause.js.map +1 -0
- package/dist/core/tenancy/tenant-state-path.d.ts +28 -0
- package/dist/core/tenancy/tenant-state-path.js +49 -0
- package/dist/core/tenancy/tenant-state-path.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/wiki/project.d.ts +17 -0
- package/dist/core/wiki/project.js +37 -0
- package/dist/core/wiki/project.js.map +1 -0
- package/dist/core/wiki/reconcile.d.ts +15 -0
- package/dist/core/wiki/reconcile.js +14 -0
- package/dist/core/wiki/reconcile.js.map +1 -0
- package/dist/core/wiki/render.d.ts +5 -0
- package/dist/core/wiki/render.js +84 -0
- package/dist/core/wiki/render.js.map +1 -0
- package/dist/core/wiki/rollup.d.ts +28 -0
- package/dist/core/wiki/rollup.js +91 -0
- package/dist/core/wiki/rollup.js.map +1 -0
- package/dist/core/wiki/select.d.ts +8 -0
- package/dist/core/wiki/select.js +7 -0
- package/dist/core/wiki/select.js.map +1 -0
- package/dist/core/wiki/slug.d.ts +36 -0
- package/dist/core/wiki/slug.js +71 -0
- package/dist/core/wiki/slug.js.map +1 -0
- package/dist/core/wiki/types.d.ts +37 -0
- package/dist/core/wiki/types.js +2 -0
- package/dist/core/wiki/types.js.map +1 -0
- package/dist/core/work-digest/build-work-digest.d.ts +1 -1
- package/dist/core/work-digest/build-work-digest.js +3 -3
- package/dist/core/work-digest/build-work-digest.js.map +1 -1
- package/dist/core/workstream/bind.d.ts +3 -2
- package/dist/core/workstream/bind.js +29 -10
- package/dist/core/workstream/bind.js.map +1 -1
- package/dist/core/workstream/model.d.ts +1 -0
- package/dist/core/workstream/model.js.map +1 -1
- package/dist/core/workstream/rollup.d.ts +1 -1
- package/dist/core/workstream/rollup.js +6 -6
- package/dist/core/workstream/rollup.js.map +1 -1
- package/dist/hook/hook-helpers.d.ts +14 -0
- package/dist/hook/hook-helpers.js +29 -2
- package/dist/hook/hook-helpers.js.map +1 -1
- package/dist/hook/pre-compact-hook.js +8 -5
- package/dist/hook/pre-compact-hook.js.map +1 -1
- package/dist/hook/prompt-recall-hook.d.ts +7 -3
- package/dist/hook/prompt-recall-hook.js +35 -20
- package/dist/hook/prompt-recall-hook.js.map +1 -1
- package/dist/hook/recall-over-http.d.ts +28 -3
- package/dist/hook/recall-over-http.js +58 -12
- package/dist/hook/recall-over-http.js.map +1 -1
- package/dist/hook/session-end-hook.js +7 -5
- package/dist/hook/session-end-hook.js.map +1 -1
- package/dist/hook/session-start-hook.d.ts +14 -1
- package/dist/hook/session-start-hook.js +57 -16
- package/dist/hook/session-start-hook.js.map +1 -1
- package/dist/hook/stop-hook.js +11 -8
- package/dist/hook/stop-hook.js.map +1 -1
- package/dist/http/app.d.ts +47 -3
- package/dist/http/app.js +379 -132
- package/dist/http/app.js.map +1 -1
- package/dist/install/claude-code.d.ts +17 -0
- package/dist/install/claude-code.js +44 -5
- package/dist/install/claude-code.js.map +1 -1
- package/dist/install/codex.d.ts +23 -2
- package/dist/install/codex.js +111 -14
- package/dist/install/codex.js.map +1 -1
- package/dist/install/cursor.d.ts +2 -2
- package/dist/install/cursor.js +7 -7
- package/dist/install/cursor.js.map +1 -1
- package/dist/install/hermes.d.ts +8 -0
- package/dist/install/hermes.js +28 -5
- package/dist/install/hermes.js.map +1 -1
- package/dist/install/local-source.d.ts +20 -0
- package/dist/install/local-source.js +28 -0
- package/dist/install/local-source.js.map +1 -0
- package/dist/install/mcp-block.d.ts +77 -0
- package/dist/install/mcp-block.js +99 -0
- package/dist/install/mcp-block.js.map +1 -0
- package/dist/install/pi.d.ts +26 -0
- package/dist/install/pi.js +43 -0
- package/dist/install/pi.js.map +1 -1
- package/dist/install/setup.js +6 -0
- package/dist/install/setup.js.map +1 -1
- package/dist/install/windsurf.d.ts +2 -2
- package/dist/install/windsurf.js +7 -7
- package/dist/install/windsurf.js.map +1 -1
- package/dist/llm/classifier-box.d.ts +5 -0
- package/dist/llm/classifier-box.js +5 -0
- package/dist/llm/classifier-box.js.map +1 -1
- package/dist/mcp/server.d.ts +94 -15
- package/dist/mcp/server.js +402 -79
- package/dist/mcp/server.js.map +1 -1
- package/dist/ports/clock.d.ts +12 -0
- package/dist/ports/clock.js +10 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/code-exemplar-store.d.ts +13 -13
- package/dist/ports/entity-store.d.ts +5 -1
- package/dist/ports/fact-store.d.ts +0 -0
- package/dist/ports/fact-store.js +0 -0
- package/dist/ports/fact-store.js.map +1 -1
- package/dist/ports/learner-store.d.ts +80 -0
- package/dist/ports/learner-store.js +19 -0
- package/dist/ports/learner-store.js.map +1 -0
- 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 +20 -20
- package/dist/ports/session-store.js +5 -0
- package/dist/ports/session-store.js.map +1 -1
- package/dist/ports/signal-store.d.ts +12 -8
- package/dist/ports/spawn-child.d.ts +14 -0
- package/dist/ports/spawn-child.js +10 -0
- package/dist/ports/spawn-child.js.map +1 -0
- package/dist/ports/storage.d.ts +6 -0
- package/dist/ports/transcript-adapter.d.ts +11 -0
- package/dist/ports/wiki-writer.d.ts +19 -0
- package/dist/ports/wiki-writer.js +12 -0
- package/dist/ports/wiki-writer.js.map +1 -0
- package/dist/ports/workstream-store.d.ts +20 -12
- 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 +55 -0
- package/dist/shared/types.js +4 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/shared/version.d.ts +9 -0
- package/dist/shared/version.js +11 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/ui/assets/index-D6F3xAis.js +11 -0
- package/dist/ui/index.html +1 -1
- package/migrations/029_project_scope.sql +21 -0
- package/migrations/032_session_derivables.sql +11 -0
- package/migrations/033_actions_kind_check.sql +58 -0
- package/migrations/034_tenancy_teams_and_stamps.sql +40 -0
- package/migrations/035_tenancy_entity_rekey.sql +75 -0
- package/migrations/036_tenant_name_uniqueness.sql +59 -0
- package/migrations/037_sessions_covering_index.sql +25 -0
- package/migrations/038_sources_muse.sql +33 -0
- package/migrations/039_learner_evidence.sql +51 -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/migrations/pg/033_actions_kind_check.sql +17 -0
- package/migrations/pg/034_tenancy.sql +77 -0
- package/migrations/pg/035_tenant_name_uniqueness.sql +15 -0
- package/migrations/pg/036_sources_muse.sql +12 -0
- package/migrations/pg/037_learner_evidence.sql +55 -0
- package/nlm/index.js +258 -59
- package/package.json +10 -5
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/scripts/prompt-recall-hook.mjs +256 -58
- package/plugin/scripts/session-start-hook.mjs +283 -55
- package/plugin/scripts/stop-hook.mjs +199 -38
- package/dist/hook/subagent-start-hook.d.ts +0 -28
- package/dist/hook/subagent-start-hook.js +0 -81
- package/dist/hook/subagent-start-hook.js.map +0 -1
- package/dist/ui/assets/index-CMpQkbQy.js +0 -11
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JobSupervisor — owns at most one child `reprocess` run at a time.
|
|
3
|
+
*
|
|
4
|
+
* Built because a detached `nlm reprocess` died on a V8 OOM and sat dead
|
|
5
|
+
* ~16h unobserved (twice: 07-27 and 07-27→08-03). Supervision now lives in
|
|
6
|
+
* the daemon instead of a bolt-on watchdog script: this class tracks a
|
|
7
|
+
* single child process's progress, restarts it on a non-clean exit while
|
|
8
|
+
* work remains, and reports stalls when the child is alive but stuck.
|
|
9
|
+
*
|
|
10
|
+
* One job type only (reprocess) — no generic job framework (YAGNI). Ports
|
|
11
|
+
* (`spawnChild`, `clock`) are injected so this stays pure-core: no real
|
|
12
|
+
* child_process, no real timers, testable with fakes. Same posture as
|
|
13
|
+
* `ScanScheduler` (src/core/scheduler/scheduler.ts).
|
|
14
|
+
*
|
|
15
|
+
* Progress-line format verified directly in src/cli/nlm.ts's `reprocess`
|
|
16
|
+
* command (the only place reprocess's `onProgress` callback is wired to
|
|
17
|
+
* output — reprocess.ts itself never prints, it just calls the callback):
|
|
18
|
+
*
|
|
19
|
+
* process.stderr.write(` [${i}/${n}] ${sid} ${status}\n`);
|
|
20
|
+
*
|
|
21
|
+
* i.e. two leading spaces, `[i/n]` (no zero-padding), a single space, the
|
|
22
|
+
* session id, TWO spaces, then a status word (`ok`, `empty_body`,
|
|
23
|
+
* `classify_failed`, or `ingest_failed`). The plan's assumed format
|
|
24
|
+
* (`[N/total] <id> ok`) was close but understated the leading indent and
|
|
25
|
+
* the double space before status. `processed` in reprocess.ts increments
|
|
26
|
+
* on every branch (success or failure), so ANY line matching `[i/n]` is a
|
|
27
|
+
* genuine advance regardless of the trailing status word — the parser
|
|
28
|
+
* below only needs the two numbers.
|
|
29
|
+
*
|
|
30
|
+
* Restart policy: child exit with work remaining → re-spawn same args, up
|
|
31
|
+
* to `maxRestartsWithoutProgress` (default 3) consecutive restarts with no
|
|
32
|
+
* progress observed since the last one. A progress line resets the
|
|
33
|
+
* counter to 0. Exceeding the cap fires `{kind: "exhausted"}` and stops
|
|
34
|
+
* respawning. "Work remaining" is `code !== 0` (covers OOM/signal kills
|
|
35
|
+
* and any non-clean exit, including one where no progress line was ever
|
|
36
|
+
* seen) OR `processed < total` on an otherwise-clean exit — reprocess only
|
|
37
|
+
* exits 0 after its `work` loop finishes every item, so a clean exit with
|
|
38
|
+
* `processed < total` is a defensive fallback, not the expected path.
|
|
39
|
+
*
|
|
40
|
+
* Stall: child alive, no progress advance for `stallMinutes` (default 20,
|
|
41
|
+
* from `NLM_JOB_STALL_MINUTES`) → fires `{kind: "stalled"}` ONCE per stall
|
|
42
|
+
* episode. A later progress advance ends the episode (state returns to
|
|
43
|
+
* "running"); a subsequent stall re-arms and can fire again. Checked on a
|
|
44
|
+
* coarse interval behind the injected `clock` port so tests never need
|
|
45
|
+
* real timers.
|
|
46
|
+
*
|
|
47
|
+
* `restarts` in both the snapshot and event payloads is the
|
|
48
|
+
* restarts-without-progress counter (reset by any advance), not a
|
|
49
|
+
* lifetime total — it directly explains "why is this run stalled/
|
|
50
|
+
* exhausted" the way an operator reading /api/health would want. It counts
|
|
51
|
+
* only respawns that actually happened: the cap check runs BEFORE the
|
|
52
|
+
* increment, so an exhausted run's `restarts` equals the number of real
|
|
53
|
+
* respawns performed (== `maxRestartsWithoutProgress`), never that value
|
|
54
|
+
* plus the refused final attempt.
|
|
55
|
+
*
|
|
56
|
+
* `restartsTotal` is a companion lifetime counter: every successful respawn
|
|
57
|
+
* increments it, and unlike `restarts` it is never reset by progress —
|
|
58
|
+
* only a fresh `start()` resets it to 0. It exists because `restarts`
|
|
59
|
+
* resetting on every advance makes OOM churn invisible on a long run (a job
|
|
60
|
+
* that OOM-restarts 40 times but keeps making progress between crashes
|
|
61
|
+
* reports `restarts: 0`); `restartsTotal` is the "how much churn has this
|
|
62
|
+
* run actually had" number surfaced alongside it.
|
|
63
|
+
*
|
|
64
|
+
* State machine: "idle" (never started, or this JobSupervisor instance's
|
|
65
|
+
* initial state — not one of the plan's five snapshot states, but the
|
|
66
|
+
* natural "no job block" representation Task 2's health-route mapping
|
|
67
|
+
* needs) → "running" → ("stalled" ⇄ "running" across stall episodes) →
|
|
68
|
+
* terminal "completed" | "exhausted" | "stopped". A terminal state is
|
|
69
|
+
* left only by calling `start()` again, which begins a fresh run.
|
|
70
|
+
*
|
|
71
|
+
* Spawn-failure safety: `spawnChild` can throw synchronously (EMFILE,
|
|
72
|
+
* ENOMEM, a bad binary path) instead of returning a handle — both at
|
|
73
|
+
* `start()` and at every mid-run respawn inside `handleExit`. Both call
|
|
74
|
+
* sites wrap `spawnAndWire()` in try/catch: on a thrown spawn, the run
|
|
75
|
+
* lands in the terminal "exhausted" state (never leaves `state` reading
|
|
76
|
+
* "running" with no child) and fires a `{kind: "spawn_failed"}` event so
|
|
77
|
+
* an operator can tell "never got off the ground" apart from "ran out of
|
|
78
|
+
* restart budget". `start()` still rethrows the original error after
|
|
79
|
+
* landing that terminal state, preserving its existing contract (the HTTP
|
|
80
|
+
* route at `POST /api/jobs/reprocess` catches the throw and returns a
|
|
81
|
+
* clean 500) — the fix is that by the time it throws, `snapshot()` already
|
|
82
|
+
* reads a terminal state, so a subsequent `start()` call is not refused
|
|
83
|
+
* with "already active" and a subsequent POST does not 409 forever.
|
|
84
|
+
* `handleExit` has no caller to rethrow to (it runs off an async `onExit`
|
|
85
|
+
* callback), so it only logs and stops there.
|
|
86
|
+
*
|
|
87
|
+
* Stale-callback safety: `spawnAndWire` captures the child instance it just
|
|
88
|
+
* spawned and every onLine/onExit callback checks `this.child === child`
|
|
89
|
+
* before touching any state. A killed child (stop(), or a fast respawn
|
|
90
|
+
* chain) can still deliver a queued exit or line after this.child has moved
|
|
91
|
+
* on to a newer child or gone null; without the identity guard that stale
|
|
92
|
+
* delivery would corrupt the live run's processed/total/restarts, null out
|
|
93
|
+
* the live child (leaking it — now unmanaged and unkillable), or trigger a
|
|
94
|
+
* bogus respawn.
|
|
95
|
+
*/
|
|
96
|
+
const DEFAULT_STALL_MINUTES = 20;
|
|
97
|
+
const DEFAULT_MAX_RESTARTS_WITHOUT_PROGRESS = 3;
|
|
98
|
+
const DEFAULT_STALL_CHECK_INTERVAL_MS = 60_000;
|
|
99
|
+
// Matches the two-space-indented `[i/n] <sid> <status>` verbose line reprocess's
|
|
100
|
+
// CLI wiring writes (src/cli/nlm.ts, reprocess command). Only the counters
|
|
101
|
+
// matter: reprocess increments `processed` on every outcome branch, so any
|
|
102
|
+
// line of this shape is an advance regardless of what follows it.
|
|
103
|
+
const PROGRESS_RE = /^\s*\[(\d+)\/(\d+)\]/;
|
|
104
|
+
function stallMinutesFromEnv() {
|
|
105
|
+
const raw = process.env["NLM_JOB_STALL_MINUTES"];
|
|
106
|
+
if (raw === undefined)
|
|
107
|
+
return DEFAULT_STALL_MINUTES;
|
|
108
|
+
const parsed = Number(raw);
|
|
109
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_STALL_MINUTES;
|
|
110
|
+
}
|
|
111
|
+
export class JobSupervisor {
|
|
112
|
+
spawnChildFn;
|
|
113
|
+
clock;
|
|
114
|
+
onEventCb;
|
|
115
|
+
stallMs;
|
|
116
|
+
maxRestartsWithoutProgress;
|
|
117
|
+
log;
|
|
118
|
+
state = "idle";
|
|
119
|
+
args = [];
|
|
120
|
+
child = null;
|
|
121
|
+
processed = 0;
|
|
122
|
+
total = 0;
|
|
123
|
+
startedAt = null;
|
|
124
|
+
lastAdvanceAt = null;
|
|
125
|
+
restarts = 0;
|
|
126
|
+
restartsTotal = 0;
|
|
127
|
+
stallEpisodeFired = false;
|
|
128
|
+
stopRequested = false;
|
|
129
|
+
constructor(opts) {
|
|
130
|
+
this.spawnChildFn = opts.spawnChild;
|
|
131
|
+
this.clock = opts.clock;
|
|
132
|
+
this.onEventCb = opts.onEvent ?? (() => { });
|
|
133
|
+
this.stallMs = (opts.stallMinutes ?? stallMinutesFromEnv()) * 60_000;
|
|
134
|
+
this.maxRestartsWithoutProgress =
|
|
135
|
+
opts.maxRestartsWithoutProgress ?? DEFAULT_MAX_RESTARTS_WITHOUT_PROGRESS;
|
|
136
|
+
this.log = opts.log ?? ((msg) => console.error(msg));
|
|
137
|
+
const stallCheckIntervalMs = opts.stallCheckIntervalMs ?? DEFAULT_STALL_CHECK_INTERVAL_MS;
|
|
138
|
+
this.clock.setInterval(() => this.checkStall(), stallCheckIntervalMs);
|
|
139
|
+
}
|
|
140
|
+
/** Starts a new run. Refuses (throws) if a run is already active. */
|
|
141
|
+
start(args) {
|
|
142
|
+
if (this.state === "running" || this.state === "stalled") {
|
|
143
|
+
throw new Error("reprocess job already active");
|
|
144
|
+
}
|
|
145
|
+
this.args = args;
|
|
146
|
+
this.processed = 0;
|
|
147
|
+
this.total = 0;
|
|
148
|
+
this.restarts = 0;
|
|
149
|
+
this.restartsTotal = 0;
|
|
150
|
+
this.stallEpisodeFired = false;
|
|
151
|
+
this.stopRequested = false;
|
|
152
|
+
this.startedAt = new Date(this.clock.now()).toISOString();
|
|
153
|
+
this.lastAdvanceAt = null;
|
|
154
|
+
this.state = "running";
|
|
155
|
+
try {
|
|
156
|
+
this.spawnAndWire();
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
this.child = null;
|
|
160
|
+
this.state = "exhausted";
|
|
161
|
+
this.emitEvent({
|
|
162
|
+
kind: "spawn_failed",
|
|
163
|
+
job: "reprocess",
|
|
164
|
+
processed: this.processed,
|
|
165
|
+
total: this.total,
|
|
166
|
+
restarts: this.restarts,
|
|
167
|
+
restartsTotal: this.restartsTotal,
|
|
168
|
+
lastAdvanceAt: this.lastAdvanceAt,
|
|
169
|
+
});
|
|
170
|
+
throw e;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Kills the active child (if any) and moves to "stopped". No-op if idle/terminal. */
|
|
174
|
+
stop() {
|
|
175
|
+
if (this.state !== "running" && this.state !== "stalled")
|
|
176
|
+
return;
|
|
177
|
+
this.stopRequested = true;
|
|
178
|
+
const child = this.child;
|
|
179
|
+
this.child = null;
|
|
180
|
+
this.state = "stopped";
|
|
181
|
+
try {
|
|
182
|
+
child?.kill();
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
this.log(`[job-supervisor] kill failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
snapshot() {
|
|
189
|
+
return {
|
|
190
|
+
name: "reprocess",
|
|
191
|
+
state: this.state,
|
|
192
|
+
processed: this.processed,
|
|
193
|
+
total: this.total,
|
|
194
|
+
startedAt: this.startedAt,
|
|
195
|
+
lastAdvanceAt: this.lastAdvanceAt,
|
|
196
|
+
restarts: this.restarts,
|
|
197
|
+
restartsTotal: this.restartsTotal,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
spawnAndWire() {
|
|
201
|
+
const child = this.spawnChildFn(this.args);
|
|
202
|
+
this.child = child;
|
|
203
|
+
// Identity-guard every callback against `this.child`: a killed-but-not-yet-
|
|
204
|
+
// exited child (stop(), or a respawn racing a slow exit/line delivery) can
|
|
205
|
+
// still deliver onLine/onExit after this.child has moved on to a newer
|
|
206
|
+
// child (or null). Without this guard a stale callback from child A would
|
|
207
|
+
// mutate state for the live run, null out this.child (leaking A's
|
|
208
|
+
// now-unmanaged, unkillable successor if A had one), or trigger a bogus
|
|
209
|
+
// restart/respawn.
|
|
210
|
+
child.onLine((line) => {
|
|
211
|
+
if (this.child !== child)
|
|
212
|
+
return;
|
|
213
|
+
try {
|
|
214
|
+
this.handleLine(line);
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
this.log(`[job-supervisor] onLine handler error: ${e instanceof Error ? e.message : String(e)}`);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
child.onExit((code) => {
|
|
221
|
+
if (this.child !== child)
|
|
222
|
+
return;
|
|
223
|
+
try {
|
|
224
|
+
this.handleExit(code);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
this.log(`[job-supervisor] onExit handler error: ${e instanceof Error ? e.message : String(e)}`);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
handleLine(line) {
|
|
232
|
+
const match = PROGRESS_RE.exec(line);
|
|
233
|
+
if (!match)
|
|
234
|
+
return;
|
|
235
|
+
const processed = Number(match[1]);
|
|
236
|
+
const total = Number(match[2]);
|
|
237
|
+
if (!Number.isFinite(processed) || !Number.isFinite(total))
|
|
238
|
+
return;
|
|
239
|
+
this.processed = processed;
|
|
240
|
+
this.total = total;
|
|
241
|
+
this.lastAdvanceAt = new Date(this.clock.now()).toISOString();
|
|
242
|
+
this.restarts = 0;
|
|
243
|
+
if (this.state === "stalled")
|
|
244
|
+
this.state = "running";
|
|
245
|
+
this.stallEpisodeFired = false;
|
|
246
|
+
}
|
|
247
|
+
handleExit(code) {
|
|
248
|
+
this.child = null;
|
|
249
|
+
if (this.stopRequested)
|
|
250
|
+
return; // stop() already set state to "stopped"
|
|
251
|
+
const workRemaining = code !== 0 || (this.total > 0 && this.processed < this.total);
|
|
252
|
+
if (!workRemaining) {
|
|
253
|
+
this.state = "completed";
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// Checked BEFORE incrementing: `restarts` only counts respawns that
|
|
257
|
+
// actually happened, so exhaustion trips exactly when that many real
|
|
258
|
+
// respawns have already been spent — the emitted `restarts` on the
|
|
259
|
+
// "exhausted" event below is never one higher than the real count.
|
|
260
|
+
if (this.restarts >= this.maxRestartsWithoutProgress) {
|
|
261
|
+
this.state = "exhausted";
|
|
262
|
+
this.emitEvent({
|
|
263
|
+
kind: "exhausted",
|
|
264
|
+
job: "reprocess",
|
|
265
|
+
processed: this.processed,
|
|
266
|
+
total: this.total,
|
|
267
|
+
restarts: this.restarts,
|
|
268
|
+
restartsTotal: this.restartsTotal,
|
|
269
|
+
lastAdvanceAt: this.lastAdvanceAt,
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
this.state = "running";
|
|
274
|
+
this.stallEpisodeFired = false;
|
|
275
|
+
try {
|
|
276
|
+
this.spawnAndWire();
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
this.child = null;
|
|
280
|
+
this.state = "exhausted";
|
|
281
|
+
this.log(`[job-supervisor] respawn failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
282
|
+
this.emitEvent({
|
|
283
|
+
kind: "spawn_failed",
|
|
284
|
+
job: "reprocess",
|
|
285
|
+
processed: this.processed,
|
|
286
|
+
total: this.total,
|
|
287
|
+
restarts: this.restarts,
|
|
288
|
+
restartsTotal: this.restartsTotal,
|
|
289
|
+
lastAdvanceAt: this.lastAdvanceAt,
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
this.restarts += 1;
|
|
294
|
+
this.restartsTotal += 1;
|
|
295
|
+
}
|
|
296
|
+
checkStall() {
|
|
297
|
+
try {
|
|
298
|
+
if (this.state !== "running" && this.state !== "stalled")
|
|
299
|
+
return;
|
|
300
|
+
if (!this.child)
|
|
301
|
+
return;
|
|
302
|
+
if (this.stallEpisodeFired)
|
|
303
|
+
return;
|
|
304
|
+
const referenceIso = this.lastAdvanceAt ?? this.startedAt;
|
|
305
|
+
if (!referenceIso)
|
|
306
|
+
return;
|
|
307
|
+
const elapsed = this.clock.now() - Date.parse(referenceIso);
|
|
308
|
+
if (elapsed < this.stallMs)
|
|
309
|
+
return;
|
|
310
|
+
this.stallEpisodeFired = true;
|
|
311
|
+
this.state = "stalled";
|
|
312
|
+
this.emitEvent({
|
|
313
|
+
kind: "stalled",
|
|
314
|
+
job: "reprocess",
|
|
315
|
+
processed: this.processed,
|
|
316
|
+
total: this.total,
|
|
317
|
+
restarts: this.restarts,
|
|
318
|
+
restartsTotal: this.restartsTotal,
|
|
319
|
+
lastAdvanceAt: this.lastAdvanceAt,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
this.log(`[job-supervisor] stall check error: ${e instanceof Error ? e.message : String(e)}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
emitEvent(event) {
|
|
327
|
+
try {
|
|
328
|
+
this.onEventCb(event);
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
this.log(`[job-supervisor] onEvent handler threw: ${e instanceof Error ? e.message : String(e)}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=job-supervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-supervisor.js","sourceRoot":"","sources":["../../../src/core/jobs/job-supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type EnrollmentStatus = "active" | "completed";
|
|
2
|
+
export type AttemptResult = "correct" | "partial" | "incorrect";
|
|
3
|
+
export type AttemptHelp = "none" | "hint" | "worked-example";
|
|
4
|
+
export type AttemptKind = "application" | "retrieval" | "transfer";
|
|
5
|
+
export interface Learner {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly label: string;
|
|
8
|
+
readonly profile: Record<string, unknown>;
|
|
9
|
+
readonly createdAt: string;
|
|
10
|
+
readonly updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Attempt {
|
|
13
|
+
readonly concept: string;
|
|
14
|
+
readonly task: string;
|
|
15
|
+
readonly response: string;
|
|
16
|
+
readonly result: AttemptResult;
|
|
17
|
+
readonly help: AttemptHelp;
|
|
18
|
+
readonly kind: AttemptKind;
|
|
19
|
+
}
|
|
20
|
+
export interface LearningEvent {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly date: string;
|
|
23
|
+
readonly covered: ReadonlyArray<string>;
|
|
24
|
+
readonly attempts: ReadonlyArray<Attempt>;
|
|
25
|
+
readonly interpretation: string;
|
|
26
|
+
readonly nextStep: string;
|
|
27
|
+
readonly createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface Enrollment {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly learnerId: string;
|
|
32
|
+
readonly courseId: string;
|
|
33
|
+
readonly workstreamId: string | null;
|
|
34
|
+
readonly planRef: string;
|
|
35
|
+
readonly planRevision: number;
|
|
36
|
+
readonly planFingerprint: string;
|
|
37
|
+
readonly status: EnrollmentStatus;
|
|
38
|
+
readonly completionHistory: ReadonlyArray<unknown>;
|
|
39
|
+
readonly createdAt: string;
|
|
40
|
+
readonly updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function makeEnrollmentId(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Canonical JSON: object keys sorted recursively, arrays preserved. pg JSONB
|
|
45
|
+
* reorders object keys on write, so byte comparison of round-tripped JSON
|
|
46
|
+
* false-conflicts; canonical form is the only backend-agnostic equality.
|
|
47
|
+
*/
|
|
48
|
+
export declare function canonicalJson(value: unknown): string;
|
|
49
|
+
export interface StoredEventContent {
|
|
50
|
+
readonly date: string;
|
|
51
|
+
readonly covered: ReadonlyArray<string>;
|
|
52
|
+
readonly attempts: ReadonlyArray<Attempt>;
|
|
53
|
+
readonly interpretation: string;
|
|
54
|
+
readonly nextStep: string;
|
|
55
|
+
}
|
|
56
|
+
/** Idempotent-record comparison: same event if all content fields match. */
|
|
57
|
+
export declare function learningEventContentEqual(input: {
|
|
58
|
+
date: string;
|
|
59
|
+
covered: ReadonlyArray<string>;
|
|
60
|
+
attempts: ReadonlyArray<Attempt>;
|
|
61
|
+
interpretation: string;
|
|
62
|
+
nextStep: string;
|
|
63
|
+
}, stored: StoredEventContent): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/core/learner/model.ts
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
export function makeEnrollmentId() {
|
|
4
|
+
return `enr_${randomUUID()}`;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Canonical JSON: object keys sorted recursively, arrays preserved. pg JSONB
|
|
8
|
+
* reorders object keys on write, so byte comparison of round-tripped JSON
|
|
9
|
+
* false-conflicts; canonical form is the only backend-agnostic equality.
|
|
10
|
+
*/
|
|
11
|
+
export function canonicalJson(value) {
|
|
12
|
+
if (Array.isArray(value))
|
|
13
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
14
|
+
if (value !== null && typeof value === "object") {
|
|
15
|
+
const entries = Object.entries(value)
|
|
16
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
17
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`);
|
|
18
|
+
return `{${entries.join(",")}}`;
|
|
19
|
+
}
|
|
20
|
+
return JSON.stringify(value) ?? "null";
|
|
21
|
+
}
|
|
22
|
+
/** Idempotent-record comparison: same event if all content fields match. */
|
|
23
|
+
export function learningEventContentEqual(input, stored) {
|
|
24
|
+
return (input.date === stored.date &&
|
|
25
|
+
canonicalJson(input.covered) === canonicalJson(stored.covered) &&
|
|
26
|
+
canonicalJson(input.attempts) === canonicalJson(stored.attempts) &&
|
|
27
|
+
input.interpretation === stored.interpretation &&
|
|
28
|
+
input.nextStep === stored.nextStep);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/learner/model.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiDzC,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,UAAU,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAChD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;AACzC,CAAC;AAUD,4EAA4E;AAC5E,MAAM,UAAU,yBAAyB,CACvC,KAAmI,EACnI,MAA0B;IAE1B,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;QAC1B,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC;QAC9D,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC;QAChE,KAAK,CAAC,cAAc,KAAK,MAAM,CAAC,cAAc;QAC9C,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CACnC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learner service — orchestration over LearnerStore + WorkstreamStore.
|
|
3
|
+
*
|
|
4
|
+
* Owns validation (validate.ts), learner auto-init, workstream id-or-label
|
|
5
|
+
* resolution, and summary assembly. Idempotency, fingerprint matching, and
|
|
6
|
+
* tenant isolation stay in the store. The MCP layer (M3) calls these
|
|
7
|
+
* operations; unrecorded-session listing needs SessionStore and lands in M3.
|
|
8
|
+
*/
|
|
9
|
+
import type { LearnerStore, LearningEventInput, RecordResult } from "../../ports/learner-store.js";
|
|
10
|
+
import type { WorkstreamStore } from "../../ports/workstream-store.js";
|
|
11
|
+
import type { Enrollment, EnrollmentStatus } from "./model.js";
|
|
12
|
+
import { type ConceptSummary } from "./summary.js";
|
|
13
|
+
export interface LearnerServiceDeps {
|
|
14
|
+
readonly learners: LearnerStore;
|
|
15
|
+
readonly workstreams: Pick<WorkstreamStore, "getById" | "findByNormalizedLabel">;
|
|
16
|
+
}
|
|
17
|
+
export interface EnrollRequest {
|
|
18
|
+
readonly learnerId: string;
|
|
19
|
+
readonly learnerLabel?: string;
|
|
20
|
+
readonly courseId: string;
|
|
21
|
+
/** Workstream id or label; resolved like recall_workstream. */
|
|
22
|
+
readonly workstream: string;
|
|
23
|
+
readonly planRef: string;
|
|
24
|
+
readonly planRevision: number;
|
|
25
|
+
readonly planFingerprint: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CourseSummary {
|
|
28
|
+
readonly learnerId: string;
|
|
29
|
+
readonly courseId: string;
|
|
30
|
+
readonly status: EnrollmentStatus;
|
|
31
|
+
readonly workstreamId: string | null;
|
|
32
|
+
readonly planRevision: number;
|
|
33
|
+
readonly concepts: ReadonlyArray<ConceptSummary>;
|
|
34
|
+
/** Latest event's next step, or null when nothing is recorded yet. */
|
|
35
|
+
readonly nextStep: string | null;
|
|
36
|
+
readonly eventCount: number;
|
|
37
|
+
readonly lastEventDate: string | null;
|
|
38
|
+
}
|
|
39
|
+
export declare function enrollCourse(deps: LearnerServiceDeps, tenantId: string, req: EnrollRequest): Promise<Enrollment>;
|
|
40
|
+
export declare function recordLearningEvent(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string, event: LearningEventInput): Promise<RecordResult>;
|
|
41
|
+
export declare function completeLearnerCourse(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string): Promise<void>;
|
|
42
|
+
export declare function getCourseSummary(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string): Promise<CourseSummary | null>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learner service — orchestration over LearnerStore + WorkstreamStore.
|
|
3
|
+
*
|
|
4
|
+
* Owns validation (validate.ts), learner auto-init, workstream id-or-label
|
|
5
|
+
* resolution, and summary assembly. Idempotency, fingerprint matching, and
|
|
6
|
+
* tenant isolation stay in the store. The MCP layer (M3) calls these
|
|
7
|
+
* operations; unrecorded-session listing needs SessionStore and lands in M3.
|
|
8
|
+
*/
|
|
9
|
+
import { normalizeLabel } from "../workstream/model.js";
|
|
10
|
+
import { assertValidEnrollmentInput, assertValidEventInput, assertValidLearnerInit, assertValidSlug, } from "./validate.js";
|
|
11
|
+
import { summarizeConcepts } from "./summary.js";
|
|
12
|
+
async function resolveWorkstreamId(deps, tenantId, idOrLabel) {
|
|
13
|
+
const trimmed = idOrLabel.trim();
|
|
14
|
+
if (!trimmed)
|
|
15
|
+
throw new Error("learner service: workstream id or label is required");
|
|
16
|
+
const found = (await deps.workstreams.getById(tenantId, trimmed)) ??
|
|
17
|
+
(await deps.workstreams.findByNormalizedLabel(tenantId, normalizeLabel(trimmed)));
|
|
18
|
+
if (!found)
|
|
19
|
+
throw new Error(`learner service: no workstream matches "${trimmed}"`);
|
|
20
|
+
return found.id;
|
|
21
|
+
}
|
|
22
|
+
export async function enrollCourse(deps, tenantId, req) {
|
|
23
|
+
assertValidSlug(req.learnerId, "enrollment.learnerId");
|
|
24
|
+
assertValidSlug(req.courseId, "enrollment.courseId");
|
|
25
|
+
const workstreamId = await resolveWorkstreamId(deps, tenantId, req.workstream);
|
|
26
|
+
const existing = await deps.learners.getLearner(tenantId, req.learnerId);
|
|
27
|
+
if (!existing) {
|
|
28
|
+
const init = { id: req.learnerId, label: req.learnerLabel ?? req.learnerId };
|
|
29
|
+
assertValidLearnerInit(init);
|
|
30
|
+
await deps.learners.initLearner(tenantId, init);
|
|
31
|
+
}
|
|
32
|
+
const input = {
|
|
33
|
+
learnerId: req.learnerId,
|
|
34
|
+
courseId: req.courseId,
|
|
35
|
+
workstreamId,
|
|
36
|
+
planRef: req.planRef,
|
|
37
|
+
planRevision: req.planRevision,
|
|
38
|
+
planFingerprint: req.planFingerprint,
|
|
39
|
+
};
|
|
40
|
+
assertValidEnrollmentInput(input);
|
|
41
|
+
return deps.learners.enroll(tenantId, input);
|
|
42
|
+
}
|
|
43
|
+
export async function recordLearningEvent(deps, tenantId, learnerId, courseId, event) {
|
|
44
|
+
assertValidSlug(learnerId, "enrollment.learnerId");
|
|
45
|
+
assertValidSlug(courseId, "enrollment.courseId");
|
|
46
|
+
assertValidEventInput(event);
|
|
47
|
+
return deps.learners.recordEvent(tenantId, learnerId, courseId, event);
|
|
48
|
+
}
|
|
49
|
+
export async function completeLearnerCourse(deps, tenantId, learnerId, courseId) {
|
|
50
|
+
assertValidSlug(learnerId, "enrollment.learnerId");
|
|
51
|
+
assertValidSlug(courseId, "enrollment.courseId");
|
|
52
|
+
return deps.learners.completeCourse(tenantId, learnerId, courseId);
|
|
53
|
+
}
|
|
54
|
+
export async function getCourseSummary(deps, tenantId, learnerId, courseId) {
|
|
55
|
+
assertValidSlug(learnerId, "enrollment.learnerId");
|
|
56
|
+
assertValidSlug(courseId, "enrollment.courseId");
|
|
57
|
+
const enrollment = await deps.learners.getEnrollment(tenantId, learnerId, courseId);
|
|
58
|
+
if (!enrollment)
|
|
59
|
+
return null;
|
|
60
|
+
const events = await deps.learners.listEvents(tenantId, learnerId, courseId);
|
|
61
|
+
const last = events.length > 0 ? events[events.length - 1] : null;
|
|
62
|
+
return {
|
|
63
|
+
learnerId,
|
|
64
|
+
courseId,
|
|
65
|
+
status: enrollment.status,
|
|
66
|
+
workstreamId: enrollment.workstreamId,
|
|
67
|
+
planRevision: enrollment.planRevision,
|
|
68
|
+
concepts: summarizeConcepts(events),
|
|
69
|
+
nextStep: last ? last.nextStep : null,
|
|
70
|
+
eventCount: events.length,
|
|
71
|
+
lastEventDate: last ? last.date : null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/core/learner/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learner summary derivation — port of the Gnos deterministic summary rule
|
|
3
|
+
* (`skills/learner-tracking/references/evidence.md`, MIT, attribution kept).
|
|
4
|
+
*
|
|
5
|
+
* Per concept, from events in `listEvents` order ((date, id) ascending):
|
|
6
|
+
*
|
|
7
|
+
* - covered, never attempted: **exposed**
|
|
8
|
+
* - latest attempt assisted, partial, or incorrect: **practicing**
|
|
9
|
+
* - latest attempt correct + unassisted: **demonstrated**
|
|
10
|
+
* - latest success is retrieval/transfer on a strictly later date than an
|
|
11
|
+
* earlier unassisted success: **retained**
|
|
12
|
+
*
|
|
13
|
+
* Latest failure demotes back to practicing; earlier evidence is preserved in
|
|
14
|
+
* the events, not in the label. These are workflow labels, not calibrated
|
|
15
|
+
* scores — the summary never issues "mastered".
|
|
16
|
+
*/
|
|
17
|
+
import type { LearningEvent } from "./model.js";
|
|
18
|
+
export type ConceptStatus = "exposed" | "practicing" | "demonstrated" | "retained";
|
|
19
|
+
export interface ConceptSummary {
|
|
20
|
+
readonly concept: string;
|
|
21
|
+
readonly status: ConceptStatus;
|
|
22
|
+
readonly attemptCount: number;
|
|
23
|
+
/** Latest attempt date, else the date the concept was last covered. */
|
|
24
|
+
readonly latestDate: string | null;
|
|
25
|
+
/** Latest attempt's task, or null when never attempted. */
|
|
26
|
+
readonly latestTask: string | null;
|
|
27
|
+
}
|
|
28
|
+
export declare function summarizeConcepts(events: ReadonlyArray<LearningEvent>): ReadonlyArray<ConceptSummary>;
|