@modusensus/dsh-mneme 0.13.0 → 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 -828
- package/assets/icon.svg +3 -3
- package/bin/cli.mjs +964 -964
- package/bin/dsh-mneme-choro.mjs +262 -254
- 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 -402
- 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 -3394
- 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 -13436
- package/lib/commands.js +95 -95
- package/lib/config-keys.js +144 -144
- package/lib/config.js +1361 -1317
- 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 -907
- package/lib/dream/narratives.js +44 -44
- package/lib/dream/sleep.js +2027 -1887
- package/lib/dream/triage.js +139 -0
- package/lib/dream.js +1879 -1764
- 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 -107
- package/lib/image-describe.js +158 -0
- package/lib/index.js +852 -822
- package/lib/inject.js +656 -639
- 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 -584
- 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 -3196
- package/lib/session-gate.js +91 -91
- package/lib/session-usage.js +350 -350
- package/lib/settings.js +827 -802
- 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 -5662
- 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 -0
- 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 -474
- package/lib/wiki/render.js +261 -187
- package/lib/wiki/scan.js +371 -371
- package/lib/wiki/t0.js +500 -463
- package/lib/wiki/t1-jobs.js +232 -232
- package/lib/wiki/t1.js +609 -502
- package/lib/write-admission.js +404 -404
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +15 -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 -204
- package/scripts/release-notes.mjs +164 -161
- 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 -3394
- 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 -1317
- 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 -907
- package/src/dream/narratives.js +44 -44
- package/src/dream/sleep.js +2027 -1887
- package/src/dream/triage.js +139 -0
- package/src/dream.js +1879 -1764
- 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 -107
- package/src/image-describe.js +158 -0
- package/src/index.js +852 -822
- package/src/inject.js +656 -639
- 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 -584
- 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 -3196
- package/src/session-gate.js +91 -91
- package/src/session-usage.js +350 -350
- package/src/settings.js +827 -802
- 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 -5662
- 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 -0
- 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 -474
- package/src/wiki/render.js +261 -187
- package/src/wiki/scan.js +371 -371
- package/src/wiki/t0.js +500 -463
- package/src/wiki/t1-jobs.js +232 -232
- package/src/wiki/t1.js +609 -502
- 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 -2988
- 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 -4184
- 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 -0
- package/test/conflict-ledger.test.js +242 -0
- 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 -320
- 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 -0
- 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 -209
- 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 -211
- 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 -106
- 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 -494
- 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 -0
- package/test/panel-tokens.test.js +245 -0
- 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 -136
- package/test/release-notes.test.js +265 -240
- 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 -178
- 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 -1418
- 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 -627
- package/test/wiki-claims.test.js +194 -0
- 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 -0
- package/test/wiki-queries.test.js +398 -277
- package/test/wiki-scan.test.js +208 -208
- package/test/wiki-store.test.js +190 -190
- package/test/wiki-t0.test.js +456 -380
- package/test/wiki-t1-jobs.test.js +125 -125
- package/test/wiki-t1.test.js +460 -235
- package/test/wiki-tools.test.js +176 -176
- package/test/workflow-guards.test.js +155 -155
- package/test/write-admission.test.js +668 -668
package/lib/diary/library.js
CHANGED
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 日记库:把 `<memoryDir>/diaries/` 底下的所有分桶读成「谁写了多少篇」的清单。
|
|
3
|
-
*
|
|
4
|
-
* 和 `dir.js` 的分工:那边管**一个桶在哪**(给定 projectRoot 算键与路径),
|
|
5
|
-
* 这边管**有哪些桶**——左侧面板要的是后者,它拿不到「当前会话的项目」,
|
|
6
|
-
* 因为 `main` 座位不注入 sessionId。
|
|
7
|
-
*
|
|
8
|
-
* 「谁写的」只记在正文 frontmatter 里(`persona: settings (amarre)`),库里
|
|
9
|
-
* 没有第二份。所以这里读最新一篇的 frontmatter 来认人:读不到就退回桶名,
|
|
10
|
-
* 绝不猜——猜错会把 A 的日记挂到 B 名下,而那在界面上看不出来。
|
|
11
|
-
*/
|
|
12
|
-
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
import { DIARY_FILE_RE, diaryRoot } from "./dir.js";
|
|
15
|
-
|
|
16
|
-
/** frontmatter 块:文件必须以 `---` 起头,第二道横线收尾。 */
|
|
17
|
-
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 桶键 → 项目根与写日记的 agent。来源是 diaries/state.json(compose 自己写的账本)。
|
|
21
|
-
*
|
|
22
|
-
* 为什么面板要这个:一个桶就是一份 `projectKey(projectRoot)` 的产物,桶名里只有
|
|
23
|
-
* 目录名和 8 位哈希,**反推不回绝对路径**。而「现在再写一篇」必须给 `project`。
|
|
24
|
-
* 读不到就返回空表——那是「这个桶是别人手工放的」或「账本丢了」,面板那时候
|
|
25
|
-
* 就不该长出一个点了必然 400 的按钮。
|
|
26
|
-
*/
|
|
27
|
-
function readBucketState(root) {
|
|
28
|
-
let raw;
|
|
29
|
-
try {
|
|
30
|
-
raw = JSON.parse(readFileSync(join(root, "state.json"), "utf8"));
|
|
31
|
-
} catch {
|
|
32
|
-
return {};
|
|
33
|
-
}
|
|
34
|
-
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 桶名合法性:**不按字符集白名单**,按「它是个单层目录名」判。
|
|
39
|
-
*
|
|
40
|
-
* 一开始写的是 `^[A-Za-z0-9._-]+$`,理由是路径成分越严越好——但现场就有一个
|
|
41
|
-
* 保留中文的桶(`QQ对话-4f061790`,用户那天让另一个会话手工写的)。白名单会把
|
|
42
|
-
* 那一本从导航栏里抹掉,等于把用户写过的日记藏起来,而界面上看不出来。
|
|
43
|
-
*
|
|
44
|
-
* 所以要挡的是**路径语义**,不是字符:分隔符、上跳、控制字符、首尾空白、
|
|
45
|
-
* 以及 `.` / `..` 这两个特殊目录名。挡完这些,桶名就只剩「单层目录名」这一种
|
|
46
|
-
* 可能,下游可以直接拼路径,不需要再比一次路径前缀(见 `bucketDir` 的注释)。
|
|
47
|
-
*/
|
|
48
|
-
export function isBucketName(name) {
|
|
49
|
-
const s = String(name ?? "");
|
|
50
|
-
if (!s || s !== s.trim()) return false;
|
|
51
|
-
if (s === "." || s === "..") return false;
|
|
52
|
-
if (/[/\\]/.test(s)) return false;
|
|
53
|
-
// eslint-disable-next-line no-control-regex
|
|
54
|
-
if (/[\u0000-\u001f\u007f]/.test(s)) return false;
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 桶名 → 目录绝对路径。名字不合法就是 `null`。
|
|
60
|
-
*
|
|
61
|
-
* 这里**没有**第二道「解析后比对根前缀」的校验:`isBucketName` 已经挡住了
|
|
62
|
-
* 分隔符与上跳,剩下的只可能是单层名字,前缀比对永远走不到。试过写它,
|
|
63
|
-
* 变异测试(把比对删掉)一跑八条用例全绿,正说明它是死代码——那就别留,
|
|
64
|
-
* 留着的防御没人能验证,反而让人以为这里有一层额外的兜底。
|
|
65
|
-
*/
|
|
66
|
-
function bucketDir(root, bucket) {
|
|
67
|
-
if (!isBucketName(bucket)) return null;
|
|
68
|
-
return join(root, bucket);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function readFrontmatter(path) {
|
|
72
|
-
let text;
|
|
73
|
-
try {
|
|
74
|
-
text = readFileSync(path, "utf8");
|
|
75
|
-
} catch {
|
|
76
|
-
return {};
|
|
77
|
-
}
|
|
78
|
-
const hit = FRONTMATTER_RE.exec(text);
|
|
79
|
-
if (!hit) return {};
|
|
80
|
-
const out = {};
|
|
81
|
-
for (const line of hit[1].split(/\r?\n/)) {
|
|
82
|
-
const at = line.indexOf(":");
|
|
83
|
-
if (at <= 0) continue;
|
|
84
|
-
const key = line.slice(0, at).trim();
|
|
85
|
-
const value = line.slice(at + 1).trim();
|
|
86
|
-
if (key) out[key] = value;
|
|
87
|
-
}
|
|
88
|
-
return out;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 列一个桶里的日记文件。与 `export.js` 的 `listDiaries` 同一口径
|
|
93
|
-
* (只认 `YYYY-MM-DD.md`、跳过空文件、新的在前),这里不重复实现。
|
|
94
|
-
*/
|
|
95
|
-
function diaryFiles(dir) {
|
|
96
|
-
let names;
|
|
97
|
-
try {
|
|
98
|
-
names = readdirSync(dir);
|
|
99
|
-
} catch {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
const rows = [];
|
|
103
|
-
for (const name of names) {
|
|
104
|
-
if (!DIARY_FILE_RE.test(name)) continue;
|
|
105
|
-
const path = join(dir, name);
|
|
106
|
-
let st;
|
|
107
|
-
try {
|
|
108
|
-
st = statSync(path);
|
|
109
|
-
} catch {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (!st.isFile() || st.size <= 0) continue;
|
|
113
|
-
rows.push({ date: name.slice(0, 10), path, bytes: st.size, mtime: st.mtimeMs });
|
|
114
|
-
}
|
|
115
|
-
return rows.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 全部桶。读不到根目录返回空清单而不是抛——「还没写过第一篇」与「路径坏了」
|
|
120
|
-
* 在面板上是两种不同的话,但这一层分不出来,交给调用方去看 `root`。
|
|
121
|
-
*/
|
|
122
|
-
export function listDiaryLibrary({ memoryDir, configured = "" } = {}) {
|
|
123
|
-
const root = diaryRoot({ memoryDir, configured });
|
|
124
|
-
let names;
|
|
125
|
-
try {
|
|
126
|
-
names = readdirSync(root);
|
|
127
|
-
} catch {
|
|
128
|
-
return { root, buckets: [] };
|
|
129
|
-
}
|
|
130
|
-
const buckets = [];
|
|
131
|
-
const state = readBucketState(root);
|
|
132
|
-
for (const name of names) {
|
|
133
|
-
// `.deleted` 一类是回收站,不是日记。
|
|
134
|
-
if (name.startsWith(".")) continue;
|
|
135
|
-
if (!isBucketName(name)) continue;
|
|
136
|
-
const dir = join(root, name);
|
|
137
|
-
let st;
|
|
138
|
-
try {
|
|
139
|
-
st = statSync(dir);
|
|
140
|
-
} catch {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (!st.isDirectory()) continue;
|
|
144
|
-
const files = diaryFiles(dir);
|
|
145
|
-
// 空桶(文件被删光、只剩 runs.jsonl)不列:导航栏里挂一个点开是空的项
|
|
146
|
-
// 比不列更糟。
|
|
147
|
-
if (!files.length) continue;
|
|
148
|
-
let persona = "";
|
|
149
|
-
let project = "";
|
|
150
|
-
for (const file of files) {
|
|
151
|
-
const fm = readFrontmatter(file.path);
|
|
152
|
-
if (!project && fm.project) project = fm.project;
|
|
153
|
-
// persona 只认第一篇带它的:同一桶里可能先后住过两个人格(#613 要拆的那种),
|
|
154
|
-
// 面板先按最新那篇记的人显示,不混。
|
|
155
|
-
if (fm.persona) {
|
|
156
|
-
persona = fm.persona;
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const meta = state[name] && typeof state[name] === "object" ? state[name] : {};
|
|
161
|
-
buckets.push({
|
|
162
|
-
key: name,
|
|
163
|
-
project,
|
|
164
|
-
// 「现在再写一篇」需要的两样:绝对路径与写作者身份。账本里没有就留空,
|
|
165
|
-
// 面板据此把按钮禁掉——不猜路径,猜错会写在别的项目里。
|
|
166
|
-
projectRoot: typeof meta.projectRoot === "string" ? meta.projectRoot : "",
|
|
167
|
-
agentId: typeof meta.agentId === "string" ? meta.agentId : "",
|
|
168
|
-
persona,
|
|
169
|
-
latest: files[0].date,
|
|
170
|
-
count: files.length,
|
|
171
|
-
entries: files.map((file) => ({ date: file.date, bytes: file.bytes, mtime: file.mtime }))
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
buckets.sort((a, b) => (a.latest < b.latest ? 1 : a.latest > b.latest ? -1 : 0));
|
|
175
|
-
return { root, buckets };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/** 读一篇的正文。桶名与日期都要过校验:这条路径直接拼文件系统。 */
|
|
179
|
-
export function readLibraryEntry({ memoryDir, configured = "", bucket, date } = {}) {
|
|
180
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(date ?? ""))) return null;
|
|
181
|
-
const root = diaryRoot({ memoryDir, configured });
|
|
182
|
-
const dir = bucketDir(root, bucket);
|
|
183
|
-
if (dir === null) return null;
|
|
184
|
-
try {
|
|
185
|
-
return readFileSync(join(dir, `${date}.md`), "utf8");
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 日记库:把 `<memoryDir>/diaries/` 底下的所有分桶读成「谁写了多少篇」的清单。
|
|
3
|
+
*
|
|
4
|
+
* 和 `dir.js` 的分工:那边管**一个桶在哪**(给定 projectRoot 算键与路径),
|
|
5
|
+
* 这边管**有哪些桶**——左侧面板要的是后者,它拿不到「当前会话的项目」,
|
|
6
|
+
* 因为 `main` 座位不注入 sessionId。
|
|
7
|
+
*
|
|
8
|
+
* 「谁写的」只记在正文 frontmatter 里(`persona: settings (amarre)`),库里
|
|
9
|
+
* 没有第二份。所以这里读最新一篇的 frontmatter 来认人:读不到就退回桶名,
|
|
10
|
+
* 绝不猜——猜错会把 A 的日记挂到 B 名下,而那在界面上看不出来。
|
|
11
|
+
*/
|
|
12
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { DIARY_FILE_RE, diaryRoot } from "./dir.js";
|
|
15
|
+
|
|
16
|
+
/** frontmatter 块:文件必须以 `---` 起头,第二道横线收尾。 */
|
|
17
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 桶键 → 项目根与写日记的 agent。来源是 diaries/state.json(compose 自己写的账本)。
|
|
21
|
+
*
|
|
22
|
+
* 为什么面板要这个:一个桶就是一份 `projectKey(projectRoot)` 的产物,桶名里只有
|
|
23
|
+
* 目录名和 8 位哈希,**反推不回绝对路径**。而「现在再写一篇」必须给 `project`。
|
|
24
|
+
* 读不到就返回空表——那是「这个桶是别人手工放的」或「账本丢了」,面板那时候
|
|
25
|
+
* 就不该长出一个点了必然 400 的按钮。
|
|
26
|
+
*/
|
|
27
|
+
function readBucketState(root) {
|
|
28
|
+
let raw;
|
|
29
|
+
try {
|
|
30
|
+
raw = JSON.parse(readFileSync(join(root, "state.json"), "utf8"));
|
|
31
|
+
} catch {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 桶名合法性:**不按字符集白名单**,按「它是个单层目录名」判。
|
|
39
|
+
*
|
|
40
|
+
* 一开始写的是 `^[A-Za-z0-9._-]+$`,理由是路径成分越严越好——但现场就有一个
|
|
41
|
+
* 保留中文的桶(`QQ对话-4f061790`,用户那天让另一个会话手工写的)。白名单会把
|
|
42
|
+
* 那一本从导航栏里抹掉,等于把用户写过的日记藏起来,而界面上看不出来。
|
|
43
|
+
*
|
|
44
|
+
* 所以要挡的是**路径语义**,不是字符:分隔符、上跳、控制字符、首尾空白、
|
|
45
|
+
* 以及 `.` / `..` 这两个特殊目录名。挡完这些,桶名就只剩「单层目录名」这一种
|
|
46
|
+
* 可能,下游可以直接拼路径,不需要再比一次路径前缀(见 `bucketDir` 的注释)。
|
|
47
|
+
*/
|
|
48
|
+
export function isBucketName(name) {
|
|
49
|
+
const s = String(name ?? "");
|
|
50
|
+
if (!s || s !== s.trim()) return false;
|
|
51
|
+
if (s === "." || s === "..") return false;
|
|
52
|
+
if (/[/\\]/.test(s)) return false;
|
|
53
|
+
// eslint-disable-next-line no-control-regex
|
|
54
|
+
if (/[\u0000-\u001f\u007f]/.test(s)) return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 桶名 → 目录绝对路径。名字不合法就是 `null`。
|
|
60
|
+
*
|
|
61
|
+
* 这里**没有**第二道「解析后比对根前缀」的校验:`isBucketName` 已经挡住了
|
|
62
|
+
* 分隔符与上跳,剩下的只可能是单层名字,前缀比对永远走不到。试过写它,
|
|
63
|
+
* 变异测试(把比对删掉)一跑八条用例全绿,正说明它是死代码——那就别留,
|
|
64
|
+
* 留着的防御没人能验证,反而让人以为这里有一层额外的兜底。
|
|
65
|
+
*/
|
|
66
|
+
function bucketDir(root, bucket) {
|
|
67
|
+
if (!isBucketName(bucket)) return null;
|
|
68
|
+
return join(root, bucket);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readFrontmatter(path) {
|
|
72
|
+
let text;
|
|
73
|
+
try {
|
|
74
|
+
text = readFileSync(path, "utf8");
|
|
75
|
+
} catch {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
const hit = FRONTMATTER_RE.exec(text);
|
|
79
|
+
if (!hit) return {};
|
|
80
|
+
const out = {};
|
|
81
|
+
for (const line of hit[1].split(/\r?\n/)) {
|
|
82
|
+
const at = line.indexOf(":");
|
|
83
|
+
if (at <= 0) continue;
|
|
84
|
+
const key = line.slice(0, at).trim();
|
|
85
|
+
const value = line.slice(at + 1).trim();
|
|
86
|
+
if (key) out[key] = value;
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 列一个桶里的日记文件。与 `export.js` 的 `listDiaries` 同一口径
|
|
93
|
+
* (只认 `YYYY-MM-DD.md`、跳过空文件、新的在前),这里不重复实现。
|
|
94
|
+
*/
|
|
95
|
+
function diaryFiles(dir) {
|
|
96
|
+
let names;
|
|
97
|
+
try {
|
|
98
|
+
names = readdirSync(dir);
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const rows = [];
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
if (!DIARY_FILE_RE.test(name)) continue;
|
|
105
|
+
const path = join(dir, name);
|
|
106
|
+
let st;
|
|
107
|
+
try {
|
|
108
|
+
st = statSync(path);
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!st.isFile() || st.size <= 0) continue;
|
|
113
|
+
rows.push({ date: name.slice(0, 10), path, bytes: st.size, mtime: st.mtimeMs });
|
|
114
|
+
}
|
|
115
|
+
return rows.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 全部桶。读不到根目录返回空清单而不是抛——「还没写过第一篇」与「路径坏了」
|
|
120
|
+
* 在面板上是两种不同的话,但这一层分不出来,交给调用方去看 `root`。
|
|
121
|
+
*/
|
|
122
|
+
export function listDiaryLibrary({ memoryDir, configured = "" } = {}) {
|
|
123
|
+
const root = diaryRoot({ memoryDir, configured });
|
|
124
|
+
let names;
|
|
125
|
+
try {
|
|
126
|
+
names = readdirSync(root);
|
|
127
|
+
} catch {
|
|
128
|
+
return { root, buckets: [] };
|
|
129
|
+
}
|
|
130
|
+
const buckets = [];
|
|
131
|
+
const state = readBucketState(root);
|
|
132
|
+
for (const name of names) {
|
|
133
|
+
// `.deleted` 一类是回收站,不是日记。
|
|
134
|
+
if (name.startsWith(".")) continue;
|
|
135
|
+
if (!isBucketName(name)) continue;
|
|
136
|
+
const dir = join(root, name);
|
|
137
|
+
let st;
|
|
138
|
+
try {
|
|
139
|
+
st = statSync(dir);
|
|
140
|
+
} catch {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!st.isDirectory()) continue;
|
|
144
|
+
const files = diaryFiles(dir);
|
|
145
|
+
// 空桶(文件被删光、只剩 runs.jsonl)不列:导航栏里挂一个点开是空的项
|
|
146
|
+
// 比不列更糟。
|
|
147
|
+
if (!files.length) continue;
|
|
148
|
+
let persona = "";
|
|
149
|
+
let project = "";
|
|
150
|
+
for (const file of files) {
|
|
151
|
+
const fm = readFrontmatter(file.path);
|
|
152
|
+
if (!project && fm.project) project = fm.project;
|
|
153
|
+
// persona 只认第一篇带它的:同一桶里可能先后住过两个人格(#613 要拆的那种),
|
|
154
|
+
// 面板先按最新那篇记的人显示,不混。
|
|
155
|
+
if (fm.persona) {
|
|
156
|
+
persona = fm.persona;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const meta = state[name] && typeof state[name] === "object" ? state[name] : {};
|
|
161
|
+
buckets.push({
|
|
162
|
+
key: name,
|
|
163
|
+
project,
|
|
164
|
+
// 「现在再写一篇」需要的两样:绝对路径与写作者身份。账本里没有就留空,
|
|
165
|
+
// 面板据此把按钮禁掉——不猜路径,猜错会写在别的项目里。
|
|
166
|
+
projectRoot: typeof meta.projectRoot === "string" ? meta.projectRoot : "",
|
|
167
|
+
agentId: typeof meta.agentId === "string" ? meta.agentId : "",
|
|
168
|
+
persona,
|
|
169
|
+
latest: files[0].date,
|
|
170
|
+
count: files.length,
|
|
171
|
+
entries: files.map((file) => ({ date: file.date, bytes: file.bytes, mtime: file.mtime }))
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
buckets.sort((a, b) => (a.latest < b.latest ? 1 : a.latest > b.latest ? -1 : 0));
|
|
175
|
+
return { root, buckets };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** 读一篇的正文。桶名与日期都要过校验:这条路径直接拼文件系统。 */
|
|
179
|
+
export function readLibraryEntry({ memoryDir, configured = "", bucket, date } = {}) {
|
|
180
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(date ?? ""))) return null;
|
|
181
|
+
const root = diaryRoot({ memoryDir, configured });
|
|
182
|
+
const dir = bucketDir(root, bucket);
|
|
183
|
+
if (dir === null) return null;
|
|
184
|
+
try {
|
|
185
|
+
return readFileSync(join(dir, `${date}.md`), "utf8");
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
package/lib/diary/qq-inbox.js
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 素材来源之一:QQ 聊天收件箱(只读)。
|
|
3
|
-
*
|
|
4
|
-
* 这个模块**不 import `dsh-onebot-qq` 的任何东西**——那个包是 npm 装进来的普通目录,
|
|
5
|
-
* 每次升级都会被覆盖,边界靠这个 provider 的入参/返回值切开,而不是靠 require 路径。
|
|
6
|
-
*
|
|
7
|
-
* 素材口径(实测 `D:\\DSH\\.dsh\\media\\onebot\\qq-inbox.jsonl` 的 decision 分布定下来的):
|
|
8
|
-
* 只取 `decision.stage === "dispatch" && decision.ok === true` 且消息里真有 text 段的行。
|
|
9
|
-
* 同一份样本里 70 行中,白名单挡掉的群聊 20 行、投递成功但无有效文本 5 行——不滤掉,
|
|
10
|
-
* 日记就会变成「今天什么也没干」的噪音,还占掉 document 注入预算。
|
|
11
|
-
*
|
|
12
|
-
* 已知缺口(照实写在这里,别当它不存在):inbox 只有**入站**消息,机器人自己的回复
|
|
13
|
-
* 不在其中(样本里 message_sent 命中 0)。所以日记能写「今天她说了什么」,
|
|
14
|
-
* 写不了「我怎么回的」——需要时得另找来源,不能靠这一份编。
|
|
15
|
-
*/
|
|
16
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import { localDateString } from "./rollover.js";
|
|
19
|
-
import { memoryBase } from "./dir.js";
|
|
20
|
-
|
|
21
|
-
/** `qq-inbox.jsonl` = 当前代(0),`qq-inbox.N.jsonl` = 越旧 N 越大。 */
|
|
22
|
-
export const INBOX_FILE_RE = /^qq-inbox(?:\.(\d+))?\.jsonl$/;
|
|
23
|
-
|
|
24
|
-
function generationOf(name) {
|
|
25
|
-
const m = INBOX_FILE_RE.exec(name);
|
|
26
|
-
return m && m[1] ? Number(m[1]) : 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 收件箱文件,**新的在前**。
|
|
31
|
-
*
|
|
32
|
-
* 目录读不到返回 null(= 没有这个来源),与「目录在但没有消息」严格区分:
|
|
33
|
-
* 前者是 `no-inbox-dir`,后者是 `no-material`,两者的处置完全不同。
|
|
34
|
-
*/
|
|
35
|
-
export function inboxFiles(dir) {
|
|
36
|
-
if (!dir) return null;
|
|
37
|
-
let names;
|
|
38
|
-
try {
|
|
39
|
-
names = readdirSync(dir);
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
return names
|
|
44
|
-
.filter((n) => INBOX_FILE_RE.test(n))
|
|
45
|
-
.sort((a, b) => generationOf(a) - generationOf(b));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** 只打码长度 ≥6 的数字串(与 onebot `lib/record.js:47-49` 的 redactDigits 同口径)。 */
|
|
49
|
-
export function redactDigits(text) {
|
|
50
|
-
return String(text ?? "").replace(/\d{6,}/g, (m) => m.slice(0, 3) + "****");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** 一条消息里所有 text 段的拼接。图片/表情/语音段没有可读正文,直接跳过。 */
|
|
54
|
-
export function textOf(frame) {
|
|
55
|
-
const msg = frame && frame.message;
|
|
56
|
-
if (!Array.isArray(msg)) return "";
|
|
57
|
-
return msg
|
|
58
|
-
.filter((s) => s && s.type === "text" && s.data && typeof s.data.text === "string")
|
|
59
|
-
.map((s) => s.data.text)
|
|
60
|
-
.join("")
|
|
61
|
-
.trim();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** 有效投递:`dispatch` 且 `ok === true`。其余(whitelist / command / 失败)都不算素材。 */
|
|
65
|
-
export function isEffective(frame) {
|
|
66
|
-
const d = frame && frame.decision;
|
|
67
|
-
return !!d && d.stage === "dispatch" && d.ok === true;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 采一段窗口内的素材。
|
|
72
|
-
*
|
|
73
|
-
* 返回三选一:
|
|
74
|
-
* - `{items, scanned, ignored}` 拿到东西(items 可以为空 = 当天确实没聊)
|
|
75
|
-
* - `{error: "no-inbox-dir"}` 没有这个来源(目录不在)
|
|
76
|
-
* - `{error: "inbox-unreadable", detail}` 目录在但一个文件都读不出来
|
|
77
|
-
*/
|
|
78
|
-
export function collectQqInbox({ dir, dateLocal, maxItems = 400 } = {}) {
|
|
79
|
-
if (!dir) return { error: "no-inbox-dir" };
|
|
80
|
-
const files = inboxFiles(dir);
|
|
81
|
-
if (files === null) return { error: "no-inbox-dir", detail: String(dir) };
|
|
82
|
-
if (files.length === 0) return { error: "inbox-unreadable", detail: String(dir) };
|
|
83
|
-
const items = [];
|
|
84
|
-
let scanned = 0;
|
|
85
|
-
let ignored = 0;
|
|
86
|
-
let readable = 0;
|
|
87
|
-
for (const name of files) {
|
|
88
|
-
let text;
|
|
89
|
-
try {
|
|
90
|
-
text = readFileSync(join(dir, name), "utf8");
|
|
91
|
-
} catch {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
readable += 1;
|
|
95
|
-
for (const line of text.split("\n")) {
|
|
96
|
-
if (!line.trim()) continue;
|
|
97
|
-
scanned += 1;
|
|
98
|
-
let row;
|
|
99
|
-
try {
|
|
100
|
-
row = JSON.parse(line);
|
|
101
|
-
} catch {
|
|
102
|
-
// 坏行跳过但计数(与 loadObservations 同一条纪律)。
|
|
103
|
-
ignored += 1;
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
if (!row || row.kind !== "message" || !isEffective(row.frame)) {
|
|
107
|
-
ignored += 1;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
const ts = Number(row.ts);
|
|
111
|
-
if (!Number.isFinite(ts)) {
|
|
112
|
-
ignored += 1;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
// 不在当天窗口内的行不算「忽略」,它属于别的日子。
|
|
116
|
-
if (localDateString(new Date(ts)) !== dateLocal) continue;
|
|
117
|
-
const body = textOf(row.frame);
|
|
118
|
-
if (!body) {
|
|
119
|
-
ignored += 1;
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
items.push({ ts, role: "user", text: redactDigits(body).slice(0, 2000) });
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (readable === 0) return { error: "inbox-unreadable", detail: String(dir) };
|
|
126
|
-
items.sort((a, b) => a.ts - b.ts);
|
|
127
|
-
return { items: items.slice(-maxItems), scanned, ignored };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 把 `<DSH_HOME>/media/onebot` 推出来:memoryDir 默认就是 `<DSH_HOME>/memory`。
|
|
132
|
-
*
|
|
133
|
-
* 推不出来(memoryDir 被指到别处、或给了显式配置)就用配置值;两者都没有时这个来源
|
|
134
|
-
* 直接报 `no-inbox-dir`——宁可这一路没有素材,也不去猜别的盘。
|
|
135
|
-
*/
|
|
136
|
-
export function resolveInboxDir({ memoryDir, configured = "" } = {}) {
|
|
137
|
-
const custom = String(configured ?? "").trim();
|
|
138
|
-
if (custom) return custom;
|
|
139
|
-
// 必须走 memoryBase 而不是直接用配置串:默认值就是带 `~` 的字面量,
|
|
140
|
-
// 不展开的话 join 出来的路径永远落空,旁挂会静默读不到(CodeRabbit #458 F2)。
|
|
141
|
-
return join(memoryBase(memoryDir), "..", "media", "onebot");
|
|
142
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 素材来源之一:QQ 聊天收件箱(只读)。
|
|
3
|
+
*
|
|
4
|
+
* 这个模块**不 import `dsh-onebot-qq` 的任何东西**——那个包是 npm 装进来的普通目录,
|
|
5
|
+
* 每次升级都会被覆盖,边界靠这个 provider 的入参/返回值切开,而不是靠 require 路径。
|
|
6
|
+
*
|
|
7
|
+
* 素材口径(实测 `D:\\DSH\\.dsh\\media\\onebot\\qq-inbox.jsonl` 的 decision 分布定下来的):
|
|
8
|
+
* 只取 `decision.stage === "dispatch" && decision.ok === true` 且消息里真有 text 段的行。
|
|
9
|
+
* 同一份样本里 70 行中,白名单挡掉的群聊 20 行、投递成功但无有效文本 5 行——不滤掉,
|
|
10
|
+
* 日记就会变成「今天什么也没干」的噪音,还占掉 document 注入预算。
|
|
11
|
+
*
|
|
12
|
+
* 已知缺口(照实写在这里,别当它不存在):inbox 只有**入站**消息,机器人自己的回复
|
|
13
|
+
* 不在其中(样本里 message_sent 命中 0)。所以日记能写「今天她说了什么」,
|
|
14
|
+
* 写不了「我怎么回的」——需要时得另找来源,不能靠这一份编。
|
|
15
|
+
*/
|
|
16
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { localDateString } from "./rollover.js";
|
|
19
|
+
import { memoryBase } from "./dir.js";
|
|
20
|
+
|
|
21
|
+
/** `qq-inbox.jsonl` = 当前代(0),`qq-inbox.N.jsonl` = 越旧 N 越大。 */
|
|
22
|
+
export const INBOX_FILE_RE = /^qq-inbox(?:\.(\d+))?\.jsonl$/;
|
|
23
|
+
|
|
24
|
+
function generationOf(name) {
|
|
25
|
+
const m = INBOX_FILE_RE.exec(name);
|
|
26
|
+
return m && m[1] ? Number(m[1]) : 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 收件箱文件,**新的在前**。
|
|
31
|
+
*
|
|
32
|
+
* 目录读不到返回 null(= 没有这个来源),与「目录在但没有消息」严格区分:
|
|
33
|
+
* 前者是 `no-inbox-dir`,后者是 `no-material`,两者的处置完全不同。
|
|
34
|
+
*/
|
|
35
|
+
export function inboxFiles(dir) {
|
|
36
|
+
if (!dir) return null;
|
|
37
|
+
let names;
|
|
38
|
+
try {
|
|
39
|
+
names = readdirSync(dir);
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return names
|
|
44
|
+
.filter((n) => INBOX_FILE_RE.test(n))
|
|
45
|
+
.sort((a, b) => generationOf(a) - generationOf(b));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 只打码长度 ≥6 的数字串(与 onebot `lib/record.js:47-49` 的 redactDigits 同口径)。 */
|
|
49
|
+
export function redactDigits(text) {
|
|
50
|
+
return String(text ?? "").replace(/\d{6,}/g, (m) => m.slice(0, 3) + "****");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 一条消息里所有 text 段的拼接。图片/表情/语音段没有可读正文,直接跳过。 */
|
|
54
|
+
export function textOf(frame) {
|
|
55
|
+
const msg = frame && frame.message;
|
|
56
|
+
if (!Array.isArray(msg)) return "";
|
|
57
|
+
return msg
|
|
58
|
+
.filter((s) => s && s.type === "text" && s.data && typeof s.data.text === "string")
|
|
59
|
+
.map((s) => s.data.text)
|
|
60
|
+
.join("")
|
|
61
|
+
.trim();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 有效投递:`dispatch` 且 `ok === true`。其余(whitelist / command / 失败)都不算素材。 */
|
|
65
|
+
export function isEffective(frame) {
|
|
66
|
+
const d = frame && frame.decision;
|
|
67
|
+
return !!d && d.stage === "dispatch" && d.ok === true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 采一段窗口内的素材。
|
|
72
|
+
*
|
|
73
|
+
* 返回三选一:
|
|
74
|
+
* - `{items, scanned, ignored}` 拿到东西(items 可以为空 = 当天确实没聊)
|
|
75
|
+
* - `{error: "no-inbox-dir"}` 没有这个来源(目录不在)
|
|
76
|
+
* - `{error: "inbox-unreadable", detail}` 目录在但一个文件都读不出来
|
|
77
|
+
*/
|
|
78
|
+
export function collectQqInbox({ dir, dateLocal, maxItems = 400 } = {}) {
|
|
79
|
+
if (!dir) return { error: "no-inbox-dir" };
|
|
80
|
+
const files = inboxFiles(dir);
|
|
81
|
+
if (files === null) return { error: "no-inbox-dir", detail: String(dir) };
|
|
82
|
+
if (files.length === 0) return { error: "inbox-unreadable", detail: String(dir) };
|
|
83
|
+
const items = [];
|
|
84
|
+
let scanned = 0;
|
|
85
|
+
let ignored = 0;
|
|
86
|
+
let readable = 0;
|
|
87
|
+
for (const name of files) {
|
|
88
|
+
let text;
|
|
89
|
+
try {
|
|
90
|
+
text = readFileSync(join(dir, name), "utf8");
|
|
91
|
+
} catch {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
readable += 1;
|
|
95
|
+
for (const line of text.split("\n")) {
|
|
96
|
+
if (!line.trim()) continue;
|
|
97
|
+
scanned += 1;
|
|
98
|
+
let row;
|
|
99
|
+
try {
|
|
100
|
+
row = JSON.parse(line);
|
|
101
|
+
} catch {
|
|
102
|
+
// 坏行跳过但计数(与 loadObservations 同一条纪律)。
|
|
103
|
+
ignored += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (!row || row.kind !== "message" || !isEffective(row.frame)) {
|
|
107
|
+
ignored += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const ts = Number(row.ts);
|
|
111
|
+
if (!Number.isFinite(ts)) {
|
|
112
|
+
ignored += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// 不在当天窗口内的行不算「忽略」,它属于别的日子。
|
|
116
|
+
if (localDateString(new Date(ts)) !== dateLocal) continue;
|
|
117
|
+
const body = textOf(row.frame);
|
|
118
|
+
if (!body) {
|
|
119
|
+
ignored += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
items.push({ ts, role: "user", text: redactDigits(body).slice(0, 2000) });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (readable === 0) return { error: "inbox-unreadable", detail: String(dir) };
|
|
126
|
+
items.sort((a, b) => a.ts - b.ts);
|
|
127
|
+
return { items: items.slice(-maxItems), scanned, ignored };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 把 `<DSH_HOME>/media/onebot` 推出来:memoryDir 默认就是 `<DSH_HOME>/memory`。
|
|
132
|
+
*
|
|
133
|
+
* 推不出来(memoryDir 被指到别处、或给了显式配置)就用配置值;两者都没有时这个来源
|
|
134
|
+
* 直接报 `no-inbox-dir`——宁可这一路没有素材,也不去猜别的盘。
|
|
135
|
+
*/
|
|
136
|
+
export function resolveInboxDir({ memoryDir, configured = "" } = {}) {
|
|
137
|
+
const custom = String(configured ?? "").trim();
|
|
138
|
+
if (custom) return custom;
|
|
139
|
+
// 必须走 memoryBase 而不是直接用配置串:默认值就是带 `~` 的字面量,
|
|
140
|
+
// 不展开的话 join 出来的路径永远落空,旁挂会静默读不到(CodeRabbit #458 F2)。
|
|
141
|
+
return join(memoryBase(memoryDir), "..", "media", "onebot");
|
|
142
|
+
}
|