imprint-mcp 0.5.0 → 0.6.0
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 +6 -4
- package/examples/discoverandgo/README.md +1 -1
- package/examples/discoverandgo/book_discoverandgo_museum_pass/workflow.json +7 -0
- package/examples/google-flights/README.md +41 -14
- package/examples/google-flights/_shared/google_batchexecute_parser.ts +165 -0
- package/examples/google-flights/_shared/google_flights_transport.ts +70 -0
- package/examples/google-flights/_shared/google_flights_types.ts +56 -0
- package/examples/google-flights/get_flight_booking_options/backends.json +43 -0
- package/examples/google-flights/get_flight_booking_options/index.ts +129 -0
- package/examples/google-flights/get_flight_booking_options/parser.ts +209 -0
- package/examples/google-flights/get_flight_booking_options/playbook.yaml +46 -0
- package/examples/google-flights/get_flight_booking_options/request-transform.ts +210 -0
- package/examples/google-flights/get_flight_booking_options/workflow.json +85 -0
- package/examples/google-flights/get_flight_calendar_prices/backends.json +27 -0
- package/examples/google-flights/get_flight_calendar_prices/index.ts +44 -29
- package/examples/google-flights/get_flight_calendar_prices/parser.ts +76 -69
- package/examples/google-flights/get_flight_calendar_prices/playbook.yaml +67 -40
- package/examples/google-flights/get_flight_calendar_prices/workflow.json +29 -17
- package/examples/google-flights/get_flight_location_details/backends.json +27 -0
- package/examples/google-flights/{lookup_airport → get_flight_location_details}/index.ts +30 -20
- package/examples/google-flights/get_flight_location_details/parser.ts +103 -0
- package/examples/google-flights/get_flight_location_details/playbook.yaml +59 -0
- package/examples/google-flights/{lookup_airport → get_flight_location_details}/workflow.json +19 -12
- package/examples/google-flights/lookup_flight_locations/backends.json +27 -0
- package/examples/google-flights/lookup_flight_locations/index.ts +111 -0
- package/examples/google-flights/lookup_flight_locations/package.json +9 -0
- package/examples/google-flights/lookup_flight_locations/parser.ts +135 -0
- package/examples/google-flights/lookup_flight_locations/playbook.yaml +47 -0
- package/examples/google-flights/lookup_flight_locations/request-transform.ts +53 -0
- package/examples/google-flights/lookup_flight_locations/workflow.json +64 -0
- package/examples/google-flights/search_flights/backends.json +42 -0
- package/examples/google-flights/search_flights/index.ts +105 -69
- package/examples/google-flights/search_flights/parser.ts +186 -183
- package/examples/google-flights/search_flights/playbook.yaml +295 -88
- package/examples/google-flights/search_flights/request-transform.ts +247 -92
- package/examples/google-flights/search_flights/workflow.json +66 -40
- package/examples/google-flights/validate_flight_itinerary/backends.json +27 -0
- package/examples/google-flights/validate_flight_itinerary/index.ts +103 -0
- package/examples/google-flights/validate_flight_itinerary/package.json +9 -0
- package/examples/google-flights/validate_flight_itinerary/parser.ts +101 -0
- package/examples/google-flights/validate_flight_itinerary/playbook.yaml +84 -0
- package/examples/google-flights/validate_flight_itinerary/request-transform.ts +92 -0
- package/examples/google-flights/validate_flight_itinerary/workflow.json +59 -0
- package/examples/google-hotels/README.md +40 -15
- package/examples/google-hotels/get_hotel_details/backends.json +16 -0
- package/examples/google-hotels/get_hotel_details/index.ts +122 -0
- package/examples/google-hotels/get_hotel_details/parser.ts +275 -0
- package/examples/google-hotels/get_hotel_details/playbook.yaml +40 -0
- package/examples/google-hotels/get_hotel_details/request-transform.ts +67 -0
- package/examples/google-hotels/get_hotel_details/workflow.json +67 -0
- package/examples/google-hotels/get_hotel_photos/backends.json +16 -0
- package/examples/google-hotels/get_hotel_photos/index.ts +90 -0
- package/examples/google-hotels/get_hotel_photos/parser.ts +75 -0
- package/examples/google-hotels/get_hotel_photos/playbook.yaml +23 -0
- package/examples/google-hotels/get_hotel_photos/request-transform.ts +27 -0
- package/examples/google-hotels/get_hotel_photos/workflow.json +44 -0
- package/examples/google-hotels/get_hotel_reviews/backends.json +16 -0
- package/examples/google-hotels/get_hotel_reviews/index.ts +14 -17
- package/examples/google-hotels/get_hotel_reviews/parser.ts +177 -105
- package/examples/google-hotels/get_hotel_reviews/playbook.yaml +9 -54
- package/examples/google-hotels/get_hotel_reviews/workflow.json +10 -13
- package/examples/google-hotels/get_travel_area_boundaries/backends.json +16 -0
- package/examples/google-hotels/get_travel_area_boundaries/index.ts +104 -0
- package/examples/google-hotels/get_travel_area_boundaries/package.json +9 -0
- package/examples/google-hotels/get_travel_area_boundaries/parser.ts +127 -0
- package/examples/google-hotels/get_travel_area_boundaries/playbook.yaml +38 -0
- package/examples/google-hotels/get_travel_area_boundaries/request-transform.ts +52 -0
- package/examples/google-hotels/get_travel_area_boundaries/workflow.json +54 -0
- package/examples/google-hotels/list_hotel_booking_options/backends.json +16 -0
- package/examples/google-hotels/list_hotel_booking_options/index.ts +123 -0
- package/examples/google-hotels/list_hotel_booking_options/package.json +9 -0
- package/examples/google-hotels/list_hotel_booking_options/parser.ts +308 -0
- package/examples/google-hotels/list_hotel_booking_options/playbook.yaml +95 -0
- package/examples/google-hotels/list_hotel_booking_options/request-transform.ts +77 -0
- package/examples/google-hotels/list_hotel_booking_options/workflow.json +67 -0
- package/examples/google-hotels/search_hotel_web_links/backends.json +16 -0
- package/examples/google-hotels/search_hotel_web_links/index.ts +90 -0
- package/examples/google-hotels/search_hotel_web_links/package.json +9 -0
- package/examples/google-hotels/search_hotel_web_links/parser.ts +96 -0
- package/examples/google-hotels/search_hotel_web_links/playbook.yaml +53 -0
- package/examples/google-hotels/search_hotel_web_links/request-transform.ts +17 -0
- package/examples/google-hotels/search_hotel_web_links/workflow.json +43 -0
- package/examples/google-hotels/search_hotels/backends.json +16 -0
- package/examples/google-hotels/search_hotels/index.ts +32 -102
- package/examples/google-hotels/search_hotels/parser.ts +237 -216
- package/examples/google-hotels/search_hotels/playbook.yaml +177 -42
- package/examples/google-hotels/search_hotels/request-transform.ts +101 -170
- package/examples/google-hotels/search_hotels/workflow.json +24 -73
- package/examples/google-hotels/suggest_hotel_destinations/backends.json +16 -0
- package/examples/google-hotels/{autocomplete_hotel_location → suggest_hotel_destinations}/index.ts +30 -19
- package/examples/google-hotels/suggest_hotel_destinations/package.json +9 -0
- package/examples/google-hotels/suggest_hotel_destinations/parser.ts +138 -0
- package/examples/google-hotels/suggest_hotel_destinations/playbook.yaml +47 -0
- package/examples/google-hotels/suggest_hotel_destinations/request-transform.ts +33 -0
- package/examples/google-hotels/suggest_hotel_destinations/workflow.json +44 -0
- package/examples/southwest/README.md +31 -70
- package/examples/southwest/get_account_details/backends.json +22 -0
- package/examples/southwest/get_account_details/credentials.manifest.json +17 -0
- package/examples/southwest/get_account_details/index.ts +138 -0
- package/examples/{google-hotels/_shared → southwest/get_account_details}/package.json +2 -2
- package/examples/southwest/get_account_details/parser.ts +136 -0
- package/examples/southwest/get_account_details/playbook.yaml +19 -0
- package/examples/southwest/get_account_details/workflow.json +93 -0
- package/examples/southwest/get_flight_status/backends.json +21 -0
- package/examples/southwest/get_flight_status/index.ts +130 -0
- package/examples/southwest/get_flight_status/package.json +9 -0
- package/examples/southwest/get_flight_status/parser.ts +138 -0
- package/examples/southwest/get_flight_status/playbook.yaml +58 -0
- package/examples/southwest/get_flight_status/workflow.json +77 -0
- package/examples/southwest/get_low_fare_calendar/backends.json +21 -0
- package/examples/southwest/get_low_fare_calendar/index.ts +134 -0
- package/examples/southwest/get_low_fare_calendar/package.json +9 -0
- package/examples/southwest/get_low_fare_calendar/parser.ts +107 -0
- package/examples/southwest/get_low_fare_calendar/playbook.yaml +41 -0
- package/examples/southwest/get_low_fare_calendar/workflow.json +81 -0
- package/examples/southwest/list_upcoming_trips/backends.json +22 -0
- package/examples/southwest/list_upcoming_trips/credentials.manifest.json +17 -0
- package/examples/southwest/list_upcoming_trips/index.ts +123 -0
- package/examples/southwest/list_upcoming_trips/package.json +9 -0
- package/examples/southwest/list_upcoming_trips/parser.ts +160 -0
- package/examples/southwest/list_upcoming_trips/playbook.yaml +24 -0
- package/examples/southwest/list_upcoming_trips/workflow.json +78 -0
- package/examples/southwest/search_flights/backends.json +21 -0
- package/examples/southwest/search_flights/index.ts +150 -0
- package/examples/southwest/search_flights/package.json +9 -0
- package/examples/southwest/search_flights/parser.ts +214 -0
- package/examples/southwest/search_flights/playbook.yaml +52 -0
- package/examples/southwest/search_flights/workflow.json +88 -0
- package/package.json +1 -1
- package/prompts/audit-agent.md +2 -1
- package/prompts/build-planning.md +22 -1
- package/prompts/compile-agent.md +66 -53
- package/prompts/prereq-builder.md +3 -1
- package/prompts/tool-planning.md +2 -0
- package/src/cli.ts +8 -3
- package/src/imprint/audit.ts +337 -44
- package/src/imprint/backend-cache.ts +114 -0
- package/src/imprint/backend-ladder.ts +74 -24
- package/src/imprint/bot-defense.ts +12 -0
- package/src/imprint/build-plan.ts +346 -53
- package/src/imprint/cdp-browser-fetch.ts +96 -43
- package/src/imprint/cdp-jar-cache.ts +6 -5
- package/src/imprint/chromium.ts +34 -0
- package/src/imprint/codex-cli-compile.ts +13 -2
- package/src/imprint/compile-agent.ts +3 -1
- package/src/imprint/compile-tools.ts +715 -138
- package/src/imprint/compile.ts +41 -9
- package/src/imprint/concurrency.ts +26 -3
- package/src/imprint/cron.ts +0 -3
- package/src/imprint/endpoint-key.ts +23 -0
- package/src/imprint/llm-url.ts +69 -0
- package/src/imprint/llm.ts +366 -47
- package/src/imprint/login.ts +83 -75
- package/src/imprint/mcp-compile-server.ts +4 -1
- package/src/imprint/mcp-server.ts +51 -16
- package/src/imprint/param-grounding.ts +61 -52
- package/src/imprint/probe-backends.ts +21 -115
- package/src/imprint/redact.ts +17 -15
- package/src/imprint/request-capture.ts +94 -0
- package/src/imprint/runtime.ts +1 -78
- package/src/imprint/session-diff.ts +14 -1
- package/src/imprint/stealth-fetch.ts +30 -20
- package/src/imprint/stealth-token-cache.ts +2 -2
- package/src/imprint/teach-plan.ts +2 -0
- package/src/imprint/teach-state.ts +14 -9
- package/src/imprint/teach.ts +41 -7
- package/src/imprint/telemetry.ts +3 -0
- package/src/imprint/tool-candidates.ts +8 -7
- package/src/imprint/tool-plan.ts +29 -3
- package/src/imprint/tracing.ts +18 -2
- package/examples/google-flights/_shared/batchexecute.ts +0 -63
- package/examples/google-flights/_shared/flights_request.ts +0 -97
- package/examples/google-flights/get_flight_booking_details/index.ts +0 -159
- package/examples/google-flights/get_flight_booking_details/parser.ts +0 -182
- package/examples/google-flights/get_flight_booking_details/playbook.yaml +0 -138
- package/examples/google-flights/get_flight_booking_details/request-transform.ts +0 -86
- package/examples/google-flights/get_flight_booking_details/workflow.json +0 -98
- package/examples/google-flights/get_flight_calendar_prices/request-transform.ts +0 -31
- package/examples/google-flights/lookup_airport/parser.ts +0 -66
- package/examples/google-flights/lookup_airport/playbook.yaml +0 -47
- package/examples/google-flights/lookup_airport/request-transform.ts +0 -20
- package/examples/google-hotels/_shared/batchexecute.ts +0 -73
- package/examples/google-hotels/_shared/freq.ts +0 -158
- package/examples/google-hotels/autocomplete_hotel_location/parser.ts +0 -71
- package/examples/google-hotels/autocomplete_hotel_location/playbook.yaml +0 -36
- package/examples/google-hotels/autocomplete_hotel_location/request-transform.ts +0 -37
- package/examples/google-hotels/autocomplete_hotel_location/workflow.json +0 -36
- package/examples/google-hotels/get_hotel_booking_options/index.ts +0 -143
- package/examples/google-hotels/get_hotel_booking_options/parser.ts +0 -271
- package/examples/google-hotels/get_hotel_booking_options/playbook.yaml +0 -154
- package/examples/google-hotels/get_hotel_booking_options/request-transform.ts +0 -154
- package/examples/google-hotels/get_hotel_booking_options/workflow.json +0 -84
- package/examples/google-hotels/get_hotel_reviews/request-transform.ts +0 -42
- package/examples/southwest/search_southwest_flights/backends.json +0 -23
- package/examples/southwest/search_southwest_flights/cron.json +0 -19
- package/examples/southwest/search_southwest_flights/index.ts +0 -127
- package/examples/southwest/search_southwest_flights/playbook.yaml +0 -46
- package/examples/southwest/search_southwest_flights/workflow.json +0 -71
- /package/examples/google-flights/{get_flight_booking_details → get_flight_booking_options}/package.json +0 -0
- /package/examples/google-flights/{lookup_airport → get_flight_location_details}/package.json +0 -0
- /package/examples/google-hotels/{autocomplete_hotel_location → get_hotel_details}/package.json +0 -0
- /package/examples/google-hotels/{get_hotel_booking_options → get_hotel_photos}/package.json +0 -0
package/src/imprint/teach.ts
CHANGED
|
@@ -126,16 +126,29 @@ export {
|
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
128
|
* How many compile agents run in parallel when more than one tool is selected.
|
|
129
|
-
*
|
|
130
|
-
* short window raise the model's usage-policy safety-filter false-positive rate,
|
|
131
|
-
* so we trade a little wall-clock for fewer spurious refusals. Single-tool runs
|
|
132
|
-
* still use concurrency 1.
|
|
129
|
+
* Single-tool runs still use concurrency 1.
|
|
133
130
|
*/
|
|
134
|
-
const COMPILE_CONCURRENCY =
|
|
131
|
+
const COMPILE_CONCURRENCY = 3;
|
|
135
132
|
|
|
136
133
|
/** Module logger — suppressed during teach's spinner phases via muteLog(). */
|
|
137
134
|
const log = createLog('teach');
|
|
138
135
|
|
|
136
|
+
export function isProviderUsageLimitedError(err: unknown): boolean {
|
|
137
|
+
const text = err instanceof Error ? `${err.name}\n${err.message}` : String(err);
|
|
138
|
+
return (
|
|
139
|
+
/\berror_code=rate_limited\b/i.test(text) ||
|
|
140
|
+
/\byou['’]?ve hit your usage limit\b/i.test(text) ||
|
|
141
|
+
/\btry again at \d{1,2}:\d{2}\s*(?:am|pm)\b/i.test(text)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function skippedAfterProviderLimitError(err: unknown): Error {
|
|
146
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
147
|
+
return new Error(
|
|
148
|
+
`skipped because the compile provider hit a usage limit before this tool started. Retry the teach run after the provider reset; root cause: ${message.split('\n')[0] ?? message}`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
139
152
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
140
153
|
|
|
141
154
|
interface TeachOptions {
|
|
@@ -1932,13 +1945,34 @@ async function compileCandidatePlans(opts: {
|
|
|
1932
1945
|
buildPlan,
|
|
1933
1946
|
);
|
|
1934
1947
|
const outcomeByKey = new Map<string, CompileOutcome>();
|
|
1935
|
-
for (
|
|
1948
|
+
for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
|
|
1949
|
+
const level = levels[levelIndex];
|
|
1950
|
+
if (!level) continue;
|
|
1936
1951
|
const levelPlans = level.map((k) => k.plan);
|
|
1937
|
-
const levelOutcomes = await mapLimitSettled(levelPlans, concurrency, compileOne
|
|
1952
|
+
const levelOutcomes = await mapLimitSettled(levelPlans, concurrency, compileOne, {
|
|
1953
|
+
stopOnError: isProviderUsageLimitedError,
|
|
1954
|
+
});
|
|
1938
1955
|
levelPlans.forEach((plan, i) => {
|
|
1939
1956
|
const outcome = levelOutcomes[i];
|
|
1940
1957
|
if (outcome) outcomeByKey.set(plan.workflowKey, outcome);
|
|
1941
1958
|
});
|
|
1959
|
+
const usageLimitOutcome = levelOutcomes.find(
|
|
1960
|
+
(outcome) => outcome && !outcome.ok && isProviderUsageLimitedError(outcome.error),
|
|
1961
|
+
);
|
|
1962
|
+
if (usageLimitOutcome && !usageLimitOutcome.ok) {
|
|
1963
|
+
const skippedError = skippedAfterProviderLimitError(usageLimitOutcome.error);
|
|
1964
|
+
for (const plan of levelPlans) {
|
|
1965
|
+
if (!outcomeByKey.has(plan.workflowKey)) {
|
|
1966
|
+
outcomeByKey.set(plan.workflowKey, { ok: false, error: skippedError });
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
for (const remainingLevel of levels.slice(levelIndex + 1)) {
|
|
1970
|
+
for (const { plan } of remainingLevel) {
|
|
1971
|
+
outcomeByKey.set(plan.workflowKey, { ok: false, error: skippedError });
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
break;
|
|
1975
|
+
}
|
|
1942
1976
|
}
|
|
1943
1977
|
const outcomes: CompileOutcome[] = opts.plans.map(
|
|
1944
1978
|
(plan) =>
|
package/src/imprint/telemetry.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface TelemetryRequestLike {
|
|
|
9
9
|
|
|
10
10
|
const HARD_TELEMETRY_PATH_PATTERN =
|
|
11
11
|
/\/(log|gen_204|jserror|ping|beacon|csi|batchlog|metrics|stats|collect|analytics|adsct|pagead|ccm)(?=$|[/?])/i;
|
|
12
|
+
const HARD_TELEMETRY_HOST_PATTERN =
|
|
13
|
+
/(^|[.-])(analytics|telemetry|metrics?|quantummetric|smetrics|demdex|qualtrics|liveramp)([.-]|$)/i;
|
|
12
14
|
const TERMINAL_EVENT_PATH_PATTERN = /\/events?\/?$/i;
|
|
13
15
|
const EVENT_COLLECTOR_BODY_PATTERNS = [
|
|
14
16
|
/"app_(?:version|build)"/i,
|
|
@@ -31,6 +33,7 @@ export function isTelemetryRequest(request: TelemetryRequestLike): boolean {
|
|
|
31
33
|
return false;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
if (HARD_TELEMETRY_HOST_PATTERN.test(url.hostname)) return true;
|
|
34
37
|
if (isTelemetryPath(url.pathname)) return true;
|
|
35
38
|
if (!TERMINAL_EVENT_PATH_PATTERN.test(url.pathname)) return false;
|
|
36
39
|
if (request.method.toUpperCase() !== 'POST') return false;
|
|
@@ -10,7 +10,9 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
10
10
|
import { join as pathJoin } from 'node:path';
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import { inferAppApiHosts } from './app-api-hosts.ts';
|
|
13
|
+
import { getEndpointKey } from './endpoint-key.ts';
|
|
13
14
|
import { isSameRegistrableDomain, registrableDomain } from './etld.ts';
|
|
15
|
+
import { compactUrlForLlm } from './llm-url.ts';
|
|
14
16
|
import { type LLMOptions, extractJsonObject, resolveProvider } from './llm.ts';
|
|
15
17
|
import { createLog } from './log.ts';
|
|
16
18
|
import { compactRequestContexts, requestContextDigest } from './request-context.ts';
|
|
@@ -205,10 +207,12 @@ export async function detectToolCandidates(
|
|
|
205
207
|
const payload = buildToolCandidatePayload(session, {
|
|
206
208
|
trustSessionScope: opts.trustSessionScope,
|
|
207
209
|
});
|
|
210
|
+
const payloadChars = JSON.stringify(payload).length;
|
|
208
211
|
|
|
209
212
|
setSpanAttributes(span, {
|
|
210
213
|
'imprint.events_considered': payload.events.length,
|
|
211
214
|
'imprint.requests_considered': payload.requests.length,
|
|
215
|
+
'imprint.detect.payload_chars': payloadChars,
|
|
212
216
|
});
|
|
213
217
|
|
|
214
218
|
if (payload.requests.length === 0) {
|
|
@@ -222,7 +226,7 @@ export async function detectToolCandidates(
|
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
log(
|
|
225
|
-
`detecting candidate tools from ${payload.events.length} event(s), ${payload.requests.length} request(s)…`,
|
|
229
|
+
`detecting candidate tools from ${payload.events.length} event(s), ${payload.requests.length} request(s); ${Math.round(payloadChars / 1024)} KB payload…`,
|
|
226
230
|
);
|
|
227
231
|
const llm = resolveProvider(llmConfig ?? {});
|
|
228
232
|
const runOnce = async (): Promise<{
|
|
@@ -388,7 +392,7 @@ export function buildToolCandidatePayload(
|
|
|
388
392
|
seq: request.seq,
|
|
389
393
|
timestamp: request.timestamp,
|
|
390
394
|
method: request.method,
|
|
391
|
-
url: request.url,
|
|
395
|
+
url: compactUrlForLlm(request.url),
|
|
392
396
|
resourceType: request.resourceType,
|
|
393
397
|
status: request.response?.status,
|
|
394
398
|
mimeType: request.response?.mimeType,
|
|
@@ -408,7 +412,7 @@ export function buildToolCandidatePayload(
|
|
|
408
412
|
|
|
409
413
|
return {
|
|
410
414
|
site: session.site,
|
|
411
|
-
url: session.url,
|
|
415
|
+
url: compactUrlForLlm(session.url),
|
|
412
416
|
narration: session.narration.map((n) => ({
|
|
413
417
|
seq: n.seq,
|
|
414
418
|
timestamp: n.timestamp,
|
|
@@ -473,10 +477,7 @@ function candidateRequestGroupKey(request: CandidateRequestPayload): unknown[] {
|
|
|
473
477
|
function distinctEndpointFamilies(payload: ToolCandidatePayload): number {
|
|
474
478
|
const counts = new Map<string, number>();
|
|
475
479
|
for (const r of payload.requests) {
|
|
476
|
-
const
|
|
477
|
-
if (!url) continue;
|
|
478
|
-
const rpc = /[?&]rpcids?=([^&]+)/.exec(url.search)?.[1];
|
|
479
|
-
const key = rpc ? `rpc:${decodeURIComponent(rpc)}` : `${r.method} ${url.pathname}`;
|
|
480
|
+
const key = getEndpointKey(r as unknown as CapturedRequest);
|
|
480
481
|
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
481
482
|
}
|
|
482
483
|
let families = 0;
|
package/src/imprint/tool-plan.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
resolveAssignedModules,
|
|
23
23
|
} from './build-plan.ts';
|
|
24
24
|
import { withTimeout } from './concurrency.ts';
|
|
25
|
+
import { compactUrlForLlm } from './llm-url.ts';
|
|
25
26
|
import { type ProviderName, resolveProvider } from './llm.ts';
|
|
26
27
|
import { loadJsonFile } from './load-json.ts';
|
|
27
28
|
import { createLog } from './log.ts';
|
|
@@ -148,7 +149,7 @@ export function buildToolPlanPayload(opts: {
|
|
|
148
149
|
seq: r.seq,
|
|
149
150
|
timestamp: r.timestamp,
|
|
150
151
|
method: r.method,
|
|
151
|
-
url: r.url,
|
|
152
|
+
url: compactUrlForLlm(r.url),
|
|
152
153
|
status: r.response?.status,
|
|
153
154
|
mimeType: r.response?.mimeType,
|
|
154
155
|
headers: truncate(JSON.stringify(r.headers), HEADER_LIMIT) ?? '{}',
|
|
@@ -164,7 +165,7 @@ export function buildToolPlanPayload(opts: {
|
|
|
164
165
|
|
|
165
166
|
return {
|
|
166
167
|
site: session.site,
|
|
167
|
-
url: session.url,
|
|
168
|
+
url: compactUrlForLlm(session.url),
|
|
168
169
|
tool: {
|
|
169
170
|
toolName: candidate.toolName,
|
|
170
171
|
description: candidate.description,
|
|
@@ -266,8 +267,12 @@ export async function planToolCompile(opts: {
|
|
|
266
267
|
'tool planner',
|
|
267
268
|
);
|
|
268
269
|
const plan = stripCodeFences(result.text).trim();
|
|
269
|
-
|
|
270
|
+
const validation = validateToolPlan(plan);
|
|
271
|
+
if (!validation.valid) {
|
|
270
272
|
setSpanAttributes(span, { 'imprint.tool_plan.skipped': true });
|
|
273
|
+
log(
|
|
274
|
+
`tool planning skipped for ${opts.toolName} (${validation.reason}) — compiling without a plan`,
|
|
275
|
+
);
|
|
271
276
|
return undefined;
|
|
272
277
|
}
|
|
273
278
|
|
|
@@ -292,6 +297,27 @@ export async function planToolCompile(opts: {
|
|
|
292
297
|
);
|
|
293
298
|
}
|
|
294
299
|
|
|
300
|
+
export function validateToolPlan(plan: string): { valid: true } | { valid: false; reason: string } {
|
|
301
|
+
const trimmed = plan.trim();
|
|
302
|
+
if (trimmed.length === 0) return { valid: false, reason: 'empty plan' };
|
|
303
|
+
if (/^(?:\$\{credential\.[A-Za-z0-9_]+\}|\{credential\.[A-Za-z0-9_]+\})$/.test(trimmed)) {
|
|
304
|
+
return { valid: false, reason: 'looks like a credential placeholder, not a plan' };
|
|
305
|
+
}
|
|
306
|
+
if (/^\{[\s\S]*\}$/.test(trimmed) || /^\[[\s\S]*\]$/.test(trimmed)) {
|
|
307
|
+
return { valid: false, reason: 'looks like JSON, not markdown plan' };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const requiredSections = ['### Parameters', '### Requests', '### Response parsing'];
|
|
311
|
+
const missing = requiredSections.filter((section) => !trimmed.includes(section));
|
|
312
|
+
if (missing.length > 0) {
|
|
313
|
+
return { valid: false, reason: `missing required section(s): ${missing.join(', ')}` };
|
|
314
|
+
}
|
|
315
|
+
if (trimmed.length < 200)
|
|
316
|
+
return { valid: false, reason: 'too short to be an implementation plan' };
|
|
317
|
+
|
|
318
|
+
return { valid: true };
|
|
319
|
+
}
|
|
320
|
+
|
|
295
321
|
function toolPlanDisabled(): boolean {
|
|
296
322
|
const v = process.env.IMPRINT_NO_TOOL_PLAN;
|
|
297
323
|
return !!v && !['0', 'false', 'no', 'off'].includes(v.toLowerCase());
|
package/src/imprint/tracing.ts
CHANGED
|
@@ -48,6 +48,7 @@ export function suppressTracingInit(): void {
|
|
|
48
48
|
suppressInit = true;
|
|
49
49
|
}
|
|
50
50
|
const DEFAULT_TRACE_IO_MAX_CHARS = 50_000;
|
|
51
|
+
const DEFAULT_TRACE_ERROR_MAX_CHARS = 2000;
|
|
51
52
|
const CACHE_READ_MULTIPLIER = 0.1;
|
|
52
53
|
const CACHE_WRITE_MULTIPLIER = 1.25;
|
|
53
54
|
|
|
@@ -473,8 +474,23 @@ function cleanAttributeValue(value: unknown): AttributeValue | undefined {
|
|
|
473
474
|
|
|
474
475
|
function recordSpanError(span: Span, err: unknown): void {
|
|
475
476
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
476
|
-
|
|
477
|
-
|
|
477
|
+
const message = sanitizeTraceErrorMessage(error.message);
|
|
478
|
+
const stack = error.stack ? sanitizeTraceErrorMessage(error.stack) : undefined;
|
|
479
|
+
span.recordException({
|
|
480
|
+
name: error.name,
|
|
481
|
+
message,
|
|
482
|
+
stack,
|
|
483
|
+
});
|
|
484
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function sanitizeTraceErrorMessage(
|
|
488
|
+
message: string,
|
|
489
|
+
maxChars = DEFAULT_TRACE_ERROR_MAX_CHARS,
|
|
490
|
+
): string {
|
|
491
|
+
if (message.length <= maxChars) return message;
|
|
492
|
+
if (maxChars <= 0) return `...[truncated ${message.length} chars]`;
|
|
493
|
+
return `${message.slice(0, maxChars)}\n...[truncated ${message.length - maxChars} chars]`;
|
|
478
494
|
}
|
|
479
495
|
|
|
480
496
|
function openInferenceProvider(provider: string): string {
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// Decode Google's batchexecute streaming envelope used by every FlightsFrontendUi RPC.
|
|
2
|
-
//
|
|
3
|
-
// Wire format (verified against recorded seq 69/97/111/667):
|
|
4
|
-
// )]}'\n\n <- anti-XSSI magic prefix
|
|
5
|
-
// <decimal length>\n <- length line (jsonChars + 2; counts bounding newlines)
|
|
6
|
-
// [[...rows...]]\n <- one chunk = single-line JSON array of rows
|
|
7
|
-
// ...repeats...
|
|
8
|
-
//
|
|
9
|
-
// We DELIBERATELY do not slice by the length lines: the stated length is
|
|
10
|
-
// `jsonChars + 2`, so naive slice(pos, pos+len) overshoots into the next token.
|
|
11
|
-
// Because every chunk is single-line JSON (all interior newlines are escaped as
|
|
12
|
-
// \n), splitting on "\n" is exact and robust. Each real RPC result is a row
|
|
13
|
-
// ["wrb.fr", <rpcid|null>, "<doubly-encoded JSON payload>", ...]; row[2] must be
|
|
14
|
-
// JSON.parse'd a SECOND time. Sidecar rows ("di", "af.httprm", "e") are ignored.
|
|
15
|
-
|
|
16
|
-
export function decodeBatchExecute(raw: string): Array<{ rpcid: string | null; payload: any }> {
|
|
17
|
-
let text = raw;
|
|
18
|
-
if (text.startsWith(")]}'")) {
|
|
19
|
-
text = text.slice(4);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const out: Array<{ rpcid: string | null; payload: any }> = [];
|
|
23
|
-
const lines = text.split('\n');
|
|
24
|
-
|
|
25
|
-
for (const line of lines) {
|
|
26
|
-
const trimmed = line.trim();
|
|
27
|
-
if (trimmed.length === 0) continue;
|
|
28
|
-
// Skip the decimal length marker lines.
|
|
29
|
-
if (/^\d+$/.test(trimmed)) continue;
|
|
30
|
-
// Chunks are JSON arrays; anything else is noise / partial.
|
|
31
|
-
if (trimmed[0] !== '[') continue;
|
|
32
|
-
|
|
33
|
-
let chunk: any;
|
|
34
|
-
try {
|
|
35
|
-
chunk = JSON.parse(trimmed);
|
|
36
|
-
} catch {
|
|
37
|
-
// Tolerate truncated / partial trailing lines.
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (!Array.isArray(chunk)) continue;
|
|
41
|
-
|
|
42
|
-
for (const row of chunk) {
|
|
43
|
-
if (!Array.isArray(row) || row[0] !== 'wrb.fr') continue;
|
|
44
|
-
const rpcid = typeof row[1] === 'string' ? row[1] : null;
|
|
45
|
-
if (typeof row[2] !== 'string') continue;
|
|
46
|
-
let payload: any;
|
|
47
|
-
try {
|
|
48
|
-
payload = JSON.parse(row[2]);
|
|
49
|
-
} catch {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
out.push({ rpcid, payload });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return out;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function extractRpcPayload(raw: string, rpcid?: string): any {
|
|
60
|
-
const frames = decodeBatchExecute(raw);
|
|
61
|
-
const frame = rpcid != null ? frames.find((f) => f.rpcid === rpcid) : frames[0];
|
|
62
|
-
return frame?.payload;
|
|
63
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// Builds the double-encoded `f.req` form body for FlightsFrontendService RPCs
|
|
2
|
-
// (GetShoppingResults / GetCalendarPicker / GetBookingResults). Body construction
|
|
3
|
-
// is session-independent; f.sid/bl/_reqid/X-Goog-BatchExecute-Bgr are runtime state
|
|
4
|
-
// and are intentionally left untouched (transform returns the input url verbatim).
|
|
5
|
-
//
|
|
6
|
-
// NOTE (correction to spec): the leg encoding is NOT byte-for-byte identical across
|
|
7
|
-
// all three RPCs. GetCalendarPicker uses 4-slot legs ([ORIGIN,DEST,TIMES|null,STOPS])
|
|
8
|
-
// with the date range living in the outer wrapper; Shopping/Booking use the full
|
|
9
|
-
// 15-slot leg with DATE at [6]. Verified by decoding seq 97 vs seq 111.
|
|
10
|
-
|
|
11
|
-
// Fresh searches emit wrapper `...,0,0,0,1]` and leg[14]=3 (proven seq 111/140).
|
|
12
|
-
// In-page-refined searches use `...,0,1,0,1]` with return-leg[14]=1 (seq 194/425) —
|
|
13
|
-
// a UI freshness flag, not a user param; we always emit the fresh form for shopping.
|
|
14
|
-
// Booking outbound legs use [14]=3, return legs [14]=1 (seq 764/811).
|
|
15
|
-
|
|
16
|
-
function buildLeg(leg: any): any[] {
|
|
17
|
-
const out: any[] = new Array(15).fill(null);
|
|
18
|
-
out[0] = [[[leg?.origin, 0]]];
|
|
19
|
-
out[1] = [[[leg?.dest, 0]]];
|
|
20
|
-
out[2] = leg?.times ?? null;
|
|
21
|
-
out[3] = leg?.stops ?? 0;
|
|
22
|
-
// Google uses slot 4 for included alliances and carrier codes. Slot 5 is an
|
|
23
|
-
// exclusion list; putting carrier codes there inverts the filter.
|
|
24
|
-
out[4] = leg?.includeAirlines ?? leg?.alliances ?? null;
|
|
25
|
-
out[5] = leg?.excludeAirlines ?? null;
|
|
26
|
-
out[6] = leg?.date ?? null;
|
|
27
|
-
out[7] = leg?.duration ?? null;
|
|
28
|
-
out[8] = Array.isArray(leg?.selected)
|
|
29
|
-
? leg.selected.map((s: any) => [s?.origin, s?.date, s?.dest, null, s?.carrier, s?.flightNumber])
|
|
30
|
-
: null;
|
|
31
|
-
out[14] = 3;
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function buildFlightSearchParams(params: Record<string, any>): any[] {
|
|
36
|
-
const p: Record<string, any> = params ?? {};
|
|
37
|
-
// 18-slot positional search array shared by every body (proven seq 111).
|
|
38
|
-
const sp: any[] = new Array(18).fill(null);
|
|
39
|
-
sp[2] = p.tripType ?? 1; // 1=round, 2=one-way, 3=multi-city
|
|
40
|
-
sp[4] = [];
|
|
41
|
-
sp[5] = 1;
|
|
42
|
-
sp[6] = [p.adults ?? 1, p.children ?? 0, p.infantsSeat ?? 0, p.infantsLap ?? 0];
|
|
43
|
-
sp[7] = p.maxPrice != null ? [null, p.maxPrice] : null;
|
|
44
|
-
sp[10] = p.bags ? [p.bags.carryOn ?? 0, p.bags.checked ?? 0] : null;
|
|
45
|
-
const legs: any[] = Array.isArray(p.legs) ? p.legs : [];
|
|
46
|
-
sp[13] = legs.map((l: any) => buildLeg(l));
|
|
47
|
-
sp[17] = 1;
|
|
48
|
-
return sp;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function encodeFreq(payload: any): string {
|
|
52
|
-
// payload -> inner json string -> embedded in [null, inner] -> x-www-form-urlencoded.
|
|
53
|
-
// Verified byte-for-byte against seq 111: `\"`->%5C%22, `[`->%5B, `,`->%2C, `=`->%3D.
|
|
54
|
-
const inner = JSON.stringify(payload);
|
|
55
|
-
const outer = JSON.stringify([null, inner]);
|
|
56
|
-
return 'f.req=' + encodeURIComponent(outer) + '&';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function transform(
|
|
60
|
-
method: string,
|
|
61
|
-
url: string,
|
|
62
|
-
responses: Record<string, any>,
|
|
63
|
-
params?: Record<string, any>,
|
|
64
|
-
): { url: string; body: string } {
|
|
65
|
-
// method/responses are part of the contract but unused for body construction
|
|
66
|
-
// (the booking token arrives via params.flight_token). Reference to satisfy strict.
|
|
67
|
-
void method;
|
|
68
|
-
void responses;
|
|
69
|
-
|
|
70
|
-
const p: Record<string, any> = params ?? {};
|
|
71
|
-
const m = /FlightsFrontendService\/(\w+)/.exec(url);
|
|
72
|
-
if (!m || !m[1]) throw new Error(`unrecognized FlightsFrontendService rpc in url: ${url}`);
|
|
73
|
-
const rpc: string = m[1];
|
|
74
|
-
|
|
75
|
-
const sp = buildFlightSearchParams(p);
|
|
76
|
-
let payload: any;
|
|
77
|
-
|
|
78
|
-
if (rpc === 'GetShoppingResults') {
|
|
79
|
-
payload = [[], sp, 0, 0, 0, 1];
|
|
80
|
-
} else if (rpc === 'GetCalendarPicker') {
|
|
81
|
-
const legs = sp[13];
|
|
82
|
-
if (Array.isArray(legs)) sp[13] = legs.map((l: any) => (Array.isArray(l) ? l.slice(0, 4) : l));
|
|
83
|
-
payload = [null, sp, [p.startDate ?? null, p.endDate ?? null], null, [7, 7]];
|
|
84
|
-
} else if (rpc === 'GetBookingResults') {
|
|
85
|
-
const legs = sp[13];
|
|
86
|
-
if (Array.isArray(legs)) {
|
|
87
|
-
legs.forEach((l: any, i: number) => {
|
|
88
|
-
if (i >= 1 && Array.isArray(l)) l[14] = 1; // return leg(s)
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
payload = [[null, p.flight_token ?? null], sp, null, p.tripType === 2 ? 0 : 1];
|
|
92
|
-
} else {
|
|
93
|
-
throw new Error(`unsupported FlightsFrontendService rpc: ${rpc}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { url, body: encodeFreq(payload) };
|
|
97
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GENERATED by `imprint emit` — DO NOT EDIT BY HAND.
|
|
3
|
-
*
|
|
4
|
-
* Tool: get_flight_booking_details
|
|
5
|
-
* Site: google-flights
|
|
6
|
-
* Intent: Get booking/fare details and bookable options for a specific selected flight itinerary on Google Flights.
|
|
7
|
-
*
|
|
8
|
-
* To regenerate: imprint emit ~/.imprint/google-flights/get_flight_booking_details/workflow.json --force
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { dirname, join } from 'node:path';
|
|
13
|
-
import {
|
|
14
|
-
executeWorkflow,
|
|
15
|
-
type CredentialStore,
|
|
16
|
-
} from 'imprint/runtime';
|
|
17
|
-
import type { ToolResult, Workflow } from 'imprint/types';
|
|
18
|
-
|
|
19
|
-
const WORKFLOW: Workflow = {
|
|
20
|
-
"toolName": "get_flight_booking_details",
|
|
21
|
-
"intent": {
|
|
22
|
-
"description": "Get booking/fare details and bookable options for a specific selected flight itinerary on Google Flights.",
|
|
23
|
-
"userSaid": "clicked one of the one-way fares to find more details and booking details; saw the options for another flight; chose round trip fares; kept exploring round trip fare details"
|
|
24
|
-
},
|
|
25
|
-
"parameters": [
|
|
26
|
-
{
|
|
27
|
-
"name": "origin",
|
|
28
|
-
"type": "string",
|
|
29
|
-
"description": "Origin airport IATA code for the outbound leg, e.g. SJC",
|
|
30
|
-
"verified": false,
|
|
31
|
-
"verifyNote": "annotated"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "destination",
|
|
35
|
-
"type": "string",
|
|
36
|
-
"description": "Destination airport IATA code for the outbound leg, e.g. SAN",
|
|
37
|
-
"verified": false,
|
|
38
|
-
"verifyNote": "annotated"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"name": "departure_date",
|
|
42
|
-
"type": "string",
|
|
43
|
-
"description": "Outbound date in YYYY-MM-DD",
|
|
44
|
-
"verified": false,
|
|
45
|
-
"verifyNote": "annotated"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"name": "return_date",
|
|
49
|
-
"type": "string",
|
|
50
|
-
"description": "Return date in YYYY-MM-DD. Empty/omit for a one-way booking.",
|
|
51
|
-
"default": "",
|
|
52
|
-
"verified": true
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"name": "outbound_flight",
|
|
56
|
-
"type": "string",
|
|
57
|
-
"description": "Selected outbound flight as 'carrier number', e.g. 'WN 3489'",
|
|
58
|
-
"verified": false,
|
|
59
|
-
"verifyNote": "annotated"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"name": "return_flight",
|
|
63
|
-
"type": "string",
|
|
64
|
-
"description": "Selected return flight as 'carrier number' (round trip only), e.g. 'WN 3540'. Empty for one-way.",
|
|
65
|
-
"default": "",
|
|
66
|
-
"verified": false,
|
|
67
|
-
"verifyNote": "annotated"
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"name": "flight_token",
|
|
71
|
-
"type": "string",
|
|
72
|
-
"description": "Opaque per-itinerary booking token minted by the search_flights tool's flight_token output for the selected itinerary."
|
|
73
|
-
}
|
|
74
|
-
],
|
|
75
|
-
"requests": [
|
|
76
|
-
{
|
|
77
|
-
"method": "POST",
|
|
78
|
-
"url": "https://www.google.com/_/FlightsFrontendUi/data/travel.frontend.flights.FlightsFrontendService/GetBookingResults?f.sid=${state.f_sid}&bl=${state.bl}&hl=en-US&soc-app=162&soc-platform=1&soc-device=1&_reqid=1659189&rt=c",
|
|
79
|
-
"headers": {
|
|
80
|
-
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
81
|
-
"X-Same-Domain": "1",
|
|
82
|
-
"Referer": "https://www.google.com/travel/flights",
|
|
83
|
-
"x-goog-ext-259736195-jspb": "[\"en-US\",\"US\",\"USD\",2,null,[420],null,null,7,[]]"
|
|
84
|
-
},
|
|
85
|
-
"body": "f.req=${param.origin}|${param.destination}|${param.departure_date}|${param.return_date}|${param.outbound_flight}|${param.return_flight}|${param.flight_token}&",
|
|
86
|
-
"effect": "safe"
|
|
87
|
-
}
|
|
88
|
-
],
|
|
89
|
-
"site": "google-flights",
|
|
90
|
-
"bootstrap": {
|
|
91
|
-
"url": "https://www.google.com/travel/flights",
|
|
92
|
-
"waitUntil": "domcontentloaded",
|
|
93
|
-
"timeoutMs": 30000,
|
|
94
|
-
"captures": [
|
|
95
|
-
{
|
|
96
|
-
"name": "f_sid",
|
|
97
|
-
"required": false,
|
|
98
|
-
"capability": "browser_bootstrap",
|
|
99
|
-
"source": "html_regex",
|
|
100
|
-
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
101
|
-
"group": 1
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
"name": "bl",
|
|
105
|
-
"required": false,
|
|
106
|
-
"capability": "browser_bootstrap",
|
|
107
|
-
"source": "html_regex",
|
|
108
|
-
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
109
|
-
"group": 1
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
},
|
|
113
|
-
"parserModule": "./parser.ts",
|
|
114
|
-
"requestTransformModule": "./request-transform.ts",
|
|
115
|
-
"liveVerified": true
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export interface GetFlightBookingDetailsInput {
|
|
119
|
-
/** Origin airport IATA code for the outbound leg, e.g. SJC */
|
|
120
|
-
origin: string;
|
|
121
|
-
/** Destination airport IATA code for the outbound leg, e.g. SAN */
|
|
122
|
-
destination: string;
|
|
123
|
-
/** Outbound date in YYYY-MM-DD */
|
|
124
|
-
departure_date: string;
|
|
125
|
-
/** Return date in YYYY-MM-DD. Empty/omit for a one-way booking. */
|
|
126
|
-
return_date?: string;
|
|
127
|
-
/** Selected outbound flight as 'carrier number', e.g. 'WN 3489' */
|
|
128
|
-
outbound_flight: string;
|
|
129
|
-
/** Selected return flight as 'carrier number' (round trip only), e.g. 'WN 3540'. Empty for one-way. */
|
|
130
|
-
return_flight?: string;
|
|
131
|
-
/** Opaque per-itinerary booking token minted by the search_flights tool's flight_token output for the selected itinerary. */
|
|
132
|
-
flight_token: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function getFlightBookingDetails(
|
|
136
|
-
input: GetFlightBookingDetailsInput,
|
|
137
|
-
opts: { credentials?: CredentialStore; fetchImpl?: typeof fetch; initialState?: Record<string, unknown> } = {},
|
|
138
|
-
): Promise<ToolResult> {
|
|
139
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
140
|
-
const params: Record<string, string | number | boolean> = {
|
|
141
|
-
return_date: input.return_date ?? "",
|
|
142
|
-
return_flight: input.return_flight ?? "",
|
|
143
|
-
origin: input.origin,
|
|
144
|
-
destination: input.destination,
|
|
145
|
-
departure_date: input.departure_date,
|
|
146
|
-
outbound_flight: input.outbound_flight,
|
|
147
|
-
flight_token: input.flight_token,
|
|
148
|
-
};
|
|
149
|
-
return executeWorkflow({
|
|
150
|
-
workflow: WORKFLOW,
|
|
151
|
-
params,
|
|
152
|
-
credentials: opts.credentials,
|
|
153
|
-
fetchImpl: opts.fetchImpl,
|
|
154
|
-
initialState: opts.initialState,
|
|
155
|
-
workflowPath: join(__dirname, 'workflow.json'),
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export { WORKFLOW };
|