@danceiny/gotry 0.0.1-rc.16 → 0.0.1-rc.18

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 (229) hide show
  1. package/README.md +161 -116
  2. package/README.zh-CN.md +173 -144
  3. package/bin/gotry-booking-copilot.js +53 -0
  4. package/bin/gotry-bootstrap.js +113 -66
  5. package/bin/gotry-inner.js +436 -70
  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 +5 -1
  10. package/dist/capabilities/agent-reach-deep.js +1 -1
  11. package/dist/capabilities/agent-reach.js +1 -1
  12. package/dist/capabilities/anything.js +1 -1
  13. package/dist/capabilities/artifacts.js +1 -1
  14. package/dist/capabilities/effect.js +1 -1
  15. package/dist/capabilities/fact-log.js +1 -1
  16. package/dist/capabilities/flyai.js +20 -6
  17. package/dist/capabilities/hbcli.js +1 -1
  18. package/dist/capabilities/incident-log.js +1 -1
  19. package/dist/capabilities/model-override.js +18 -0
  20. package/dist/capabilities/opensky.js +1 -1
  21. package/dist/capabilities/resilience.js +1 -1
  22. package/dist/capabilities/session/action-cache.js +1 -1
  23. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  24. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  25. package/dist/capabilities/session/benchmark.js +1 -1
  26. package/dist/capabilities/session/extension-bridge.js +19 -6
  27. package/dist/capabilities/session/extension-channel.js +3 -3
  28. package/dist/capabilities/session/extension-distribution.js +234 -0
  29. package/dist/capabilities/session/extract.js +1 -1
  30. package/dist/capabilities/session/golden-score.js +92 -0
  31. package/dist/capabilities/session/health-watch.js +1 -1
  32. package/dist/capabilities/session/read-guard.js +1 -1
  33. package/dist/capabilities/session/static-flight-golden.js +137 -0
  34. package/dist/capabilities/session/transport.js +1 -1
  35. package/dist/capabilities/session/wizard.js +21 -251
  36. package/dist/capabilities/session-consent.js +1 -1
  37. package/dist/capabilities/session-login.js +11 -5
  38. package/dist/capabilities/session-search.js +46 -4
  39. package/dist/capabilities/weather.js +168 -46
  40. package/dist/data/session-golden-20.json +25 -0
  41. package/dist/data/sf-golden-manifest.json +102 -0
  42. package/dist/data/sf-static-routes.json +91 -0
  43. package/dist/scripts/action-cache-tests.js +1 -1
  44. package/dist/scripts/agent-planning-budget-e2e.js +227 -0
  45. package/dist/scripts/agent-planning-budget-tests.js +173 -0
  46. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  47. package/dist/scripts/agent-reach-tests.js +1 -1
  48. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  49. package/dist/scripts/anything-tests.js +1 -1
  50. package/dist/scripts/async-collect.js +1 -1
  51. package/dist/scripts/benchmark-environment-bridge-e2e.js +981 -0
  52. package/dist/scripts/benchmark-environment-bridge-tests.js +2544 -0
  53. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +335 -0
  54. package/dist/scripts/booking-copilot-availability-policy-v2-tests.js +1355 -0
  55. package/dist/scripts/booking-copilot-bin-proof-tests.js +89 -0
  56. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +113 -0
  57. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +356 -0
  58. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +557 -0
  59. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +140 -0
  60. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +202 -0
  61. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +102 -0
  62. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +374 -0
  63. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +447 -0
  64. package/dist/scripts/booking-copilot-runtime-proof-tests.js +205 -0
  65. package/dist/scripts/booking-copilot-server-proof-tests.js +317 -0
  66. package/dist/scripts/booking-copilot-startup-proof-tests.js +283 -0
  67. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +3935 -0
  68. package/dist/scripts/booking-saga-tests.js +1 -1
  69. package/dist/scripts/booking-surface-contract-proof-tests.js +393 -0
  70. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +1174 -0
  71. package/dist/scripts/bootstrap-tests.js +45 -39
  72. package/dist/scripts/build-changelog.js +1 -1
  73. package/dist/scripts/changelog-tests.js +1 -1
  74. package/dist/scripts/companion-tests.js +1 -1
  75. package/dist/scripts/diff-test.js +1 -1
  76. package/dist/scripts/dsh-runtime-closure-tests.js +225 -0
  77. package/dist/scripts/dsh-runtime-closure.js +150 -0
  78. package/dist/scripts/effect-tests.js +1 -1
  79. package/dist/scripts/engine-run.js +1 -1
  80. package/dist/scripts/engine-tests.js +1 -1
  81. package/dist/scripts/evaluation-cadence-tests.js +347 -0
  82. package/dist/scripts/evaluation-contract-tests.js +574 -0
  83. package/dist/scripts/extension-distribution-cli.js +35 -0
  84. package/dist/scripts/extension-distribution-tests.js +341 -0
  85. package/dist/scripts/extension-tests.js +128 -23
  86. package/dist/scripts/fact-gate-tests.js +1 -1
  87. package/dist/scripts/flyai-tests.js +1 -1
  88. package/dist/scripts/hbcli-e2e-tests.js +1 -1
  89. package/dist/scripts/hbcli-tests.js +1 -1
  90. package/dist/scripts/health-watch-cli.js +1 -1
  91. package/dist/scripts/i18n-tests.js +1 -1
  92. package/dist/scripts/incident-tests.js +1 -1
  93. package/dist/scripts/journey-tests.js +1 -1
  94. package/dist/scripts/ledger-tests.js +1 -1
  95. package/dist/scripts/ledger-workflow-crash.js +1 -1
  96. package/dist/scripts/memory-capture-tests.js +1 -1
  97. package/dist/scripts/memory-decay-tests.js +1 -1
  98. package/dist/scripts/memory-metrics.js +1 -1
  99. package/dist/scripts/memory-value-report.js +1 -1
  100. package/dist/scripts/model-override-e2e.js +176 -0
  101. package/dist/scripts/nightly-evidence-tests.js +1 -1
  102. package/dist/scripts/nightly-evidence.js +1 -1
  103. package/dist/scripts/nudge-digest.js +1 -1
  104. package/dist/scripts/onboarding-tests.js +21 -53
  105. package/dist/scripts/opensky-check.js +1 -1
  106. package/dist/scripts/opensky-tests.js +1 -1
  107. package/dist/scripts/pnpm-dsh-closure-proof.js +20 -0
  108. package/dist/scripts/price-drift-tests.js +1 -1
  109. package/dist/scripts/price-drift-watch.js +1 -1
  110. package/dist/scripts/probe-poi-tests.js +1 -1
  111. package/dist/scripts/product-metrics.js +1 -1
  112. package/dist/scripts/publish-preverify.js +40 -4
  113. package/dist/scripts/realtime-pricing-tests.js +1 -1
  114. package/dist/scripts/replay-async.js +1 -1
  115. package/dist/scripts/replay-real.js +1 -1
  116. package/dist/scripts/replay.js +1 -1
  117. package/dist/scripts/session-attach-diagnose.js +1 -1
  118. package/dist/scripts/session-attach-poc.js +1 -1
  119. package/dist/scripts/session-benchmark.js +1 -1
  120. package/dist/scripts/session-extract-tests.js +1 -1
  121. package/dist/scripts/session-login.js +1 -1
  122. package/dist/scripts/session-tests.js +70 -20
  123. package/dist/scripts/sf-live-benchmark.js +338 -0
  124. package/dist/scripts/sf-live-cli-tests.js +21 -0
  125. package/dist/scripts/sf-soft-score-tests.js +108 -0
  126. package/dist/scripts/sf-summary.js +93 -0
  127. package/dist/scripts/skeleton-check.js +1 -1
  128. package/dist/scripts/skeleton-integration-test.js +1 -1
  129. package/dist/scripts/skills-contract-tests.js +1 -1
  130. package/dist/scripts/smoke-session-gate-tests.js +29 -0
  131. package/dist/scripts/smoke.js +79 -33
  132. package/dist/scripts/state-cli-tests.js +1 -1
  133. package/dist/scripts/state-cli.js +1 -1
  134. package/dist/scripts/static-golden-tests.js +299 -0
  135. package/dist/scripts/time-eval-tests.js +1 -1
  136. package/dist/scripts/travel-timeline-tests.js +1 -1
  137. package/dist/scripts/unified-tests.js +1 -1
  138. package/dist/scripts/weather-tests.js +694 -44
  139. package/dist/scripts/z3-race-tests.js +1 -1
  140. package/dist/src/artifact-gate.js +1 -1
  141. package/dist/src/benchmark-agent-conformance.js +370 -0
  142. package/dist/src/benchmark-environment-bridge.js +384 -0
  143. package/dist/src/benchmark-headless-child-diagnostics.js +173 -0
  144. package/dist/src/benchmark-tool-isolation.js +124 -0
  145. package/dist/src/bookable-facts.js +1 -1
  146. package/dist/src/booking-saga.js +1 -1
  147. package/dist/src/booking-surface/availability-policy-v2.js +830 -0
  148. package/dist/src/booking-surface/canonical-schema.js +113 -0
  149. package/dist/src/booking-surface/contracts-v2.js +89 -0
  150. package/dist/src/booking-surface/contracts.js +46 -0
  151. package/dist/src/booking-surface/dsh-planner.js +453 -0
  152. package/dist/src/booking-surface/dsh-plugin.js +93 -0
  153. package/dist/src/booking-surface/error-codes.js +94 -0
  154. package/dist/src/booking-surface/index.js +15 -0
  155. package/dist/src/booking-surface/profile.js +68 -0
  156. package/dist/src/booking-surface/runtime-v2.js +1771 -0
  157. package/dist/src/booking-surface/runtime.js +351 -0
  158. package/dist/src/booking-surface/server-v2.js +334 -0
  159. package/dist/src/booking-surface/server.js +302 -0
  160. package/dist/src/booking-surface/startup.js +159 -0
  161. package/dist/src/booking-surface/validation-v2.js +319 -0
  162. package/dist/src/booking-surface/validation.js +809 -0
  163. package/dist/src/bridge.js +1 -1
  164. package/dist/src/companions.js +1 -1
  165. package/dist/src/contracts.js +1 -1
  166. package/dist/src/dsh-llm.js +1 -1
  167. package/dist/src/engine.js +1 -1
  168. package/dist/src/evaluation-cadence.js +234 -0
  169. package/dist/src/evaluation-contracts.js +906 -0
  170. package/dist/src/i18n.js +1 -1
  171. package/dist/src/index.js +50 -19
  172. package/dist/src/journey.js +1 -1
  173. package/dist/src/loop.js +1 -1
  174. package/dist/src/memory-capture.js +1 -1
  175. package/dist/src/memory-decay.js +1 -1
  176. package/dist/src/memory-utility.js +1 -1
  177. package/dist/src/mock-llm.js +1 -1
  178. package/dist/src/model.js +1 -1
  179. package/dist/src/realtime-pricing.js +1 -1
  180. package/dist/src/slot-spec.js +1 -1
  181. package/dist/src/state-ledger.js +2 -1
  182. package/dist/src/time-anchor.js +1 -1
  183. package/dist/src/tool-budget.js +136 -0
  184. package/dist/src/tool-packet.js +1 -1
  185. package/dist/src/travel-slots.js +1 -1
  186. package/dist/src/travel-timeline.js +1 -1
  187. package/dist/src/unified.js +1 -1
  188. package/dist/src/wish-pool.js +1 -1
  189. package/dist/src/z3-shared.js +1 -1
  190. package/extension/README.md +31 -7
  191. package/package.json +286 -11
  192. package/schemas/booking.surface.v1.schema.json +927 -0
  193. package/schemas/booking.surface.v2.schema.json +61 -0
  194. package/ts/capabilities/flyai.ts +16 -3
  195. package/ts/capabilities/session/extension-bridge.ts +37 -14
  196. package/ts/capabilities/session/extension-channel.ts +6 -3
  197. package/ts/capabilities/session/extension-distribution.ts +264 -0
  198. package/ts/capabilities/session/golden-score.ts +139 -0
  199. package/ts/capabilities/session/health-watch.ts +1 -1
  200. package/ts/capabilities/session/static-flight-golden.ts +209 -0
  201. package/ts/capabilities/session/wizard.ts +34 -176
  202. package/ts/capabilities/session-login.ts +15 -5
  203. package/ts/capabilities/session-search.ts +40 -3
  204. package/ts/capabilities/weather.ts +141 -52
  205. package/ts/package.json +3 -3
  206. package/ts/src/benchmark-agent-conformance.ts +448 -0
  207. package/ts/src/benchmark-environment-bridge.ts +348 -0
  208. package/ts/src/benchmark-headless-child-diagnostics.ts +184 -0
  209. package/ts/src/benchmark-tool-isolation.ts +166 -0
  210. package/ts/src/booking-surface/availability-policy-v2.ts +523 -0
  211. package/ts/src/booking-surface/canonical-schema.js +113 -0
  212. package/ts/src/booking-surface/contracts-v2.ts +118 -0
  213. package/ts/src/booking-surface/contracts.ts +380 -0
  214. package/ts/src/booking-surface/dsh-planner.ts +452 -0
  215. package/ts/src/booking-surface/dsh-plugin.js +93 -0
  216. package/ts/src/booking-surface/error-codes.ts +101 -0
  217. package/ts/src/booking-surface/index.ts +12 -0
  218. package/ts/src/booking-surface/profile.ts +42 -0
  219. package/ts/src/booking-surface/runtime-v2.ts +1466 -0
  220. package/ts/src/booking-surface/runtime.ts +483 -0
  221. package/ts/src/booking-surface/server-v2.ts +247 -0
  222. package/ts/src/booking-surface/server.ts +324 -0
  223. package/ts/src/booking-surface/startup.ts +196 -0
  224. package/ts/src/booking-surface/validation-v2.ts +205 -0
  225. package/ts/src/booking-surface/validation.ts +453 -0
  226. package/ts/src/index.ts +64 -11
  227. package/ts/src/state-ledger.ts +1 -0
  228. package/ts/src/tool-budget.ts +165 -0
  229. package/dist/scripts/wizard-bootstrap.js +0 -32
