@danceiny/gotry 0.0.1-rc.21 → 0.0.1-rc.24
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 +102 -194
- package/README.zh-CN.md +115 -210
- package/bin/gotry-backend.js +53 -0
- package/bin/gotry-bootstrap.js +466 -35
- package/bin/gotry-inner.js +255 -36
- package/bin/gotry-process-liveness.d.ts +27 -0
- package/bin/gotry-process-liveness.js +97 -0
- package/bin/gotry-runtime-resolution.d.ts +1 -1
- package/bin/gotry-runtime-resolution.js +2 -2
- package/client/client.js +174 -0
- package/cordis.gotry-patch.yml +70 -93
- package/dist/capabilities/agent-reach-deep.js +45 -122
- package/dist/capabilities/agent-reach.js +63 -148
- package/dist/capabilities/anything.js +89 -75
- package/dist/capabilities/artifacts.js +123 -104
- package/dist/capabilities/channel-health.js +65 -45
- package/dist/capabilities/channel-registry.js +108 -87
- package/dist/capabilities/doctor.js +357 -235
- package/dist/capabilities/effect.js +223 -309
- package/dist/capabilities/fact-log.js +28 -14
- package/dist/capabilities/flyai.js +245 -156
- package/dist/capabilities/ground-transfer.js +509 -0
- package/dist/capabilities/hbcli.js +144 -148
- package/dist/capabilities/hotelbyte-transaction.js +194 -0
- package/dist/capabilities/incident-log.js +131 -61
- package/dist/capabilities/model-override.js +30 -10
- package/dist/capabilities/opensky.js +54 -43
- package/dist/capabilities/resilience.js +50 -32
- package/dist/capabilities/session/action-cache.js +50 -44
- package/dist/capabilities/session/adapters/ctrip-flight.js +119 -62
- package/dist/capabilities/session/adapters/ctrip-hotel.js +125 -110
- package/dist/capabilities/session/adapters/dida-portal.js +206 -0
- package/dist/capabilities/session/adapters/meituan-local.js +35 -54
- package/dist/capabilities/session/adapters/rail-12306.js +169 -112
- package/dist/capabilities/session/benchmark.js +85 -149
- package/dist/capabilities/session/extension-bridge.js +320 -259
- package/dist/capabilities/session/extension-channel.js +57 -76
- package/dist/capabilities/session/extension-distribution.js +115 -119
- package/dist/capabilities/session/extract.js +31 -21
- package/dist/capabilities/session/golden-score.js +55 -33
- package/dist/capabilities/session/health-watch.js +146 -154
- package/dist/capabilities/session/read-guard.js +55 -47
- package/dist/capabilities/session/static-flight-golden.js +49 -36
- package/dist/capabilities/session/transport.js +54 -61
- package/dist/capabilities/session/wizard.js +63 -93
- package/dist/capabilities/session-consent.js +143 -47
- package/dist/capabilities/session-login.js +95 -126
- package/dist/capabilities/session-search.js +391 -253
- package/dist/capabilities/sponsor-plugin.js +341 -0
- package/dist/capabilities/visa-policy.js +87 -73
- package/dist/capabilities/weather.js +163 -235
- package/dist/scripts/action-cache-tests.js +24 -59
- package/dist/scripts/agent-planning-turn-deadline-e2e.js +91 -142
- package/dist/scripts/agent-planning-turn-deadline-tests.js +80 -176
- package/dist/scripts/agent-reach-deep-tests.js +30 -40
- package/dist/scripts/agent-reach-tests.js +26 -18
- package/dist/scripts/agent-reach-wrapper-tests.js +46 -50
- package/dist/scripts/anything-tests.js +58 -62
- package/dist/scripts/artifact-client-contract-tests.js +69 -0
- package/dist/scripts/artifacts-capability-tests.js +235 -0
- package/dist/scripts/async-collect.js +26 -15
- package/dist/scripts/benchmark-environment-bridge-e2e.js +287 -684
- package/dist/scripts/benchmark-environment-bridge-tests.js +818 -3123
- package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +66 -279
- package/dist/scripts/booking-copilot-availability-policy-tests.js +153 -1257
- package/dist/scripts/booking-copilot-bin-proof-tests.js +22 -45
- package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +64 -76
- package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +69 -199
- package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +393 -759
- package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +33 -75
- package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +60 -110
- package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +28 -66
- package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +72 -271
- package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +73 -275
- package/dist/scripts/booking-copilot-runtime-proof-tests.js +784 -3398
- package/dist/scripts/booking-copilot-server-proof-tests.js +63 -168
- package/dist/scripts/booking-copilot-startup-proof-tests.js +38 -124
- 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 +61 -123
- package/dist/scripts/booking-surface-contract-proof-tests.js +152 -1167
- package/dist/scripts/bootstrap-tests.js +1383 -128
- package/dist/scripts/build-changelog.js +89 -130
- package/dist/scripts/build-metrics-report.js +135 -143
- package/dist/scripts/changelog-tests.js +35 -82
- package/dist/scripts/channel-probe-tests.js +44 -116
- package/dist/scripts/channel-probe.js +84 -167
- package/dist/scripts/channel-registry-tests.js +97 -185
- package/dist/scripts/companion-tests.js +25 -70
- package/dist/scripts/dida-cookie-probe.js +87 -0
- package/dist/scripts/dida-sw-probe.js +114 -0
- package/dist/scripts/diff-test.js +17 -12
- package/dist/scripts/doctor-tests.js +428 -120
- package/dist/scripts/dsh-artifact-e2e.js +150 -0
- package/dist/scripts/dsh-artifact-web-e2e.js +443 -0
- package/dist/scripts/dsh-http-proxy-sse-proof.js +90 -0
- package/dist/scripts/dsh-runtime-closure-tests.js +110 -153
- package/dist/scripts/dsh-runtime-closure.js +75 -63
- package/dist/scripts/dsh-session-v3-migration-proof.js +106 -0
- package/dist/scripts/dsh-subprocess-local-proof.js +149 -0
- package/dist/scripts/dsh-target-closure-proof.js +178 -0
- package/dist/scripts/effect-tests.js +165 -552
- package/dist/scripts/engine-run.js +1 -3
- package/dist/scripts/engine-tests.js +16 -10
- package/dist/scripts/evaluation-cadence-tests.js +69 -242
- package/dist/scripts/evaluation-contract-tests.js +193 -499
- package/dist/scripts/extension-distribution-cli.js +19 -15
- package/dist/scripts/extension-distribution-tests.js +98 -211
- package/dist/scripts/extension-tests.js +366 -389
- package/dist/scripts/fact-gate-tests.js +725 -263
- 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-tests.js +325 -68
- package/dist/scripts/g5-guard-tests.js +131 -0
- package/dist/scripts/g5-guard.js +189 -0
- package/dist/scripts/gen-lunar-anchors.js +106 -0
- package/dist/scripts/gotry-backend-kernel-tests.js +79 -0
- package/dist/scripts/ground-transfer-tests.js +956 -0
- package/dist/scripts/hbcli-e2e-tests.js +71 -90
- 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 +41 -115
- package/dist/scripts/health-watch-cli.js +42 -28
- 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 +16 -9
- 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 +547 -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/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/ledger-repair-apply-tests.js +316 -0
- package/dist/scripts/ledger-repair-plan-tests.js +375 -0
- package/dist/scripts/ledger-tests.js +325 -340
- package/dist/scripts/ledger-workflow-crash.js +10 -20
- package/dist/scripts/map-tools-vendor-package-proof.js +206 -134
- 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 +474 -222
- package/dist/scripts/memory-value-tests.js +328 -0
- package/dist/scripts/metrics-report-tests.js +62 -199
- package/dist/scripts/model-override-e2e.js +66 -105
- package/dist/scripts/nightly-evidence-tests.js +64 -89
- package/dist/scripts/nightly-evidence.js +108 -111
- package/dist/scripts/nudge-digest.js +39 -28
- package/dist/scripts/onboarding-tests.js +63 -88
- package/dist/scripts/opensky-check.js +18 -13
- package/dist/scripts/opensky-tests.js +28 -28
- package/dist/scripts/persona-surface-guard-tests.js +37 -12
- package/dist/scripts/pnpm-dsh-closure-proof.js +2 -5
- package/dist/scripts/policy-anchor-359-registered-e2e.js +214 -0
- package/dist/scripts/price-drift-tests.js +126 -227
- package/dist/scripts/price-drift-watch.js +209 -253
- package/dist/scripts/probe-poi-tests.js +52 -45
- package/dist/scripts/product-metrics.js +187 -273
- package/dist/scripts/publish-preverify.js +47 -41
- package/dist/scripts/realtime-pricing-tests.js +37 -76
- package/dist/scripts/replay-async.js +20 -18
- package/dist/scripts/replay-real.js +16 -18
- package/dist/scripts/replay.js +52 -82
- package/dist/scripts/session-attach-diagnose.js +16 -12
- package/dist/scripts/session-attach-poc.js +34 -37
- package/dist/scripts/session-benchmark.js +66 -147
- package/dist/scripts/session-dida-live-e2e.js +151 -0
- package/dist/scripts/session-extract-tests.js +14 -35
- package/dist/scripts/session-login.js +14 -13
- package/dist/scripts/session-tests.js +545 -583
- package/dist/scripts/sf-live-benchmark.js +92 -110
- package/dist/scripts/sf-live-cli-tests.js +2 -9
- package/dist/scripts/sf-soft-score-tests.js +19 -60
- package/dist/scripts/sf-summary-tests.js +149 -0
- package/dist/scripts/sf-summary.js +466 -84
- package/dist/scripts/skeleton-check.js +16 -14
- package/dist/scripts/skeleton-integration-test.js +11 -10
- package/dist/scripts/skills-contract-tests.js +51 -46
- package/dist/scripts/smoke-session-gate-tests.js +12 -20
- package/dist/scripts/smoke.js +541 -422
- package/dist/scripts/sponsor-reuse-tests.js +262 -0
- package/dist/scripts/state-cli-tests.js +226 -101
- package/dist/scripts/state-cli.js +549 -212
- package/dist/scripts/static-golden-tests.js +46 -180
- package/dist/scripts/time-eval-tests.js +210 -192
- package/dist/scripts/travel-timeline-tests.js +29 -93
- package/dist/scripts/turn-handoff-collect-tests.js +46 -93
- package/dist/scripts/turn-handoff-collect.js +71 -99
- package/dist/scripts/turn-policy-tests.js +28 -29
- package/dist/scripts/typed-contract-canary.js +77 -162
- package/dist/scripts/tz-resolver-sanity.js +50 -0
- package/dist/scripts/tz-resolver-tests.js +584 -0
- package/dist/scripts/unified-tests.js +23 -19
- package/dist/scripts/visa-policy-tests.js +31 -44
- package/dist/scripts/weather-tests.js +121 -543
- package/dist/scripts/wish-channel-gate-tests.js +29 -86
- 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 +18 -12
- package/dist/src/artifact-gate.js +652 -221
- package/dist/src/backend/kernel.js +91 -0
- package/dist/src/backend/modules/booking-copilot.js +55 -0
- package/dist/src/backend/modules/booking-executor.js +124 -0
- package/dist/src/backend/modules/session-search.js +194 -0
- package/dist/src/benchmark-agent-conformance.js +296 -262
- package/dist/src/benchmark-environment-bridge.js +298 -413
- package/dist/src/benchmark-headless-child-diagnostics.js +93 -100
- package/dist/src/benchmark-tool-isolation.js +94 -67
- package/dist/src/bookable-facts.js +299 -174
- package/dist/src/booking-saga.js +64 -120
- package/dist/src/booking-surface/availability-policy.js +443 -576
- package/dist/src/booking-surface/cancel-refund-commission.js +379 -0
- package/dist/src/booking-surface/contracts.js +24 -80
- package/dist/src/booking-surface/dsh-planner.js +510 -281
- package/dist/src/booking-surface/dsh-plugin.js +17 -1
- package/dist/src/booking-surface/error-codes.js +82 -10
- package/dist/src/booking-surface/index.js +0 -3
- package/dist/src/booking-surface/profile.js +10 -54
- package/dist/src/booking-surface/recovery-chain.js +367 -0
- package/dist/src/booking-surface/runtime.js +765 -891
- package/dist/src/booking-surface/server.js +200 -251
- package/dist/src/booking-surface/startup.js +58 -54
- package/dist/src/booking-surface/validation.js +150 -218
- package/dist/src/bridge.js +20 -13
- package/dist/src/companions.js +48 -74
- package/dist/src/contracts.js +21 -43
- package/dist/src/dsh-llm.js +82 -120
- package/dist/src/engine.js +253 -239
- package/dist/src/evaluation-cadence.js +72 -209
- package/dist/src/evaluation-contracts.js +220 -843
- package/dist/src/flight-pack-adapter.js +54 -0
- package/dist/src/flight-pack-contract.js +2 -0
- package/dist/src/gotry-backend.js +70 -0
- package/dist/src/hotel-date-gate.js +118 -0
- package/dist/src/i18n.js +18 -7
- package/dist/src/index.js +1425 -1629
- package/dist/src/journey.js +132 -81
- package/dist/src/ledger-repair-apply.js +538 -0
- package/dist/src/ledger-repair-plan.js +463 -0
- package/dist/src/loop.js +257 -166
- 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/realtime-pricing.js +63 -72
- package/dist/src/slot-spec.js +84 -106
- package/dist/src/state-ledger.js +310 -471
- 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 +121 -127
- package/dist/src/turn-policy.js +61 -89
- package/dist/src/tz-resolver.js +143 -0
- package/dist/src/unified.js +504 -381
- package/dist/src/wish-pool.js +39 -14
- package/dist/src/write-gate.js +495 -0
- package/dist/src/z3-shared.js +273 -7
- package/extension/README.md +4 -3
- package/extension/background.js +125 -14
- package/extension/content-bridge.js +23 -1
- package/extension/content-main.js +15 -4
- package/extension/manifest.json +16 -8
- package/package.json +251 -220
- package/ts/capabilities/agent-reach.ts +4 -4
- package/ts/capabilities/anything.ts +1 -1
- package/ts/capabilities/artifacts.ts +46 -9
- package/ts/capabilities/flyai.ts +156 -37
- package/ts/capabilities/hbcli.ts +1 -1
- package/ts/capabilities/incident-log.ts +88 -59
- package/ts/capabilities/session/adapters/ctrip-flight.ts +99 -36
- package/ts/capabilities/session/adapters/dida-portal.ts +250 -0
- package/ts/capabilities/session/adapters/rail-12306.ts +123 -24
- package/ts/capabilities/session/extension-bridge.ts +226 -101
- package/ts/capabilities/session/extension-channel.ts +22 -9
- package/ts/capabilities/session/wizard.ts +2 -2
- package/ts/capabilities/session-consent.ts +98 -14
- package/ts/capabilities/session-login.ts +1 -0
- package/ts/capabilities/session-search.ts +315 -31
- package/ts/dsh-runtime/vendor/README.md +8 -4
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.en.md +7 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/README.md +5 -3
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/settings-ns.js +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/lib/types/settings-ns.d.ts +1 -1
- package/ts/dsh-runtime/vendor/dsh-map-tools/package.json +6 -6
- package/ts/package.json +236 -16
- package/ts/scripts/map-tools-vendor-package-proof.ts +152 -31
- package/ts/scripts/state-cli.ts +418 -51
- package/ts/src/artifact-gate.ts +561 -15
- package/ts/src/benchmark-environment-bridge.ts +37 -20
- package/ts/src/bookable-facts.ts +183 -7
- package/ts/src/booking-surface/cancel-refund-commission.ts +488 -0
- package/ts/src/booking-surface/dsh-planner.ts +240 -32
- package/ts/src/booking-surface/dsh-plugin.js +17 -1
- package/ts/src/booking-surface/error-codes.ts +75 -0
- package/ts/src/booking-surface/recovery-chain.ts +409 -0
- package/ts/src/booking-surface/server.ts +84 -51
- package/ts/src/booking-surface/validation.ts +2 -0
- package/ts/src/contracts.ts +12 -2
- package/ts/src/dsh-llm.ts +4 -8
- package/ts/src/index.ts +609 -90
- package/ts/src/loop.ts +116 -7
- package/ts/src/memory-capture.ts +227 -3
- package/ts/src/mock-llm.ts +4 -8
- package/ts/src/model.ts +88 -2
- package/ts/src/state-ledger.ts +47 -29
- package/ts/src/subagent-job-id-guard.ts +72 -0
- package/ts/src/time-anchor.ts +217 -13
- package/ts/src/unified.ts +316 -54
|
@@ -1,31 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn routing & policy table tests (ADR-24 v2).
|
|
3
|
+
* 分类器是纯函数(Tier 0,零 LLM 零 IO),表测锁定 v1 规则面;
|
|
4
|
+
* 首条 fixture 是 2026-09-02 失败轨迹的用户原文——canonical 深规划
|
|
5
|
+
* case 必须命中 deep-planning,这是整个重设计的回归锚点。
|
|
6
|
+
*/
|
|
1
7
|
import assert from 'node:assert/strict';
|
|
2
|
-
import { classifyTurn, extractTurnSignals, TURN_POLICIES, turnPolicyFor } from '../src/turn-policy.js';
|
|
3
|
-
|
|
8
|
+
import { classifyTurn, extractTurnSignals, TURN_POLICIES, turnPolicyFor, } from '../src/turn-policy.js';
|
|
9
|
+
// ---- 分类表(规则面快照) ---------------------------------------------------
|
|
10
|
+
// 失败轨迹原文:13 天行程 + 10.3 婚礼 + 请假 + IRW → 必须转后台。
|
|
11
|
+
const TRAJECTORY_MESSAGE = '2026年我还有6天IRW额度 ,我准备再请几天假,在国内待个十几天。'
|
|
12
|
+
+ '其中10.3要去湖南衡阳参加同学婚礼。其他我没有特别的要求,请根据我的实际情况,偏好,外部环境等因素,给我安排行程';
|
|
4
13
|
assert.equal(classifyTurn(TRAJECTORY_MESSAGE), 'deep-planning');
|
|
5
|
-
assert.equal(classifyTurn('9.26到10.12在国内玩,中间要参加同学婚礼'), 'deep-planning');
|
|
6
|
-
assert.equal(classifyTurn('国庆请假带父母去新疆,大概十几天'), 'deep-planning');
|
|
14
|
+
assert.equal(classifyTurn('9.26到10.12在国内玩,中间要参加同学婚礼'), 'deep-planning'); // 双绝对日期+约束,跨度16天
|
|
15
|
+
assert.equal(classifyTurn('国庆请假带父母去新疆,大概十几天'), 'deep-planning'); // 跨度词+双约束
|
|
7
16
|
assert.equal(classifyTurn('帮我查一下明天深圳的天气'), 'quick');
|
|
8
17
|
assert.equal(classifyTurn('你好'), 'quick');
|
|
9
|
-
assert.equal(classifyTurn('帮我看看10.1去杭州的高铁票'), 'sync-planning');
|
|
10
|
-
assert.equal(classifyTurn('3天后出发,帮我查下天气'), 'sync-planning');
|
|
11
|
-
assert.equal(classifyTurn('把这段翻译成英文:会议纪要如下……省略若干字'.repeat(3)), 'sync-planning');
|
|
18
|
+
assert.equal(classifyTurn('帮我看看10.1去杭州的高铁票'), 'sync-planning'); // 单日期零约束 → 默认中间档
|
|
19
|
+
assert.equal(classifyTurn('3天后出发,帮我查下天气'), 'sync-planning'); // 短跨度词 → 不进 quick 也不进 deep
|
|
20
|
+
assert.equal(classifyTurn('把这段翻译成英文:会议纪要如下……省略若干字'.repeat(3)), 'sync-planning'); // 长但零信号
|
|
21
|
+
// ---- 信号提取 ---------------------------------------------------------------
|
|
12
22
|
const full = extractTurnSignals('2026-10-03 出发');
|
|
13
|
-
assert.deepEqual(full.absoluteDates, [
|
|
14
|
-
{
|
|
15
|
-
year: 2026,
|
|
16
|
-
month: 10,
|
|
17
|
-
day: 3
|
|
18
|
-
}
|
|
19
|
-
]);
|
|
23
|
+
assert.deepEqual(full.absoluteDates, [{ year: 2026, month: 10, day: 3 }]);
|
|
20
24
|
const md = extractTurnSignals('10月3日到衡阳,10.5回深圳');
|
|
21
25
|
assert.equal(md.absoluteDates.length, 2);
|
|
22
26
|
assert.equal(md.spanDays, 2);
|
|
23
|
-
assert.deepEqual(md.absoluteDates[0], {
|
|
24
|
-
|
|
25
|
-
month: 10,
|
|
26
|
-
day: 3
|
|
27
|
-
});
|
|
27
|
+
assert.deepEqual(md.absoluteDates[0], { year: null, month: 10, day: 3 });
|
|
28
|
+
// 无效月/日过滤;时间(冒号)与版本号外的数字不产日期
|
|
28
29
|
assert.equal(extractTurnSignals('13.45 与 22:40 都是数字').absoluteDates.length, 0);
|
|
30
|
+
// 跨度词:按强度取先
|
|
29
31
|
assert.equal(extractTurnSignals('待十几天').spanDays, 12);
|
|
30
32
|
assert.equal(extractTurnSignals('待半个月').spanDays, 15);
|
|
31
33
|
assert.equal(extractTurnSignals('待一个月').spanDays, 30);
|
|
@@ -33,24 +35,21 @@ assert.equal(extractTurnSignals('大概两周').spanDays, 14);
|
|
|
33
35
|
assert.equal(extractTurnSignals('大概一周').spanDays, 7);
|
|
34
36
|
assert.equal(extractTurnSignals('还有6天额度').spanDays, 6);
|
|
35
37
|
assert.equal(extractTurnSignals('没有跨度').spanDays, null);
|
|
38
|
+
// 绝对日期 ≥2 时跨度词不参与
|
|
36
39
|
const both = extractTurnSignals('10.1 到 10.9 玩十几天');
|
|
37
40
|
assert.equal(both.spanDays, 8);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
'IRW',
|
|
41
|
-
'请假'
|
|
42
|
-
]);
|
|
41
|
+
// 约束词:命中去重、按词表序
|
|
42
|
+
assert.deepEqual(extractTurnSignals('婚礼加IRW,还要请假').constraintHits, ['婚礼', 'IRW', '请假']);
|
|
43
43
|
assert.deepEqual(extractTurnSignals('没有任何约束词在这里').constraintHits, []);
|
|
44
|
-
|
|
44
|
+
// user 消息里最常见的误触:quick 判据必须同时满足零日期/零约束/零跨度/短
|
|
45
|
+
assert.equal(classifyTurn('10.3 婚礼那天衡阳天气怎么样?穿什么'), 'sync-planning'); // 有日期+约束 → 不冒进 quick
|
|
46
|
+
// ---- policy 表 ---------------------------------------------------------------
|
|
45
47
|
assert.equal(TURN_POLICIES['deep-planning'].exit, 'handoff');
|
|
46
48
|
assert.equal(TURN_POLICIES['sync-planning'].exit, 'converge');
|
|
47
49
|
assert.equal(TURN_POLICIES['quick'].exit, 'converge');
|
|
48
|
-
for (const policy of Object.values(TURN_POLICIES)){
|
|
50
|
+
for (const policy of Object.values(TURN_POLICIES)) {
|
|
49
51
|
assert.ok(policy.hardMs > policy.softMs, 'hard must exceed soft');
|
|
50
52
|
assert.ok(policy.softMs > 0 && policy.hardMs > 0);
|
|
51
53
|
}
|
|
52
54
|
assert.deepEqual(turnPolicyFor('quick'), TURN_POLICIES['quick']);
|
|
53
55
|
console.log('turn policy tests: OK (routing table incl. trajectory verbatim, date/span extraction, policy shape)');
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//# sourceURL=ts/scripts/turn-policy-tests.ts
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D-30 普通模型 canary 运行器(issue #112 验收,tool-orchestration-design §4⑥)。
|
|
3
|
+
*
|
|
4
|
+
* 量什么:普通 LLM 面对投影后的工具 JSON Schema,能否**一次成型**产出合法参数——
|
|
5
|
+
* 这是 typed 参数契约迁移(D-30)要解决的核心问题(blob 时代强弱模型差距全部
|
|
6
|
+
* 暴露在「猜参数形状」上)。
|
|
7
|
+
*
|
|
8
|
+
* 怎么量:真实 LLM(env 三件套 LLM_API_KEY/LLM_BASE_URL/LLM_MODEL,或仓库 .env)
|
|
9
|
+
* + 工具投影 schema(defineTool 后的 parameters,与模型所见完全一致)+ tool_choice
|
|
10
|
+
* 强制单工具 → 模型出参 → `validateJsonSchemaValue`(与 dsh defineTool execute 前
|
|
11
|
+
* 同款校验)→ 违例为零 = 该例一次成型。
|
|
12
|
+
*
|
|
13
|
+
* 不执行工具:零检索副作用、零站点请求——canary 只量参数形态,不量业务结果。
|
|
14
|
+
*
|
|
15
|
+
* 达标线:工具调用一次成型率 ≥90%(10 例容 1 失);语义正确性(城市/日期/kind)
|
|
16
|
+
* 作为二级信号同报,不进门。
|
|
17
|
+
*
|
|
18
|
+
* 运行(在仓根或 ts/):LLM 三件套已配 → npx tsx ts/scripts/typed-contract-canary.ts
|
|
19
|
+
*/
|
|
1
20
|
import assert from 'node:assert/strict';
|
|
2
21
|
import { readFileSync, existsSync, mkdtempSync } from 'node:fs';
|
|
3
22
|
import { tmpdir } from 'node:os';
|
|
@@ -6,6 +25,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
25
|
import { validateJsonSchemaValue } from '@deepseek-ai/dsh-tools';
|
|
7
26
|
import { apply } from '../src/index.js';
|
|
8
27
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
28
|
+
// --- LLM 三件套:env 优先,回退仓库 .env(只取值,永不回显) ---
|
|
9
29
|
function loadEnvTriple() {
|
|
10
30
|
const env = process.env;
|
|
11
31
|
let key = env.LLM_API_KEY ?? '';
|
|
@@ -13,95 +33,47 @@ function loadEnvTriple() {
|
|
|
13
33
|
let model = env.LLM_MODEL ?? '';
|
|
14
34
|
const envFile = join(ROOT, '.env');
|
|
15
35
|
if (existsSync(envFile)) {
|
|
16
|
-
for (const line of readFileSync(envFile, 'utf-8').split('\n')){
|
|
36
|
+
for (const line of readFileSync(envFile, 'utf-8').split('\n')) {
|
|
17
37
|
const m = /^([A-Z_]+)=(.*)$/.exec(line.trim());
|
|
18
|
-
if (!m)
|
|
38
|
+
if (!m)
|
|
39
|
+
continue;
|
|
19
40
|
const [, k, v] = m;
|
|
20
|
-
if (k === 'LLM_API_KEY' && !key)
|
|
21
|
-
|
|
22
|
-
if (k === '
|
|
41
|
+
if (k === 'LLM_API_KEY' && !key)
|
|
42
|
+
key = v.trim();
|
|
43
|
+
if (k === 'LLM_BASE_URL' && !base)
|
|
44
|
+
base = v.trim();
|
|
45
|
+
if (k === 'LLM_MODEL' && !model)
|
|
46
|
+
model = v.trim();
|
|
23
47
|
}
|
|
24
48
|
}
|
|
25
49
|
assert.ok(key && base && model, 'LLM_API_KEY/LLM_BASE_URL/LLM_MODEL 必须已配(env 或 .env)');
|
|
26
|
-
return {
|
|
27
|
-
key,
|
|
28
|
-
base,
|
|
29
|
-
model
|
|
30
|
-
};
|
|
50
|
+
return { key, base, model };
|
|
31
51
|
}
|
|
52
|
+
// --- 捕获插件注册表(隔离 stateRoot:canary 不写任何共享状态;sessionAccess=off 免审批) ---
|
|
32
53
|
const stateRoot = mkdtempSync(join(tmpdir(), 'gotry-canary-state-'));
|
|
33
54
|
const registered = [];
|
|
34
55
|
const ctx = {
|
|
35
|
-
tools: {
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
systemPrompt: {
|
|
39
|
-
variable: ()=>()=>''
|
|
40
|
-
},
|
|
41
|
-
on: ()=>()=>{}
|
|
56
|
+
tools: { register: (t) => registered.push(t) },
|
|
57
|
+
systemPrompt: { variable: () => () => '' },
|
|
58
|
+
on: () => () => { },
|
|
42
59
|
};
|
|
43
|
-
apply(ctx, {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
hbcliBin: 'hbcli-not-on-path',
|
|
47
|
-
sessionAccess: 'off'
|
|
48
|
-
});
|
|
49
|
-
const byName = (n)=>{
|
|
50
|
-
const t = registered.find((x)=>x.name === n);
|
|
60
|
+
apply(ctx, { stateRoot, timeoutMs: 30_000, hbcliBin: 'hbcli-not-on-path', sessionAccess: 'off' });
|
|
61
|
+
const byName = (n) => {
|
|
62
|
+
const t = registered.find(x => x.name === n);
|
|
51
63
|
assert.ok(t, `工具 ${n} 应已注册`);
|
|
52
64
|
return t;
|
|
53
65
|
};
|
|
54
66
|
const CASES = [
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
label: '酒店(带档期)',
|
|
67
|
-
tool: 'gotry_flyai_search',
|
|
68
|
-
prompt: '查一下大理 10 月 1 日入住、10 月 3 日退房的酒店'
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
label: '酒店(摸底,无档期)',
|
|
72
|
-
tool: 'gotry_flyai_search',
|
|
73
|
-
prompt: '先看看大理有哪些酒店'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
label: '天气预报',
|
|
77
|
-
tool: 'gotry_weather_check',
|
|
78
|
-
prompt: '大理这几天天气怎么样'
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
label: '酒店实时源(hbcli)',
|
|
82
|
-
tool: 'gotry_hotel_search',
|
|
83
|
-
prompt: '查一下大理的酒店,入住下周五,住三晚'
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
label: '会话面机票(登录态)',
|
|
87
|
-
tool: 'gotry_session_search',
|
|
88
|
-
prompt: '用我的携程账号查 10 月 1 日上海到丽江的机票'
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
label: '目的地候选库',
|
|
92
|
-
tool: 'gotry_anything_search',
|
|
93
|
-
prompt: '大连在你们的目的地库里吗?帮我查一下'
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
label: 'GitHub 搜索',
|
|
97
|
-
tool: 'gotry_github_search',
|
|
98
|
-
prompt: '在 GitHub 上搜一下 agent-reach 这个项目'
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
label: '网页读取',
|
|
102
|
-
tool: 'gotry_web_search',
|
|
103
|
-
prompt: '帮我读一下 https://example.com 这个页面的内容'
|
|
104
|
-
}
|
|
67
|
+
{ label: '机·票(城市对+日期)', tool: 'gotry_flyai_search', prompt: '帮我查一下 10 月 1 日从上海飞丽江的机票' },
|
|
68
|
+
{ label: '火车票', tool: 'gotry_flyai_search', prompt: '10 月 1 日上海到昆明的火车票帮我查一下' },
|
|
69
|
+
{ label: '酒店(带档期)', tool: 'gotry_flyai_search', prompt: '查一下大理 10 月 1 日入住、10 月 3 日退房的酒店' },
|
|
70
|
+
{ label: '酒店(摸底,无档期)', tool: 'gotry_flyai_search', prompt: '先看看大理有哪些酒店' },
|
|
71
|
+
{ label: '天气预报', tool: 'gotry_weather_check', prompt: '大理这几天天气怎么样' },
|
|
72
|
+
{ label: '酒店实时源(hbcli)', tool: 'gotry_hotel_search', prompt: '查一下大理的酒店,入住下周五,住三晚' },
|
|
73
|
+
{ label: '会话面机票(登录态)', tool: 'gotry_session_search', prompt: '用我的携程账号查 10 月 1 日上海到丽江的机票' },
|
|
74
|
+
{ label: '目的地候选库', tool: 'gotry_anything_search', prompt: '大连在你们的目的地库里吗?帮我查一下' },
|
|
75
|
+
{ label: 'GitHub 搜索', tool: 'gotry_github_search', prompt: '在 GitHub 上搜一下 agent-reach 这个项目' },
|
|
76
|
+
{ label: '网页读取', tool: 'gotry_web_search', prompt: '帮我读一下 https://example.com 这个页面的内容' },
|
|
105
77
|
];
|
|
106
78
|
async function callLlmMessages(cfg, toolName, messages) {
|
|
107
79
|
const tool = byName(toolName);
|
|
@@ -109,32 +81,15 @@ async function callLlmMessages(cfg, toolName, messages) {
|
|
|
109
81
|
const body = {
|
|
110
82
|
model: cfg.model,
|
|
111
83
|
messages,
|
|
112
|
-
tools: [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
function: {
|
|
116
|
-
name: tool.name,
|
|
117
|
-
description: tool.description,
|
|
118
|
-
parameters: tool.parameters
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
|
-
tool_choice: {
|
|
123
|
-
type: 'function',
|
|
124
|
-
function: {
|
|
125
|
-
name: tool.name
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
temperature: 0
|
|
84
|
+
tools: [{ type: 'function', function: { name: tool.name, description: tool.description, parameters: tool.parameters } }],
|
|
85
|
+
tool_choice: { type: 'function', function: { name: tool.name } },
|
|
86
|
+
temperature: 0,
|
|
129
87
|
};
|
|
130
88
|
const res = await fetch(url, {
|
|
131
89
|
method: 'POST',
|
|
132
|
-
headers: {
|
|
133
|
-
'content-type': 'application/json',
|
|
134
|
-
authorization: `Bearer ${cfg.key}`
|
|
135
|
-
},
|
|
90
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${cfg.key}` },
|
|
136
91
|
body: JSON.stringify(body),
|
|
137
|
-
signal: AbortSignal.timeout(60_000)
|
|
92
|
+
signal: AbortSignal.timeout(60_000),
|
|
138
93
|
});
|
|
139
94
|
const bodyText = await res.text();
|
|
140
95
|
assert.equal(res.status, 200, `LLM HTTP ${res.status}: ${bodyText.slice(0, 200)}`);
|
|
@@ -144,111 +99,74 @@ async function callLlmMessages(cfg, toolName, messages) {
|
|
|
144
99
|
if (!tc?.function?.arguments) {
|
|
145
100
|
const text = String(msg.content ?? '');
|
|
146
101
|
assert.ok(/年份|日期|确认|2024|2025|2026/.test(text), `模型既未调工具也未按拒绝指引向用户确认: ${text.slice(0, 120)}`);
|
|
147
|
-
return {
|
|
148
|
-
args: undefined,
|
|
149
|
-
callId: 'text-reply',
|
|
150
|
-
text,
|
|
151
|
-
textReply: true
|
|
152
|
-
};
|
|
102
|
+
return { args: undefined, callId: 'text-reply', text, textReply: true };
|
|
153
103
|
}
|
|
154
|
-
return {
|
|
155
|
-
args: JSON.parse(tc.function.arguments),
|
|
156
|
-
callId: tc.id ?? 'canary-call',
|
|
157
|
-
textReply: false
|
|
158
|
-
};
|
|
104
|
+
return { args: JSON.parse(tc.function.arguments), callId: tc.id ?? 'canary-call', textReply: false };
|
|
159
105
|
}
|
|
160
106
|
async function main() {
|
|
161
107
|
const cfg = loadEnvTriple();
|
|
162
108
|
console.log(`typed-contract canary:model=${cfg.model} 已配(值不回显);用例 ${CASES.length} 条,达标线 ≥90% 一次成型\n`);
|
|
163
109
|
let pass = 0;
|
|
164
110
|
const failures = [];
|
|
165
|
-
for (const c of CASES){
|
|
111
|
+
for (const c of CASES) {
|
|
166
112
|
const tool = byName(c.tool);
|
|
167
113
|
process.stdout.write(`- ${c.label} → ${c.tool} … `);
|
|
168
114
|
try {
|
|
169
115
|
const { args } = await callLlmMessages(cfg, c.tool, [
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
content: '你是 GoTry 旅行助手。需要查数据时必须调用提供的工具,参数从用户话里取,不要编造。'
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
role: 'user',
|
|
176
|
-
content: c.prompt
|
|
177
|
-
}
|
|
116
|
+
{ role: 'system', content: '你是 GoTry 旅行助手。需要查数据时必须调用提供的工具,参数从用户话里取,不要编造。' },
|
|
117
|
+
{ role: 'user', content: c.prompt },
|
|
178
118
|
]);
|
|
179
119
|
const violations = validateJsonSchemaValue(tool.parameters, args);
|
|
180
120
|
if (violations.length === 0) {
|
|
181
121
|
pass++;
|
|
182
122
|
console.log(`PASS 一次成型 ${JSON.stringify(args).slice(0, 120)}`);
|
|
183
|
-
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
184
125
|
failures.push(`${c.label}: schema 违例 ${violations.join(';')}`);
|
|
185
126
|
console.log(`FAIL schema 违例: ${violations.join(';').slice(0, 160)}`);
|
|
186
127
|
}
|
|
187
|
-
}
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
188
130
|
failures.push(`${c.label}: ${e.message.slice(0, 160)}`);
|
|
189
131
|
console.log(`FAIL ${e.message.slice(0, 140)}`);
|
|
190
132
|
}
|
|
191
133
|
}
|
|
134
|
+
// ---------------------------------------------------------------
|
|
135
|
+
// 验收②(issue #140):畸形/越界调用被宿主权拒绝后,模型按 violations 自纠。
|
|
136
|
+
// 确定性播种:预置一次带过去日期的被拒调用与结构化拒绝结果(tool 角色),
|
|
137
|
+
// 验证模型从拒绝中自纠出「schema 合规 + 日期非过去」的参数。只校验不执行(零副作用)。
|
|
138
|
+
// ---------------------------------------------------------------
|
|
192
139
|
{
|
|
193
140
|
const tool = byName('gotry_flyai_search');
|
|
194
141
|
const today = new Date().toISOString().slice(0, 10);
|
|
195
142
|
try {
|
|
196
143
|
const messages = [
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
role: 'user',
|
|
203
|
-
content: '帮我查一下 10 月 1 日从上海飞丽江的机票'
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
role: 'assistant',
|
|
207
|
-
content: null,
|
|
208
|
-
tool_calls: [
|
|
209
|
-
{
|
|
210
|
-
id: 'seed-reject',
|
|
211
|
-
type: 'function',
|
|
212
|
-
function: {
|
|
213
|
-
name: 'gotry_flyai_search',
|
|
214
|
-
arguments: JSON.stringify({
|
|
215
|
-
kind: 'flight',
|
|
216
|
-
from: '上海',
|
|
217
|
-
to: '丽江',
|
|
218
|
-
date: '2024-10-01'
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
]
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
role: 'tool',
|
|
226
|
-
tool_call_id: 'seed-reject',
|
|
227
|
-
content: JSON.stringify({
|
|
228
|
-
ok: false,
|
|
229
|
-
verdict: 'error',
|
|
230
|
-
summary: `未发起查询:日期 2024-10-01 已是过去(今天 ${today}),过去不存在在售机票。多为用户时间表达未带年份所致——向用户确认年份(或按未来最近的同月日修正)后再查。`
|
|
231
|
-
})
|
|
232
|
-
}
|
|
144
|
+
{ role: 'system', content: '你是 GoTry 旅行助手。需要查数据时必须调用提供的工具,参数从用户话里取,不要编造。' },
|
|
145
|
+
{ role: 'user', content: '帮我查一下 10 月 1 日从上海飞丽江的机票' },
|
|
146
|
+
{ role: 'assistant', content: null, tool_calls: [{ id: 'seed-reject', type: 'function', function: { name: 'gotry_flyai_search', arguments: JSON.stringify({ kind: 'flight', from: '上海', to: '丽江', date: '2024-10-01' }) } }] },
|
|
147
|
+
{ role: 'tool', tool_call_id: 'seed-reject', content: JSON.stringify({ ok: false, verdict: 'error', summary: `未发起查询:日期 2024-10-01 已是过去(今天 ${today}),过去不存在在售机票。多为用户时间表达未带年份所致——向用户确认年份(或按未来最近的同月日修正)后再查。` }) },
|
|
233
148
|
];
|
|
234
149
|
const reply = await callLlmMessages(cfg, 'gotry_flyai_search', messages);
|
|
235
150
|
const violations = reply.args ? validateJsonSchemaValue(tool.parameters, reply.args) : [];
|
|
236
151
|
if (reply.textReply) {
|
|
237
152
|
assert.match(reply.text ?? '', /年份|日期|确认/, '文字形态应向用户确认年份/日期(契约允许的自纠)');
|
|
238
153
|
console.log('自纠回路(验收②):模型按拒绝指引转为向用户确认年份(文字形态,契约允许)');
|
|
239
|
-
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
240
156
|
assert.equal(violations.length, 0, `自纠出参 schema 违例: ${violations.join(';')}`);
|
|
241
157
|
assert.ok(reply.args && reply.args.date && reply.args.date >= today, `自纠后日期应为今天或未来,实际 ${JSON.stringify(reply.args).slice(0, 80)}`);
|
|
242
158
|
console.log(`自纠回路(验收②):模型按拒绝自纠出参 → PASS(${JSON.stringify(reply.args).slice(0, 100)})`);
|
|
243
159
|
}
|
|
244
|
-
}
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
245
162
|
failures.push(`验收② 自纠回路异常: ${e.message.slice(0, 140)}`);
|
|
246
163
|
console.log(`FAIL 自纠回路异常(计入失败,不中断报告): ${e.message.slice(0, 140)}`);
|
|
247
164
|
}
|
|
248
165
|
}
|
|
249
|
-
const rate = Math.round(pass / CASES.length * 100);
|
|
166
|
+
const rate = Math.round((pass / CASES.length) * 100);
|
|
250
167
|
console.log(`\n一次成型率:${pass}/${CASES.length} = ${rate}%(达标线 90%)`);
|
|
251
|
-
if (failures.length)
|
|
168
|
+
if (failures.length)
|
|
169
|
+
console.log('失败明细:\n' + failures.map(f => ` - ${f}`).join('\n'));
|
|
252
170
|
if (rate < 90) {
|
|
253
171
|
console.error('TYPED CONTRACT CANARY FAIL(不达标不合入)');
|
|
254
172
|
process.exit(1);
|
|
@@ -256,6 +174,3 @@ async function main() {
|
|
|
256
174
|
console.log('TYPED CONTRACT CANARY OK');
|
|
257
175
|
}
|
|
258
176
|
await main();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
//# sourceURL=ts/scripts/typed-contract-canary.ts
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Quick sanity check for tz-resolver. Run with: TZ=UTC npx tsx scripts/tz-resolver-sanity.ts
|
|
2
|
+
import { resolveOffsetForLocalDate } from '../src/tz-resolver.js';
|
|
3
|
+
const cases = [
|
|
4
|
+
// Asia/Shanghai has no DST throughout the year
|
|
5
|
+
['Asia/Shanghai', '2026-07-18', '14:45', 480],
|
|
6
|
+
['Asia/Shanghai', '2026-01-01', '09:00', 480],
|
|
7
|
+
['Asia/Tokyo', '2026-07-04', '09:00', 540],
|
|
8
|
+
['Asia/Hong_Kong', '2026-07-18', '14:45', 480],
|
|
9
|
+
['Asia/Bangkok', '2026-08-09', '23:55', 420],
|
|
10
|
+
['Asia/Singapore', '2026-08-09', '23:55', 480],
|
|
11
|
+
['Asia/Dubai', '2026-07-18', '00:45', 240],
|
|
12
|
+
// America/New_York DST: EST = -5, EDT = -4. 2026 spring-forward = 2026-03-08, fall-back = 2026-11-01
|
|
13
|
+
['America/New_York', '2026-03-07', '14:00', -300], // day before spring forward -> EST
|
|
14
|
+
['America/New_York', '2026-03-09', '14:00', -240], // day after spring forward -> EDT
|
|
15
|
+
['America/New_York', '2026-06-15', '09:00', -240], // summer -> EDT
|
|
16
|
+
['America/New_York', '2026-12-15', '09:00', -300], // winter -> EST
|
|
17
|
+
['America/Los_Angeles', '2026-06-15', '14:00', -420],
|
|
18
|
+
['UTC', '2026-07-18', '12:00', 0],
|
|
19
|
+
['Europe/London', '2026-06-15', '14:00', 60],
|
|
20
|
+
['Europe/London', '2026-01-15', '14:00', 0],
|
|
21
|
+
];
|
|
22
|
+
let ok = 0, fail = 0;
|
|
23
|
+
for (const [zone, ymd, hhmm, expected] of cases) {
|
|
24
|
+
const r = resolveOffsetForLocalDate(zone, ymd, hhmm);
|
|
25
|
+
const pass = r.ok && r.offsetMin === expected;
|
|
26
|
+
if (pass)
|
|
27
|
+
ok++;
|
|
28
|
+
else {
|
|
29
|
+
fail++;
|
|
30
|
+
console.log('FAIL', zone, ymd, hhmm, 'expected', expected, 'got', r.ok ? r.offsetMin : r);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const negatives = [
|
|
34
|
+
['Foo/Bar', '2026-07-18', '14:45', 'unknown_zone'],
|
|
35
|
+
['America/New_York', '2026-03-08', '02:30', 'gap_nonexistent'], // spring forward day
|
|
36
|
+
['America/New_York', '2026-11-01', '01:30', 'overlap_ambiguous'], // fall back day
|
|
37
|
+
];
|
|
38
|
+
for (const [zone, ymd, hhmm, expected] of negatives) {
|
|
39
|
+
const r = resolveOffsetForLocalDate(zone, ymd, hhmm);
|
|
40
|
+
const pass = !r.ok && r.kind === expected;
|
|
41
|
+
if (pass)
|
|
42
|
+
ok++;
|
|
43
|
+
else {
|
|
44
|
+
fail++;
|
|
45
|
+
console.log('FAIL', zone, ymd, hhmm, 'expected', expected, 'got', r.ok ? `ok(${r.offsetMin})` : r.kind);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
console.log(`\n${ok}/${ok + fail} sanity ok`);
|
|
49
|
+
if (fail > 0)
|
|
50
|
+
process.exit(1);
|