@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
@@ -0,0 +1,491 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const FETCH_TIMEOUT_MS = 8000;
4
+ const DEFAULT_PROVIDERS = [
5
+ {
6
+ id: 'deepseek',
7
+ source_url: 'https://api-docs.deepseek.com/quick_start/pricing',
8
+ baseline_path: 'data/llm-price-baseline-deepseek.json',
9
+ parse: parseDeepseekHtml
10
+ },
11
+ {
12
+ id: 'minimax',
13
+ source_url: 'https://platform.minimax.io/docs/guides/pricing-paygo',
14
+ baseline_path: 'data/llm-price-baseline-minimax.json',
15
+ parse: parseMinimaxHtml
16
+ },
17
+ {
18
+ id: 'openai',
19
+ source_url: 'https://openai.com/api/pricing/',
20
+ baseline_path: 'data/llm-price-baseline-openai.json',
21
+ snapshot_path: 'data/llm-pricing-snapshot-openai.html',
22
+ parse: parseOpenaiHtml
23
+ },
24
+ {
25
+ id: 'anthropic',
26
+ source_url: 'https://www.anthropic.com/pricing',
27
+ baseline_path: 'data/llm-price-baseline-anthropic.json',
28
+ snapshot_path: 'data/llm-pricing-snapshot-anthropic.html',
29
+ parse: parseAnthropicHtml
30
+ }
31
+ ];
32
+ async function fetchWithTimeout(url, timeoutMs) {
33
+ const controller = new AbortController();
34
+ const timer = setTimeout(()=>controller.abort(), timeoutMs);
35
+ try {
36
+ const res = await fetch(url, {
37
+ signal: controller.signal,
38
+ headers: {
39
+ 'User-Agent': 'GoTry-price-drift-watch/1 (issue #49)'
40
+ }
41
+ });
42
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
43
+ return await res.text();
44
+ } finally{
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ function parseDeepseekHtml(html) {
49
+ const rows = [];
50
+ const rowRe = /<tr[^>]*>([\s\S]*?)<\/tr>/g;
51
+ const cellRe = /<td[^>]*>([\s\S]*?)<\/td>/g;
52
+ let tr;
53
+ while((tr = rowRe.exec(html)) !== null){
54
+ const cells = [];
55
+ let m;
56
+ while((m = cellRe.exec(tr[1])) !== null)cells.push(m[1].replace(/<[^>]+>/g, '').trim());
57
+ if (cells.length < 4) continue;
58
+ const model = cells[0];
59
+ if (!/^deepseek-v[34]/.test(model)) continue;
60
+ const miss = Number(cells[1]);
61
+ const hit = Number(cells[2]);
62
+ const out = Number(cells[3]);
63
+ if (!Number.isFinite(miss) || !Number.isFinite(out)) continue;
64
+ rows.push({
65
+ model,
66
+ input_cache_miss_usd_per_1m: miss,
67
+ input_cache_hit_usd_per_1m: Number.isFinite(hit) ? hit : null,
68
+ output_usd_per_1m: out
69
+ });
70
+ }
71
+ return rows.length > 0 ? rows : null;
72
+ }
73
+ function parseMinimaxHtml(html) {
74
+ const rows = [];
75
+ const lineRe = /<p[^>]*>([\s\S]*?)<\/p>/g;
76
+ let p;
77
+ const seen = new Set();
78
+ while((p = lineRe.exec(html)) !== null){
79
+ const text = p[1].replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim();
80
+ const modelMatch = text.match(/(MiniMax-M2(?:\.1)?(?:-highspeed)?|MiniMax-M3)/);
81
+ if (!modelMatch) continue;
82
+ const model = modelMatch[1];
83
+ const numbers = Array.from(text.matchAll(/\$([0-9]+(?:\.[0-9]+)?)\s*\/\s*M\s*tokens?/g)).map((m)=>Number(m[1]));
84
+ if (numbers.length < 2) continue;
85
+ const [miss, out] = numbers;
86
+ const hit = numbers.length >= 3 ? numbers[2] : null;
87
+ if (seen.has(model)) continue;
88
+ seen.add(model);
89
+ rows.push({
90
+ model,
91
+ input_cache_miss_usd_per_1m: miss,
92
+ input_cache_hit_usd_per_1m: hit,
93
+ output_usd_per_1m: out
94
+ });
95
+ }
96
+ return rows.length > 0 ? rows : null;
97
+ }
98
+ export function parseOpenaiHtml(html) {
99
+ const fromNextData = parseOpenaiNextData(html);
100
+ if (fromNextData) return fromNextData;
101
+ return parseOpenaiParagraphFallback(html);
102
+ }
103
+ function parseOpenaiNextData(html) {
104
+ const m = html.match(/<script[^>]*id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);
105
+ if (!m) return null;
106
+ try {
107
+ const data = JSON.parse(m[1]);
108
+ const models = data?.props?.pageProps?.models;
109
+ if (!Array.isArray(models) || models.length === 0) return null;
110
+ const rows = [];
111
+ for (const m of models){
112
+ const name = String(m['name'] ?? m['model'] ?? '').trim();
113
+ if (!name) continue;
114
+ const rawMiss = Number(m['input_cost_per_token'] ?? m['input_per_million'] ?? m['input']);
115
+ const rawOut = Number(m['output_cost_per_token'] ?? m['output_per_million'] ?? m['output']);
116
+ const rawHit = Number(m['cached_input_cost_per_token'] ?? m['cached_input_per_million'] ?? m['cache_read']);
117
+ if (!Number.isFinite(rawMiss) || !Number.isFinite(rawOut)) continue;
118
+ rows.push({
119
+ model: name,
120
+ input_cache_miss_usd_per_1m: rawMiss < 1 ? rawMiss * 1_000_000 : rawMiss,
121
+ input_cache_hit_usd_per_1m: Number.isFinite(rawHit) ? rawHit < 1 ? rawHit * 1_000_000 : rawHit : null,
122
+ output_usd_per_1m: rawOut < 1 ? rawOut * 1_000_000 : rawOut
123
+ });
124
+ }
125
+ return rows.length > 0 ? rows : null;
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ function parseOpenaiParagraphFallback(html) {
131
+ const rows = [];
132
+ const lineRe = /<p[^>]*>([\s\S]*?)<\/p>|<li[^>]*>([\s\S]*?)<\/li>/g;
133
+ let m;
134
+ while((m = lineRe.exec(html)) !== null){
135
+ const text = (m[1] ?? m[2] ?? '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim();
136
+ const modelMatch = text.match(/(gpt-4o(?:-mini)?|gpt-4\.1(?:-mini)?|gpt-5(?:-mini)?|o[1-9](?:-mini|-pro)?)/);
137
+ if (!modelMatch) continue;
138
+ const numbers = Array.from(text.matchAll(/\$([0-9]+(?:\.[0-9]+)?)\s*\/\s*(?:1M|M)/g)).map((x)=>Number(x[1]));
139
+ if (numbers.length < 2) continue;
140
+ const [miss, out] = numbers;
141
+ rows.push({
142
+ model: modelMatch[1],
143
+ input_cache_miss_usd_per_1m: miss,
144
+ input_cache_hit_usd_per_1m: null,
145
+ output_usd_per_1m: out
146
+ });
147
+ }
148
+ return rows.length > 0 ? rows : null;
149
+ }
150
+ function parseAnthropicHtml(html) {
151
+ const fromNextData = parseAnthropicNextData(html);
152
+ if (fromNextData) return fromNextData;
153
+ return parseAnthropicParagraphFallback(html);
154
+ }
155
+ function parseAnthropicNextData(html) {
156
+ const m = html.match(/<script[^>]*id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);
157
+ if (!m) return null;
158
+ try {
159
+ const data = JSON.parse(m[1]);
160
+ const pricing = data?.props?.pageProps?.pricing;
161
+ if (!Array.isArray(pricing) || pricing.length === 0) return null;
162
+ const rows = [];
163
+ for (const p of pricing){
164
+ const name = String(p['model'] ?? p['name'] ?? '').trim();
165
+ if (!name) continue;
166
+ const miss = Number(p['input_cost_per_mtok'] ?? p['input']);
167
+ const out = Number(p['output_cost_per_mtok'] ?? p['output']);
168
+ const hit = Number(p['cache_read_cost_per_mtok'] ?? p['cache_read']);
169
+ if (!Number.isFinite(miss) || !Number.isFinite(out)) continue;
170
+ rows.push({
171
+ model: name,
172
+ input_cache_miss_usd_per_1m: miss,
173
+ input_cache_hit_usd_per_1m: Number.isFinite(hit) ? hit : null,
174
+ output_usd_per_1m: out
175
+ });
176
+ }
177
+ return rows.length > 0 ? rows : null;
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+ function parseAnthropicParagraphFallback(html) {
183
+ const rows = [];
184
+ const lineRe = /<p[^>]*>([\s\S]*?)<\/p>|<li[^>]*>([\s\S]*?)<\/li>/g;
185
+ let m;
186
+ while((m = lineRe.exec(html)) !== null){
187
+ const text = (m[1] ?? m[2] ?? '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim();
188
+ const modelMatch = text.match(/(claude-(?:opus|sonnet|haiku)-[0-9](?:\.[0-9])?(?:-[0-9]+k)?)/);
189
+ if (!modelMatch) continue;
190
+ const numbers = Array.from(text.matchAll(/\$([0-9]+(?:\.[0-9]+)?)\s*\/\s*(?:1M|M|MT)/g)).map((x)=>Number(x[1]));
191
+ if (numbers.length < 2) continue;
192
+ const [miss, out] = numbers;
193
+ rows.push({
194
+ model: modelMatch[1],
195
+ input_cache_miss_usd_per_1m: miss,
196
+ input_cache_hit_usd_per_1m: null,
197
+ output_usd_per_1m: out
198
+ });
199
+ }
200
+ return rows.length > 0 ? rows : null;
201
+ }
202
+ function diffRows(baseline, current) {
203
+ const baseMap = new Map(baseline.map((r)=>[
204
+ r.model,
205
+ r
206
+ ]));
207
+ const currMap = new Map(current.map((r)=>[
208
+ r.model,
209
+ r
210
+ ]));
211
+ const drift = [];
212
+ for (const [model, curr] of currMap){
213
+ const base = baseMap.get(model);
214
+ if (!base) {
215
+ drift.push({
216
+ model,
217
+ field: 'input_cache_miss_usd_per_1m',
218
+ from: null,
219
+ to: curr.input_cache_miss_usd_per_1m,
220
+ direction: 'new'
221
+ });
222
+ continue;
223
+ }
224
+ if (base.input_cache_miss_usd_per_1m !== curr.input_cache_miss_usd_per_1m) {
225
+ drift.push({
226
+ model,
227
+ field: 'input_cache_miss_usd_per_1m',
228
+ from: base.input_cache_miss_usd_per_1m,
229
+ to: curr.input_cache_miss_usd_per_1m,
230
+ direction: curr.input_cache_miss_usd_per_1m > base.input_cache_miss_usd_per_1m ? 'up' : 'down'
231
+ });
232
+ }
233
+ if (base.input_cache_hit_usd_per_1m !== curr.input_cache_hit_usd_per_1m) {
234
+ drift.push({
235
+ model,
236
+ field: 'input_cache_hit_usd_per_1m',
237
+ from: base.input_cache_hit_usd_per_1m,
238
+ to: curr.input_cache_hit_usd_per_1m,
239
+ direction: (curr.input_cache_hit_usd_per_1m ?? 0) > (base.input_cache_hit_usd_per_1m ?? 0) ? 'up' : 'down'
240
+ });
241
+ }
242
+ if (base.output_usd_per_1m !== curr.output_usd_per_1m) {
243
+ drift.push({
244
+ model,
245
+ field: 'output_usd_per_1m',
246
+ from: base.output_usd_per_1m,
247
+ to: curr.output_usd_per_1m,
248
+ direction: curr.output_usd_per_1m > base.output_usd_per_1m ? 'up' : 'down'
249
+ });
250
+ }
251
+ }
252
+ for (const [model, base] of baseMap){
253
+ if (!currMap.has(model)) {
254
+ drift.push({
255
+ model,
256
+ field: 'input_cache_miss_usd_per_1m',
257
+ from: base.input_cache_miss_usd_per_1m,
258
+ to: null,
259
+ direction: 'removed'
260
+ });
261
+ }
262
+ }
263
+ return drift;
264
+ }
265
+ function loadBaseline(path) {
266
+ if (!existsSync(path)) return null;
267
+ try {
268
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
269
+ if (raw['schema_version'] !== 'gotry_llm_price_baseline_v1') throw new Error(`baseline ${path} schema_version must be gotry_llm_price_baseline_v1, got ${String(raw['schema_version'])}`);
270
+ const rows = raw['rows'];
271
+ if (!Array.isArray(rows)) throw new Error(`baseline ${path} rows must be array`);
272
+ return raw;
273
+ } catch (error) {
274
+ return null;
275
+ }
276
+ }
277
+ function loadSnapshot(path) {
278
+ if (!existsSync(path)) return null;
279
+ try {
280
+ const text = readFileSync(path, 'utf8');
281
+ return text.length > 0 ? text : null;
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function writeSnapshot(path, html) {
287
+ writeFileSync(path, html, 'utf8');
288
+ }
289
+ function writeBaseline(path, rows, sourceUrl) {
290
+ const payload = {
291
+ schema_version: 'gotry_llm_price_baseline_v1',
292
+ as_of: new Date().toISOString(),
293
+ source_url: sourceUrl,
294
+ rows
295
+ };
296
+ writeFileSync(path, JSON.stringify(payload, null, 2) + '\n', 'utf8');
297
+ }
298
+ export async function runPriceDriftWatch(options) {
299
+ const observedAt = options.observedAt().toISOString();
300
+ const providers = {};
301
+ const providerSpecs = options.providers ?? DEFAULT_PROVIDERS;
302
+ const fetcher = options.fetcher ?? ((url)=>fetchWithTimeout(url, FETCH_TIMEOUT_MS));
303
+ for (const spec of providerSpecs){
304
+ const baseline = loadBaseline(spec.baseline_path);
305
+ const snapshot = spec.snapshot_path ? loadSnapshot(spec.snapshot_path) : null;
306
+ if (options.mode === 'offline') {
307
+ if (!baseline) {
308
+ providers[spec.id] = {
309
+ status: 'skipped_no_baseline',
310
+ source_url: spec.source_url,
311
+ reason: `no baseline fixture at ${spec.baseline_path}; run --fetch once to seed`
312
+ };
313
+ continue;
314
+ }
315
+ if (snapshot === null) {
316
+ if (spec.snapshot_path) {
317
+ providers[spec.id] = {
318
+ status: 'skipped_no_snapshot',
319
+ source_url: spec.source_url,
320
+ reason: `no snapshot at ${spec.snapshot_path}; run --fetch once to seed (or: founder manually save official pricing page HTML as the snapshot)`
321
+ };
322
+ } else {
323
+ providers[spec.id] = {
324
+ status: 'unchanged',
325
+ source_url: spec.source_url,
326
+ reason: 'offline mode; baseline present, run --fetch to compare against current official prices'
327
+ };
328
+ }
329
+ continue;
330
+ }
331
+ const currentFromSnapshot = spec.parse(snapshot);
332
+ if (!currentFromSnapshot) {
333
+ providers[spec.id] = {
334
+ status: 'skipped_parse_error',
335
+ source_url: spec.source_url,
336
+ reason: 'snapshot present but parser failed to extract rows; refresh snapshot or fix parser'
337
+ };
338
+ continue;
339
+ }
340
+ const driftFromSnapshot = diffRows(baseline.rows, currentFromSnapshot) ?? [];
341
+ if (driftFromSnapshot.length === 0) {
342
+ providers[spec.id] = {
343
+ status: 'unchanged',
344
+ source_url: spec.source_url,
345
+ reason: 'offline; baseline == snapshot-derived current'
346
+ };
347
+ } else {
348
+ providers[spec.id] = {
349
+ status: 'drift',
350
+ source_url: spec.source_url,
351
+ fetched_at: observedAt,
352
+ drift: driftFromSnapshot,
353
+ reason: 'offline diff baseline vs snapshot'
354
+ };
355
+ }
356
+ continue;
357
+ }
358
+ let html;
359
+ try {
360
+ html = await fetcher(spec.source_url);
361
+ } catch (error) {
362
+ providers[spec.id] = {
363
+ status: 'skipped_no_network',
364
+ source_url: spec.source_url,
365
+ reason: `fetch failed: ${error.message}`
366
+ };
367
+ continue;
368
+ }
369
+ if (spec.snapshot_path) writeSnapshot(spec.snapshot_path, html);
370
+ const current = spec.parse(html);
371
+ if (!current) {
372
+ providers[spec.id] = {
373
+ status: 'skipped_parse_error',
374
+ source_url: spec.source_url,
375
+ reason: 'parser did not extract any rows; snapshot was written anyway (manual review of fixture + parser required)'
376
+ };
377
+ continue;
378
+ }
379
+ if (!baseline) {
380
+ writeBaseline(spec.baseline_path, current, spec.source_url);
381
+ providers[spec.id] = {
382
+ status: 'fetched_no_table_entry',
383
+ source_url: spec.source_url,
384
+ fetched_at: observedAt,
385
+ reason: 'first fetch; baseline fixture written; no current llm-price-table.json entry (add a PR if you want this provider in nightly-evidence cost math)'
386
+ };
387
+ continue;
388
+ }
389
+ const drift = diffRows(baseline.rows, current) ?? [];
390
+ if (drift.length === 0) {
391
+ providers[spec.id] = {
392
+ status: 'unchanged',
393
+ source_url: spec.source_url,
394
+ fetched_at: observedAt
395
+ };
396
+ } else {
397
+ providers[spec.id] = {
398
+ status: 'drift',
399
+ source_url: spec.source_url,
400
+ fetched_at: observedAt,
401
+ drift
402
+ };
403
+ }
404
+ }
405
+ const suggestedPrParagraph = renderSuggestedPr(providers, observedAt);
406
+ return {
407
+ schema_version: 'gotry_llm_price_drift_v1',
408
+ observed_at: observedAt,
409
+ mode: options.mode,
410
+ providers,
411
+ suggested_pr_paragraph: suggestedPrParagraph
412
+ };
413
+ }
414
+ function renderSuggestedPr(providers, observedAt) {
415
+ const lines = [
416
+ `## LLM 价格漂移监测(${observedAt})`
417
+ ];
418
+ for (const [id, report] of Object.entries(providers)){
419
+ if (report.status === 'drift' && report.drift && report.drift.length > 0) {
420
+ const driftItems = report.drift;
421
+ lines.push(`- **${id}**(${report.source_url}):${driftItems.length} 项变动`);
422
+ for (const d of driftItems){
423
+ lines.push(` - \`${d.model}\` ${d.field}: ${d.from ?? '∅'} → ${d.to ?? '∅'} (${d.direction})`);
424
+ }
425
+ lines.push(' - 建议:按 ADR-11 复核,人工提 PR 改 `data/llm-price-table.json`。');
426
+ } else if (report.status === 'unchanged') {
427
+ lines.push(`- **${id}**:无变动`);
428
+ } else if (report.status === 'fetched_no_table_entry') {
429
+ lines.push(`- **${id}**:首次 fetch,baseline 已写入(${report.reason ?? ''})`);
430
+ } else {
431
+ lines.push(`- **${id}** [${report.status}]:${report.reason ?? ''}`);
432
+ }
433
+ }
434
+ lines.push('');
435
+ lines.push('纪律:本监测只产 diff,不自动改价表;价格调整必须人工 PR,符合 ADR-11 「peak only-high-not-low」。');
436
+ return lines.join('\n');
437
+ }
438
+ function formatMarkdown(report) {
439
+ const lines = [
440
+ `# LLM 价格漂移监测(${report.observed_at},mode=${report.mode})`,
441
+ ''
442
+ ];
443
+ for (const [id, r] of Object.entries(report.providers)){
444
+ lines.push(`## ${id} [${r.status}]`);
445
+ lines.push(`source: ${r.source_url}`);
446
+ if (r.reason) lines.push(`reason: ${r.reason}`);
447
+ if (r.drift && r.drift.length > 0) {
448
+ lines.push('| model | field | from | to | direction |');
449
+ lines.push('|---|---|---|---|---|');
450
+ for (const d of r.drift)lines.push(`| \`${d.model}\` | ${d.field} | ${d.from ?? '∅'} | ${d.to ?? '∅'} | ${d.direction} |`);
451
+ }
452
+ lines.push('');
453
+ }
454
+ lines.push('---');
455
+ lines.push('');
456
+ lines.push(report.suggested_pr_paragraph);
457
+ return lines.join('\n');
458
+ }
459
+ function arg(name) {
460
+ const index = process.argv.indexOf(name);
461
+ return index >= 0 ? process.argv[index + 1] : undefined;
462
+ }
463
+ async function main() {
464
+ const asJson = (arg('--format') ?? 'markdown') === 'json';
465
+ const mode = process.argv.includes('--fetch') ? 'fetch' : 'offline';
466
+ mkdirSync('gotry-state', {
467
+ recursive: true
468
+ });
469
+ try {
470
+ const report = await runPriceDriftWatch({
471
+ mode,
472
+ observedAt: ()=>new Date()
473
+ });
474
+ if (asJson) console.log(JSON.stringify(report, null, 2));
475
+ else console.log(formatMarkdown(report));
476
+ } catch (error) {
477
+ const payload = {
478
+ state: 'error',
479
+ error: error.message
480
+ };
481
+ if (asJson) console.log(JSON.stringify(payload, null, 2));
482
+ else console.error(`price-drift-watch error: ${error.message}`);
483
+ process.exitCode = 1;
484
+ }
485
+ }
486
+ if (process.argv[1]?.endsWith('price-drift-watch.ts')) {
487
+ await main();
488
+ }
489
+
490
+
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/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/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/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/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/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/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/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/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/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/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/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/ts/scripts/session-login.ts
28
+ //# sourceURL=ts/scripts/session-login.ts