@danceiny/gotry 0.0.1-rc.9 → 0.2.0-rc.28
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/README.md +172 -219
- package/README.zh-CN.md +222 -0
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-booking-copilot.js +53 -0
- package/bin/gotry-bootstrap.js +1788 -0
- package/bin/gotry-inner.js +733 -83
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +27 -0
- package/bin/gotry-runtime-resolution.js +49 -0
- package/bin/gotry.js +1 -1
- package/client/client.js +203 -0
- package/cordis.gotry-patch.yml +80 -56
- package/data/airline-airports.json +39 -0
- package/data/stations-12306-verify.json +524 -0
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +101 -146
- package/dist/capabilities/anything.js +161 -94
- package/dist/capabilities/artifacts.js +370 -0
- package/dist/capabilities/channel-health.js +131 -0
- package/dist/capabilities/channel-registry.js +256 -0
- package/dist/capabilities/doctor.js +553 -0
- package/dist/capabilities/effect.js +394 -0
- package/dist/capabilities/external-event.js +149 -0
- package/dist/capabilities/fact-log.js +55 -0
- package/dist/capabilities/flyai-config.js +337 -0
- package/dist/capabilities/flyai.js +947 -110
- package/dist/capabilities/fx-contract.js +293 -0
- package/dist/capabilities/geo-atlas.js +246 -0
- package/dist/capabilities/ground-transfer.js +676 -0
- package/dist/capabilities/hbcli.js +260 -123
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/itinerary-artifact.js +190 -0
- package/dist/capabilities/itinerary-deck-artifact.js +194 -0
- package/dist/capabilities/itinerary-deck-export.js +339 -0
- package/dist/capabilities/lavish-local.js +924 -0
- package/dist/capabilities/model-override.js +38 -0
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/provider-detail-sanitize.js +64 -0
- package/dist/capabilities/resilience.js +191 -0
- package/dist/capabilities/route-provider-conformance.js +341 -0
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +120 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +222 -0
- package/dist/capabilities/session/adapters/dida-portal.js +274 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +342 -0
- package/dist/capabilities/session/benchmark.js +189 -0
- package/dist/capabilities/session/bridge-ledger.js +224 -0
- package/dist/capabilities/session/extension-bridge.js +636 -0
- package/dist/capabilities/session/extension-channel.js +109 -0
- package/dist/capabilities/session/extension-distribution.js +230 -0
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +114 -0
- package/dist/capabilities/session/health-watch.js +215 -0
- package/dist/capabilities/session/read-guard.js +75 -24
- package/dist/capabilities/session/static-flight-golden.js +150 -0
- package/dist/capabilities/session/transport.js +97 -83
- package/dist/capabilities/session/wizard.js +117 -0
- package/dist/capabilities/session-consent.js +178 -0
- package/dist/capabilities/session-login.js +170 -0
- package/dist/capabilities/session-search.js +635 -44
- package/dist/capabilities/spawn-bounded.js +203 -0
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +140 -0
- package/dist/capabilities/weather.js +243 -138
- package/dist/data/session-golden-20.json +25 -0
- package/dist/data/sf-golden-manifest.json +103 -0
- package/dist/data/sf-static-routes.json +91 -0
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +242 -0
- package/dist/scripts/agent-planning-turn-deadline-tests.js +185 -0
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-error-tests.js +354 -0
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +50 -50
- package/dist/scripts/anything-path-measure-tests.js +133 -0
- package/dist/scripts/anything-path-measure.js +150 -0
- package/dist/scripts/anything-tests.js +250 -60
- package/dist/scripts/artifact-client-contract-tests.js +220 -0
- package/dist/scripts/artifacts-capability-tests.js +727 -0
- package/dist/scripts/async-collect.js +50 -11
- package/dist/scripts/benchmark-environment-bridge-e2e.js +741 -0
- package/dist/scripts/benchmark-environment-bridge-tests.js +1452 -0
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +126 -0
- package/dist/scripts/booking-copilot-availability-policy-tests.js +265 -0
- package/dist/scripts/booking-copilot-bin-proof-tests.js +66 -0
- package/dist/scripts/booking-copilot-cleanup-proof-helper.js +76 -0
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +144 -0
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +424 -0
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +2839 -0
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +309 -0
- package/dist/scripts/booking-copilot-dsh-readiness-proof.js +238 -0
- package/dist/scripts/booking-copilot-dsh-warmer-proof.js +126 -0
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +156 -0
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +70 -0
- package/dist/scripts/booking-copilot-intent-proof-tests.js +422 -0
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +184 -0
- package/dist/scripts/booking-copilot-planner-live-probe.js +95 -0
- package/dist/scripts/booking-copilot-proof-runtime.js +42 -0
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +210 -0
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +2349 -0
- package/dist/scripts/booking-copilot-server-proof-tests.js +784 -0
- package/dist/scripts/booking-copilot-startup-proof-tests.js +94 -0
- package/dist/scripts/booking-executor-tests.js +80 -0
- package/dist/scripts/booking-recovery-chain-tests.js +182 -0
- package/dist/scripts/booking-saga-tests.js +125 -0
- package/dist/scripts/booking-surface-contract-proof-tests.js +212 -0
- package/dist/scripts/bootstrap-tests.js +1549 -0
- package/dist/scripts/bridge-events-tests.js +239 -0
- package/dist/scripts/bridge-ledger-tests.js +428 -0
- package/dist/scripts/build-changelog.js +238 -0
- package/dist/scripts/build-metrics-report.js +331 -0
- package/dist/scripts/changelog-tests.js +118 -0
- package/dist/scripts/channel-probe-tests.js +103 -0
- package/dist/scripts/channel-probe.js +169 -0
- package/dist/scripts/channel-registry-tests.js +208 -0
- package/dist/scripts/city-scenario-tier-tests.js +234 -0
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-runner-stop-tests.js +311 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +600 -0
- package/dist/scripts/drill-multiuser-ledger-tests.js +1076 -0
- package/dist/scripts/drill-sdk-descendant-cleanup-tests.js +606 -0
- package/dist/scripts/drill-w2a-sensor-tests.js +788 -0
- package/dist/scripts/dsh-artifact-e2e.js +279 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +675 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-llm-tests.js +437 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +182 -0
- package/dist/scripts/dsh-runtime-closure.js +162 -0
- package/dist/scripts/dsh-session-v3-migration-proof.js +109 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +150 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +539 -0
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +174 -0
- package/dist/scripts/evaluation-contract-tests.js +268 -0
- package/dist/scripts/extension-distribution-cli.js +39 -0
- package/dist/scripts/extension-distribution-tests.js +443 -0
- package/dist/scripts/extension-tests.js +988 -0
- package/dist/scripts/external-event-tests.js +350 -0
- package/dist/scripts/fact-gate-tests.js +869 -0
- package/dist/scripts/fixtures/extension-bridge-unref-child.mjs +21 -0
- package/dist/scripts/flight-hotel-anchor-363-registered-e2e.js +169 -0
- package/dist/scripts/flight-malformed-tests.js +159 -0
- package/dist/scripts/flyai-setup-tests.js +228 -0
- package/dist/scripts/flyai-setup-tool-tests.js +54 -0
- package/dist/scripts/flyai-tests.js +692 -0
- package/dist/scripts/flyai-tool-registration-contract-tests.js +91 -0
- package/dist/scripts/fx-contract-tests.js +201 -0
- package/dist/scripts/g5-guard-tests.js +150 -0
- package/dist/scripts/g5-guard.js +193 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/geo-atlas-tests.js +283 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +206 -0
- package/dist/scripts/gotry-try-demo-tests.js +79 -0
- package/dist/scripts/gotry-try-demo.js +49 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +159 -0
- package/dist/scripts/hbcli-live-optin-tests.js +96 -0
- package/dist/scripts/hbcli-release-contract-tests.js +122 -0
- package/dist/scripts/hbcli-tests.js +121 -69
- package/dist/scripts/health-watch-cli.js +73 -0
- package/dist/scripts/hotel-date-gate-tests.js +496 -0
- package/dist/scripts/hotelbyte-reconcile-tests.js +141 -0
- package/dist/scripts/hotelbyte-spawn-e2e-tests.js +373 -0
- package/dist/scripts/i18n-tests.js +41 -0
- package/dist/scripts/incident-tests.js +281 -87
- package/dist/scripts/issue-194-job-id-guard-tests.js +106 -0
- package/dist/scripts/issue-233-cancel-refund-commission-tests.js +392 -0
- package/dist/scripts/issue-271-liveness-tests.js +569 -0
- package/dist/scripts/issue-289-model-retry-real-tests.js +296 -0
- package/dist/scripts/issue-289-model-retry-tests.js +441 -0
- package/dist/scripts/issue-289-web-retry-e2e.js +630 -0
- package/dist/scripts/issue-338-home-city-e2e.js +577 -0
- package/dist/scripts/issue343-real-entry-e2e.js +160 -0
- package/dist/scripts/issue436-persisted-routing-e2e.js +247 -0
- package/dist/scripts/itinerary-artifact-tests.js +368 -0
- package/dist/scripts/itinerary-deck-artifact-tests.js +416 -0
- package/dist/scripts/itinerary-deck-export-tests.js +552 -0
- package/dist/scripts/itinerary-deck-tests.js +435 -0
- package/dist/scripts/itinerary-html-tests.js +426 -0
- package/dist/scripts/journey-tests.js +22 -27
- package/dist/scripts/kernel-manifest-evidence.js +109 -0
- package/dist/scripts/kernel-manifest-gate.js +126 -0
- package/dist/scripts/kernel-manifest-tests.js +212 -0
- package/dist/scripts/kernel-manifest-trace-entry.js +7 -0
- package/dist/scripts/kernel-manifest-trace-hook.mjs +28 -0
- package/dist/scripts/kernel-manifest.js +369 -0
- package/dist/scripts/lavish-local-tests.js +1226 -0
- package/dist/scripts/lavish-product-tools-tests.js +537 -0
- package/dist/scripts/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +382 -246
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/m3-cohort-tests.js +714 -0
- package/dist/scripts/m3-cohort.js +310 -0
- package/dist/scripts/managed-dsh-cleanup-diagnostic-proof.js +184 -0
- package/dist/scripts/managed-dsh-close-failure-proof.js +172 -0
- package/dist/scripts/managed-dsh-run-port-proof.js +153 -0
- package/dist/scripts/managed-dsh-terminal-proof.js +252 -0
- package/dist/scripts/map-tools-vendor-package-proof.js +365 -0
- package/dist/scripts/memory-capture-tests.js +242 -61
- package/dist/scripts/memory-decay-tests.js +18 -52
- package/dist/scripts/memory-lifecycle-tests.js +657 -0
- package/dist/scripts/memory-lifecycle.js +196 -0
- package/dist/scripts/memory-metrics.js +24 -12
- package/dist/scripts/memory-value-report.js +631 -0
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +151 -0
- package/dist/scripts/model-override-e2e.js +137 -0
- package/dist/scripts/nightly-evidence-tests.js +203 -0
- package/dist/scripts/nightly-evidence.js +303 -0
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/observability-tests.js +381 -0
- package/dist/scripts/onboarding-tests.js +161 -0
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/outcome-projection-tests.js +427 -0
- package/dist/scripts/persona-sim-tests.js +603 -0
- package/dist/scripts/persona-sim.js +1081 -0
- package/dist/scripts/persona-surface-guard-tests.js +49 -0
- package/dist/scripts/pnpm-dsh-closure-proof.js +17 -0
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +340 -0
- package/dist/scripts/price-drift-watch.js +447 -0
- package/dist/scripts/probe-poi-tests.js +53 -45
- package/dist/scripts/product-metrics.js +483 -0
- package/dist/scripts/publish-preverify.js +110 -0
- package/dist/scripts/realtime-pricing-tests.js +76 -0
- package/dist/scripts/recall-tests.js +513 -0
- package/dist/scripts/replay-async.js +22 -18
- package/dist/scripts/replay-real.js +18 -18
- package/dist/scripts/replay.js +54 -82
- package/dist/scripts/route-provider-conformance-tests.js +391 -0
- package/dist/scripts/session-attach-diagnose.js +20 -20
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +180 -0
- package/dist/scripts/session-dida-live-e2e.js +199 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-link-tests.js +478 -0
- package/dist/scripts/session-login.js +27 -46
- package/dist/scripts/session-tests.js +796 -96
- package/dist/scripts/session-zone-crash.js +49 -0
- package/dist/scripts/session-zone-ledger-tests.js +432 -0
- package/dist/scripts/session-zone-observation-tests.js +492 -0
- package/dist/scripts/session-zone-observe.js +135 -0
- package/dist/scripts/session-zone-wiring-tests.js +566 -0
- package/dist/scripts/session-zones-tests.js +374 -0
- package/dist/scripts/sf-live-benchmark.js +367 -0
- package/dist/scripts/sf-live-challenge-stop-tests.js +453 -0
- package/dist/scripts/sf-live-cli-tests.js +92 -0
- package/dist/scripts/sf-live-options.js +46 -0
- package/dist/scripts/sf-manifest-tests.js +30 -0
- package/dist/scripts/sf-manifest.js +33 -0
- package/dist/scripts/sf-soft-score-tests.js +67 -0
- package/dist/scripts/sf-summary-tests.js +324 -0
- package/dist/scripts/sf-summary.js +525 -0
- package/dist/scripts/share-tests.js +432 -0
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +28 -11
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +21 -0
- package/dist/scripts/smoke.js +690 -246
- package/dist/scripts/spawn-bounded-abort-tests.js +244 -0
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +565 -198
- package/dist/scripts/state-repair-cli.js +66 -0
- package/dist/scripts/state-repair-tests.js +140 -0
- package/dist/scripts/static-golden-tests.js +165 -0
- package/dist/scripts/time-eval-tests.js +212 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +169 -0
- package/dist/scripts/turn-handoff-collect.js +161 -0
- package/dist/scripts/turn-policy-tests.js +55 -0
- package/dist/scripts/typed-contract-canary.js +176 -0
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +60 -20
- package/dist/scripts/visa-policy-tests.js +62 -0
- package/dist/scripts/weather-tests.js +276 -41
- package/dist/scripts/wish-channel-gate-tests.js +54 -0
- package/dist/scripts/write-gate-tests.js +522 -0
- package/dist/scripts/z3-lifecycle-fault-tests.js +188 -0
- package/dist/scripts/z3-lifecycle-tests.js +170 -0
- package/dist/scripts/z3-race-repeat-tests.js +39 -0
- package/dist/scripts/z3-race-tests.js +46 -0
- package/dist/src/artifact-gate.js +829 -0
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +92 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +551 -0
- package/dist/src/benchmark-agent-conformance.js +578 -0
- package/dist/src/benchmark-environment-bridge.js +515 -0
- package/dist/src/benchmark-headless-child-diagnostics.js +166 -0
- package/dist/src/benchmark-tool-isolation.js +151 -0
- package/dist/src/bookable-facts.js +533 -0
- package/dist/src/booking-saga.js +97 -0
- package/dist/src/booking-surface/availability-policy.js +697 -0
- package/dist/src/booking-surface/booking-intent.js +43 -0
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/canonical-schema.js +116 -0
- package/dist/src/booking-surface/contracts.js +75 -0
- package/dist/src/booking-surface/dsh-planner.js +1854 -0
- package/dist/src/booking-surface/dsh-plugin.js +378 -0
- package/dist/src/booking-surface/dsh-worker.js +98 -0
- package/dist/src/booking-surface/error-codes.js +231 -0
- package/dist/src/booking-surface/index.js +9 -0
- package/dist/src/booking-surface/managed-dsh-cleanup-diagnostic.js +97 -0
- package/dist/src/booking-surface/managed-dsh-run-port.js +244 -0
- package/dist/src/booking-surface/profile.js +24 -0
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +1968 -0
- package/dist/src/booking-surface/server.js +674 -0
- package/dist/src/booking-surface/startup.js +168 -0
- package/dist/src/booking-surface/validation.js +261 -0
- package/dist/src/bridge.js +20 -13
- package/dist/src/city-scenario-tier.js +378 -0
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +171 -115
- package/dist/src/engine.js +276 -193
- package/dist/src/evaluation-cadence.js +97 -0
- package/dist/src/evaluation-contracts.js +283 -0
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/flyai-setup-tool.js +70 -0
- package/dist/src/gotry-backend.js +125 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +88 -0
- package/dist/src/index.js +2385 -881
- package/dist/src/itinerary-deck.js +139 -0
- package/dist/src/itinerary-doc-shared.js +661 -0
- package/dist/src/itinerary-html.js +131 -0
- package/dist/src/journey.js +155 -108
- package/dist/src/lavish-tools.js +400 -0
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +331 -161
- package/dist/src/m3-cohort.js +1577 -0
- package/dist/src/memory-capture.js +180 -32
- package/dist/src/memory-decay.js +30 -14
- package/dist/src/memory-lifecycle.js +1310 -0
- package/dist/src/memory-utility.js +27 -40
- package/dist/src/mock-llm.js +36 -61
- package/dist/src/model.js +81 -36
- package/dist/src/outcome-projection.js +746 -0
- package/dist/src/realtime-pricing.js +103 -0
- package/dist/src/recall/card.js +74 -0
- package/dist/src/recall/evaluator.js +140 -0
- package/dist/src/recall/integration.js +17 -0
- package/dist/src/recall/tick.js +166 -0
- package/dist/src/session-link/plan-it-action.js +48 -0
- package/dist/src/session-link/session-link.js +248 -0
- package/dist/src/session-zone-ledger.js +312 -0
- package/dist/src/session-zone-observation.js +354 -0
- package/dist/src/session-zone-wiring.js +658 -0
- package/dist/src/session-zones.js +510 -0
- package/dist/src/share/adapters.js +51 -0
- package/dist/src/share/share-consent.js +76 -0
- package/dist/src/share/share-deck.js +58 -0
- package/dist/src/share/share-token.js +146 -0
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +696 -524
- package/dist/src/state-repair.js +299 -0
- package/dist/src/subagent-job-id-guard.js +58 -0
- package/dist/src/time-anchor.js +231 -33
- package/dist/src/tool-packet.js +20 -8
- package/dist/src/travel-slots.js +58 -35
- package/dist/src/travel-timeline.js +44 -63
- package/dist/src/turn-deadline.js +287 -0
- package/dist/src/turn-policy.js +118 -0
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +600 -360
- package/dist/src/wish-pool.js +42 -12
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +284 -0
- package/extension/README.md +21 -0
- package/extension/README.zh-CN.md +21 -0
- package/extension/background.js +463 -0
- package/extension/content-bridge.js +270 -0
- package/extension/content-main.js +111 -0
- package/extension/manifest.json +60 -0
- package/package.json +407 -14
- package/schemas/booking.intent.schema.json +193 -0
- package/schemas/booking.surface.schema.json +2622 -0
- package/ts/capabilities/agent-reach-deep.ts +114 -0
- package/ts/capabilities/agent-reach.ts +198 -0
- package/ts/capabilities/anything.ts +282 -0
- package/ts/capabilities/artifacts.ts +428 -0
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/flyai-config.ts +388 -0
- package/ts/capabilities/flyai.ts +1274 -0
- package/ts/capabilities/hbcli.ts +253 -71
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/itinerary-artifact.ts +214 -0
- package/ts/capabilities/itinerary-deck-artifact.ts +218 -0
- package/ts/capabilities/itinerary-deck-export.ts +378 -0
- package/ts/capabilities/opensky.ts +121 -0
- package/ts/capabilities/session/action-cache.ts +148 -0
- package/ts/capabilities/session/adapters/ctrip-flight.ts +181 -0
- package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
- package/ts/capabilities/session/adapters/dida-portal.ts +338 -0
- package/ts/capabilities/session/adapters/meituan-local.ts +70 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +392 -0
- package/ts/capabilities/session/benchmark.ts +276 -0
- package/ts/capabilities/session/bridge-ledger.ts +333 -0
- package/ts/capabilities/session/extension-bridge.ts +794 -0
- package/ts/capabilities/session/extension-channel.ts +144 -0
- package/ts/capabilities/session/extension-distribution.ts +266 -0
- package/ts/capabilities/session/extract.ts +54 -0
- package/ts/capabilities/session/golden-score.ts +139 -0
- package/ts/capabilities/session/health-watch.ts +264 -0
- package/ts/capabilities/session/read-guard.ts +141 -0
- package/ts/capabilities/session/static-flight-golden.ts +209 -0
- package/ts/capabilities/session/transport.ts +146 -0
- package/ts/capabilities/session/wizard.ts +140 -0
- package/ts/capabilities/session-consent.ts +211 -0
- package/ts/capabilities/session-login.ts +208 -0
- package/ts/capabilities/session-search.ts +893 -0
- package/ts/capabilities/weather.ts +330 -0
- package/ts/dsh-runtime/vendor/README.md +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/LICENSE +21 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +405 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +404 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/client/client.js +1584 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/cordis.patch.yml +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/icon.svg +5 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/amap.js +595 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/nominatim.js +63 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/osrm.js +58 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/clients/photon.js +55 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-file.js +87 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config-route.js +131 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/config.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/geo.js +232 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/index.js +120 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +40 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/staticmap-route.js +115 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/geocode.js +127 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/poi.js +84 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/tools/routes.js +267 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/amap.d.ts +90 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/nominatim.d.ts +17 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/osrm.d.ts +15 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/clients/photon.d.ts +22 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-file.d.ts +33 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config-route.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/config.d.ts +23 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/geo.d.ts +61 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/index.d.ts +18 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +14 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/staticmap-route.d.ts +31 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/geocode.d.ts +11 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/poi.d.ts +9 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/tools/routes.d.ts +75 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/types.d.ts +68 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types.js +16 -0
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +97 -0
- package/ts/package.json +293 -3
- package/ts/scripts/async-collect.ts +91 -0
- package/ts/scripts/map-tools-vendor-package-proof.ts +404 -0
- package/ts/scripts/skeleton-check.ts +1 -1
- package/ts/scripts/skeleton-integration-test.ts +23 -1
- package/ts/scripts/state-cli.ts +586 -0
- package/ts/scripts/turn-handoff-collect.ts +178 -0
- package/ts/src/artifact-gate.ts +991 -0
- package/ts/src/benchmark-agent-conformance.ts +619 -0
- package/ts/src/benchmark-environment-bridge.ts +538 -0
- package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
- package/ts/src/benchmark-tool-isolation.ts +166 -0
- package/ts/src/bookable-facts.ts +837 -0
- package/ts/src/booking-saga.ts +133 -0
- package/ts/src/booking-surface/availability-policy.ts +522 -0
- package/ts/src/booking-surface/booking-intent.ts +86 -0
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/canonical-schema.js +116 -0
- package/ts/src/booking-surface/contracts.ts +475 -0
- package/ts/src/booking-surface/dsh-planner.ts +1949 -0
- package/ts/src/booking-surface/dsh-plugin.js +378 -0
- package/ts/src/booking-surface/dsh-worker.ts +110 -0
- package/ts/src/booking-surface/error-codes.ts +241 -0
- package/ts/src/booking-surface/index.ts +9 -0
- package/ts/src/booking-surface/managed-dsh-cleanup-diagnostic.ts +151 -0
- package/ts/src/booking-surface/managed-dsh-run-port.ts +273 -0
- package/ts/src/booking-surface/profile.ts +42 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/runtime.ts +1793 -0
- package/ts/src/booking-surface/server.ts +733 -0
- package/ts/src/booking-surface/startup.ts +201 -0
- package/ts/src/booking-surface/validation.ts +217 -0
- package/ts/src/companions.ts +113 -0
- package/ts/src/contracts.ts +17 -3
- package/ts/src/dsh-llm.ts +152 -17
- package/ts/src/flyai-setup-tool.ts +77 -0
- package/ts/src/index.ts +2123 -200
- package/ts/src/itinerary-deck.ts +189 -0
- package/ts/src/itinerary-doc-shared.ts +741 -0
- package/ts/src/itinerary-html.ts +171 -0
- package/ts/src/loop.ts +234 -26
- package/ts/src/memory-capture.ts +283 -0
- package/ts/src/memory-decay.ts +55 -0
- package/ts/src/memory-utility.ts +70 -0
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/recall/card.ts +106 -0
- package/ts/src/recall/evaluator.ts +170 -0
- package/ts/src/recall/integration.ts +35 -0
- package/ts/src/recall/tick.ts +213 -0
- package/ts/src/session-link/plan-it-action.ts +78 -0
- package/ts/src/session-link/session-link.ts +275 -0
- package/ts/src/share/adapters.ts +93 -0
- package/ts/src/share/share-consent.ts +105 -0
- package/ts/src/share/share-deck.ts +85 -0
- package/ts/src/share/share-token.ts +166 -0
- package/ts/src/slot-spec.ts +172 -0
- package/ts/src/state-ledger.ts +1166 -0
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +338 -0
- package/ts/src/tool-packet.ts +32 -0
- package/ts/src/travel-slots.ts +219 -0
- package/ts/src/travel-timeline.ts +83 -0
- package/ts/src/turn-deadline.ts +366 -0
- package/ts/src/turn-policy.ts +154 -0
- package/ts/src/unified.ts +444 -105
- package/ts/src/wish-pool.ts +79 -0
- package/dist/scripts/session-attach-wait.js +0 -41
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session link token 签发/校验(issue #580,Phase E;研究决策见
|
|
3
|
+
* docs/research/karpo-deck-web-research.md §3 Phase E——one-tap 深链回本地
|
|
4
|
+
* session,不碰写路径,WriteGate 保持 sealed;消费端注册是 M4 激活)。
|
|
5
|
+
*
|
|
6
|
+
* HMAC-SHA256 over base64url(payload-json),签名为 base64url。
|
|
7
|
+
* Token 形如 `${body}.${sig}`(body = base64url(JSON.stringify(payload)))。
|
|
8
|
+
*
|
|
9
|
+
* 与 share-token(Phase C)的差异(刻意,非偶然):
|
|
10
|
+
* - **schema 闭集到未知键拒绝**:verify 对 payload 里出现 schema 之外的键
|
|
11
|
+
* 直接 link_invalid——session link 是「de-identified by construction」面,
|
|
12
|
+
* 链接只携带引用(link_id/session_ref/wish_id)不携带事实/记忆,闭集
|
|
13
|
+
* schema 让这句承诺变成可测试字面真(share token 允许附加键,因为 share
|
|
14
|
+
* manifest 本身就需要开放扩展;两者威胁模型不同,不共享校验器);
|
|
15
|
+
* - **action 词位闭集 = WriteGate 链接层结构性封死**:`plan_it` | `open`,
|
|
16
|
+
* 词位里不存在 book/pay/confirm——任何写动词连构造都构造不出来
|
|
17
|
+
* (sign 层抛错,不只是 verify 层拒绝);
|
|
18
|
+
* - **plan_it ⇔ wish_id 成对**:`plan_it` 必须带非空 wish_id(防「规划
|
|
19
|
+
* 空 wish」退化),`open` 必须不带(防通用打开链接夹带定向意图);
|
|
20
|
+
* - **session_ref 路径护栏**:不透明引用,拒绝含 `/` `\` `..` NUL——
|
|
21
|
+
* 「不写共享状态」的链接层版:链接永远无法命名 state 路径;
|
|
22
|
+
* - 失败闭集只有 2 类(`link_invalid` | `link_expired`)——session link
|
|
23
|
+
* 无 channel/adapter/consent 维度,不需要 share 的 8 类;
|
|
24
|
+
* - token 不进 URL:scheme/宿主是消费端渲染期选择(`formatSessionLink`),
|
|
25
|
+
* 链接产物跨消费端可移植,签名载荷不含宿主细节。
|
|
26
|
+
*
|
|
27
|
+
* 硬化清单与 share-token 同源:恰一个点、timingSafeEqual(先长度)、
|
|
28
|
+
* ttl 非负整数上限 2^31-1(防 expiresMs 溢出 Infinity 永不过期)、
|
|
29
|
+
* 时间注入(测试不依赖 wall clock)、secret 缺省且 NODE_ENV=production
|
|
30
|
+
* 时 fail-closed(绝不静默用仓库里公开的 dev 常量签生产链接)——刻意抛错
|
|
31
|
+
* 保留在签发面(resolve/sign);verify 面把同样的配置错误收敛为 link_invalid,
|
|
32
|
+
* 不让异常越出「永不抛错」的校验边界。注入时钟本身失效(getTime 非有限值
|
|
33
|
+
* 或回调抛错)→ fail-closed link_invalid:NaN 与 expiresMs 比较恒 false,
|
|
34
|
+
* 曾把过期链接静默判成有效——无效时间上不产生任何接受。verify 期 secret
|
|
35
|
+
* 为非字符串运行期键(数字/对象/Symbol 等 JS 畸形输入)同样收敛 link_invalid,
|
|
36
|
+
* HMAC 计算本身也在守卫内——任何调用方/配置输入都无法越出「永不抛错」边界。
|
|
37
|
+
*/
|
|
38
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
39
|
+
/** 行动闭集:`plan_it`(回 session 继续规划某条 wish)| `open`(通用打开)。
|
|
40
|
+
* **词位即安全边界**——新增行动先 review;book/pay/confirm 等写动词
|
|
41
|
+
* 永不入集(WriteGate sealed 在链接层的结构性表达)。 */
|
|
42
|
+
export const SESSION_LINK_ACTIONS = ['plan_it', 'open'];
|
|
43
|
+
/** 显式 dev 默认 secret——生产环境必须由 `SESSION_LINK_HMAC_SECRET` env 覆盖 */
|
|
44
|
+
export const DEFAULT_DEV_SESSION_LINK_SECRET = 'gotry-session-link-dev-secret-DO-NOT-USE-IN-PROD';
|
|
45
|
+
/** ttl 上界(秒):2^31-1,防 created_at + ttl*1000 溢出 Number 上限 */
|
|
46
|
+
export const SESSION_LINK_TTL_MAX_SECONDS = 2_147_483_647;
|
|
47
|
+
/** payload schema 的键闭集(verify 用:未知键 → link_invalid) */
|
|
48
|
+
const PAYLOAD_KEYS = ['link_id', 'session_ref', 'action', 'wish_id', 'created_at', 'ttl_seconds'];
|
|
49
|
+
/** 获取运行期 secret:env 优先;production 缺 env 时 fail-closed */
|
|
50
|
+
export function resolveSessionLinkSecret(env = process.env) {
|
|
51
|
+
const s = env.SESSION_LINK_HMAC_SECRET;
|
|
52
|
+
if (typeof s === 'string' && s.length > 0)
|
|
53
|
+
return s;
|
|
54
|
+
if (env.NODE_ENV === 'production') {
|
|
55
|
+
throw new Error('SESSION_LINK_HMAC_SECRET 未设置且 NODE_ENV=production:拒绝回落公开的 dev 默认 secret(fail-closed;任何读到仓库的人都能伪造 session link)');
|
|
56
|
+
}
|
|
57
|
+
return DEFAULT_DEV_SESSION_LINK_SECRET;
|
|
58
|
+
}
|
|
59
|
+
/** session_ref 路径护栏:不透明引用——含路径分隔/上跳/NUL 即拒(链接永远无法命名 state 路径) */
|
|
60
|
+
function isSafeSessionRef(value) {
|
|
61
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 128
|
|
62
|
+
&& !value.includes('/') && !value.includes('\\') && !value.includes('..')
|
|
63
|
+
&& !value.includes('\0');
|
|
64
|
+
}
|
|
65
|
+
/** 签发:payload 校验(闭集 action / plan_it⇔wish_id 成对 / session_ref 护栏 / ttl 上界)
|
|
66
|
+
* 不过期检查由 verify 做(签名时刻的时钟是消费端的)。非法 payload 抛错——
|
|
67
|
+
* 构造层把坏链接挡在签名之前,不是等到校验才发现。 */
|
|
68
|
+
export function signSessionLink(payload, secret = resolveSessionLinkSecret()) {
|
|
69
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
70
|
+
throw new Error(`signSessionLink:payload 必须是对象(实测 ${String(payload)})`);
|
|
71
|
+
}
|
|
72
|
+
if (typeof payload.link_id !== 'string' || payload.link_id.length === 0) {
|
|
73
|
+
throw new Error('signSessionLink:link_id 必须是非空字符串');
|
|
74
|
+
}
|
|
75
|
+
if (!isSafeSessionRef(payload.session_ref)) {
|
|
76
|
+
throw new Error(`signSessionLink:session_ref 必须是不含路径分隔符的不透明引用(实测 ${String(payload.session_ref)})`);
|
|
77
|
+
}
|
|
78
|
+
if (!SESSION_LINK_ACTIONS.includes(payload.action)) {
|
|
79
|
+
throw new Error(`signSessionLink:action 不在闭集(实测 ${String(payload.action)};允许:${SESSION_LINK_ACTIONS.join(' / ')})。写动词(book/pay/confirm)永不入集——WriteGate 在链接层结构性封死`);
|
|
80
|
+
}
|
|
81
|
+
// plan_it ⇔ wish_id 成对(双向:plan_it 必须有目标;open 必须不夹带定向意图)
|
|
82
|
+
if (payload.action === 'plan_it' && (typeof payload.wish_id !== 'string' || payload.wish_id.length === 0)) {
|
|
83
|
+
throw new Error('signSessionLink:action=plan_it 必须携带非空 wish_id(否则「规划」无对象,是退化链接)');
|
|
84
|
+
}
|
|
85
|
+
if (payload.action === 'open' && payload.wish_id !== undefined) {
|
|
86
|
+
throw new Error('signSessionLink:action=open 必须不携带 wish_id(通用打开链接夹带定向意图 = 混淆行动语义)');
|
|
87
|
+
}
|
|
88
|
+
if (typeof payload.created_at !== 'string' || payload.created_at.length === 0 || !Number.isFinite(Date.parse(payload.created_at))) {
|
|
89
|
+
throw new Error('signSessionLink:created_at 必须是可解析的 ISO 时间字符串');
|
|
90
|
+
}
|
|
91
|
+
if (typeof payload.ttl_seconds !== 'number' || !Number.isInteger(payload.ttl_seconds)
|
|
92
|
+
|| payload.ttl_seconds < 0 || payload.ttl_seconds > SESSION_LINK_TTL_MAX_SECONDS) {
|
|
93
|
+
throw new Error(`signSessionLink:ttl_seconds 必须为 0..${SESSION_LINK_TTL_MAX_SECONDS} 的整数(实测 ${String(payload.ttl_seconds)})`);
|
|
94
|
+
}
|
|
95
|
+
// 序列化前剥掉 undefined 键(wish_id 缺省时),保证 body 可复现
|
|
96
|
+
const clean = {};
|
|
97
|
+
const raw = payload;
|
|
98
|
+
for (const k of PAYLOAD_KEYS) {
|
|
99
|
+
const v = raw[k];
|
|
100
|
+
if (v !== undefined)
|
|
101
|
+
clean[k] = v;
|
|
102
|
+
}
|
|
103
|
+
const body = Buffer.from(JSON.stringify(clean), 'utf-8').toString('base64url');
|
|
104
|
+
const sig = createHmac('sha256', secret).update(body).digest('base64url');
|
|
105
|
+
return `${body}.${sig}`;
|
|
106
|
+
}
|
|
107
|
+
/** 校验:secret 解析(守卫体)→ 格式(恰一个点)→ HMAC 等值(timingSafeEqual)
|
|
108
|
+
* → schema 闭集(未知键拒绝)→ 字段形态 → plan_it⇔wish_id 成对 → session_ref
|
|
109
|
+
* 护栏 → 过期(注入时钟失效 = link_invalid)。**永不抛错**:production 缺 env
|
|
110
|
+
* secret 与失效时钟都是操作面错误,收敛进 link_invalid(消费端拿到 link_invalid
|
|
111
|
+
* 只能 fail-closed 不打开任何 session),刻意的配置/签发异常只保留在 resolve/sign 侧。 */
|
|
112
|
+
export function verifySessionLink(token, secret, now = () => new Date()) {
|
|
113
|
+
// 运行期 secret 解析放进守卫体:production 缺 env 的配置错在验证面 fail-closed
|
|
114
|
+
// 为 link_invalid,不抛错(若静默回落公开 dev 常量,伪造链接将被放行);
|
|
115
|
+
// 非字符串运行期键(数字/对象/Symbol 等 JS 畸形调用方输入)同样收敛
|
|
116
|
+
// link_invalid——createHmac 对它们会抛 TypeError,绝不让异常越出本函数
|
|
117
|
+
let hmacSecret;
|
|
118
|
+
try {
|
|
119
|
+
const resolved = secret === undefined ? resolveSessionLinkSecret() : secret;
|
|
120
|
+
if (typeof resolved !== 'string')
|
|
121
|
+
return { ok: false, reason: 'link_invalid' };
|
|
122
|
+
hmacSecret = resolved;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return { ok: false, reason: 'link_invalid' };
|
|
126
|
+
}
|
|
127
|
+
if (typeof token !== 'string' || token.length === 0)
|
|
128
|
+
return { ok: false, reason: 'link_invalid' };
|
|
129
|
+
const dot = token.indexOf('.');
|
|
130
|
+
// 恰一个点:多点(base64url 解码器会静默吞非法字符)与无点都拒绝
|
|
131
|
+
if (dot <= 0 || dot === token.length - 1)
|
|
132
|
+
return { ok: false, reason: 'link_invalid' };
|
|
133
|
+
if (token.indexOf('.', dot + 1) !== -1)
|
|
134
|
+
return { ok: false, reason: 'link_invalid' };
|
|
135
|
+
const body = token.slice(0, dot);
|
|
136
|
+
const sig = token.slice(dot + 1);
|
|
137
|
+
// HMAC 等值比较(先长度,再 timingSafeEqual;Buffer-vs-Buffer);加密计算
|
|
138
|
+
// 本身也在守卫内——任何运行期键异常收敛 link_invalid,绝不外泄异常
|
|
139
|
+
let expected;
|
|
140
|
+
try {
|
|
141
|
+
expected = createHmac('sha256', hmacSecret).update(body).digest();
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return { ok: false, reason: 'link_invalid' };
|
|
145
|
+
}
|
|
146
|
+
let actual;
|
|
147
|
+
try {
|
|
148
|
+
actual = Buffer.from(sig, 'base64url');
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return { ok: false, reason: 'link_invalid' };
|
|
152
|
+
}
|
|
153
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
|
|
154
|
+
return { ok: false, reason: 'link_invalid' };
|
|
155
|
+
}
|
|
156
|
+
// 解析 payload
|
|
157
|
+
let payload;
|
|
158
|
+
try {
|
|
159
|
+
const json = Buffer.from(body, 'base64url').toString('utf-8');
|
|
160
|
+
payload = JSON.parse(json);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return { ok: false, reason: 'link_invalid' };
|
|
164
|
+
}
|
|
165
|
+
if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
|
|
166
|
+
return { ok: false, reason: 'link_invalid' };
|
|
167
|
+
}
|
|
168
|
+
// schema 闭集:未知键拒绝(de-identified by construction 的可测试形式)
|
|
169
|
+
for (const key of Object.keys(payload)) {
|
|
170
|
+
if (!PAYLOAD_KEYS.includes(key))
|
|
171
|
+
return { ok: false, reason: 'link_invalid' };
|
|
172
|
+
}
|
|
173
|
+
// 字段形态
|
|
174
|
+
if (typeof payload.link_id !== 'string' || payload.link_id.length === 0
|
|
175
|
+
|| !isSafeSessionRef(payload.session_ref)
|
|
176
|
+
|| typeof payload.action !== 'string' || !SESSION_LINK_ACTIONS.includes(payload.action)
|
|
177
|
+
|| typeof payload.created_at !== 'string' || payload.created_at.length === 0) {
|
|
178
|
+
return { ok: false, reason: 'link_invalid' };
|
|
179
|
+
}
|
|
180
|
+
// plan_it ⇔ wish_id 成对(与 sign 同规则,双向)
|
|
181
|
+
if (payload.action === 'plan_it' && (typeof payload.wish_id !== 'string' || payload.wish_id.length === 0)) {
|
|
182
|
+
return { ok: false, reason: 'link_invalid' };
|
|
183
|
+
}
|
|
184
|
+
if (payload.action === 'open' && payload.wish_id !== undefined) {
|
|
185
|
+
return { ok: false, reason: 'link_invalid' };
|
|
186
|
+
}
|
|
187
|
+
// ttl:非负整数且不溢出(防 created_at + ttl*1000 = Infinity 永不过期)
|
|
188
|
+
const ttl = payload.ttl_seconds;
|
|
189
|
+
if (typeof ttl !== 'number' || !Number.isInteger(ttl) || ttl < 0 || ttl > SESSION_LINK_TTL_MAX_SECONDS) {
|
|
190
|
+
return { ok: false, reason: 'link_invalid' };
|
|
191
|
+
}
|
|
192
|
+
const createdMs = Date.parse(payload.created_at);
|
|
193
|
+
if (!Number.isFinite(createdMs))
|
|
194
|
+
return { ok: false, reason: 'link_invalid' };
|
|
195
|
+
const expiresMs = createdMs + ttl * 1000;
|
|
196
|
+
if (!Number.isFinite(expiresMs))
|
|
197
|
+
return { ok: false, reason: 'link_invalid' };
|
|
198
|
+
// 注入时钟失效(抛错/非有限值)fail-closed 为 link_invalid——
|
|
199
|
+
// NaN 与 expiresMs 比较恒 false,失效时钟不得让任何链接通过
|
|
200
|
+
let nowMs;
|
|
201
|
+
try {
|
|
202
|
+
nowMs = now().getTime();
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return { ok: false, reason: 'link_invalid' };
|
|
206
|
+
}
|
|
207
|
+
if (!Number.isFinite(nowMs))
|
|
208
|
+
return { ok: false, reason: 'link_invalid' };
|
|
209
|
+
if (nowMs > expiresMs)
|
|
210
|
+
return { ok: false, reason: 'link_expired' };
|
|
211
|
+
return { ok: true, payload: payload };
|
|
212
|
+
}
|
|
213
|
+
/** 默认深链 base(scheme handler 注册是 M4 激活;消费端可传自己的 base) */
|
|
214
|
+
export const DEFAULT_SESSION_LINK_BASE = 'gotry://session';
|
|
215
|
+
/** 渲染期拼 URL:`${base}/${token}`——token 不进 URL 的签名,宿主是消费端选择 */
|
|
216
|
+
export function formatSessionLink(token, base = DEFAULT_SESSION_LINK_BASE) {
|
|
217
|
+
if (typeof token !== 'string' || token.length === 0) {
|
|
218
|
+
throw new Error('formatSessionLink:token 必须是非空字符串');
|
|
219
|
+
}
|
|
220
|
+
if (typeof base !== 'string' || base.length === 0 || base.endsWith('/')) {
|
|
221
|
+
throw new Error(`formatSessionLink:base 必须是非空字符串且不以 '/' 结尾(实测 ${String(base)})`);
|
|
222
|
+
}
|
|
223
|
+
if (base.includes('?') || base.includes('#')) {
|
|
224
|
+
throw new Error('formatSessionLink:base 不得含 query/hash(产出即 parseSessionLink 拒绝的形态,锋利边角)');
|
|
225
|
+
}
|
|
226
|
+
return `${base}/${token}`;
|
|
227
|
+
}
|
|
228
|
+
/** 从 URL 严格反解 token:路径最后一段,且段本身须呈 `body.sig` 形态(恰
|
|
229
|
+
* 一个非空 body、非空 sig;段内多点由 verify 终裁——本函数只保证不把一个
|
|
230
|
+
* 裸 base(`gotry://session`)的最后一段误当 token)。**拒绝 query/hash**——
|
|
231
|
+
* 拼接歧义(如 `?x=../../etc`)一律 fail-closed;形态不符返回 null(消费端
|
|
232
|
+
* 不打开)。 */
|
|
233
|
+
export function parseSessionLink(url) {
|
|
234
|
+
if (typeof url !== 'string' || url.length === 0)
|
|
235
|
+
return null;
|
|
236
|
+
const q = url.indexOf('?');
|
|
237
|
+
const h = url.indexOf('#');
|
|
238
|
+
if (q !== -1 || h !== -1)
|
|
239
|
+
return null;
|
|
240
|
+
const segments = url.split('/').filter(s => s.length > 0);
|
|
241
|
+
const token = segments[segments.length - 1];
|
|
242
|
+
if (segments.length < 2 || token === undefined || token.length === 0)
|
|
243
|
+
return null;
|
|
244
|
+
const dot = token.indexOf('.');
|
|
245
|
+
if (dot <= 0 || dot === token.length - 1)
|
|
246
|
+
return null;
|
|
247
|
+
return token;
|
|
248
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话双区记忆的账本落点(P4-2,docs/design/session-dual-zone-memory-design.md §2):
|
|
3
|
+
* 把 P4-1 纯核(session-zones.ts)的六个边界事件落到**既有** events 表上,零新表、
|
|
4
|
+
* 零 schema 迁移、对内核钉死的 state-ledger.ts 零改动——只走它的公开面
|
|
5
|
+
* (`db.transaction` / `readEvents` / `insertEvent` / `forgetSubject`)。
|
|
6
|
+
*
|
|
7
|
+
* - 写路径 = 单事务{fold 读当前 revs;守门闸;INSERT}:守门在事务内,拒绝即回滚,
|
|
8
|
+
* 账本无痕(与 motivation/wish 红线同纪律)。批量写共用一个事务 → 全有或全无。
|
|
9
|
+
* - 幂等键 = P4-1 的 `zoneIdemKey`(物理化为 events 表的 UNIQUE 部分索引):同一
|
|
10
|
+
* rev 的重放在守门层返回 appended:false,万一绕过守门也会在 UNIQUE 处变成物理
|
|
11
|
+
* no-op(insertEvent 返回 null)——两道都保证「同 rev 重放零新行」。
|
|
12
|
+
* - 读路径 = `readEvents(kind)` ×6 + P4-1 的确定性 fold(`readTrips`/
|
|
13
|
+
* `projectUtilityNow` 同款):无持久投影(P4 v1 单用户量级下读时 fold 够用)。
|
|
14
|
+
* 过期是读时视图,不是事件——墙钟动作不进 append-only 账本。
|
|
15
|
+
* - 删除 = 既有 `forgetSubject`(物理硬删 + 审计一行;红线 6「可删除」);
|
|
16
|
+
* 会话级遗忘把该会话全部主体放进**一次** forgetSubject 调用 → 仍只留一行审计。
|
|
17
|
+
* - 导出 = 从 fold 派生的两个视图(`hot-context.jsonl` / `notebook.json`),
|
|
18
|
+
* state-cli export 单向写出;**视图永不是写路径**(导出零新事件)。
|
|
19
|
+
*
|
|
20
|
+
* 边界:本模块不接会话捕获缝/persona 读回(P4-3),不做观测与指标(P4-4);
|
|
21
|
+
* 日志类事件在 state-ledger 的 `foldEvent` default 分支无投影,与
|
|
22
|
+
* `trip.logged`/`memory_utility.event` 完全同形。
|
|
23
|
+
*/
|
|
24
|
+
import { ZONE_EVENT_KINDS, captureHotNote, dropHotNote, dropNotebookEntry, foldZoneEvents, makeHotNoteId, promoteHotNote, readNotebook, reviseHotNote, reviseNotebookEntry, zoneIdemKey, } from './session-zones.js';
|
|
25
|
+
/** 工作区三事件(forget 的 hot 主体 kinds) */
|
|
26
|
+
export const HOT_ZONE_EVENT_KINDS = [
|
|
27
|
+
'hotctx.note.captured',
|
|
28
|
+
'hotctx.note.revised',
|
|
29
|
+
'hotctx.note.dropped',
|
|
30
|
+
];
|
|
31
|
+
/** 笔记本三事件(forget 的 durable 主体 kinds) */
|
|
32
|
+
export const NOTEBOOK_ZONE_EVENT_KINDS = [
|
|
33
|
+
'notebook.entry.promoted',
|
|
34
|
+
'notebook.entry.revised',
|
|
35
|
+
'notebook.entry.dropped',
|
|
36
|
+
];
|
|
37
|
+
/** 单 kind 读上界:触顶即 fail-closed(截断的日志 fold 出来的 rev 是错的,不许写) */
|
|
38
|
+
export const ZONE_LOG_READ_LIMIT = 20_000;
|
|
39
|
+
/** 默认 actor(P4-3 的工具面会传各自的具名 actor) */
|
|
40
|
+
export const ZONE_DEFAULT_ACTOR = 'tool:gotry_session_zone';
|
|
41
|
+
/** 事件行 → ZoneEvent(形状不合法返回 null:确定性跳过,不猜) */
|
|
42
|
+
function rowToZoneEvent(row) {
|
|
43
|
+
let payload;
|
|
44
|
+
try {
|
|
45
|
+
payload = JSON.parse(row.payload);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (typeof payload !== 'object' || payload === null)
|
|
51
|
+
return null;
|
|
52
|
+
const p = payload;
|
|
53
|
+
switch (row.kind) {
|
|
54
|
+
case 'hotctx.note.captured':
|
|
55
|
+
case 'hotctx.note.revised':
|
|
56
|
+
return typeof p.note === 'object' && p.note !== null
|
|
57
|
+
? { kind: row.kind, ts: row.ts, note: p.note }
|
|
58
|
+
: null;
|
|
59
|
+
case 'hotctx.note.dropped':
|
|
60
|
+
return typeof p.note_id === 'string' && p.note_id.length > 0
|
|
61
|
+
? { kind: row.kind, ts: row.ts, note_id: p.note_id }
|
|
62
|
+
: null;
|
|
63
|
+
case 'notebook.entry.promoted':
|
|
64
|
+
case 'notebook.entry.revised':
|
|
65
|
+
return typeof p.entry === 'object' && p.entry !== null
|
|
66
|
+
? { kind: row.kind, ts: row.ts, entry: p.entry }
|
|
67
|
+
: null;
|
|
68
|
+
case 'notebook.entry.dropped':
|
|
69
|
+
return typeof p.entry_id === 'string' && p.entry_id.length > 0
|
|
70
|
+
? { kind: row.kind, ts: row.ts, entry_id: p.entry_id }
|
|
71
|
+
: null;
|
|
72
|
+
default:
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** 事件 → 落账载荷(与 `trip.logged` 的 `{ trip }` 同形:变更后全量文档随事件走) */
|
|
77
|
+
function eventPayload(ev) {
|
|
78
|
+
switch (ev.kind) {
|
|
79
|
+
case 'hotctx.note.captured':
|
|
80
|
+
case 'hotctx.note.revised':
|
|
81
|
+
return { subjectId: ev.note.note_id, payload: { note: ev.note } };
|
|
82
|
+
case 'hotctx.note.dropped':
|
|
83
|
+
return { subjectId: ev.note_id, payload: { note_id: ev.note_id } };
|
|
84
|
+
case 'notebook.entry.promoted':
|
|
85
|
+
case 'notebook.entry.revised':
|
|
86
|
+
return { subjectId: ev.entry.entry_id, payload: { entry: ev.entry } };
|
|
87
|
+
case 'notebook.entry.dropped':
|
|
88
|
+
return { subjectId: ev.entry_id, payload: { entry_id: ev.entry_id } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 读分区日志:六个 kind 各自 `readEvents` → 按 seq 升序归并 → P4-1 确定性 fold。
|
|
93
|
+
* 任一 kind 触到 limit 即 truncated(写路径据此 fail-closed:截断日志的 rev 不可信)。
|
|
94
|
+
*/
|
|
95
|
+
export function readZoneLog(ledger, opts = {}) {
|
|
96
|
+
const limit = opts.limit ?? ZONE_LOG_READ_LIMIT;
|
|
97
|
+
const rows = [];
|
|
98
|
+
let truncated = false;
|
|
99
|
+
for (const kind of ZONE_EVENT_KINDS) {
|
|
100
|
+
const batch = ledger.readEvents(kind, limit);
|
|
101
|
+
if (batch.length >= limit)
|
|
102
|
+
truncated = true;
|
|
103
|
+
for (const r of batch)
|
|
104
|
+
rows.push({ seq: r.seq, ts: r.ts, kind: r.kind, payload: r.payload });
|
|
105
|
+
}
|
|
106
|
+
rows.sort((a, b) => a.seq - b.seq);
|
|
107
|
+
const events = [];
|
|
108
|
+
let malformed = 0;
|
|
109
|
+
for (const row of rows) {
|
|
110
|
+
const ev = rowToZoneEvent(row);
|
|
111
|
+
if (ev === null)
|
|
112
|
+
malformed++;
|
|
113
|
+
else
|
|
114
|
+
events.push(ev);
|
|
115
|
+
}
|
|
116
|
+
return { state: foldZoneEvents(events), events, truncated, malformed };
|
|
117
|
+
}
|
|
118
|
+
/** 单请求过 P4-1 写门(账本层不重写任何算术/闸语义,只转发) */
|
|
119
|
+
function gateRequest(state, req) {
|
|
120
|
+
switch (req.op) {
|
|
121
|
+
case 'capture':
|
|
122
|
+
return captureHotNote(state, req.input);
|
|
123
|
+
case 'capture_or_touch': {
|
|
124
|
+
// 先走 capture 的完整闸链(闭集/形状/有界/负面清单/时钟);只有「同 id 已存在」
|
|
125
|
+
// 这一种拒收(stale_rev)才转成续命写入——其余拒收原样上报,闸语义零放宽。
|
|
126
|
+
const birth = captureHotNote(state, req.input);
|
|
127
|
+
if (birth.ok || birth.code !== 'stale_rev')
|
|
128
|
+
return birth;
|
|
129
|
+
const noteId = makeHotNoteId({
|
|
130
|
+
session_ref: req.input.session_ref,
|
|
131
|
+
tier: req.input.tier,
|
|
132
|
+
kind: req.input.kind,
|
|
133
|
+
payload: req.input.payload,
|
|
134
|
+
});
|
|
135
|
+
const cur = noteId === null ? undefined : state.hot[noteId];
|
|
136
|
+
if (!cur)
|
|
137
|
+
return birth;
|
|
138
|
+
// 同一写入时刻的同形重放:TTL 已自该时刻起算,再续一次只是空转 rev → 幂等 no-op。
|
|
139
|
+
// (id 由会话+层+kind+规范载荷派生,所以撞到这里时载荷与 kind 必然相同,只有 ts 可能不同。)
|
|
140
|
+
if (cur.last_touched_at === req.input.ts) {
|
|
141
|
+
return {
|
|
142
|
+
ok: true,
|
|
143
|
+
appended: false,
|
|
144
|
+
idemKey: zoneIdemKey({ kind: 'hotctx.note.revised', ts: req.input.ts, note: cur }),
|
|
145
|
+
detail: '同写入时刻同形重放:幂等 no-op(TTL 已自该时刻起算)',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// 同语义片段再次出现 = 续命写入(TTL 自本次写入起算;过期笔记据此复活)
|
|
149
|
+
return reviseHotNote(state, {
|
|
150
|
+
note_id: cur.note_id,
|
|
151
|
+
expected_rev: cur.rev,
|
|
152
|
+
payload: req.input.payload,
|
|
153
|
+
kind: req.input.kind,
|
|
154
|
+
ts: req.input.ts,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
case 'revise':
|
|
158
|
+
return reviseHotNote(state, req.input);
|
|
159
|
+
case 'drop':
|
|
160
|
+
return dropHotNote(state, req.note_id, req.ts);
|
|
161
|
+
case 'promote':
|
|
162
|
+
return promoteHotNote(state, req.input);
|
|
163
|
+
case 'notebook_revise':
|
|
164
|
+
return reviseNotebookEntry(state, req.input);
|
|
165
|
+
case 'notebook_drop':
|
|
166
|
+
return dropNotebookEntry(state, req.entry_id, req.ts);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** 批量写被拒时的回滚载体(事务内抛出 → better-sqlite3 回滚 → 外层取回结果) */
|
|
170
|
+
class ZoneBatchRejected extends Error {
|
|
171
|
+
results;
|
|
172
|
+
constructor(results) {
|
|
173
|
+
super('zone batch rejected');
|
|
174
|
+
this.results = results;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 批量追加(单事务,全有或全无):
|
|
179
|
+
* - 事务内先 fold 读一次当前状态,逐请求过闸并把已接受的事件并入在算状态
|
|
180
|
+
* (批内自洽:同一事务里 capture→revise 的 rev CAS 成立);
|
|
181
|
+
* - 任一请求被拒 → 抛出 → 整个事务回滚 → 账本零新行,返回逐项结果;
|
|
182
|
+
* - 日志截断 → 第一项即 `log_truncated`,零写入(不在不全的日志上猜 rev)。
|
|
183
|
+
*/
|
|
184
|
+
export function appendZoneWrites(ledger, requests, opts = {}) {
|
|
185
|
+
if (requests.length === 0)
|
|
186
|
+
return [];
|
|
187
|
+
const actor = opts.actor ?? ZONE_DEFAULT_ACTOR;
|
|
188
|
+
const run = ledger.db.transaction(() => {
|
|
189
|
+
const read = readZoneLog(ledger, { limit: opts.limit });
|
|
190
|
+
if (read.truncated) {
|
|
191
|
+
throw new ZoneBatchRejected([
|
|
192
|
+
{ ok: false, code: 'log_truncated', detail: `分区事件日志触到读上界(${opts.limit ?? ZONE_LOG_READ_LIMIT});截断日志的 rev 不可信,写路径 fail-closed` },
|
|
193
|
+
]);
|
|
194
|
+
}
|
|
195
|
+
const events = [...read.events];
|
|
196
|
+
let state = read.state;
|
|
197
|
+
const results = [];
|
|
198
|
+
for (const req of requests) {
|
|
199
|
+
const gate = gateRequest(state, req);
|
|
200
|
+
if (!gate.ok) {
|
|
201
|
+
results.push({ ok: false, code: gate.code, detail: gate.detail });
|
|
202
|
+
throw new ZoneBatchRejected(results);
|
|
203
|
+
}
|
|
204
|
+
if (!gate.appended) {
|
|
205
|
+
results.push({ ok: true, appended: false, idemKey: gate.idemKey, detail: gate.detail });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const ev = gate.event;
|
|
209
|
+
const { subjectId, payload } = eventPayload(ev);
|
|
210
|
+
const seq = ledger.insertEvent({ actor, kind: ev.kind, subjectId, payload, idemKey: gate.idemKey, ts: ev.ts });
|
|
211
|
+
if (seq === null) {
|
|
212
|
+
// 守门闸刚说该追加(fold 里没有这条),却撞上既有幂等键 → 生代碰撞:
|
|
213
|
+
// 同一毫秒内 capture→drop→重捕获会复用 `<id>:<created_at>:1`。这不是幂等重放,
|
|
214
|
+
// 而是**这次写入没发生**。静默报 appended:false 会骗调用方,所以显式失败、整批回滚。
|
|
215
|
+
results.push({
|
|
216
|
+
ok: false,
|
|
217
|
+
code: 'idem_collision',
|
|
218
|
+
detail: `幂等键已存在但 fold 中无此主体(生代碰撞:${gate.idemKey});本次写入未发生,请用不同的写入时刻重试`,
|
|
219
|
+
});
|
|
220
|
+
throw new ZoneBatchRejected(results);
|
|
221
|
+
}
|
|
222
|
+
events.push(ev);
|
|
223
|
+
state = foldZoneEvents(events);
|
|
224
|
+
results.push({ ok: true, appended: true, idemKey: gate.idemKey, seq, event: ev });
|
|
225
|
+
}
|
|
226
|
+
opts.beforeCommit?.();
|
|
227
|
+
return results;
|
|
228
|
+
});
|
|
229
|
+
try {
|
|
230
|
+
return run();
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
if (e instanceof ZoneBatchRejected)
|
|
234
|
+
return e.results;
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/** 单请求追加(批量的 1 元特例) */
|
|
239
|
+
export function appendZoneWrite(ledger, request, opts = {}) {
|
|
240
|
+
return appendZoneWrites(ledger, [request], opts)[0];
|
|
241
|
+
}
|
|
242
|
+
/** 主体级遗忘:一次 forgetSubject 调用 → 物理删除全部相关事件 + 恰一行审计 */
|
|
243
|
+
export function forgetZoneSubjects(ledger, subjects, actor = 'system:state-cli') {
|
|
244
|
+
if (subjects.length === 0)
|
|
245
|
+
return { deleted: 0, subjects: 0 };
|
|
246
|
+
const specs = subjects.map(s => ({
|
|
247
|
+
kinds: [...(s.zone === 'hot_context' ? HOT_ZONE_EVENT_KINDS : NOTEBOOK_ZONE_EVENT_KINDS)],
|
|
248
|
+
subjectId: s.id,
|
|
249
|
+
}));
|
|
250
|
+
const r = ledger.forgetSubject(specs, actor);
|
|
251
|
+
return { deleted: r.deleted, subjects: specs.length };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 会话级遗忘:该会话的全部工作区笔记 + 血缘指向该会话的全部笔记本条目,
|
|
255
|
+
* 放进**一次** forgetSubject 调用 → 仍只留一行审计(「删除本身留一行」)。
|
|
256
|
+
*
|
|
257
|
+
* 截断即拒(fail-closed):主体集合是从 fold 推出来的,而 `readEvents` 触到读上界时
|
|
258
|
+
* 丢的是**最老**的事件——在截断日志上做遗忘会静默漏删主体,红线 6「可删除」就
|
|
259
|
+
* fail-open 了。宁可报错让调用方提高上界/先归档,也不假装删干净了。
|
|
260
|
+
*/
|
|
261
|
+
export function forgetZoneSession(ledger, sessionRef, actor = 'system:state-cli', opts = {}) {
|
|
262
|
+
const { state, truncated } = readZoneLog(ledger, opts);
|
|
263
|
+
if (truncated) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
code: 'log_truncated',
|
|
267
|
+
detail: `分区事件日志触到读上界(${opts.limit ?? ZONE_LOG_READ_LIMIT}):截断日志会漏掉最老的主体,遗忘拒绝在不完整视图上执行`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const subjects = [];
|
|
271
|
+
for (const note of Object.values(state.hot)) {
|
|
272
|
+
if (note.session_ref === sessionRef)
|
|
273
|
+
subjects.push({ zone: 'hot_context', id: note.note_id });
|
|
274
|
+
}
|
|
275
|
+
for (const entry of Object.values(state.notebook)) {
|
|
276
|
+
if (entry.origin?.session_ref === sessionRef)
|
|
277
|
+
subjects.push({ zone: 'trip_notebook', id: entry.entry_id });
|
|
278
|
+
}
|
|
279
|
+
const r = forgetZoneSubjects(ledger, subjects, actor);
|
|
280
|
+
return { ok: true, deleted: r.deleted, subjects: r.subjects };
|
|
281
|
+
}
|
|
282
|
+
// ---- 导出视图(从 fold 派生;单向 DB→文件,永不是写路径) --------------------------
|
|
283
|
+
export const ZONE_EXPORT_HOT_FILE = 'hot-context.jsonl';
|
|
284
|
+
export const ZONE_EXPORT_NOTEBOOK_FILE = 'notebook.json';
|
|
285
|
+
/**
|
|
286
|
+
* 两个派生视图:直接取 fold 结果渲染,确定性排序。
|
|
287
|
+
* hot 视图导出的是 fold 里**未被 drop** 的全部笔记(每条自带 ttl_expires_at,
|
|
288
|
+
* 过期判定留给读者/同一纯函数)——导出不引入第二个过期口径。
|
|
289
|
+
*/
|
|
290
|
+
export function renderZoneExportViews(state) {
|
|
291
|
+
const notes = Object.values(state.hot).sort((a, b) => (a.note_id < b.note_id ? -1 : 1));
|
|
292
|
+
return {
|
|
293
|
+
hotContextJsonl: notes.length ? notes.map(n => JSON.stringify(n)).join('\n') + '\n' : '',
|
|
294
|
+
notebookJson: JSON.stringify(readNotebook(state), null, 2),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 账本 → 导出视图(导出面唯一入口;截断即拒 fail-closed)。
|
|
299
|
+
* 「可见可导出」同样不能 fail-open:截断日志导出的是**残缺**视图,而读者会把
|
|
300
|
+
* 它当全量。纯渲染器 `renderZoneExportViews` 留给已确认完整的 state 使用。
|
|
301
|
+
*/
|
|
302
|
+
export function readZoneExportViews(ledger, opts = {}) {
|
|
303
|
+
const { state, truncated } = readZoneLog(ledger, opts);
|
|
304
|
+
if (truncated) {
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
code: 'log_truncated',
|
|
308
|
+
detail: `分区事件日志触到读上界(${opts.limit ?? ZONE_LOG_READ_LIMIT}):导出拒绝输出残缺视图(读者会把它当全量)`,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return { ok: true, views: renderZoneExportViews(state) };
|
|
312
|
+
}
|