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

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 (142) hide show
  1. package/README.md +26 -20
  2. package/README.zh-CN.md +27 -21
  3. package/bin/gotry-bootstrap.js +331 -3
  4. package/cordis.gotry-patch.yml +10 -0
  5. package/data/airline-airports.json +39 -0
  6. package/dist/capabilities/agent-reach-deep.js +1 -1
  7. package/dist/capabilities/agent-reach.js +1 -1
  8. package/dist/capabilities/anything.js +1 -1
  9. package/dist/capabilities/artifacts.js +217 -0
  10. package/dist/capabilities/effect.js +293 -0
  11. package/dist/capabilities/fact-log.js +41 -0
  12. package/dist/capabilities/flyai.js +1 -1
  13. package/dist/capabilities/hbcli.js +43 -1
  14. package/dist/capabilities/incident-log.js +1 -1
  15. package/dist/capabilities/opensky.js +1 -1
  16. package/dist/capabilities/resilience.js +90 -0
  17. package/dist/capabilities/session/action-cache.js +1 -1
  18. package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
  19. package/dist/capabilities/session/adapters/meituan-local.js +1 -1
  20. package/dist/capabilities/session/benchmark.js +2 -1
  21. package/dist/capabilities/session/extension-bridge.js +345 -0
  22. package/dist/capabilities/session/extension-channel.js +93 -0
  23. package/dist/capabilities/session/extract.js +1 -1
  24. package/dist/capabilities/session/health-watch.js +216 -0
  25. package/dist/capabilities/session/read-guard.js +1 -1
  26. package/dist/capabilities/session/transport.js +1 -1
  27. package/dist/capabilities/session/wizard.js +377 -0
  28. package/dist/capabilities/session-consent.js +1 -1
  29. package/dist/capabilities/session-login.js +77 -1
  30. package/dist/capabilities/session-search.js +69 -2
  31. package/dist/capabilities/weather.js +1 -1
  32. package/dist/scripts/action-cache-tests.js +1 -1
  33. package/dist/scripts/agent-reach-deep-tests.js +1 -1
  34. package/dist/scripts/agent-reach-tests.js +1 -1
  35. package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
  36. package/dist/scripts/anything-tests.js +1 -1
  37. package/dist/scripts/async-collect.js +1 -1
  38. package/dist/scripts/booking-saga-tests.js +187 -0
  39. package/dist/scripts/bootstrap-tests.js +49 -3
  40. package/dist/scripts/build-changelog.js +279 -0
  41. package/dist/scripts/changelog-tests.js +165 -0
  42. package/dist/scripts/companion-tests.js +1 -1
  43. package/dist/scripts/diff-test.js +1 -1
  44. package/dist/scripts/effect-tests.js +474 -0
  45. package/dist/scripts/engine-run.js +1 -1
  46. package/dist/scripts/engine-tests.js +1 -1
  47. package/dist/scripts/extension-tests.js +468 -0
  48. package/dist/scripts/fact-gate-tests.js +318 -0
  49. package/dist/scripts/flyai-tests.js +1 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +180 -0
  51. package/dist/scripts/hbcli-tests.js +1 -1
  52. package/dist/scripts/health-watch-cli.js +51 -0
  53. package/dist/scripts/i18n-tests.js +1 -1
  54. package/dist/scripts/incident-tests.js +1 -1
  55. package/dist/scripts/journey-tests.js +1 -1
  56. package/dist/scripts/ledger-tests.js +1 -1
  57. package/dist/scripts/ledger-workflow-crash.js +1 -1
  58. package/dist/scripts/memory-capture-tests.js +1 -1
  59. package/dist/scripts/memory-decay-tests.js +1 -1
  60. package/dist/scripts/memory-metrics.js +1 -1
  61. package/dist/scripts/memory-value-report.js +1 -1
  62. package/dist/scripts/nightly-evidence-tests.js +186 -0
  63. package/dist/scripts/nightly-evidence.js +289 -0
  64. package/dist/scripts/nudge-digest.js +1 -1
  65. package/dist/scripts/onboarding-tests.js +218 -0
  66. package/dist/scripts/opensky-check.js +1 -1
  67. package/dist/scripts/opensky-tests.js +1 -1
  68. package/dist/scripts/price-drift-tests.js +441 -0
  69. package/dist/scripts/price-drift-watch.js +491 -0
  70. package/dist/scripts/probe-poi-tests.js +1 -1
  71. package/dist/scripts/product-metrics.js +1 -1
  72. package/dist/scripts/publish-preverify.js +1 -1
  73. package/dist/scripts/realtime-pricing-tests.js +1 -1
  74. package/dist/scripts/replay-async.js +1 -1
  75. package/dist/scripts/replay-real.js +1 -1
  76. package/dist/scripts/replay.js +1 -1
  77. package/dist/scripts/session-attach-diagnose.js +1 -1
  78. package/dist/scripts/session-attach-poc.js +1 -1
  79. package/dist/scripts/session-benchmark.js +1 -1
  80. package/dist/scripts/session-extract-tests.js +1 -1
  81. package/dist/scripts/session-login.js +1 -1
  82. package/dist/scripts/session-tests.js +1 -1
  83. package/dist/scripts/skeleton-check.js +1 -1
  84. package/dist/scripts/skeleton-integration-test.js +1 -1
  85. package/dist/scripts/skills-contract-tests.js +1 -1
  86. package/dist/scripts/smoke.js +159 -1
  87. package/dist/scripts/state-cli-tests.js +1 -1
  88. package/dist/scripts/state-cli.js +1 -1
  89. package/dist/scripts/time-eval-tests.js +1 -1
  90. package/dist/scripts/travel-timeline-tests.js +1 -1
  91. package/dist/scripts/unified-tests.js +1 -1
  92. package/dist/scripts/weather-tests.js +1 -1
  93. package/dist/scripts/wizard-bootstrap.js +32 -0
  94. package/dist/scripts/z3-race-tests.js +1 -1
  95. package/dist/src/artifact-gate.js +310 -0
  96. package/dist/src/bookable-facts.js +349 -0
  97. package/dist/src/booking-saga.js +153 -0
  98. package/dist/src/bridge.js +1 -1
  99. package/dist/src/companions.js +1 -1
  100. package/dist/src/contracts.js +1 -1
  101. package/dist/src/dsh-llm.js +31 -7
  102. package/dist/src/engine.js +1 -1
  103. package/dist/src/i18n.js +1 -1
  104. package/dist/src/index.js +322 -46
  105. package/dist/src/journey.js +1 -1
  106. package/dist/src/loop.js +1 -1
  107. package/dist/src/memory-capture.js +1 -1
  108. package/dist/src/memory-decay.js +1 -1
  109. package/dist/src/memory-utility.js +1 -1
  110. package/dist/src/mock-llm.js +1 -1
  111. package/dist/src/model.js +1 -1
  112. package/dist/src/realtime-pricing.js +14 -7
  113. package/dist/src/slot-spec.js +1 -1
  114. package/dist/src/state-ledger.js +1 -1
  115. package/dist/src/time-anchor.js +1 -1
  116. package/dist/src/tool-packet.js +1 -1
  117. package/dist/src/travel-slots.js +1 -1
  118. package/dist/src/travel-timeline.js +1 -1
  119. package/dist/src/unified.js +1 -1
  120. package/dist/src/wish-pool.js +1 -1
  121. package/dist/src/z3-shared.js +1 -1
  122. package/extension/README.md +26 -0
  123. package/extension/background.js +160 -0
  124. package/extension/content-bridge.js +41 -0
  125. package/extension/content-main.js +62 -0
  126. package/extension/manifest.json +32 -0
  127. package/package.json +8 -2
  128. package/ts/capabilities/artifacts.ts +235 -0
  129. package/ts/capabilities/fact-log.ts +54 -0
  130. package/ts/capabilities/hbcli.ts +46 -0
  131. package/ts/capabilities/session/benchmark.ts +4 -1
  132. package/ts/capabilities/session/extension-bridge.ts +354 -0
  133. package/ts/capabilities/session/extension-channel.ts +92 -0
  134. package/ts/capabilities/session/health-watch.ts +257 -0
  135. package/ts/capabilities/session/wizard.ts +282 -0
  136. package/ts/capabilities/session-login.ts +53 -2
  137. package/ts/capabilities/session-search.ts +68 -3
  138. package/ts/src/artifact-gate.ts +365 -0
  139. package/ts/src/bookable-facts.ts +557 -0
  140. package/ts/src/booking-saga.ts +133 -0
  141. package/ts/src/dsh-llm.ts +40 -3
  142. package/ts/src/index.ts +230 -28
