@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,315 +1,315 @@
|
|
|
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 { createVectorIndex } from "../src/vector-index.js";
|
|
6
|
-
import { createSleepScheduler, runSleep } from "../src/dream/sleep.js";
|
|
7
|
-
|
|
8
|
-
// Issue #529(续排):本轮被预算截断(conflict 相位还有没进这一轮的对)且送出去的每一对
|
|
9
|
-
// 都落地了时,调度器不该等满一个 sleepMinIntervalHours(默认 8h)才跑下一轮——池子没清空,
|
|
10
|
-
// 剩下的对只能等到下一次到点,等于把"分批"变成"隔夜慢慢分批"。
|
|
11
|
-
//
|
|
12
|
-
// 这一组用例锁的是续排语义,防的回归是:
|
|
13
|
-
// ① 判据拿 status 反推 → 全判 keep 的轮次 applied 恒为 0、status 是 noop(永远到不了 ok),
|
|
14
|
-
// 续排在最该追的场景静默失效;
|
|
15
|
-
// ② 只把冷却项改成 0、不在回调里补挂闹钟 → 续排只追一轮就断,剩下的对又回到隔夜等;
|
|
16
|
-
// ③ 不限制连击 → 用户持续写入时池子进得比追得快,变成永不落地的每五分钟一轮,
|
|
17
|
-
// 而审计上每轮都"有产出",肉眼很难发现;
|
|
18
|
-
// ④ 失败轮也追 → 与连败退避抢同一只闹钟,退避被自己踩空。
|
|
19
|
-
|
|
20
|
-
const embedder = {
|
|
21
|
-
embedSingle: async () => [1, 0, 0],
|
|
22
|
-
embed: async () => [1, 0, 0],
|
|
23
|
-
schedule: () => {},
|
|
24
|
-
modelHash: "mock#1",
|
|
25
|
-
dimension: 3
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
function mockCtx(responder) {
|
|
29
|
-
let call = 0;
|
|
30
|
-
return {
|
|
31
|
-
logger: { warn: () => {}, info: () => {} },
|
|
32
|
-
agentDefaultModel: { currentSelection: () => ({ provider: "mock", model: "sleep-model" }) },
|
|
33
|
-
llm: {
|
|
34
|
-
async *stream(options) {
|
|
35
|
-
const userText = options.messages.find((m) => m.role === "user")?.content?.[0]?.text ?? "";
|
|
36
|
-
let reply = "[]";
|
|
37
|
-
if (options.purpose === "sleep-conflict") reply = responder(userText, ++call);
|
|
38
|
-
yield { type: "text-delta", index: 0, text: reply };
|
|
39
|
-
yield { type: "finish", reason: { kind: "stop" } };
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function setup() {
|
|
46
|
-
const store = createStore(":memory:");
|
|
47
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
48
|
-
const vectorIndex = createVectorIndex({ store });
|
|
49
|
-
service.setEmbedder(embedder);
|
|
50
|
-
service.setVectorIndex(vectorIndex);
|
|
51
|
-
return { store, service, vectorIndex };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 全部推到同一个方向附近:任意两条的 cosine 都 ≈ 1,findPotentialConflicts 生成 C(n,2) 对,
|
|
55
|
-
// 去重阶段按相似度留下 n/2 对(每记忆至多一对)。
|
|
56
|
-
function seedSimilarPairs(service, vectorIndex, n) {
|
|
57
|
-
for (let i = 0; i < n; i++) {
|
|
58
|
-
const m = service.saveWithDedupe({ type: "project", title: `主题${i}`, content: `内容${i}`, importance: 3 }).memory;
|
|
59
|
-
vectorIndex.saveEmbedding(m.id, [1, 0, 0]);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function baseConfig(overrides = {}) {
|
|
64
|
-
return {
|
|
65
|
-
sleepModeEnabled: true,
|
|
66
|
-
sleepIdleMinutes: 5,
|
|
67
|
-
sleepMinIntervalHours: 8,
|
|
68
|
-
sleepConflictStrictness: "normal",
|
|
69
|
-
sleepArchiveDays: 30,
|
|
70
|
-
sleepCompressDays: 90,
|
|
71
|
-
sleepPatternMinMemories: 100,
|
|
72
|
-
sleepMaxPatternPerRun: 3,
|
|
73
|
-
...overrides
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 与 test/sleep-failure-backoff.test.js 同款房型:时钟与定时器全部注入。
|
|
78
|
-
function makeHarness(overrides = {}) {
|
|
79
|
-
let nowMs = 10_000_000;
|
|
80
|
-
const timers = [];
|
|
81
|
-
let seq = 1;
|
|
82
|
-
const store = createStore(":memory:");
|
|
83
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
84
|
-
let runs = 0;
|
|
85
|
-
let outcome = () => ({ ok: true, status: "noop" });
|
|
86
|
-
// 运行**期间**要发生的事(写入 / 耗时)——用来复现"跑完那一刻已挂着的表只剩几秒"。
|
|
87
|
-
let duringRun = null;
|
|
88
|
-
const sched = createSleepScheduler({
|
|
89
|
-
service,
|
|
90
|
-
config: { sleepModeEnabled: true, sleepIdleMinutes: 5, sleepMinIntervalHours: 1, ...overrides },
|
|
91
|
-
logger: { warn: () => {} },
|
|
92
|
-
onRun: async () => {
|
|
93
|
-
runs++;
|
|
94
|
-
if (duringRun) await duringRun();
|
|
95
|
-
return outcome();
|
|
96
|
-
},
|
|
97
|
-
now: () => nowMs,
|
|
98
|
-
setTimeoutFn: (fn, delay) => {
|
|
99
|
-
const t = { id: seq++, at: nowMs + delay, fn };
|
|
100
|
-
timers.push(t);
|
|
101
|
-
return t.id;
|
|
102
|
-
},
|
|
103
|
-
clearTimeoutFn: (id) => {
|
|
104
|
-
const i = timers.findIndex((t) => t.id === id);
|
|
105
|
-
if (i >= 0) timers.splice(i, 1);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
const fireNext = async () => {
|
|
109
|
-
timers.sort((a, b) => a.at - b.at);
|
|
110
|
-
const t = timers.shift();
|
|
111
|
-
if (!t) throw new Error("no armed timer to fire");
|
|
112
|
-
nowMs = t.at;
|
|
113
|
-
await t.fn();
|
|
114
|
-
};
|
|
115
|
-
return {
|
|
116
|
-
sched,
|
|
117
|
-
store,
|
|
118
|
-
timers,
|
|
119
|
-
fireNext,
|
|
120
|
-
runs: () => runs,
|
|
121
|
-
now: () => nowMs,
|
|
122
|
-
setOutcome: (fn) => { outcome = fn; },
|
|
123
|
-
setDuringRun: (fn) => { duringRun = fn; },
|
|
124
|
-
write: () => { nowMs += 1000; sched.noteWrite(); },
|
|
125
|
-
advance: (ms) => { nowMs += ms; }
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const MIN = 60_000;
|
|
130
|
-
const HOUR = 3_600_000;
|
|
131
|
-
|
|
132
|
-
test("issue#529 续排:clean + 还有没进这一轮的对 ⇒ 下一个闹钟钉在静默窗口,不等最小间隔", async () => {
|
|
133
|
-
const h = makeHarness();
|
|
134
|
-
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
135
|
-
h.write();
|
|
136
|
-
await h.fireNext();
|
|
137
|
-
assert.equal(h.runs(), 1, "静默到点起跑一轮");
|
|
138
|
-
assert.equal(h.timers.length, 1, "关键:追 backlog 的轮次跑完必须自己补挂闹钟(此前一条都不挂)");
|
|
139
|
-
const delay = h.timers[0].at - h.now();
|
|
140
|
-
assert.equal(delay, 5 * MIN + 1000, "下限是一个静默窗口,不是 1 秒也不是 1 小时");
|
|
141
|
-
|
|
142
|
-
await h.fireNext();
|
|
143
|
-
assert.equal(h.runs(), 2, "关键:闹钟真的起跑了第二轮(只补挂不起跑等于没追)");
|
|
144
|
-
h.store.close();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("issue#529 续排:不 clean 时不追,间隔回到最小间隔", async () => {
|
|
148
|
-
const h = makeHarness();
|
|
149
|
-
h.setOutcome(() => ({ ok: true, status: "degraded", catchup: false, backlog: true }));
|
|
150
|
-
h.write();
|
|
151
|
-
await h.fireNext();
|
|
152
|
-
assert.equal(h.runs(), 1);
|
|
153
|
-
assert.equal(h.timers.length, 0, "有对没落地 ⇒ 不补挂短闹钟,等下一次写入重新起算");
|
|
154
|
-
const t = h.now();
|
|
155
|
-
assert.equal(h.sched.shouldRun(t + HOUR - 1), false, "冷却仍是基数间隔");
|
|
156
|
-
assert.equal(h.sched.shouldRun(t + HOUR), true);
|
|
157
|
-
h.store.close();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test("issue#529 续排:运行期间用户写入挂的表要在收尾时重挂,不能剩几秒就开下一批", async () => {
|
|
161
|
-
const h = makeHarness();
|
|
162
|
-
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
163
|
-
h.write();
|
|
164
|
-
// 运行一开始就按静默窗口挂好了表(at = 起跑时刻 + 5min + 1s)。
|
|
165
|
-
const armedAt = h.timers[0].at;
|
|
166
|
-
h.setDuringRun(async () => {
|
|
167
|
-
// 运行早期用户又写了一次:noteWrite 撤表重挂,新表从**那一刻**起算。
|
|
168
|
-
h.advance(MIN);
|
|
169
|
-
h.write();
|
|
170
|
-
// 这一轮本身跑了很久(LLM 往返 + 落库),早就越过那只表的到点时刻。真实环境里
|
|
171
|
-
// 这只表此刻已经可以立即触发——收尾若不重挂,下一批就是"几秒后开跑",
|
|
172
|
-
// 「让库安静一个静默窗口再追」形同虚设。
|
|
173
|
-
h.advance(10 * MIN);
|
|
174
|
-
});
|
|
175
|
-
await h.fireNext();
|
|
176
|
-
assert.equal(h.runs(), 1);
|
|
177
|
-
assert.ok(h.now() > armedAt, "前置:这一轮确实跑过了原来那只表的到点时刻");
|
|
178
|
-
assert.equal(h.timers.length, 1, "收尾必须只剩(重挂的)一只表");
|
|
179
|
-
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000, "关键:从**收尾这一刻**重算整一个静默窗口,不是沿用运行期挂的旧表");
|
|
180
|
-
h.store.close();
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test("issue#529 续排:台账写入失败要让这一轮不 clean,不能每个静默窗口重问同一个冲突", async () => {
|
|
184
|
-
const { service, store, vectorIndex } = setup();
|
|
185
|
-
seedSimilarPairs(service, vectorIndex, 8);
|
|
186
|
-
// 留痕写不进去:这一对既没被记成"已审",下一轮还会原样回到候选池。若仍判 clean,
|
|
187
|
-
// 续排会一个静默窗口问一次 LLM、直到连击上限,而审计上每轮都是干净的成功轮。
|
|
188
|
-
service.saveConflictReviewed = () => {
|
|
189
|
-
throw new Error("ledger unavailable");
|
|
190
|
-
};
|
|
191
|
-
const ctx = mockCtx(() => "[]");
|
|
192
|
-
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
193
|
-
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
194
|
-
assert.ok(result.phases.conflicts.ledgerFailed > 0, "前置:确实有留痕写入失败");
|
|
195
|
-
assert.equal(result.phases.conflicts.clean, false, "关键:没记上账就不算落地");
|
|
196
|
-
assert.equal(result.catchup, false, "不 clean ⇒ 不追下一批,回到正常节奏");
|
|
197
|
-
store.close();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("issue#529 续排:失败轮不追(与连败退避互斥)", async () => {
|
|
201
|
-
const h = makeHarness({ sleepFailureBackoff: true });
|
|
202
|
-
h.setOutcome(() => ({ ok: false, status: "failed", catchup: true, backlog: true }));
|
|
203
|
-
h.write();
|
|
204
|
-
await h.fireNext();
|
|
205
|
-
// 失败本身仍然要补挂(#187:不补挂就永远等不到下一轮),但补的是**退避后的**长表,
|
|
206
|
-
// 不是续排那张静默窗口的短表——这才是"互斥"的可观测形态。
|
|
207
|
-
assert.equal(h.timers.length, 1, "失败轮按 #187 仍补挂一张表");
|
|
208
|
-
assert.equal(h.timers[0].at - h.now(), 2 * HOUR + 1000, "补的是 1h × 2^1 的退避表,不是 5 分钟的续排表");
|
|
209
|
-
const t = h.now();
|
|
210
|
-
assert.equal(h.sched.shouldRun(t + 2 * HOUR - 1), false, "退避仍然生效(1h 基数 × 2^1)");
|
|
211
|
-
assert.equal(h.sched.shouldRun(t + 2 * HOUR), true);
|
|
212
|
-
h.store.close();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("issue#529 续排:连击到上限即收手,闹钟回到正常间隔", async () => {
|
|
216
|
-
const h = makeHarness();
|
|
217
|
-
// 用 runSleep 的真实返回形状:全判 keep 的一轮 status 是 noop,而 ok 只在
|
|
218
|
-
// ok/degraded 时为真——桩里写 ok:true 会让 maybeSchedule 走进与真实路径不同的分支
|
|
219
|
-
// (!ran 那条),于是「到顶后没有定时器」测的其实是桩的偏差,不是实现的行为。
|
|
220
|
-
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
221
|
-
h.write();
|
|
222
|
-
await h.fireNext();
|
|
223
|
-
// 24 是实现里的 CATCHUP_STREAK_CAP:每轮都 clean 但池子永远追不完时,必须自己刹住。
|
|
224
|
-
for (let i = 2; i <= 24; i++) {
|
|
225
|
-
assert.equal(h.timers.length, 1, `第 ${i} 轮之前应当有一只已挂好的闹钟`);
|
|
226
|
-
await h.fireNext();
|
|
227
|
-
}
|
|
228
|
-
assert.equal(h.runs(), 24, "连追 24 轮");
|
|
229
|
-
const t = h.now();
|
|
230
|
-
assert.equal(h.sched.shouldRun(t + 5 * MIN), false, "收手后回到最小间隔,不再按静默窗口放行");
|
|
231
|
-
assert.equal(h.sched.shouldRun(t + HOUR), true);
|
|
232
|
-
// 收手后既不能是「一直按静默窗口重排」(空转链),也不能是「什么都不挂」
|
|
233
|
-
// (#187:CD 到期成了没有监听者的时刻)。正确形态是回到正常的最小间隔。
|
|
234
|
-
assert.equal(h.timers.length, 1, "按 #187 补挂一张表,但不能是续排的短表");
|
|
235
|
-
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "补的是正常最小间隔,不是 5 分钟的续排表");
|
|
236
|
-
h.store.close();
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
test("issue#529 续排:全判 keep 的一轮 status=noop 仍是 clean,续排判据不依赖 status", async () => {
|
|
240
|
-
const { service, store, vectorIndex } = setup();
|
|
241
|
-
seedSimilarPairs(service, vectorIndex, 8);
|
|
242
|
-
const ctx = mockCtx(() => "[]");
|
|
243
|
-
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
244
|
-
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
245
|
-
assert.equal(result.phases.conflicts.applied, 0, "全判 keep ⇒ 没有任何落地");
|
|
246
|
-
assert.equal(result.phases.conflicts.status, "noop", "关键:这一轮永远到不了 ok(拿 status 当判据就死在这里)");
|
|
247
|
-
assert.ok(result.phases.conflicts.perRunSkipped > 0, "前置:本轮确实被预算截断");
|
|
248
|
-
assert.equal(result.phases.conflicts.clean, true, "送出去的每一对都留了台账 ⇒ clean");
|
|
249
|
-
assert.equal(result.catchup, true, "关键:noop 轮次也要能追");
|
|
250
|
-
assert.equal(result.backlog, true);
|
|
251
|
-
store.close();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("issue#529 续排:坏批让这一轮不 clean,即使还有 backlog 也不追", async () => {
|
|
255
|
-
const { service, store, vectorIndex } = setup();
|
|
256
|
-
seedSimilarPairs(service, vectorIndex, 8);
|
|
257
|
-
let call = 0;
|
|
258
|
-
// 第一批吐空(stream 正常结束但没有 JSON 数组 ⇒ 这批 failed),后面的批正常。
|
|
259
|
-
const ctx = mockCtx(() => (++call === 1 ? "" : "[]"));
|
|
260
|
-
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
261
|
-
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
262
|
-
assert.equal(result.backlog, true, "前置:确实有对没进这一轮");
|
|
263
|
-
assert.equal(result.phases.conflicts.clean, false, "有批没走通 ⇒ 不 clean");
|
|
264
|
-
assert.equal(result.catchup, false, "关键:不 clean 时宁可少追一轮,也不要在失败状态里把间隔压到静默窗口");
|
|
265
|
-
store.close();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
test("issue#529 续排:连击到顶收手之后,下一次正常触发要能重新起算(刹车不能变成永久闸)", async () => {
|
|
269
|
-
const h = makeHarness();
|
|
270
|
-
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
271
|
-
h.write();
|
|
272
|
-
await h.fireNext();
|
|
273
|
-
for (let i = 2; i <= 24; i++) await h.fireNext();
|
|
274
|
-
assert.equal(h.runs(), 24);
|
|
275
|
-
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "前置:到顶后已收手,闹钟回到正常间隔");
|
|
276
|
-
|
|
277
|
-
// 收手若只把 catchupWanted 置 false、**不把连击清零**,这个进程此后每一轮都会被同一道闸
|
|
278
|
-
// 挡下(streak 只增不减),"等下一个静默窗口重新起算"就成了一句空话。
|
|
279
|
-
h.write();
|
|
280
|
-
for (let i = 0; i < 5 && h.runs() < 25; i++) await h.fireNext();
|
|
281
|
-
assert.equal(h.runs(), 25, "关键:收手之后仍能被正常触发救回来");
|
|
282
|
-
assert.equal(h.timers.length, 1, "重新起算后又该补挂续排闹钟");
|
|
283
|
-
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000, "补的是静默窗口的短表,说明连击已从 0 重新算");
|
|
284
|
-
h.store.close();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test("issue#529 续排:一轮抛错要把连击一并清零,不能把上一轮的进度带进下一次起算", async () => {
|
|
288
|
-
const h = makeHarness();
|
|
289
|
-
// 同上面两条:runSleep 在 status==="noop" 时给 ok:false,桩要照它写。
|
|
290
|
-
const clean = () => ({ ok: false, status: "noop", catchup: true, backlog: true });
|
|
291
|
-
h.setOutcome(clean);
|
|
292
|
-
h.write();
|
|
293
|
-
await h.fireNext();
|
|
294
|
-
// 先追到 23(上限 24 的前一格):此时连击若不被清掉,下一轮 clean 就会立刻撞上限。
|
|
295
|
-
for (let i = 2; i <= 23; i++) await h.fireNext();
|
|
296
|
-
assert.equal(h.runs(), 23);
|
|
297
|
-
|
|
298
|
-
h.setOutcome(() => {
|
|
299
|
-
throw new Error("boom");
|
|
300
|
-
});
|
|
301
|
-
h.write();
|
|
302
|
-
await h.fireNext();
|
|
303
|
-
assert.equal(h.runs(), 24, "第 24 轮抛错");
|
|
304
|
-
assert.equal(h.timers.length, 1, "抛错仍按 #187 补挂一张表(补的是退避长表)");
|
|
305
|
-
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "抛错后回落到最小间隔,续排的短表不再挂");
|
|
306
|
-
|
|
307
|
-
// 关键:抛错回合若不把 catchupStreak 清零,接下来这一轮 clean 的连击会从 23 累到 24,
|
|
308
|
-
// 于是"刚被救回来"的进程立刻又被同一道闸挡下。
|
|
309
|
-
h.setOutcome(clean);
|
|
310
|
-
await h.fireNext();
|
|
311
|
-
assert.equal(h.runs(), 25, "抛错后的第一次干净轮次仍要真的跑");
|
|
312
|
-
assert.equal(h.timers.length, 1, "关键:连击已清零,这一轮又能挂上续排闹钟");
|
|
313
|
-
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000);
|
|
314
|
-
h.store.close();
|
|
315
|
-
});
|
|
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 { createVectorIndex } from "../src/vector-index.js";
|
|
6
|
+
import { createSleepScheduler, runSleep } from "../src/dream/sleep.js";
|
|
7
|
+
|
|
8
|
+
// Issue #529(续排):本轮被预算截断(conflict 相位还有没进这一轮的对)且送出去的每一对
|
|
9
|
+
// 都落地了时,调度器不该等满一个 sleepMinIntervalHours(默认 8h)才跑下一轮——池子没清空,
|
|
10
|
+
// 剩下的对只能等到下一次到点,等于把"分批"变成"隔夜慢慢分批"。
|
|
11
|
+
//
|
|
12
|
+
// 这一组用例锁的是续排语义,防的回归是:
|
|
13
|
+
// ① 判据拿 status 反推 → 全判 keep 的轮次 applied 恒为 0、status 是 noop(永远到不了 ok),
|
|
14
|
+
// 续排在最该追的场景静默失效;
|
|
15
|
+
// ② 只把冷却项改成 0、不在回调里补挂闹钟 → 续排只追一轮就断,剩下的对又回到隔夜等;
|
|
16
|
+
// ③ 不限制连击 → 用户持续写入时池子进得比追得快,变成永不落地的每五分钟一轮,
|
|
17
|
+
// 而审计上每轮都"有产出",肉眼很难发现;
|
|
18
|
+
// ④ 失败轮也追 → 与连败退避抢同一只闹钟,退避被自己踩空。
|
|
19
|
+
|
|
20
|
+
const embedder = {
|
|
21
|
+
embedSingle: async () => [1, 0, 0],
|
|
22
|
+
embed: async () => [1, 0, 0],
|
|
23
|
+
schedule: () => {},
|
|
24
|
+
modelHash: "mock#1",
|
|
25
|
+
dimension: 3
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function mockCtx(responder) {
|
|
29
|
+
let call = 0;
|
|
30
|
+
return {
|
|
31
|
+
logger: { warn: () => {}, info: () => {} },
|
|
32
|
+
agentDefaultModel: { currentSelection: () => ({ provider: "mock", model: "sleep-model" }) },
|
|
33
|
+
llm: {
|
|
34
|
+
async *stream(options) {
|
|
35
|
+
const userText = options.messages.find((m) => m.role === "user")?.content?.[0]?.text ?? "";
|
|
36
|
+
let reply = "[]";
|
|
37
|
+
if (options.purpose === "sleep-conflict") reply = responder(userText, ++call);
|
|
38
|
+
yield { type: "text-delta", index: 0, text: reply };
|
|
39
|
+
yield { type: "finish", reason: { kind: "stop" } };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function setup() {
|
|
46
|
+
const store = createStore(":memory:");
|
|
47
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
48
|
+
const vectorIndex = createVectorIndex({ store });
|
|
49
|
+
service.setEmbedder(embedder);
|
|
50
|
+
service.setVectorIndex(vectorIndex);
|
|
51
|
+
return { store, service, vectorIndex };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 全部推到同一个方向附近:任意两条的 cosine 都 ≈ 1,findPotentialConflicts 生成 C(n,2) 对,
|
|
55
|
+
// 去重阶段按相似度留下 n/2 对(每记忆至多一对)。
|
|
56
|
+
function seedSimilarPairs(service, vectorIndex, n) {
|
|
57
|
+
for (let i = 0; i < n; i++) {
|
|
58
|
+
const m = service.saveWithDedupe({ type: "project", title: `主题${i}`, content: `内容${i}`, importance: 3 }).memory;
|
|
59
|
+
vectorIndex.saveEmbedding(m.id, [1, 0, 0]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function baseConfig(overrides = {}) {
|
|
64
|
+
return {
|
|
65
|
+
sleepModeEnabled: true,
|
|
66
|
+
sleepIdleMinutes: 5,
|
|
67
|
+
sleepMinIntervalHours: 8,
|
|
68
|
+
sleepConflictStrictness: "normal",
|
|
69
|
+
sleepArchiveDays: 30,
|
|
70
|
+
sleepCompressDays: 90,
|
|
71
|
+
sleepPatternMinMemories: 100,
|
|
72
|
+
sleepMaxPatternPerRun: 3,
|
|
73
|
+
...overrides
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 与 test/sleep-failure-backoff.test.js 同款房型:时钟与定时器全部注入。
|
|
78
|
+
function makeHarness(overrides = {}) {
|
|
79
|
+
let nowMs = 10_000_000;
|
|
80
|
+
const timers = [];
|
|
81
|
+
let seq = 1;
|
|
82
|
+
const store = createStore(":memory:");
|
|
83
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
84
|
+
let runs = 0;
|
|
85
|
+
let outcome = () => ({ ok: true, status: "noop" });
|
|
86
|
+
// 运行**期间**要发生的事(写入 / 耗时)——用来复现"跑完那一刻已挂着的表只剩几秒"。
|
|
87
|
+
let duringRun = null;
|
|
88
|
+
const sched = createSleepScheduler({
|
|
89
|
+
service,
|
|
90
|
+
config: { sleepModeEnabled: true, sleepIdleMinutes: 5, sleepMinIntervalHours: 1, ...overrides },
|
|
91
|
+
logger: { warn: () => {} },
|
|
92
|
+
onRun: async () => {
|
|
93
|
+
runs++;
|
|
94
|
+
if (duringRun) await duringRun();
|
|
95
|
+
return outcome();
|
|
96
|
+
},
|
|
97
|
+
now: () => nowMs,
|
|
98
|
+
setTimeoutFn: (fn, delay) => {
|
|
99
|
+
const t = { id: seq++, at: nowMs + delay, fn };
|
|
100
|
+
timers.push(t);
|
|
101
|
+
return t.id;
|
|
102
|
+
},
|
|
103
|
+
clearTimeoutFn: (id) => {
|
|
104
|
+
const i = timers.findIndex((t) => t.id === id);
|
|
105
|
+
if (i >= 0) timers.splice(i, 1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const fireNext = async () => {
|
|
109
|
+
timers.sort((a, b) => a.at - b.at);
|
|
110
|
+
const t = timers.shift();
|
|
111
|
+
if (!t) throw new Error("no armed timer to fire");
|
|
112
|
+
nowMs = t.at;
|
|
113
|
+
await t.fn();
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
sched,
|
|
117
|
+
store,
|
|
118
|
+
timers,
|
|
119
|
+
fireNext,
|
|
120
|
+
runs: () => runs,
|
|
121
|
+
now: () => nowMs,
|
|
122
|
+
setOutcome: (fn) => { outcome = fn; },
|
|
123
|
+
setDuringRun: (fn) => { duringRun = fn; },
|
|
124
|
+
write: () => { nowMs += 1000; sched.noteWrite(); },
|
|
125
|
+
advance: (ms) => { nowMs += ms; }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const MIN = 60_000;
|
|
130
|
+
const HOUR = 3_600_000;
|
|
131
|
+
|
|
132
|
+
test("issue#529 续排:clean + 还有没进这一轮的对 ⇒ 下一个闹钟钉在静默窗口,不等最小间隔", async () => {
|
|
133
|
+
const h = makeHarness();
|
|
134
|
+
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
135
|
+
h.write();
|
|
136
|
+
await h.fireNext();
|
|
137
|
+
assert.equal(h.runs(), 1, "静默到点起跑一轮");
|
|
138
|
+
assert.equal(h.timers.length, 1, "关键:追 backlog 的轮次跑完必须自己补挂闹钟(此前一条都不挂)");
|
|
139
|
+
const delay = h.timers[0].at - h.now();
|
|
140
|
+
assert.equal(delay, 5 * MIN + 1000, "下限是一个静默窗口,不是 1 秒也不是 1 小时");
|
|
141
|
+
|
|
142
|
+
await h.fireNext();
|
|
143
|
+
assert.equal(h.runs(), 2, "关键:闹钟真的起跑了第二轮(只补挂不起跑等于没追)");
|
|
144
|
+
h.store.close();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("issue#529 续排:不 clean 时不追,间隔回到最小间隔", async () => {
|
|
148
|
+
const h = makeHarness();
|
|
149
|
+
h.setOutcome(() => ({ ok: true, status: "degraded", catchup: false, backlog: true }));
|
|
150
|
+
h.write();
|
|
151
|
+
await h.fireNext();
|
|
152
|
+
assert.equal(h.runs(), 1);
|
|
153
|
+
assert.equal(h.timers.length, 0, "有对没落地 ⇒ 不补挂短闹钟,等下一次写入重新起算");
|
|
154
|
+
const t = h.now();
|
|
155
|
+
assert.equal(h.sched.shouldRun(t + HOUR - 1), false, "冷却仍是基数间隔");
|
|
156
|
+
assert.equal(h.sched.shouldRun(t + HOUR), true);
|
|
157
|
+
h.store.close();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("issue#529 续排:运行期间用户写入挂的表要在收尾时重挂,不能剩几秒就开下一批", async () => {
|
|
161
|
+
const h = makeHarness();
|
|
162
|
+
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
163
|
+
h.write();
|
|
164
|
+
// 运行一开始就按静默窗口挂好了表(at = 起跑时刻 + 5min + 1s)。
|
|
165
|
+
const armedAt = h.timers[0].at;
|
|
166
|
+
h.setDuringRun(async () => {
|
|
167
|
+
// 运行早期用户又写了一次:noteWrite 撤表重挂,新表从**那一刻**起算。
|
|
168
|
+
h.advance(MIN);
|
|
169
|
+
h.write();
|
|
170
|
+
// 这一轮本身跑了很久(LLM 往返 + 落库),早就越过那只表的到点时刻。真实环境里
|
|
171
|
+
// 这只表此刻已经可以立即触发——收尾若不重挂,下一批就是"几秒后开跑",
|
|
172
|
+
// 「让库安静一个静默窗口再追」形同虚设。
|
|
173
|
+
h.advance(10 * MIN);
|
|
174
|
+
});
|
|
175
|
+
await h.fireNext();
|
|
176
|
+
assert.equal(h.runs(), 1);
|
|
177
|
+
assert.ok(h.now() > armedAt, "前置:这一轮确实跑过了原来那只表的到点时刻");
|
|
178
|
+
assert.equal(h.timers.length, 1, "收尾必须只剩(重挂的)一只表");
|
|
179
|
+
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000, "关键:从**收尾这一刻**重算整一个静默窗口,不是沿用运行期挂的旧表");
|
|
180
|
+
h.store.close();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("issue#529 续排:台账写入失败要让这一轮不 clean,不能每个静默窗口重问同一个冲突", async () => {
|
|
184
|
+
const { service, store, vectorIndex } = setup();
|
|
185
|
+
seedSimilarPairs(service, vectorIndex, 8);
|
|
186
|
+
// 留痕写不进去:这一对既没被记成"已审",下一轮还会原样回到候选池。若仍判 clean,
|
|
187
|
+
// 续排会一个静默窗口问一次 LLM、直到连击上限,而审计上每轮都是干净的成功轮。
|
|
188
|
+
service.saveConflictReviewed = () => {
|
|
189
|
+
throw new Error("ledger unavailable");
|
|
190
|
+
};
|
|
191
|
+
const ctx = mockCtx(() => "[]");
|
|
192
|
+
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
193
|
+
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
194
|
+
assert.ok(result.phases.conflicts.ledgerFailed > 0, "前置:确实有留痕写入失败");
|
|
195
|
+
assert.equal(result.phases.conflicts.clean, false, "关键:没记上账就不算落地");
|
|
196
|
+
assert.equal(result.catchup, false, "不 clean ⇒ 不追下一批,回到正常节奏");
|
|
197
|
+
store.close();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("issue#529 续排:失败轮不追(与连败退避互斥)", async () => {
|
|
201
|
+
const h = makeHarness({ sleepFailureBackoff: true });
|
|
202
|
+
h.setOutcome(() => ({ ok: false, status: "failed", catchup: true, backlog: true }));
|
|
203
|
+
h.write();
|
|
204
|
+
await h.fireNext();
|
|
205
|
+
// 失败本身仍然要补挂(#187:不补挂就永远等不到下一轮),但补的是**退避后的**长表,
|
|
206
|
+
// 不是续排那张静默窗口的短表——这才是"互斥"的可观测形态。
|
|
207
|
+
assert.equal(h.timers.length, 1, "失败轮按 #187 仍补挂一张表");
|
|
208
|
+
assert.equal(h.timers[0].at - h.now(), 2 * HOUR + 1000, "补的是 1h × 2^1 的退避表,不是 5 分钟的续排表");
|
|
209
|
+
const t = h.now();
|
|
210
|
+
assert.equal(h.sched.shouldRun(t + 2 * HOUR - 1), false, "退避仍然生效(1h 基数 × 2^1)");
|
|
211
|
+
assert.equal(h.sched.shouldRun(t + 2 * HOUR), true);
|
|
212
|
+
h.store.close();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("issue#529 续排:连击到上限即收手,闹钟回到正常间隔", async () => {
|
|
216
|
+
const h = makeHarness();
|
|
217
|
+
// 用 runSleep 的真实返回形状:全判 keep 的一轮 status 是 noop,而 ok 只在
|
|
218
|
+
// ok/degraded 时为真——桩里写 ok:true 会让 maybeSchedule 走进与真实路径不同的分支
|
|
219
|
+
// (!ran 那条),于是「到顶后没有定时器」测的其实是桩的偏差,不是实现的行为。
|
|
220
|
+
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
221
|
+
h.write();
|
|
222
|
+
await h.fireNext();
|
|
223
|
+
// 24 是实现里的 CATCHUP_STREAK_CAP:每轮都 clean 但池子永远追不完时,必须自己刹住。
|
|
224
|
+
for (let i = 2; i <= 24; i++) {
|
|
225
|
+
assert.equal(h.timers.length, 1, `第 ${i} 轮之前应当有一只已挂好的闹钟`);
|
|
226
|
+
await h.fireNext();
|
|
227
|
+
}
|
|
228
|
+
assert.equal(h.runs(), 24, "连追 24 轮");
|
|
229
|
+
const t = h.now();
|
|
230
|
+
assert.equal(h.sched.shouldRun(t + 5 * MIN), false, "收手后回到最小间隔,不再按静默窗口放行");
|
|
231
|
+
assert.equal(h.sched.shouldRun(t + HOUR), true);
|
|
232
|
+
// 收手后既不能是「一直按静默窗口重排」(空转链),也不能是「什么都不挂」
|
|
233
|
+
// (#187:CD 到期成了没有监听者的时刻)。正确形态是回到正常的最小间隔。
|
|
234
|
+
assert.equal(h.timers.length, 1, "按 #187 补挂一张表,但不能是续排的短表");
|
|
235
|
+
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "补的是正常最小间隔,不是 5 分钟的续排表");
|
|
236
|
+
h.store.close();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("issue#529 续排:全判 keep 的一轮 status=noop 仍是 clean,续排判据不依赖 status", async () => {
|
|
240
|
+
const { service, store, vectorIndex } = setup();
|
|
241
|
+
seedSimilarPairs(service, vectorIndex, 8);
|
|
242
|
+
const ctx = mockCtx(() => "[]");
|
|
243
|
+
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
244
|
+
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
245
|
+
assert.equal(result.phases.conflicts.applied, 0, "全判 keep ⇒ 没有任何落地");
|
|
246
|
+
assert.equal(result.phases.conflicts.status, "noop", "关键:这一轮永远到不了 ok(拿 status 当判据就死在这里)");
|
|
247
|
+
assert.ok(result.phases.conflicts.perRunSkipped > 0, "前置:本轮确实被预算截断");
|
|
248
|
+
assert.equal(result.phases.conflicts.clean, true, "送出去的每一对都留了台账 ⇒ clean");
|
|
249
|
+
assert.equal(result.catchup, true, "关键:noop 轮次也要能追");
|
|
250
|
+
assert.equal(result.backlog, true);
|
|
251
|
+
store.close();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("issue#529 续排:坏批让这一轮不 clean,即使还有 backlog 也不追", async () => {
|
|
255
|
+
const { service, store, vectorIndex } = setup();
|
|
256
|
+
seedSimilarPairs(service, vectorIndex, 8);
|
|
257
|
+
let call = 0;
|
|
258
|
+
// 第一批吐空(stream 正常结束但没有 JSON 数组 ⇒ 这批 failed),后面的批正常。
|
|
259
|
+
const ctx = mockCtx(() => (++call === 1 ? "" : "[]"));
|
|
260
|
+
const config = baseConfig({ sleepConflictMaxPairs: 1, sleepConflictMaxPairsPerRun: 2 });
|
|
261
|
+
const result = await runSleep(ctx, service, config, ctx.logger, { embedder, vectorIndex }, null);
|
|
262
|
+
assert.equal(result.backlog, true, "前置:确实有对没进这一轮");
|
|
263
|
+
assert.equal(result.phases.conflicts.clean, false, "有批没走通 ⇒ 不 clean");
|
|
264
|
+
assert.equal(result.catchup, false, "关键:不 clean 时宁可少追一轮,也不要在失败状态里把间隔压到静默窗口");
|
|
265
|
+
store.close();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("issue#529 续排:连击到顶收手之后,下一次正常触发要能重新起算(刹车不能变成永久闸)", async () => {
|
|
269
|
+
const h = makeHarness();
|
|
270
|
+
h.setOutcome(() => ({ ok: false, status: "noop", catchup: true, backlog: true }));
|
|
271
|
+
h.write();
|
|
272
|
+
await h.fireNext();
|
|
273
|
+
for (let i = 2; i <= 24; i++) await h.fireNext();
|
|
274
|
+
assert.equal(h.runs(), 24);
|
|
275
|
+
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "前置:到顶后已收手,闹钟回到正常间隔");
|
|
276
|
+
|
|
277
|
+
// 收手若只把 catchupWanted 置 false、**不把连击清零**,这个进程此后每一轮都会被同一道闸
|
|
278
|
+
// 挡下(streak 只增不减),"等下一个静默窗口重新起算"就成了一句空话。
|
|
279
|
+
h.write();
|
|
280
|
+
for (let i = 0; i < 5 && h.runs() < 25; i++) await h.fireNext();
|
|
281
|
+
assert.equal(h.runs(), 25, "关键:收手之后仍能被正常触发救回来");
|
|
282
|
+
assert.equal(h.timers.length, 1, "重新起算后又该补挂续排闹钟");
|
|
283
|
+
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000, "补的是静默窗口的短表,说明连击已从 0 重新算");
|
|
284
|
+
h.store.close();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("issue#529 续排:一轮抛错要把连击一并清零,不能把上一轮的进度带进下一次起算", async () => {
|
|
288
|
+
const h = makeHarness();
|
|
289
|
+
// 同上面两条:runSleep 在 status==="noop" 时给 ok:false,桩要照它写。
|
|
290
|
+
const clean = () => ({ ok: false, status: "noop", catchup: true, backlog: true });
|
|
291
|
+
h.setOutcome(clean);
|
|
292
|
+
h.write();
|
|
293
|
+
await h.fireNext();
|
|
294
|
+
// 先追到 23(上限 24 的前一格):此时连击若不被清掉,下一轮 clean 就会立刻撞上限。
|
|
295
|
+
for (let i = 2; i <= 23; i++) await h.fireNext();
|
|
296
|
+
assert.equal(h.runs(), 23);
|
|
297
|
+
|
|
298
|
+
h.setOutcome(() => {
|
|
299
|
+
throw new Error("boom");
|
|
300
|
+
});
|
|
301
|
+
h.write();
|
|
302
|
+
await h.fireNext();
|
|
303
|
+
assert.equal(h.runs(), 24, "第 24 轮抛错");
|
|
304
|
+
assert.equal(h.timers.length, 1, "抛错仍按 #187 补挂一张表(补的是退避长表)");
|
|
305
|
+
assert.equal(h.timers[0].at - h.now(), HOUR + 1000, "抛错后回落到最小间隔,续排的短表不再挂");
|
|
306
|
+
|
|
307
|
+
// 关键:抛错回合若不把 catchupStreak 清零,接下来这一轮 clean 的连击会从 23 累到 24,
|
|
308
|
+
// 于是"刚被救回来"的进程立刻又被同一道闸挡下。
|
|
309
|
+
h.setOutcome(clean);
|
|
310
|
+
await h.fireNext();
|
|
311
|
+
assert.equal(h.runs(), 25, "抛错后的第一次干净轮次仍要真的跑");
|
|
312
|
+
assert.equal(h.timers.length, 1, "关键:连击已清零,这一轮又能挂上续排闹钟");
|
|
313
|
+
assert.equal(h.timers[0].at - h.now(), 5 * MIN + 1000);
|
|
314
|
+
h.store.close();
|
|
315
|
+
});
|