@danceiny/gotry 0.0.1-rc.15 → 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.
- package/README.md +10 -6
- package/README.zh-CN.md +10 -6
- package/bin/gotry-bootstrap.js +294 -5
- package/cordis.gotry-patch.yml +10 -0
- package/data/airline-airports.json +39 -0
- package/dist/capabilities/agent-reach-deep.js +1 -1
- package/dist/capabilities/agent-reach.js +1 -1
- package/dist/capabilities/anything.js +1 -1
- package/dist/capabilities/artifacts.js +1 -1
- package/dist/capabilities/effect.js +293 -0
- package/dist/capabilities/fact-log.js +41 -0
- package/dist/capabilities/flyai.js +1 -1
- package/dist/capabilities/hbcli.js +43 -1
- package/dist/capabilities/incident-log.js +1 -1
- package/dist/capabilities/opensky.js +1 -1
- package/dist/capabilities/resilience.js +90 -0
- package/dist/capabilities/session/action-cache.js +1 -1
- package/dist/capabilities/session/adapters/ctrip-flight.js +1 -1
- package/dist/capabilities/session/adapters/meituan-local.js +1 -1
- package/dist/capabilities/session/benchmark.js +2 -1
- package/dist/capabilities/session/extension-bridge.js +345 -0
- package/dist/capabilities/session/extension-channel.js +93 -0
- package/dist/capabilities/session/extract.js +1 -1
- package/dist/capabilities/session/health-watch.js +216 -0
- package/dist/capabilities/session/read-guard.js +1 -1
- package/dist/capabilities/session/transport.js +1 -1
- package/dist/capabilities/session/wizard.js +377 -0
- package/dist/capabilities/session-consent.js +1 -1
- package/dist/capabilities/session-login.js +77 -1
- package/dist/capabilities/session-search.js +69 -2
- package/dist/capabilities/weather.js +1 -1
- package/dist/scripts/action-cache-tests.js +1 -1
- package/dist/scripts/agent-reach-deep-tests.js +1 -1
- package/dist/scripts/agent-reach-tests.js +1 -1
- package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
- package/dist/scripts/anything-tests.js +1 -1
- package/dist/scripts/async-collect.js +1 -1
- package/dist/scripts/booking-saga-tests.js +1 -1
- package/dist/scripts/bootstrap-tests.js +39 -3
- package/dist/scripts/build-changelog.js +279 -0
- package/dist/scripts/changelog-tests.js +165 -0
- package/dist/scripts/companion-tests.js +1 -1
- package/dist/scripts/diff-test.js +1 -1
- package/dist/scripts/effect-tests.js +474 -0
- package/dist/scripts/engine-run.js +1 -1
- package/dist/scripts/engine-tests.js +1 -1
- package/dist/scripts/extension-tests.js +468 -0
- package/dist/scripts/fact-gate-tests.js +318 -0
- package/dist/scripts/flyai-tests.js +1 -1
- package/dist/scripts/hbcli-e2e-tests.js +180 -0
- package/dist/scripts/hbcli-tests.js +1 -1
- package/dist/scripts/health-watch-cli.js +51 -0
- package/dist/scripts/i18n-tests.js +1 -1
- package/dist/scripts/incident-tests.js +1 -1
- package/dist/scripts/journey-tests.js +1 -1
- package/dist/scripts/ledger-tests.js +1 -1
- package/dist/scripts/ledger-workflow-crash.js +1 -1
- package/dist/scripts/memory-capture-tests.js +1 -1
- package/dist/scripts/memory-decay-tests.js +1 -1
- package/dist/scripts/memory-metrics.js +1 -1
- package/dist/scripts/memory-value-report.js +1 -1
- package/dist/scripts/nightly-evidence-tests.js +67 -4
- package/dist/scripts/nightly-evidence.js +59 -3
- package/dist/scripts/nudge-digest.js +1 -1
- package/dist/scripts/onboarding-tests.js +218 -0
- package/dist/scripts/opensky-check.js +1 -1
- package/dist/scripts/opensky-tests.js +1 -1
- package/dist/scripts/price-drift-tests.js +441 -0
- package/dist/scripts/price-drift-watch.js +491 -0
- package/dist/scripts/probe-poi-tests.js +1 -1
- package/dist/scripts/product-metrics.js +1 -1
- package/dist/scripts/publish-preverify.js +1 -1
- package/dist/scripts/realtime-pricing-tests.js +1 -1
- package/dist/scripts/replay-async.js +1 -1
- package/dist/scripts/replay-real.js +1 -1
- package/dist/scripts/replay.js +1 -1
- package/dist/scripts/session-attach-diagnose.js +1 -1
- package/dist/scripts/session-attach-poc.js +1 -1
- package/dist/scripts/session-benchmark.js +1 -1
- package/dist/scripts/session-extract-tests.js +1 -1
- package/dist/scripts/session-login.js +1 -1
- package/dist/scripts/session-tests.js +1 -1
- package/dist/scripts/skeleton-check.js +1 -1
- package/dist/scripts/skeleton-integration-test.js +1 -1
- package/dist/scripts/skills-contract-tests.js +1 -1
- package/dist/scripts/smoke.js +76 -1
- package/dist/scripts/state-cli-tests.js +1 -1
- package/dist/scripts/state-cli.js +1 -1
- package/dist/scripts/time-eval-tests.js +1 -1
- package/dist/scripts/travel-timeline-tests.js +1 -1
- package/dist/scripts/unified-tests.js +1 -1
- package/dist/scripts/weather-tests.js +1 -1
- package/dist/scripts/wizard-bootstrap.js +32 -0
- package/dist/scripts/z3-race-tests.js +1 -1
- package/dist/src/artifact-gate.js +310 -0
- package/dist/src/bookable-facts.js +349 -0
- package/dist/src/booking-saga.js +1 -1
- package/dist/src/bridge.js +1 -1
- package/dist/src/companions.js +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/dsh-llm.js +1 -1
- package/dist/src/engine.js +1 -1
- package/dist/src/i18n.js +1 -1
- package/dist/src/index.js +189 -46
- package/dist/src/journey.js +1 -1
- package/dist/src/loop.js +1 -1
- package/dist/src/memory-capture.js +1 -1
- package/dist/src/memory-decay.js +1 -1
- package/dist/src/memory-utility.js +1 -1
- package/dist/src/mock-llm.js +1 -1
- package/dist/src/model.js +1 -1
- package/dist/src/realtime-pricing.js +14 -7
- package/dist/src/slot-spec.js +1 -1
- package/dist/src/state-ledger.js +1 -1
- package/dist/src/time-anchor.js +1 -1
- package/dist/src/tool-packet.js +1 -1
- package/dist/src/travel-slots.js +1 -1
- package/dist/src/travel-timeline.js +1 -1
- package/dist/src/unified.js +1 -1
- package/dist/src/wish-pool.js +1 -1
- package/dist/src/z3-shared.js +1 -1
- package/extension/README.md +26 -0
- package/extension/background.js +160 -0
- package/extension/content-bridge.js +41 -0
- package/extension/content-main.js +62 -0
- package/extension/manifest.json +32 -0
- package/package.json +6 -2
- package/ts/capabilities/fact-log.ts +54 -0
- package/ts/capabilities/hbcli.ts +46 -0
- package/ts/capabilities/session/benchmark.ts +4 -1
- package/ts/capabilities/session/extension-bridge.ts +354 -0
- package/ts/capabilities/session/extension-channel.ts +92 -0
- package/ts/capabilities/session/health-watch.ts +257 -0
- package/ts/capabilities/session/wizard.ts +282 -0
- package/ts/capabilities/session-login.ts +53 -2
- package/ts/capabilities/session-search.ts +68 -3
- package/ts/src/artifact-gate.ts +365 -0
- package/ts/src/bookable-facts.ts +557 -0
- package/ts/src/index.ts +102 -8
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,16 +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 {
|
|
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';
|
|
26
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
|
+
}
|
|
27
38
|
export const name = 'gotry-tools';
|
|
28
39
|
export const inject = [
|
|
29
40
|
'tools',
|
|
@@ -549,16 +560,20 @@ export function apply(ctx, config) {
|
|
|
549
560
|
if (r.raw !== r.date) dateNotes.push(`slot-resolved: ${r.raw} → ${r.date}`);
|
|
550
561
|
return r.date;
|
|
551
562
|
};
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
+
}
|
|
561
574
|
});
|
|
575
|
+
if (!itp.result) return declinedObservation('HBCLI_HOTEL_SEARCH', itp.trace);
|
|
576
|
+
const resp = itp.result;
|
|
562
577
|
const dir = await ensureStateDir(config.stateRoot);
|
|
563
578
|
const isLive = resp.via === 'hbcli-realtime';
|
|
564
579
|
const evidence = isLive ? resp.evidence : '[静态包:估算]';
|
|
@@ -672,7 +687,14 @@ export function apply(ctx, config) {
|
|
|
672
687
|
let placeLabel = q.place ?? `${q.lat},${q.lng}`;
|
|
673
688
|
if (lat === undefined || lng === undefined) {
|
|
674
689
|
if (!q.place) throw new Error('gotry_weather_check requires place name or lat/lng');
|
|
675
|
-
const
|
|
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;
|
|
676
698
|
if (!geo.ok || geo.results.length === 0) {
|
|
677
699
|
return JSON.parse(JSON.stringify({
|
|
678
700
|
ok: false,
|
|
@@ -686,15 +708,23 @@ export function apply(ctx, config) {
|
|
|
686
708
|
placeLabel = `${hit.name}(${hit.admin1 ?? hit.country ?? ''})`;
|
|
687
709
|
}
|
|
688
710
|
const isClimate = q.mode === 'climate' || q.month !== undefined && q.mode !== 'forecast';
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
+
}
|
|
697
725
|
});
|
|
726
|
+
if (!wxItp.result) return declinedObservation(isClimate ? 'WEATHER_CLIMATE' : 'WEATHER_FORECAST', wxItp.trace);
|
|
727
|
+
const r = wxItp.result;
|
|
698
728
|
const dir = await ensureStateDir(config.stateRoot);
|
|
699
729
|
await recordLatency(join(dir, 'bridge-latency.jsonl'), Date.now() - started, `weather:${r.via}`).catch(()=>{});
|
|
700
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}%` : ''}`);
|
|
@@ -762,11 +792,16 @@ export function apply(ctx, config) {
|
|
|
762
792
|
summary: 'callsign 必填'
|
|
763
793
|
}));
|
|
764
794
|
}
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
+
}
|
|
769
802
|
});
|
|
803
|
+
if (!itp.result) return declinedObservation('OPENSKY_FLIGHT_VERIFY', itp.trace);
|
|
804
|
+
const r = itp.result;
|
|
770
805
|
const dir = await ensureStateDir(config.stateRoot);
|
|
771
806
|
await recordLatency(join(dir, 'bridge-latency.jsonl'), Date.now() - started, `flight_verify:${r.via}`).catch(()=>{});
|
|
772
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}`;
|
|
@@ -842,13 +877,18 @@ export function apply(ctx, config) {
|
|
|
842
877
|
};
|
|
843
878
|
}
|
|
844
879
|
}
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
+
}
|
|
851
889
|
});
|
|
890
|
+
if (!itp.result) return declinedObservation('FLYAI_SEARCH', itp.trace);
|
|
891
|
+
const r = itp.result;
|
|
852
892
|
const top = (r.hotels ?? []).slice(0, 8).map((o)=>`${o.name}${o.star ? `(${o.star})` : ''} ${o.priceRaw ?? '价待询'}${o.poi ? ` · ${o.poi}` : ''}`);
|
|
853
893
|
const summary = r.verdict === 'hit' ? `${dest} 酒店(飞猪官方只读)前 ${top.length} 家(价格多为打码,真实价以 jumpUrl 为准):\n${top.join('\n')}\n${r.evidence}` : `${dest} 酒店无结果或失败:${r.error ?? 'miss'} ${r.evidence}`;
|
|
854
894
|
return JSON.parse(JSON.stringify({
|
|
@@ -873,12 +913,24 @@ export function apply(ctx, config) {
|
|
|
873
913
|
summary: `未发起查询:日期 ${q.date} 已是过去(今天 ${todayYmd}),过去不存在在售机/火车票。` + `多为用户时间表达未带年份所致——向用户确认年份(或按未来最近的同月日修正)后再查。`
|
|
874
914
|
}));
|
|
875
915
|
}
|
|
876
|
-
const
|
|
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({
|
|
877
929
|
kind,
|
|
878
930
|
origin: q.from,
|
|
879
931
|
destination: q.to,
|
|
880
|
-
|
|
881
|
-
});
|
|
932
|
+
date: q.date
|
|
933
|
+
}, r, new Date().toISOString(), avMap?.city_alias));
|
|
882
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}`);
|
|
883
935
|
const label = kind === 'flight' ? '机票' : '火车票';
|
|
884
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}`;
|
|
@@ -912,7 +964,7 @@ export function apply(ctx, config) {
|
|
|
912
964
|
}));
|
|
913
965
|
registerGuarded(defineTool({
|
|
914
966
|
name: 'gotry_session_login',
|
|
915
|
-
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-
|
|
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].',
|
|
916
968
|
parameters: {
|
|
917
969
|
query: {
|
|
918
970
|
type: 'json',
|
|
@@ -936,7 +988,7 @@ export function apply(ctx, config) {
|
|
|
936
988
|
const r = await sessionLogin({
|
|
937
989
|
waitMs: typeof q?.waitSeconds === 'number' ? q.waitSeconds * 1000 : undefined
|
|
938
990
|
});
|
|
939
|
-
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' ?
|
|
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}`;
|
|
940
992
|
return JSON.parse(JSON.stringify({
|
|
941
993
|
...r,
|
|
942
994
|
summary
|
|
@@ -965,7 +1017,7 @@ export function apply(ctx, config) {
|
|
|
965
1017
|
}));
|
|
966
1018
|
registerGuarded(defineTool({
|
|
967
1019
|
name: 'gotry_session_search',
|
|
968
|
-
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). ' + '
|
|
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).',
|
|
969
1021
|
parameters: {
|
|
970
1022
|
query: {
|
|
971
1023
|
type: 'json',
|
|
@@ -992,12 +1044,23 @@ export function apply(ctx, config) {
|
|
|
992
1044
|
summary: '需要 from/to(中文城市名,词表内)与 date(YYYY-MM-DD)'
|
|
993
1045
|
};
|
|
994
1046
|
}
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
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
|
+
}
|
|
1000
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));
|
|
1001
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}`);
|
|
1002
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}`;
|
|
1003
1066
|
return JSON.parse(JSON.stringify({
|
|
@@ -1013,7 +1076,7 @@ export function apply(ctx, config) {
|
|
|
1013
1076
|
}),
|
|
1014
1077
|
presentResult: (_args, value)=>{
|
|
1015
1078
|
const r = value;
|
|
1016
|
-
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 ?? '降级';
|
|
1017
1080
|
return {
|
|
1018
1081
|
card: 'generic',
|
|
1019
1082
|
title: `会话检索:${label}`,
|
|
@@ -1490,7 +1553,87 @@ export function apply(ctx, config) {
|
|
|
1490
1553
|
};
|
|
1491
1554
|
}
|
|
1492
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
|
+
}));
|
|
1493
1636
|
}
|
|
1494
1637
|
|
|
1495
1638
|
|
|
1496
|
-
//# sourceURL=/Users/bytedance/work/gotry/ts/src/index.ts
|
|
1639
|
+
//# sourceURL=/Users/bytedance/work/gotry-publish/ts/src/index.ts
|
package/dist/src/journey.js
CHANGED
package/dist/src/loop.js
CHANGED
package/dist/src/memory-decay.js
CHANGED
package/dist/src/mock-llm.js
CHANGED
package/dist/src/model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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)=>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
package/dist/src/slot-spec.js
CHANGED
package/dist/src/state-ledger.js
CHANGED
package/dist/src/time-anchor.js
CHANGED
package/dist/src/tool-packet.js
CHANGED
package/dist/src/travel-slots.js
CHANGED
package/dist/src/unified.js
CHANGED
package/dist/src/wish-pool.js
CHANGED
package/dist/src/z3-shared.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# GoTry Session Bridge(浏览器扩展)
|
|
2
|
+
|
|
3
|
+
一次性安装的会话检索数据面(issue #21 传输层方案 C,2026-08-29 founder 定案):在你**自己的** Chrome 登录态里,只读嗅探携程机票检索回包([会话:ctrip-flight]。gotry 不再需要逐连接弹 Chrome 权限框,也不需要开 `chrome://inspect` 远程调试。
|
|
4
|
+
|
|
5
|
+
## 它做什么 / 不做什么
|
|
6
|
+
|
|
7
|
+
- ✅ 检索:你(或 agent)发起会话检索时,后台标签页打开携程机票列表页,被动嗅探页面自己发出的 `batchSearch` 响应,文本回传给本机的 gotry。收尾自动关闭自己开的标签。
|
|
8
|
+
- ✅ 登录检查:只读登录票据 cookie 的**名字**(cticket/uid/uname/passport),判断「是否已登录」这个布尔事实。
|
|
9
|
+
- ✅ 登录引导:把登录入口页**置前台**打开,登录由你在携程官网完成。
|
|
10
|
+
- ❌ **不读取、不存储、不回传任何 cookie 值,不接触密码/验证码**;
|
|
11
|
+
- ❌ 不向任何站点发起请求(检索请求由站点页面自己发出,扩展只听不写);
|
|
12
|
+
- ❌ 不动你已打开的任何标签页(只创建/关闭自己的检索标签;登录标签留给你)。
|
|
13
|
+
|
|
14
|
+
## 安装(每台浏览器一次,约 30 秒)
|
|
15
|
+
|
|
16
|
+
1. 跑一次 `npx gotry setup`(或手动:把本目录整体拷到任意固定位置,建议 `~/.gotry/extension/`);
|
|
17
|
+
2. Chrome 地址栏打开 `chrome://extensions`;
|
|
18
|
+
3. 右上角开启「开发者模式」;
|
|
19
|
+
4. 点「加载已解压的扩展程序」,选择本目录(manifest.json 所在目录)。
|
|
20
|
+
|
|
21
|
+
装好即生效,无任何系统弹窗。卸载/随时可停:扩展卡片上的开关即是总闸(仍受 gotry 授权闸 `sessionAccess` 双重控制)。
|
|
22
|
+
|
|
23
|
+
## 自检
|
|
24
|
+
|
|
25
|
+
- 扩展 ID 应为固定的 `olpgkofjhhiiiahdkkbcninhjmegghfe`(manifest 带 `key`,跨机器稳定)——若不一致,说明 manifest 被改动过,不要安装;
|
|
26
|
+
- gotry 侧运行 `npx gotry` 后发起一次会话检索,扩展图标应出现(无需点击);桥状态可看 `curl http://127.0.0.1:8791/status`(仅本机回环)。
|