@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/pipeline-confirm.js
CHANGED
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* M.O. 的人确认层:把「本次观察到什么」变成「哪几步真的走了」。
|
|
3
|
-
*
|
|
4
|
-
* ## 为什么必须由人来说
|
|
5
|
-
*
|
|
6
|
-
* 自动归属被实测否决(详见 mneme-research 的 08/09):
|
|
7
|
-
* - 拿单条命令匹配手写步骤表,唯一归属只有 **14.2%**;非发版会话也有 4.4%,**区分力仅 3 倍**
|
|
8
|
-
* - 放宽到"包含匹配",天花板仍 14%,且大头是 `git log` / `git diff` 这类通用命令
|
|
9
|
-
* - 工作流里的关键步骤 `gh pr merge --rebase` 在 21723 次调用里**出现 0 次**——
|
|
10
|
-
* 说明清单本身可能与实际行为脱节
|
|
11
|
-
*
|
|
12
|
-
* 归到本文档的一句话:**机器猜不准,而猜错的代价是把没走过的步骤记成走过**
|
|
13
|
-
* (`deriveState` 会据此把它染成 `trusted`)。所以这一步交给人。
|
|
14
|
-
*
|
|
15
|
-
* ## 三个设计决定
|
|
16
|
-
*
|
|
17
|
-
* 1. **提案不改动任何东西**。`buildProposal` 是纯函数,产出的是"给人看的待确认清单"。
|
|
18
|
-
* 写账只发生在 `confirm` 之后,且只写人点过的步骤。
|
|
19
|
-
* 2. **"不确定"是一个合法的答案**。人可以对某一步说"我不知道"——它既不记为走过,
|
|
20
|
-
* 也不记为跳过。这与账本那边「缺字段的行不许被算成 uses」是同一条纪律:
|
|
21
|
-
* **没确认 ≠ 没发生,也 ≠ 发生**。
|
|
22
|
-
* 3. **必须能报"整条流程都没走"**。人可以说"这次什么都没做",那就是一条**零步 run**
|
|
23
|
-
* ——但账本契约要求 `run.steps` 至少一项(见 `pipeline-runs` 的校验),所以那种情况
|
|
24
|
-
* 本模块如实返回 `{ kind: "nothing" }` 而不是伪造一条空 run。
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 造一份确认提案(纯函数,不落盘、不改任何状态)。
|
|
29
|
-
*
|
|
30
|
-
* 输入:
|
|
31
|
-
* - `pipeline`:本次要走的流程(含 steps)
|
|
32
|
-
* - `activities`:P1 产出的业务动作清单(`[{key, count, flags}]`),给人当参照
|
|
33
|
-
* - `hints`:自动归属的候选(可空),**只作提示**
|
|
34
|
-
*
|
|
35
|
-
* 输出刻意把两类信息分开:
|
|
36
|
-
* - `steps`:**流程自己的**步骤表(人在这里勾)
|
|
37
|
-
* - `activities`:**观察到的**业务动作(人在这对照)
|
|
38
|
-
* 两者不做自动配对——那正是被否决的那件事。
|
|
39
|
-
*/
|
|
40
|
-
export function buildProposal({ pipeline, activities = [], hints = null } = {}) {
|
|
41
|
-
if (!pipeline || !Array.isArray(pipeline.steps)) {
|
|
42
|
-
return { ok: false, reason: "no-pipeline" };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const steps = pipeline.steps.map((s) => ({
|
|
46
|
-
id: s.id,
|
|
47
|
-
title: s.title,
|
|
48
|
-
// 步骤表里人写的 tool 原文——给对照用,不是匹配依据
|
|
49
|
-
declared: typeof s.tool === "string" ? s.tool : "",
|
|
50
|
-
// 机器给的候选提示:**只给一个次数**,不给命令行原文(原文含绝对路径)。
|
|
51
|
-
// 它只是提示,不预选——人仍然要自己说这一步做没做。
|
|
52
|
-
hintCount: Number.isFinite(hints?.[s.id]) ? hints[s.id] : 0
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
ok: true,
|
|
57
|
-
pipelineId: pipeline.id,
|
|
58
|
-
steps,
|
|
59
|
-
activities: activities.map((a) => ({ key: a.key, count: a.count, flags: a.flags || [] }))
|
|
60
|
-
// 不返回 answers 词表:命令层有自己的解析表,写死两份只会漂。
|
|
61
|
-
// 也不返回 pipelineTitle / observations:零消费者(ponytail 复核点名的两处)。
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 校验并归一人的回答。
|
|
67
|
-
*
|
|
68
|
-
* `answers` 形如 `{ "P6": "done", "V2": "notDone" }`。
|
|
69
|
-
* **只认三类值**,其余一律拒——静默把非法值当"没发生"是编造的一种。
|
|
70
|
-
*/
|
|
71
|
-
export function normalizeAnswers(answersResult, pipeline) {
|
|
72
|
-
if (!pipeline || !Array.isArray(pipeline.steps)) return { ok: false, reason: "no-pipeline" };
|
|
73
|
-
if (!answersResult || typeof answersResult !== "object" || Array.isArray(answersResult)) {
|
|
74
|
-
return { ok: false, reason: "bad-answers" };
|
|
75
|
-
}
|
|
76
|
-
const ids = new Set(pipeline.steps.map((s) => s.id));
|
|
77
|
-
const clean = {};
|
|
78
|
-
for (const [id, value] of Object.entries(answersResult)) {
|
|
79
|
-
if (!ids.has(id)) return { ok: false, reason: "unknown-step", stepId: id };
|
|
80
|
-
if (value !== "done" && value !== "notDone" && value !== "unsure") {
|
|
81
|
-
return { ok: false, reason: "bad-answer-value", stepId: id, value };
|
|
82
|
-
}
|
|
83
|
-
clean[id] = value;
|
|
84
|
-
}
|
|
85
|
-
return { ok: true, answers: clean };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 把确认结果变成一条 run(**仍然不落盘**,落盘由调用方做)。
|
|
90
|
-
*
|
|
91
|
-
* 三种结局分得很清:
|
|
92
|
-
* - `kind:"run"`:**至少一条记录**(`done` 或 `notDone`)→ 产出一条 run
|
|
93
|
-
* - `kind:"nothing"`:**什么记录都没有**(全 unsure / 全没答)→ 不产 run
|
|
94
|
-
* - `kind:"error"`:回答非法
|
|
95
|
-
*
|
|
96
|
-
* ⚠️ 一处容易写错的语义(本文档的注释最初就写错过,被测试抓出来):
|
|
97
|
-
* **「全部 notDone」不等于「零步」**。人确认"这两步这次都没做"是一条**合法且完整**的
|
|
98
|
-
* 记录(两条 `skipped`),它描述的事实是"走了这条流程、但一步都没执行"。
|
|
99
|
-
* 真正进 `nothing` 的只有**什么都没确认**(人没开口)——那才是没有信息可记。
|
|
100
|
-
*
|
|
101
|
-
* 关于 `notDone` 与 `unsure` 的差别(这是本模块最要紧的语义):
|
|
102
|
-
* - 人明确说 **notDone**(这一步这次没做)→ 记 `skipped: true`
|
|
103
|
-
* - 人 **没回答**或说 **unsure** → **什么都不写**(既不 skipped 也不 uses)
|
|
104
|
-
* 因为"没确认"与"确认没做"是两回事,混起来会把"忘了勾"记成"这步被跳过",
|
|
105
|
-
* 而跳过计数正是 `RETIRE_AFTER_SKIPS` 提议删步的依据——错一次就多一条错提议。
|
|
106
|
-
*/
|
|
107
|
-
export function runFromAnswers({ pipeline, answers, by = "human", triggeredBy = "", now = () => new Date() } = {}) {
|
|
108
|
-
const normalized = normalizeAnswers(answers, pipeline);
|
|
109
|
-
if (!normalized.ok) return { kind: "error", reason: normalized.reason, stepId: normalized.stepId, value: normalized.value };
|
|
110
|
-
|
|
111
|
-
const at = new Date(now()).toISOString();
|
|
112
|
-
const steps = [];
|
|
113
|
-
for (const step of pipeline.steps) {
|
|
114
|
-
const answer = normalized.answers[step.id];
|
|
115
|
-
if (answer === "done") {
|
|
116
|
-
steps.push({ id: step.id, at, skipped: false, detoured: false, refuted: false, note: "" });
|
|
117
|
-
} else if (answer === "notDone") {
|
|
118
|
-
steps.push({ id: step.id, at, skipped: true, detoured: false, refuted: false, note: "人确认这次没做" });
|
|
119
|
-
}
|
|
120
|
-
// unsure / 无回答 → 不写任何东西(见函数头注释)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (steps.length === 0) {
|
|
124
|
-
return {
|
|
125
|
-
kind: "nothing",
|
|
126
|
-
reason: "no-steps-confirmed",
|
|
127
|
-
// 如实回报:哪些是"人确认没做"、哪些是"没答"
|
|
128
|
-
notDone: pipeline.steps.filter((s) => normalized.answers[s.id] === "notDone").map((s) => s.id),
|
|
129
|
-
unanswered: pipeline.steps.filter((s) => normalized.answers[s.id] === undefined).map((s) => s.id),
|
|
130
|
-
unsure: pipeline.steps.filter((s) => normalized.answers[s.id] === "unsure").map((s) => s.id)
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
kind: "run",
|
|
136
|
-
run: { at, by, triggeredBy, steps },
|
|
137
|
-
// 没写进账的两种原因分开报——「确认没做」与「没答」不该长得一样
|
|
138
|
-
notDone: pipeline.steps.filter((s) => normalized.answers[s.id] === "notDone").map((s) => s.id),
|
|
139
|
-
unanswered: pipeline.steps.filter((s) => normalized.answers[s.id] === undefined).map((s) => s.id),
|
|
140
|
-
unsure: pipeline.steps.filter((s) => normalized.answers[s.id] === "unsure").map((s) => s.id)
|
|
141
|
-
};
|
|
142
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* M.O. 的人确认层:把「本次观察到什么」变成「哪几步真的走了」。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么必须由人来说
|
|
5
|
+
*
|
|
6
|
+
* 自动归属被实测否决(详见 mneme-research 的 08/09):
|
|
7
|
+
* - 拿单条命令匹配手写步骤表,唯一归属只有 **14.2%**;非发版会话也有 4.4%,**区分力仅 3 倍**
|
|
8
|
+
* - 放宽到"包含匹配",天花板仍 14%,且大头是 `git log` / `git diff` 这类通用命令
|
|
9
|
+
* - 工作流里的关键步骤 `gh pr merge --rebase` 在 21723 次调用里**出现 0 次**——
|
|
10
|
+
* 说明清单本身可能与实际行为脱节
|
|
11
|
+
*
|
|
12
|
+
* 归到本文档的一句话:**机器猜不准,而猜错的代价是把没走过的步骤记成走过**
|
|
13
|
+
* (`deriveState` 会据此把它染成 `trusted`)。所以这一步交给人。
|
|
14
|
+
*
|
|
15
|
+
* ## 三个设计决定
|
|
16
|
+
*
|
|
17
|
+
* 1. **提案不改动任何东西**。`buildProposal` 是纯函数,产出的是"给人看的待确认清单"。
|
|
18
|
+
* 写账只发生在 `confirm` 之后,且只写人点过的步骤。
|
|
19
|
+
* 2. **"不确定"是一个合法的答案**。人可以对某一步说"我不知道"——它既不记为走过,
|
|
20
|
+
* 也不记为跳过。这与账本那边「缺字段的行不许被算成 uses」是同一条纪律:
|
|
21
|
+
* **没确认 ≠ 没发生,也 ≠ 发生**。
|
|
22
|
+
* 3. **必须能报"整条流程都没走"**。人可以说"这次什么都没做",那就是一条**零步 run**
|
|
23
|
+
* ——但账本契约要求 `run.steps` 至少一项(见 `pipeline-runs` 的校验),所以那种情况
|
|
24
|
+
* 本模块如实返回 `{ kind: "nothing" }` 而不是伪造一条空 run。
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 造一份确认提案(纯函数,不落盘、不改任何状态)。
|
|
29
|
+
*
|
|
30
|
+
* 输入:
|
|
31
|
+
* - `pipeline`:本次要走的流程(含 steps)
|
|
32
|
+
* - `activities`:P1 产出的业务动作清单(`[{key, count, flags}]`),给人当参照
|
|
33
|
+
* - `hints`:自动归属的候选(可空),**只作提示**
|
|
34
|
+
*
|
|
35
|
+
* 输出刻意把两类信息分开:
|
|
36
|
+
* - `steps`:**流程自己的**步骤表(人在这里勾)
|
|
37
|
+
* - `activities`:**观察到的**业务动作(人在这对照)
|
|
38
|
+
* 两者不做自动配对——那正是被否决的那件事。
|
|
39
|
+
*/
|
|
40
|
+
export function buildProposal({ pipeline, activities = [], hints = null } = {}) {
|
|
41
|
+
if (!pipeline || !Array.isArray(pipeline.steps)) {
|
|
42
|
+
return { ok: false, reason: "no-pipeline" };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const steps = pipeline.steps.map((s) => ({
|
|
46
|
+
id: s.id,
|
|
47
|
+
title: s.title,
|
|
48
|
+
// 步骤表里人写的 tool 原文——给对照用,不是匹配依据
|
|
49
|
+
declared: typeof s.tool === "string" ? s.tool : "",
|
|
50
|
+
// 机器给的候选提示:**只给一个次数**,不给命令行原文(原文含绝对路径)。
|
|
51
|
+
// 它只是提示,不预选——人仍然要自己说这一步做没做。
|
|
52
|
+
hintCount: Number.isFinite(hints?.[s.id]) ? hints[s.id] : 0
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
ok: true,
|
|
57
|
+
pipelineId: pipeline.id,
|
|
58
|
+
steps,
|
|
59
|
+
activities: activities.map((a) => ({ key: a.key, count: a.count, flags: a.flags || [] }))
|
|
60
|
+
// 不返回 answers 词表:命令层有自己的解析表,写死两份只会漂。
|
|
61
|
+
// 也不返回 pipelineTitle / observations:零消费者(ponytail 复核点名的两处)。
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 校验并归一人的回答。
|
|
67
|
+
*
|
|
68
|
+
* `answers` 形如 `{ "P6": "done", "V2": "notDone" }`。
|
|
69
|
+
* **只认三类值**,其余一律拒——静默把非法值当"没发生"是编造的一种。
|
|
70
|
+
*/
|
|
71
|
+
export function normalizeAnswers(answersResult, pipeline) {
|
|
72
|
+
if (!pipeline || !Array.isArray(pipeline.steps)) return { ok: false, reason: "no-pipeline" };
|
|
73
|
+
if (!answersResult || typeof answersResult !== "object" || Array.isArray(answersResult)) {
|
|
74
|
+
return { ok: false, reason: "bad-answers" };
|
|
75
|
+
}
|
|
76
|
+
const ids = new Set(pipeline.steps.map((s) => s.id));
|
|
77
|
+
const clean = {};
|
|
78
|
+
for (const [id, value] of Object.entries(answersResult)) {
|
|
79
|
+
if (!ids.has(id)) return { ok: false, reason: "unknown-step", stepId: id };
|
|
80
|
+
if (value !== "done" && value !== "notDone" && value !== "unsure") {
|
|
81
|
+
return { ok: false, reason: "bad-answer-value", stepId: id, value };
|
|
82
|
+
}
|
|
83
|
+
clean[id] = value;
|
|
84
|
+
}
|
|
85
|
+
return { ok: true, answers: clean };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 把确认结果变成一条 run(**仍然不落盘**,落盘由调用方做)。
|
|
90
|
+
*
|
|
91
|
+
* 三种结局分得很清:
|
|
92
|
+
* - `kind:"run"`:**至少一条记录**(`done` 或 `notDone`)→ 产出一条 run
|
|
93
|
+
* - `kind:"nothing"`:**什么记录都没有**(全 unsure / 全没答)→ 不产 run
|
|
94
|
+
* - `kind:"error"`:回答非法
|
|
95
|
+
*
|
|
96
|
+
* ⚠️ 一处容易写错的语义(本文档的注释最初就写错过,被测试抓出来):
|
|
97
|
+
* **「全部 notDone」不等于「零步」**。人确认"这两步这次都没做"是一条**合法且完整**的
|
|
98
|
+
* 记录(两条 `skipped`),它描述的事实是"走了这条流程、但一步都没执行"。
|
|
99
|
+
* 真正进 `nothing` 的只有**什么都没确认**(人没开口)——那才是没有信息可记。
|
|
100
|
+
*
|
|
101
|
+
* 关于 `notDone` 与 `unsure` 的差别(这是本模块最要紧的语义):
|
|
102
|
+
* - 人明确说 **notDone**(这一步这次没做)→ 记 `skipped: true`
|
|
103
|
+
* - 人 **没回答**或说 **unsure** → **什么都不写**(既不 skipped 也不 uses)
|
|
104
|
+
* 因为"没确认"与"确认没做"是两回事,混起来会把"忘了勾"记成"这步被跳过",
|
|
105
|
+
* 而跳过计数正是 `RETIRE_AFTER_SKIPS` 提议删步的依据——错一次就多一条错提议。
|
|
106
|
+
*/
|
|
107
|
+
export function runFromAnswers({ pipeline, answers, by = "human", triggeredBy = "", now = () => new Date() } = {}) {
|
|
108
|
+
const normalized = normalizeAnswers(answers, pipeline);
|
|
109
|
+
if (!normalized.ok) return { kind: "error", reason: normalized.reason, stepId: normalized.stepId, value: normalized.value };
|
|
110
|
+
|
|
111
|
+
const at = new Date(now()).toISOString();
|
|
112
|
+
const steps = [];
|
|
113
|
+
for (const step of pipeline.steps) {
|
|
114
|
+
const answer = normalized.answers[step.id];
|
|
115
|
+
if (answer === "done") {
|
|
116
|
+
steps.push({ id: step.id, at, skipped: false, detoured: false, refuted: false, note: "" });
|
|
117
|
+
} else if (answer === "notDone") {
|
|
118
|
+
steps.push({ id: step.id, at, skipped: true, detoured: false, refuted: false, note: "人确认这次没做" });
|
|
119
|
+
}
|
|
120
|
+
// unsure / 无回答 → 不写任何东西(见函数头注释)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (steps.length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
kind: "nothing",
|
|
126
|
+
reason: "no-steps-confirmed",
|
|
127
|
+
// 如实回报:哪些是"人确认没做"、哪些是"没答"
|
|
128
|
+
notDone: pipeline.steps.filter((s) => normalized.answers[s.id] === "notDone").map((s) => s.id),
|
|
129
|
+
unanswered: pipeline.steps.filter((s) => normalized.answers[s.id] === undefined).map((s) => s.id),
|
|
130
|
+
unsure: pipeline.steps.filter((s) => normalized.answers[s.id] === "unsure").map((s) => s.id)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
kind: "run",
|
|
136
|
+
run: { at, by, triggeredBy, steps },
|
|
137
|
+
// 没写进账的两种原因分开报——「确认没做」与「没答」不该长得一样
|
|
138
|
+
notDone: pipeline.steps.filter((s) => normalized.answers[s.id] === "notDone").map((s) => s.id),
|
|
139
|
+
unanswered: pipeline.steps.filter((s) => normalized.answers[s.id] === undefined).map((s) => s.id),
|
|
140
|
+
unsure: pipeline.steps.filter((s) => normalized.answers[s.id] === "unsure").map((s) => s.id)
|
|
141
|
+
};
|
|
142
|
+
}
|
package/lib/pipeline-exec.js
CHANGED
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* M.O. 的**执行窗口**:一次 run 从「有人决定按这条工作流走一遍」开始记账,到这一遍结束为止。
|
|
3
|
-
*
|
|
4
|
-
* ## 为什么是「窗口」而不是「自动观察一切」
|
|
5
|
-
*
|
|
6
|
-
* 这是本模块存在的核心理由,也是接线前必须先想清楚的一件事:
|
|
7
|
-
*
|
|
8
|
-
* 宿主钩子(`tools/pre-execute` 等)能看到「这次调了什么工具」,但它**看不到「正在走哪条
|
|
9
|
-
* 工作流」**。工作流的 `trigger` 字段是人话(`{kind:"phrase", value:"发版"}`),今天没有任何
|
|
10
|
-
* 代码消费它;想靠它自动判定「用户说这句话就意味着开始走某条流程」,只能猜——而猜错的代价
|
|
11
|
-
* 是把一条**从没跑过的流程**记成跑过。那正是本仓反复防的「伪造正面证据」。
|
|
12
|
-
*
|
|
13
|
-
* 所以窗口的边界由**显式动作**划:有人(或按人指令的 agent)说明「现在按 X 走一遍」,窗口才开;
|
|
14
|
-
* 窗口内的工具调用才可能被记账;窗口一关就 flush 成一条 run。**窗口之外发生的一切一个字都不记**
|
|
15
|
-
* ——漏记账是可接受的,编造账不是。
|
|
16
|
-
*
|
|
17
|
-
* ## 一次观察有三种结局,必须分开
|
|
18
|
-
*
|
|
19
|
-
* - **归属成功**:写进这次 run 的 `steps`
|
|
20
|
-
* - **归属不了**:进 `unattributed`。它在图上是「没走这条路的痕迹」,即绕开的候选——
|
|
21
|
-
* 但它**还没被确认**是这条流程的一步,所以不进 `steps`
|
|
22
|
-
* - **归属有歧义**:进 `ambiguous`,**不猜**,留给人消歧
|
|
23
|
-
*
|
|
24
|
-
* 三者分开报的纪律与账本那边一致:**「认不出来」与「没做」必须长得不一样**,
|
|
25
|
-
* 否则一份全是 unattributed 的账会被读成「这次什么也没做」。
|
|
26
|
-
*
|
|
27
|
-
* ## 唯一没被记下来的东西:失败
|
|
28
|
-
*
|
|
29
|
-
* 账本契约要求一条 run 至少有一个步骤(`run.steps 至少要有一条`),所以「开了窗口、
|
|
30
|
-
* 一步都没走成」目前**落不成账**。这是已知缺口,本模块如实返回 `reason:"no-steps"`,
|
|
31
|
-
* 不伪造一条空 run 去凑。要真正表示失败,得先改账本契约(让零步 run 合法)。
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import { attributionCandidates } from "./pipeline-attribution.js";
|
|
35
|
-
|
|
36
|
-
/** 一次观察被拒的三种原因;调用方据此决定要不要请人消歧。 */
|
|
37
|
-
export const OBSERVED = "observed";
|
|
38
|
-
export const UNATTRIBUTED = "unattributed";
|
|
39
|
-
export const AMBIGUOUS = "ambiguous";
|
|
40
|
-
|
|
41
|
-
function toIso(value) {
|
|
42
|
-
if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
|
|
43
|
-
const ms = typeof value === "number" ? value : Date.parse(String(value));
|
|
44
|
-
if (!Number.isFinite(ms)) throw new Error("时间必须能解析成 ISO");
|
|
45
|
-
return new Date(ms).toISOString();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 开一个记录窗口。
|
|
50
|
-
*
|
|
51
|
-
* `pipeline` 是这次要走的**那一条**工作流(由调用方点名,不由本模块猜)。
|
|
52
|
-
* `by` / `triggeredBy` 落进 run 的描述字段:账本里「谁做的」「为什么这次会跑」要有出处。
|
|
53
|
-
*/
|
|
54
|
-
export function openRun({ pipeline, by = "main", triggeredBy = "", now = () => new Date() } = {}) {
|
|
55
|
-
if (!pipeline || !Array.isArray(pipeline.steps)) {
|
|
56
|
-
throw new Error("openRun 需要一条工作流(含 steps)");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const startedAt = toIso(now());
|
|
60
|
-
/** stepId → { at, refuted, repeats };同一步在一个窗口里只计一次,重复的进 repeats。 */
|
|
61
|
-
const steps = new Map();
|
|
62
|
-
const unattributed = [];
|
|
63
|
-
const ambiguous = [];
|
|
64
|
-
let observations = 0;
|
|
65
|
-
let closed = null;
|
|
66
|
-
|
|
67
|
-
/** 记下一步。同一步重复出现时不重复计数(`tallyRuns` 按 run 计数,重复会让 uses 虚高)。 */
|
|
68
|
-
function record(stepId, at, refuted) {
|
|
69
|
-
const existing = steps.get(stepId);
|
|
70
|
-
if (existing) {
|
|
71
|
-
existing.repeats += 1;
|
|
72
|
-
// 任何一次失败都算被推翻过——「跑砸过一次」不该被后来的成功抹掉。
|
|
73
|
-
if (refuted) existing.refuted = true;
|
|
74
|
-
return existing;
|
|
75
|
-
}
|
|
76
|
-
const entry = { id: stepId, at, refuted, repeats: 0 };
|
|
77
|
-
steps.set(stepId, entry);
|
|
78
|
-
return entry;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
/**
|
|
83
|
-
* 观察一次工具动作。
|
|
84
|
-
*
|
|
85
|
-
* `stepId` 是**人确认过的归属**:给了就直接记,不再走匹配(人说了算)。
|
|
86
|
-
* 不给则按归属规则自动判——只有 `exact` 且唯一时才自动记账;
|
|
87
|
-
* 多候选或只有 weak 一律进 `ambiguous`,**绝不自选一条**。
|
|
88
|
-
*/
|
|
89
|
-
observe(observation, { stepId = null, at = null } = {}) {
|
|
90
|
-
if (closed) return { accepted: false, result: "run-closed" };
|
|
91
|
-
observations += 1;
|
|
92
|
-
const when = toIso(at ?? now());
|
|
93
|
-
const failed = observation && observation.ok === false;
|
|
94
|
-
|
|
95
|
-
if (stepId) {
|
|
96
|
-
record(stepId, when, failed);
|
|
97
|
-
return { accepted: true, result: OBSERVED, stepId, resolved: "explicit" };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const candidates = attributionCandidates(observation, pipeline);
|
|
101
|
-
if (candidates.length === 0) {
|
|
102
|
-
unattributed.push({ observation, at: when });
|
|
103
|
-
return { accepted: false, result: UNATTRIBUTED };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const exact = candidates.filter((c) => c.confidence === "exact");
|
|
107
|
-
if (candidates.length === 1 && exact.length === 1) {
|
|
108
|
-
record(exact[0].stepId, when, failed);
|
|
109
|
-
return { accepted: true, result: OBSERVED, stepId: exact[0].stepId, resolved: "exact" };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 弱匹配、多候选都走这里:图省事挑一条就是替人做决定,而挑错的代价是染色错误的一步。
|
|
113
|
-
ambiguous.push({ observation, at: when, candidates });
|
|
114
|
-
return { accepted: false, result: AMBIGUOUS, candidates };
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
/** 只看状态,不结账。给「这一遍走到哪了」这种实时查询用。 */
|
|
118
|
-
state() {
|
|
119
|
-
return {
|
|
120
|
-
open: closed === null,
|
|
121
|
-
pipelineId: pipeline.id,
|
|
122
|
-
startedAt,
|
|
123
|
-
observations,
|
|
124
|
-
recorded: [...steps.values()].map((s) => s.id),
|
|
125
|
-
unattributed: unattributed.length,
|
|
126
|
-
ambiguous: ambiguous.length
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 结账:产出一条**还没落盘**的 run(落盘由调用方做 `appendRun`,本模块不碰文件系统)。
|
|
132
|
-
*
|
|
133
|
-
* `steps` 里只有**真被观察到**的步骤。没观察到的步骤既不写 `skipped` 也不写 `used`——
|
|
134
|
-
* 缺席不等于跳过(可能是归属没认出来),把它写成跳过就是编造。它们进 `unobserved`,
|
|
135
|
-
* 让人自己看。
|
|
136
|
-
*/
|
|
137
|
-
close() {
|
|
138
|
-
if (closed) return closed;
|
|
139
|
-
const runSteps = [...steps.values()].map((s) => ({
|
|
140
|
-
id: s.id,
|
|
141
|
-
at: s.at,
|
|
142
|
-
skipped: false,
|
|
143
|
-
detoured: false,
|
|
144
|
-
refuted: s.refuted === true,
|
|
145
|
-
note: s.repeats > 0 ? `这一步在窗口内被观察到 ${s.repeats + 1} 次,只计一次` : ""
|
|
146
|
-
}));
|
|
147
|
-
const observedIds = new Set(runSteps.map((s) => s.id));
|
|
148
|
-
const unobserved = pipeline.steps.map((s) => s.id).filter((id) => !observedIds.has(id));
|
|
149
|
-
|
|
150
|
-
const base = {
|
|
151
|
-
startedAt,
|
|
152
|
-
endedAt: toIso(now()),
|
|
153
|
-
pipelineId: pipeline.id,
|
|
154
|
-
observations,
|
|
155
|
-
unattributed,
|
|
156
|
-
ambiguous,
|
|
157
|
-
unobserved
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (runSteps.length === 0) {
|
|
161
|
-
// 契约要求至少一步,所以这一步落不成账。如实说,不伪造。
|
|
162
|
-
closed = { ...base, run: null, reason: "no-steps" };
|
|
163
|
-
return closed;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
closed = {
|
|
167
|
-
...base,
|
|
168
|
-
run: { at: startedAt, by, triggeredBy, steps: runSteps },
|
|
169
|
-
reason: null
|
|
170
|
-
};
|
|
171
|
-
return closed;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* M.O. 的**执行窗口**:一次 run 从「有人决定按这条工作流走一遍」开始记账,到这一遍结束为止。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么是「窗口」而不是「自动观察一切」
|
|
5
|
+
*
|
|
6
|
+
* 这是本模块存在的核心理由,也是接线前必须先想清楚的一件事:
|
|
7
|
+
*
|
|
8
|
+
* 宿主钩子(`tools/pre-execute` 等)能看到「这次调了什么工具」,但它**看不到「正在走哪条
|
|
9
|
+
* 工作流」**。工作流的 `trigger` 字段是人话(`{kind:"phrase", value:"发版"}`),今天没有任何
|
|
10
|
+
* 代码消费它;想靠它自动判定「用户说这句话就意味着开始走某条流程」,只能猜——而猜错的代价
|
|
11
|
+
* 是把一条**从没跑过的流程**记成跑过。那正是本仓反复防的「伪造正面证据」。
|
|
12
|
+
*
|
|
13
|
+
* 所以窗口的边界由**显式动作**划:有人(或按人指令的 agent)说明「现在按 X 走一遍」,窗口才开;
|
|
14
|
+
* 窗口内的工具调用才可能被记账;窗口一关就 flush 成一条 run。**窗口之外发生的一切一个字都不记**
|
|
15
|
+
* ——漏记账是可接受的,编造账不是。
|
|
16
|
+
*
|
|
17
|
+
* ## 一次观察有三种结局,必须分开
|
|
18
|
+
*
|
|
19
|
+
* - **归属成功**:写进这次 run 的 `steps`
|
|
20
|
+
* - **归属不了**:进 `unattributed`。它在图上是「没走这条路的痕迹」,即绕开的候选——
|
|
21
|
+
* 但它**还没被确认**是这条流程的一步,所以不进 `steps`
|
|
22
|
+
* - **归属有歧义**:进 `ambiguous`,**不猜**,留给人消歧
|
|
23
|
+
*
|
|
24
|
+
* 三者分开报的纪律与账本那边一致:**「认不出来」与「没做」必须长得不一样**,
|
|
25
|
+
* 否则一份全是 unattributed 的账会被读成「这次什么也没做」。
|
|
26
|
+
*
|
|
27
|
+
* ## 唯一没被记下来的东西:失败
|
|
28
|
+
*
|
|
29
|
+
* 账本契约要求一条 run 至少有一个步骤(`run.steps 至少要有一条`),所以「开了窗口、
|
|
30
|
+
* 一步都没走成」目前**落不成账**。这是已知缺口,本模块如实返回 `reason:"no-steps"`,
|
|
31
|
+
* 不伪造一条空 run 去凑。要真正表示失败,得先改账本契约(让零步 run 合法)。
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { attributionCandidates } from "./pipeline-attribution.js";
|
|
35
|
+
|
|
36
|
+
/** 一次观察被拒的三种原因;调用方据此决定要不要请人消歧。 */
|
|
37
|
+
export const OBSERVED = "observed";
|
|
38
|
+
export const UNATTRIBUTED = "unattributed";
|
|
39
|
+
export const AMBIGUOUS = "ambiguous";
|
|
40
|
+
|
|
41
|
+
function toIso(value) {
|
|
42
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
|
|
43
|
+
const ms = typeof value === "number" ? value : Date.parse(String(value));
|
|
44
|
+
if (!Number.isFinite(ms)) throw new Error("时间必须能解析成 ISO");
|
|
45
|
+
return new Date(ms).toISOString();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 开一个记录窗口。
|
|
50
|
+
*
|
|
51
|
+
* `pipeline` 是这次要走的**那一条**工作流(由调用方点名,不由本模块猜)。
|
|
52
|
+
* `by` / `triggeredBy` 落进 run 的描述字段:账本里「谁做的」「为什么这次会跑」要有出处。
|
|
53
|
+
*/
|
|
54
|
+
export function openRun({ pipeline, by = "main", triggeredBy = "", now = () => new Date() } = {}) {
|
|
55
|
+
if (!pipeline || !Array.isArray(pipeline.steps)) {
|
|
56
|
+
throw new Error("openRun 需要一条工作流(含 steps)");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const startedAt = toIso(now());
|
|
60
|
+
/** stepId → { at, refuted, repeats };同一步在一个窗口里只计一次,重复的进 repeats。 */
|
|
61
|
+
const steps = new Map();
|
|
62
|
+
const unattributed = [];
|
|
63
|
+
const ambiguous = [];
|
|
64
|
+
let observations = 0;
|
|
65
|
+
let closed = null;
|
|
66
|
+
|
|
67
|
+
/** 记下一步。同一步重复出现时不重复计数(`tallyRuns` 按 run 计数,重复会让 uses 虚高)。 */
|
|
68
|
+
function record(stepId, at, refuted) {
|
|
69
|
+
const existing = steps.get(stepId);
|
|
70
|
+
if (existing) {
|
|
71
|
+
existing.repeats += 1;
|
|
72
|
+
// 任何一次失败都算被推翻过——「跑砸过一次」不该被后来的成功抹掉。
|
|
73
|
+
if (refuted) existing.refuted = true;
|
|
74
|
+
return existing;
|
|
75
|
+
}
|
|
76
|
+
const entry = { id: stepId, at, refuted, repeats: 0 };
|
|
77
|
+
steps.set(stepId, entry);
|
|
78
|
+
return entry;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
/**
|
|
83
|
+
* 观察一次工具动作。
|
|
84
|
+
*
|
|
85
|
+
* `stepId` 是**人确认过的归属**:给了就直接记,不再走匹配(人说了算)。
|
|
86
|
+
* 不给则按归属规则自动判——只有 `exact` 且唯一时才自动记账;
|
|
87
|
+
* 多候选或只有 weak 一律进 `ambiguous`,**绝不自选一条**。
|
|
88
|
+
*/
|
|
89
|
+
observe(observation, { stepId = null, at = null } = {}) {
|
|
90
|
+
if (closed) return { accepted: false, result: "run-closed" };
|
|
91
|
+
observations += 1;
|
|
92
|
+
const when = toIso(at ?? now());
|
|
93
|
+
const failed = observation && observation.ok === false;
|
|
94
|
+
|
|
95
|
+
if (stepId) {
|
|
96
|
+
record(stepId, when, failed);
|
|
97
|
+
return { accepted: true, result: OBSERVED, stepId, resolved: "explicit" };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const candidates = attributionCandidates(observation, pipeline);
|
|
101
|
+
if (candidates.length === 0) {
|
|
102
|
+
unattributed.push({ observation, at: when });
|
|
103
|
+
return { accepted: false, result: UNATTRIBUTED };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const exact = candidates.filter((c) => c.confidence === "exact");
|
|
107
|
+
if (candidates.length === 1 && exact.length === 1) {
|
|
108
|
+
record(exact[0].stepId, when, failed);
|
|
109
|
+
return { accepted: true, result: OBSERVED, stepId: exact[0].stepId, resolved: "exact" };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 弱匹配、多候选都走这里:图省事挑一条就是替人做决定,而挑错的代价是染色错误的一步。
|
|
113
|
+
ambiguous.push({ observation, at: when, candidates });
|
|
114
|
+
return { accepted: false, result: AMBIGUOUS, candidates };
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/** 只看状态,不结账。给「这一遍走到哪了」这种实时查询用。 */
|
|
118
|
+
state() {
|
|
119
|
+
return {
|
|
120
|
+
open: closed === null,
|
|
121
|
+
pipelineId: pipeline.id,
|
|
122
|
+
startedAt,
|
|
123
|
+
observations,
|
|
124
|
+
recorded: [...steps.values()].map((s) => s.id),
|
|
125
|
+
unattributed: unattributed.length,
|
|
126
|
+
ambiguous: ambiguous.length
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 结账:产出一条**还没落盘**的 run(落盘由调用方做 `appendRun`,本模块不碰文件系统)。
|
|
132
|
+
*
|
|
133
|
+
* `steps` 里只有**真被观察到**的步骤。没观察到的步骤既不写 `skipped` 也不写 `used`——
|
|
134
|
+
* 缺席不等于跳过(可能是归属没认出来),把它写成跳过就是编造。它们进 `unobserved`,
|
|
135
|
+
* 让人自己看。
|
|
136
|
+
*/
|
|
137
|
+
close() {
|
|
138
|
+
if (closed) return closed;
|
|
139
|
+
const runSteps = [...steps.values()].map((s) => ({
|
|
140
|
+
id: s.id,
|
|
141
|
+
at: s.at,
|
|
142
|
+
skipped: false,
|
|
143
|
+
detoured: false,
|
|
144
|
+
refuted: s.refuted === true,
|
|
145
|
+
note: s.repeats > 0 ? `这一步在窗口内被观察到 ${s.repeats + 1} 次,只计一次` : ""
|
|
146
|
+
}));
|
|
147
|
+
const observedIds = new Set(runSteps.map((s) => s.id));
|
|
148
|
+
const unobserved = pipeline.steps.map((s) => s.id).filter((id) => !observedIds.has(id));
|
|
149
|
+
|
|
150
|
+
const base = {
|
|
151
|
+
startedAt,
|
|
152
|
+
endedAt: toIso(now()),
|
|
153
|
+
pipelineId: pipeline.id,
|
|
154
|
+
observations,
|
|
155
|
+
unattributed,
|
|
156
|
+
ambiguous,
|
|
157
|
+
unobserved
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (runSteps.length === 0) {
|
|
161
|
+
// 契约要求至少一步,所以这一步落不成账。如实说,不伪造。
|
|
162
|
+
closed = { ...base, run: null, reason: "no-steps" };
|
|
163
|
+
return closed;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
closed = {
|
|
167
|
+
...base,
|
|
168
|
+
run: { at: startedAt, by, triggeredBy, steps: runSteps },
|
|
169
|
+
reason: null
|
|
170
|
+
};
|
|
171
|
+
return closed;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|