@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,264 +1,264 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { Config } from "../src/config.js";
|
|
4
|
-
import { createSessionGate } from "../src/session-gate.js";
|
|
5
|
-
import { createStore } from "../src/store.js";
|
|
6
|
-
import { createService } from "../src/service.js";
|
|
7
|
-
import { createSettings } from "../src/settings.js";
|
|
8
|
-
import { createInjector } from "../src/inject.js";
|
|
9
|
-
import { MEMORY_GUIDE_SECTION } from "../src/guide.js";
|
|
10
|
-
import { createTools } from "../src/tools.js";
|
|
11
|
-
import { createSummarizer } from "../src/summarize.js";
|
|
12
|
-
|
|
13
|
-
test("session gate defaults off and gates subagents by the host's delegation marks", () => {
|
|
14
|
-
const defaults = Config({});
|
|
15
|
-
assert.equal(defaults.sessionGateEnabled, false);
|
|
16
|
-
assert.equal(defaults.sessionGateSubagents, true);
|
|
17
|
-
assert.deepEqual(defaults.sessionGateSessions, []);
|
|
18
|
-
|
|
19
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
20
|
-
const sessions = [
|
|
21
|
-
{ id: "header-origin", header: { origin: "subagent" } },
|
|
22
|
-
{ id: "header-depth", header: { delegationDepth: 2 } },
|
|
23
|
-
{ id: "header-both", header: { parentSession: "captain", origin: "subagent", delegationDepth: 1 } },
|
|
24
|
-
{ id: "request-origin", requestHeader: () => ({ config: { origin: "subagent", delegationDepth: 1 } }) }
|
|
25
|
-
];
|
|
26
|
-
for (const session of sessions) {
|
|
27
|
-
assert.equal(gate.isOff(session), true, session.id);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const firstEvent = { id: "first-event", header: { parentSession: "captain", origin: "subagent" } };
|
|
31
|
-
assert.equal(gate.isOff(firstEvent), true, "header match works before any session/event was observed");
|
|
32
|
-
gate.observe(firstEvent);
|
|
33
|
-
assert.equal(gate.isOff("first-event"), true, "observed child ids remain recognizable in log scans");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// 形状锁:`parentSession` 是「有父会话」,不是「是子代理」。dsh-session 的
|
|
37
|
-
// sessions.fork() 给**主会话分支**也写 parentSession(且不写 origin / 不带
|
|
38
|
-
// delegationDepth),把它当判据就会让用户 fork 出来的普通会话整体失忆。
|
|
39
|
-
// 本机 641 个真实会话头实测:origin/delegationDepth 命中 455 个子代理、零漏检,
|
|
40
|
-
// parentSession 对子代理识别零贡献、只多判出 3 个 fork 主会话(各有 74–136 条
|
|
41
|
-
// 用户消息、694 次工具调用)。这条用例锁的就是那次误判不许回来。
|
|
42
|
-
test("parentSession alone never gates: forked captain sessions stay fully enabled", () => {
|
|
43
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
44
|
-
const forked = { id: "forked-captain", header: { parentSession: "captain", isSeeded: true, delegationDepth: 0 } };
|
|
45
|
-
assert.equal(gate.isOff(forked), false, "a fork of a captain session is still a captain session");
|
|
46
|
-
assert.equal(gate.isOff({ id: "flat-parent", parentSession: "captain" }), false);
|
|
47
|
-
assert.equal(gate.isOff({ id: "alias-parent", parentSessionId: "captain" }), false);
|
|
48
|
-
assert.equal(gate.isOff({ id: "request-parent", requestHeader: () => ({ config: { parentSession: "captain" } }) }), false);
|
|
49
|
-
gate.observe(forked);
|
|
50
|
-
assert.equal(gate.isOff("forked-captain"), false, "observing a non-child must not record it as one");
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("manual ids survive the subagent switch, master-off is inert, and config is read live", () => {
|
|
54
|
-
const cfg = { sessionGateEnabled: false, sessionGateSubagents: false, sessionGateSessions: ["manual"] };
|
|
55
|
-
const gate = createSessionGate(cfg);
|
|
56
|
-
const manual = { id: "manual" };
|
|
57
|
-
const child = { id: "child", header: { origin: "subagent" } };
|
|
58
|
-
|
|
59
|
-
assert.equal(gate.isOff(manual), false, "the master is the only opt-in");
|
|
60
|
-
cfg.sessionGateEnabled = true;
|
|
61
|
-
assert.equal(gate.isOff(manual), true, "manual id matches even when automatic child detection is off");
|
|
62
|
-
assert.equal(gate.isOff(child), false, "subagent switch independently controls automatic matching");
|
|
63
|
-
cfg.sessionGateSubagents = true;
|
|
64
|
-
assert.equal(gate.isOff(child), true, "the next call sees a live config change");
|
|
65
|
-
cfg.sessionGateEnabled = false;
|
|
66
|
-
assert.equal(gate.isOff(manual), false, "turning the master off restores normal behavior immediately");
|
|
67
|
-
|
|
68
|
-
// 复现 comment 6541389(#541):上面刚把总开关关掉,若在这里直接断言
|
|
69
|
-
// doesNotThrow,isEnabled() 就先把调用挡回去了,id getter 那条保护路径
|
|
70
|
-
// 一次都不会执行——测的是「关掉时不出事」,不是「读不到 id 时不出事」。
|
|
71
|
-
// 所以先开总开关,再断言两件事:不抛,且被当成普通会话(false),
|
|
72
|
-
// 而不是被当成「id 为空所以放行」的巧合。
|
|
73
|
-
cfg.sessionGateEnabled = true;
|
|
74
|
-
const inaccessible = {};
|
|
75
|
-
Object.defineProperty(inaccessible, "id", { get() { throw new Error("host getter"); } });
|
|
76
|
-
assert.doesNotThrow(() => gate.isOff(inaccessible));
|
|
77
|
-
assert.equal(gate.isOff(inaccessible), false, "an unreadable id is an ordinary session, not a gated one");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// #539 原文:「子会话在首次 session/event 之前就可能组装上下文,故必须直接检查
|
|
81
|
-
// 传入 session,不能只靠事件后记录 ID」。所以「拿不到 id」不能当作「不是无痕
|
|
82
|
-
// 会话」——那一行早退正是这条要求最容易失守的地方(注入面在上下文组装期跑的
|
|
83
|
-
// 就是这条路径)。手动名单仍然依赖 id,那是名单本身的形状,不是缺陷。
|
|
84
|
-
test("a subagent session without a usable id is still gated", () => {
|
|
85
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
86
|
-
assert.equal(gate.isOff({ header: { origin: "subagent" } }), true, "id-less subagent header");
|
|
87
|
-
assert.equal(gate.isOff({ header: { delegationDepth: 1 } }), true, "id-less delegation depth");
|
|
88
|
-
assert.equal(gate.isOff({ id: "", header: { origin: "subagent" } }), true, "empty id is the same as missing");
|
|
89
|
-
assert.equal(gate.isOff({}), false, "no id and no child mark is an ordinary session");
|
|
90
|
-
assert.equal(gate.isOff(null), false);
|
|
91
|
-
assert.equal(gate.isOff(undefined), false);
|
|
92
|
-
const offSwitch = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: false });
|
|
93
|
-
assert.equal(offSwitch.isOff({ header: { origin: "subagent" } }), false, "the subagent switch still wins");
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// 记账表只服务「日志扫描拿得到 id、却解析不出头」这一条退路,所以它必须有界:
|
|
97
|
-
// 长驻宿主跑上几百个子代理后只涨不落会把每个 id 永久留在内存里,而淘汰最旧的
|
|
98
|
-
// 代价只是那一次日志过滤少认一个会话,活路径永远直接用会话对象判定。
|
|
99
|
-
test("tracked child ids stay bounded and eviction only costs the log-scan fallback", () => {
|
|
100
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
101
|
-
for (let i = 0; i < 600; i += 1) gate.observe({ id: `child-${i}`, header: { origin: "subagent" } });
|
|
102
|
-
assert.equal(gate.isOff("child-599"), true, "the newest child is still tracked");
|
|
103
|
-
assert.equal(gate.isOff("child-0"), false, "the oldest id was evicted instead of kept forever");
|
|
104
|
-
gate.observe({ id: "child-599", header: { origin: "subagent" } });
|
|
105
|
-
assert.equal(gate.isOff("child-599"), true, "re-observing the same id stays stable");
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
function injectionSetup() {
|
|
109
|
-
const store = createStore(":memory:");
|
|
110
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
111
|
-
const settings = createSettings(store.db);
|
|
112
|
-
service.saveWithDedupe({ type: "preference", title: "语言", content: "中文", importance: 5 });
|
|
113
|
-
settings.setProfile("用户画像正文");
|
|
114
|
-
settings.setRules(["先给结论"]);
|
|
115
|
-
const contexts = [];
|
|
116
|
-
const sections = [];
|
|
117
|
-
const ctx = {
|
|
118
|
-
systemPrompt: {
|
|
119
|
-
context(definition) { contexts.push(definition); return () => {}; },
|
|
120
|
-
section(definition) { sections.push(definition); return () => {}; }
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
const gate = createSessionGate({
|
|
124
|
-
sessionGateEnabled: true,
|
|
125
|
-
sessionGateSubagents: true,
|
|
126
|
-
sessionGateSessions: ["private"]
|
|
127
|
-
});
|
|
128
|
-
createInjector(ctx, service, settings, { injectGuidanceEnabled: true }, null, gate);
|
|
129
|
-
return { store, contexts, sections, gate };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
test("all system-prompt surfaces are empty for incognito and remain available to captain", () => {
|
|
133
|
-
const { contexts, sections, gate } = injectionSetup();
|
|
134
|
-
const memory = contexts.find((item) => item.name === "memory");
|
|
135
|
-
const userSettings = contexts.find((item) => item.name === "user-settings");
|
|
136
|
-
const guide = sections.find((item) => item.name === "memory-guide");
|
|
137
|
-
const privateContext = { agent: { session: { id: "private" } } };
|
|
138
|
-
const childContext = { agent: { session: { id: "worker", header: { parentSession: "captain", origin: "subagent" } } } };
|
|
139
|
-
const captainContext = { agent: { session: { id: "captain", header: { origin: "main" } } } };
|
|
140
|
-
|
|
141
|
-
assert.equal(memory.text(privateContext), "");
|
|
142
|
-
assert.equal(userSettings.text(privateContext), "");
|
|
143
|
-
assert.equal(guide.text(privateContext), "");
|
|
144
|
-
assert.equal(memory.text(childContext), "");
|
|
145
|
-
assert.equal(userSettings.text(childContext), "");
|
|
146
|
-
assert.equal(guide.text(childContext), "");
|
|
147
|
-
|
|
148
|
-
assert.match(memory.text(captainContext), /\[记忆库\]/);
|
|
149
|
-
assert.match(userSettings.text(captainContext), /用户画像正文/);
|
|
150
|
-
assert.equal(guide.text(captainContext), MEMORY_GUIDE_SECTION);
|
|
151
|
-
gate.observe(childContext.agent.session);
|
|
152
|
-
assert.equal(gate.isOff("worker"), true);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("every registered memory tool rejects before side effects for an incognito session", async () => {
|
|
156
|
-
const store = createStore(":memory:");
|
|
157
|
-
const baseService = createService({ store, mirror: null, config: {} });
|
|
158
|
-
let serviceCalls = 0;
|
|
159
|
-
const service = new Proxy(baseService, {
|
|
160
|
-
get(target, key, receiver) {
|
|
161
|
-
const value = Reflect.get(target, key, receiver);
|
|
162
|
-
if (typeof value !== "function") return value;
|
|
163
|
-
return (...args) => {
|
|
164
|
-
serviceCalls += 1;
|
|
165
|
-
return value.apply(target, args);
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
const registered = [];
|
|
170
|
-
const registry = { register(tool) { registered.push(tool); return () => {}; } };
|
|
171
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
172
|
-
createTools({ tools: registry }, service, {}, null, gate);
|
|
173
|
-
const exec = { agent: { session: { id: "private" } } };
|
|
174
|
-
|
|
175
|
-
assert.ok(registered.length >= 11);
|
|
176
|
-
for (const tool of registered) {
|
|
177
|
-
await assert.rejects(tool.execute({}, exec), /incognito session/, tool.name);
|
|
178
|
-
}
|
|
179
|
-
assert.equal(serviceCalls, 0, "rejected calls do not reach memory service reads or writes");
|
|
180
|
-
assert.equal(store.count(), 0, "no memory rows were written through an executor");
|
|
181
|
-
assert.equal(store.db.prepare("SELECT COUNT(*) AS n FROM scope_state").get().n, 0, "set_scope_state also rejects before writing");
|
|
182
|
-
|
|
183
|
-
const captainSave = registered.find((tool) => tool.name === "memory_save");
|
|
184
|
-
const result = await captainSave.execute(
|
|
185
|
-
{ type: "preference", title: "captain", content: "可用", importance: 4 },
|
|
186
|
-
{ agent: { session: { id: "captain", header: { origin: "main" } } } }
|
|
187
|
-
);
|
|
188
|
-
assert.equal(result.action, "created", "normal captain tool use is unaffected");
|
|
189
|
-
assert.equal(store.count(), 1);
|
|
190
|
-
store.close();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("retained tool executors use the latest gate after live registry reapply", async () => {
|
|
194
|
-
const store = createStore(":memory:");
|
|
195
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
196
|
-
const registered = [];
|
|
197
|
-
const registry = { register(tool) { registered.push(tool); return () => {}; } };
|
|
198
|
-
const initialGate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
199
|
-
createTools({ tools: registry }, service, {}, null, initialGate);
|
|
200
|
-
const retainedSave = registered.find((tool) => tool.name === "memory_save");
|
|
201
|
-
const privateExec = { agent: { session: { id: "private" } } };
|
|
202
|
-
const args = { type: "preference", title: "live gate", content: "check", importance: 4 };
|
|
203
|
-
|
|
204
|
-
await assert.rejects(retainedSave.execute(args, privateExec), /incognito session/);
|
|
205
|
-
const openGate = createSessionGate({ sessionGateEnabled: false });
|
|
206
|
-
createTools({ tools: registry }, service, {}, null, openGate);
|
|
207
|
-
assert.equal(registered.length, 12, "live reapply keeps the host's existing tool registrations");
|
|
208
|
-
assert.equal((await retainedSave.execute(args, privateExec)).action, "created", "off restores the original tool behavior");
|
|
209
|
-
const closedGate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
210
|
-
createTools({ tools: registry }, service, {}, null, closedGate);
|
|
211
|
-
await assert.rejects(
|
|
212
|
-
retainedSave.execute({ ...args, content: "blocked" }, privateExec),
|
|
213
|
-
/incognito session/,
|
|
214
|
-
"a retained tool uses the gate from the latest live reapply"
|
|
215
|
-
);
|
|
216
|
-
assert.equal(store.count(), 1, "the re-applied gate still runs before any write");
|
|
217
|
-
store.close();
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
function summarySession(id, header = {}) {
|
|
221
|
-
return {
|
|
222
|
-
id,
|
|
223
|
-
header,
|
|
224
|
-
requestHeader: () => ({ config: { provider: "deepseek", model: "deepseek-chat" } }),
|
|
225
|
-
events: [
|
|
226
|
-
{ type: "user/message", data: { source: { kind: "user" }, content: [{ type: "text", text: "选型" }] } },
|
|
227
|
-
{ type: "turn/end", seq: 2 }
|
|
228
|
-
]
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
test("distillation skips incognito sessions before model or storage work", async () => {
|
|
233
|
-
const store = createStore(":memory:");
|
|
234
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
235
|
-
const listeners = [];
|
|
236
|
-
const calls = [];
|
|
237
|
-
const ctx = {
|
|
238
|
-
on(name, handler) { listeners.push({ name, handler }); return () => {}; },
|
|
239
|
-
llm: {
|
|
240
|
-
stream() {
|
|
241
|
-
calls.push(true);
|
|
242
|
-
const output = JSON.stringify([{ type: "decision", title: "选择", content: "落库", importance: 4 }]);
|
|
243
|
-
return (async function* () {
|
|
244
|
-
yield { type: "block-start", block: { type: "text" } };
|
|
245
|
-
yield { type: "text-delta", delta: output };
|
|
246
|
-
yield { type: "block-end", block: { type: "text" } };
|
|
247
|
-
yield { type: "finish", kind: "ok" };
|
|
248
|
-
})();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
253
|
-
const summarizer = createSummarizer(ctx, service, { autoSummarize: true, distillRateLimitIntervalMs: 0 }, {}, gate);
|
|
254
|
-
const handler = listeners.find((item) => item.name === "session/event").handler;
|
|
255
|
-
|
|
256
|
-
await handler(summarySession("private"), { type: "turn/end", seq: 2 });
|
|
257
|
-
assert.equal(calls.length, 0);
|
|
258
|
-
assert.equal(store.count(), 0);
|
|
259
|
-
await handler(summarySession("captain", { origin: "main" }), { type: "turn/end", seq: 2 });
|
|
260
|
-
assert.equal(calls.length, 1);
|
|
261
|
-
assert.equal(store.count(), 1, "captain still distills normally");
|
|
262
|
-
summarizer.dispose();
|
|
263
|
-
store.close();
|
|
264
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { Config } from "../src/config.js";
|
|
4
|
+
import { createSessionGate } from "../src/session-gate.js";
|
|
5
|
+
import { createStore } from "../src/store.js";
|
|
6
|
+
import { createService } from "../src/service.js";
|
|
7
|
+
import { createSettings } from "../src/settings.js";
|
|
8
|
+
import { createInjector } from "../src/inject.js";
|
|
9
|
+
import { MEMORY_GUIDE_SECTION } from "../src/guide.js";
|
|
10
|
+
import { createTools } from "../src/tools.js";
|
|
11
|
+
import { createSummarizer } from "../src/summarize.js";
|
|
12
|
+
|
|
13
|
+
test("session gate defaults off and gates subagents by the host's delegation marks", () => {
|
|
14
|
+
const defaults = Config({});
|
|
15
|
+
assert.equal(defaults.sessionGateEnabled, false);
|
|
16
|
+
assert.equal(defaults.sessionGateSubagents, true);
|
|
17
|
+
assert.deepEqual(defaults.sessionGateSessions, []);
|
|
18
|
+
|
|
19
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
20
|
+
const sessions = [
|
|
21
|
+
{ id: "header-origin", header: { origin: "subagent" } },
|
|
22
|
+
{ id: "header-depth", header: { delegationDepth: 2 } },
|
|
23
|
+
{ id: "header-both", header: { parentSession: "captain", origin: "subagent", delegationDepth: 1 } },
|
|
24
|
+
{ id: "request-origin", requestHeader: () => ({ config: { origin: "subagent", delegationDepth: 1 } }) }
|
|
25
|
+
];
|
|
26
|
+
for (const session of sessions) {
|
|
27
|
+
assert.equal(gate.isOff(session), true, session.id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const firstEvent = { id: "first-event", header: { parentSession: "captain", origin: "subagent" } };
|
|
31
|
+
assert.equal(gate.isOff(firstEvent), true, "header match works before any session/event was observed");
|
|
32
|
+
gate.observe(firstEvent);
|
|
33
|
+
assert.equal(gate.isOff("first-event"), true, "observed child ids remain recognizable in log scans");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 形状锁:`parentSession` 是「有父会话」,不是「是子代理」。dsh-session 的
|
|
37
|
+
// sessions.fork() 给**主会话分支**也写 parentSession(且不写 origin / 不带
|
|
38
|
+
// delegationDepth),把它当判据就会让用户 fork 出来的普通会话整体失忆。
|
|
39
|
+
// 本机 641 个真实会话头实测:origin/delegationDepth 命中 455 个子代理、零漏检,
|
|
40
|
+
// parentSession 对子代理识别零贡献、只多判出 3 个 fork 主会话(各有 74–136 条
|
|
41
|
+
// 用户消息、694 次工具调用)。这条用例锁的就是那次误判不许回来。
|
|
42
|
+
test("parentSession alone never gates: forked captain sessions stay fully enabled", () => {
|
|
43
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
44
|
+
const forked = { id: "forked-captain", header: { parentSession: "captain", isSeeded: true, delegationDepth: 0 } };
|
|
45
|
+
assert.equal(gate.isOff(forked), false, "a fork of a captain session is still a captain session");
|
|
46
|
+
assert.equal(gate.isOff({ id: "flat-parent", parentSession: "captain" }), false);
|
|
47
|
+
assert.equal(gate.isOff({ id: "alias-parent", parentSessionId: "captain" }), false);
|
|
48
|
+
assert.equal(gate.isOff({ id: "request-parent", requestHeader: () => ({ config: { parentSession: "captain" } }) }), false);
|
|
49
|
+
gate.observe(forked);
|
|
50
|
+
assert.equal(gate.isOff("forked-captain"), false, "observing a non-child must not record it as one");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("manual ids survive the subagent switch, master-off is inert, and config is read live", () => {
|
|
54
|
+
const cfg = { sessionGateEnabled: false, sessionGateSubagents: false, sessionGateSessions: ["manual"] };
|
|
55
|
+
const gate = createSessionGate(cfg);
|
|
56
|
+
const manual = { id: "manual" };
|
|
57
|
+
const child = { id: "child", header: { origin: "subagent" } };
|
|
58
|
+
|
|
59
|
+
assert.equal(gate.isOff(manual), false, "the master is the only opt-in");
|
|
60
|
+
cfg.sessionGateEnabled = true;
|
|
61
|
+
assert.equal(gate.isOff(manual), true, "manual id matches even when automatic child detection is off");
|
|
62
|
+
assert.equal(gate.isOff(child), false, "subagent switch independently controls automatic matching");
|
|
63
|
+
cfg.sessionGateSubagents = true;
|
|
64
|
+
assert.equal(gate.isOff(child), true, "the next call sees a live config change");
|
|
65
|
+
cfg.sessionGateEnabled = false;
|
|
66
|
+
assert.equal(gate.isOff(manual), false, "turning the master off restores normal behavior immediately");
|
|
67
|
+
|
|
68
|
+
// 复现 comment 6541389(#541):上面刚把总开关关掉,若在这里直接断言
|
|
69
|
+
// doesNotThrow,isEnabled() 就先把调用挡回去了,id getter 那条保护路径
|
|
70
|
+
// 一次都不会执行——测的是「关掉时不出事」,不是「读不到 id 时不出事」。
|
|
71
|
+
// 所以先开总开关,再断言两件事:不抛,且被当成普通会话(false),
|
|
72
|
+
// 而不是被当成「id 为空所以放行」的巧合。
|
|
73
|
+
cfg.sessionGateEnabled = true;
|
|
74
|
+
const inaccessible = {};
|
|
75
|
+
Object.defineProperty(inaccessible, "id", { get() { throw new Error("host getter"); } });
|
|
76
|
+
assert.doesNotThrow(() => gate.isOff(inaccessible));
|
|
77
|
+
assert.equal(gate.isOff(inaccessible), false, "an unreadable id is an ordinary session, not a gated one");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// #539 原文:「子会话在首次 session/event 之前就可能组装上下文,故必须直接检查
|
|
81
|
+
// 传入 session,不能只靠事件后记录 ID」。所以「拿不到 id」不能当作「不是无痕
|
|
82
|
+
// 会话」——那一行早退正是这条要求最容易失守的地方(注入面在上下文组装期跑的
|
|
83
|
+
// 就是这条路径)。手动名单仍然依赖 id,那是名单本身的形状,不是缺陷。
|
|
84
|
+
test("a subagent session without a usable id is still gated", () => {
|
|
85
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
86
|
+
assert.equal(gate.isOff({ header: { origin: "subagent" } }), true, "id-less subagent header");
|
|
87
|
+
assert.equal(gate.isOff({ header: { delegationDepth: 1 } }), true, "id-less delegation depth");
|
|
88
|
+
assert.equal(gate.isOff({ id: "", header: { origin: "subagent" } }), true, "empty id is the same as missing");
|
|
89
|
+
assert.equal(gate.isOff({}), false, "no id and no child mark is an ordinary session");
|
|
90
|
+
assert.equal(gate.isOff(null), false);
|
|
91
|
+
assert.equal(gate.isOff(undefined), false);
|
|
92
|
+
const offSwitch = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: false });
|
|
93
|
+
assert.equal(offSwitch.isOff({ header: { origin: "subagent" } }), false, "the subagent switch still wins");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// 记账表只服务「日志扫描拿得到 id、却解析不出头」这一条退路,所以它必须有界:
|
|
97
|
+
// 长驻宿主跑上几百个子代理后只涨不落会把每个 id 永久留在内存里,而淘汰最旧的
|
|
98
|
+
// 代价只是那一次日志过滤少认一个会话,活路径永远直接用会话对象判定。
|
|
99
|
+
test("tracked child ids stay bounded and eviction only costs the log-scan fallback", () => {
|
|
100
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSubagents: true });
|
|
101
|
+
for (let i = 0; i < 600; i += 1) gate.observe({ id: `child-${i}`, header: { origin: "subagent" } });
|
|
102
|
+
assert.equal(gate.isOff("child-599"), true, "the newest child is still tracked");
|
|
103
|
+
assert.equal(gate.isOff("child-0"), false, "the oldest id was evicted instead of kept forever");
|
|
104
|
+
gate.observe({ id: "child-599", header: { origin: "subagent" } });
|
|
105
|
+
assert.equal(gate.isOff("child-599"), true, "re-observing the same id stays stable");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
function injectionSetup() {
|
|
109
|
+
const store = createStore(":memory:");
|
|
110
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
111
|
+
const settings = createSettings(store.db);
|
|
112
|
+
service.saveWithDedupe({ type: "preference", title: "语言", content: "中文", importance: 5 });
|
|
113
|
+
settings.setProfile("用户画像正文");
|
|
114
|
+
settings.setRules(["先给结论"]);
|
|
115
|
+
const contexts = [];
|
|
116
|
+
const sections = [];
|
|
117
|
+
const ctx = {
|
|
118
|
+
systemPrompt: {
|
|
119
|
+
context(definition) { contexts.push(definition); return () => {}; },
|
|
120
|
+
section(definition) { sections.push(definition); return () => {}; }
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const gate = createSessionGate({
|
|
124
|
+
sessionGateEnabled: true,
|
|
125
|
+
sessionGateSubagents: true,
|
|
126
|
+
sessionGateSessions: ["private"]
|
|
127
|
+
});
|
|
128
|
+
createInjector(ctx, service, settings, { injectGuidanceEnabled: true }, null, gate);
|
|
129
|
+
return { store, contexts, sections, gate };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
test("all system-prompt surfaces are empty for incognito and remain available to captain", () => {
|
|
133
|
+
const { contexts, sections, gate } = injectionSetup();
|
|
134
|
+
const memory = contexts.find((item) => item.name === "memory");
|
|
135
|
+
const userSettings = contexts.find((item) => item.name === "user-settings");
|
|
136
|
+
const guide = sections.find((item) => item.name === "memory-guide");
|
|
137
|
+
const privateContext = { agent: { session: { id: "private" } } };
|
|
138
|
+
const childContext = { agent: { session: { id: "worker", header: { parentSession: "captain", origin: "subagent" } } } };
|
|
139
|
+
const captainContext = { agent: { session: { id: "captain", header: { origin: "main" } } } };
|
|
140
|
+
|
|
141
|
+
assert.equal(memory.text(privateContext), "");
|
|
142
|
+
assert.equal(userSettings.text(privateContext), "");
|
|
143
|
+
assert.equal(guide.text(privateContext), "");
|
|
144
|
+
assert.equal(memory.text(childContext), "");
|
|
145
|
+
assert.equal(userSettings.text(childContext), "");
|
|
146
|
+
assert.equal(guide.text(childContext), "");
|
|
147
|
+
|
|
148
|
+
assert.match(memory.text(captainContext), /\[记忆库\]/);
|
|
149
|
+
assert.match(userSettings.text(captainContext), /用户画像正文/);
|
|
150
|
+
assert.equal(guide.text(captainContext), MEMORY_GUIDE_SECTION);
|
|
151
|
+
gate.observe(childContext.agent.session);
|
|
152
|
+
assert.equal(gate.isOff("worker"), true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("every registered memory tool rejects before side effects for an incognito session", async () => {
|
|
156
|
+
const store = createStore(":memory:");
|
|
157
|
+
const baseService = createService({ store, mirror: null, config: {} });
|
|
158
|
+
let serviceCalls = 0;
|
|
159
|
+
const service = new Proxy(baseService, {
|
|
160
|
+
get(target, key, receiver) {
|
|
161
|
+
const value = Reflect.get(target, key, receiver);
|
|
162
|
+
if (typeof value !== "function") return value;
|
|
163
|
+
return (...args) => {
|
|
164
|
+
serviceCalls += 1;
|
|
165
|
+
return value.apply(target, args);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const registered = [];
|
|
170
|
+
const registry = { register(tool) { registered.push(tool); return () => {}; } };
|
|
171
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
172
|
+
createTools({ tools: registry }, service, {}, null, gate);
|
|
173
|
+
const exec = { agent: { session: { id: "private" } } };
|
|
174
|
+
|
|
175
|
+
assert.ok(registered.length >= 11);
|
|
176
|
+
for (const tool of registered) {
|
|
177
|
+
await assert.rejects(tool.execute({}, exec), /incognito session/, tool.name);
|
|
178
|
+
}
|
|
179
|
+
assert.equal(serviceCalls, 0, "rejected calls do not reach memory service reads or writes");
|
|
180
|
+
assert.equal(store.count(), 0, "no memory rows were written through an executor");
|
|
181
|
+
assert.equal(store.db.prepare("SELECT COUNT(*) AS n FROM scope_state").get().n, 0, "set_scope_state also rejects before writing");
|
|
182
|
+
|
|
183
|
+
const captainSave = registered.find((tool) => tool.name === "memory_save");
|
|
184
|
+
const result = await captainSave.execute(
|
|
185
|
+
{ type: "preference", title: "captain", content: "可用", importance: 4 },
|
|
186
|
+
{ agent: { session: { id: "captain", header: { origin: "main" } } } }
|
|
187
|
+
);
|
|
188
|
+
assert.equal(result.action, "created", "normal captain tool use is unaffected");
|
|
189
|
+
assert.equal(store.count(), 1);
|
|
190
|
+
store.close();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("retained tool executors use the latest gate after live registry reapply", async () => {
|
|
194
|
+
const store = createStore(":memory:");
|
|
195
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
196
|
+
const registered = [];
|
|
197
|
+
const registry = { register(tool) { registered.push(tool); return () => {}; } };
|
|
198
|
+
const initialGate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
199
|
+
createTools({ tools: registry }, service, {}, null, initialGate);
|
|
200
|
+
const retainedSave = registered.find((tool) => tool.name === "memory_save");
|
|
201
|
+
const privateExec = { agent: { session: { id: "private" } } };
|
|
202
|
+
const args = { type: "preference", title: "live gate", content: "check", importance: 4 };
|
|
203
|
+
|
|
204
|
+
await assert.rejects(retainedSave.execute(args, privateExec), /incognito session/);
|
|
205
|
+
const openGate = createSessionGate({ sessionGateEnabled: false });
|
|
206
|
+
createTools({ tools: registry }, service, {}, null, openGate);
|
|
207
|
+
assert.equal(registered.length, 12, "live reapply keeps the host's existing tool registrations");
|
|
208
|
+
assert.equal((await retainedSave.execute(args, privateExec)).action, "created", "off restores the original tool behavior");
|
|
209
|
+
const closedGate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
210
|
+
createTools({ tools: registry }, service, {}, null, closedGate);
|
|
211
|
+
await assert.rejects(
|
|
212
|
+
retainedSave.execute({ ...args, content: "blocked" }, privateExec),
|
|
213
|
+
/incognito session/,
|
|
214
|
+
"a retained tool uses the gate from the latest live reapply"
|
|
215
|
+
);
|
|
216
|
+
assert.equal(store.count(), 1, "the re-applied gate still runs before any write");
|
|
217
|
+
store.close();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
function summarySession(id, header = {}) {
|
|
221
|
+
return {
|
|
222
|
+
id,
|
|
223
|
+
header,
|
|
224
|
+
requestHeader: () => ({ config: { provider: "deepseek", model: "deepseek-chat" } }),
|
|
225
|
+
events: [
|
|
226
|
+
{ type: "user/message", data: { source: { kind: "user" }, content: [{ type: "text", text: "选型" }] } },
|
|
227
|
+
{ type: "turn/end", seq: 2 }
|
|
228
|
+
]
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
test("distillation skips incognito sessions before model or storage work", async () => {
|
|
233
|
+
const store = createStore(":memory:");
|
|
234
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
235
|
+
const listeners = [];
|
|
236
|
+
const calls = [];
|
|
237
|
+
const ctx = {
|
|
238
|
+
on(name, handler) { listeners.push({ name, handler }); return () => {}; },
|
|
239
|
+
llm: {
|
|
240
|
+
stream() {
|
|
241
|
+
calls.push(true);
|
|
242
|
+
const output = JSON.stringify([{ type: "decision", title: "选择", content: "落库", importance: 4 }]);
|
|
243
|
+
return (async function* () {
|
|
244
|
+
yield { type: "block-start", block: { type: "text" } };
|
|
245
|
+
yield { type: "text-delta", delta: output };
|
|
246
|
+
yield { type: "block-end", block: { type: "text" } };
|
|
247
|
+
yield { type: "finish", kind: "ok" };
|
|
248
|
+
})();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const gate = createSessionGate({ sessionGateEnabled: true, sessionGateSessions: ["private"] });
|
|
253
|
+
const summarizer = createSummarizer(ctx, service, { autoSummarize: true, distillRateLimitIntervalMs: 0 }, {}, gate);
|
|
254
|
+
const handler = listeners.find((item) => item.name === "session/event").handler;
|
|
255
|
+
|
|
256
|
+
await handler(summarySession("private"), { type: "turn/end", seq: 2 });
|
|
257
|
+
assert.equal(calls.length, 0);
|
|
258
|
+
assert.equal(store.count(), 0);
|
|
259
|
+
await handler(summarySession("captain", { origin: "main" }), { type: "turn/end", seq: 2 });
|
|
260
|
+
assert.equal(calls.length, 1);
|
|
261
|
+
assert.equal(store.count(), 1, "captain still distills normally");
|
|
262
|
+
summarizer.dispose();
|
|
263
|
+
store.close();
|
|
264
|
+
});
|