package/dist/src/index.js CHANGED
@@ -5,7 +5,6 @@ import { ensureStateDir, recordLatency } from './bridge.js';
5
5
  import { segmentsFromCandidate, solveChoiceSegment } from './unified.js';
6
6
  import { checkConnectivity } from '../scripts/skeleton-check.js';
7
7
  import { parseCandidate, parseRequest } from './model.js';
8
- import { searchHotels as hbcliSearchHotels } from '../capabilities/hbcli.js';
9
8
  import { installProcessGuards, guardToolExecute } from '../capabilities/incident-log.js';
10
9
  import { interpretArgs } from './tool-packet.js';
11
10
  import { projectUtility } from './memory-utility.js';
@@ -14,15 +13,28 @@ import { resolveTimelineDate } from './travel-timeline.js';
14
13
  import { ensureLedger, readCompanionsWithFallback, readMotivationWithFallback, readTripsWithFallback, readWishPoolWithFallback } from './state-ledger.js';
15
14
  import { buildTimeAnchor } from './time-anchor.js';
16
15
  import { resolveSlotDate } from './slot-spec.js';
17
- import { geocodePlace, getForecast, getClimate, wmoLabel } from '../capabilities/weather.js';
18
- import { verifyFlight } from '../capabilities/opensky.js';
16
+ import { wmoLabel } from '../capabilities/weather.js';
19
17
  import { anythingSearch } from '../capabilities/anything.js';
20
18
  import { readUrl, reach, reachStatus } from '../capabilities/agent-reach.js';
21
19
  import { videoSubtitle, githubSearch } from '../capabilities/agent-reach-deep.js';
22
- import { flyaiSearch } from '../capabilities/flyai.js';
23
- import { sessionFlightSearch } from '../capabilities/session-search.js';
24
20
  import { sessionLogin } from '../capabilities/session-login.js';
25
21
  import { createConsentGate, approvalFromContext } from '../capabilities/session-consent.js';
