@alexkroman1/aai-cli 6.11.0 → 8.0.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 +1 -0
- package/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
- package/dist/{_bundler-C2BDwU7r.mjs → _bundler-CDuYl5Gb.mjs} +2 -2
- package/dist/_dev-env.d.ts +22 -2
- package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-Me06wgeF.mjs} +38 -47
- package/dist/_dev-server.d.ts +0 -17
- package/dist/{_init-C57bWAQ8.mjs → _init-BhEfQ6Yi.mjs} +3 -2
- package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
- package/dist/_preflight.d.ts +1 -1
- package/dist/_resource-commands.d.ts +23 -0
- package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-z-USxPtL.mjs} +16 -12
- package/dist/_slug-api.d.ts +5 -2
- package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
- package/dist/{_templates-Dxb_P2Wz.mjs → _templates-4WcKOjS5.mjs} +5 -6
- package/dist/_templates.d.ts +3 -4
- package/dist/_workflow-scan.d.ts +108 -0
- package/dist/{build-Dfza2pRI.mjs → build-BBxf8vom.mjs} +6 -3
- package/dist/cli.mjs +346 -349
- package/dist/{client-bundler-BEIqgOtd.mjs → client-bundler-CWnG42cU.mjs} +2 -1
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-vbpjGzqs.mjs → delete-Dl_LSfvI.mjs} +3 -3
- package/dist/{deploy-B8lubiRT.mjs → deploy-DRC8jWr8.mjs} +5 -5
- package/dist/{dev-Uq5ujP8a.mjs → dev-BvcctCn2.mjs} +1 -1
- package/dist/eval-DfQ8cm5p.mjs +56 -0
- package/dist/eval.d.ts +43 -0
- package/dist/{init-l6xfU4xX.mjs → init-CUo87gcI.mjs} +3 -3
- package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
- package/dist/{logs-BWVGSjLR.mjs → logs-CQ2T46dr.mjs} +2 -2
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +442 -80
- package/dist/scaffold/package.json +6 -3
- package/dist/scaffold/server.mjs +11 -4
- package/dist/scaffold/tsconfig.json +0 -1
- package/dist/{secret-BuMuFR4B.mjs → secret-Ct34kdgU.mjs} +2 -2
- package/dist/{storage-DzRZ-eCw.mjs → storage-i_79ZwAR.mjs} +2 -2
- package/dist/{studio-BCNUpDgP.mjs → studio-CzTzPqe1.mjs} +4 -4
- package/dist/templates/briefing-desk/agent.test.ts +350 -0
- package/dist/templates/briefing-desk/agent.ts +30 -0
- package/dist/templates/briefing-desk/shared.ts +200 -0
- package/dist/templates/briefing-desk/system-prompt.md +39 -0
- package/dist/templates/briefing-desk/tools/briefing_so_far.ts +35 -0
- package/dist/templates/briefing-desk/tools/research_topic.ts +88 -0
- package/dist/templates/briefing-desk/tools/verify_claim.ts +77 -0
- package/dist/templates/call-audit/agent.eval.test.ts +474 -0
- package/dist/templates/call-audit/agent.test.ts +57 -204
- package/dist/templates/call-audit/agent.ts +32 -19
- package/dist/templates/call-audit/client.tsx +17 -54
- package/dist/templates/call-audit/workflows/audit.ts +11 -18
- package/dist/templates/call-audit/workflows/ingest.ts +106 -114
- package/dist/templates/call-audit/workflows/media.ts +2 -12
- package/dist/templates/call-audit/workflows/summarize.ts +47 -52
- package/dist/templates/call-audit/workflows/sync-api.ts +7 -7
- package/dist/templates/code-interpreter/agent.eval.test.ts +165 -0
- package/dist/templates/dispatch-center/agent.eval.test.ts +349 -0
- package/dist/templates/dispatch-center/agent.test.ts +149 -25
- package/dist/templates/dispatch-center/client.tsx +239 -129
- package/dist/templates/dispatch-center/shared.ts +99 -1
- package/dist/templates/dispatch-center/system-prompt.md +3 -1
- package/dist/templates/dispatch-center/tools/incident_add_note.ts +16 -13
- package/dist/templates/dispatch-center/tools/incident_create.ts +19 -1
- package/dist/templates/dispatch-center/tools/incident_escalate.ts +68 -59
- package/dist/templates/dispatch-center/tools/incident_triage.ts +57 -43
- package/dist/templates/dispatch-center/tools/incident_update_status.ts +49 -40
- package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +8 -2
- package/dist/templates/dispatch-center/tools/resources_dispatch.ts +69 -52
- package/dist/templates/dispatch-center/tools/resources_update_status.ts +45 -41
- package/dist/templates/embedded-assets/agent.eval.test.ts +138 -0
- package/dist/templates/embedded-assets/agent.test.ts +17 -14
- package/dist/templates/health-assistant/agent.eval.test.ts +167 -0
- package/dist/templates/health-assistant/agent.test.ts +22 -4
- package/dist/templates/infocom-adventure/agent.eval.test.ts +158 -0
- package/dist/templates/infocom-adventure/agent.test.ts +53 -24
- package/dist/templates/infocom-adventure/agent.ts +21 -0
- package/dist/templates/infocom-adventure/client.tsx +246 -164
- package/dist/templates/infocom-adventure/shared.ts +20 -9
- package/dist/templates/infocom-adventure/system-prompt.md +1 -1
- package/dist/templates/infocom-adventure/tools/game_state_move.ts +3 -2
- package/dist/templates/link-digest/agent.eval.test.ts +195 -0
- package/dist/templates/link-digest/agent.test.ts +24 -19
- package/dist/templates/link-digest/client.tsx +47 -61
- package/dist/templates/link-digest/workflows/digest.ts +19 -31
- package/dist/templates/math-buddy/agent.eval.test.ts +146 -0
- package/dist/templates/night-owl/agent.eval.test.ts +272 -0
- package/dist/templates/night-owl/agent.test.ts +70 -19
- package/dist/templates/night-owl/agent.ts +5 -0
- package/dist/templates/night-owl/client.tsx +56 -56
- package/dist/templates/night-owl/shared.ts +24 -0
- package/dist/templates/night-owl/tools/recommend.ts +18 -6
- package/dist/templates/personal-finance/agent.eval.test.ts +168 -0
- package/dist/templates/personal-finance/system-prompt.md +4 -0
- package/dist/templates/pipeline-simple/agent.eval.test.ts +78 -0
- package/dist/templates/pipeline-simple/agent.ts +2 -2
- package/dist/templates/pizza-ordering/agent.eval.test.ts +171 -0
- package/dist/templates/pizza-ordering/agent.test.ts +42 -27
- package/dist/templates/pizza-ordering/client.tsx +9 -26
- package/dist/templates/pizza-ordering/shared.ts +20 -1
- package/dist/templates/pizza-ordering/system-prompt.md +6 -0
- package/dist/templates/plan-and-execute/agent.eval.test.ts +209 -0
- package/dist/templates/plan-and-execute/agent.test.ts +99 -58
- package/dist/templates/plan-and-execute/agent.ts +1 -1
- package/dist/templates/plan-and-execute/client.tsx +12 -15
- package/dist/templates/plan-and-execute/shared.ts +71 -2
- package/dist/templates/plan-and-execute/tools/plan_status.ts +18 -6
- package/dist/templates/plan-and-execute/tools/revise_plan.ts +18 -10
- package/dist/templates/plan-and-execute/tools/start_plan.ts +13 -2
- package/dist/templates/plan-and-execute/tools/work_next_step.ts +70 -22
- package/dist/templates/podcast-digest/agent.eval.test.ts +411 -0
- package/dist/templates/podcast-digest/agent.test.ts +717 -0
- package/dist/templates/podcast-digest/agent.ts +139 -0
- package/dist/templates/podcast-digest/client.tsx +154 -0
- package/dist/templates/podcast-digest/workflows/digest.ts +444 -0
- package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
- package/dist/templates/podcast-digest/workflows/slack.ts +88 -0
- package/dist/templates/recap-workflow/agent.eval.test.ts +366 -0
- package/dist/templates/recap-workflow/agent.test.ts +97 -94
- package/dist/templates/recap-workflow/tools/recap_progress.ts +9 -12
- package/dist/templates/recap-workflow/tools/recap_status.ts +8 -9
- package/dist/templates/recap-workflow/tools/request_recap.ts +2 -1
- package/dist/templates/recap-workflow/workflows/recap.ts +36 -40
- package/dist/templates/redline/agent.eval.test.ts +209 -0
- package/dist/templates/redline/agent.test.ts +17 -15
- package/dist/templates/redline/client.tsx +12 -12
- package/dist/templates/redline/workflows/redline.ts +19 -31
- package/dist/templates/research-workflow/agent.eval.test.ts +329 -0
- package/dist/templates/research-workflow/agent.test.ts +60 -59
- package/dist/templates/research-workflow/tools/research_progress.ts +9 -12
- package/dist/templates/research-workflow/tools/research_status.ts +9 -11
- package/dist/templates/research-workflow/workflows/research.ts +44 -61
- package/dist/templates/retail/address.ts +87 -1
- package/dist/templates/retail/agent.eval.test.ts +314 -0
- package/dist/templates/retail/agent.test.ts +195 -112
- package/dist/templates/retail/agent.ts +35 -1
- package/dist/templates/retail/cancel.ts +116 -0
- package/dist/templates/retail/client.tsx +265 -117
- package/dist/templates/retail/payment.ts +107 -0
- package/dist/templates/retail/pending.test.ts +196 -0
- package/dist/templates/retail/pending.ts +137 -0
- package/dist/templates/retail/registry.test.ts +150 -7
- package/dist/templates/retail/returns.ts +102 -0
- package/dist/templates/retail/shared.test.ts +42 -0
- package/dist/templates/retail/shared.ts +35 -0
- package/dist/templates/retail/store.test.ts +84 -15
- package/dist/templates/retail/store.ts +231 -48
- package/dist/templates/retail/swap.test.ts +4 -4
- package/dist/templates/retail/swap.ts +248 -15
- package/dist/templates/retail/system-prompt.md +69 -28
- package/dist/templates/retail/tools/cancel_change.ts +33 -0
- package/dist/templates/retail/tools/cancel_pending_order.ts +16 -60
- package/dist/templates/retail/tools/confirm_change.ts +42 -0
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -67
- package/dist/templates/retail/tools/find_user_id_by_email.ts +8 -5
- package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +4 -5
- package/dist/templates/retail/tools/get_item_details.ts +3 -4
- package/dist/templates/retail/tools/get_order_details.ts +3 -4
- package/dist/templates/retail/tools/get_product_details.ts +3 -4
- package/dist/templates/retail/tools/get_user_details.ts +2 -2
- package/dist/templates/retail/tools/list_all_product_types.ts +5 -5
- package/dist/templates/retail/tools/modify_pending_order_address.ts +13 -25
- package/dist/templates/retail/tools/modify_pending_order_items.ts +23 -62
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +16 -78
- package/dist/templates/retail/tools/modify_user_address.ts +13 -16
- package/dist/templates/retail/tools/return_delivered_order_items.ts +17 -72
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +12 -6
- package/dist/templates/simple/agent.eval.test.ts +54 -0
- package/dist/templates/solo-rpg/agent.eval.test.ts +320 -0
- package/dist/templates/solo-rpg/agent.test.ts +227 -48
- package/dist/templates/solo-rpg/client.tsx +38 -37
- package/dist/templates/solo-rpg/shared.ts +145 -19
- package/dist/templates/solo-rpg/system-prompt.md +3 -2
- package/dist/templates/solo-rpg/tools/action_roll.ts +82 -63
- package/dist/templates/solo-rpg/tools/burn_momentum.ts +85 -50
- package/dist/templates/solo-rpg/tools/check_state.ts +24 -7
- package/dist/templates/solo-rpg/tools/load_game.ts +13 -1
- package/dist/templates/solo-rpg/tools/save_game.ts +16 -7
- package/dist/templates/solo-rpg/tools/setup_character.ts +22 -2
- package/dist/templates/solo-rpg/tools/update_state.ts +113 -100
- package/dist/templates/spoken-summary/agent.eval.test.ts +342 -0
- package/dist/templates/spoken-summary/agent.test.ts +68 -97
- package/dist/templates/spoken-summary/agent.ts +30 -17
- package/dist/templates/spoken-summary/client.tsx +10 -50
- package/dist/templates/spoken-summary/workflows/summarize.ts +17 -22
- package/dist/templates/spoken-summary/workflows/transcribe.ts +19 -26
- package/dist/templates/support-line/agent.eval.test.ts +273 -0
- package/dist/templates/support-line/agent.test.ts +11 -16
- package/dist/templates/support-line/agent.ts +1 -1
- package/dist/templates/support-line/client.tsx +9 -9
- package/dist/templates/support-line/nodes.ts +100 -0
- package/dist/templates/support-line/procedure.ts +407 -0
- package/dist/templates/support-line/system-prompt.md +2 -1
- package/dist/templates/support-line/tools/answer_question.ts +66 -4
- package/dist/templates/transcription-workflow/agent.eval.test.ts +234 -0
- package/dist/templates/transcription-workflow/agent.test.ts +94 -194
- package/dist/templates/transcription-workflow/agent.ts +1 -1
- package/dist/templates/transcription-workflow/client.tsx +17 -42
- package/dist/templates/transcription-workflow/workflows/batch.ts +19 -22
- package/dist/templates/transcription-workflow/workflows/normalize.ts +90 -186
- package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
- package/dist/templates/transcription-workflow/workflows/stream.ts +4 -5
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +6 -6
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +32 -26
- package/dist/templates/transcription-workflow/workflows/wav.ts +0 -31
- package/dist/templates/travel-concierge/agent.eval.test.ts +323 -0
- package/dist/templates/travel-concierge/agent.test.ts +174 -43
- package/dist/templates/travel-concierge/agent.ts +18 -9
- package/dist/templates/travel-concierge/client.tsx +11 -23
- package/dist/templates/travel-concierge/routing.ts +34 -15
- package/dist/templates/travel-concierge/shared.ts +180 -12
- package/dist/templates/travel-concierge/system-prompt.md +7 -1
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +8 -5
- package/dist/templates/travel-concierge/tools/book_excursion.ts +8 -5
- package/dist/templates/travel-concierge/tools/book_hotel.ts +8 -5
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +8 -5
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +9 -5
- package/dist/templates/travel-concierge/tools/search_excursions.ts +9 -5
- package/dist/templates/travel-concierge/tools/search_flights.ts +9 -6
- package/dist/templates/travel-concierge/tools/search_hotels.ts +9 -5
- package/dist/templates/travel-concierge/tools/update_ticket.ts +8 -5
- package/dist/templates/web-researcher/agent.eval.test.ts +121 -0
- package/dist/templates/web-researcher/system-prompt.md +15 -4
- package/dist/{test-9kPKJI-w.mjs → test-BvyT6sAc.mjs} +24 -10
- package/dist/test.d.ts +29 -5
- package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-CCVEDjm1.mjs} +241 -3
- package/dist/worker-bundler.mjs +1 -1
- package/dist/{workflow-CFpxOFfQ.mjs → workflow-BflATMmU.mjs} +47 -12
- package/dist/workflow-bundler.d.ts +6 -1
- package/dist/workflow.d.ts +1 -1
- package/package.json +5 -4
- package/dist/eject-31gjtaHF.mjs +0 -116
- package/dist/eject.d.ts +0 -60
- package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
- package/dist/templates/infocom-adventure/tools/game_state_history.ts +0 -14
- package/dist/templates/support-line/graph.ts +0 -224
- package/dist/{_config-D_s09e7g.mjs → _config-CmJOFsAP.mjs} +1 -1
- /package/dist/templates/plan-and-execute/{graph.ts → procedure.ts} +0 -0
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* | `ToFlightBookingAssistant` & friends (delegation tools) | the four tools {@link SPECIALISTS} generates in `routing.ts` |
|
|
15
15
|
* | `CompleteOrEscalate` | `complete_or_escalate`, which pops the same stack |
|
|
16
16
|
* | `interrupt_before=["…_sensitive_tools"]` | {@link stageAction} + `confirm_action` |
|
|
17
|
+
* | a specialist node's BOUND tool set | {@link requireDesk}, checked by every desk tool |
|
|
17
18
|
* | `fetch_user_flight_information` (sqlite) | `lookup_booking` over {@link seedTrip} |
|
|
18
19
|
*
|
|
19
20
|
* **The dialog stack is what keeps a long call on the rails.** Their insight is
|
|
@@ -23,7 +24,9 @@
|
|
|
23
24
|
* session's prompt is fixed at connect — so the specialist's brief arrives as
|
|
24
25
|
* the DELEGATION TOOL'S RESULT, which is the last thing the model reads before
|
|
25
26
|
* it answers. The stack itself is real state either way, which is what
|
|
26
|
-
* `complete_or_escalate` pops and what the sidebar renders
|
|
27
|
+
* `complete_or_escalate` pops and what the sidebar renders — and what
|
|
28
|
+
* {@link requireDesk} makes binding, so the position is never merely a label on
|
|
29
|
+
* work that happened somewhere else.
|
|
27
30
|
*
|
|
28
31
|
* **`interrupt_before` becomes a spoken confirmation, and that is not a
|
|
29
32
|
* downgrade.** Their graph halts before a sensitive tool and waits for a human
|
|
@@ -35,7 +38,15 @@
|
|
|
35
38
|
* agent that can rebook a flight and one that can rebook a flight *by mistake*.
|
|
36
39
|
*/
|
|
37
40
|
|
|
38
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
type DeepReadonly,
|
|
43
|
+
type DialogSpec,
|
|
44
|
+
dialog,
|
|
45
|
+
pushCapped,
|
|
46
|
+
sessionSlot,
|
|
47
|
+
type ToolContext,
|
|
48
|
+
type ToolFailure,
|
|
49
|
+
} from "@alexkroman1/aai";
|
|
39
50
|
|
|
40
51
|
// ─── The booking world ───────────────────────────────────────────────────────
|
|
41
52
|
// Their notebook downloads a sqlite database of a real airline's schedule and
|
|
@@ -199,6 +210,19 @@ export interface Specialist {
|
|
|
199
210
|
instructions: string;
|
|
200
211
|
}
|
|
201
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Each brief spells the STAGE-then-read-back ORDER out, and that is a fix
|
|
215
|
+
* measured rather than a preference.
|
|
216
|
+
*
|
|
217
|
+
* The flight desk's used to say "read the fare change back to the caller before
|
|
218
|
+
* you change a ticket", which is true of `confirm_action` and reads as a rule
|
|
219
|
+
* about `update_ticket` — and live, that is how it was read: the desk searched,
|
|
220
|
+
* quoted a fare it had not staged, waited for a yes, and then called
|
|
221
|
+
* `confirm_action` with nothing waiting for it. Two of three runs never staged
|
|
222
|
+
* anything at all. The sentence to read back IS the staging tool's return
|
|
223
|
+
* value, so a brief that puts the read-back first describes a call the desk
|
|
224
|
+
* cannot make.
|
|
225
|
+
*/
|
|
202
226
|
export const SPECIALISTS: Record<SpecialistId, Specialist> = {
|
|
203
227
|
flight: {
|
|
204
228
|
title: "flight desk",
|
|
@@ -206,9 +230,22 @@ export const SPECIALISTS: Record<SpecialistId, Specialist> = {
|
|
|
206
230
|
"You are now the flight desk. Handle searching and changing flights only.",
|
|
207
231
|
"Search before you quote anything, and be persistent: if the first search",
|
|
208
232
|
"finds nothing, widen it rather than telling the caller there is nothing.",
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
"
|
|
233
|
+
"update_ticket is a QUOTE, not a change: it moves nothing, so call it as",
|
|
234
|
+
"soon as you know which flight they want and never ask permission first.",
|
|
235
|
+
"Changing a ticket is TWO steps and never one: call update_ticket first —",
|
|
236
|
+
"it changes nothing and hands you the sentence to say — then read the",
|
|
237
|
+
"flight number, the day, the departure time and the fare change back to",
|
|
238
|
+
"the caller and ask them plainly to confirm. A ticket is only changed once",
|
|
239
|
+
"confirm_action has run, and confirm_action has nothing to apply until",
|
|
240
|
+
"update_ticket has staged it.",
|
|
241
|
+
"Never quote a departure time or a fare you did not just read out of a",
|
|
242
|
+
"tool result, and never end a turn on 'shall I confirm?' with nothing",
|
|
243
|
+
"staged: update_ticket goes out in the SAME turn as that question, before",
|
|
244
|
+
"you stop. There is nothing for a yes to apply until it has.",
|
|
245
|
+
"Your next action, right now, is a TOOL and not a sentence: call",
|
|
246
|
+
"update_ticket if you already know which flight they want, or",
|
|
247
|
+
"search_flights if you do not. Do not speak until one of them has",
|
|
248
|
+
"answered — anything you would say before that is invented.",
|
|
212
249
|
"If the caller wants a hotel, a car or something to do, or changes their",
|
|
213
250
|
"mind, call complete_or_escalate instead of improvising.",
|
|
214
251
|
].join(" "),
|
|
@@ -219,8 +256,18 @@ export const SPECIALISTS: Record<SpecialistId, Specialist> = {
|
|
|
219
256
|
"You are now the hotel desk. Handle searching and booking hotels only.",
|
|
220
257
|
"Ask which city and roughly what budget, then search. Offer at most three",
|
|
221
258
|
"options out loud, cheapest first, with the neighbourhood and nightly rate.",
|
|
222
|
-
"
|
|
223
|
-
"
|
|
259
|
+
"book_hotel is a QUOTE, not a change: it holds nothing, so call it as",
|
|
260
|
+
"soon as you know what the caller wants and never ask permission",
|
|
261
|
+
"first.",
|
|
262
|
+
"Booking is TWO steps and never one: call book_hotel first — it holds",
|
|
263
|
+
"nothing and hands you the sentence to say — then read the hotel, the",
|
|
264
|
+
"number of nights and the total back and ask them to confirm. A room is",
|
|
265
|
+
"only held once confirm_action has run, and confirm_action has nothing to",
|
|
266
|
+
"apply until book_hotel has staged it.",
|
|
267
|
+
"Your next action, right now, is a TOOL and not a sentence: call",
|
|
268
|
+
"book_hotel if you already know which room and how many nights, or",
|
|
269
|
+
"search_hotels if you do not. Do not speak until one of them has",
|
|
270
|
+
"answered — anything you would say before that is invented.",
|
|
224
271
|
"If the caller wants flights, a car or an excursion, call",
|
|
225
272
|
"complete_or_escalate.",
|
|
226
273
|
].join(" "),
|
|
@@ -230,8 +277,19 @@ export const SPECIALISTS: Record<SpecialistId, Specialist> = {
|
|
|
230
277
|
instructions: [
|
|
231
278
|
"You are now the car rental desk. Handle searching and booking cars only.",
|
|
232
279
|
"Ask the city and how many days, then search. Name the vendor, the tier",
|
|
233
|
-
"and the daily rate
|
|
234
|
-
"
|
|
280
|
+
"and the daily rate.",
|
|
281
|
+
"book_car_rental is a QUOTE, not a change: it holds nothing, so call it as",
|
|
282
|
+
"soon as you know what the caller wants and never ask permission",
|
|
283
|
+
"first.",
|
|
284
|
+
"Reserving is TWO steps and never one: call book_car_rental first — it",
|
|
285
|
+
"reserves nothing and hands you the sentence to say — then read the total",
|
|
286
|
+
"back and ask them to confirm. A car is only reserved once confirm_action",
|
|
287
|
+
"has run, and confirm_action has nothing to apply until book_car_rental",
|
|
288
|
+
"has staged it.",
|
|
289
|
+
"Your next action, right now, is a TOOL and not a sentence: call",
|
|
290
|
+
"book_car_rental if you already know which car and how many days, or",
|
|
291
|
+
"search_car_rentals if you do not. Do not speak until one of them has",
|
|
292
|
+
"answered — anything you would say before that is invented.",
|
|
235
293
|
"If the caller wants flights, a hotel or an excursion, call",
|
|
236
294
|
"complete_or_escalate.",
|
|
237
295
|
].join(" "),
|
|
@@ -243,7 +301,17 @@ export const SPECIALISTS: Record<SpecialistId, Specialist> = {
|
|
|
243
301
|
"to do only. Ask the city and what kind of thing they enjoy, then search.",
|
|
244
302
|
"Describe two or three options in a sentence each — this is the part of",
|
|
245
303
|
"the call a caller actually wants to hear about.",
|
|
246
|
-
"
|
|
304
|
+
"book_excursion is a QUOTE, not a change: it holds nothing, so call it as",
|
|
305
|
+
"soon as you know what the caller wants and never ask permission",
|
|
306
|
+
"first.",
|
|
307
|
+
"Booking is TWO steps and never one: call book_excursion first — it books",
|
|
308
|
+
"nothing and hands you the sentence to say — then read it back and ask",
|
|
309
|
+
"them to confirm. A booking is only made once confirm_action has run, and",
|
|
310
|
+
"confirm_action has nothing to apply until book_excursion has staged it.",
|
|
311
|
+
"Your next action, right now, is a TOOL and not a sentence: call",
|
|
312
|
+
"book_excursion if you already know which one they want, or",
|
|
313
|
+
"search_excursions if you do not. Do not speak until one of them has",
|
|
314
|
+
"answered — anything you would say before that is invented.",
|
|
247
315
|
"If the caller wants flights, a hotel or a car, call complete_or_escalate.",
|
|
248
316
|
].join(" "),
|
|
249
317
|
},
|
|
@@ -339,8 +407,94 @@ export function note(state: TripState, entry: string): void {
|
|
|
339
407
|
pushCapped(state.log, entry, MAX_LOG_ENTRIES);
|
|
340
408
|
}
|
|
341
409
|
|
|
410
|
+
// ─── The desk gate ───────────────────────────────────────────────────────────
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Is the call at `id`'s desk? A {@link ToolFailure} naming the way in if not.
|
|
414
|
+
*
|
|
415
|
+
* **This is the narrowing their graph gets for free, and it used to be prose.**
|
|
416
|
+
* Each specialist node there binds its own tool set, so a node physically cannot
|
|
417
|
+
* call another desk's tools; a voice session has ONE model with ONE tool list
|
|
418
|
+
* for its whole life, so for a long time the stack was real state that the model
|
|
419
|
+
* was merely ASKED to respect — `agent.ts` said so in place, and this guide's
|
|
420
|
+
* own eval predicted the failure. Measured: the prompt lost 0 of 5 live runs.
|
|
421
|
+
* The caller said "I need a hotel in Boston" and the model called
|
|
422
|
+
* `search_hotels` at the concierge desk every time, so `dialogState` said
|
|
423
|
+
* `primary` while the call was plainly doing hotel work — a stack the sidebar
|
|
424
|
+
* renders and nothing keeps true.
|
|
425
|
+
*
|
|
426
|
+
* A tool list cannot be narrowed mid-session, but a tool can REFUSE, and a
|
|
427
|
+
* refusal is a thing the model recovers from inside the same turn: it reads the
|
|
428
|
+
* failure, calls `to_hotel_assistant`, gets the desk's brief, and searches. So
|
|
429
|
+
* the delegation is now structural — every desk tool is reachable only from its
|
|
430
|
+
* own desk — and the brief the delegation returns is guaranteed to have been
|
|
431
|
+
* read before any of that desk's work happens, which was the whole point of
|
|
432
|
+
* handing it back as a tool result.
|
|
433
|
+
*
|
|
434
|
+
* It costs one wasted round trip the first time the model reaches past the
|
|
435
|
+
* stack, which is the right price: the alternative is a desk whose position is
|
|
436
|
+
* decoration.
|
|
437
|
+
*/
|
|
438
|
+
export function requireDesk(state: FrozenTripState, id: SpecialistId): ToolFailure | undefined {
|
|
439
|
+
const at = activeAssistant(state);
|
|
440
|
+
if (at === id) return undefined;
|
|
441
|
+
const here = at === "primary" ? "the main concierge" : SPECIALISTS[at].title;
|
|
442
|
+
return {
|
|
443
|
+
error:
|
|
444
|
+
`That tool belongs to the ${SPECIALISTS[id].title} and this call is at ${here}. ` +
|
|
445
|
+
`Nothing was searched, staged or booked. Call to_${id}_assistant with what the ` +
|
|
446
|
+
"caller asked for — its answer is that desk's brief — and then call this tool again. " +
|
|
447
|
+
"Do not tell the caller about any of this; they should hear one continuous conversation.",
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
342
451
|
// ─── The confirmation gate ───────────────────────────────────────────────────
|
|
343
452
|
|
|
453
|
+
/**
|
|
454
|
+
* The gate as a STATE MAP — their `interrupt_before`, which is a graph construct.
|
|
455
|
+
*
|
|
456
|
+
* This port had to hand-roll it: a voice session had no graph, so "halt before a
|
|
457
|
+
* sensitive tool and resume on approval" became "every sensitive tool STAGES and
|
|
458
|
+
* `confirm_action` is the only thing that applies one". That half is unchanged
|
|
459
|
+
* and is still the mechanism — see {@link stageAction}. What the machine adds is
|
|
460
|
+
* the PRECONDITION on the other side of the halt: `confirm_action` and
|
|
461
|
+
* `cancel_action` are legal only while something is actually waiting, which was
|
|
462
|
+
* a null check inside each of them and is now `when`.
|
|
463
|
+
*
|
|
464
|
+
* **The staging tools are deliberately NOT gated on `browsing`.**
|
|
465
|
+
* {@link stageAction}'s refusal NAMES the sentence already waiting, which is what
|
|
466
|
+
* lets the model settle that one first and restage the rest; a state gate can
|
|
467
|
+
* only say "you are in awaitingConfirmation", since a state's instruction is
|
|
468
|
+
* static. So the specific refusal stays where it is and the flow follows it.
|
|
469
|
+
*/
|
|
470
|
+
const gateSpec = {
|
|
471
|
+
initial: "browsing",
|
|
472
|
+
states: {
|
|
473
|
+
browsing: {
|
|
474
|
+
instruction:
|
|
475
|
+
"Nothing is waiting for the caller's yes. Stage a change with a booking tool first.",
|
|
476
|
+
on: { STAGED: "awaitingConfirmation" },
|
|
477
|
+
},
|
|
478
|
+
awaitingConfirmation: {
|
|
479
|
+
instruction:
|
|
480
|
+
"Read the staged change back and hear a clear yes or no, then use confirm_action or cancel_action.",
|
|
481
|
+
on: { SETTLED: "browsing" },
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
} as const satisfies DialogSpec;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Whether a change is waiting on the caller's word.
|
|
488
|
+
*
|
|
489
|
+
* Its own slot, beside {@link tripSlot}: the flow holds the POSITION and the
|
|
490
|
+
* trip holds the staged action itself, because an inspectable
|
|
491
|
+
* {@link PendingAction} is what `confirm_action` re-derives the effect from. One
|
|
492
|
+
* tool call always moves both — {@link stageAction} sends `STAGED` in the same
|
|
493
|
+
* synchronous window it writes `pending` in, and the two settling tools send
|
|
494
|
+
* `SETTLED` only on success.
|
|
495
|
+
*/
|
|
496
|
+
export const gateFlow = dialog("gate", gateSpec);
|
|
497
|
+
|
|
344
498
|
/**
|
|
345
499
|
* Describe a staged action in one sentence, in the second person — this is
|
|
346
500
|
* read aloud, so it is a question's worth of text and not a receipt.
|
|
@@ -396,6 +550,7 @@ export function describeAction(action: DeepReadonly<PendingAction>): string | To
|
|
|
396
550
|
* model ask about that one first and restage the rest afterwards.
|
|
397
551
|
*/
|
|
398
552
|
export function stageAction(
|
|
553
|
+
ctx: ToolContext,
|
|
399
554
|
state: TripState,
|
|
400
555
|
action: PendingAction,
|
|
401
556
|
):
|
|
@@ -413,6 +568,11 @@ export function stageAction(
|
|
|
413
568
|
};
|
|
414
569
|
}
|
|
415
570
|
state.pending = action;
|
|
571
|
+
// The flow moves in the SAME synchronous window `pending` is written in, so
|
|
572
|
+
// the position and the payload cannot be observed disagreeing. `gateFlow` is
|
|
573
|
+
// a different slot from `tripSlot`, so this is not a nested write to the draft
|
|
574
|
+
// being held — the open-draft guard is per slot.
|
|
575
|
+
gateFlow.send(ctx, { type: "STAGED" });
|
|
416
576
|
note(state, `Awaiting confirmation: ${described}`);
|
|
417
577
|
return {
|
|
418
578
|
awaitingConfirmation: true,
|
|
@@ -421,13 +581,21 @@ export function stageAction(
|
|
|
421
581
|
};
|
|
422
582
|
}
|
|
423
583
|
|
|
424
|
-
/**
|
|
584
|
+
/**
|
|
585
|
+
* Apply the staged action. The ONE place any of them takes effect.
|
|
586
|
+
*
|
|
587
|
+
* The "nothing is waiting" guard is gone: `confirm_action` is gated on
|
|
588
|
+
* `awaitingConfirmation`, so reaching here means something IS staged. The
|
|
589
|
+
* remaining `!action` arm is the flow and the payload disagreeing, which one
|
|
590
|
+
* synchronous window per transition is what rules out — it reports rather than
|
|
591
|
+
* throwing, because a live call is the wrong place to find out.
|
|
592
|
+
*/
|
|
425
593
|
export function applyPending(
|
|
426
594
|
state: TripState,
|
|
427
595
|
): { applied: string; reference?: string } | ToolFailure {
|
|
428
596
|
const action = state.pending;
|
|
429
597
|
if (!action) {
|
|
430
|
-
return { error: "Nothing is
|
|
598
|
+
return { error: "Nothing is staged after all — ask the caller again what they want." };
|
|
431
599
|
}
|
|
432
600
|
const described = describeAction(action);
|
|
433
601
|
if (typeof described !== "string") return described;
|
|
@@ -10,15 +10,21 @@ You do not do the specialist work yourself. There are four desks, and each one h
|
|
|
10
10
|
How to run a call:
|
|
11
11
|
|
|
12
12
|
- Start with `lookup_booking` so you know who you are speaking to and what they are holding.
|
|
13
|
-
-
|
|
13
|
+
- **A desk's tools only work while the call is at that desk, and they refuse from anywhere else.** So the moment the caller raises something a desk owns — flights, hotels, cars, excursions — your very next tool call is that desk's `to_…_assistant`, passing along what they asked for. Never a search or a booking first: at the concierge desk those refuse, and you have spent the caller's time on nothing.
|
|
14
|
+
- Do not mention the transfer — the caller should hear one continuous conversation.
|
|
14
15
|
- The desk's reply tells you how it works. Follow it until it is done, then use `complete_or_escalate` to come back.
|
|
15
16
|
- Answer small talk, general questions and anything about the existing booking yourself.
|
|
16
17
|
|
|
17
18
|
Confirming changes — this part matters:
|
|
18
19
|
|
|
19
20
|
- Every tool that would change a booking stages the change instead of making it. Nothing has happened until `confirm_action` runs.
|
|
21
|
+
- **Staging is a QUOTE, not a change, so it needs no permission.** `update_ticket`, `book_hotel`, `book_car_rental` and `book_excursion` price the request and hand you the sentence to say; they move nothing. Call the staging tool as soon as you know what the caller wants — you are getting them a quote, not doing the thing — and never ask whether you may.
|
|
22
|
+
- **So the staging tool comes before the question, always.** "Shall I confirm?" is a question about a staged change, so the tool that stages it runs first and its answer is what you read back. Asking before staging is asking about something that does not exist, and the yes it earns has nothing to apply.
|
|
23
|
+
- **Never end a turn on "shall I confirm?" with nothing staged.** The staging call and that question belong to the same turn: stage, then ask, in that order, before you stop. If you have already asked and the caller says anything other than no, stage it now — asking a second time does not stage it either, and a caller who has to repeat themselves is a caller nobody is helping.
|
|
24
|
+
- **Never say a time, a fare or a total that did not come out of a tool result on this call**, and never say you are doing something — "I'll move your ticket", "I'm booking that" — before the tool that stages it has answered. If you need a number you have not got, call the desk's search first.
|
|
20
25
|
- When a tool answers with `awaitingConfirmation`, read the change back in your own words and ask a yes-or-no question. Say the price.
|
|
21
26
|
- If the caller says yes, call `confirm_action`. If they say no, or want different details, call `cancel_action`.
|
|
27
|
+
- **`confirm_action` only ever answers a read-back you have just given.** If no tool has answered `awaitingConfirmation` on this call, there is nothing staged, `confirm_action` will refuse, and the caller's request is still waiting to be done — reach for the tool that stages it instead. A caller who says "yes" or "go ahead" before anything is staged is agreeing to the request they already made, not to a read-back.
|
|
22
28
|
- Never call `confirm_action` on your own initiative, and never claim something is booked before it has run.
|
|
23
29
|
|
|
24
30
|
Voice manners: numbers spoken out ("six forty" not "$640.00"), flight numbers letter by letter when reading them back, and one question at a time.
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { stageAction, tripSlot } from "../shared.ts";
|
|
2
|
+
import { requireDesk, stageAction, tripSlot } from "../shared.ts";
|
|
3
3
|
|
|
4
4
|
/** SENSITIVE — their `book_car_rental`, staged rather than applied. */
|
|
5
5
|
export default tripSlot.updateTool({
|
|
6
6
|
description:
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"The CAR RENTAL DESK's booking tool: reserve a rental car. Only usable while the call " +
|
|
8
|
+
"is at that desk. This does NOT reserve anything yet — it stages the reservation so " +
|
|
9
|
+
"you can read the total back and hear a yes.",
|
|
9
10
|
inputSchema: z.object({
|
|
10
11
|
carId: z.string().max(20).describe("The car id from search_car_rentals, e.g. 'C2'"),
|
|
11
12
|
days: z.number().int().min(1).max(60).describe("How many days"),
|
|
12
13
|
}),
|
|
13
|
-
execute(args, trip) {
|
|
14
|
-
|
|
14
|
+
execute(args, trip, ctx) {
|
|
15
|
+
const offDesk = requireDesk(trip, "car_rental");
|
|
16
|
+
if (offDesk) return offDesk;
|
|
17
|
+
return stageAction(ctx, trip, {
|
|
15
18
|
kind: "book_car",
|
|
16
19
|
carId: args.carId.toUpperCase(),
|
|
17
20
|
days: args.days,
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { stageAction, tripSlot } from "../shared.ts";
|
|
2
|
+
import { requireDesk, stageAction, tripSlot } from "../shared.ts";
|
|
3
3
|
|
|
4
4
|
/** SENSITIVE — their `book_excursion`, staged rather than applied. */
|
|
5
5
|
export default tripSlot.updateTool({
|
|
6
6
|
description:
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"The EXCURSIONS DESK's booking tool: book an excursion. Only usable while the call is " +
|
|
8
|
+
"at that desk. This does NOT book anything yet — it stages the booking so you can read " +
|
|
9
|
+
"it back and hear a yes.",
|
|
9
10
|
inputSchema: z.object({
|
|
10
11
|
excursionId: z.string().max(20).describe("The excursion id from search_excursions, e.g. 'E2'"),
|
|
11
12
|
}),
|
|
12
|
-
execute(args, trip) {
|
|
13
|
-
|
|
13
|
+
execute(args, trip, ctx) {
|
|
14
|
+
const offDesk = requireDesk(trip, "excursion");
|
|
15
|
+
if (offDesk) return offDesk;
|
|
16
|
+
return stageAction(ctx, trip, {
|
|
14
17
|
kind: "book_excursion",
|
|
15
18
|
excursionId: args.excursionId.toUpperCase(),
|
|
16
19
|
});
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { stageAction, tripSlot } from "../shared.ts";
|
|
2
|
+
import { requireDesk, stageAction, tripSlot } from "../shared.ts";
|
|
3
3
|
|
|
4
4
|
/** SENSITIVE — their `book_hotel`, staged rather than applied. */
|
|
5
5
|
export default tripSlot.updateTool({
|
|
6
6
|
description:
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"The HOTEL DESK's booking tool: hold a hotel room. Only usable while the call is at " +
|
|
8
|
+
"that desk. This does NOT book anything yet — it stages the booking so you can read " +
|
|
9
|
+
"the total back and hear a yes.",
|
|
9
10
|
inputSchema: z.object({
|
|
10
11
|
hotelId: z.string().max(20).describe("The hotel id from search_hotels, e.g. 'H1'"),
|
|
11
12
|
nights: z.number().int().min(1).max(30).describe("How many nights"),
|
|
12
13
|
}),
|
|
13
|
-
execute(args, trip) {
|
|
14
|
-
|
|
14
|
+
execute(args, trip, ctx) {
|
|
15
|
+
const offDesk = requireDesk(trip, "hotel");
|
|
16
|
+
if (offDesk) return offDesk;
|
|
17
|
+
return stageAction(ctx, trip, {
|
|
15
18
|
kind: "book_hotel",
|
|
16
19
|
hotelId: args.hotelId.toUpperCase(),
|
|
17
20
|
nights: args.nights,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stageAction, tripSlot } from "../shared.ts";
|
|
1
|
+
import { requireDesk, stageAction, tripSlot } from "../shared.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* SENSITIVE — their `cancel_ticket`. The most destructive thing on the call and
|
|
@@ -7,10 +7,13 @@ import { stageAction, tripSlot } from "../shared.ts";
|
|
|
7
7
|
*/
|
|
8
8
|
export default tripSlot.updateTool({
|
|
9
9
|
description:
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
10
|
+
"The FLIGHT DESK's cancellation tool: cancel the caller's ticket outright. Only usable " +
|
|
11
|
+
"while the call is at that desk. This does NOT cancel anything yet — it stages the " +
|
|
12
|
+
"cancellation so you can read it back and hear a yes.",
|
|
13
|
+
execute(_args, trip, ctx) {
|
|
14
|
+
const offDesk = requireDesk(trip, "flight");
|
|
15
|
+
if (offDesk) return offDesk;
|
|
13
16
|
if (!trip.ticket) return { error: "There is no ticket to cancel." };
|
|
14
|
-
return stageAction(trip, { kind: "cancel_ticket" });
|
|
17
|
+
return stageAction(ctx, trip, { kind: "cancel_ticket" });
|
|
15
18
|
},
|
|
16
19
|
});
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { tool } from "@alexkroman1/aai";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { CAR_RENTALS, formatPrice } from "../shared.ts";
|
|
2
|
+
import { CAR_RENTALS, formatPrice, requireDesk, tripSlot } from "../shared.ts";
|
|
4
3
|
|
|
5
4
|
/** Their `search_car_rentals`, by city and tier. */
|
|
6
|
-
export default tool({
|
|
7
|
-
description:
|
|
5
|
+
export default tripSlot.tool({
|
|
6
|
+
description:
|
|
7
|
+
"The CAR RENTAL DESK's search: cars by city, and optionally by tier (compact, midsize, " +
|
|
8
|
+
"suv). Only usable while the call is at that desk — from anywhere else it refuses, so " +
|
|
9
|
+
"call to_car_rental_assistant first.",
|
|
8
10
|
inputSchema: z.object({
|
|
9
11
|
city: z.string().max(80).describe("City to search, e.g. 'Boston'"),
|
|
10
12
|
tier: z.string().max(40).describe("compact, midsize or suv").optional(),
|
|
11
13
|
}),
|
|
12
|
-
execute(args) {
|
|
14
|
+
execute(args, trip) {
|
|
15
|
+
const offDesk = requireDesk(trip, "car_rental");
|
|
16
|
+
if (offDesk) return offDesk;
|
|
13
17
|
const city = args.city.trim().toLowerCase();
|
|
14
18
|
const tier = args.tier?.trim().toLowerCase();
|
|
15
19
|
const cars = CAR_RENTALS.filter(
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { tool } from "@alexkroman1/aai";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { EXCURSIONS, formatPrice } from "../shared.ts";
|
|
2
|
+
import { EXCURSIONS, formatPrice, requireDesk, tripSlot } from "../shared.ts";
|
|
4
3
|
|
|
5
4
|
/** Their `search_trip_recommendations` — city plus a loose keyword, matched
|
|
6
5
|
* against the name and the kind so "boat" and "sail" both land. */
|
|
7
|
-
export default tool({
|
|
8
|
-
description:
|
|
6
|
+
export default tripSlot.tool({
|
|
7
|
+
description:
|
|
8
|
+
"The EXCURSIONS DESK's search: things to do in a city, with an optional keyword matched " +
|
|
9
|
+
"loosely. Only usable while the call is at that desk — from anywhere else it refuses, so " +
|
|
10
|
+
"call to_excursion_assistant first.",
|
|
9
11
|
inputSchema: z.object({
|
|
10
12
|
city: z.string().max(80).describe("City to search, e.g. 'Boston'"),
|
|
11
13
|
keyword: z
|
|
@@ -14,7 +16,9 @@ export default tool({
|
|
|
14
16
|
.describe("What they enjoy, e.g. 'food', 'boat', 'history'")
|
|
15
17
|
.optional(),
|
|
16
18
|
}),
|
|
17
|
-
execute(args) {
|
|
19
|
+
execute(args, trip) {
|
|
20
|
+
const offDesk = requireDesk(trip, "excursion");
|
|
21
|
+
if (offDesk) return offDesk;
|
|
18
22
|
const city = args.city.trim().toLowerCase();
|
|
19
23
|
const keyword = args.keyword?.trim().toLowerCase();
|
|
20
24
|
const inCity = EXCURSIONS.filter((e) => e.city.toLowerCase().includes(city));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { tool } from "@alexkroman1/aai";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { FLIGHTS, formatPrice } from "../shared.ts";
|
|
2
|
+
import { FLIGHTS, formatPrice, requireDesk, tripSlot } from "../shared.ts";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Their `search_flights`, with the same "be generous" behaviour their flight
|
|
@@ -8,10 +7,12 @@ import { FLIGHTS, formatPrice } from "../shared.ts";
|
|
|
8
7
|
* rather than an empty list, so the desk can say what it DOES fly instead of
|
|
9
8
|
* "nothing found" — which is what sends a caller to a human.
|
|
10
9
|
*/
|
|
11
|
-
export default tool({
|
|
10
|
+
export default tripSlot.tool({
|
|
12
11
|
description:
|
|
13
|
-
"
|
|
14
|
-
"'Boston' or 'Zurich to Boston'. Omit the route to hear everything."
|
|
12
|
+
"The FLIGHT DESK's search: the schedule, matched on any part of the route, e.g. " +
|
|
13
|
+
"'Boston' or 'Zurich to Boston'. Omit the route to hear everything. Only usable while " +
|
|
14
|
+
"the call is at that desk — from anywhere else it refuses, so call to_flight_assistant " +
|
|
15
|
+
"first.",
|
|
15
16
|
inputSchema: z.object({
|
|
16
17
|
route: z
|
|
17
18
|
.string()
|
|
@@ -20,7 +21,9 @@ export default tool({
|
|
|
20
21
|
.optional(),
|
|
21
22
|
maxFare: z.number().positive().describe("Only flights at or under this fare").optional(),
|
|
22
23
|
}),
|
|
23
|
-
execute(args) {
|
|
24
|
+
execute(args, trip) {
|
|
25
|
+
const offDesk = requireDesk(trip, "flight");
|
|
26
|
+
if (offDesk) return offDesk;
|
|
24
27
|
const needle = args.route?.trim().toLowerCase();
|
|
25
28
|
const matched = FLIGHTS.filter(
|
|
26
29
|
(f) =>
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { tool } from "@alexkroman1/aai";
|
|
2
1
|
import { z } from "zod";
|
|
3
|
-
import { formatPrice, HOTELS } from "../shared.ts";
|
|
2
|
+
import { formatPrice, HOTELS, requireDesk, tripSlot } from "../shared.ts";
|
|
4
3
|
|
|
5
4
|
/** Their `search_hotels`, with `price_tier` collapsed to a nightly ceiling —
|
|
6
5
|
* a caller says "under two hundred", not "midscale". */
|
|
7
|
-
export default tool({
|
|
8
|
-
description:
|
|
6
|
+
export default tripSlot.tool({
|
|
7
|
+
description:
|
|
8
|
+
"The HOTEL DESK's search: hotels by city, and optionally by the most they want to pay " +
|
|
9
|
+
"per night. Only usable while the call is at that desk — from anywhere else it refuses, " +
|
|
10
|
+
"so call to_hotel_assistant first.",
|
|
9
11
|
inputSchema: z.object({
|
|
10
12
|
city: z.string().max(80).describe("City to search, e.g. 'Boston'"),
|
|
11
13
|
maxPerNight: z.number().positive().describe("Nightly ceiling in dollars").optional(),
|
|
12
14
|
}),
|
|
13
|
-
execute(args) {
|
|
15
|
+
execute(args, trip) {
|
|
16
|
+
const offDesk = requireDesk(trip, "hotel");
|
|
17
|
+
if (offDesk) return offDesk;
|
|
14
18
|
const city = args.city.trim().toLowerCase();
|
|
15
19
|
const hotels = HOTELS.filter(
|
|
16
20
|
(h) =>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { stageAction, tripSlot } from "../shared.ts";
|
|
2
|
+
import { requireDesk, stageAction, tripSlot } from "../shared.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* SENSITIVE — their `update_ticket_to_new_flight`, behind the confirmation
|
|
@@ -8,15 +8,18 @@ import { stageAction, tripSlot } from "../shared.ts";
|
|
|
8
8
|
*/
|
|
9
9
|
export default tripSlot.updateTool({
|
|
10
10
|
description:
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"The FLIGHT DESK's rebooking tool: move the caller's ticket to a different flight. " +
|
|
12
|
+
"Only usable while the call is at that desk. This does NOT change the ticket — it " +
|
|
13
|
+
"stages the change so you can read it back and hear a yes.",
|
|
13
14
|
inputSchema: z.object({
|
|
14
15
|
flightId: z.string().max(20).describe("The flight to move to, e.g. 'LX52'"),
|
|
15
16
|
}),
|
|
16
|
-
execute(args, trip) {
|
|
17
|
+
execute(args, trip, ctx) {
|
|
18
|
+
const offDesk = requireDesk(trip, "flight");
|
|
19
|
+
if (offDesk) return offDesk;
|
|
17
20
|
if (!trip.ticket) {
|
|
18
21
|
return { error: "This caller has no ticket to move — it was cancelled on this call." };
|
|
19
22
|
}
|
|
20
|
-
return stageAction(trip, { kind: "update_ticket", flightId: args.flightId.toUpperCase() });
|
|
23
|
+
return stageAction(ctx, trip, { kind: "update_ticket", flightId: args.flightId.toUpperCase() });
|
|
21
24
|
},
|
|
22
25
|
});
|