@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,658 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话双区记忆的会话接线(P4-3,docs/design/session-dual-zone-memory-design.md §5):
|
|
3
|
+
* 把 P4-1 契约 + P4-2 账本落点接到真实会话面——捕获缝、persona 读回、owner 确认
|
|
4
|
+
* 晋升、更正即弃。**默认关闭**:`sessionZones: 'off' | 'on'` 插件配置,关时接线惰性
|
|
5
|
+
* (零账本事件、读回空串、工具不注册、execute 不被包裹)。
|
|
6
|
+
*
|
|
7
|
+
* 纪律:
|
|
8
|
+
* - 证据只走**指针**(session_ref + 观察序号),永不承载转写原文;session_ref 是
|
|
9
|
+
* 会话 id 的单向摘要(不透明、有界、无路径字符,与 session-link 护栏同族),
|
|
10
|
+
* 分区里永远看不到宿主会话 id 原文。
|
|
11
|
+
* - 工具观察缝只从**闭集白名单**工具的结果信封里投影形状字段(verdict / 条数 /
|
|
12
|
+
* 价格带两个数),不读名称、不读 URL、不读任何自由文本;投影后仍过 P4-1 写闸
|
|
13
|
+
* (负面清单二次把关)。捕获失败永不影响工具返回值。
|
|
14
|
+
* - 晋升只有 owner 确认一条入口:模型可**提议**(零落账),确认必须携带 owner 原话
|
|
15
|
+
* 引用;命中既有语义的晋升**经既有闸**落入唯一写权威(动机/时间线/同行人),
|
|
16
|
+
* 笔记本只记断言 + 血缘,绝不成为平行事实库(设计 §3 路由规则)。
|
|
17
|
+
* - 路由缺料 fail-closed(`routing_required`):宁可拒绝晋升,也不在笔记本里私存
|
|
18
|
+
* 一份本该归动机/时间线/同行人的事实。
|
|
19
|
+
*
|
|
20
|
+
* 边界:观测计数与指标在 P4-4;本模块零定时器、零网络;所有 IO 只在账本公开面。
|
|
21
|
+
*/
|
|
22
|
+
import { createHash } from 'node:crypto';
|
|
23
|
+
import { OWNER_QUOTE_MAX, isConfirmSurface, isHotNoteKind, isHotTier, isNotebookKind, makeHotNoteId, readNotebook, readWorkingZone, validSessionRef, } from './session-zones.js';
|
|
24
|
+
import { appendZoneWrite, appendZoneWrites, readZoneLog, } from './session-zone-ledger.js';
|
|
25
|
+
import { noteZoneSignal } from './session-zone-observation.js';
|
|
26
|
+
// ---- 总闸(默认关;未知值 fail-closed 关) -----------------------------------------
|
|
27
|
+
export const ZONE_SWITCH_VALUES = ['off', 'on'];
|
|
28
|
+
/** 未知/缺失/非字符串一律视为 off——开关只认明示的 'on' */
|
|
29
|
+
export function resolveZoneSwitch(raw) {
|
|
30
|
+
return raw === 'on' ? 'on' : 'off';
|
|
31
|
+
}
|
|
32
|
+
// ---- 会话引用(单向摘要;不透明、有界、无路径字符) --------------------------------
|
|
33
|
+
export const ZONE_SESSION_REF_PREFIX = 's-';
|
|
34
|
+
/**
|
|
35
|
+
* 宿主会话 id → 不透明分区会话引用。单向摘要:分区与导出里永不出现宿主 id 原文
|
|
36
|
+
* (会话 id 可能本身就是路径/长串/含 PII 形态的字符串)。
|
|
37
|
+
* 非字符串/空白 → null(无会话身份即不捕获,不猜)。
|
|
38
|
+
*/
|
|
39
|
+
export function zoneSessionRef(rawSessionId) {
|
|
40
|
+
if (typeof rawSessionId !== 'string' || rawSessionId.trim().length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
const ref = ZONE_SESSION_REF_PREFIX + createHash('sha256').update(rawSessionId.trim()).digest('hex').slice(0, 24);
|
|
43
|
+
return validSessionRef(ref) ? ref : null;
|
|
44
|
+
}
|
|
45
|
+
/** 从 dsh exec 取会话身份(与 lavish-tools 同一权威:exec.agent.session.id,无 agent.id 回落) */
|
|
46
|
+
export function zoneSessionRefFromExec(exec) {
|
|
47
|
+
const candidate = exec;
|
|
48
|
+
return zoneSessionRef(candidate?.agent?.session?.id);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 观察序号(证据指针的第二个分量):dsh 未向插件暴露转写行号,这里用**本进程内
|
|
52
|
+
* 该会话的单调观察序号**作指针——它只说「这是本会话第 n 个捕获点」,不承载内容。
|
|
53
|
+
*/
|
|
54
|
+
const observationCursor = new Map();
|
|
55
|
+
export function nextObservationTurn(sessionRef) {
|
|
56
|
+
const next = (observationCursor.get(sessionRef) ?? -1) + 1;
|
|
57
|
+
observationCursor.set(sessionRef, next);
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 会话绑定(persona 读回用:resource 层只在**本会话**可读)。
|
|
62
|
+
*
|
|
63
|
+
* 按 scope 键存,不存进程全局:dsh 的 agent-loop 以 agent 对象为 ScopeKey
|
|
64
|
+
* (`createScope(loopCtx, this)`),工具侧的 `exec.agent` 与 persona 组装侧的
|
|
65
|
+
* `AssembleContext.scope` 因此是同一个对象身份。进程全局单值会让同一宿主进程里
|
|
66
|
+
* 的两个会话互相读到对方的 resource 层笔记——那正是设计 §1.1 禁止的事
|
|
67
|
+
* (P4-3 复查发现)。WeakMap:会话消失即自动回收。
|
|
68
|
+
* 取不到 scope(轻量宿主/无绑定)→ null → 读回**不出** resource 段(fail-closed)。
|
|
69
|
+
*/
|
|
70
|
+
const sessionRefByScope = new WeakMap();
|
|
71
|
+
/** 从 exec 取 scope 键(与 dsh agent-loop 的 ScopeKey 同一对象身份) */
|
|
72
|
+
export function zoneScopeOfExec(exec) {
|
|
73
|
+
const agent = exec?.agent;
|
|
74
|
+
return typeof agent === 'object' && agent !== null ? agent : undefined;
|
|
75
|
+
}
|
|
76
|
+
export function bindZoneSession(scope, sessionRef) {
|
|
77
|
+
if (!scope)
|
|
78
|
+
return;
|
|
79
|
+
if (sessionRef === null) {
|
|
80
|
+
sessionRefByScope.delete(scope);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!validSessionRef(sessionRef))
|
|
84
|
+
return;
|
|
85
|
+
sessionRefByScope.set(scope, sessionRef);
|
|
86
|
+
}
|
|
87
|
+
export function boundZoneSession(scope) {
|
|
88
|
+
if (!scope)
|
|
89
|
+
return null;
|
|
90
|
+
return sessionRefByScope.get(scope) ?? null;
|
|
91
|
+
}
|
|
92
|
+
/** 测试用:清空观察游标与提议去重集(scope 绑定随对象回收,无需清)。产品路径不调用。 */
|
|
93
|
+
export function resetZoneSessionBindingForTests() {
|
|
94
|
+
observationCursor.clear();
|
|
95
|
+
proposalSeen.clear();
|
|
96
|
+
}
|
|
97
|
+
// ---- 工具观察缝(闭集白名单 + 形状投影) -------------------------------------------
|
|
98
|
+
/** 可观察工具闭集:只有检索类工具的结果信封进入工作区(resource 层) */
|
|
99
|
+
export const ZONE_OBSERVABLE_TOOLS = [
|
|
100
|
+
'gotry_flyai_search',
|
|
101
|
+
'gotry_hotel_search',
|
|
102
|
+
'gotry_session_search',
|
|
103
|
+
'gotry_anything_search',
|
|
104
|
+
];
|
|
105
|
+
export function isZoneObservableTool(name) {
|
|
106
|
+
return typeof name === 'string' && ZONE_OBSERVABLE_TOOLS.includes(name);
|
|
107
|
+
}
|
|
108
|
+
const RESULT_LIST_KEYS = ['options', 'hotels', 'pois', 'trains', 'rates', 'keywords', 'packages'];
|
|
109
|
+
const ZONE_VERDICTS = ['hit', 'miss', 'error', 'declined'];
|
|
110
|
+
function verdictOf(result) {
|
|
111
|
+
const v = result['verdict'];
|
|
112
|
+
if (typeof v === 'string' && ZONE_VERDICTS.includes(v))
|
|
113
|
+
return v;
|
|
114
|
+
return result['ok'] === true ? 'hit' : 'error';
|
|
115
|
+
}
|
|
116
|
+
/** 结果信封里的候选数(只数长度,不读任何条目字段) */
|
|
117
|
+
function optionCountOf(result) {
|
|
118
|
+
let count = 0;
|
|
119
|
+
for (const key of RESULT_LIST_KEYS) {
|
|
120
|
+
const list = result[key];
|
|
121
|
+
if (Array.isArray(list))
|
|
122
|
+
count = Math.max(count, list.length);
|
|
123
|
+
}
|
|
124
|
+
return count;
|
|
125
|
+
}
|
|
126
|
+
/** 结果信封里的价格带(只取有限正数的 min/max 两个数;没有就没有) */
|
|
127
|
+
function priceBandOf(result) {
|
|
128
|
+
const prices = [];
|
|
129
|
+
for (const key of RESULT_LIST_KEYS) {
|
|
130
|
+
const list = result[key];
|
|
131
|
+
if (!Array.isArray(list))
|
|
132
|
+
continue;
|
|
133
|
+
for (const item of list) {
|
|
134
|
+
const price = item?.price;
|
|
135
|
+
if (typeof price === 'number' && Number.isFinite(price) && price > 0)
|
|
136
|
+
prices.push(price);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (prices.length === 0)
|
|
140
|
+
return null;
|
|
141
|
+
return { low: Math.min(...prices), high: Math.max(...prices), samples: prices.length };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 工具观察 → resource 层捕获输入(纯函数):
|
|
145
|
+
* - 白名单外工具 / 非对象结果 → 零捕获;
|
|
146
|
+
* - availability 笔记 = { tool, verdict, option_count };
|
|
147
|
+
* - 命中价格时追加 price_band 笔记 = { tool, low, high, samples };
|
|
148
|
+
* - 两者都只含形状数字与闭集标签,**零名称、零 URL、零自由文本**。
|
|
149
|
+
*/
|
|
150
|
+
export function projectToolObservation(input) {
|
|
151
|
+
if (!isZoneObservableTool(input.tool))
|
|
152
|
+
return [];
|
|
153
|
+
if (typeof input.result !== 'object' || input.result === null || Array.isArray(input.result))
|
|
154
|
+
return [];
|
|
155
|
+
const result = input.result;
|
|
156
|
+
const verdict = verdictOf(result);
|
|
157
|
+
const evidence_ref = { session_ref: input.session_ref, turn: input.turn };
|
|
158
|
+
const captures = [{
|
|
159
|
+
session_ref: input.session_ref,
|
|
160
|
+
tier: 'resource',
|
|
161
|
+
kind: 'availability',
|
|
162
|
+
payload: { tool: input.tool, verdict, option_count: optionCountOf(result) },
|
|
163
|
+
evidence_ref,
|
|
164
|
+
ts: input.ts,
|
|
165
|
+
}];
|
|
166
|
+
const band = priceBandOf(result);
|
|
167
|
+
if (band) {
|
|
168
|
+
captures.push({
|
|
169
|
+
session_ref: input.session_ref,
|
|
170
|
+
tier: 'resource',
|
|
171
|
+
kind: 'price_band',
|
|
172
|
+
payload: { tool: input.tool, low: band.low, high: band.high, samples: band.samples },
|
|
173
|
+
evidence_ref,
|
|
174
|
+
ts: input.ts,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return captures;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 观察缝落账(非抛出):任何失败(账本不可用/写闸拒绝)都只是「没记住」,
|
|
181
|
+
* 绝不影响工具返回值——记忆面永远不能把产品主路径带红。
|
|
182
|
+
*
|
|
183
|
+
* 写门用 `capture_or_touch`:同一检索重复出现时**续命**(TTL 自本次写入起算),
|
|
184
|
+
* 而不是撞 stale_rev 被默默丢掉。纯 capture 会让 30min 后的同一检索永远刷不新
|
|
185
|
+
* 那条 resource 笔记,工作区于是悄悄停止供述该形态(P4-3 复查发现)。
|
|
186
|
+
* 返回值区分 captured/touched/rejected,让「没记住」在计数上可见而不是无声。
|
|
187
|
+
*/
|
|
188
|
+
export function observeToolResult(ledger, input) {
|
|
189
|
+
const zero = { captured: 0, touched: 0, rejected: 0 };
|
|
190
|
+
if (!ledger)
|
|
191
|
+
return zero;
|
|
192
|
+
try {
|
|
193
|
+
const turn = nextObservationTurn(input.session_ref);
|
|
194
|
+
const captures = projectToolObservation({ ...input, turn });
|
|
195
|
+
let captured = 0;
|
|
196
|
+
let touched = 0;
|
|
197
|
+
let rejected = 0;
|
|
198
|
+
for (const capture of captures) {
|
|
199
|
+
// 逐条独立事务:一条被负面清单拒收不连带丢掉另一条形状笔记
|
|
200
|
+
const r = appendZoneWrite(ledger, { op: 'capture_or_touch', input: capture }, { actor: `tool:${input.tool}` });
|
|
201
|
+
if (!r.ok)
|
|
202
|
+
rejected++;
|
|
203
|
+
else if (!r.appended)
|
|
204
|
+
touched++; // 同 ts 同内容重放:已是本次写入时刻,无需续命
|
|
205
|
+
else if (r.event?.kind === 'hotctx.note.revised')
|
|
206
|
+
touched++;
|
|
207
|
+
else
|
|
208
|
+
captured++;
|
|
209
|
+
}
|
|
210
|
+
return { captured, touched, rejected };
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return zero;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export const ZONE_NOTE_ACTIONS = ['capture', 'revise', 'drop', 'propose', 'deny'];
|
|
217
|
+
export function isZoneNoteAction(v) {
|
|
218
|
+
return typeof v === 'string' && ZONE_NOTE_ACTIONS.includes(v);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 提议/否决去重集(进程内;观测计数与指标口径对齐):
|
|
222
|
+
* 指标 ① 的分母按 `proposal_ref` 去重,所以同一「笔记 × 目标 kind」的重复提议
|
|
223
|
+
* 只算一个提议;确认侧同理——只在**真的落了**笔记本条目时记一次 confirm。
|
|
224
|
+
*/
|
|
225
|
+
const proposalSeen = new Set();
|
|
226
|
+
function markProposal(scope, noteId, kind) {
|
|
227
|
+
const key = `${scope}|${noteId}|${kind ?? ''}`;
|
|
228
|
+
if (proposalSeen.has(key))
|
|
229
|
+
return false;
|
|
230
|
+
proposalSeen.add(key);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
function err(code, detail) {
|
|
234
|
+
return { ok: false, code, detail };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 用户陈述吸收 / 更正即弃 / 晋升提议的统一入口。
|
|
238
|
+
* propose 分支**零落账**:返回一句要 owner 确认的话术,写入只能走
|
|
239
|
+
* `promoteWithRouting`(必须携带 owner 原话引用)。
|
|
240
|
+
*/
|
|
241
|
+
export function applyZoneNote(ledger, input) {
|
|
242
|
+
if (!isZoneNoteAction(input.action)) {
|
|
243
|
+
return err('bad_action', `未知 action:${String(input.action)}(闭集 ${ZONE_NOTE_ACTIONS.join('|')})`);
|
|
244
|
+
}
|
|
245
|
+
if (!validSessionRef(input.session_ref))
|
|
246
|
+
return err('bad_session', '缺少会话身份:无 session 绑定不捕获');
|
|
247
|
+
if (!ledger)
|
|
248
|
+
return err('no_ledger', '账本不可用:本轮不记忆(不影响工具主路径)');
|
|
249
|
+
if (input.action === 'propose') {
|
|
250
|
+
const kind = input.promoteKind;
|
|
251
|
+
if (!isNotebookKind(kind)) {
|
|
252
|
+
return err('closed_set', `未知笔记本 kind:${String(kind)}(闭集 trip_fact|preference|constraint|lesson)`);
|
|
253
|
+
}
|
|
254
|
+
if (typeof input.noteId !== 'string' || input.noteId.length === 0) {
|
|
255
|
+
return err('unknown_subject', 'propose 需要 noteId(提议把哪条工作区笔记转为持久条目)');
|
|
256
|
+
}
|
|
257
|
+
const { state } = readZoneLog(ledger);
|
|
258
|
+
if (!state.hot[input.noteId])
|
|
259
|
+
return err('unknown_subject', `工作区笔记不存在:${input.noteId}`);
|
|
260
|
+
// 按提议主体去重:指标 ① 的分母是 proposal_ref(每个提议一次),
|
|
261
|
+
// 同一条笔记被重复提议两次仍是**一个**提议——否则计数口径与指标口径不同(复查发现)。
|
|
262
|
+
if (markProposal('propose', input.noteId, kind))
|
|
263
|
+
noteZoneSignal('proposal');
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
action: 'propose',
|
|
267
|
+
appended: false,
|
|
268
|
+
proposed: true,
|
|
269
|
+
noteId: input.noteId,
|
|
270
|
+
requiresOwnerConfirm: true,
|
|
271
|
+
ask: '这条要不要我长期记住?请用你自己的话回我一句确认(模型自述不算确认);确认后我再写入笔记本。',
|
|
272
|
+
detail: '提议零落账:持久区唯一入口是 owner 确认晋升',
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
// owner 否决晋升提议(P4-4 观测面需要真实分母:没有否决面,确认率恒为 1 = 假指标)。
|
|
276
|
+
// 零落账:笔记仍留在工作区服务本次会话,只是本会话不再提议它进笔记本。
|
|
277
|
+
if (input.action === 'deny') {
|
|
278
|
+
if (typeof input.noteId !== 'string' || input.noteId.length === 0) {
|
|
279
|
+
return err('unknown_subject', 'deny 需要 noteId(owner 否决的是哪条提议)');
|
|
280
|
+
}
|
|
281
|
+
const { state } = readZoneLog(ledger);
|
|
282
|
+
if (!state.hot[input.noteId])
|
|
283
|
+
return err('unknown_subject', `工作区笔记不存在:${input.noteId}`);
|
|
284
|
+
if (markProposal('deny', input.noteId))
|
|
285
|
+
noteZoneSignal('deny');
|
|
286
|
+
return {
|
|
287
|
+
ok: true,
|
|
288
|
+
action: 'deny',
|
|
289
|
+
appended: false,
|
|
290
|
+
noteId: input.noteId,
|
|
291
|
+
detail: 'owner 否决:零落账;笔记仍服务本会话,本会话不再提议它进笔记本',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (input.action === 'drop') {
|
|
295
|
+
if (typeof input.noteId !== 'string' || input.noteId.length === 0) {
|
|
296
|
+
return err('unknown_subject', 'drop 需要 noteId');
|
|
297
|
+
}
|
|
298
|
+
const r = appendZoneWrite(ledger, { op: 'drop', note_id: input.noteId, ts: input.ts });
|
|
299
|
+
return r.ok
|
|
300
|
+
? { ok: true, action: 'drop', appended: r.appended, noteId: input.noteId, detail: r.detail }
|
|
301
|
+
: err(r.code, r.detail);
|
|
302
|
+
}
|
|
303
|
+
if (input.action === 'revise') {
|
|
304
|
+
if (typeof input.noteId !== 'string' || input.noteId.length === 0) {
|
|
305
|
+
return err('unknown_subject', 'revise 需要 noteId');
|
|
306
|
+
}
|
|
307
|
+
if (!Number.isInteger(input.expectedRev) || input.expectedRev < 1) {
|
|
308
|
+
return err('stale_rev', 'revise 需要 expectedRev(≥1 整数;父 rev 必须等于当前 rev)');
|
|
309
|
+
}
|
|
310
|
+
if (input.kind !== undefined && !isHotNoteKind(input.kind)) {
|
|
311
|
+
return err('closed_set', `未知 kind:${String(input.kind)}`);
|
|
312
|
+
}
|
|
313
|
+
const r = appendZoneWrite(ledger, {
|
|
314
|
+
op: 'revise',
|
|
315
|
+
input: {
|
|
316
|
+
note_id: input.noteId,
|
|
317
|
+
expected_rev: input.expectedRev,
|
|
318
|
+
...(input.payload !== undefined ? { payload: input.payload } : {}),
|
|
319
|
+
...(input.kind !== undefined ? { kind: input.kind } : {}),
|
|
320
|
+
ts: input.ts,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
return r.ok
|
|
324
|
+
? { ok: true, action: 'revise', appended: r.appended, noteId: input.noteId, detail: r.detail }
|
|
325
|
+
: err(r.code, r.detail);
|
|
326
|
+
}
|
|
327
|
+
// capture:用户陈述片段入 intent 层(默认);tier 由调用方显式给
|
|
328
|
+
if (!isHotTier(input.tier))
|
|
329
|
+
return err('closed_set', `未知 tier:${String(input.tier)}(闭集 resource|intent)`);
|
|
330
|
+
if (!isHotNoteKind(input.kind))
|
|
331
|
+
return err('closed_set', `未知 kind:${String(input.kind)}`);
|
|
332
|
+
if (typeof input.payload !== 'object' || input.payload === null || Array.isArray(input.payload)) {
|
|
333
|
+
return err('payload_bound', 'payload 必须是结构化对象(slot-spec 族片段,不接受自由文本)');
|
|
334
|
+
}
|
|
335
|
+
const turn = nextObservationTurn(input.session_ref);
|
|
336
|
+
// capture_or_touch:用户把同一片段再说一次 = 它仍在场 → 续 TTL,不是 stale_rev
|
|
337
|
+
const r = appendZoneWrite(ledger, {
|
|
338
|
+
op: 'capture_or_touch',
|
|
339
|
+
input: {
|
|
340
|
+
session_ref: input.session_ref,
|
|
341
|
+
tier: input.tier,
|
|
342
|
+
kind: input.kind,
|
|
343
|
+
payload: input.payload,
|
|
344
|
+
evidence_ref: { session_ref: input.session_ref, turn },
|
|
345
|
+
ts: input.ts,
|
|
346
|
+
},
|
|
347
|
+
}, { actor: 'tool:gotry_session_zone_note' });
|
|
348
|
+
if (!r.ok)
|
|
349
|
+
return err(r.code, r.detail);
|
|
350
|
+
const noteId = r.event?.kind === 'hotctx.note.captured' || r.event?.kind === 'hotctx.note.revised'
|
|
351
|
+
? r.event.note.note_id
|
|
352
|
+
: makeHotNoteId({ session_ref: input.session_ref, tier: input.tier, kind: input.kind, payload: input.payload }) ?? undefined;
|
|
353
|
+
return { ok: true, action: 'capture', appended: r.appended, noteId, detail: r.detail };
|
|
354
|
+
}
|
|
355
|
+
// ---- 晋升路由(设计 §3:命中既有语义即经既有闸,笔记本绝不平行存事实) ------------
|
|
356
|
+
export const PROMOTION_AUTHORITIES = ['motivation', 'timeline', 'companion', 'notebook'];
|
|
357
|
+
/** 同行人语义的识别键(载荷里点名了同行人 → 约束归 companion 闸) */
|
|
358
|
+
export const COMPANION_LABEL_KEYS = ['companion_label', 'companion'];
|
|
359
|
+
/**
|
|
360
|
+
* 路由分类(纯函数):
|
|
361
|
+
* - preference → 动机画像(gotry_motivation_save 的同一闸)
|
|
362
|
+
* - trip_fact → 旅行时间线(gotry_trip_log 的同一闸)
|
|
363
|
+
* - constraint + 载荷点名同行人 → 同行人档案(gotry_companion_save 的同一闸)
|
|
364
|
+
* - constraint(无同行人)/ lesson → 笔记本自身就是该语义的唯一权威
|
|
365
|
+
*/
|
|
366
|
+
export function classifyPromotionRouting(kind, payload) {
|
|
367
|
+
if (kind === 'preference')
|
|
368
|
+
return 'motivation';
|
|
369
|
+
if (kind === 'trip_fact')
|
|
370
|
+
return 'timeline';
|
|
371
|
+
if (kind === 'constraint') {
|
|
372
|
+
const named = COMPANION_LABEL_KEYS.some(k => typeof payload[k] === 'string' && payload[k].trim().length > 0);
|
|
373
|
+
return named ? 'companion' : 'notebook';
|
|
374
|
+
}
|
|
375
|
+
return 'notebook';
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* 路由逃逸检测(纯函数;分类之后、落账之前):notebook 路由只对**真的没有既有
|
|
379
|
+
* 权威**的断言成立。两条判据——
|
|
380
|
+
* 1. 载荷带着权威形状键(偏好权重/行程日期/同行人约束字段);
|
|
381
|
+
* 2. 源工作区笔记的 kind 本身就属于某个既有权威的语义(destination/date_window →
|
|
382
|
+
* 时间线,party_size → 同行人,budget_stance → 动机画像)。
|
|
383
|
+
* 命中任一条即返回拒收理由:请改按对应 kind 声明并带上该权威的路由载荷。
|
|
384
|
+
* 模型自己选的 kind 不构成豁免——这正是「单一写权威」要防的事。
|
|
385
|
+
*/
|
|
386
|
+
export function routingEscapeViolation(input) {
|
|
387
|
+
if (input.authority !== 'notebook')
|
|
388
|
+
return null;
|
|
389
|
+
for (const [authority, keys] of Object.entries(AUTHORITY_SHAPED_KEYS)) {
|
|
390
|
+
const hit = keys.find(k => input.payload[k] !== undefined);
|
|
391
|
+
if (hit !== undefined) {
|
|
392
|
+
return `载荷字段 ${hit} 属 ${authority} 语义:kind=${input.kind} 会绕过该权威的闸私存事实;请按对应 kind 声明并带上 ${authority} 路由载荷`;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const sourceAuthority = HOT_KIND_AUTHORITY[input.sourceKind];
|
|
396
|
+
if (sourceAuthority) {
|
|
397
|
+
return `源笔记 kind=${input.sourceKind} 属 ${sourceAuthority} 语义:kind=${input.kind} 会绕过该权威的闸;请按对应 kind 声明并带上 ${sourceAuthority} 路由载荷`;
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
/** 路由载荷与分类权威必须一致:为 A 权威备料却声明成 B 类别,同样是绕闸 */
|
|
402
|
+
export function routedPayloadMismatch(input) {
|
|
403
|
+
const supplied = [];
|
|
404
|
+
if (input.supplied.motivation !== undefined)
|
|
405
|
+
supplied.push('motivation');
|
|
406
|
+
if (input.supplied.trip !== undefined)
|
|
407
|
+
supplied.push('timeline');
|
|
408
|
+
if (input.supplied.companion !== undefined)
|
|
409
|
+
supplied.push('companion');
|
|
410
|
+
const stray = supplied.filter(a => a !== input.authority);
|
|
411
|
+
if (stray.length === 0)
|
|
412
|
+
return null;
|
|
413
|
+
return `路由载荷 ${stray.join('/')} 与本次分类权威 ${input.authority} 不符:请按该事实真正的类别声明 kind(模型选的 kind 不构成豁免)`;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* 权威形状键(路由逃逸检测):出现在 notebook 路由的载荷里,就说明这条断言其实
|
|
417
|
+
* 有既有写权威——把它当「教训/一般约束」存进笔记本等于绕闸私存(复查发现:
|
|
418
|
+
* 同一条持久偏好只要声明成 kind='lesson',或同行人约束的载荷不写 companion_label,
|
|
419
|
+
* 就永远到不了既有闸)。检测到即 routing_required 拒收,要求改声明到对应 kind。
|
|
420
|
+
*/
|
|
421
|
+
export const AUTHORITY_SHAPED_KEYS = {
|
|
422
|
+
motivation: ['weights', 'hard', 'homeCity', 'home_city', 'budget_tier', 'budgetTier', 'motivation', 'preference', 'redeye', 'wake_not_before'],
|
|
423
|
+
timeline: ['destination', 'start', 'end', 'trip', 'visited', 'city', 'date_window', 'checkIn', 'checkOut'],
|
|
424
|
+
companion: ['companion_label', 'companion', 'companions', 'mobility', 'health', 'prefs', 'party_size', 'adults'],
|
|
425
|
+
};
|
|
426
|
+
/** 源笔记 kind → 已有写权威的语义(notebook 路由在这些 kind 上须显式举证) */
|
|
427
|
+
export const HOT_KIND_AUTHORITY = {
|
|
428
|
+
destination: 'timeline',
|
|
429
|
+
date_window: 'timeline',
|
|
430
|
+
party_size: 'companion',
|
|
431
|
+
budget_stance: 'motivation',
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* 动机断言落地核对(纯函数):返回第一个**没有**出现在结果画像里的字段描述,
|
|
435
|
+
* 全部落地则返回 null。`appendMotivationPatch` 的 saved:false 既可能是「无变化」
|
|
436
|
+
* (已落地,幂等)也可能是「被拒」(未落地),且不带 reason——核对结果是唯一
|
|
437
|
+
* 可靠的区分方式,也顺带覆盖未来新增的拒收理由。
|
|
438
|
+
*/
|
|
439
|
+
function motivationPatchUnmet(patch, profile) {
|
|
440
|
+
for (const [k, v] of Object.entries(patch.weights ?? {})) {
|
|
441
|
+
if (profile.weights?.[k] !== v)
|
|
442
|
+
return `weights.${k} 未落地`;
|
|
443
|
+
}
|
|
444
|
+
for (const [k, v] of Object.entries(patch.hard ?? {})) {
|
|
445
|
+
if (JSON.stringify(profile.hard?.[k]) !== JSON.stringify(v))
|
|
446
|
+
return `hard.${k} 未落地`;
|
|
447
|
+
}
|
|
448
|
+
if (patch.homeCity !== undefined) {
|
|
449
|
+
const current = profile.homeCityPreference?.value;
|
|
450
|
+
const expected = typeof patch.homeCity === 'string' ? patch.homeCity.trim() : patch.homeCity;
|
|
451
|
+
if (current !== expected)
|
|
452
|
+
return 'homeCity 未落地';
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
/** 回滚载体:路由闸拒绝 → 抛出 → 整个外层事务回滚(笔记本条目与权威写不可能分叉) */
|
|
457
|
+
class ZonePromotionRejected extends Error {
|
|
458
|
+
failure;
|
|
459
|
+
constructor(failure) {
|
|
460
|
+
super(failure.code);
|
|
461
|
+
this.failure = failure;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* owner 确认晋升(唯一持久区入口):
|
|
466
|
+
* 1) owner 引用闸(缺失/超界/表面闭集外即拒;模型自述永远不是确认);
|
|
467
|
+
* 2) 路由分类 → 命中既有语义时**必须**带该权威的载荷,否则 routing_required 拒绝;
|
|
468
|
+
* 3) 单一外层事务:既有闸写入 + 笔记本事件同生共死(nested savepoint);
|
|
469
|
+
* 4) 既有闸拒绝(冲突/校验)→ 整笔回滚,返回 routed_rejected。
|
|
470
|
+
*/
|
|
471
|
+
export function promoteWithRouting(ledger, input) {
|
|
472
|
+
if (!ledger)
|
|
473
|
+
return err('no_ledger', '账本不可用:晋升不落账');
|
|
474
|
+
if (!isNotebookKind(input.kind)) {
|
|
475
|
+
return err('closed_set', `未知笔记本 kind:${String(input.kind)}(闭集 trip_fact|preference|constraint|lesson)`);
|
|
476
|
+
}
|
|
477
|
+
if (!isConfirmSurface(input.surface)) {
|
|
478
|
+
return err('closed_set', `未知确认表面:${String(input.surface)}(闭集 user_reply|approval_card)`);
|
|
479
|
+
}
|
|
480
|
+
if (typeof input.ownerQuote !== 'string' || input.ownerQuote.trim().length === 0) {
|
|
481
|
+
return err('owner_confirm', 'owner 确认引用缺失:晋升必须携带用户本人的原话(模型自述不算确认)');
|
|
482
|
+
}
|
|
483
|
+
if (input.ownerQuote.length > OWNER_QUOTE_MAX) {
|
|
484
|
+
return err('owner_confirm', `owner 确认引用超界(${input.ownerQuote.length}>${OWNER_QUOTE_MAX})`);
|
|
485
|
+
}
|
|
486
|
+
const kind = input.kind;
|
|
487
|
+
const confirm = { surface: input.surface, quote: input.ownerQuote };
|
|
488
|
+
const run = ledger.db.transaction(() => {
|
|
489
|
+
const { state } = readZoneLog(ledger);
|
|
490
|
+
const source = state.hot[input.noteId];
|
|
491
|
+
if (!source) {
|
|
492
|
+
throw new ZonePromotionRejected(err('unknown_subject', `工作区笔记不存在:${input.noteId}(已 drop/未捕获)`));
|
|
493
|
+
}
|
|
494
|
+
const payload = input.payload ?? source.payload;
|
|
495
|
+
const authority = classifyPromotionRouting(kind, payload);
|
|
496
|
+
// 路由逃逸两道闸(分类之后、任何写入之前):载荷/源 kind 暗示既有权威,
|
|
497
|
+
// 或路由载荷与分类权威不符 → 一律拒收,绝不在笔记本里私存有主的事实。
|
|
498
|
+
const escape = routingEscapeViolation({ kind, authority, payload, sourceKind: source.kind });
|
|
499
|
+
if (escape)
|
|
500
|
+
throw new ZonePromotionRejected(err('routing_required', escape));
|
|
501
|
+
const mismatch = routedPayloadMismatch({
|
|
502
|
+
authority,
|
|
503
|
+
supplied: {
|
|
504
|
+
...(input.motivation !== undefined ? { motivation: input.motivation } : {}),
|
|
505
|
+
...(input.trip !== undefined ? { trip: input.trip } : {}),
|
|
506
|
+
...(input.companion !== undefined ? { companion: input.companion } : {}),
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
if (mismatch)
|
|
510
|
+
throw new ZonePromotionRejected(err('routing_required', mismatch));
|
|
511
|
+
let routed;
|
|
512
|
+
if (authority === 'motivation') {
|
|
513
|
+
const patch = input.motivation;
|
|
514
|
+
if (!patch || (patch.weights === undefined && patch.hard === undefined && patch.homeCity === undefined)) {
|
|
515
|
+
throw new ZonePromotionRejected(err('routing_required', 'preference 晋升必须经动机画像闸:请带 motivation.{weights|hard|homeCity} 至少一项(单一写权威,笔记本不私存偏好)'));
|
|
516
|
+
}
|
|
517
|
+
const res = ledger.appendMotivationPatch({
|
|
518
|
+
...(patch.weights !== undefined ? { weights: patch.weights } : {}),
|
|
519
|
+
...(patch.hard !== undefined ? { hard: patch.hard } : {}),
|
|
520
|
+
...(patch.homeCity !== undefined ? { homeCity: patch.homeCity, homeCityEvidence: input.ownerQuote } : {}),
|
|
521
|
+
evidence: [input.ownerQuote],
|
|
522
|
+
}, 'tool:gotry_session_zone_promote');
|
|
523
|
+
// 动机闸的 saved:false 是二义的(「无变化」与「被拒」同一个返回,且不带 reason),
|
|
524
|
+
// 不能像时间线/同行人那样读 reason。所以改为**核对结果**:断言的每个字段必须
|
|
525
|
+
// 真的出现在返回画像里,否则就是被拒 → 整笔回滚(复查发现:被拒的偏好晋升
|
|
526
|
+
// 原先仍会落一条笔记本条目,等于笔记本替动机画像背书)。
|
|
527
|
+
const unmet = motivationPatchUnmet(patch, res.profile);
|
|
528
|
+
if (unmet) {
|
|
529
|
+
throw new ZonePromotionRejected(err('routed_rejected', `动机画像闸未落地该断言(${unmet});整笔晋升回滚`));
|
|
530
|
+
}
|
|
531
|
+
routed = { authority, applied: res.saved, idempotent: !res.saved };
|
|
532
|
+
}
|
|
533
|
+
else if (authority === 'timeline') {
|
|
534
|
+
const trip = input.trip;
|
|
535
|
+
if (!trip || typeof trip.destination !== 'string' || typeof trip.start !== 'string') {
|
|
536
|
+
throw new ZonePromotionRejected(err('routing_required', 'trip_fact 晋升必须经时间线闸:请带 trip.{destination,start(YYYY-MM-DD),end?}(单一写权威,笔记本不私存行程)'));
|
|
537
|
+
}
|
|
538
|
+
const res = ledger.appendTripEvent({
|
|
539
|
+
destination: trip.destination,
|
|
540
|
+
start: trip.start,
|
|
541
|
+
...(trip.end !== undefined ? { end: trip.end } : {}),
|
|
542
|
+
...(trip.companions !== undefined ? { companions: trip.companions } : {}),
|
|
543
|
+
source: 'user-verbatim',
|
|
544
|
+
evidence: input.ownerQuote,
|
|
545
|
+
}, 'tool:gotry_session_zone_promote');
|
|
546
|
+
if (!res.appended && res.reason) {
|
|
547
|
+
throw new ZonePromotionRejected(err('routed_rejected', `时间线闸拒绝:${res.reason}(整笔晋升回滚)`));
|
|
548
|
+
}
|
|
549
|
+
routed = { authority, applied: res.appended, idempotent: !res.appended, ...(res.tripId ? { ref: res.tripId } : {}) };
|
|
550
|
+
}
|
|
551
|
+
else if (authority === 'companion') {
|
|
552
|
+
const companion = input.companion;
|
|
553
|
+
if (!companion || typeof companion.label !== 'string' || companion.label.trim().length === 0) {
|
|
554
|
+
throw new ZonePromotionRejected(err('routing_required', '同行人约束晋升必须经同行人闸:请带 companion.{label,constraints}(单一写权威,笔记本不私存同行人约束)'));
|
|
555
|
+
}
|
|
556
|
+
const res = ledger.appendCompanion({
|
|
557
|
+
label: companion.label,
|
|
558
|
+
constraints: companion.constraints ?? {},
|
|
559
|
+
evidence: input.ownerQuote,
|
|
560
|
+
}, 'tool:gotry_session_zone_promote');
|
|
561
|
+
if (!res.appended && res.reason) {
|
|
562
|
+
throw new ZonePromotionRejected(err('routed_rejected', `同行人闸拒绝:${res.reason}(整笔晋升回滚)`));
|
|
563
|
+
}
|
|
564
|
+
routed = { authority, applied: res.appended, idempotent: !res.appended, ...(res.companionId ? { ref: res.companionId } : {}) };
|
|
565
|
+
}
|
|
566
|
+
const results = appendZoneWrites(ledger, [{
|
|
567
|
+
op: 'promote',
|
|
568
|
+
input: {
|
|
569
|
+
note_id: input.noteId,
|
|
570
|
+
kind,
|
|
571
|
+
...(input.payload !== undefined ? { payload: input.payload } : {}),
|
|
572
|
+
owner_confirm: confirm,
|
|
573
|
+
ts: input.ts,
|
|
574
|
+
},
|
|
575
|
+
}], { actor: 'tool:gotry_session_zone_promote' });
|
|
576
|
+
const r = results[0];
|
|
577
|
+
if (!r.ok)
|
|
578
|
+
throw new ZonePromotionRejected(err(r.code, r.detail));
|
|
579
|
+
// 只有真的落了条目才算一次 owner 确认(幂等重放不是第二次确认);同口径去重
|
|
580
|
+
if (r.appended && markProposal('confirm', input.noteId, kind))
|
|
581
|
+
noteZoneSignal('confirm');
|
|
582
|
+
const entryId = r.event?.kind === 'notebook.entry.promoted' ? r.event.entry.entry_id : undefined;
|
|
583
|
+
return {
|
|
584
|
+
ok: true,
|
|
585
|
+
appended: r.appended,
|
|
586
|
+
...(entryId ? { entryId } : {}),
|
|
587
|
+
authority,
|
|
588
|
+
...(routed ? { routed } : {}),
|
|
589
|
+
...(r.detail ? { detail: r.detail } : {}),
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
try {
|
|
593
|
+
return run();
|
|
594
|
+
}
|
|
595
|
+
catch (e) {
|
|
596
|
+
if (e instanceof ZonePromotionRejected)
|
|
597
|
+
return e.failure;
|
|
598
|
+
return err('routed_rejected', `晋升事务失败,整笔回滚:${e instanceof Error ? e.message : String(e)}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// ---- persona 读回(预算有界;首访为空串) ------------------------------------------
|
|
602
|
+
export const ZONE_BRIEF_MAX_WORKING = 6;
|
|
603
|
+
export const ZONE_BRIEF_MAX_NOTEBOOK = 5;
|
|
604
|
+
export const ZONE_BRIEF_MAX_CHARS = 900;
|
|
605
|
+
function compactPayload(payload, max = 120) {
|
|
606
|
+
const body = Object.entries(payload)
|
|
607
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
608
|
+
.map(([k, v]) => `${k}=${typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v)}`)
|
|
609
|
+
.join(' ');
|
|
610
|
+
return body.length > max ? body.slice(0, max - 1) + '…' : body;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* 读回 brief(纯函数;空 = 首访/无活笔记):
|
|
614
|
+
* - 在谈(intent 层,跨会话可读到过期)= 「回来接着聊」的那部分;
|
|
615
|
+
* - 本次会话资源(resource 层)只在绑定会话内出现;
|
|
616
|
+
* - 笔记本 = owner 确认过的持久条目(带原话依据指针)。
|
|
617
|
+
* 预算:每段条数上界 + 全文字符上界(超界截断并显式标注)。
|
|
618
|
+
*/
|
|
619
|
+
export function renderZoneBrief(input) {
|
|
620
|
+
const intent = input.working.filter(n => n.tier === 'intent').slice(0, ZONE_BRIEF_MAX_WORKING);
|
|
621
|
+
const resource = input.sessionRef
|
|
622
|
+
? input.working.filter(n => n.tier === 'resource' && n.session_ref === input.sessionRef).slice(0, ZONE_BRIEF_MAX_WORKING)
|
|
623
|
+
: [];
|
|
624
|
+
const notebook = input.notebook.slice(0, ZONE_BRIEF_MAX_NOTEBOOK);
|
|
625
|
+
if (intent.length === 0 && resource.length === 0 && notebook.length === 0)
|
|
626
|
+
return '';
|
|
627
|
+
const lines = ['## 会话双区记忆(工作区=仍在谈的;笔记本=你确认过要长期记的;与你当轮说法冲突时以你为准)'];
|
|
628
|
+
if (intent.length) {
|
|
629
|
+
lines.push(`- 仍在谈(24h 内): ${intent.map(n => `${n.kind}{${compactPayload(n.payload)}}`).join('; ')}——已确立的字段不重复问`);
|
|
630
|
+
}
|
|
631
|
+
if (resource.length) {
|
|
632
|
+
lines.push(`- 本次会话查到的(30min 内): ${resource.map(n => `${n.kind}{${compactPayload(n.payload)}}`).join('; ')}——过期即失效,可下单事实仍须当轮工具复核`);
|
|
633
|
+
}
|
|
634
|
+
if (notebook.length) {
|
|
635
|
+
lines.push(`- 笔记本(你确认过): ${notebook.map(e => `${e.kind}{${compactPayload(e.payload)}}`).join('; ')}——引用时带当初的原话依据`);
|
|
636
|
+
}
|
|
637
|
+
const text = lines.join('\n');
|
|
638
|
+
return text.length > ZONE_BRIEF_MAX_CHARS ? text.slice(0, ZONE_BRIEF_MAX_CHARS - 1) + '…' : text;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* 读回组装(账本 → fold → 读视图 → brief):
|
|
642
|
+
* 总闸关闭 / 无账本 / 读失败 一律返回空串——读回永不抛,永不建库。
|
|
643
|
+
*/
|
|
644
|
+
export function renderSessionZoneBrief(input) {
|
|
645
|
+
if (input.zoneSwitch !== 'on' || !input.ledger)
|
|
646
|
+
return '';
|
|
647
|
+
try {
|
|
648
|
+
const { state } = readZoneLog(input.ledger);
|
|
649
|
+
const working = readWorkingZone(state, { now: input.now, session_ref: input.sessionRef ?? '' });
|
|
650
|
+
const brief = renderZoneBrief({ working, notebook: readNotebook(state), sessionRef: input.sessionRef });
|
|
651
|
+
// 工作区读命中/未命中(形状计数;opt-in 关闭时为 no-op)
|
|
652
|
+
noteZoneSignal(brief === '' ? 'read_miss' : 'read_hit');
|
|
653
|
+
return brief;
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
return '';
|
|
657
|
+
}
|
|
658
|
+
}
|