@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,83 +1,83 @@
|
|
|
1
|
-
// 修复一:人格寻址的兜底口径。default 卡的存在理由是「这一帧解析不到会话身份」,
|
|
2
|
-
// 而不是「所有没建卡的 agent 都该拿到它」——后者会把某个人格无条件塞进外部 bot 的
|
|
3
|
-
// 会话(DS Bot 那类:它自己带身份与设定,两处身份声明撞在同一帧里)。
|
|
4
|
-
// 现状(personaStrictAddressing 缺省 = false)已由 test/persona-inject.test.js:113
|
|
5
|
-
// 与 :124 钉住,本文件第一条照样锁它——这是 opt-in,不是把别人的行为改了。
|
|
6
|
-
import test from "node:test";
|
|
7
|
-
import assert from "node:assert/strict";
|
|
8
|
-
import { createStore } from "../src/store.js";
|
|
9
|
-
import { createService } from "../src/service.js";
|
|
10
|
-
import { createInjector } from "../src/inject.js";
|
|
11
|
-
import { createSettings } from "../src/settings.js";
|
|
12
|
-
|
|
13
|
-
function setup(over = {}) {
|
|
14
|
-
const store = createStore(":memory:");
|
|
15
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
16
|
-
const settings = createSettings(store.db);
|
|
17
|
-
const contexts = [];
|
|
18
|
-
const ctx = {
|
|
19
|
-
systemPrompt: {
|
|
20
|
-
context(def) {
|
|
21
|
-
contexts.push(def);
|
|
22
|
-
return () => {};
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
createInjector(ctx, service, settings, { maxInjectedItems: 3, importanceThreshold: 3, ...over });
|
|
27
|
-
return { store, settings, settingsCtx: contexts.find((c) => c.name === "user-settings") };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const withPreset = (agentPreset) => ({ agent: { session: { id: "s1", header: { agentPreset } } } });
|
|
31
|
-
|
|
32
|
-
// 种子:default 卡(Amarre)开着 + 一张 zephyr 卡。
|
|
33
|
-
function seed(settings, zephyrEnabled = true) {
|
|
34
|
-
settings.setPersona({ name: "Amarre", personality: "轻松幽默", enabled: true });
|
|
35
|
-
settings.setPersonaFor("zephyr", { name: "泽菲尔", personality: "冷危", enabled: zephyrEnabled });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
test("缺省(开关关):没建卡的身份仍落 default——本 PR 不改默认行为", () => {
|
|
39
|
-
const { store, settings, settingsCtx } = setup({ personaEnabled: true });
|
|
40
|
-
seed(settings);
|
|
41
|
-
const text = settingsCtx.text(withPreset("unknown-bot"));
|
|
42
|
-
assert.ok(text.includes("Amarre"), "personaStrictAddressing 缺省时行为逐字节不变");
|
|
43
|
-
store.close();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("strict on:没建卡的身份不注入任何人格行", () => {
|
|
47
|
-
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
48
|
-
seed(settings);
|
|
49
|
-
settings.setProfile("用户是规划背景");
|
|
50
|
-
const text = settingsCtx.text(withPreset("unknown-bot"));
|
|
51
|
-
assert.ok(!text.includes("Amarre"), "外部身份不该收到别人的人格");
|
|
52
|
-
assert.ok(!text.includes("你的人格名字"), "无人格时整段人格行都不出现,不是留个空标签");
|
|
53
|
-
store.close();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("strict on:显式关掉的卡不再回落 default——这让「关掉这张卡」第一次有意义", () => {
|
|
57
|
-
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
58
|
-
seed(settings, false);
|
|
59
|
-
const text = settingsCtx.text(withPreset("zephyr"));
|
|
60
|
-
assert.ok(!text.includes("泽菲尔"), "关掉的卡本身不注入");
|
|
61
|
-
assert.ok(!text.includes("Amarre"), "关掉之后落回 default 等于没关");
|
|
62
|
-
store.close();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("strict on:解析不到身份(无 session.header)照旧用 default 卡", () => {
|
|
66
|
-
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
67
|
-
seed(settings);
|
|
68
|
-
const text = settingsCtx.text({});
|
|
69
|
-
assert.ok(text.includes("你的人格名字:Amarre"), "无名会话正是 default 卡的受众,strict 不许把它一起关掉");
|
|
70
|
-
store.close();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("strict on:命中的卡照常注入,画像与规则不受影响", () => {
|
|
74
|
-
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
75
|
-
seed(settings);
|
|
76
|
-
settings.setProfile("用户是规划背景");
|
|
77
|
-
settings.setRules(["不提及用户名"]);
|
|
78
|
-
const hit = settingsCtx.text(withPreset("zephyr"));
|
|
79
|
-
assert.ok(hit.includes("你的人格名字:泽菲尔"), "身份命中时行为与现状一致");
|
|
80
|
-
assert.ok(hit.includes("- 用户画像:用户是规划背景"), "画像恒注入:本开关只管人格行");
|
|
81
|
-
assert.ok(hit.includes("- 规则:不提及用户名"), "规则恒注入:同上");
|
|
82
|
-
store.close();
|
|
83
|
-
});
|
|
1
|
+
// 修复一:人格寻址的兜底口径。default 卡的存在理由是「这一帧解析不到会话身份」,
|
|
2
|
+
// 而不是「所有没建卡的 agent 都该拿到它」——后者会把某个人格无条件塞进外部 bot 的
|
|
3
|
+
// 会话(DS Bot 那类:它自己带身份与设定,两处身份声明撞在同一帧里)。
|
|
4
|
+
// 现状(personaStrictAddressing 缺省 = false)已由 test/persona-inject.test.js:113
|
|
5
|
+
// 与 :124 钉住,本文件第一条照样锁它——这是 opt-in,不是把别人的行为改了。
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { createStore } from "../src/store.js";
|
|
9
|
+
import { createService } from "../src/service.js";
|
|
10
|
+
import { createInjector } from "../src/inject.js";
|
|
11
|
+
import { createSettings } from "../src/settings.js";
|
|
12
|
+
|
|
13
|
+
function setup(over = {}) {
|
|
14
|
+
const store = createStore(":memory:");
|
|
15
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
16
|
+
const settings = createSettings(store.db);
|
|
17
|
+
const contexts = [];
|
|
18
|
+
const ctx = {
|
|
19
|
+
systemPrompt: {
|
|
20
|
+
context(def) {
|
|
21
|
+
contexts.push(def);
|
|
22
|
+
return () => {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
createInjector(ctx, service, settings, { maxInjectedItems: 3, importanceThreshold: 3, ...over });
|
|
27
|
+
return { store, settings, settingsCtx: contexts.find((c) => c.name === "user-settings") };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const withPreset = (agentPreset) => ({ agent: { session: { id: "s1", header: { agentPreset } } } });
|
|
31
|
+
|
|
32
|
+
// 种子:default 卡(Amarre)开着 + 一张 zephyr 卡。
|
|
33
|
+
function seed(settings, zephyrEnabled = true) {
|
|
34
|
+
settings.setPersona({ name: "Amarre", personality: "轻松幽默", enabled: true });
|
|
35
|
+
settings.setPersonaFor("zephyr", { name: "泽菲尔", personality: "冷危", enabled: zephyrEnabled });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
test("缺省(开关关):没建卡的身份仍落 default——本 PR 不改默认行为", () => {
|
|
39
|
+
const { store, settings, settingsCtx } = setup({ personaEnabled: true });
|
|
40
|
+
seed(settings);
|
|
41
|
+
const text = settingsCtx.text(withPreset("unknown-bot"));
|
|
42
|
+
assert.ok(text.includes("Amarre"), "personaStrictAddressing 缺省时行为逐字节不变");
|
|
43
|
+
store.close();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("strict on:没建卡的身份不注入任何人格行", () => {
|
|
47
|
+
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
48
|
+
seed(settings);
|
|
49
|
+
settings.setProfile("用户是规划背景");
|
|
50
|
+
const text = settingsCtx.text(withPreset("unknown-bot"));
|
|
51
|
+
assert.ok(!text.includes("Amarre"), "外部身份不该收到别人的人格");
|
|
52
|
+
assert.ok(!text.includes("你的人格名字"), "无人格时整段人格行都不出现,不是留个空标签");
|
|
53
|
+
store.close();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("strict on:显式关掉的卡不再回落 default——这让「关掉这张卡」第一次有意义", () => {
|
|
57
|
+
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
58
|
+
seed(settings, false);
|
|
59
|
+
const text = settingsCtx.text(withPreset("zephyr"));
|
|
60
|
+
assert.ok(!text.includes("泽菲尔"), "关掉的卡本身不注入");
|
|
61
|
+
assert.ok(!text.includes("Amarre"), "关掉之后落回 default 等于没关");
|
|
62
|
+
store.close();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("strict on:解析不到身份(无 session.header)照旧用 default 卡", () => {
|
|
66
|
+
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
67
|
+
seed(settings);
|
|
68
|
+
const text = settingsCtx.text({});
|
|
69
|
+
assert.ok(text.includes("你的人格名字:Amarre"), "无名会话正是 default 卡的受众,strict 不许把它一起关掉");
|
|
70
|
+
store.close();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("strict on:命中的卡照常注入,画像与规则不受影响", () => {
|
|
74
|
+
const { store, settings, settingsCtx } = setup({ personaEnabled: true, personaStrictAddressing: true });
|
|
75
|
+
seed(settings);
|
|
76
|
+
settings.setProfile("用户是规划背景");
|
|
77
|
+
settings.setRules(["不提及用户名"]);
|
|
78
|
+
const hit = settingsCtx.text(withPreset("zephyr"));
|
|
79
|
+
assert.ok(hit.includes("你的人格名字:泽菲尔"), "身份命中时行为与现状一致");
|
|
80
|
+
assert.ok(hit.includes("- 用户画像:用户是规划背景"), "画像恒注入:本开关只管人格行");
|
|
81
|
+
assert.ok(hit.includes("- 规则:不提及用户名"), "规则恒注入:同上");
|
|
82
|
+
store.close();
|
|
83
|
+
});
|
package/test/persona-api.test.js
CHANGED
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
// Persona API routes: internal (/api/dsh-mneme/persona, requireAuth on write)
|
|
2
|
-
// and standalone (/persona behind the global Bearer gate).
|
|
3
|
-
// Locks: GET reads current value; unauthorized writes are rejected; a corrupt
|
|
4
|
-
// JSON body never lands in the store (design: reports/persona-page-design.md ②).
|
|
5
|
-
// Call shapes mirror test/api.test.js (route registry + FakeRes) and
|
|
6
|
-
// test/standalone-api.test.js (real server on port 0 + fetch).
|
|
7
|
-
import test from "node:test";
|
|
8
|
-
import assert from "node:assert/strict";
|
|
9
|
-
import { EventEmitter } from "node:events";
|
|
10
|
-
import { createStore } from "../src/store.js";
|
|
11
|
-
import { createService } from "../src/service.js";
|
|
12
|
-
import { createApi } from "../src/api.js";
|
|
13
|
-
import { createSettings } from "../src/settings.js";
|
|
14
|
-
import { createStandaloneApi } from "../src/api-standalone.js";
|
|
15
|
-
|
|
16
|
-
class FakeRes extends EventEmitter {
|
|
17
|
-
constructor() { super(); this.statusCode = 200; this.body = ""; }
|
|
18
|
-
writeHead(code, headers) { this.statusCode = code; this.headers = headers; return this; }
|
|
19
|
-
end(text) { this.body = text ?? ""; this.emit("end"); return this; }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function req(path, method = "GET", body = null, headers = {}) {
|
|
23
|
-
const r = new EventEmitter();
|
|
24
|
-
r.url = path;
|
|
25
|
-
r.method = method;
|
|
26
|
-
r.headers = headers;
|
|
27
|
-
if (body !== null) {
|
|
28
|
-
process.nextTick(() => {
|
|
29
|
-
r.emit("data", Buffer.from(typeof body === "string" ? body : JSON.stringify(body)));
|
|
30
|
-
r.emit("end");
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return r;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function setupInternal(apiToken = "") {
|
|
37
|
-
const store = createStore(":memory:");
|
|
38
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
39
|
-
const settings = createSettings(store.db);
|
|
40
|
-
const commands = {
|
|
41
|
-
add: (def) => settings.addCommand(def),
|
|
42
|
-
remove: (id) => settings.removeCommand(id),
|
|
43
|
-
list: () => settings.listCommands()
|
|
44
|
-
};
|
|
45
|
-
const routes = [];
|
|
46
|
-
const ctx = {
|
|
47
|
-
webServer: {
|
|
48
|
-
register(route) {
|
|
49
|
-
routes.push(route);
|
|
50
|
-
return () => {};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
createApi(ctx, service, settings, commands, undefined, undefined, apiToken, {});
|
|
55
|
-
const route = routes.find((r) => r.path === "/api/dsh-mneme/persona");
|
|
56
|
-
return { store, settings, route, apiToken };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ---- internal API(宿主内 /api/dsh-mneme/*) --------------------------------
|
|
60
|
-
|
|
61
|
-
test("internal GET /persona returns current persona without auth", async () => {
|
|
62
|
-
const { store, settings, route } = setupInternal();
|
|
63
|
-
settings.setPersona({ name: "Amarre", enabled: false });
|
|
64
|
-
const res = new FakeRes();
|
|
65
|
-
await route.handler(req("/api/dsh-mneme/persona"), res);
|
|
66
|
-
assert.equal(res.statusCode, 200);
|
|
67
|
-
assert.equal(JSON.parse(res.body).persona.default.name, "Amarre");
|
|
68
|
-
store.close();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test("internal PUT /persona without token is rejected and store untouched", async () => {
|
|
72
|
-
// setupInternal("t"):必须带 token 启动,无 token 的写请求才应被拒
|
|
73
|
-
// (apiToken 为空时 isAuthorized 恒放行——与 /profile 的既有语义一致)。
|
|
74
|
-
const { store, settings, route } = setupInternal("t");
|
|
75
|
-
const res = new FakeRes();
|
|
76
|
-
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "evil" } }), res);
|
|
77
|
-
assert.equal(res.statusCode, 401);
|
|
78
|
-
assert.deepEqual(settings.getPersona(), { default: {}, byAgent: {}, bySession: {} });
|
|
79
|
-
store.close();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("internal PUT /persona with token round-trips; corrupt JSON body does not land", async () => {
|
|
83
|
-
const { store, settings, route } = setupInternal("t");
|
|
84
|
-
const headers = { authorization: "Bearer t" };
|
|
85
|
-
const ok = new FakeRes();
|
|
86
|
-
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "Amarre", enabled: true } }, headers), ok);
|
|
87
|
-
assert.equal(ok.statusCode, 200);
|
|
88
|
-
assert.equal(settings.getPersona().default.name, "Amarre");
|
|
89
|
-
const bad = new FakeRes();
|
|
90
|
-
await route.handler(req("/api/dsh-mneme/persona", "PUT", "not json", headers), bad);
|
|
91
|
-
// 路由层对坏 JSON 容错(parseBody 口径:坏体不清空库内值),原样回显现值
|
|
92
|
-
assert.equal(bad.statusCode, 200);
|
|
93
|
-
assert.equal(settings.getPersona().default.name, "Amarre");
|
|
94
|
-
store.close();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("internal PUT /persona multi-card form replaces fully; legacy form merges default", async () => {
|
|
98
|
-
const { store, settings, route } = setupInternal("t");
|
|
99
|
-
const headers = { authorization: "Bearer t" };
|
|
100
|
-
// 先铺一张 byAgent 卡
|
|
101
|
-
settings.setPersonaFor("zephyr", { name: "泽菲尔", enabled: true });
|
|
102
|
-
// 旧形 PUT(单对象):只动 default、byAgent 保留
|
|
103
|
-
const legacy = new FakeRes();
|
|
104
|
-
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "Amarre-改", enabled: true } }, headers), legacy);
|
|
105
|
-
assert.equal(legacy.statusCode, 200);
|
|
106
|
-
assert.equal(settings.getPersona().default.name, "Amarre-改");
|
|
107
|
-
assert.equal(settings.getPersona().byAgent.zephyr.name, "泽菲尔", "legacy PUT keeps byAgent");
|
|
108
|
-
// 新形 PUT(多卡):全量替换
|
|
109
|
-
const multi = new FakeRes();
|
|
110
|
-
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { default: { name: "默认卡", enabled: true }, byAgent: { zephyr: { name: "泽菲尔-新", enabled: true } } } }, headers), multi);
|
|
111
|
-
assert.equal(multi.statusCode, 200);
|
|
112
|
-
const got = settings.getPersona();
|
|
113
|
-
assert.equal(got.default.name, "默认卡");
|
|
114
|
-
assert.equal(got.byAgent.zephyr.name, "泽菲尔-新");
|
|
115
|
-
store.close();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// ---- standalone API(Bearer 数据面,真端口 + fetch) ------------------------
|
|
119
|
-
|
|
120
|
-
async function setupStandalone() {
|
|
121
|
-
const store = createStore(":memory:");
|
|
122
|
-
const service = createService({ store, mirror: null, config: {} });
|
|
123
|
-
const settings = createSettings(store.db);
|
|
124
|
-
const api = createStandaloneApi({ service, store, config: {}, settings, logger: null, port: 0 });
|
|
125
|
-
await api.ready;
|
|
126
|
-
const base = `http://127.0.0.1:${api.port}`;
|
|
127
|
-
const auth = { authorization: `Bearer ${api.token}` };
|
|
128
|
-
return {
|
|
129
|
-
store, settings, base, auth,
|
|
130
|
-
close: () => {
|
|
131
|
-
api.server.closeIdleConnections?.();
|
|
132
|
-
api.server.close();
|
|
133
|
-
store.close();
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
test("standalone GET/PUT /persona behind Bearer gate round-trips", async () => {
|
|
139
|
-
const t = await setupStandalone();
|
|
140
|
-
try {
|
|
141
|
-
const get1 = await fetch(`${t.base}/persona`, { headers: t.auth });
|
|
142
|
-
assert.equal(get1.status, 200);
|
|
143
|
-
assert.deepEqual((await get1.json()).persona, { default: {}, byAgent: {}, bySession: {} });
|
|
144
|
-
const put = await fetch(`${t.base}/persona`, {
|
|
145
|
-
method: "PUT",
|
|
146
|
-
headers: { ...t.auth, "content-type": "application/json" },
|
|
147
|
-
body: JSON.stringify({ persona: { name: "Zelphi", enabled: false } })
|
|
148
|
-
});
|
|
149
|
-
assert.equal(put.status, 200);
|
|
150
|
-
assert.equal((await put.json()).persona.default.name, "Zelphi");
|
|
151
|
-
const bad = await fetch(`${t.base}/persona`, {
|
|
152
|
-
method: "PUT",
|
|
153
|
-
headers: { ...t.auth, "content-type": "application/json" },
|
|
154
|
-
body: "not json"
|
|
155
|
-
});
|
|
156
|
-
assert.equal(bad.status, 400);
|
|
157
|
-
assert.equal(t.settings.getPersona().default.name, "Zelphi");
|
|
158
|
-
} finally {
|
|
159
|
-
t.close();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("standalone /persona without Bearer is 401", async () => {
|
|
164
|
-
const t = await setupStandalone();
|
|
165
|
-
try {
|
|
166
|
-
const res = await fetch(`${t.base}/persona`, { method: "PUT", body: "{}" });
|
|
167
|
-
assert.equal(res.status, 401);
|
|
168
|
-
assert.deepEqual(t.settings.getPersona(), { default: {}, byAgent: {}, bySession: {} });
|
|
169
|
-
} finally {
|
|
170
|
-
t.close();
|
|
171
|
-
}
|
|
172
|
-
});
|
|
1
|
+
// Persona API routes: internal (/api/dsh-mneme/persona, requireAuth on write)
|
|
2
|
+
// and standalone (/persona behind the global Bearer gate).
|
|
3
|
+
// Locks: GET reads current value; unauthorized writes are rejected; a corrupt
|
|
4
|
+
// JSON body never lands in the store (design: reports/persona-page-design.md ②).
|
|
5
|
+
// Call shapes mirror test/api.test.js (route registry + FakeRes) and
|
|
6
|
+
// test/standalone-api.test.js (real server on port 0 + fetch).
|
|
7
|
+
import test from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { EventEmitter } from "node:events";
|
|
10
|
+
import { createStore } from "../src/store.js";
|
|
11
|
+
import { createService } from "../src/service.js";
|
|
12
|
+
import { createApi } from "../src/api.js";
|
|
13
|
+
import { createSettings } from "../src/settings.js";
|
|
14
|
+
import { createStandaloneApi } from "../src/api-standalone.js";
|
|
15
|
+
|
|
16
|
+
class FakeRes extends EventEmitter {
|
|
17
|
+
constructor() { super(); this.statusCode = 200; this.body = ""; }
|
|
18
|
+
writeHead(code, headers) { this.statusCode = code; this.headers = headers; return this; }
|
|
19
|
+
end(text) { this.body = text ?? ""; this.emit("end"); return this; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function req(path, method = "GET", body = null, headers = {}) {
|
|
23
|
+
const r = new EventEmitter();
|
|
24
|
+
r.url = path;
|
|
25
|
+
r.method = method;
|
|
26
|
+
r.headers = headers;
|
|
27
|
+
if (body !== null) {
|
|
28
|
+
process.nextTick(() => {
|
|
29
|
+
r.emit("data", Buffer.from(typeof body === "string" ? body : JSON.stringify(body)));
|
|
30
|
+
r.emit("end");
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return r;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setupInternal(apiToken = "") {
|
|
37
|
+
const store = createStore(":memory:");
|
|
38
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
39
|
+
const settings = createSettings(store.db);
|
|
40
|
+
const commands = {
|
|
41
|
+
add: (def) => settings.addCommand(def),
|
|
42
|
+
remove: (id) => settings.removeCommand(id),
|
|
43
|
+
list: () => settings.listCommands()
|
|
44
|
+
};
|
|
45
|
+
const routes = [];
|
|
46
|
+
const ctx = {
|
|
47
|
+
webServer: {
|
|
48
|
+
register(route) {
|
|
49
|
+
routes.push(route);
|
|
50
|
+
return () => {};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
createApi(ctx, service, settings, commands, undefined, undefined, apiToken, {});
|
|
55
|
+
const route = routes.find((r) => r.path === "/api/dsh-mneme/persona");
|
|
56
|
+
return { store, settings, route, apiToken };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---- internal API(宿主内 /api/dsh-mneme/*) --------------------------------
|
|
60
|
+
|
|
61
|
+
test("internal GET /persona returns current persona without auth", async () => {
|
|
62
|
+
const { store, settings, route } = setupInternal();
|
|
63
|
+
settings.setPersona({ name: "Amarre", enabled: false });
|
|
64
|
+
const res = new FakeRes();
|
|
65
|
+
await route.handler(req("/api/dsh-mneme/persona"), res);
|
|
66
|
+
assert.equal(res.statusCode, 200);
|
|
67
|
+
assert.equal(JSON.parse(res.body).persona.default.name, "Amarre");
|
|
68
|
+
store.close();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("internal PUT /persona without token is rejected and store untouched", async () => {
|
|
72
|
+
// setupInternal("t"):必须带 token 启动,无 token 的写请求才应被拒
|
|
73
|
+
// (apiToken 为空时 isAuthorized 恒放行——与 /profile 的既有语义一致)。
|
|
74
|
+
const { store, settings, route } = setupInternal("t");
|
|
75
|
+
const res = new FakeRes();
|
|
76
|
+
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "evil" } }), res);
|
|
77
|
+
assert.equal(res.statusCode, 401);
|
|
78
|
+
assert.deepEqual(settings.getPersona(), { default: {}, byAgent: {}, bySession: {} });
|
|
79
|
+
store.close();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("internal PUT /persona with token round-trips; corrupt JSON body does not land", async () => {
|
|
83
|
+
const { store, settings, route } = setupInternal("t");
|
|
84
|
+
const headers = { authorization: "Bearer t" };
|
|
85
|
+
const ok = new FakeRes();
|
|
86
|
+
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "Amarre", enabled: true } }, headers), ok);
|
|
87
|
+
assert.equal(ok.statusCode, 200);
|
|
88
|
+
assert.equal(settings.getPersona().default.name, "Amarre");
|
|
89
|
+
const bad = new FakeRes();
|
|
90
|
+
await route.handler(req("/api/dsh-mneme/persona", "PUT", "not json", headers), bad);
|
|
91
|
+
// 路由层对坏 JSON 容错(parseBody 口径:坏体不清空库内值),原样回显现值
|
|
92
|
+
assert.equal(bad.statusCode, 200);
|
|
93
|
+
assert.equal(settings.getPersona().default.name, "Amarre");
|
|
94
|
+
store.close();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("internal PUT /persona multi-card form replaces fully; legacy form merges default", async () => {
|
|
98
|
+
const { store, settings, route } = setupInternal("t");
|
|
99
|
+
const headers = { authorization: "Bearer t" };
|
|
100
|
+
// 先铺一张 byAgent 卡
|
|
101
|
+
settings.setPersonaFor("zephyr", { name: "泽菲尔", enabled: true });
|
|
102
|
+
// 旧形 PUT(单对象):只动 default、byAgent 保留
|
|
103
|
+
const legacy = new FakeRes();
|
|
104
|
+
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { name: "Amarre-改", enabled: true } }, headers), legacy);
|
|
105
|
+
assert.equal(legacy.statusCode, 200);
|
|
106
|
+
assert.equal(settings.getPersona().default.name, "Amarre-改");
|
|
107
|
+
assert.equal(settings.getPersona().byAgent.zephyr.name, "泽菲尔", "legacy PUT keeps byAgent");
|
|
108
|
+
// 新形 PUT(多卡):全量替换
|
|
109
|
+
const multi = new FakeRes();
|
|
110
|
+
await route.handler(req("/api/dsh-mneme/persona", "PUT", { persona: { default: { name: "默认卡", enabled: true }, byAgent: { zephyr: { name: "泽菲尔-新", enabled: true } } } }, headers), multi);
|
|
111
|
+
assert.equal(multi.statusCode, 200);
|
|
112
|
+
const got = settings.getPersona();
|
|
113
|
+
assert.equal(got.default.name, "默认卡");
|
|
114
|
+
assert.equal(got.byAgent.zephyr.name, "泽菲尔-新");
|
|
115
|
+
store.close();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ---- standalone API(Bearer 数据面,真端口 + fetch) ------------------------
|
|
119
|
+
|
|
120
|
+
async function setupStandalone() {
|
|
121
|
+
const store = createStore(":memory:");
|
|
122
|
+
const service = createService({ store, mirror: null, config: {} });
|
|
123
|
+
const settings = createSettings(store.db);
|
|
124
|
+
const api = createStandaloneApi({ service, store, config: {}, settings, logger: null, port: 0 });
|
|
125
|
+
await api.ready;
|
|
126
|
+
const base = `http://127.0.0.1:${api.port}`;
|
|
127
|
+
const auth = { authorization: `Bearer ${api.token}` };
|
|
128
|
+
return {
|
|
129
|
+
store, settings, base, auth,
|
|
130
|
+
close: () => {
|
|
131
|
+
api.server.closeIdleConnections?.();
|
|
132
|
+
api.server.close();
|
|
133
|
+
store.close();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
test("standalone GET/PUT /persona behind Bearer gate round-trips", async () => {
|
|
139
|
+
const t = await setupStandalone();
|
|
140
|
+
try {
|
|
141
|
+
const get1 = await fetch(`${t.base}/persona`, { headers: t.auth });
|
|
142
|
+
assert.equal(get1.status, 200);
|
|
143
|
+
assert.deepEqual((await get1.json()).persona, { default: {}, byAgent: {}, bySession: {} });
|
|
144
|
+
const put = await fetch(`${t.base}/persona`, {
|
|
145
|
+
method: "PUT",
|
|
146
|
+
headers: { ...t.auth, "content-type": "application/json" },
|
|
147
|
+
body: JSON.stringify({ persona: { name: "Zelphi", enabled: false } })
|
|
148
|
+
});
|
|
149
|
+
assert.equal(put.status, 200);
|
|
150
|
+
assert.equal((await put.json()).persona.default.name, "Zelphi");
|
|
151
|
+
const bad = await fetch(`${t.base}/persona`, {
|
|
152
|
+
method: "PUT",
|
|
153
|
+
headers: { ...t.auth, "content-type": "application/json" },
|
|
154
|
+
body: "not json"
|
|
155
|
+
});
|
|
156
|
+
assert.equal(bad.status, 400);
|
|
157
|
+
assert.equal(t.settings.getPersona().default.name, "Zelphi");
|
|
158
|
+
} finally {
|
|
159
|
+
t.close();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("standalone /persona without Bearer is 401", async () => {
|
|
164
|
+
const t = await setupStandalone();
|
|
165
|
+
try {
|
|
166
|
+
const res = await fetch(`${t.base}/persona`, { method: "PUT", body: "{}" });
|
|
167
|
+
assert.equal(res.status, 401);
|
|
168
|
+
assert.deepEqual(t.settings.getPersona(), { default: {}, byAgent: {}, bySession: {} });
|
|
169
|
+
} finally {
|
|
170
|
+
t.close();
|
|
171
|
+
}
|
|
172
|
+
});
|