@@ -0,0 +1,176 @@
1
+ import { createServer } from 'node:http';
2
+ import { existsSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { spawn } from 'node:child_process';
6
+ const BOOT_TIMEOUT_MS = 120_000;
7
+ async function startMockRelay() {
8
+ const bodies = [];
9
+ const server = createServer((req, res)=>{
10
+ if (req.method !== 'POST' || !req.url?.endsWith('/chat/completions')) {
11
+ res.writeHead(404).end('not found');
12
+ return;
13
+ }
14
+ const chunks = [];
15
+ req.on('data', (c)=>chunks.push(c));
16
+ req.on('end', ()=>{
17
+ try {
18
+ bodies.push(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
19
+ } catch {
20
+ bodies.push({});
21
+ }
22
+ const chunk = (delta, finish)=>`data: ${JSON.stringify({
23
+ id: 'e2e',
24
+ object: 'chat.completion.chunk',
25
+ created: 1,
26
+ model: 'mock',
27
+ choices: [
28
+ {
29
+ index: 0,
30
+ delta,
31
+ finish_reason: finish
32
+ }
33
+ ]
34
+ })}\n\n`;
35
+ res.writeHead(200, {
36
+ 'content-type': 'text/event-stream',
37
+ 'cache-control': 'no-cache'
38
+ });
39
+ res.write(chunk({
40
+ role: 'assistant',
41
+ content: 'ok'
42
+ }, null));
43
+ res.write(chunk({}, 'stop'));
44
+ res.write('data: [DONE]\n\n');
45
+ res.end();
46
+ });
47
+ });
48
+ await new Promise((resolve)=>server.listen(0, '127.0.0.1', resolve));
49
+ const port = server.address().port;
50
+ return {
51
+ port,
52
+ bodies,
53
+ close: ()=>new Promise((resolve)=>server.close(()=>resolve()))
54
+ };
55
+ }
56
+ async function runScenario(s, packageBin) {
57
+ const relay = await startMockRelay();
58
+ const dshHome = mkdtempSync(join(tmpdir(), 'gotry-77-dsh-home-'));
59
+ const childCwd = mkdtempSync(join(tmpdir(), 'gotry-77-cwd-'));
60
+ try {
61
+ if (s.userLayerModel) {
62
+ writeFileSync(join(dshHome, 'settings.yaml'), `agent-default-model:\n provider: deepseek-official\n model: ${s.userLayerModel}\n`);
63
+ }
64
+ const env = {
65
+ ...process.env,
66
+ LLM_API_KEY: 'e2e-mock-key',
67
+ LLM_BASE_URL: `http://127.0.0.1:${relay.port}/v1`,
68
+ DSH_HOME: dshHome
69
+ };
70
+ delete env['GOTRY_LLM_MODEL'];
71
+ if (s.llmModel) env['LLM_MODEL'] = s.llmModel;
72
+ else delete env['LLM_MODEL'];
73
+ const child = spawn(packageBin, [
74
+ 'reply with the word ok'
75
+ ], {
76
+ env,
77
+ cwd: childCwd,
78
+ stdio: [
79
+ 'ignore',
80
+ 'pipe',
81
+ 'pipe'
82
+ ]
83
+ });
84
+ let logTail = '';
85
+ child.stdout.on('data', (d)=>{
86
+ logTail = (logTail + d).slice(-2000);
87
+ });
88
+ child.stderr.on('data', (d)=>{
89
+ logTail = (logTail + d).slice(-2000);
90
+ });
91
+ const exit = await new Promise((resolve)=>{
92
+ const timer = setTimeout(()=>{
93
+ child.kill('SIGKILL');
94
+ resolve(null);
95
+ }, BOOT_TIMEOUT_MS);
96
+ child.on('exit', (code)=>{
97
+ clearTimeout(timer);
98
+ resolve(code);
99
+ });
100
+ });
101
+ if (relay.bodies.length === 0) {
102
+ return {
103
+ ok: false,
104
+ detail: `中转未收到任何请求(exit=${exit});尾部日志: ${logTail.slice(-400)}`
105
+ };
106
+ }
107
+ const models = relay.bodies.map((b)=>String(b.model));
108
+ const bad = models.filter((m)=>m !== s.expectModel);
109
+ return bad.length === 0 ? {
110
+ ok: true,
111
+ detail: `${models.length} 个请求体 model 全部 = ${s.expectModel}(exit=${exit})`
112
+ } : {
113
+ ok: false,
114
+ detail: `请求体 model 不符:期望 ${s.expectModel},实际 ${JSON.stringify(models)}`
115
+ };
116
+ } finally{
117
+ await relay.close();
118
+ rmSync(dshHome, {
119
+ recursive: true,
120
+ force: true
121
+ });
122
+ rmSync(childCwd, {
123
+ recursive: true,
124
+ force: true
125
+ });
126
+ }
127
+ }
128
+ async function main() {
129
+ const packageBin = process.env.GOTRY_MODEL_OVERRIDE_E2E_BIN;
130
+ if (!packageBin || !existsSync(packageBin)) {
131
+ console.error('GOTRY_MODEL_OVERRIDE_E2E_BIN 必须指向当前 tarball 的 clean installed-package bin');
132
+ process.exit(2);
133
+ }
134
+ await runAll(packageBin);
135
+ }
136
+ async function runAll(packageBin) {
137
+ const scenarios = [
138
+ {
139
+ name: '① 设 LLM_MODEL(干净 DSH_HOME)',
140
+ llmModel: 'gotry-e2e-model',
141
+ expectModel: 'gotry-e2e-model'
142
+ },
143
+ {
144
+ name: '② 设 LLM_MODEL + 用户层已选模型',
145
+ llmModel: 'gotry-e2e-model',
146
+ userLayerModel: 'user-picked-model',
147
+ expectModel: 'gotry-e2e-model'
148
+ },
149
+ {
150
+ name: '③ 不设 LLM_MODEL(干净 DSH_HOME)',
151
+ expectModel: 'deepseek-v4-flash'
152
+ },
153
+ {
154
+ name: '④ 不设 LLM_MODEL + 用户层已选模型',
155
+ userLayerModel: 'user-picked-model',
156
+ expectModel: 'user-picked-model'
157
+ }
158
+ ];
159
+ let fail = 0;
160
+ for (const s of scenarios){
161
+ process.stdout.write(`${s.name} … `);
162
+ const t0 = Date.now();
163
+ const r = await runScenario(s, packageBin);
164
+ console.log(`${r.ok ? 'PASS' : 'FAIL'}(${((Date.now() - t0) / 1000).toFixed(1)}s)${r.detail}`);
165
+ if (!r.ok) fail = 1;
166
+ }
167
+ if (fail) {
168
+ console.error('\nMODEL OVERRIDE E2E FAIL');
169
+ process.exit(1);
170
+ }
171
+ console.log('\nMODEL OVERRIDE E2E OK');
172
+ }
173
+ await main();
174
+
175
+
176
+ //# sourceURL=ts/scripts/model-override-e2e.ts
@@ -183,4 +183,4 @@ ok('dry-run mock 全链演练 + real_llm=false + 零落盘');
183
183
  console.log(`nightly-evidence tests: ${passed} 组断言全绿(offline,真跑不在 CI)`);
184
184
 
185
185
 
186
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/nightly-evidence-tests.ts
186
+ //# sourceURL=ts/scripts/nightly-evidence-tests.ts
@@ -286,4 +286,4 @@ if (process.argv[1]?.endsWith('nightly-evidence.ts')) {
286
286
  }
287
287
 
288
288
 
289
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/nightly-evidence.ts
289
+ //# sourceURL=ts/scripts/nightly-evidence.ts
@@ -80,4 +80,4 @@ if (channel === 'file') {
80
80
  }
81
81
 
82
82
 
83
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/nudge-digest.ts
83
+ //# sourceURL=ts/scripts/nudge-digest.ts
@@ -37,27 +37,20 @@ async function asyncSleep(ms) {
37
37
  await new Promise((resolve)=>setTimeout(resolve, ms));
38
38
  }
39
39
  console.log('ONBOARDING UX (§40)');
40
- await check('1. wizard --dry-run 零网络零浏览器,exit 0,5 步齐全', async ()=>{
41
- const { spawn } = captureSpawn();
40
+ await check('1. wizard --dry-run 零网络零浏览器零 GUI spawn,exit 0,2 步齐全', async ()=>{
42
41
  const result = await runOnboardingWizard({
43
- dryRun: true,
44
- forcePlatform: 'darwin',
45
- spawn
42
+ dryRun: true
46
43
  });
47
44
  assert.equal(result.ok, true, `wizard ok 应 true,实际 ${JSON.stringify(result.steps)}`);
48
- assert.equal(result.steps.length, 5, `应有 5 步,实际 ${result.steps.length}`);
45
+ assert.equal(result.steps.length, 2, `应有 2 步,实际 ${result.steps.length}`);
49
46
  const expected = [
50
47
  'ensure-extension-files',
51
- 'open-chrome-extensions',
52
- 'clipboard-extension-path',
53
- 'panel-guide',
54
48
  'watch-extension-ready'
55
49
  ];
56
50
  for(let i = 0; i < expected.length; i++){
57
51
  assert.equal(result.steps[i].step, expected[i]);
58
52
  assert.equal(result.steps[i].status, 'skip', `dry-run 每步应为 skip,${result.steps[i].step} 实为 ${result.steps[i].status}`);
59
53
  }
60
- assert.equal(result.platform, 'darwin');
61
54
  });
62
55
  await check('2. health-watch 三时序:0ms / 中途 / 120s+1ms', async ()=>{
63
56
  const w0 = startExtensionHealthWatch({
@@ -88,40 +81,21 @@ await check('2. health-watch 三时序:0ms / 中途 / 120s+1ms', async ()=>{
88
81
  assert.equal(oTimeout.ready, false);
89
82
  assert.equal(oTimeout.reason, 'timeout');
90
83
  });
91
- await check('3. 剪贴板 + GUI 面板跨平台降级', async ()=>{
92
- const darwin = captureSpawn();
93
- const rDarwin = await runOnboardingWizard({
94
- dryRun: false,
95
- forcePlatform: 'darwin',
96
- extensionDir: '/tmp/gotry-test-ext',
97
- spawn: darwin.spawn,
98
- sourceDir: '/'
84
+ await check('3. wizard 完全不调 spawn(GUI 面板/剪贴板/直开浏览器均已退役)', async ()=>{
85
+ const dry = captureSpawn();
86
+ await runOnboardingWizard({
87
+ dryRun: true,
88
+ sourceDir: '/',
89
+ extensionDir: '/tmp/none'
99
90
  });
100
- const darwinBins = darwin.calls.map((c)=>c.bin);
101
- assert.ok(darwinBins.includes('open'), `darwin 应调 open(实调 ${darwinBins.join(',')})`);
102
- assert.ok(darwinBins.includes('pbcopy'), 'darwin 应调 pbcopy');
103
- assert.ok(darwinBins.includes('osascript'), 'darwin 应调 osascript');
104
- const linux = captureSpawn();
91
+ assert.equal(dry.calls.length, 0, `dry-run 不应 spawn 任何命令(实调 ${dry.calls.length})`);
92
+ const fail = captureSpawn();
105
93
  await runOnboardingWizard({
106
94
  dryRun: false,
107
- forcePlatform: 'linux',
108
- extensionDir: '/tmp/gotry-test-ext',
109
- spawn: linux.spawn,
110
- sourceDir: '/'
111
- });
112
- const linuxBins = linux.calls.map((c)=>c.bin);
113
- assert.ok(linuxBins.includes('xdg-open'), `linux 应调 xdg-open(实调 ${linuxBins.join(',')})`);
114
- assert.ok(linuxBins.includes('xclip'), 'linux 应调 xclip');
115
- assert.ok(linuxBins.includes('zenity'), 'linux 应调 zenity');
116
- const headless = captureSpawn();
117
- const rHeadless = await runOnboardingWizard({
118
- dryRun: true,
119
- forcePlatform: 'headless',
120
- extensionDir: '/tmp/gotry-test-ext',
121
- spawn: headless.spawn
95
+ sourceDir: '/nonexistent-source-dir-for-onboarding-tests',
96
+ extensionDir: '/tmp/none-fail'
122
97
  });
123
- assert.equal(headless.calls.length, 0, `headless 不应 spawn 任何命令(实调 ${headless.calls.length})`);
124
- assert.equal(rHeadless.platform, 'headless');
98
+ assert.equal(fail.calls.length, 0, `fail 路径也不应 spawn(实调 ${fail.calls.length})`);
125
99
  });
126
100
  await check('4. retry-after-watch:needs-extension → watch ready → 自动重放同 query_id', async ()=>{
127
101
  let fetchCalls = 0;
@@ -151,21 +125,15 @@ await check('4. retry-after-watch:needs-extension → watch ready → 自动重
151
125
  assert.equal(retry.query_id, queryId, '同 query_id 必须保持');
152
126
  assert.equal(fetchCalls, 2, `应调用 fetch 两次(首次 + 重放),实 ${fetchCalls}`);
153
127
  });
154
- await check('5. wizard 无 GUI 走终端降级', async ()=>{
155
- const headless = captureSpawn();
128
+ await check('5. wizard 不依赖 platform/panel 参数(GUI 跨平台降级退役)', async ()=>{
156
129
  const result = await runOnboardingWizard({
157
130
  dryRun: true,
158
- forcePlatform: 'headless',
159
- extensionDir: '/tmp/gotry-test-ext',
160
- spawn: headless.spawn
131
+ extensionDir: '/tmp/gotry-test-ext-2'
161
132
  });
162
- const clip = result.steps.find((s)=>s.step === 'clipboard-extension-path');
163
- const panel = result.steps.find((s)=>s.step === 'panel-guide');
164
- const watch = result.steps.find((s)=>s.step === 'watch-extension-ready');
165
- assert.equal(clip?.status, 'skip', 'headless 剪贴板应 skip');
166
- assert.equal(panel?.status, 'skip', 'headless 面板应 skip(终端降级)');
167
- assert.equal(watch?.status, 'skip', 'dry-run watch 应 skip');
168
- assert.equal(headless.calls.length, 0, `headless 不应调任何 spawn(实调 ${headless.calls.length})`);
133
+ assert.equal(result.steps.length, 2, `应 2 步,实 ${result.steps.length}`);
134
+ assert.equal(result.steps[0].step, 'ensure-extension-files');
135
+ assert.equal(result.steps[1].step, 'watch-extension-ready');
136
+ assert.equal(result.platform, undefined, 'platform 字段应不再存在');
169
137
  });
170
138
  await check('6. withAutoRetryOnExtension 端到端:首次 needs-ext → 自动重放', async ()=>{
171
139
  let callCount = 0;
@@ -215,4 +183,4 @@ await asyncSleep(0);
215
183
  console.log(`ONBOARDING UX (§40): ${passed} pass`);
216
184
 
217
185
 
218
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/onboarding-tests.ts
186
+ //# sourceURL=ts/scripts/onboarding-tests.ts
@@ -17,4 +17,4 @@ const target = process.argv[2] ?? 'EK329';
17
17
  console.log(await checkArrival(target, process.argv[3] ?? null));
18
18
 
19
19
 
20
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/opensky-check.ts
20
+ //# sourceURL=ts/scripts/opensky-check.ts
@@ -49,4 +49,4 @@ console.log('3. timeout → unavailable OK');
49
49
  console.log('\nOPEN-SKY TESTS: 3/3 OK(免费匿名 OpenSky API)');
50
50
 
51
51
 
52
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/opensky-tests.ts
52
+ //# sourceURL=ts/scripts/opensky-tests.ts
@@ -0,0 +1,20 @@
1
+ import assert from 'node:assert/strict';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { REQUIRED_BENCHMARK_DSH_VERSION } from '../../bin/gotry-runtime-resolution.js';
5
+ import { parsePnpmDshLock, REQUIRED_DSH_RUNTIME_PACKAGE_COUNT, validateDshRuntimeClosure } from './dsh-runtime-closure.js';
6
+ const consumerRoot = process.argv[2];
7
+ assert.ok(consumerRoot, 'usage: pnpm-dsh-closure-proof.ts <clean-consumer-root>');
8
+ const installedPackage = JSON.parse(readFileSync(resolve(consumerRoot, 'node_modules/@danceiny/gotry/package.json'), 'utf8'));
9
+ const lockPackages = parsePnpmDshLock(readFileSync(resolve(consumerRoot, 'pnpm-lock.yaml'), 'utf8'));
10
+ const closure = validateDshRuntimeClosure({
11
+ dependencies: installedPackage.dependencies ?? {},
12
+ lockPackages,
13
+ runtimeVersion: REQUIRED_BENCHMARK_DSH_VERSION,
14
+ expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT
15
+ });
16
+ assert.equal(closure.names.length, REQUIRED_DSH_RUNTIME_PACKAGE_COUNT, 'clean pnpm consumer must resolve the complete Round 5 DSH closure');
17
+ console.log(`PNPM DSH CLOSURE PROOF: ${closure.names.length} packages at ${closure.version}`);
18
+
19
+
20
+ //# sourceURL=ts/scripts/pnpm-dsh-closure-proof.ts
@@ -438,4 +438,4 @@ await testSnapshotMissing();
438
438
  console.log(`price-drift-watch tests: ${passed} 组断言全绿(offline,fetch 模式手动/CI 心跳)`);
439
439
 
440
440
 
441
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/price-drift-tests.ts
441
+ //# sourceURL=ts/scripts/price-drift-tests.ts
@@ -488,4 +488,4 @@ if (process.argv[1]?.endsWith('price-drift-watch.ts')) {
488
488
  }
489
489
 
490
490
 
491
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/price-drift-watch.ts
491
+ //# sourceURL=ts/scripts/price-drift-watch.ts
@@ -85,4 +85,4 @@ pass('7. 金标准对话噪音回归(2026-08-28 巡检:访谈答案/多段首查
85
85
  console.log('\nprobePoi TESTS: 6 类 OK(不崩 + 5 类触发 + 1 类不触发返 null)');
86
86
 
87
87
 
88
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/probe-poi-tests.ts
88
+ //# sourceURL=ts/scripts/probe-poi-tests.ts
@@ -566,4 +566,4 @@ if (process.argv[1]?.endsWith('product-metrics.ts')) {
566
566
  }
567
567
 
568
568
 
569
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/product-metrics.ts
569
+ //# sourceURL=ts/scripts/product-metrics.ts
@@ -1,7 +1,10 @@
1
1
  import { execSync } from 'node:child_process';
2
- import { mkdtempSync, rmSync, existsSync } from 'node:fs';
2
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { REQUIRED_BENCHMARK_DSH_VERSION } from '../../bin/gotry-runtime-resolution.js';
7
+ import { parsePnpmDshLock, parsePnpmRootDshImporter, REQUIRED_DSH_RUNTIME_PACKAGE_COUNT, validateDshRuntimeClosure, validatePnpmRootDshImporter } from './dsh-runtime-closure.js';
5
8
  const repoRoot = join(import.meta.dirname, '..', '..');
6
9
  process.chdir(repoRoot);
7
10
  const pkg = JSON.parse(execSync('cat package.json', {
@@ -29,8 +32,30 @@ try {
29
32
  encoding: 'utf-8'
30
33
  }));
31
34
  if (installedPkg.version !== version) throw new Error(`版本不符:${installedPkg.version}`);
35
+ const rootLock = JSON.parse(readFileSync(join(repoRoot, 'package-lock.json'), 'utf-8'));
36
+ const dshClosure = validateDshRuntimeClosure({
37
+ dependencies: installedPkg.dependencies ?? {},
38
+ lockPackages: rootLock.packages ?? {},
39
+ runtimeVersion: REQUIRED_BENCHMARK_DSH_VERSION,
40
+ expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT
41
+ });
42
+ console.log(`dsh closure: ${dshClosure.names.length} packages pinned at ${dshClosure.version}`);
43
+ const pnpmLock = readFileSync(join(repoRoot, 'pnpm-lock.yaml'), 'utf-8');
44
+ const pnpmClosure = validateDshRuntimeClosure({
45
+ dependencies: installedPkg.dependencies ?? {},
46
+ lockPackages: parsePnpmDshLock(pnpmLock),
47
+ runtimeVersion: REQUIRED_BENCHMARK_DSH_VERSION,
48
+ expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT
49
+ });
50
+ const pnpmImporter = validatePnpmRootDshImporter({
51
+ dependencies: installedPkg.dependencies ?? {},
52
+ importerEntries: parsePnpmRootDshImporter(pnpmLock),
53
+ runtimeVersion: REQUIRED_BENCHMARK_DSH_VERSION,
54
+ expectedPackageCount: REQUIRED_DSH_RUNTIME_PACKAGE_COUNT
55
+ });
56
+ console.log(`pnpm closure: ${pnpmClosure.names.length} packages, root importer ${pnpmImporter.names.length}`);
32
57
  const deps = Object.keys(installedPkg.dependencies ?? {});
33
- console.log(`dependencies: ${deps.join(', ')}`);
58
+ console.log(`dependencies: ${deps.length} declared`);
34
59
  const runtimeImports = [
35
60
  'better-sqlite3'
36
61
  ];
@@ -41,7 +66,13 @@ try {
41
66
  for (const e of [
42
67
  'dist/src/state-ledger.js',
43
68
  'dist/src/index.js',
44
- 'bin/gotry-inner.js'
69
+ 'bin/gotry-inner.js',
70
+ 'dist/capabilities/session/static-flight-golden.js',
71
+ 'dist/capabilities/session/golden-score.js',
72
+ 'dist/scripts/sf-live-benchmark.js',
73
+ 'dist/data/session-golden-20.json',
74
+ 'dist/data/sf-golden-manifest.json',
75
+ 'dist/data/sf-static-routes.json'
45
76
  ]){
46
77
  if (!existsSync(join(pkgDir, e))) throw new Error(`发布缺文件: ${e}`);
47
78
  }
@@ -50,6 +81,11 @@ try {
50
81
  }).trim().match(/^[1-9]/)) {
51
82
  throw new Error('dist/src/state-ledger.js 未引用 better-sqlite3(声明与实现不符)');
52
83
  }
84
+ const staticProvider = await import(pathToFileURL(join(pkgDir, 'dist/capabilities/session/static-flight-golden.js')).href);
85
+ const staticSnapshot = staticProvider.loadStaticFlightSnapshot();
86
+ if (staticSnapshot.schema_version !== 'sf-static-routes.v1' || staticSnapshot.routes.length !== 8) {
87
+ throw new Error('发布 static golden 快照不可加载或覆盖不足 8 条');
88
+ }
53
89
  console.log('PUBLISH PREVERIFY: OK');
54
90
  } catch (e) {
55
91
  console.error(`PREVERIFY FAILED: ${e.message}`);
@@ -65,4 +101,4 @@ try {
65
101
  }
66
102
 
67
103
 
68
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/publish-preverify.ts
104
+ //# sourceURL=ts/scripts/publish-preverify.ts
@@ -112,4 +112,4 @@ assert.equal(bare.spec.segments[0].options[0].move.services[0].priceCny, 3400, '
112
112
  console.log('\nREALTIME PRICING TESTS: 5/5 OK(hit 覆写+证据/error 降级/日期词表闸/求解集成/无段零触碰)');
113
113
 
114
114
 
115
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/realtime-pricing-tests.ts
115
+ //# sourceURL=ts/scripts/realtime-pricing-tests.ts
@@ -41,4 +41,4 @@ if (process.argv.includes('--request-only')) {
41
41
  }
42
42
 
43
43
 
44
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/replay-async.ts
44
+ //# sourceURL=ts/scripts/replay-async.ts
@@ -37,4 +37,4 @@ for (const t of turns){
37
37
  }
38
38
 
39
39
 
40
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/replay-real.ts
40
+ //# sourceURL=ts/scripts/replay-real.ts
@@ -113,4 +113,4 @@ console.log('REPLAY ASSERTS OK');
113
113
  console.log('SPEC-SLOT DATE GATE OK(单日期段分歧拦截 + 多段旁路不误伤)');
114
114
  }
115
115
 
116
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/replay.ts
116
+ //# sourceURL=ts/scripts/replay.ts
@@ -24,4 +24,4 @@ try {
24
24
  }
25
25
 
26
26
 
27
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-attach-diagnose.ts
27
+ //# sourceURL=ts/scripts/session-attach-diagnose.ts
@@ -62,4 +62,4 @@ main().catch((e)=>{
62
62
  });
63
63
 
64
64
 
65
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-attach-poc.ts
65
+ //# sourceURL=ts/scripts/session-attach-poc.ts
@@ -258,4 +258,4 @@ check('FlyAI 无完整 itemList 对象时保持 fail-closed', ()=>{
258
258
  console.log(`SESSION DOUBLE-SOURCE CONTRACT: ${passed} pass`);
259
259
 
260
260
 
261
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-benchmark.ts
261
+ //# sourceURL=ts/scripts/session-benchmark.ts
@@ -78,4 +78,4 @@ console.log(`\nSESSION-EXTRACT: ${pass} pass, ${fail} fail`);
78
78
  process.exit(fail > 0 ? 1 : 0);
79
79
 
80
80
 
81
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-extract-tests.ts
81
+ //# sourceURL=ts/scripts/session-extract-tests.ts
@@ -25,4 +25,4 @@ main().catch((e)=>{
25
25
  });
26
26
 
27
27
 
28
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-login.ts
28
+ //# sourceURL=ts/scripts/session-login.ts
@@ -108,9 +108,9 @@ if (process.env.GOTRY_SESSION_LIVE === '0') {
108
108
  destination: '丽江',
109
109
  depDate: '2026-10-01'
110
110
  });
111
- const sentinelBlocked = fr.verdict === 'error' && /sentinel|block/i.test(fr.error ?? '');
111
+ const sentinelBlocked = fr.verdict === 'error' && /sentinel|block|trial limit/i.test(fr.error ?? '');
112
112
  if (sentinelBlocked) {
113
- console.log(' WARN - 飞猪 Sentinel 限流(2026-08-28 实测,配额未文档化)——降级合同验证通过,跳过 hit 断言');
113
+ console.log(' WARN - 飞猪上游限流(Sentinel 2026-08-28 / trial-limit 429 2026-08-31 实测,配额未文档化)——降级合同验证通过,跳过 hit 断言');
114
114
  assert(fr.ok === false && /\[实时API:flyai@error@/.test(fr.evidence), '限流降级:结构化 error + 证据链错误形');
115
115
  } else {
116
116
  assert(fr.ok === true && fr.verdict === 'hit', '上海→丽江 hit', fr);
@@ -137,6 +137,48 @@ if (process.env.GOTRY_SESSION_LIVE === '0') {
137
137
  recursive: true,
138
138
  force: true
139
139
  });
140
+ }{
141
+ const bigDir = mkdtempSync(join(tmpdir(), 'flyai-trunc-'));
142
+ const payloadJs = join(bigDir, 'payload.js');
143
+ const itemCount = 100;
144
+ writeFileSync(payloadJs, `
145
+ const items = []
146
+ for (let i = 0; i < ${itemCount}; i++) {
147
+ items.push({
148
+ journeys: [{ segments: [{
149
+ marketingTransportName: '吉祥航空', marketingTransportNo: 'HO' + (1000 + i),
150
+ depDateTime: '2026-09-15 08:30', arrDateTime: '2026-09-15 12:10',
151
+ depStationName: '浦东国际机场', arrStationName: '长水机场', duration: '220', seatClassName: '经济舱',
152
+ }] }],
153
+ ticketPrice: String(800 + i),
154
+ jumpUrl: 'https://fliggy.com/item/' + 'x'.repeat(800) + i,
155
+ })
156
+ }
157
+ const payload = JSON.stringify({ data: { itemList: items } })
158
+ const step = Math.ceil(payload.length / 100)
159
+ for (let i = 0; i < 100; i++) process.stdout.write(payload.slice(i * step, (i + 1) * step))
160
+ process.exit(0)
161
+ `);
162
+ const bigBin = join(bigDir, 'flyai-cli-big');
163
+ writeFileSync(bigBin, `#!/bin/sh\nexec node "${payloadJs}"\n`, {
164
+ mode: 0o755
165
+ });
166
+ const tr = await flyaiSearch({
167
+ kind: 'flight',
168
+ origin: '上海',
169
+ destination: '昆明',
170
+ depDate: '2026-09-15',
171
+ cliBin: bigBin
172
+ });
173
+ assert(tr.ok === true && tr.verdict === 'hit' && tr.options?.length === itemCount, `大载荷 ${itemCount} 条(>64KB)完整解析无截断(#84)`, {
174
+ verdict: tr.verdict,
175
+ options: tr.options?.length,
176
+ error: tr.error
177
+ });
178
+ rmSync(bigDir, {
179
+ recursive: true,
180
+ force: true
181
+ });
140
182
  }console.log('G. sessionFlightSearch(live,Chrome+携程;默认跳过,GOTRY_SESSION_LIVE=1 显式开启)');
141
183
  if (process.env.GOTRY_SESSION_LIVE !== '1') {
142
184
  console.log(' SKIP - 会话 live 探针默认关(测试不再自动开浏览器窗口;GOTRY_SESSION_LIVE=1 显式开启)');
@@ -193,24 +235,28 @@ if (process.env.GOTRY_SESSION_LIVE !== '1') {
193
235
  }
194
236
  console.log('H. flyaiSearch hotel(飞猪官方 search-hotel)');
195
237
  {
196
- const hr = await flyaiSearch({
197
- kind: 'hotel',
198
- destName: '大理',
199
- checkInDate: '2026-10-01',
200
- checkOutDate: '2026-10-03'
201
- });
202
- const hSentinel = hr.verdict === 'error' && /sentinel|block/i.test(hr.error ?? '');
203
- if (hSentinel) {
204
- console.log(' WARN - 飞猪 Sentinel 限流——降级合同验证通过,跳过 hit 断言');
205
- assert(hr.ok === false && /\[实时API:flyai@error@/.test(hr.evidence), '酒店限流降级:结构化 error + 证据链错误形');
206
- } else if (hr.ok === false) {
207
- console.log(` WARN - flyai hotel 端点降级(${String(hr.error).slice(0, 60)})——证据链合同通过,hit 断言跳过`);
208
- assert(/\[实时API:flyai@error@/.test(hr.evidence), '端点降级仍带证据链错误形');
238
+ if (process.env.GOTRY_SESSION_LIVE === '0') {
239
+ console.log(' SKIP - GOTRY_SESSION_LIVE=0(离线门禁不调用 FlyAI 酒店实时端点)');
209
240
  } else {
210
- assert(hr.verdict === 'hit' && (hr.hotels?.length ?? 0) >= 1, '大理酒店 hit', hr.hotels?.[0]);
211
- assert(hr.hotels?.every((h)=>!!h.name && !!h.jumpUrl) ?? false, '条目结构化(name + jumpUrl 透传)', hr.hotels?.[0]);
241
+ const hr = await flyaiSearch({
242
+ kind: 'hotel',
243
+ destName: '大理',
244
+ checkInDate: '2026-10-01',
245
+ checkOutDate: '2026-10-03'
246
+ });
247
+ const hSentinel = hr.verdict === 'error' && /sentinel|block/i.test(hr.error ?? '');
248
+ if (hSentinel) {
249
+ console.log(' WARN - 飞猪 Sentinel 限流——降级合同验证通过,跳过 hit 断言');
250
+ assert(hr.ok === false && /\[实时API:flyai@error@/.test(hr.evidence), '酒店限流降级:结构化 error + 证据链错误形');
251
+ } else if (hr.ok === false) {
252
+ console.log(` WARN - flyai hotel 端点降级(${String(hr.error).slice(0, 60)})——证据链合同通过,hit 断言跳过`);
253
+ assert(/\[实时API:flyai@error@/.test(hr.evidence), '端点降级仍带证据链错误形');
254
+ } else {
255
+ assert(hr.verdict === 'hit' && (hr.hotels?.length ?? 0) >= 1, '大理酒店 hit', hr.hotels?.[0]);
256
+ assert(hr.hotels?.every((h)=>!!h.name && !!h.jumpUrl) ?? false, '条目结构化(name + jumpUrl 透传)', hr.hotels?.[0]);
257
+ }
258
+ assert(/\[实时API:flyai/.test(hr.evidence), '证据链 [实时API:flyai@*]');
212
259
  }
213
- assert(/\[实时API:flyai/.test(hr.evidence), '证据链 [实时API:flyai@*]');
214
260
  const fakeDir = mkdtempSync(join(tmpdir(), 'flyai-hotel-fake-'));
215
261
  const fakeCliH = join(fakeDir, 'flyai-hotel-fake');
216
262
  writeFileSync(fakeCliH, '#!/bin/sh\necho \'{"data":{"itemList":[{"name":"大理A 酒店","shId":"1","star":"高档型","rate":null,"price":"\\u00a57xx","address":"addr","interestsPoi":"近洱海","detailUrl":"https://router.feizhu.com/x"},{"star":"舒适型"}]}}\'\nexit 0\n', {
@@ -399,7 +445,11 @@ console.log('H. flyaiSearch hotel(飞猪官方 search-hotel)');
399
445
  } else {
400
446
  console.log(' SKIP - 登录 live 探针默认关(GOTRY_SESSION_LIVE=1 opt-in;工具面/桌面入口才真调)');
401
447
  }
402
- }console.log(`\nSESSION P1: ${pass} pass, ${fail} fail`);
448
+ }if (process.env.GOTRY_SESSION_TEST_FORCE_FAILURE === '1') {
449
+ assert(false, 'forced session failure propagation proof');
450
+ }
451
+ console.log(`\nSESSION P1: ${pass} pass, ${fail} fail`);
452
+ if (fail > 0) process.exitCode = 1;
403
453
 
404
454
 
405
- //# sourceURL=/Users/bytedance/work/gotry-publish/ts/scripts/session-tests.ts
455
+ //# sourceURL=ts/scripts/session-tests.ts