@danceiny/gotry 0.0.1-rc.15 → 0.0.1-rc.17

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.
Files changed (238) hide show
  1. package/README.md +51 -13
  2. package/README.zh-CN.md +26 -12
  3. package/bin/gotry-booking-copilot.js +53 -0
  4. package/bin/gotry-bootstrap.js +357 -8
  5. package/bin/gotry-inner.js +442 -60
  6. package/bin/gotry-runtime-resolution.d.ts +27 -0
  7. package/bin/gotry-runtime-resolution.js +50 -0
  8. package/bin/gotry.js +1 -1
  9. package/cordis.gotry-patch.yml +15 -1
  10. package/data/airline-airports.json +39 -0
  11. package/dist/capabilities/agent-reach-deep.js +1 -1
  12. package/dist/capabilities/agent-reach.js +1 -1
  13. package/dist/capabilities/anything.js +1 -1
  14. package/dist/capabilities/artifacts.js +1 -1
  15. package/dist/capabilities/effect.js +293 -0
  16. package/dist/capabilities/fact-log.js +41 -0
  17. package/dist/capabilities/flyai.js +20 -6
  18. package/dist/capabilities/hbcli.js +43 -1
  19. package/dist/capabilities/incident-log.js +1 -1
  20. package/dist/capabilities/model-override.js +18 -0
  21. package/dist/capabilities/opensky.js +1 -1
  22. package/dist/capabilities/resilience.js +90 -0
  23. package/dist/capabilities/session/action-cache.js +1 -1
  24. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  25. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  26. package/dist/capabilities/session/benchmark.js +2 -1
  27. package/dist/capabilities/session/extension-bridge.js +358 -0
  28. package/dist/capabilities/session/extension-channel.js +93 -0
  29. package/dist/capabilities/session/extension-distribution.js +234 -0
  30. package/dist/capabilities/session/extract.js +1 -1
  31. package/dist/capabilities/session/golden-score.js +92 -0
  32. package/dist/capabilities/session/health-watch.js +216 -0
  33. package/dist/capabilities/session/read-guard.js +1 -1
  34. package/dist/capabilities/session/static-flight-golden.js +137 -0
  35. package/dist/capabilities/session/transport.js +1 -1
  36. package/dist/capabilities/session/wizard.js +147 -0
  37. package/dist/capabilities/session-consent.js +1 -1
  38. package/dist/capabilities/session-login.js +83 -1
  39. package/dist/capabilities/session-search.js +111 -2
  40. package/dist/capabilities/weather.js +168 -46
  41. package/dist/data/session-golden-20.json +25 -0
  42. package/dist/data/sf-golden-manifest.json +102 -0
  43. package/dist/data/sf-static-routes.json +91 -0
  44. package/dist/scripts/action-cache-tests.js +1 -1
  45. package/dist/scripts/agent-planning-budget-e2e.js +227 -0
  46. package/dist/scripts/agent-planning-budget-tests.js +173 -0
  47. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  48. package/dist/scripts/agent-reach-tests.js +1 -1
  49. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  50. package/dist/scripts/anything-tests.js +1 -1
  51. package/dist/scripts/async-collect.js +1 -1
  52. package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
  53. package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
  54. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
  55. package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
  56. package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
  57. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
  58. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
  59. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
  60. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
  61. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
  62. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
  63. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
  64. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
  65. package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
  66. package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
  67. package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
  68. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
  69. package/dist/scripts/booking-saga-tests.js +1 -1
  70. package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
  71. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
  72. package/dist/scripts/bootstrap-tests.js +59 -3
  73. package/dist/scripts/build-changelog.js +279 -0
  74. package/dist/scripts/changelog-tests.js +165 -0
  75. package/dist/scripts/companion-tests.js +1 -1
  76. package/dist/scripts/diff-test.js +1 -1
  77. package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
  78. package/dist/scripts/dsh-runtime-closure.js +150 -0
  79. package/dist/scripts/effect-tests.js +474 -0
  80. package/dist/scripts/engine-run.js +1 -1
  81. package/dist/scripts/engine-tests.js +1 -1
  82. package/dist/scripts/evaluation-cadence-tests.js +347 -0
  83. package/dist/scripts/evaluation-contract-tests.js +574 -0
  84. package/dist/scripts/extension-distribution-cli.js +35 -0
  85. package/dist/scripts/extension-distribution-tests.js +341 -0
  86. package/dist/scripts/extension-tests.js +573 -0
  87. package/dist/scripts/fact-gate-tests.js +318 -0
  88. package/dist/scripts/flyai-tests.js +1 -1
  89. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  90. package/dist/scripts/hbcli-tests.js +1 -1
  91. package/dist/scripts/health-watch-cli.js +51 -0
  92. package/dist/scripts/i18n-tests.js +1 -1
  93. package/dist/scripts/incident-tests.js +1 -1
  94. package/dist/scripts/journey-tests.js +1 -1
  95. package/dist/scripts/ledger-tests.js +1 -1
  96. package/dist/scripts/ledger-workflow-crash.js +1 -1
  97. package/dist/scripts/memory-capture-tests.js +1 -1
  98. package/dist/scripts/memory-decay-tests.js +1 -1
  99. package/dist/scripts/memory-metrics.js +1 -1
  100. package/dist/scripts/memory-value-report.js +1 -1
  101. package/dist/scripts/model-override-e2e.js +176 -0
  102. package/dist/scripts/nightly-evidence-tests.js +67 -4
  103. package/dist/scripts/nightly-evidence.js +59 -3
  104. package/dist/scripts/nudge-digest.js +1 -1
  105. package/dist/scripts/onboarding-tests.js +186 -0
  106. package/dist/scripts/opensky-check.js +1 -1
  107. package/dist/scripts/opensky-tests.js +1 -1
  108. package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
  109. package/dist/scripts/price-drift-tests.js +441 -0
  110. package/dist/scripts/price-drift-watch.js +491 -0
  111. package/dist/scripts/probe-poi-tests.js +1 -1
  112. package/dist/scripts/product-metrics.js +1 -1
  113. package/dist/scripts/publish-preverify.js +40 -4
  114. package/dist/scripts/realtime-pricing-tests.js +1 -1
  115. package/dist/scripts/replay-async.js +1 -1
  116. package/dist/scripts/replay-real.js +1 -1
  117. package/dist/scripts/replay.js +1 -1
  118. package/dist/scripts/session-attach-diagnose.js +1 -1
  119. package/dist/scripts/session-attach-poc.js +1 -1
  120. package/dist/scripts/session-benchmark.js +1 -1
  121. package/dist/scripts/session-extract-tests.js +1 -1
  122. package/dist/scripts/session-login.js +1 -1
  123. package/dist/scripts/session-tests.js +70 -20
  124. package/dist/scripts/sf-live-benchmark.js +338 -0
  125. package/dist/scripts/sf-live-cli-tests.js +21 -0
  126. package/dist/scripts/sf-soft-score-tests.js +108 -0
  127. package/dist/scripts/sf-summary.js +93 -0
  128. package/dist/scripts/skeleton-check.js +1 -1
  129. package/dist/scripts/skeleton-integration-test.js +1 -1
  130. package/dist/scripts/skills-contract-tests.js +1 -1
  131. package/dist/scripts/smoke-session-gate-tests.js +29 -0
  132. package/dist/scripts/smoke.js +154 -33
  133. package/dist/scripts/state-cli-tests.js +1 -1
  134. package/dist/scripts/state-cli.js +1 -1
  135. package/dist/scripts/static-golden-tests.js +299 -0
  136. package/dist/scripts/time-eval-tests.js +1 -1
  137. package/dist/scripts/travel-timeline-tests.js +1 -1
  138. package/dist/scripts/unified-tests.js +1 -1
  139. package/dist/scripts/weather-tests.js +694 -44
  140. package/dist/scripts/z3-race-tests.js +1 -1
  141. package/dist/src/artifact-gate.js +310 -0
  142. package/dist/src/benchmark-agent-conformance.js +370 -0
  143. package/dist/src/benchmark-environment-bridge.js +384 -0
  144. package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
  145. package/dist/src/benchmark-tool-isolation.js +124 -0
  146. package/dist/src/bookable-facts.js +349 -0
  147. package/dist/src/booking-saga.js +1 -1
  148. package/dist/src/booking-surface/availability-policy-v2.js +830 -0
  149. package/dist/src/booking-surface/canonical-schema.js +113 -0
  150. package/dist/src/booking-surface/contracts-v2.js +89 -0
  151. package/dist/src/booking-surface/contracts.js +46 -0
  152. package/dist/src/booking-surface/dsh-planner.js +453 -0
  153. package/dist/src/booking-surface/dsh-plugin.js +93 -0
  154. package/dist/src/booking-surface/error-codes.js +94 -0
  155. package/dist/src/booking-surface/index.js +15 -0
  156. package/dist/src/booking-surface/profile.js +68 -0
  157. package/dist/src/booking-surface/runtime-v2.js +1771 -0
  158. package/dist/src/booking-surface/runtime.js +351 -0
  159. package/dist/src/booking-surface/server-v2.js +334 -0
  160. package/dist/src/booking-surface/server.js +302 -0
  161. package/dist/src/booking-surface/startup.js +159 -0
  162. package/dist/src/booking-surface/validation-v2.js +319 -0
  163. package/dist/src/booking-surface/validation.js +809 -0
  164. package/dist/src/bridge.js +1 -1
  165. package/dist/src/companions.js +1 -1
  166. package/dist/src/contracts.js +1 -1
  167. package/dist/src/dsh-llm.js +1 -1
  168. package/dist/src/engine.js +1 -1
  169. package/dist/src/evaluation-cadence.js +234 -0
  170. package/dist/src/evaluation-contracts.js +906 -0
  171. package/dist/src/i18n.js +1 -1
  172. package/dist/src/index.js +234 -60
  173. package/dist/src/journey.js +1 -1
  174. package/dist/src/loop.js +1 -1
  175. package/dist/src/memory-capture.js +1 -1
  176. package/dist/src/memory-decay.js +1 -1
  177. package/dist/src/memory-utility.js +1 -1
  178. package/dist/src/mock-llm.js +1 -1
  179. package/dist/src/model.js +1 -1
  180. package/dist/src/realtime-pricing.js +14 -7
  181. package/dist/src/slot-spec.js +1 -1
  182. package/dist/src/state-ledger.js +2 -1
  183. package/dist/src/time-anchor.js +1 -1
  184. package/dist/src/tool-budget.js +136 -0
  185. package/dist/src/tool-packet.js +1 -1
  186. package/dist/src/travel-slots.js +1 -1
  187. package/dist/src/travel-timeline.js +1 -1
  188. package/dist/src/unified.js +1 -1
  189. package/dist/src/wish-pool.js +1 -1
  190. package/dist/src/z3-shared.js +1 -1
  191. package/extension/README.md +50 -0
  192. package/extension/background.js +160 -0
  193. package/extension/content-bridge.js +41 -0
  194. package/extension/content-main.js +62 -0
  195. package/extension/manifest.json +32 -0
  196. package/package.json +291 -12
  197. package/schemas/booking.surface.v1.schema.json +927 -0
  198. package/schemas/booking.surface.v2.schema.json +61 -0
  199. package/ts/capabilities/fact-log.ts +54 -0
  200. package/ts/capabilities/flyai.ts +16 -3
  201. package/ts/capabilities/hbcli.ts +46 -0
  202. package/ts/capabilities/session/benchmark.ts +4 -1
  203. package/ts/capabilities/session/extension-bridge.ts +377 -0
  204. package/ts/capabilities/session/extension-channel.ts +95 -0
  205. package/ts/capabilities/session/extension-distribution.ts +264 -0
  206. package/ts/capabilities/session/golden-score.ts +139 -0
  207. package/ts/capabilities/session/health-watch.ts +257 -0
  208. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  209. package/ts/capabilities/session/wizard.ts +140 -0
  210. package/ts/capabilities/session-login.ts +63 -2
  211. package/ts/capabilities/session-search.ts +105 -3
  212. package/ts/capabilities/weather.ts +141 -52
  213. package/ts/package.json +3 -3
  214. package/ts/src/artifact-gate.ts +365 -0
  215. package/ts/src/benchmark-agent-conformance.ts +448 -0
  216. package/ts/src/benchmark-environment-bridge.ts +348 -0
  217. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  218. package/ts/src/benchmark-tool-isolation.ts +166 -0
  219. package/ts/src/bookable-facts.ts +557 -0
  220. package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
  221. package/ts/src/booking-surface/canonical-schema.js +113 -0
  222. package/ts/src/booking-surface/contracts-v2.ts +118 -0
  223. package/ts/src/booking-surface/contracts.ts +380 -0
  224. package/ts/src/booking-surface/dsh-planner.ts +452 -0
  225. package/ts/src/booking-surface/dsh-plugin.js +93 -0
  226. package/ts/src/booking-surface/error-codes.ts +101 -0
  227. package/ts/src/booking-surface/index.ts +12 -0
  228. package/ts/src/booking-surface/profile.ts +42 -0
  229. package/ts/src/booking-surface/runtime-v2.ts +1466 -0
  230. package/ts/src/booking-surface/runtime.ts +483 -0
  231. package/ts/src/booking-surface/server-v2.ts +247 -0
  232. package/ts/src/booking-surface/server.ts +324 -0
  233. package/ts/src/booking-surface/startup.ts +196 -0
  234. package/ts/src/booking-surface/validation-v2.ts +205 -0
  235. package/ts/src/booking-surface/validation.ts +453 -0
  236. package/ts/src/index.ts +161 -14
  237. package/ts/src/state-ledger.ts +1 -0
  238. package/ts/src/tool-budget.ts +165 -0
