@modusensus/dsh-mneme 0.14.0-beta.1 → 0.14.0-beta.2
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/LICENSE +21 -21
- package/README.md +830 -830
- package/assets/icon.svg +3 -3
- package/bin/cli.mjs +964 -964
- package/bin/dsh-mneme-choro.mjs +262 -262
- package/bin/dsh-mneme-mcp.mjs +510 -510
- package/bin/dsh-mneme-serve.mjs +151 -151
- package/cordis.patch.yml +21 -21
- package/docs/CONFIGURATION.md +407 -404
- package/docs/CONTINUITY.md +139 -139
- package/docs/DAEMON.md +57 -57
- package/docs/DIRECTORIES.md +114 -114
- package/docs/DISTILL.md +397 -371
- package/docs/DSH-GO.md +97 -97
- package/docs/ENTITIES.md +247 -247
- package/docs/FORGET.md +77 -77
- package/docs/KB.md +110 -110
- package/docs/LOCAL_MODEL.md +222 -222
- package/docs/SEMANTIC.md +256 -256
- package/docs/SHARE.md +552 -552
- package/docs/SLEEP.md +202 -202
- package/docs/STORAGE.md +122 -122
- package/docs/WORKFLOWS.md +366 -366
- package/lib/api-standalone.js +1007 -1007
- package/lib/api.js +3600 -3600
- package/lib/attachment-text.js +122 -122
- package/lib/bootstrap-http.js +105 -105
- package/lib/bootstrap.js +215 -215
- package/lib/candidates.js +454 -454
- package/lib/capabilities.js +819 -819
- package/lib/choro-command.js +111 -111
- package/lib/client.js +14420 -14322
- package/lib/commands.js +95 -95
- package/lib/config-keys.js +144 -144
- package/lib/config.js +1361 -1344
- package/lib/content-hash.js +54 -54
- package/lib/continuity.js +258 -258
- package/lib/date-hint.js +32 -0
- package/lib/diary/auto.js +190 -190
- package/lib/diary/compose.js +440 -440
- package/lib/diary/dir.js +59 -59
- package/lib/diary/export.js +97 -97
- package/lib/diary/index.js +26 -26
- package/lib/diary/library.js +189 -189
- package/lib/diary/qq-inbox.js +142 -142
- package/lib/diary/rollover.js +61 -61
- package/lib/diary/session-provider.js +122 -122
- package/lib/document-index.js +94 -94
- package/lib/document.js +322 -322
- package/lib/dream/clustering.js +123 -123
- package/lib/dream/decisions.js +945 -945
- package/lib/dream/narratives.js +44 -44
- package/lib/dream/sleep.js +2027 -2027
- package/lib/dream/triage.js +139 -139
- package/lib/dream.js +1879 -1879
- package/lib/dsh-go/host.js +162 -162
- package/lib/dsh-go/panel.js +2091 -2091
- package/lib/dsh-go/text.js +342 -342
- package/lib/dsh-go/views/common.js +68 -68
- package/lib/dsh-go/views/entity.js +48 -48
- package/lib/dsh-go/views/memory.js +198 -198
- package/lib/dsh-go/views/root.js +30 -30
- package/lib/dsh-go/views/settings.js +398 -398
- package/lib/dsh-go/views/status.js +51 -51
- package/lib/embedding.js +342 -342
- package/lib/entities/extractor.js +285 -285
- package/lib/fence.js +24 -24
- package/lib/graph/anchoring.js +184 -184
- package/lib/guide.js +74 -74
- package/lib/heat.js +144 -144
- package/lib/hot-memory.js +119 -119
- package/lib/image-describe.js +158 -0
- package/lib/index.js +852 -852
- package/lib/inject.js +656 -651
- package/lib/kb/index.js +309 -309
- package/lib/kb/routes.js +219 -219
- package/lib/kb/tools.js +148 -148
- package/lib/lang.js +607 -595
- package/lib/local-embedder.js +358 -358
- package/lib/maintenance.js +162 -162
- package/lib/memory-source.js +9 -9
- package/lib/mirror.js +330 -330
- package/lib/organize.js +456 -456
- package/lib/peak-hours.js +172 -172
- package/lib/pipeline-activities.js +178 -178
- package/lib/pipeline-attribution.js +165 -165
- package/lib/pipeline-command.js +279 -279
- package/lib/pipeline-confirm.js +142 -142
- package/lib/pipeline-exec.js +174 -174
- package/lib/pipeline-nudge-auto.js +129 -129
- package/lib/pipeline-nudge.js +1623 -1623
- package/lib/pipeline-observations.js +181 -181
- package/lib/pipeline-observe.js +133 -133
- package/lib/pipeline-runner.js +361 -361
- package/lib/pipeline-runs.js +442 -442
- package/lib/pipeline.js +716 -716
- package/lib/placement.js +13 -13
- package/lib/pre-inject-gate.js +354 -354
- package/lib/profile-extract.js +164 -164
- package/lib/profile-facts.js +356 -356
- package/lib/provenance-http.js +251 -251
- package/lib/quality-filter.js +162 -162
- package/lib/recall-stats.js +190 -190
- package/lib/reclaim-http.js +150 -150
- package/lib/reranker.js +269 -269
- package/lib/runtime/adopt.js +176 -176
- package/lib/runtime/closure.js +172 -172
- package/lib/runtime/download.js +247 -247
- package/lib/runtime/layout.js +265 -265
- package/lib/runtime/loader.js +252 -252
- package/lib/runtime/model-fetch.js +361 -361
- package/lib/runtime/provision.js +237 -237
- package/lib/runtime/tarball.js +130 -130
- package/lib/runtime/verify.js +223 -223
- package/lib/safe-slice.js +18 -18
- package/lib/scope-state.js +277 -277
- package/lib/scope.js +187 -187
- package/lib/search/adaptive.js +60 -60
- package/lib/search/bm25.js +96 -96
- package/lib/semantic.js +572 -572
- package/lib/sensitive-scan.js +209 -209
- package/lib/serve.js +228 -228
- package/lib/service.js +3268 -3268
- package/lib/session-gate.js +91 -91
- package/lib/session-usage.js +350 -350
- package/lib/settings.js +827 -817
- package/lib/share-http.js +281 -281
- package/lib/share-import.js +234 -234
- package/lib/share.js +335 -335
- package/lib/store.js +5895 -5895
- package/lib/summarize.js +1388 -1343
- package/lib/tools.js +1370 -1356
- package/lib/user-settings.js +230 -230
- package/lib/vector-index.js +180 -180
- package/lib/version-check.js +122 -122
- package/lib/wiki/claims.js +267 -267
- package/lib/wiki/cli-hint.js +90 -90
- package/lib/wiki/inject.js +164 -164
- package/lib/wiki/partition.js +200 -200
- package/lib/wiki/plan.js +214 -214
- package/lib/wiki/queries.js +640 -640
- package/lib/wiki/render.js +261 -261
- package/lib/wiki/scan.js +371 -371
- package/lib/wiki/t0.js +500 -500
- package/lib/wiki/t1-jobs.js +232 -232
- package/lib/wiki/t1.js +609 -609
- package/lib/write-admission.js +404 -404
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +1 -1
- package/runtime-manifest.json +526 -526
- package/scripts/benchmark-embed.js +201 -201
- package/scripts/benchmark-recall.js +249 -249
- package/scripts/benchmark-rerank.js +166 -166
- package/scripts/build-runtime-manifest.mjs +224 -224
- package/scripts/changelog-prep.mjs +91 -91
- package/scripts/check-sync.js +44 -44
- package/scripts/e2e-dsh.js +220 -220
- package/scripts/mneme-runtime.mjs +277 -277
- package/scripts/publish-gate.mjs +197 -197
- package/scripts/release-docs.mjs +210 -210
- package/scripts/release-notes.mjs +164 -164
- package/scripts/stress-dsh.js +255 -255
- package/scripts/sync-lib.js +47 -47
- package/scripts/sync-test-badge.mjs +69 -69
- package/scripts/test-count-sync.mjs +48 -48
- package/src/api-standalone.js +1007 -1007
- package/src/api.js +3600 -3600
- package/src/attachment-text.js +122 -122
- package/src/bootstrap-http.js +105 -105
- package/src/bootstrap.js +215 -215
- package/src/candidates.js +454 -454
- package/src/capabilities.js +819 -819
- package/src/choro-command.js +111 -111
- package/src/commands.js +95 -95
- package/src/config-keys.js +144 -144
- package/src/config.js +1361 -1344
- package/src/content-hash.js +54 -54
- package/src/continuity.js +258 -258
- package/src/date-hint.js +32 -0
- package/src/diary/auto.js +190 -190
- package/src/diary/compose.js +440 -440
- package/src/diary/dir.js +59 -59
- package/src/diary/export.js +97 -97
- package/src/diary/index.js +26 -26
- package/src/diary/library.js +189 -189
- package/src/diary/qq-inbox.js +142 -142
- package/src/diary/rollover.js +61 -61
- package/src/diary/session-provider.js +122 -122
- package/src/document-index.js +94 -94
- package/src/document.js +322 -322
- package/src/dream/clustering.js +123 -123
- package/src/dream/decisions.js +945 -945
- package/src/dream/narratives.js +44 -44
- package/src/dream/sleep.js +2027 -2027
- package/src/dream/triage.js +139 -139
- package/src/dream.js +1879 -1879
- package/src/dsh-go/host.js +162 -162
- package/src/dsh-go/panel.js +2091 -2091
- package/src/dsh-go/text.js +342 -342
- package/src/dsh-go/views/common.js +68 -68
- package/src/dsh-go/views/entity.js +48 -48
- package/src/dsh-go/views/memory.js +198 -198
- package/src/dsh-go/views/root.js +30 -30
- package/src/dsh-go/views/settings.js +398 -398
- package/src/dsh-go/views/status.js +51 -51
- package/src/embedding.js +342 -342
- package/src/entities/extractor.js +285 -285
- package/src/fence.js +24 -24
- package/src/graph/anchoring.js +184 -184
- package/src/guide.js +74 -74
- package/src/heat.js +144 -144
- package/src/hot-memory.js +119 -119
- package/src/image-describe.js +158 -0
- package/src/index.js +852 -852
- package/src/inject.js +656 -651
- package/src/kb/index.js +309 -309
- package/src/kb/routes.js +219 -219
- package/src/kb/tools.js +148 -148
- package/src/lang.js +607 -595
- package/src/local-embedder.js +358 -358
- package/src/maintenance.js +162 -162
- package/src/memory-source.js +9 -9
- package/src/mirror.js +330 -330
- package/src/organize.js +456 -456
- package/src/peak-hours.js +172 -172
- package/src/pipeline-activities.js +178 -178
- package/src/pipeline-attribution.js +165 -165
- package/src/pipeline-command.js +279 -279
- package/src/pipeline-confirm.js +142 -142
- package/src/pipeline-exec.js +174 -174
- package/src/pipeline-nudge-auto.js +129 -129
- package/src/pipeline-nudge.js +1623 -1623
- package/src/pipeline-observations.js +181 -181
- package/src/pipeline-observe.js +133 -133
- package/src/pipeline-runner.js +361 -361
- package/src/pipeline-runs.js +442 -442
- package/src/pipeline.js +716 -716
- package/src/placement.js +13 -13
- package/src/pre-inject-gate.js +354 -354
- package/src/profile-extract.js +164 -164
- package/src/profile-facts.js +356 -356
- package/src/provenance-http.js +251 -251
- package/src/quality-filter.js +162 -162
- package/src/recall-stats.js +190 -190
- package/src/reclaim-http.js +150 -150
- package/src/reranker.js +269 -269
- package/src/runtime/adopt.js +176 -176
- package/src/runtime/closure.js +172 -172
- package/src/runtime/download.js +247 -247
- package/src/runtime/layout.js +265 -265
- package/src/runtime/loader.js +252 -252
- package/src/runtime/model-fetch.js +361 -361
- package/src/runtime/provision.js +237 -237
- package/src/runtime/tarball.js +130 -130
- package/src/runtime/verify.js +223 -223
- package/src/safe-slice.js +18 -18
- package/src/scope-state.js +277 -277
- package/src/scope.js +187 -187
- package/src/search/adaptive.js +60 -60
- package/src/search/bm25.js +96 -96
- package/src/semantic.js +572 -572
- package/src/sensitive-scan.js +209 -209
- package/src/serve.js +228 -228
- package/src/service.js +3268 -3268
- package/src/session-gate.js +91 -91
- package/src/session-usage.js +350 -350
- package/src/settings.js +827 -817
- package/src/share-http.js +281 -281
- package/src/share-import.js +234 -234
- package/src/share.js +335 -335
- package/src/store.js +5895 -5895
- package/src/summarize.js +1388 -1343
- package/src/tools.js +1370 -1356
- package/src/user-settings.js +230 -230
- package/src/vector-index.js +180 -180
- package/src/version-check.js +122 -122
- package/src/wiki/claims.js +267 -267
- package/src/wiki/cli-hint.js +90 -90
- package/src/wiki/inject.js +164 -164
- package/src/wiki/partition.js +200 -200
- package/src/wiki/plan.js +214 -214
- package/src/wiki/queries.js +640 -640
- package/src/wiki/render.js +261 -261
- package/src/wiki/scan.js +371 -371
- package/src/wiki/t0.js +500 -500
- package/src/wiki/t1-jobs.js +232 -232
- package/src/wiki/t1.js +609 -609
- package/src/write-admission.js +404 -404
- package/test/adaptive-inject-budget.test.js +85 -85
- package/test/api-hardening.test.js +173 -173
- package/test/api-nudge-route.test.js +259 -259
- package/test/api-routes-gaps.test.js +1092 -1092
- package/test/api.test.js +3007 -2998
- package/test/attachment-text.test.js +139 -139
- package/test/audit.test.js +448 -448
- package/test/baseline-persist.test.js +248 -248
- package/test/benchmark.test.js +94 -94
- package/test/bm25-corpus-cache.test.js +145 -145
- package/test/bootstrap.test.js +228 -228
- package/test/candidates.test.js +345 -345
- package/test/capabilities.test.js +577 -577
- package/test/capacity-warning.test.js +262 -262
- package/test/changelog-prep.test.js +125 -125
- package/test/choro-command.test.js +218 -218
- package/test/cli-enums.test.js +37 -37
- package/test/cli-flag-value.test.js +86 -86
- package/test/cli-receive.test.js +390 -390
- package/test/cli-share.test.js +437 -437
- package/test/client-share.test.js +225 -225
- package/test/client.test.js +4595 -4425
- package/test/clustering.test.js +100 -100
- package/test/commands.test.js +106 -106
- package/test/config-keys-routes.test.js +233 -233
- package/test/config.test.js +153 -153
- package/test/conflict-freeze.test.js +344 -344
- package/test/conflict-ledger-enforce.test.js +452 -452
- package/test/conflict-ledger.test.js +242 -242
- package/test/conflict-reviewed.test.js +79 -79
- package/test/content-hash.test.js +204 -204
- package/test/continuity.test.js +452 -452
- package/test/contrast.test.js +432 -432
- package/test/date-hint.test.js +156 -0
- package/test/dedupe-candidate-window.test.js +180 -180
- package/test/dependency-overrides.test.js +78 -78
- package/test/diary-library.test.js +187 -187
- package/test/diary.test.js +576 -576
- package/test/differentiate-feedback.test.js +248 -248
- package/test/distill-tx-embed-gap.test.js +110 -110
- package/test/docs-config-keys.test.js +65 -65
- package/test/document-dir.test.js +233 -233
- package/test/document.test.js +703 -703
- package/test/dream-archive-guardrail.test.js +116 -116
- package/test/dream-baseline-clamp.test.js +174 -174
- package/test/dream-candidate-hybrid.test.js +281 -281
- package/test/dream-coverage-degrade.test.js +86 -86
- package/test/dream-decision-evidence.test.js +314 -314
- package/test/dream-failure-backoff.test.js +173 -173
- package/test/dream-fence.test.js +207 -207
- package/test/dream-maxtokens-fallback.test.js +132 -132
- package/test/dream-merge-guardrail.test.js +219 -219
- package/test/dream-peak-hours.test.js +194 -194
- package/test/dream.test.js +1528 -1528
- package/test/dsh-go-host.test.js +134 -134
- package/test/dsh-go-panel-actions.test.js +854 -854
- package/test/dsh-go-panel.test.js +251 -251
- package/test/embed-ready-poll.test.js +94 -94
- package/test/embed-text-reasons.test.js +112 -112
- package/test/embedding-cache-f32.test.js +276 -276
- package/test/embedding-ready.test.js +226 -226
- package/test/entities.test.js +711 -711
- package/test/entity-recall.test.js +179 -179
- package/test/epistemic.test.js +325 -325
- package/test/failure-query.test.js +147 -147
- package/test/failure-retention.test.js +105 -105
- package/test/fnew-0112.test.js +311 -311
- package/test/fnew-03.test.js +422 -422
- package/test/forget-corrections.test.js +70 -70
- package/test/graph-anchoring.test.js +184 -184
- package/test/graph-api.test.js +333 -333
- package/test/graph-boosted-relations.test.js +189 -189
- package/test/graph-inject-budget.test.js +103 -103
- package/test/graph-multipath.test.js +269 -269
- package/test/graph-passive-confirm.test.js +91 -91
- package/test/graph-weight-evolution.test.js +104 -104
- package/test/heat.test.js +175 -175
- package/test/helpers/dream-mock.js +86 -86
- package/test/helpers/peer-worker.mjs +25 -25
- package/test/helpers/runtime-source.js +60 -60
- package/test/helpers/tar-builder.js +80 -80
- package/test/helpers/write-admission-samples.js +312 -312
- package/test/hot-memory.test.js +259 -259
- package/test/image-description.test.js +271 -0
- package/test/image-memory.test.js +170 -170
- package/test/inject-content-cap.test.js +91 -91
- package/test/inject-first-round-bm25.test.js +89 -89
- package/test/inject-heat.test.js +77 -77
- package/test/inject-parent-gate.test.js +134 -109
- package/test/inject-pin-explicit.test.js +300 -300
- package/test/inject-pin-vector-crowd.test.js +225 -225
- package/test/inject-pools.test.js +195 -195
- package/test/inject-recall.test.js +172 -172
- package/test/inject-rotation.test.js +161 -161
- package/test/inject.test.js +553 -553
- package/test/issue-617-step1-query-and-fusion.test.js +361 -361
- package/test/kb-api-wiring.test.js +128 -128
- package/test/kb-collections.test.js +272 -272
- package/test/kb-routes.test.js +379 -379
- package/test/lang.test.js +98 -98
- package/test/lastrunat-persist.test.js +101 -101
- package/test/lib-smoke.test.js +116 -116
- package/test/llm-audit.test.js +815 -815
- package/test/llm-message-source.test.js +207 -207
- package/test/local-embedder.test.js +292 -292
- package/test/maintenance.test.js +501 -501
- package/test/mcp-stdio.test.js +385 -385
- package/test/mint-source.test.js +311 -311
- package/test/mirror-dirty.test.js +424 -424
- package/test/mirror-edit-digest.test.js +187 -187
- package/test/mirror-generation.test.js +499 -499
- package/test/mirror-types.test.js +208 -208
- package/test/mirror.test.js +249 -249
- package/test/narrative-bars.test.js +162 -162
- package/test/organize-tool.test.js +232 -232
- package/test/organize.test.js +418 -418
- package/test/panel-chart-scale.test.js +90 -90
- package/test/panel-tokens.test.js +245 -245
- package/test/peak-hours-observability.test.js +70 -70
- package/test/peer-blockers.test.js +193 -193
- package/test/persona-addressing.test.js +83 -83
- package/test/persona-api.test.js +172 -172
- package/test/persona-by-session.test.js +568 -568
- package/test/persona-flag.test.js +36 -36
- package/test/persona-inject.test.js +145 -145
- package/test/persona-settings.test.js +198 -198
- package/test/pipeline-activities.test.js +155 -155
- package/test/pipeline-attribution.test.js +175 -175
- package/test/pipeline-command.test.js +243 -243
- package/test/pipeline-confirm.test.js +148 -148
- package/test/pipeline-exec.test.js +172 -172
- package/test/pipeline-nudge-auto.test.js +248 -248
- package/test/pipeline-nudge.test.js +1278 -1278
- package/test/pipeline-observations.test.js +157 -157
- package/test/pipeline-observe.test.js +127 -127
- package/test/pipeline-r1r2.test.js +278 -278
- package/test/pipeline-runner.test.js +468 -468
- package/test/pipeline-runs.test.js +767 -767
- package/test/pipeline.test.js +636 -636
- package/test/placement.test.js +36 -36
- package/test/policy-epoch.test.js +259 -259
- package/test/pre-inject-gate.test.js +444 -444
- package/test/profile-extract.test.js +269 -269
- package/test/profile-facts.test.js +492 -492
- package/test/prompt-stance.test.js +128 -128
- package/test/provenance-http.test.js +367 -367
- package/test/publish-gate.test.js +272 -272
- package/test/quality-backfill.test.js +290 -290
- package/test/quality-filter.test.js +295 -295
- package/test/readme-claims.test.js +113 -113
- package/test/reasoning-effort.test.js +752 -752
- package/test/recall-anti-update.test.js +82 -82
- package/test/recall-evals.test.js +253 -253
- package/test/recall-layer.test.js +326 -326
- package/test/recall-retention.test.js +128 -128
- package/test/recall-stats.test.js +274 -274
- package/test/receipt-chain.test.js +451 -451
- package/test/reclaim-http.test.js +209 -209
- package/test/reflection-update-gate.test.js +129 -129
- package/test/reflection.test.js +226 -226
- package/test/reindex-backfill.test.js +475 -475
- package/test/reindex-scheduler.test.js +369 -369
- package/test/release-docs.test.js +156 -156
- package/test/release-notes.test.js +265 -265
- package/test/release-workflow.test.js +93 -93
- package/test/reranker-single-logit.test.js +73 -73
- package/test/reranker.test.js +197 -197
- package/test/resident-status.test.js +83 -83
- package/test/route-policy.test.js +113 -113
- package/test/runtime-adopt.test.js +206 -206
- package/test/runtime-cli.test.js +89 -89
- package/test/runtime-closure.test.js +193 -193
- package/test/runtime-download.test.js +302 -302
- package/test/runtime-layout.test.js +163 -163
- package/test/runtime-loader.test.js +255 -255
- package/test/runtime-manifest.test.js +159 -159
- package/test/runtime-model-fetch.test.js +348 -348
- package/test/runtime-provision.test.js +101 -101
- package/test/runtime-tarball.test.js +81 -81
- package/test/runtime-verify.test.js +319 -319
- package/test/scope-candidates.test.js +491 -491
- package/test/scope-distill-stamp.test.js +134 -134
- package/test/scope-explicit.test.js +452 -452
- package/test/scope-inject-soft.test.js +106 -106
- package/test/scope-optional-service.test.js +110 -110
- package/test/scope-retrieval.test.js +197 -197
- package/test/scope-state.test.js +610 -610
- package/test/scope-storage.test.js +255 -255
- package/test/scope-strict.test.js +367 -367
- package/test/search-fusion.test.js +90 -90
- package/test/security-doc.test.js +188 -188
- package/test/semantic.test.js +124 -124
- package/test/sensitive-scan.test.js +251 -251
- package/test/serve-bin.test.js +121 -121
- package/test/serve.test.js +258 -258
- package/test/service-search.test.js +174 -174
- package/test/service.test.js +410 -410
- package/test/session-gate.test.js +264 -264
- package/test/session-usage.test.js +317 -317
- package/test/settings.test.js +273 -273
- package/test/share-foreign-rows.test.js +144 -144
- package/test/share-http.test.js +144 -144
- package/test/share-import.test.js +577 -577
- package/test/share-imported-guard.test.js +311 -311
- package/test/share-imported-identity.test.js +111 -111
- package/test/share-pack.test.js +511 -511
- package/test/share-receive-http.test.js +756 -756
- package/test/share-routes.test.js +337 -337
- package/test/share-store.test.js +133 -133
- package/test/share-validate.test.js +101 -101
- package/test/skip-invalid-restore.test.js +262 -262
- package/test/sleep-action-set.test.js +441 -441
- package/test/sleep-catchup.test.js +315 -315
- package/test/sleep-failure-backoff.test.js +156 -156
- package/test/sleep-heat.test.js +174 -174
- package/test/sleep-scheduler-rearm.test.js +89 -89
- package/test/sleep.test.js +1455 -1455
- package/test/standalone-api.test.js +1344 -1344
- package/test/store-vector-cache.test.js +98 -98
- package/test/store-write-path-columns.test.js +191 -191
- package/test/store.test.js +744 -744
- package/test/stress.test.js +209 -209
- package/test/summarize-occurred-at.test.js +127 -127
- package/test/summarize-parse-diagnostics.test.js +75 -75
- package/test/summarize-reasoning-effort.test.js +189 -189
- package/test/summarize.test.js +2251 -2251
- package/test/supersede-state-tracking.test.js +394 -394
- package/test/test-count-sync.test.js +71 -71
- package/test/tool-state-ignore.test.js +65 -65
- package/test/tools-dto-schema-matrix.test.js +398 -398
- package/test/tools-memory-get-schema.test.js +65 -65
- package/test/tools.test.js +585 -585
- package/test/updated-at-semantics.test.js +113 -113
- package/test/user-settings-preview.test.js +127 -127
- package/test/user-settings-segments.test.js +527 -527
- package/test/vector-index.test.js +334 -334
- package/test/version-check.test.js +152 -152
- package/test/webServer-optional.test.js +82 -82
- package/test/wiki-api.test.js +734 -734
- package/test/wiki-claims.test.js +194 -194
- package/test/wiki-cli-hint.test.js +118 -118
- package/test/wiki-inject.test.js +265 -265
- package/test/wiki-partition.test.js +112 -112
- package/test/wiki-plan.test.js +80 -80
- package/test/wiki-provenance.test.js +202 -202
- package/test/wiki-queries.test.js +398 -398
- package/test/wiki-scan.test.js +208 -208
- package/test/wiki-store.test.js +190 -190
- package/test/wiki-t0.test.js +456 -456
- package/test/wiki-t1-jobs.test.js +125 -125
- package/test/wiki-t1.test.js +460 -460
- package/test/wiki-tools.test.js +176 -176
- package/test/workflow-guards.test.js +155 -155
- package/test/write-admission.test.js +668 -668
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { createStore } from "../src/store.js";
|
|
4
|
-
import { createService } from "../src/service.js";
|
|
5
|
-
import { createDreamScheduler, validateDecisions } from "../src/dream.js";
|
|
6
|
-
import { mockCtx } from "./helpers/dream-mock.js";
|
|
7
|
-
|
|
8
|
-
// 回归(issue #104 方向 2):archive 决策语义越界 + 批量大扫除。
|
|
9
|
-
// 实测事故:一轮 12 条互不相关主题被批量 archive,LLM 自述理由是「价值判断」
|
|
10
|
-
// (对核心知识库长期参考价值较低、保持整洁和高信噪比),而提示词对 archive 的
|
|
11
|
-
// 定义是「重复或过时」;12 条中 8 条疑似误伤,含 rejected_solution ×2、
|
|
12
|
-
// constraint ×3、pitfall ×1 等长保留类型——这些类型的存在意义即长期保存,
|
|
13
|
-
// 误归档损失最大。修复:
|
|
14
|
-
// 1. 类型护栏——长保留类型(preference / pattern / rejected_solution /
|
|
15
|
-
// constraint / pitfall)的 archive 必须命中「重复/过时」类理由,否则单条
|
|
16
|
-
// 跳过记 skipped(run 记 degraded),绝不整单拒绝;
|
|
17
|
-
// 2. 批量上限——单轮 archive 条数封顶(dreamMaxArchivePerRun,默认 8),
|
|
18
|
-
// 与 update 上限同类的全局闸门:skipInvalid 也不豁免,超限整单拒绝;
|
|
19
|
-
// 3. 提示词同步强化(辅助防线)。
|
|
20
|
-
// conflict 裁决的败者归档不经过 archive action,不受护栏影响。
|
|
21
|
-
|
|
22
|
-
function snap(entries) {
|
|
23
|
-
return new Map(entries.map(([id, type]) => [id, { id, type, title: `t-${id}`, content: `c-${id}`, importance: 3, archived: false, forgotten: false }]));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------- 类型护栏(纯函数)
|
|
27
|
-
|
|
28
|
-
test("#104-2: long-retention archive with a duplicate/outdated rationale passes", () => {
|
|
29
|
-
const s = snap([["p1", "preference"], ["rs1", "rejected_solution"]]);
|
|
30
|
-
const decisions = [
|
|
31
|
-
{ action: "archive", ids: ["p1"], reason: "与后续偏好重复,已被新条目取代" },
|
|
32
|
-
{ action: "archive", ids: ["rs1"], reason: "duplicate of an earlier rejected approach (stale)" }
|
|
33
|
-
];
|
|
34
|
-
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
35
|
-
assert.equal(ok, true);
|
|
36
|
-
assert.deepEqual(skipped, [], "duplicate/outdated rationales are accepted");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("#104-2: long-retention archive with a value-judgment rationale is skipped (run degrades)", () => {
|
|
40
|
-
const s = snap([["c1", "constraint"], ["k1", "project"]]);
|
|
41
|
-
const decisions = [
|
|
42
|
-
{ action: "archive", ids: ["c1"], reason: "多为一次性调试细节,对核心知识库长期参考价值较低,保持整洁和高信噪比" },
|
|
43
|
-
{ action: "archive", ids: ["k1"], reason: "价值较低" }
|
|
44
|
-
];
|
|
45
|
-
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
46
|
-
assert.equal(ok, true, "guard skips the single decision instead of rejecting the run");
|
|
47
|
-
assert.equal(skipped.length, 1, "only the guarded long-retention archive is skipped");
|
|
48
|
-
assert.match(skipped[0].error, /long-retention type\(s\) constraint/);
|
|
49
|
-
assert.match(skipped[0].error, /duplicate\/outdated rationale/);
|
|
50
|
-
// 幸存列表:普通类型照常归档,c1 被跳过后由隐式 keep 补位
|
|
51
|
-
assert.deepEqual(decisions.map((d) => d.action), ["archive", "keep"]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("#104-2: plain-type archive needs no rationale keywords (guard boundary)", () => {
|
|
55
|
-
const s = snap([["d1", "decision"], ["f1", "factor"]]);
|
|
56
|
-
const decisions = [
|
|
57
|
-
{ action: "archive", ids: ["d1"], reason: "阶段性结论,已沉淀进项目记忆" },
|
|
58
|
-
{ action: "archive", ids: ["f1"], reason: "清理" }
|
|
59
|
-
];
|
|
60
|
-
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
61
|
-
assert.equal(ok, true);
|
|
62
|
-
assert.deepEqual(skipped, [], "guard applies to long-retention types only");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("#104-2: conflict-loser archive is exempt (adjudication, not archive action)", () => {
|
|
66
|
-
const s = snap([["w", "preference"], ["l", "preference"]]);
|
|
67
|
-
const decisions = [{ action: "conflict", winner: "w", loser: "l", reason: "内容矛盾,保留更新的信息" }];
|
|
68
|
-
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
69
|
-
assert.equal(ok, true);
|
|
70
|
-
assert.deepEqual(skipped, [], "conflict adjudication does not go through the archive guard");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// ---------------------------------------------------------- 批量上限(纯函数)
|
|
74
|
-
|
|
75
|
-
test("#104-2: archive count above the cap rejects the whole batch (global gate)", () => {
|
|
76
|
-
const entries = Array.from({ length: 10 }, (_, i) => [`d${i}`, "decision"]);
|
|
77
|
-
const s = snap(entries);
|
|
78
|
-
const decisions = entries.map(([id]) => ({ action: "archive", ids: [id], reason: "stale" }));
|
|
79
|
-
const { ok, errors } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
80
|
-
assert.equal(ok, false, "9 archives > default cap 8 → reject even under skipInvalid");
|
|
81
|
-
assert.ok(errors.some((e) => e.includes("too many archive decisions")), `cap error present, got: ${errors.join("; ")}`);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("#104-2: archive cap is configurable", () => {
|
|
85
|
-
const entries = Array.from({ length: 10 }, (_, i) => [`d${i}`, "decision"]);
|
|
86
|
-
const s = snap(entries);
|
|
87
|
-
const decisions = entries.map(([id]) => ({ action: "archive", ids: [id], reason: "stale" }));
|
|
88
|
-
const { ok, errors } = validateDecisions(decisions, s, { skipInvalid: true, maxArchivePerRun: 20 });
|
|
89
|
-
assert.equal(ok, true, `raised cap lets the sweep through, got: ${errors.join("; ")}`);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// ---------------------------------------------------------- 端到端(runDream)
|
|
93
|
-
|
|
94
|
-
test("#104-2: runDream skips a value-judgment archive of a long-retention type and lands the rest", async () => {
|
|
95
|
-
const store = createStore(":memory:");
|
|
96
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
97
|
-
service.saveWithDedupe({ type: "project", title: "旧项目", content: "已完结并被取代", importance: 3 });
|
|
98
|
-
service.saveWithDedupe({ type: "constraint", title: "接口约束", content: "v1 接口只能串行调用", importance: 3 });
|
|
99
|
-
const ctx = mockCtx({
|
|
100
|
-
onConsolidation: (listText) => {
|
|
101
|
-
const entries = [...listText.matchAll(/id=([^\s|]+)\s*\|\s*type=(\w+)/g)].map((m) => ({ id: m[1], type: m[2] }));
|
|
102
|
-
const projectId = entries.find((e) => e.type === "project").id;
|
|
103
|
-
const constraintId = entries.find((e) => e.type === "constraint").id;
|
|
104
|
-
return JSON.stringify([
|
|
105
|
-
{ action: "archive", ids: [projectId], reason: "重复或过时" },
|
|
106
|
-
{ action: "archive", ids: [constraintId], reason: "保持知识库整洁,参考价值较低" }
|
|
107
|
-
]);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
const dream = createDreamScheduler({ thresholdCount: 1, thresholdChars: 0, delayMs: 0 });
|
|
111
|
-
const result = await dream.runDream(ctx, service, { dreamProvider: "deepseek", dreamModel: "deepseek-chat" });
|
|
112
|
-
assert.notEqual(result.status, "failed", "guard skips instead of failing the run");
|
|
113
|
-
const constraint = service.all().find((m) => m.type === "constraint");
|
|
114
|
-
assert.equal(constraint.archived, false, "value-judgment archive of a long-retention type must not land");
|
|
115
|
-
store.close();
|
|
116
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { createStore } from "../src/store.js";
|
|
4
|
+
import { createService } from "../src/service.js";
|
|
5
|
+
import { createDreamScheduler, validateDecisions } from "../src/dream.js";
|
|
6
|
+
import { mockCtx } from "./helpers/dream-mock.js";
|
|
7
|
+
|
|
8
|
+
// 回归(issue #104 方向 2):archive 决策语义越界 + 批量大扫除。
|
|
9
|
+
// 实测事故:一轮 12 条互不相关主题被批量 archive,LLM 自述理由是「价值判断」
|
|
10
|
+
// (对核心知识库长期参考价值较低、保持整洁和高信噪比),而提示词对 archive 的
|
|
11
|
+
// 定义是「重复或过时」;12 条中 8 条疑似误伤,含 rejected_solution ×2、
|
|
12
|
+
// constraint ×3、pitfall ×1 等长保留类型——这些类型的存在意义即长期保存,
|
|
13
|
+
// 误归档损失最大。修复:
|
|
14
|
+
// 1. 类型护栏——长保留类型(preference / pattern / rejected_solution /
|
|
15
|
+
// constraint / pitfall)的 archive 必须命中「重复/过时」类理由,否则单条
|
|
16
|
+
// 跳过记 skipped(run 记 degraded),绝不整单拒绝;
|
|
17
|
+
// 2. 批量上限——单轮 archive 条数封顶(dreamMaxArchivePerRun,默认 8),
|
|
18
|
+
// 与 update 上限同类的全局闸门:skipInvalid 也不豁免,超限整单拒绝;
|
|
19
|
+
// 3. 提示词同步强化(辅助防线)。
|
|
20
|
+
// conflict 裁决的败者归档不经过 archive action,不受护栏影响。
|
|
21
|
+
|
|
22
|
+
function snap(entries) {
|
|
23
|
+
return new Map(entries.map(([id, type]) => [id, { id, type, title: `t-${id}`, content: `c-${id}`, importance: 3, archived: false, forgotten: false }]));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------- 类型护栏(纯函数)
|
|
27
|
+
|
|
28
|
+
test("#104-2: long-retention archive with a duplicate/outdated rationale passes", () => {
|
|
29
|
+
const s = snap([["p1", "preference"], ["rs1", "rejected_solution"]]);
|
|
30
|
+
const decisions = [
|
|
31
|
+
{ action: "archive", ids: ["p1"], reason: "与后续偏好重复,已被新条目取代" },
|
|
32
|
+
{ action: "archive", ids: ["rs1"], reason: "duplicate of an earlier rejected approach (stale)" }
|
|
33
|
+
];
|
|
34
|
+
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
35
|
+
assert.equal(ok, true);
|
|
36
|
+
assert.deepEqual(skipped, [], "duplicate/outdated rationales are accepted");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("#104-2: long-retention archive with a value-judgment rationale is skipped (run degrades)", () => {
|
|
40
|
+
const s = snap([["c1", "constraint"], ["k1", "project"]]);
|
|
41
|
+
const decisions = [
|
|
42
|
+
{ action: "archive", ids: ["c1"], reason: "多为一次性调试细节,对核心知识库长期参考价值较低,保持整洁和高信噪比" },
|
|
43
|
+
{ action: "archive", ids: ["k1"], reason: "价值较低" }
|
|
44
|
+
];
|
|
45
|
+
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
46
|
+
assert.equal(ok, true, "guard skips the single decision instead of rejecting the run");
|
|
47
|
+
assert.equal(skipped.length, 1, "only the guarded long-retention archive is skipped");
|
|
48
|
+
assert.match(skipped[0].error, /long-retention type\(s\) constraint/);
|
|
49
|
+
assert.match(skipped[0].error, /duplicate\/outdated rationale/);
|
|
50
|
+
// 幸存列表:普通类型照常归档,c1 被跳过后由隐式 keep 补位
|
|
51
|
+
assert.deepEqual(decisions.map((d) => d.action), ["archive", "keep"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("#104-2: plain-type archive needs no rationale keywords (guard boundary)", () => {
|
|
55
|
+
const s = snap([["d1", "decision"], ["f1", "factor"]]);
|
|
56
|
+
const decisions = [
|
|
57
|
+
{ action: "archive", ids: ["d1"], reason: "阶段性结论,已沉淀进项目记忆" },
|
|
58
|
+
{ action: "archive", ids: ["f1"], reason: "清理" }
|
|
59
|
+
];
|
|
60
|
+
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
61
|
+
assert.equal(ok, true);
|
|
62
|
+
assert.deepEqual(skipped, [], "guard applies to long-retention types only");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("#104-2: conflict-loser archive is exempt (adjudication, not archive action)", () => {
|
|
66
|
+
const s = snap([["w", "preference"], ["l", "preference"]]);
|
|
67
|
+
const decisions = [{ action: "conflict", winner: "w", loser: "l", reason: "内容矛盾,保留更新的信息" }];
|
|
68
|
+
const { ok, skipped } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
69
|
+
assert.equal(ok, true);
|
|
70
|
+
assert.deepEqual(skipped, [], "conflict adjudication does not go through the archive guard");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------- 批量上限(纯函数)
|
|
74
|
+
|
|
75
|
+
test("#104-2: archive count above the cap rejects the whole batch (global gate)", () => {
|
|
76
|
+
const entries = Array.from({ length: 10 }, (_, i) => [`d${i}`, "decision"]);
|
|
77
|
+
const s = snap(entries);
|
|
78
|
+
const decisions = entries.map(([id]) => ({ action: "archive", ids: [id], reason: "stale" }));
|
|
79
|
+
const { ok, errors } = validateDecisions(decisions, s, { skipInvalid: true });
|
|
80
|
+
assert.equal(ok, false, "9 archives > default cap 8 → reject even under skipInvalid");
|
|
81
|
+
assert.ok(errors.some((e) => e.includes("too many archive decisions")), `cap error present, got: ${errors.join("; ")}`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("#104-2: archive cap is configurable", () => {
|
|
85
|
+
const entries = Array.from({ length: 10 }, (_, i) => [`d${i}`, "decision"]);
|
|
86
|
+
const s = snap(entries);
|
|
87
|
+
const decisions = entries.map(([id]) => ({ action: "archive", ids: [id], reason: "stale" }));
|
|
88
|
+
const { ok, errors } = validateDecisions(decisions, s, { skipInvalid: true, maxArchivePerRun: 20 });
|
|
89
|
+
assert.equal(ok, true, `raised cap lets the sweep through, got: ${errors.join("; ")}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------- 端到端(runDream)
|
|
93
|
+
|
|
94
|
+
test("#104-2: runDream skips a value-judgment archive of a long-retention type and lands the rest", async () => {
|
|
95
|
+
const store = createStore(":memory:");
|
|
96
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
97
|
+
service.saveWithDedupe({ type: "project", title: "旧项目", content: "已完结并被取代", importance: 3 });
|
|
98
|
+
service.saveWithDedupe({ type: "constraint", title: "接口约束", content: "v1 接口只能串行调用", importance: 3 });
|
|
99
|
+
const ctx = mockCtx({
|
|
100
|
+
onConsolidation: (listText) => {
|
|
101
|
+
const entries = [...listText.matchAll(/id=([^\s|]+)\s*\|\s*type=(\w+)/g)].map((m) => ({ id: m[1], type: m[2] }));
|
|
102
|
+
const projectId = entries.find((e) => e.type === "project").id;
|
|
103
|
+
const constraintId = entries.find((e) => e.type === "constraint").id;
|
|
104
|
+
return JSON.stringify([
|
|
105
|
+
{ action: "archive", ids: [projectId], reason: "重复或过时" },
|
|
106
|
+
{ action: "archive", ids: [constraintId], reason: "保持知识库整洁,参考价值较低" }
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
const dream = createDreamScheduler({ thresholdCount: 1, thresholdChars: 0, delayMs: 0 });
|
|
111
|
+
const result = await dream.runDream(ctx, service, { dreamProvider: "deepseek", dreamModel: "deepseek-chat" });
|
|
112
|
+
assert.notEqual(result.status, "failed", "guard skips instead of failing the run");
|
|
113
|
+
const constraint = service.all().find((m) => m.type === "constraint");
|
|
114
|
+
assert.equal(constraint.archived, false, "value-judgment archive of a long-retention type must not land");
|
|
115
|
+
store.close();
|
|
116
|
+
});
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { createStore } from "../src/store.js";
|
|
6
|
-
import { createService } from "../src/service.js";
|
|
7
|
-
import { createDreamScheduler } from "../src/dream.js";
|
|
8
|
-
|
|
9
|
-
// 回归(issue #634):阈值基线只在成功轮前进(dream_runs 落库 + 调度器闭包),而
|
|
10
|
-
// 归档/删除/合并把活跃量压下去——活跃量一旦跌破基线,overBase 恒为假,从此再
|
|
11
|
-
// 也不会巩固,且全程静默(报告实测停摆 13 天 / 22 小时零轮:末轮基线 1,475 条,
|
|
12
|
-
// 归档后活跃 798 条,门槛 ≥1,485 永不满足)。修法是判定口夹回:活跃量跌破基线的
|
|
13
|
-
// 轴取 min,阈值从新的低点重新起算,归掉的量不必重新长回来。本文件锁四个面:
|
|
14
|
-
// 1. 活着的调度器亲历收缩:归档压破基线后,重新增长阈值量仍能触发(停摆反例);
|
|
15
|
-
// 2. 重启后旧峰值种子在第一次判定被夹回——不落库也能自愈(收缩不触发
|
|
16
|
-
// notifyWrite,调度器本来就无从在归档当下得知,判定口是唯一可靠的收敛处);
|
|
17
|
-
// 3. 夹回事件走 auditBaselineClamp 记账:每次收缩周期至多一次、不收缩不记;
|
|
18
|
-
// 4. 两轴独立夹回:只缩字符不缩条数时,条数轴的基线不动。
|
|
19
|
-
|
|
20
|
-
/** 与阈值判定同口径的活跃库规模(测试侧独立实现,避免与被测代码共用一份过滤条件)。 */
|
|
21
|
-
function activeSize(store) {
|
|
22
|
-
const rows = store.all().filter((m) => !m.archived && m.type !== "summary" && m.type !== "document");
|
|
23
|
-
return {
|
|
24
|
-
count: rows.length,
|
|
25
|
-
chars: rows.reduce((sum, m) => sum + m.title.length + m.content.length, 0)
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function seedMemories(service, n, tag = "a") {
|
|
30
|
-
// 标题必须全局唯一(saveWithDedupe 的去重键是 type+title+scope,同名会合并成
|
|
31
|
-
// 同一条),「累计到阈值」的断言才会真涨。
|
|
32
|
-
for (let i = 0; i < n; i++) {
|
|
33
|
-
service.saveWithDedupe({ type: "history", title: `${tag}-${i}`, content: "x".repeat(300), importance: 3 });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** 把「上次成功轮的库规模」落进 dream_runs——调度器重启后读到的就是这份种子。 */
|
|
38
|
-
function persistBaseline(store) {
|
|
39
|
-
const size = activeSize(store);
|
|
40
|
-
store.saveDreamRun({
|
|
41
|
-
status: "ok", snapshot_hash: "h", input_count: 1, receipt: "r",
|
|
42
|
-
created_at: new Date(Date.now() - 10).toISOString(), run_type: "auto",
|
|
43
|
-
store_count: size.count, store_chars: size.chars
|
|
44
|
-
});
|
|
45
|
-
return size;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function makeOpts(store, overrides = {}) {
|
|
49
|
-
return {
|
|
50
|
-
onRun: async () => ({ ok: false }),
|
|
51
|
-
thresholdCount: 10, thresholdChars: 5000, delayMs: 0,
|
|
52
|
-
baselineSeed: store.lastDreamBaseline("auto"),
|
|
53
|
-
logger: { warn: () => {} },
|
|
54
|
-
...overrides
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** 把活跃行里除 excludeTag 外的前 n 条归档(归档不触发 notifyWrite,这正是 #634 的一部分)。 */
|
|
59
|
-
function archiveRows(service, store, n, excludeTag = null) {
|
|
60
|
-
const rows = store.all().filter((m) => !m.archived && (excludeTag === null || !m.title.startsWith(excludeTag)));
|
|
61
|
-
for (const m of rows.slice(0, n)) service.setArchived(m.id, true);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
test("issue#634: a live scheduler survives an archive below the baseline (the freeze, unlocked)", async () => {
|
|
65
|
-
const store = createStore(":memory:");
|
|
66
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
67
|
-
seedMemories(service, 12);
|
|
68
|
-
const baseline = persistBaseline(store);
|
|
69
|
-
const dream = createDreamScheduler(makeOpts(store));
|
|
70
|
-
assert.equal(dream.maybeSchedule(service), false, "no growth since the last run → no trigger");
|
|
71
|
-
// 收缩发生在两次写入之间:归档 10 条,活跃 2 条 < 基线 12 条。
|
|
72
|
-
archiveRows(service, store, 10);
|
|
73
|
-
assert.ok(activeSize(store).count < baseline.count, "precondition: archive pulled active below the baseline");
|
|
74
|
-
// 收缩后的第一次写入把基线夹回当前水平(2+1=3),此后按新的低点起算。
|
|
75
|
-
seedMemories(service, 1, "g");
|
|
76
|
-
assert.equal(dream.maybeSchedule(service), false, "the first post-shrink write clamps instead of triggering");
|
|
77
|
-
seedMemories(service, 9, "p");
|
|
78
|
-
assert.equal(dream.maybeSchedule(service), false, "+9 from the clamped level is still below the threshold");
|
|
79
|
-
seedMemories(service, 1, "q"); // 夹回后的第 +10 条
|
|
80
|
-
assert.equal(dream.maybeSchedule(service), true, "+10 past the clamp fires — pre-fix this stayed silent forever");
|
|
81
|
-
await dream.dispose();
|
|
82
|
-
store.close();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("issue#634: a stale peak seed from dream_runs is clamped on the first judgement after restart", async () => {
|
|
86
|
-
const store = createStore(":memory:");
|
|
87
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
88
|
-
seedMemories(service, 12);
|
|
89
|
-
persistBaseline(store);
|
|
90
|
-
// 收缩发生在上一进程末轮之后、重启之前(归档不 notifyWrite,调度器无从得知):
|
|
91
|
-
// dream_runs 里的种子还是那个峰值。
|
|
92
|
-
archiveRows(service, store, 10);
|
|
93
|
-
const dream = createDreamScheduler(makeOpts(store));
|
|
94
|
-
// 重启后第一条写入:先把旧峰值种子夹回当前水平,再谈阈值。
|
|
95
|
-
seedMemories(service, 1, "r");
|
|
96
|
-
assert.equal(dream.maybeSchedule(service), false, "the restart write clamps the stale seed instead of bypassing");
|
|
97
|
-
seedMemories(service, 9, "s");
|
|
98
|
-
assert.equal(dream.maybeSchedule(service), false, "+9 from the clamped level is still below the threshold");
|
|
99
|
-
seedMemories(service, 1, "t");
|
|
100
|
-
assert.equal(dream.maybeSchedule(service), true, "the clamp makes the threshold reachable again without re-growing archived rows");
|
|
101
|
-
await dream.dispose();
|
|
102
|
-
store.close();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("issue#634: the clamp is audited exactly once per shrink episode, and never without one", async () => {
|
|
106
|
-
const store = createStore(":memory:");
|
|
107
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
108
|
-
const clamps = [];
|
|
109
|
-
seedMemories(service, 12);
|
|
110
|
-
const baseline = persistBaseline(store);
|
|
111
|
-
const dream = createDreamScheduler(makeOpts(store, {
|
|
112
|
-
auditBaselineClamp: ({ previous, count, chars }) => clamps.push({ previous, count, chars })
|
|
113
|
-
}));
|
|
114
|
-
// 不收缩的写入:正常增长(13 > 12),既不夹回也不记账。
|
|
115
|
-
seedMemories(service, 1, "n");
|
|
116
|
-
assert.equal(dream.maybeSchedule(service), false);
|
|
117
|
-
assert.equal(clamps.length, 0, "no clamp audit without a shrink");
|
|
118
|
-
// 归档压破基线(12→2),收缩后第一次判定夹回并记一次账。
|
|
119
|
-
archiveRows(service, store, 10, "n-");
|
|
120
|
-
seedMemories(service, 1, "p");
|
|
121
|
-
assert.equal(dream.maybeSchedule(service), false);
|
|
122
|
-
assert.equal(clamps.length, 1, "the shrink episode produces exactly one clamp audit");
|
|
123
|
-
assert.deepEqual(clamps[0].previous, baseline, "previous = the stale peak baseline");
|
|
124
|
-
assert.equal(clamps[0].count, activeSize(store).count, "count/chars = the clamped (current active) level");
|
|
125
|
-
assert.equal(clamps[0].chars, activeSize(store).chars, "chars axis is clamped to the same level");
|
|
126
|
-
// 同一收缩周期内活跃只增不减:不再重复记账(夹回后基线 == 活跃量)。
|
|
127
|
-
seedMemories(service, 5, "u");
|
|
128
|
-
assert.equal(dream.maybeSchedule(service), false, "still below the threshold from the clamped level");
|
|
129
|
-
assert.equal(clamps.length, 1, "no repeated clamp audits while active only grows");
|
|
130
|
-
await dream.dispose();
|
|
131
|
-
store.close();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("issue#634: the clamp is per-axis — a chars-only shrink leaves the count baseline alone", async () => {
|
|
135
|
-
const store = createStore(":memory:");
|
|
136
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
137
|
-
seedMemories(service, 12);
|
|
138
|
-
const baseline = persistBaseline(store);
|
|
139
|
-
const clamps = [];
|
|
140
|
-
const dream = createDreamScheduler(makeOpts(store, {
|
|
141
|
-
auditBaselineClamp: (event) => clamps.push(event)
|
|
142
|
-
}));
|
|
143
|
-
// 更新类写入(同 type+title+scope 命中去重键)把一条换成短内容:条数不动、字符缩。
|
|
144
|
-
// 去重命中默认 appendContent(追加),只有显式 _overwrite 才替换——不加的话
|
|
145
|
-
// 字符数反而变多,这条用例的前置就不成立了。
|
|
146
|
-
service.saveWithDedupe({ type: "history", title: "a-0", content: "短", _overwrite: true, importance: 3 });
|
|
147
|
-
assert.equal(activeSize(store).count, baseline.count, "precondition: the count axis did not move");
|
|
148
|
-
assert.ok(activeSize(store).chars < baseline.chars, "precondition: the chars axis shrank");
|
|
149
|
-
assert.equal(dream.maybeSchedule(service), false, "the threshold itself is not met yet");
|
|
150
|
-
assert.equal(clamps.length, 1, "a chars-only shrink still clamps");
|
|
151
|
-
assert.equal(clamps[0].previous.count, baseline.count, "the count baseline was intact before the clamp");
|
|
152
|
-
assert.equal(clamps[0].count, baseline.count, "the count baseline survives — only the shrunken axis moves");
|
|
153
|
-
assert.ok(clamps[0].chars < clamps[0].previous.chars, "the chars baseline is clamped down");
|
|
154
|
-
await dream.dispose();
|
|
155
|
-
store.close();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// --- CodeRabbit 在 #291 上的同类要求(源码级锁):index.js 的接线是单行注入,仓库
|
|
159
|
-
// 没有插件级挂载 harness(apply 需要完整宿主 ctx),删掉该注入行不会让任何单测变
|
|
160
|
-
// 红——所以用正则锁注入本身。import 用 URL 相对本文件解析,不依赖测试进程 CWD。
|
|
161
|
-
const indexSrc = readFileSync(fileURLToPath(new URL("../src/index.js", import.meta.url)), "utf8");
|
|
162
|
-
|
|
163
|
-
test("issue#634: index.js wires the baseline-clamp audit into llm_audit_logs", () => {
|
|
164
|
-
assert.match(
|
|
165
|
-
indexSrc,
|
|
166
|
-
/auditBaselineClamp:/,
|
|
167
|
-
"the clamp callback must be wired at assembly — otherwise clamp events stay unobservable (the #634 silence)"
|
|
168
|
-
);
|
|
169
|
-
assert.match(
|
|
170
|
-
indexSrc,
|
|
171
|
-
/error_message:\s*"baseline-clamp"/,
|
|
172
|
-
"the audit row must carry the baseline-clamp marker so it can be told apart from peak-hours skips"
|
|
173
|
-
);
|
|
174
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createStore } from "../src/store.js";
|
|
6
|
+
import { createService } from "../src/service.js";
|
|
7
|
+
import { createDreamScheduler } from "../src/dream.js";
|
|
8
|
+
|
|
9
|
+
// 回归(issue #634):阈值基线只在成功轮前进(dream_runs 落库 + 调度器闭包),而
|
|
10
|
+
// 归档/删除/合并把活跃量压下去——活跃量一旦跌破基线,overBase 恒为假,从此再
|
|
11
|
+
// 也不会巩固,且全程静默(报告实测停摆 13 天 / 22 小时零轮:末轮基线 1,475 条,
|
|
12
|
+
// 归档后活跃 798 条,门槛 ≥1,485 永不满足)。修法是判定口夹回:活跃量跌破基线的
|
|
13
|
+
// 轴取 min,阈值从新的低点重新起算,归掉的量不必重新长回来。本文件锁四个面:
|
|
14
|
+
// 1. 活着的调度器亲历收缩:归档压破基线后,重新增长阈值量仍能触发(停摆反例);
|
|
15
|
+
// 2. 重启后旧峰值种子在第一次判定被夹回——不落库也能自愈(收缩不触发
|
|
16
|
+
// notifyWrite,调度器本来就无从在归档当下得知,判定口是唯一可靠的收敛处);
|
|
17
|
+
// 3. 夹回事件走 auditBaselineClamp 记账:每次收缩周期至多一次、不收缩不记;
|
|
18
|
+
// 4. 两轴独立夹回:只缩字符不缩条数时,条数轴的基线不动。
|
|
19
|
+
|
|
20
|
+
/** 与阈值判定同口径的活跃库规模(测试侧独立实现,避免与被测代码共用一份过滤条件)。 */
|
|
21
|
+
function activeSize(store) {
|
|
22
|
+
const rows = store.all().filter((m) => !m.archived && m.type !== "summary" && m.type !== "document");
|
|
23
|
+
return {
|
|
24
|
+
count: rows.length,
|
|
25
|
+
chars: rows.reduce((sum, m) => sum + m.title.length + m.content.length, 0)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function seedMemories(service, n, tag = "a") {
|
|
30
|
+
// 标题必须全局唯一(saveWithDedupe 的去重键是 type+title+scope,同名会合并成
|
|
31
|
+
// 同一条),「累计到阈值」的断言才会真涨。
|
|
32
|
+
for (let i = 0; i < n; i++) {
|
|
33
|
+
service.saveWithDedupe({ type: "history", title: `${tag}-${i}`, content: "x".repeat(300), importance: 3 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 把「上次成功轮的库规模」落进 dream_runs——调度器重启后读到的就是这份种子。 */
|
|
38
|
+
function persistBaseline(store) {
|
|
39
|
+
const size = activeSize(store);
|
|
40
|
+
store.saveDreamRun({
|
|
41
|
+
status: "ok", snapshot_hash: "h", input_count: 1, receipt: "r",
|
|
42
|
+
created_at: new Date(Date.now() - 10).toISOString(), run_type: "auto",
|
|
43
|
+
store_count: size.count, store_chars: size.chars
|
|
44
|
+
});
|
|
45
|
+
return size;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeOpts(store, overrides = {}) {
|
|
49
|
+
return {
|
|
50
|
+
onRun: async () => ({ ok: false }),
|
|
51
|
+
thresholdCount: 10, thresholdChars: 5000, delayMs: 0,
|
|
52
|
+
baselineSeed: store.lastDreamBaseline("auto"),
|
|
53
|
+
logger: { warn: () => {} },
|
|
54
|
+
...overrides
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 把活跃行里除 excludeTag 外的前 n 条归档(归档不触发 notifyWrite,这正是 #634 的一部分)。 */
|
|
59
|
+
function archiveRows(service, store, n, excludeTag = null) {
|
|
60
|
+
const rows = store.all().filter((m) => !m.archived && (excludeTag === null || !m.title.startsWith(excludeTag)));
|
|
61
|
+
for (const m of rows.slice(0, n)) service.setArchived(m.id, true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
test("issue#634: a live scheduler survives an archive below the baseline (the freeze, unlocked)", async () => {
|
|
65
|
+
const store = createStore(":memory:");
|
|
66
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
67
|
+
seedMemories(service, 12);
|
|
68
|
+
const baseline = persistBaseline(store);
|
|
69
|
+
const dream = createDreamScheduler(makeOpts(store));
|
|
70
|
+
assert.equal(dream.maybeSchedule(service), false, "no growth since the last run → no trigger");
|
|
71
|
+
// 收缩发生在两次写入之间:归档 10 条,活跃 2 条 < 基线 12 条。
|
|
72
|
+
archiveRows(service, store, 10);
|
|
73
|
+
assert.ok(activeSize(store).count < baseline.count, "precondition: archive pulled active below the baseline");
|
|
74
|
+
// 收缩后的第一次写入把基线夹回当前水平(2+1=3),此后按新的低点起算。
|
|
75
|
+
seedMemories(service, 1, "g");
|
|
76
|
+
assert.equal(dream.maybeSchedule(service), false, "the first post-shrink write clamps instead of triggering");
|
|
77
|
+
seedMemories(service, 9, "p");
|
|
78
|
+
assert.equal(dream.maybeSchedule(service), false, "+9 from the clamped level is still below the threshold");
|
|
79
|
+
seedMemories(service, 1, "q"); // 夹回后的第 +10 条
|
|
80
|
+
assert.equal(dream.maybeSchedule(service), true, "+10 past the clamp fires — pre-fix this stayed silent forever");
|
|
81
|
+
await dream.dispose();
|
|
82
|
+
store.close();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("issue#634: a stale peak seed from dream_runs is clamped on the first judgement after restart", async () => {
|
|
86
|
+
const store = createStore(":memory:");
|
|
87
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
88
|
+
seedMemories(service, 12);
|
|
89
|
+
persistBaseline(store);
|
|
90
|
+
// 收缩发生在上一进程末轮之后、重启之前(归档不 notifyWrite,调度器无从得知):
|
|
91
|
+
// dream_runs 里的种子还是那个峰值。
|
|
92
|
+
archiveRows(service, store, 10);
|
|
93
|
+
const dream = createDreamScheduler(makeOpts(store));
|
|
94
|
+
// 重启后第一条写入:先把旧峰值种子夹回当前水平,再谈阈值。
|
|
95
|
+
seedMemories(service, 1, "r");
|
|
96
|
+
assert.equal(dream.maybeSchedule(service), false, "the restart write clamps the stale seed instead of bypassing");
|
|
97
|
+
seedMemories(service, 9, "s");
|
|
98
|
+
assert.equal(dream.maybeSchedule(service), false, "+9 from the clamped level is still below the threshold");
|
|
99
|
+
seedMemories(service, 1, "t");
|
|
100
|
+
assert.equal(dream.maybeSchedule(service), true, "the clamp makes the threshold reachable again without re-growing archived rows");
|
|
101
|
+
await dream.dispose();
|
|
102
|
+
store.close();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("issue#634: the clamp is audited exactly once per shrink episode, and never without one", async () => {
|
|
106
|
+
const store = createStore(":memory:");
|
|
107
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
108
|
+
const clamps = [];
|
|
109
|
+
seedMemories(service, 12);
|
|
110
|
+
const baseline = persistBaseline(store);
|
|
111
|
+
const dream = createDreamScheduler(makeOpts(store, {
|
|
112
|
+
auditBaselineClamp: ({ previous, count, chars }) => clamps.push({ previous, count, chars })
|
|
113
|
+
}));
|
|
114
|
+
// 不收缩的写入:正常增长(13 > 12),既不夹回也不记账。
|
|
115
|
+
seedMemories(service, 1, "n");
|
|
116
|
+
assert.equal(dream.maybeSchedule(service), false);
|
|
117
|
+
assert.equal(clamps.length, 0, "no clamp audit without a shrink");
|
|
118
|
+
// 归档压破基线(12→2),收缩后第一次判定夹回并记一次账。
|
|
119
|
+
archiveRows(service, store, 10, "n-");
|
|
120
|
+
seedMemories(service, 1, "p");
|
|
121
|
+
assert.equal(dream.maybeSchedule(service), false);
|
|
122
|
+
assert.equal(clamps.length, 1, "the shrink episode produces exactly one clamp audit");
|
|
123
|
+
assert.deepEqual(clamps[0].previous, baseline, "previous = the stale peak baseline");
|
|
124
|
+
assert.equal(clamps[0].count, activeSize(store).count, "count/chars = the clamped (current active) level");
|
|
125
|
+
assert.equal(clamps[0].chars, activeSize(store).chars, "chars axis is clamped to the same level");
|
|
126
|
+
// 同一收缩周期内活跃只增不减:不再重复记账(夹回后基线 == 活跃量)。
|
|
127
|
+
seedMemories(service, 5, "u");
|
|
128
|
+
assert.equal(dream.maybeSchedule(service), false, "still below the threshold from the clamped level");
|
|
129
|
+
assert.equal(clamps.length, 1, "no repeated clamp audits while active only grows");
|
|
130
|
+
await dream.dispose();
|
|
131
|
+
store.close();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("issue#634: the clamp is per-axis — a chars-only shrink leaves the count baseline alone", async () => {
|
|
135
|
+
const store = createStore(":memory:");
|
|
136
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
137
|
+
seedMemories(service, 12);
|
|
138
|
+
const baseline = persistBaseline(store);
|
|
139
|
+
const clamps = [];
|
|
140
|
+
const dream = createDreamScheduler(makeOpts(store, {
|
|
141
|
+
auditBaselineClamp: (event) => clamps.push(event)
|
|
142
|
+
}));
|
|
143
|
+
// 更新类写入(同 type+title+scope 命中去重键)把一条换成短内容:条数不动、字符缩。
|
|
144
|
+
// 去重命中默认 appendContent(追加),只有显式 _overwrite 才替换——不加的话
|
|
145
|
+
// 字符数反而变多,这条用例的前置就不成立了。
|
|
146
|
+
service.saveWithDedupe({ type: "history", title: "a-0", content: "短", _overwrite: true, importance: 3 });
|
|
147
|
+
assert.equal(activeSize(store).count, baseline.count, "precondition: the count axis did not move");
|
|
148
|
+
assert.ok(activeSize(store).chars < baseline.chars, "precondition: the chars axis shrank");
|
|
149
|
+
assert.equal(dream.maybeSchedule(service), false, "the threshold itself is not met yet");
|
|
150
|
+
assert.equal(clamps.length, 1, "a chars-only shrink still clamps");
|
|
151
|
+
assert.equal(clamps[0].previous.count, baseline.count, "the count baseline was intact before the clamp");
|
|
152
|
+
assert.equal(clamps[0].count, baseline.count, "the count baseline survives — only the shrunken axis moves");
|
|
153
|
+
assert.ok(clamps[0].chars < clamps[0].previous.chars, "the chars baseline is clamped down");
|
|
154
|
+
await dream.dispose();
|
|
155
|
+
store.close();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// --- CodeRabbit 在 #291 上的同类要求(源码级锁):index.js 的接线是单行注入,仓库
|
|
159
|
+
// 没有插件级挂载 harness(apply 需要完整宿主 ctx),删掉该注入行不会让任何单测变
|
|
160
|
+
// 红——所以用正则锁注入本身。import 用 URL 相对本文件解析,不依赖测试进程 CWD。
|
|
161
|
+
const indexSrc = readFileSync(fileURLToPath(new URL("../src/index.js", import.meta.url)), "utf8");
|
|
162
|
+
|
|
163
|
+
test("issue#634: index.js wires the baseline-clamp audit into llm_audit_logs", () => {
|
|
164
|
+
assert.match(
|
|
165
|
+
indexSrc,
|
|
166
|
+
/auditBaselineClamp:/,
|
|
167
|
+
"the clamp callback must be wired at assembly — otherwise clamp events stay unobservable (the #634 silence)"
|
|
168
|
+
);
|
|
169
|
+
assert.match(
|
|
170
|
+
indexSrc,
|
|
171
|
+
/error_message:\s*"baseline-clamp"/,
|
|
172
|
+
"the audit row must carry the baseline-clamp marker so it can be told apart from peak-hours skips"
|
|
173
|
+
);
|
|
174
|
+
});
|