22
+ import { listArtifacts, readArtifact } from '../capabilities/artifacts.js';
23
+ import { interpretEffect, declinedObservation } from '../capabilities/effect.js';
24
+ import { appendFacts, loadFactRegistry } from '../capabilities/fact-log.js';
25
+ import { factsFromFlyai, factsFromSession } from './bookable-facts.js';
26
+ import { gateArtifact } from './artifact-gate.js';
27
+ let airlineAirportMapCache = null;
28
+ async function loadAirlineAirportMap() {
29
+ if (airlineAirportMapCache) return airlineAirportMapCache;
30
+ try {
31
+ const { readFile } = await import('node:fs/promises');
32
+ airlineAirportMapCache = JSON.parse(await readFile(join(import.meta.dirname, '..', '..', 'data', 'airline-airports.json'), 'utf-8'));
33
+ return airlineAirportMapCache;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
26
38
  export const name = 'gotry-tools';
27
39
  export const inject = [
28
40
  'tools',
@@ -548,16 +560,20 @@ export function apply(ctx, config) {
548
560
  if (r.raw !== r.date) dateNotes.push(`slot-resolved: ${r.raw} → ${r.date}`);
549
561
  return r.date;
550
562
  };
551
- const resp = await hbcliSearchHotels({
552
- destination: q.destination,
553
- checkIn: resolveDate(q.checkIn),
554
- checkOut: resolveDate(q.checkOut),
555
- adults: q.adults
556
- }, {
557
- hbcliBin: config.hbcliBin,
558
- timeoutMs: config.timeoutMs,
559
- fallbackPath
563
+ const itp = await interpretEffect({
564
+ effect: 'HBCLI_HOTEL_SEARCH',
565
+ params: {
566
+ destination: q.destination,
567
+ checkIn: resolveDate(q.checkIn),
568
+ checkOut: resolveDate(q.checkOut),
569
+ adults: q.adults,
570
+ hbcliBin: config.hbcliBin,
571
+ timeoutMs: config.timeoutMs,
572
+ fallbackPath
573
+ }
560
574
  });
575
+ if (!itp.result) return declinedObservation('HBCLI_HOTEL_SEARCH', itp.trace);
576
+ const resp = itp.result;
561
577
  const dir = await ensureStateDir(config.stateRoot);
562
578
  const isLive = resp.via === 'hbcli-realtime';
563
579
  const evidence = isLive ? resp.evidence : '[静态包:估算]';
@@ -671,7 +687,14 @@ export function apply(ctx, config) {
671
687
  let placeLabel = q.place ?? `${q.lat},${q.lng}`;
672
688
  if (lat === undefined || lng === undefined) {
673
689
  if (!q.place) throw new Error('gotry_weather_check requires place name or lat/lng');
674
- const geo = await geocodePlace(q.place);
690
+ const geoItp = await interpretEffect({
691
+ effect: 'WEATHER_GEOCODE',
692
+ params: {
693
+ name: q.place
694
+ }
695
+ });
696
+ if (!geoItp.result) return declinedObservation('WEATHER_GEOCODE', geoItp.trace);
697
+ const geo = geoItp.result;
675
698
  if (!geo.ok || geo.results.length === 0) {
676
699
  return JSON.parse(JSON.stringify({
677
700
  ok: false,
@@ -685,15 +708,23 @@ export function apply(ctx, config) {
685
708
  placeLabel = `${hit.name}(${hit.admin1 ?? hit.country ?? ''})`;
686
709
  }
687
710
  const isClimate = q.mode === 'climate' || q.month !== undefined && q.mode !== 'forecast';
688
- const r = isClimate ? await getClimate({
689
- latitude: lat,
690
- longitude: lng
691
- }, q.month ?? new Date().getMonth() + 1) : await getForecast({
692
- latitude: lat,
693
- longitude: lng
694
- }, {
695
- days: q.days
711
+ const wxItp = isClimate ? await interpretEffect({
712
+ effect: 'WEATHER_CLIMATE',
713
+ params: {
714
+ latitude: lat,
715
+ longitude: lng,
716
+ month: q.month ?? new Date().getMonth() + 1
717
+ }
718
+ }) : await interpretEffect({
719
+ effect: 'WEATHER_FORECAST',
720
+ params: {
721
+ latitude: lat,
722
+ longitude: lng,
723
+ days: q.days
724
+ }
696
725
  });
726
+ if (!wxItp.result) return declinedObservation(isClimate ? 'WEATHER_CLIMATE' : 'WEATHER_FORECAST', wxItp.trace);
727
+ const r = wxItp.result;
697
728
  const dir = await ensureStateDir(config.stateRoot);
698
729
  await recordLatency(join(dir, 'bridge-latency.jsonl'), Date.now() - started, `weather:${r.via}`).catch(()=>{});
699
730
  const dailyLines = (r.daily ?? []).slice(0, 7).map((d)=>`${d.date} ${d.tempMinC.toFixed(0)}–${d.tempMaxC.toFixed(0)}°C ${wmoLabel(d.weatherCode)}${d.precipProbMaxPct !== null ? ` 降水概率${d.precipProbMaxPct}%` : ''}`);
@@ -761,11 +792,16 @@ export function apply(ctx, config) {
761
792
  summary: 'callsign 必填'
762
793
  }));
763
794
  }
764
- const r = await verifyFlight({
765
- callsign: q.callsign,
766
- airport: q.airport,
767
- timeoutMs: q.timeoutMs
795
+ const itp = await interpretEffect({
796
+ effect: 'OPENSKY_FLIGHT_VERIFY',
797
+ params: {
798
+ callsign: q.callsign,
799
+ airport: q.airport,
800
+ timeoutMs: q.timeoutMs
801
+ }
768
802
  });
803
+ if (!itp.result) return declinedObservation('OPENSKY_FLIGHT_VERIFY', itp.trace);
804
+ const r = itp.result;
769
805
  const dir = await ensureStateDir(config.stateRoot);
770
806
  await recordLatency(join(dir, 'bridge-latency.jsonl'), Date.now() - started, `flight_verify:${r.via}`).catch(()=>{});
771
807
  const summary = r.verdict === 'observed' ? `${r.callsign} 当前 ADS-B 观测命中 (${r.hits?.length ?? 0} 架)${r.airport ? ` 在 ${r.airport}` : ''}\n${r.evidence}` : r.verdict === 'not_observed' ? `${r.callsign} 当前观测列表未见(ADS-B 覆盖有限,不否定该航班存在)\n${r.evidence}` : `${r.callsign} OpenSky 不可用:${r.error}\n${r.evidence}`;
@@ -841,13 +877,18 @@ export function apply(ctx, config) {
841
877
  };
842
878
  }
843
879
  }
844
- const r = await flyaiSearch({
845
- kind: 'hotel',
846
- destName: dest,
847
- checkInDate: q.checkIn,
848
- checkOutDate: q.checkOut,
849
- keyWords: q.keyWords
880
+ const itp = await interpretEffect({
881
+ effect: 'FLYAI_SEARCH',
882
+ params: {
883
+ kind: 'hotel',
884
+ destName: dest,
885
+ checkInDate: q.checkIn,
886
+ checkOutDate: q.checkOut,
887
+ keyWords: q.keyWords
888
+ }
850
889
  });
890
+ if (!itp.result) return declinedObservation('FLYAI_SEARCH', itp.trace);
891
+ const r = itp.result;
851
892
  const top = (r.hotels ?? []).slice(0, 8).map((o)=>`${o.name}${o.star ? `(${o.star})` : ''} ${o.priceRaw ?? '价待询'}${o.poi ? ` · ${o.poi}` : ''}`);
852
893
  const summary = r.verdict === 'hit' ? `${dest} 酒店(飞猪官方只读)前 ${top.length} 家(价格多为打码,真实价以 jumpUrl 为准):\n${top.join('\n')}\n${r.evidence}` : `${dest} 酒店无结果或失败:${r.error ?? 'miss'} ${r.evidence}`;
853
894
  return JSON.parse(JSON.stringify({
@@ -872,12 +913,24 @@ export function apply(ctx, config) {
872
913
  summary: `未发起查询:日期 ${q.date} 已是过去(今天 ${todayYmd}),过去不存在在售机/火车票。` + `多为用户时间表达未带年份所致——向用户确认年份(或按未来最近的同月日修正)后再查。`
873
914
  }));
874
915
  }
875
- const r = await flyaiSearch({
916
+ const itp = await interpretEffect({
917
+ effect: 'FLYAI_SEARCH',
918
+ params: {
919
+ kind,
920
+ origin: q.from,
921
+ destination: q.to,
922
+ depDate: q.date
923
+ }
924
+ });
925
+ if (!itp.result) return declinedObservation('FLYAI_SEARCH', itp.trace);
926
+ const r = itp.result;
927
+ const avMap = await loadAirlineAirportMap();
928
+ await appendFacts(config.stateRoot ?? '.', factsFromFlyai({
876
929
  kind,
877
930
  origin: q.from,
878
931
  destination: q.to,
879
- depDate: q.date
880
- });
932
+ date: q.date
933
+ }, r, new Date().toISOString(), avMap?.city_alias));
881
934
  const top = (r.options ?? []).slice(0, 8).map((o)=>`${o.no} ${o.name} ${o.depDateTime.slice(11, 16)}→${o.arrDateTime.slice(11, 16)} ¥${o.price}`);
882
935
  const label = kind === 'flight' ? '机票' : '火车票';
883
936
  const summary = r.verdict === 'hit' ? `${q.from}→${q.to} ${q.date} ${label}(飞猪官方只读)前 ${top.length} 条:\n${top.join('\n')}\n${r.evidence}` : r.verdict === 'miss' ? `${q.from}→${q.to} ${q.date} ${label}官方通道正常返回 0 条(常见原因:航线未开放/当日售罄)。${r.evidence}` : `${q.from}→${q.to} ${q.date} ${label}检索失败(可能限流/网络):${r.error ?? ''} ${r.evidence}`;
@@ -911,7 +964,7 @@ export function apply(ctx, config) {
911
964
  }));
912
965
  registerGuarded(defineTool({
913
966
  name: 'gotry_session_login',
914
- description: 'Productized login bootstrap for the account session channel (call this when gotry_session_search returns needs-login — the user never needs a terminal). ' + 'AUTO-DETECTION FIRST: it reads ticket-cookie NAMES before anything else — if the user already logged in (on the external site) it confirms instantly WITHOUT opening any page. ' + 'OPENS the site login entry in the USER\'S OWN Chrome and waits for the user to finish logging in on the external site. ' + 'GoTry NEVER collects, stores, or transmits credentials: no passwords, no SMS codes, no cookie values — it only checks the boolean fact "already logged in" (reads cookie NAMES only, zero values). ' + 'verdict logged-in (tickets detected) | pending (login tab opened, user not done yet — offer to re-check later) | needs-attach (one-time Chrome remote-debugging switch instructions). ' + 'Evidence [会话:<site>-login@ts].',
967
+ description: 'Productized login bootstrap for the account session channel (call this when gotry_session_search returns needs-login — the user never needs a terminal). ' + 'AUTO-DETECTION FIRST: it reads ticket-cookie NAMES before anything else — if the user already logged in (on the external site) it confirms instantly WITHOUT opening any page. ' + 'OPENS the site login entry in the USER\'S OWN Chrome (foreground tab, left open for the user) and waits for the user to finish logging in on the external site. ' + 'GoTry NEVER collects, stores, or transmits credentials: no passwords, no SMS codes, no cookie values — it only checks the boolean fact "already logged in" (reads cookie NAMES only, zero values). ' + 'Transport: the GoTry Session Bridge browser extension (one-time install, ZERO Chrome system dialogs). ' + 'verdict logged-in (tickets detected) | pending (login tab opened, user not done yet — offer to re-check later) | needs-extension (one-time extension install: npx gotry setup, then chrome://extensions → load unpacked ~/.gotry/extension). ' + 'Evidence [会话:<site>-login@ts].',
915
968
  parameters: {
916
969
  query: {
917
970
  type: 'json',
@@ -935,7 +988,7 @@ export function apply(ctx, config) {
935
988
  const r = await sessionLogin({
936
989
  waitMs: typeof q?.waitSeconds === 'number' ? q.waitSeconds * 1000 : undefined
937
990
  });
938
- const summary = r.verdict === 'logged-in' ? `${r.site} 登录完成确认(票据 cookie 名已检出,只读名字)。说明:登录是在携程官网、用你自己的浏览器完成的——gotry 全程未接触任何密码/验证码/cookie 值。现在可以继续会话检索了。${r.evidence}` : r.verdict === 'pending' ? `登录入口已在你的 Chrome 打开;请在弹出的标签页里正常登录携程(登录由你在官网完成,不属于 gotry)。完成后说一声"继续",我再确认。gotry 只检查"是否已登录",永不收集你的账号信息。${r.evidence}` : r.verdict === 'needs-attach' ? `需要一次性开启你 Chrome 的远程调试开关:在你的 Chrome 地址栏打开 chrome://inspect/#remote-debugging 并打开开关,然后说一声"重试"。${r.evidence}` : `登录引导未完成:${r.error ?? '未知原因'} ${r.evidence}`;
991
+ const summary = r.verdict === 'logged-in' ? `${r.site} 登录完成确认(票据 cookie 名已检出,只读名字)。说明:登录是在携程官网、用你自己的浏览器完成的——gotry 全程未接触任何密码/验证码/cookie 值。现在可以继续会话检索了。${r.evidence}` : r.verdict === 'pending' ? `登录入口已在你的 Chrome 打开;请在弹出的标签页里正常登录携程(登录由你在官网完成,不属于 gotry)。完成后说一声"继续",我再确认。gotry 只检查"是否已登录",永不收集你的账号信息。${r.evidence}` : r.verdict === 'needs-extension' ? `需要一次性安装 GoTry Session Bridge 浏览器扩展(约 30 秒,装完零弹窗):跑 npx gotry setup 拿安装指引,在 chrome://extensions 开发者模式「加载已解压的扩展程序」指向 ~/.gotry/extension,装好后说一声"重试"。${r.evidence}` : r.verdict === 'needs-attach' ? `cdp 车道需要一次性开启你 Chrome 的远程调试开关:在你的 Chrome 地址栏打开 chrome://inspect/#remote-debugging 并打开开关,然后说一声"重试"(默认走扩展车道,无需此步)。${r.evidence}` : `登录引导未完成:${r.error ?? '未知原因'} ${r.evidence}`;
939
992
  return JSON.parse(JSON.stringify({
940
993
  ...r,
941
994
  summary
@@ -964,7 +1017,7 @@ export function apply(ctx, config) {
964
1017
  }));
965
1018
  registerGuarded(defineTool({
966
1019
  name: 'gotry_session_search',
967
- description: 'Search on the USER\'S OWN logged-in browser session (Ctrip flights today; the account channel, not an anonymous instance). ' + 'Consent gate: the FIRST call in a session asks the user via the runtime approval card; once granted it holds for the session, a refusal revokes it for the session (no repeat prompting). ' + 'ReadGuard = physically read-only (write requests aborted at network layer; agent NEVER touches credentials/captcha; on captcha it stops and returns challenged). ' + 'Currently ctrip-flight: sniffs the site search API for structured options. Evidence [会话:ctrip-flight@ts]. ' + 'verdict needs-login = call gotry_session_login (product tool: opens the Ctrip login entry in the user\'s own Chrome and waits for them to log in on the external site — no terminal, no credentials through GoTry); needs-attach = one-time Chrome remote-debugging switch. ' + 'Rate-limited (≥30s between same-site calls; a challenged/timeout verdict means STOP — never retry, fall back to other tools).',
1020
+ description: 'Search on the USER\'S OWN logged-in browser session (Ctrip flights today; the account channel, not an anonymous instance). ' + 'Consent gate: the FIRST call in a session asks the user via the runtime approval card; once granted it holds for the session, a refusal revokes it for the session (no repeat prompting). ' + 'Transport: GoTry Session Bridge browser extension (one-time install) — the agent side never talks to Chrome debugging, ZERO system dialogs; read-only by construction (the extension never issues requests; it only passively forwards the site\'s own search responses; agent NEVER touches credentials/captcha; on captcha it stops and returns challenged). ' + 'Currently ctrip-flight: sniffs the site search API for structured options. Evidence [会话:ctrip-flight@ts]. ' + 'verdict needs-login = call gotry_session_login (opens the Ctrip login entry in the user\'s own foreground tab — no terminal, no credentials through GoTry); ' + 'needs-extension = one-time browser-extension install (npx gotry setup for instructions) — the DEFAULT transport; cdp (chrome://inspect remote debugging) is a diagnostic fallback only via GOTRY_SESSION_TRANSPORT=cdp. ' + 'Rate-limited (≥30s between same-site calls; a challenged/timeout verdict means STOP — never retry, fall back to other tools).',
968
1021
  parameters: {
969
1022
  query: {
970
1023
  type: 'json',
@@ -991,12 +1044,23 @@ export function apply(ctx, config) {
991
1044
  summary: '需要 from/to(中文城市名,词表内)与 date(YYYY-MM-DD)'
992
1045
  };
993
1046
  }
994
- const r = await sessionFlightSearch({
995
- from: q.from,
996
- to: q.to,
997
- date: q.date,
998
- auditPath: join(config.stateRoot ?? '.', 'gotry-state', 'session-incidents.jsonl')
1047
+ const itp = await interpretEffect({
1048
+ effect: 'SESSION_FLIGHT_SEARCH',
1049
+ params: {
1050
+ from: q.from,
1051
+ to: q.to,
1052
+ date: q.date,
1053
+ auditPath: join(config.stateRoot ?? '.', 'gotry-state', 'session-incidents.jsonl')
1054
+ }
999
1055
  });
1056
+ if (!itp.result) return declinedObservation('SESSION_FLIGHT_SEARCH', itp.trace);
1057
+ const r = itp.result;
1058
+ const avMapS = await loadAirlineAirportMap();
1059
+ await appendFacts(config.stateRoot ?? '.', factsFromSession({
1060
+ origin: q.from,
1061
+ destination: q.to,
1062
+ date: q.date
1063
+ }, r, new Date().toISOString(), avMapS?.city_alias));
1000
1064
  const top = (r.options ?? []).slice(0, 8).map((o)=>`${o.flightNo} ${o.airline} ${o.depDateTime.slice(11, 16)}→${o.arrDateTime.slice(11, 16)} ¥${o.price}`);
1001
1065
  const summary = r.verdict === 'hit' ? `${q.from}→${q.to} ${q.date} 会话检索(携程,用户本人登录态)前 ${top.length} 条:\n${top.join('\n')}\n${r.evidence}` : `会话检索未取回(${r.verdict}):${r.error ?? ''} ${r.evidence}`;
1002
1066
  return JSON.parse(JSON.stringify({
@@ -1012,7 +1076,7 @@ export function apply(ctx, config) {
1012
1076
  }),
1013
1077
  presentResult: (_args, value)=>{
1014
1078
  const r = value;
1015
- const label = r.verdict === 'hit' ? `会话 ${r.options.length} 条` : r.verdict === 'needs-login' ? '需登录' : r.verdict ?? '降级';
1079
+ const label = r.verdict === 'hit' ? `会话 ${r.options.length} 条` : r.verdict === 'needs-login' ? '需登录' : r.verdict === 'needs-extension' ? '需装扩展(一次性)' : r.verdict ?? '降级';
1016
1080
  return {
1017
1081
  card: 'generic',
1018
1082
  title: `会话检索:${label}`,
@@ -1357,7 +1421,219 @@ export function apply(ctx, config) {
1357
1421
  };
1358
1422
  }
1359
1423
  }));
1424
+ registerGuarded(defineTool({
1425
+ name: 'gotry_artifacts_list',
1426
+ description: 'List GoTry artifacts — deep-planning deliverables (async runs from the state ledger) plus agent-written markdown files ' + 'in the working directory (trip plans etc.). READ-ONLY discovery. ' + 'Use when the user asks to see/open/revisit a previously generated artifact ' + '(「看看刚才生成的行程」「上次的规划在哪」「打开那个 md」) — list first, then read with gotry_artifacts_read.',
1427
+ parameters: {
1428
+ query: {
1429
+ type: 'json',
1430
+ required: true,
1431
+ description: '{ limit?: 20 }'
1432
+ }
1433
+ },
1434
+ output: {
1435
+ schema: {
1436
+ type: 'json'
1437
+ },
1438
+ render: (_args, value)=>[
1439
+ {
1440
+ type: 'text',
1441
+ text: String(value.summary ?? JSON.stringify(value).slice(0, 600))
1442
+ }
1443
+ ]
1444
+ },
1445
+ async execute (args, _exec) {
1446
+ const q = unwrapQuery(args, 'limit');
1447
+ const r = await listArtifacts({
1448
+ stateRoot: config.stateRoot ?? '.',
1449
+ limit: q.limit
1450
+ });
1451
+ const lines = r.artifacts.map((a)=>`- [${a.source}] ${a.title}${a.status ? `(${a.status})` : ''} — ${a.path}${a.updated ? ` @ ${a.updated.slice(0, 16).replace('T', ' ')}` : ''}`);
1452
+ const summary = r.artifacts.length ? `在册产物 ${r.artifacts.length}/${r.total} 项${r.truncated ? '(截断,可加 limit)' : ''}:\n${lines.join('\n')}` : '无在册产物(异步深度规划交付与工作目录 md 文件都会出现在这里)';
1453
+ return JSON.parse(JSON.stringify({
1454
+ ok: true,
1455
+ artifacts: r.artifacts,
1456
+ total: r.total,
1457
+ truncated: r.truncated,
1458
+ summary
1459
+ }));
1460
+ },
1461
+ presentCall: ()=>({
1462
+ card: 'generic',
1463
+ title: '列出产物',
1464
+ kind: 'search'
1465
+ }),
1466
+ presentResult: (_args, value)=>{
1467
+ const r = value;
1468
+ return {
1469
+ card: 'generic',
1470
+ title: `产物:${r.total ?? 0} 项在册`,
1471
+ content: [
1472
+ {
1473
+ type: 'text',
1474
+ text: String(r.summary ?? '')
1475
+ }
1476
+ ]
1477
+ };
1478
+ }
1479
+ }));
1480
+ registerGuarded(defineTool({
1481
+ name: 'gotry_artifacts_read',
1482
+ description: 'Read one GoTry artifact as a line-numbered file view rendered directly in the chat UI. ' + 'Input: the path from gotry_artifacts_list, or a bare async ticket id (e.g. dp-xxxx). ' + 'Optional offset (1-based) / limit window for paging large files. ' + 'READ-ONLY; text artifacts only (md/txt/json/jsonl/csv/log/yaml).',
1483
+ parameters: {
1484
+ query: {
1485
+ type: 'json',
1486
+ required: true,
1487
+ description: '{ path: "<list 返回的路径或工单 id>", offset?: 1, limit?: 400 }'
1488
+ }
1489
+ },
1490
+ output: {
1491
+ schema: {
1492
+ type: 'json'
1493
+ },
1494
+ render: (_args, value)=>[
1495
+ {
1496
+ type: 'text',
1497
+ text: String(value.content ?? JSON.stringify(value).slice(0, 600))
1498
+ }
1499
+ ]
1500
+ },
1501
+ async execute (args, _exec) {
1502
+ const q = unwrapQuery(args, 'path');
1503
+ if (!q.path) return JSON.parse(JSON.stringify({
1504
+ ok: false,
1505
+ error: 'path 必填(来自 gotry_artifacts_list)'
1506
+ }));
1507
+ const r = await readArtifact({
1508
+ stateRoot: config.stateRoot ?? '.',
1509
+ path: q.path,
1510
+ offset: q.offset,
1511
+ limit: q.limit
1512
+ });
1513
+ if (!r.ok) return JSON.parse(JSON.stringify(r));
1514
+ return JSON.parse(JSON.stringify({
1515
+ ...r,
1516
+ summary: `${r.path}(${r.totalLines} 行)第 ${r.offset}-${r.offset + r.lines.length - 1} 行${r.windowed ? `(共 ${r.totalLines} 行,可翻页)` : ''}`
1517
+ }));
1518
+ },
1519
+ presentCall: (args)=>({
1520
+ card: 'generic',
1521
+ title: `读产物:${String(args.query?.path ?? '')}`,
1522
+ kind: 'read',
1523
+ rawInput: args.query
1524
+ }),
1525
+ presentResult: (_args, value)=>{
1526
+ const r = value;
1527
+ if (!r.ok) {
1528
+ return {
1529
+ card: 'generic',
1530
+ title: '读产物失败',
1531
+ content: [
1532
+ {
1533
+ type: 'text',
1534
+ text: String(r.error ?? '')
1535
+ }
1536
+ ]
1537
+ };
1538
+ }
1539
+ return {
1540
+ card: 'read',
1541
+ title: r.path?.split('/').pop() ?? r.path ?? '',
1542
+ path: r.path ?? '',
1543
+ offset: r.offset ?? 1,
1544
+ lines: r.lines ?? [],
1545
+ totalLines: r.totalLines ?? 0,
1546
+ lang: r.lang,
1547
+ content: [
1548
+ {
1549
+ type: 'text',
1550
+ text: r.content ?? ''
1551
+ }
1552
+ ]
1553
+ };
1554
+ }
1555
+ }));
1556
+ registerGuarded(defineTool({
1557
+ name: 'gotry_fact_gate',
1558
+ description: 'Factuality gate for itinerary artifacts (issue #46): call BEFORE delivering any artifact that contains bookable facts ' + '(flight numbers, times, airports, prices, visa/entry policies). Every bookable claim in the markdown is checked against ' + 'the session fact registry (exact-date tool results recorded by gotry_flyai_search / gotry_session_search — hit AND miss). ' + 'Blocked when: a claimed flight is absent from the exact-date source (never backfill from route pages/history/adjacent dates); ' + 'route+date never queried; times contradict the snapshot; carrier→airport mapping conflicts (FD=DMK vs VZ=BKK are NOT interchangeable); ' + '「联程」without protected_connection=true; policy claims without 「截至 YYYY-MM-DD」; unconditional ✓ on unverified claims. ' + 'Optional itinerary object enables machine invariants (hotel_nights + onboard_nights = total nights, O&D segments vs flight legs, ' + 'budget floor ≥ sum of item minimums). verdict=blocked ⇒ fix or downgrade wording — never present as a verified plan.',
1559
+ parameters: {
1560
+ query: {
1561
+ type: 'json',
1562
+ required: true,
1563
+ description: '{ markdown?: "<产物全文>", path?: "<产物路径(artifacts_list 返回的)>— 二选一", tripYear?: 2027, itinerary?: { trip_start, trip_end, stays: [{place,check_in,check_out}], onboard_nights, od_segments: [{from,to,date,mode,legs}], budget_items: [{label,min_cny,max_cny}], claimed_floor_cny? } }'
1564
+ }
1565
+ },
1566
+ output: {
1567
+ schema: {
1568
+ type: 'json'
1569
+ },
1570
+ render: (_args, value)=>[
1571
+ {
1572
+ type: 'text',
1573
+ text: String(value.summary ?? JSON.stringify(value).slice(0, 800))
1574
+ }
1575
+ ]
1576
+ },
1577
+ async execute (args, _exec) {
1578
+ const q = unwrapQuery(args, 'markdown');
1579
+ const avMap = await loadAirlineAirportMap();
1580
+ if (!avMap) {
1581
+ return JSON.parse(JSON.stringify({
1582
+ ok: false,
1583
+ summary: '航司机场映射缺失(data/airline-airports.json)——事实闸不可用,fail closed:产物不得宣称已验证'
1584
+ }));
1585
+ }
1586
+ let markdown = q.markdown;
1587
+ if (!markdown && q.path) {
1588
+ const r = await readArtifact({
1589
+ stateRoot: config.stateRoot ?? '.',
1590
+ path: q.path
1591
+ });
1592
+ if (!r.ok) return JSON.parse(JSON.stringify({
1593
+ ok: false,
1594
+ summary: `产物读取失败:${String(r.error ?? '')}`
1595
+ }));
1596
+ markdown = r.content;
1597
+ }
1598
+ if (!markdown) return JSON.parse(JSON.stringify({
1599
+ ok: false,
1600
+ summary: '需要 markdown(产物全文)或 path(产物路径)'
1601
+ }));
1602
+ const itinerary = q.itinerary;
1603
+ const registry = await loadFactRegistry(config.stateRoot ?? '.');
1604
+ const report = gateArtifact(markdown, registry, avMap, {
1605
+ trip_year: q.tripYear,
1606
+ itinerary
1607
+ });
1608
+ const lines = report.violations.slice(0, 20).map((v)=>` L${v.line} [${v.kind}] ${v.detail}`);
1609
+ const summary = report.verdict === 'pass' ? `事实闸 PASS:${report.traceable}/${report.claims_checked} 可下单 claim 全部回溯到 exact-date 工具结果(query_id 可重放)——可宣称「已验证方案」。` : `事实闸 BLOCKED(${report.violations.length} 违例,${report.traceable}/${report.claims_checked} claim 可回溯)——不得宣称「已验证方案」:\n${lines.join('\n')}\n修正路径:逐条改成 exact-date 源返回的事实,或降级为「未确认/当前不可售,到 D-xx 复核」;exact-date miss 的 route+date 不得用历史班期/相邻日期/航线页填充。`;
1610
+ return JSON.parse(JSON.stringify({
1611
+ ok: true,
1612
+ ...report,
1613
+ summary
1614
+ }));
1615
+ },
1616
+ presentCall: (args)=>({
1617
+ card: 'generic',
1618
+ title: '产物事实闸',
1619
+ kind: 'execute',
1620
+ rawInput: args.query
1621
+ }),
1622
+ presentResult: (_args, value)=>{
1623
+ const r = value;
1624
+ return {
1625
+ card: 'generic',
1626
+ title: `事实闸:${r.verdict === 'pass' ? '✅ pass' : `⛔ blocked(${r.violations?.length ?? '?'})`}`,
1627
+ content: [
1628
+ {
1629
+ type: 'text',
1630
+ text: String(r.summary ?? '')
1631
+ }
1632
+ ]
1633
+ };
1634
+ }
1635
+ }));
1360
1636
  }
1361
1637
 
1362
1638
 
1363
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/index.ts
1639
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/index.ts
@@ -140,4 +140,4 @@ export function parseFlightPackLegs(pack) {
140
140
  }
141
141
 
142
142
 
143
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/journey.ts
143
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/journey.ts
package/dist/src/loop.js CHANGED
@@ -408,4 +408,4 @@ export function probePoi(msg) {
408
408
  }
409
409
 
410
410
 
411
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/loop.ts
411
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/loop.ts
@@ -37,4 +37,4 @@ export function mergeProfile(current, patch) {
37
37
  }
38
38
 
39
39
 
40
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/memory-capture.ts
40
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/memory-capture.ts
@@ -28,4 +28,4 @@ function round4(x) {
28
28
  }
29
29
 
30
30
 
31
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/memory-decay.ts
31
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/memory-decay.ts
@@ -52,4 +52,4 @@ export function projectUtility(events) {
52
52
  }
53
53
 
54
54
 
55
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/memory-utility.ts
55
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/memory-utility.ts
@@ -100,4 +100,4 @@ export function createMockLlm(flightPackPath, slotScript = []) {
100
100
  }
101
101
 
102
102
 
103
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/mock-llm.ts
103
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/mock-llm.ts
package/dist/src/model.js CHANGED
@@ -131,4 +131,4 @@ export function trueCostToDict(t) {
131
131
  }
132
132
 
133
133
 
134
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/model.ts
134
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/model.ts
@@ -1,4 +1,4 @@
1
- import { flyaiSearch } from '../capabilities/flyai.js';
1
+ import { interpretEffect } from '../capabilities/effect.js';
2
2
  const IATA_TO_CITY = {
3
3
  HKG: '香港',
4
4
  HKT: '普吉',
@@ -76,12 +76,19 @@ export async function overlayRealtimeFlightPrices(spec, opts = {}) {
76
76
  }
77
77
  return out;
78
78
  }
79
- const defaultQuery = (q)=>flyaiSearch({
80
- kind: q.kind,
81
- origin: q.origin,
82
- destination: q.destination,
83
- depDate: q.depDate
79
+ const defaultQuery = async (q)=>{
80
+ const itp = await interpretEffect({
81
+ effect: 'FLYAI_SEARCH',
82
+ params: {
83
+ kind: q.kind,
84
+ origin: q.origin,
85
+ destination: q.destination,
86
+ depDate: q.depDate
87
+ }
84
88
  });
89
+ if (!itp.result) throw new Error(itp.trace.evidence.join(';'));
90
+ return itp.result;
91
+ };
85
92
  export function realtimeSolvePort(base, opts = {}) {
86
93
  return async (spec)=>{
87
94
  const enabled = opts.enabled ?? process.env['GOTRY_REALTIME_PRICING'] === '1';
@@ -102,4 +109,4 @@ export function realtimeSolvePort(base, opts = {}) {
102
109
  }
103
110
 
104
111
 
105
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/realtime-pricing.ts
112
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/realtime-pricing.ts
@@ -162,4 +162,4 @@ export function specDateMismatches(spec, resolved) {
162
162
  }
163
163
 
164
164
 
165
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/slot-spec.ts
165
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/slot-spec.ts
@@ -924,4 +924,4 @@ export function readCompanionsWithFallback(stateRoot) {
924
924
  }
925
925
 
926
926
 
927
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/state-ledger.ts
927
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/state-ledger.ts
@@ -97,4 +97,4 @@ export function parseAbsoluteDate(expr, anchorYear) {
97
97
  }
98
98
 
99
99
 
100
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/time-anchor.ts
100
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/time-anchor.ts
@@ -9,4 +9,4 @@ export function interpretArgs(args, stringKey) {
9
9
  }
10
10
 
11
11
 
12
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/tool-packet.ts
12
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/tool-packet.ts
@@ -141,4 +141,4 @@ export function scoreExtraction(expected, actual) {
141
141
  }
142
142
 
143
143
 
144
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/travel-slots.ts
144
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/travel-slots.ts
@@ -75,4 +75,4 @@ export function timelineGapsForVerified(timeline, verifiedWishes) {
75
75
  export { ymd };
76
76
 
77
77
 
78
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/travel-timeline.ts
78
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/travel-timeline.ts
@@ -588,4 +588,4 @@ function renderCandidateMarkdown(req, verdicts, recommended) {
588
588
  export { minToHhmm };
589
589
 
590
590
 
591
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/unified.ts
591
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/unified.ts
@@ -24,4 +24,4 @@ export function pickNudgeWish(pool, ctx) {
24
24
  }
25
25
 
26
26
 
27
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/wish-pool.ts
27
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/wish-pool.ts
@@ -15,4 +15,4 @@ export async function withZ3(label, session) {
15
15
  }
16
16
 
17
17
 
18
- //# sourceURL=/Users/bytedance/work/gotry/ts/src/z3-shared.ts
18
+ //# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/z3-shared.ts