@@ -34,4 +34,4 @@ if (isMain && process.argv[2] && process.argv[3]) {
34
34
  }
35
35
 
36
36
 
37
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/skeleton-check.ts
37
+ //# sourceURL=ts/scripts/skeleton-check.ts
@@ -19,4 +19,4 @@ assert.ok((r.skeleton_notes ?? []).some((n)=>n.includes('HKG') && n.includes('
19
19
  console.log(`骨架集成 OK:EK329 在枢纽否定下存活,标注=[${r.skeleton_notes?.join(' | ')}]`);
20
20
 
21
21
 
22
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/skeleton-integration-test.ts
22
+ //# sourceURL=ts/scripts/skeleton-integration-test.ts
@@ -82,4 +82,4 @@ main().catch((e)=>{
82
82
  });
83
83
 
84
84
 
85
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/skills-contract-tests.ts
85
+ //# sourceURL=ts/scripts/skills-contract-tests.ts
@@ -0,0 +1,29 @@
1
+ import { gatedSessionFlightSearch, offlineSessionFlightResult, sessionLiveEnabled } from '../capabilities/session-search.js';
2
+ let calls = 0;
3
+ const query = {
4
+ from: '上海',
5
+ to: '丽江',
6
+ date: '2026-10-01'
7
+ };
8
+ if (sessionLiveEnabled({
9
+ GOTRY_SESSION_LIVE: '0'
10
+ })) throw new Error('GOTRY_SESSION_LIVE=0 must be offline');
11
+ if (sessionLiveEnabled({
12
+ GOTRY_SESSION_LIVE: undefined
13
+ })) throw new Error('unset GOTRY_SESSION_LIVE must be offline');
14
+ const offline = await gatedSessionFlightSearch(query, async ()=>{
15
+ calls += 1;
16
+ throw new Error('live transport must not be called');
17
+ }, {
18
+ GOTRY_SESSION_LIVE: '0'
19
+ });
20
+ if (calls !== 0) throw new Error(`offline gate invoked live transport ${calls} time(s)`);
21
+ if (offline.verdict !== 'error' || offline.ok || !offline.error?.includes('GOTRY_SESSION_LIVE=1') || !offline.evidence.includes('live transport not invoked')) {
22
+ throw new Error(`offline result is not explicit: ${JSON.stringify(offline)}`);
23
+ }
24
+ const direct = offlineSessionFlightResult();
25
+ if (direct.verdict !== 'error' || direct.ok || !direct.evidence.includes('offline')) throw new Error('offline helper contract drifted');
26
+ console.log('SMOKE SESSION GATE: GOTRY_SESSION_LIVE!=1 makes zero live calls and returns explicit offline error');
27
+
28
+
29
+ //# sourceURL=ts/scripts/smoke-session-gate-tests.ts
@@ -1,6 +1,8 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { apply } from '../src/index.js';
4
+ import { installModelOverride } from '../capabilities/model-override.js';
5
+ import { offlineSessionFlightResult, sessionLiveEnabled } from '../capabilities/session-search.js';
4
6
  async function main() {
5
7
  const { mkdtempSync, rmSync } = await import('node:fs');
6
8
  const { tmpdir } = await import('node:os');
@@ -36,18 +38,17 @@ async function main() {
36
38
  return t;
37
39
  };
38
40
  const motivation = byName('gotry_motivation_save');
39
- try {
40
- await motivation.execute({
41
- profile: {
42
- weights: {
43
- escape_rest: 0.7
44
- }
41
+ const rejectedMotivation = await motivation.execute({
42
+ profile: {
43
+ weights: {
44
+ escape_rest: 0.7
45
45
  }
46
- }, null);
47
- throw new Error('FAIL: profile without evidence should have been rejected');
48
- } catch (e) {
49
- console.log(`motivation without evidence rejected: ${e.message.slice(0, 60)}...`);
46
+ }
47
+ }, null);
48
+ if (rejectedMotivation.ok !== false || !rejectedMotivation.summary?.includes('without evidence')) {
49
+ throw new Error(`FAIL: profile without evidence should have been rejected, actual ${JSON.stringify(rejectedMotivation).slice(0, 120)}`);
50
50
  }
51
+ console.log(`motivation without evidence rejected: ${rejectedMotivation.summary.slice(0, 80)}...`);
51
52
  const saved = await motivation.execute({
52
53
  profile: {
53
54
  weights: {
@@ -296,10 +297,10 @@ async function main() {
296
297
  date: '2026-10-01'
297
298
  }
298
299
  }, null);
299
- const faBlocked = fa.verdict === 'error' && /sentinel|block/i.test(fa.error ?? '');
300
+ const faBlocked = fa.verdict === 'error' && /sentinel|block|trial limit/i.test(fa.error ?? '');
300
301
  const faErrTerminal = fa.ok === false && fa.verdict === 'error' && /^flyai-error$/.test(String(fa.via ?? '')) && /\[实时API:flyai@error@/.test(String(fa.evidence ?? ''));
301
302
  if (faBlocked) {
302
- console.log(' WARN - flyai Sentinel 限流中,降级合同通过(hit 断言跳过)');
303
+ console.log(' WARN - flyai 上游限流中(Sentinel/trial-limit 429),降级合同通过(hit 断言跳过)');
303
304
  } else if (faErrTerminal) {
304
305
  console.log(' WARN - flyai 端点不可达(超时/降级),证据链合同通过(hit 断言跳过)');
305
306
  } else if (fa.ok !== true || fa.verdict !== 'hit' || (fa.options?.length ?? 0) < 1 || !/\[实时API:flyai@/.test(fa.evidence ?? '')) {
@@ -317,30 +318,36 @@ async function main() {
317
318
  if (!(faPast.ok === false && /已是过去/.test(String(faPast.summary ?? '')))) {
318
319
  throw new Error(`FAIL: flyai 过去日期应代码层预校验拒绝,实际:${JSON.stringify(faPast).slice(0, 200)}`);
319
320
  }
320
- const previousChromeUserDataDir = process.env.CHROME_USER_DATA_DIR;
321
- process.env.CHROME_USER_DATA_DIR = prof;
322
321
  let ss;
323
- try {
324
- ss = await byName('gotry_session_search').execute({
325
- query: {
326
- from: '上海',
327
- to: '丽江',
328
- date: '2026-10-01'
329
- }
330
- }, null);
331
- } finally{
332
- if (previousChromeUserDataDir === undefined) delete process.env.CHROME_USER_DATA_DIR;
333
- else process.env.CHROME_USER_DATA_DIR = previousChromeUserDataDir;
334
- rmSync(prof, {
335
- recursive: true,
336
- force: true
337
- });
322
+ if (!sessionLiveEnabled()) {
323
+ ss = offlineSessionFlightResult();
324
+ console.log(' OFFLINE - GOTRY_SESSION_LIVE!=1: session transport was not invoked');
325
+ } else {
326
+ const previousChromeUserDataDir = process.env.CHROME_USER_DATA_DIR;
327
+ process.env.CHROME_USER_DATA_DIR = prof;
328
+ try {
329
+ ss = await byName('gotry_session_search').execute({
330
+ query: {
331
+ from: '上海',
332
+ to: '丽江',
333
+ date: '2026-10-01'
334
+ }
335
+ }, null);
336
+ } finally{
337
+ if (previousChromeUserDataDir === undefined) delete process.env.CHROME_USER_DATA_DIR;
338
+ else process.env.CHROME_USER_DATA_DIR = previousChromeUserDataDir;
339
+ }
338
340
  }
341
+ rmSync(prof, {
342
+ recursive: true,
343
+ force: true
344
+ });
339
345
  const ssErrTerminal = ss.ok === false && /^session-[a-z0-9-]+-error$/.test(String(ss.via ?? '')) && !!ss.evidence;
340
346
  if (!(ss.verdict === 'needs-login' || ss.verdict === 'needs-attach' || ss.verdict === 'hit' || ss.verdict === 'cooldown' || ss.verdict === 'challenged') && !ssErrTerminal) {
341
347
  throw new Error(`FAIL: session 工具终态应属 {needs-attach,needs-login,hit,cooldown,challenged} 或带证据的 error 终态,实际:${JSON.stringify(ss).slice(0, 200)}`);
342
348
  }
343
- console.log(`session-face tools: flyai ${faBlocked ? 'sentinel-限流降级' : `live hit(${fa.options?.length ?? 0} 条)`}; session 终态=${ss.verdict ?? ss.via}(登录态存在前提合同)`);
349
+ const faOutcome = faBlocked ? 'sentinel-限流降级' : faErrTerminal ? '端点不可达降级' : `live hit(${fa.options?.length ?? 0} 条)`;
350
+ console.log(`session-face tools: flyai ${faOutcome}; session cdp 隔离门禁=needs-attach`);
344
351
  const fh = await byName('gotry_flyai_search').execute({
345
352
  query: {
346
353
  kind: 'hotel',
@@ -349,7 +356,7 @@ async function main() {
349
356
  checkOut: '2026-10-03'
350
357
  }
351
358
  }, null);
352
- const fhBlocked = fh.verdict === 'error' && /sentinel|block/i.test(fh.error ?? '');
359
+ const fhBlocked = fh.verdict === 'error' && /sentinel|block|trial limit/i.test(fh.error ?? '');
353
360
  const fhErrTerminal = fh.ok === false && fh.verdict === 'error' && /^flyai-error$/.test(String(fh.via ?? '')) && /\[实时API:flyai@error@/.test(String(fh.evidence ?? ''));
354
361
  if (fhBlocked || fhErrTerminal) {
355
362
  console.log(' WARN - flyai hotel 限流/端点降级,证据链合同通过(hit 断言跳过)');
@@ -373,7 +380,8 @@ async function main() {
373
380
  if (!(fhPast.ok === false && /不是未来合法区间/.test(String(fhPast.summary ?? '')))) {
374
381
  throw new Error(`FAIL: 酒店过去入住日应代码层预校验拒绝,实际:${JSON.stringify(fhPast).slice(0, 200)}`);
375
382
  }
376
- console.log(` hotel channel: ${fhBlocked ? 'sentinel-限流降级' : `${fh.hotels?.length ?? 0} 家`}; 参数闸/过去日闸生效`);
383
+ const fhOutcome = fhBlocked ? 'sentinel-限流降级' : fhErrTerminal ? '端点不可达降级' : `${fh.hotels?.length ?? 0} 家`;
384
+ console.log(` hotel channel: ${fhOutcome}; 参数闸/过去日闸生效`);
377
385
  }
378
386
  {
379
387
  const gate = preExecutes.at(-1);
@@ -493,6 +501,119 @@ async function main() {
493
501
  });
494
502
  console.log(`artifacts: ledger run + cwd md discovered; read window(${r2.ok ? r2.lines.length : '?'} lines @10) + read card; path/ext guardrails hold`);
495
503
  }
504
+ {
505
+ const { appendFacts, loadFactRegistry } = await import('../capabilities/fact-log.js');
506
+ const { factsFromFlyai } = await import('../src/bookable-facts.js');
507
+ const avMap = JSON.parse(await readFile(join(import.meta.dirname, '..', '..', 'data', 'airline-airports.json'), 'utf-8'));
508
+ const fetchedAt = '2026-08-29T00:00:00.000Z';
509
+ await appendFacts(smokeRoot, [
510
+ ...factsFromFlyai({
511
+ kind: 'flight',
512
+ origin: '香港',
513
+ destination: '普吉',
514
+ date: '2027-07-18'
515
+ }, {
516
+ verdict: 'hit',
517
+ options: [
518
+ {
519
+ no: 'CX771',
520
+ name: '国泰航空',
521
+ depDateTime: '2027-07-18T08:05:00+08:00',
522
+ arrDateTime: '2027-07-18T10:35:00+07:00',
523
+ price: 1234
524
+ }
525
+ ],
526
+ evidence: ''
527
+ }, fetchedAt, avMap.city_alias),
528
+ ...factsFromFlyai({
529
+ kind: 'flight',
530
+ origin: '深圳',
531
+ destination: '普吉',
532
+ date: '2027-07-16'
533
+ }, {
534
+ verdict: 'miss',
535
+ options: [],
536
+ evidence: ''
537
+ }, fetchedAt, avMap.city_alias)
538
+ ]);
539
+ const registry = await loadFactRegistry(smokeRoot);
540
+ const probeFacts = registry.filter((f)=>f.kind !== 'policy' && (f.query_id.endsWith(':2027-07-16') || f.query_id.endsWith(':2027-07-18')));
541
+ if (probeFacts.length !== 2 || !probeFacts.some((f)=>f.bookability === 'unavailable_exact_date') || !probeFacts.some((f)=>f.bookability === 'bookable_exact_date')) {
542
+ throw new Error(`FAIL: 事实侧车应落 1 正 1 负两条,实际:${JSON.stringify(probeFacts.map((f)=>f.bookability))}`);
543
+ }
544
+ const gate = byName('gotry_fact_gate');
545
+ const passMd = [
546
+ '# 行程片段',
547
+ '## D3 7.18 香港 → 普吉',
548
+ '- 国泰航空 CX771 08:05→10:35(已按 exact-date 检索记录)'
549
+ ].join('\n');
550
+ const pass = await gate.execute({
551
+ query: {
552
+ markdown: passMd,
553
+ tripYear: 2027
554
+ }
555
+ }, null);
556
+ if (pass.verdict !== 'pass' || pass.presentation !== 'verified_itinerary_allowed') {
557
+ throw new Error(`FAIL: 可回溯产物应 pass,实际:${JSON.stringify(pass).slice(0, 300)}`);
558
+ }
559
+ const badMd = [
560
+ '# 行程片段',
561
+ '## D1 7.16 深圳 → 普吉',
562
+ '- 香港快运 UO784 10:05→12:40 直飞 ✓'
563
+ ].join('\n');
564
+ const blocked = await gate.execute({
565
+ query: {
566
+ markdown: badMd,
567
+ tripYear: 2027
568
+ }
569
+ }, null);
570
+ if (blocked.verdict !== 'blocked' || !blocked.violations?.some((v)=>v.kind === 'not_in_source')) {
571
+ throw new Error(`FAIL: exact-date miss 被 UO784 填充必须 blocked(not_in_source),实际:${JSON.stringify(blocked).slice(0, 300)}`);
572
+ }
573
+ const view = gate.presentResult?.({
574
+ query: {}
575
+ }, blocked);
576
+ if (!view?.title?.includes('blocked')) throw new Error(`FAIL: blocked 呈现卡标题应含 blocked,实际:${view?.title}`);
577
+ console.log(`fact gate: registry 1+1(hit/miss);verified 措辞 pass;miss 填充 UO784 blocked(not_in_source) + 呈现卡`);
578
+ }
579
+ {
580
+ const none = [];
581
+ const ctxNone = {
582
+ on: (_e, fn)=>{
583
+ none.push(fn);
584
+ }
585
+ };
586
+ if (installModelOverride(ctxNone, {}) !== false || none.length !== 0) {
587
+ throw new Error('FAIL: 未设 GOTRY_LLM_MODEL 时不得挂 agent/request 覆盖');
588
+ }
589
+ if (installModelOverride({}, {
590
+ GOTRY_LLM_MODEL: 'MiniMax-M2'
591
+ }) !== false) {
592
+ throw new Error('FAIL: 无事件总线宿主应返回 false 而非抛错');
593
+ }
594
+ const listeners = [];
595
+ const ctxOn = {
596
+ on: (_e, fn)=>{
597
+ listeners.push(fn);
598
+ }
599
+ };
600
+ if (installModelOverride(ctxOn, {
601
+ GOTRY_LLM_MODEL: 'MiniMax-M2'
602
+ }) !== true || listeners.length !== 1) {
603
+ throw new Error('FAIL: 设定 GOTRY_LLM_MODEL 后应挂恰好一条 agent/request 覆盖');
604
+ }
605
+ const inner = async ()=>({
606
+ provider: 'deepseek-official',
607
+ model: 'glm-5.3-flash',
608
+ reasoningEffort: 'high',
609
+ maxTokens: 4096
610
+ });
611
+ const overridden = await listeners[0](null, inner);
612
+ if (overridden.model !== 'MiniMax-M2' || overridden.provider !== 'deepseek-official' || 'reasoningEffort' in overridden || overridden.maxTokens !== 4096) {
613
+ throw new Error(`FAIL: 覆盖结果不符预期:${JSON.stringify(overridden)}`);
614
+ }
615
+ console.log('model override: 未设不挂/无总线不抛/设定后压过用户层选择(glm-5.3-flash→MiniMax-M2)且清继承 effort');
616
+ }
496
617
  rmSync(smokeRoot, {
497
618
  recursive: true,
498
619
  force: true
@@ -506,4 +627,4 @@ main().catch((e)=>{
506
627
  });
507
628
 
508
629
 
509
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/smoke.ts
630
+ //# sourceURL=ts/scripts/smoke.ts
@@ -133,4 +133,4 @@ console.log(`\nSTATE-CLI TESTS: ${pass} ok, ${fail} fail`);
133
133
  if (fail > 0) process.exit(1);
134
134
 
135
135
 
136
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/state-cli-tests.ts
136
+ //# sourceURL=ts/scripts/state-cli-tests.ts
@@ -245,4 +245,4 @@ switch(cmd){
245
245
  }
246
246
 
247
247
 
248
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/state-cli.ts
248
+ //# sourceURL=ts/scripts/state-cli.ts
@@ -0,0 +1,299 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ const modulePath = '../capabilities/session/static-flight-golden.js';
6
+ const provider = await import(modulePath).catch(()=>({}));
7
+ assert.equal(typeof provider.parseGoldenSource, 'function', '缺少 parseGoldenSource 时 CLI 会把未知 vendor 静默当成 manual');
8
+ const parseGoldenSource = provider.parseGoldenSource;
9
+ assert.equal(parseGoldenSource([
10
+ '--golden=static'
11
+ ]), 'static');
12
+ assert.equal(parseGoldenSource([]), 'manual');
13
+ assert.throws(()=>parseGoldenSource([
14
+ '--golden=ctrip-open'
15
+ ]), /不支持的 golden vendor: ctrip-open/, '未知 vendor 必须 fail-closed,不能静默回退 manual');
16
+ assert.equal(typeof provider.loadStaticFlightSnapshot, 'function', 'static vendor 必须从有 provenance 的版本化快照加载,不能复用 manual 并改标签');
17
+ const snapshot = provider.loadStaticFlightSnapshot();
18
+ assert.equal(snapshot.schema_version, 'sf-static-routes.v1');
19
+ assert.deepEqual(snapshot.source, {
20
+ name: 'OpenFlights routes.dat',
21
+ url: 'https://raw.githubusercontent.com/jpatokal/openflights/4b969f8e91eb800c45f0e0e2355a0fbb93de27e4/data/routes.dat',
22
+ revision: '4b969f8e91eb800c45f0e0e2355a0fbb93de27e4',
23
+ license: 'ODbL-1.0',
24
+ retrieved_at: '2026-08-30',
25
+ limitations: [
26
+ 'route/carrier coverage only',
27
+ 'no schedule',
28
+ 'no price',
29
+ 'no availability'
30
+ ]
31
+ });
32
+ assert.deepEqual(snapshot.routes.map((route)=>route.query_id), [
33
+ 'sf-01',
34
+ 'sf-02',
35
+ 'sf-03',
36
+ 'sf-04',
37
+ 'sf-05',
38
+ 'sf-06',
39
+ 'sf-07',
40
+ 'sf-08'
41
+ ], '静态快照必须逐条覆盖本批 8 query,不能靠全量 manual fallback 蒙混');
42
+ for (const route of snapshot.routes){
43
+ assert.ok(route.legs.length >= 1, `${route.query_id} 必须至少有一个静态 OD leg`);
44
+ for (const leg of route.legs){
45
+ assert.match(leg.from_iata, /^[A-Z]{3}$/);
46
+ assert.match(leg.to_iata, /^[A-Z]{3}$/);
47
+ assert.ok(leg.carrier_codes.length >= 1);
48
+ }
49
+ assert.equal('departure_at' in route, false, 'OpenFlights 静态包不得伪造班期时刻');
50
+ assert.equal('price' in route, false, 'OpenFlights 静态包不得伪造价格');
51
+ assert.equal('availability' in route, false, 'OpenFlights 静态包不得伪造库存');
52
+ }
53
+ assert.equal(typeof provider.resolveStaticGolden, 'function', '缺少 static→comparable record 解译时 --golden=static 仍不可执行');
54
+ const manualRecord = {
55
+ query_id: 'sf-02',
56
+ route_segments: [
57
+ {
58
+ from: '北京',
59
+ to: '大理',
60
+ departure_at: '2026-10-02T14:00:00+08:00',
61
+ arrival_at: '2026-10-02T17:00:00+08:00',
62
+ transport_number: 'CA1441'
63
+ }
64
+ ],
65
+ journey_type: 'direct',
66
+ currency: 'CNY',
67
+ price: 2650,
68
+ source: 'manual-golden',
69
+ fetched_at: '2026-08-30T12:00:00.000Z',
70
+ verdict: 'hit',
71
+ latency_ms: 0,
72
+ read_guard_blocked: 0
73
+ };
74
+ const staticResolution = provider.resolveStaticGolden({
75
+ query: {
76
+ id: 'sf-02',
77
+ from: '北京',
78
+ to: '大理',
79
+ date: '2026-10-02'
80
+ },
81
+ manualRecord,
82
+ snapshot
83
+ });
84
+ assert.equal(staticResolution.requested_source, 'static');
85
+ assert.equal(staticResolution.effective_source, 'static-openflights+manual-band');
86
+ assert.equal(staticResolution.fallback_reason, undefined);
87
+ assert.equal(staticResolution.record.source, 'static-openflights+manual-band');
88
+ assert.equal(staticResolution.record.journey_type, 'transfer');
89
+ assert.equal(staticResolution.record.route_segments.length, 2);
90
+ assert.deepEqual(staticResolution.record.route_segments.map((segment)=>[
91
+ segment.from,
92
+ segment.to,
93
+ segment.transport_number
94
+ ]), [
95
+ [
96
+ '北京',
97
+ 'KMG',
98
+ '8L-STATIC'
99
+ ],
100
+ [
101
+ 'KMG',
102
+ '大理',
103
+ '8L-STATIC'
104
+ ]
105
+ ]);
106
+ assert.equal(staticResolution.record.route_segments[0]?.departure_at, '2026-10-02T14:00:00+08:00');
107
+ assert.equal(staticResolution.record.route_segments[0]?.arrival_at, '2026-10-02T15:00:00+08:00');
108
+ assert.equal(staticResolution.record.route_segments[1]?.departure_at, '2026-10-02T16:00:00+08:00');
109
+ assert.equal(staticResolution.record.route_segments[1]?.arrival_at, '2026-10-02T17:00:00+08:00');
110
+ assert.equal(staticResolution.record.price, 2650);
111
+ assert.deepEqual(staticResolution.estimated_fields, [
112
+ 'route_segments[].departure_at',
113
+ 'route_segments[].arrival_at',
114
+ 'route_segments[].transport_number',
115
+ 'price'
116
+ ]);
117
+ assert.deepEqual(staticResolution.provenance, {
118
+ route_source: snapshot.source.url,
119
+ route_revision: '4b969f8e91eb800c45f0e0e2355a0fbb93de27e4',
120
+ route_license: 'ODbL-1.0',
121
+ band_source: 'sf-golden-manifest.json'
122
+ });
123
+ const expectedStaticCoverage = [
124
+ {
125
+ id: 'sf-01',
126
+ from: '上海',
127
+ to: '丽江',
128
+ date: '2026-10-01',
129
+ transport: [
130
+ 'MU-STATIC'
131
+ ]
132
+ },
133
+ {
134
+ id: 'sf-02',
135
+ from: '北京',
136
+ to: '大理',
137
+ date: '2026-10-02',
138
+ transport: [
139
+ '8L-STATIC',
140
+ '8L-STATIC'
141
+ ]
142
+ },
143
+ {
144
+ id: 'sf-03',
145
+ from: '上海',
146
+ to: '三亚',
147
+ date: '2026-11-11',
148
+ transport: [
149
+ '9C-STATIC'
150
+ ]
151
+ },
152
+ {
153
+ id: 'sf-04',
154
+ from: '广州',
155
+ to: '昆明',
156
+ date: '2026-12-20',
157
+ transport: [
158
+ 'CA-STATIC'
159
+ ]
160
+ },
161
+ {
162
+ id: 'sf-05',
163
+ from: '深圳',
164
+ to: '成都',
165
+ date: '2026-10-05',
166
+ transport: [
167
+ '3U-STATIC'
168
+ ]
169
+ },
170
+ {
171
+ id: 'sf-06',
172
+ from: '杭州',
173
+ to: '厦门',
174
+ date: '2026-10-06',
175
+ transport: [
176
+ 'CA-STATIC'
177
+ ]
178
+ },
179
+ {
180
+ id: 'sf-07',
181
+ from: '西安',
182
+ to: '桂林',
183
+ date: '2026-10-07',
184
+ transport: [
185
+ 'CA-STATIC'
186
+ ]
187
+ },
188
+ {
189
+ id: 'sf-08',
190
+ from: '重庆',
191
+ to: '贵阳',
192
+ date: '2026-10-08',
193
+ transport: [
194
+ 'CA-STATIC'
195
+ ]
196
+ }
197
+ ];
198
+ for (const expected of expectedStaticCoverage){
199
+ const record = {
200
+ ...manualRecord,
201
+ query_id: expected.id,
202
+ route_segments: [
203
+ {
204
+ ...manualRecord.route_segments[0],
205
+ from: expected.from,
206
+ to: expected.to,
207
+ departure_at: `${expected.date}T06:00:00+08:00`,
208
+ arrival_at: `${expected.date}T12:00:00+08:00`
209
+ }
210
+ ]
211
+ };
212
+ const resolution = provider.resolveStaticGolden({
213
+ query: {
214
+ id: expected.id,
215
+ from: expected.from,
216
+ to: expected.to,
217
+ date: expected.date
218
+ },
219
+ manualRecord: record,
220
+ snapshot
221
+ });
222
+ assert.equal(resolution.effective_source, 'static-openflights+manual-band', `${expected.id} 不得全量回退 manual`);
223
+ assert.equal(resolution.fallback_reason, undefined);
224
+ assert.equal(resolution.record.route_segments.length, expected.transport.length);
225
+ assert.equal(resolution.record.route_segments[0]?.from, expected.from);
226
+ assert.equal(resolution.record.route_segments.at(-1)?.to, expected.to);
227
+ assert.deepEqual(resolution.record.route_segments.map((segment)=>segment.transport_number), expected.transport, `${expected.id} 运行时首选 carrier 必须来自已核对的 pinned route row`);
228
+ for (const segment of resolution.record.route_segments){
229
+ assert.match(segment.transport_number, /^[A-Z0-9]{2}-STATIC$/);
230
+ assert.ok(Number.isFinite(Date.parse(segment.departure_at)));
231
+ assert.ok(Number.isFinite(Date.parse(segment.arrival_at)));
232
+ assert.ok(Date.parse(segment.arrival_at) > Date.parse(segment.departure_at));
233
+ }
234
+ }
235
+ const warnings = [];
236
+ const fallbackRecord = {
237
+ ...manualRecord,
238
+ query_id: 'sf-99'
239
+ };
240
+ const fallbackResolution = provider.resolveStaticGolden({
241
+ query: {
242
+ id: 'sf-99',
243
+ from: '北京',
244
+ to: '大理',
245
+ date: '2026-10-02'
246
+ },
247
+ manualRecord: fallbackRecord,
248
+ snapshot,
249
+ warn: (message)=>warnings.push(message)
250
+ });
251
+ assert.equal(fallbackResolution.effective_source, 'manual-golden');
252
+ assert.equal(fallbackResolution.record, fallbackRecord, 'fallback 必须原样使用 manual record');
253
+ assert.equal(fallbackResolution.fallback_reason, 'static route missing for sf-99');
254
+ assert.deepEqual(warnings, [
255
+ '[sf-live-benchmark] static vendor failed for sf-99: static route missing for sf-99; fallback=manual-golden'
256
+ ]);
257
+ const snapshotWarnings = [];
258
+ const unavailableResolution = provider.resolveStaticGolden({
259
+ query: {
260
+ id: 'sf-02',
261
+ from: '北京',
262
+ to: '大理',
263
+ date: '2026-10-02'
264
+ },
265
+ manualRecord,
266
+ snapshotError: 'invalid static snapshot schema_version',
267
+ warn: (message)=>snapshotWarnings.push(message)
268
+ });
269
+ assert.equal(unavailableResolution.effective_source, 'manual-golden');
270
+ assert.equal(unavailableResolution.fallback_reason, 'static snapshot unavailable: invalid static snapshot schema_version');
271
+ assert.deepEqual(snapshotWarnings, [
272
+ '[sf-live-benchmark] static vendor failed for sf-02: static snapshot unavailable: invalid static snapshot schema_version; fallback=manual-golden'
273
+ ]);
274
+ const malformedRoot = mkdtempSync(join(tmpdir(), 'gotry-static-golden-'));
275
+ try {
276
+ const malformedPath = join(malformedRoot, 'malformed.json');
277
+ writeFileSync(malformedPath, JSON.stringify({
278
+ schema_version: 'sf-static-routes.v0',
279
+ source: {},
280
+ routes: []
281
+ }));
282
+ assert.throws(()=>provider.loadStaticFlightSnapshot(malformedPath), /invalid static snapshot schema_version: sf-static-routes.v0/, '未知 schema 不得被类型断言伪装成有效 static vendor');
283
+ const malformedRoutesPath = join(malformedRoot, 'malformed-routes.json');
284
+ writeFileSync(malformedRoutesPath, JSON.stringify({
285
+ schema_version: 'sf-static-routes.v1',
286
+ source: snapshot.source,
287
+ routes: null
288
+ }));
289
+ assert.throws(()=>provider.loadStaticFlightSnapshot(malformedRoutesPath), /invalid static snapshot routes/, 'schema 名正确但 routes 损坏时也必须由 loader 截获,runner 才能降级 manual');
290
+ } finally{
291
+ rmSync(malformedRoot, {
292
+ recursive: true,
293
+ force: true
294
+ });
295
+ }
296
+ console.log('STATIC GOLDEN TESTS: parser + snapshot + resolve/fallback contract OK');
297
+
298
+
299
+ //# sourceURL=ts/scripts/static-golden-tests.ts
@@ -315,4 +315,4 @@ if (process.argv.includes('--real')) {
315
315
  }
316
316
 
317
317
 
318
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/time-eval-tests.ts
318
+ //# sourceURL=ts/scripts/time-eval-tests.ts
@@ -120,4 +120,4 @@ pass('trip_id 语义派生:确定性可重放', ()=>{
120
120
  console.log(`\nTRAVEL TIMELINE TESTS: ${n}/7 OK(memory-design P1 守门面)`);
121
121
 
122
122
 
123
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/travel-timeline-tests.ts
123
+ //# sourceURL=ts/scripts/travel-timeline-tests.ts
@@ -46,4 +46,4 @@ assert.deepEqual(r4.work_window_exclusions, []);
46
46
  console.log(`TS UNIFIED TESTS: 4/4 OK(工作窗口生效,f2=VZ303,¥${r1.money_cny})`);
47
47
 
48
48
 
49
- //# sourceURL=/Users/bytedance/work/gotry/ts/scripts/unified-tests.ts
49
+ //# sourceURL=ts/scripts/unified-tests.ts