@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.
Files changed (234) hide show
  1. package/README.md +1 -0
  2. package/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
  3. package/dist/{_bundler-C2BDwU7r.mjs → _bundler-CDuYl5Gb.mjs} +2 -2
  4. package/dist/_dev-env.d.ts +22 -2
  5. package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-Me06wgeF.mjs} +38 -47
  6. package/dist/_dev-server.d.ts +0 -17
  7. package/dist/{_init-C57bWAQ8.mjs → _init-BhEfQ6Yi.mjs} +3 -2
  8. package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
  9. package/dist/_preflight.d.ts +1 -1
  10. package/dist/_resource-commands.d.ts +23 -0
  11. package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-z-USxPtL.mjs} +16 -12
  12. package/dist/_slug-api.d.ts +5 -2
  13. package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
  14. package/dist/{_templates-Dxb_P2Wz.mjs → _templates-4WcKOjS5.mjs} +5 -6
  15. package/dist/_templates.d.ts +3 -4
  16. package/dist/_workflow-scan.d.ts +108 -0
  17. package/dist/{build-Dfza2pRI.mjs → build-BBxf8vom.mjs} +6 -3
  18. package/dist/cli.mjs +346 -349
  19. package/dist/{client-bundler-BEIqgOtd.mjs → client-bundler-CWnG42cU.mjs} +2 -1
  20. package/dist/client-bundler.mjs +1 -1
  21. package/dist/{delete-vbpjGzqs.mjs → delete-Dl_LSfvI.mjs} +3 -3
  22. package/dist/{deploy-B8lubiRT.mjs → deploy-DRC8jWr8.mjs} +5 -5
  23. package/dist/{dev-Uq5ujP8a.mjs → dev-BvcctCn2.mjs} +1 -1
  24. package/dist/eval-DfQ8cm5p.mjs +56 -0
  25. package/dist/eval.d.ts +43 -0
  26. package/dist/{init-l6xfU4xX.mjs → init-CUo87gcI.mjs} +3 -3
  27. package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
  28. package/dist/{logs-BWVGSjLR.mjs → logs-CQ2T46dr.mjs} +2 -2
  29. package/dist/project-config.mjs +1 -1
  30. package/dist/scaffold/CLAUDE.md +442 -80
  31. package/dist/scaffold/package.json +6 -3
  32. package/dist/scaffold/server.mjs +11 -4
  33. package/dist/scaffold/tsconfig.json +0 -1
  34. package/dist/{secret-BuMuFR4B.mjs → secret-Ct34kdgU.mjs} +2 -2
  35. package/dist/{storage-DzRZ-eCw.mjs → storage-i_79ZwAR.mjs} +2 -2
  36. package/dist/{studio-BCNUpDgP.mjs → studio-CzTzPqe1.mjs} +4 -4
  37. package/dist/templates/briefing-desk/agent.test.ts +350 -0
  38. package/dist/templates/briefing-desk/agent.ts +30 -0
  39. package/dist/templates/briefing-desk/shared.ts +200 -0
  40. package/dist/templates/briefing-desk/system-prompt.md +39 -0
  41. package/dist/templates/briefing-desk/tools/briefing_so_far.ts +35 -0
  42. package/dist/templates/briefing-desk/tools/research_topic.ts +88 -0
  43. package/dist/templates/briefing-desk/tools/verify_claim.ts +77 -0
  44. package/dist/templates/call-audit/agent.eval.test.ts +474 -0
  45. package/dist/templates/call-audit/agent.test.ts +57 -204
  46. package/dist/templates/call-audit/agent.ts +32 -19
  47. package/dist/templates/call-audit/client.tsx +17 -54
  48. package/dist/templates/call-audit/workflows/audit.ts +11 -18
  49. package/dist/templates/call-audit/workflows/ingest.ts +106 -114
  50. package/dist/templates/call-audit/workflows/media.ts +2 -12
  51. package/dist/templates/call-audit/workflows/summarize.ts +47 -52
  52. package/dist/templates/call-audit/workflows/sync-api.ts +7 -7
  53. package/dist/templates/code-interpreter/agent.eval.test.ts +165 -0
  54. package/dist/templates/dispatch-center/agent.eval.test.ts +349 -0
  55. package/dist/templates/dispatch-center/agent.test.ts +149 -25
  56. package/dist/templates/dispatch-center/client.tsx +239 -129
  57. package/dist/templates/dispatch-center/shared.ts +99 -1
  58. package/dist/templates/dispatch-center/system-prompt.md +3 -1
  59. package/dist/templates/dispatch-center/tools/incident_add_note.ts +16 -13
  60. package/dist/templates/dispatch-center/tools/incident_create.ts +19 -1
  61. package/dist/templates/dispatch-center/tools/incident_escalate.ts +68 -59
  62. package/dist/templates/dispatch-center/tools/incident_triage.ts +57 -43
  63. package/dist/templates/dispatch-center/tools/incident_update_status.ts +49 -40
  64. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +8 -2
  65. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +69 -52
  66. package/dist/templates/dispatch-center/tools/resources_update_status.ts +45 -41
  67. package/dist/templates/embedded-assets/agent.eval.test.ts +138 -0
  68. package/dist/templates/embedded-assets/agent.test.ts +17 -14
  69. package/dist/templates/health-assistant/agent.eval.test.ts +167 -0
  70. package/dist/templates/health-assistant/agent.test.ts +22 -4
  71. package/dist/templates/infocom-adventure/agent.eval.test.ts +158 -0
  72. package/dist/templates/infocom-adventure/agent.test.ts +53 -24
  73. package/dist/templates/infocom-adventure/agent.ts +21 -0
  74. package/dist/templates/infocom-adventure/client.tsx +246 -164
  75. package/dist/templates/infocom-adventure/shared.ts +20 -9
  76. package/dist/templates/infocom-adventure/system-prompt.md +1 -1
  77. package/dist/templates/infocom-adventure/tools/game_state_move.ts +3 -2
  78. package/dist/templates/link-digest/agent.eval.test.ts +195 -0
  79. package/dist/templates/link-digest/agent.test.ts +24 -19
  80. package/dist/templates/link-digest/client.tsx +47 -61
  81. package/dist/templates/link-digest/workflows/digest.ts +19 -31
  82. package/dist/templates/math-buddy/agent.eval.test.ts +146 -0
  83. package/dist/templates/night-owl/agent.eval.test.ts +272 -0
  84. package/dist/templates/night-owl/agent.test.ts +70 -19
  85. package/dist/templates/night-owl/agent.ts +5 -0
  86. package/dist/templates/night-owl/client.tsx +56 -56
  87. package/dist/templates/night-owl/shared.ts +24 -0
  88. package/dist/templates/night-owl/tools/recommend.ts +18 -6
  89. package/dist/templates/personal-finance/agent.eval.test.ts +168 -0
  90. package/dist/templates/personal-finance/system-prompt.md +4 -0
  91. package/dist/templates/pipeline-simple/agent.eval.test.ts +78 -0
  92. package/dist/templates/pipeline-simple/agent.ts +2 -2
  93. package/dist/templates/pizza-ordering/agent.eval.test.ts +171 -0
  94. package/dist/templates/pizza-ordering/agent.test.ts +42 -27
  95. package/dist/templates/pizza-ordering/client.tsx +9 -26
  96. package/dist/templates/pizza-ordering/shared.ts +20 -1
  97. package/dist/templates/pizza-ordering/system-prompt.md +6 -0
  98. package/dist/templates/plan-and-execute/agent.eval.test.ts +209 -0
  99. package/dist/templates/plan-and-execute/agent.test.ts +99 -58
  100. package/dist/templates/plan-and-execute/agent.ts +1 -1
  101. package/dist/templates/plan-and-execute/client.tsx +12 -15
  102. package/dist/templates/plan-and-execute/shared.ts +71 -2
  103. package/dist/templates/plan-and-execute/tools/plan_status.ts +18 -6
  104. package/dist/templates/plan-and-execute/tools/revise_plan.ts +18 -10
  105. package/dist/templates/plan-and-execute/tools/start_plan.ts +13 -2
  106. package/dist/templates/plan-and-execute/tools/work_next_step.ts +70 -22
  107. package/dist/templates/podcast-digest/agent.eval.test.ts +411 -0
  108. package/dist/templates/podcast-digest/agent.test.ts +717 -0
  109. package/dist/templates/podcast-digest/agent.ts +139 -0
  110. package/dist/templates/podcast-digest/client.tsx +154 -0
  111. package/dist/templates/podcast-digest/workflows/digest.ts +444 -0
  112. package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
  113. package/dist/templates/podcast-digest/workflows/slack.ts +88 -0
  114. package/dist/templates/recap-workflow/agent.eval.test.ts +366 -0
  115. package/dist/templates/recap-workflow/agent.test.ts +97 -94
  116. package/dist/templates/recap-workflow/tools/recap_progress.ts +9 -12
  117. package/dist/templates/recap-workflow/tools/recap_status.ts +8 -9
  118. package/dist/templates/recap-workflow/tools/request_recap.ts +2 -1
  119. package/dist/templates/recap-workflow/workflows/recap.ts +36 -40
  120. package/dist/templates/redline/agent.eval.test.ts +209 -0
  121. package/dist/templates/redline/agent.test.ts +17 -15
  122. package/dist/templates/redline/client.tsx +12 -12
  123. package/dist/templates/redline/workflows/redline.ts +19 -31
  124. package/dist/templates/research-workflow/agent.eval.test.ts +329 -0
  125. package/dist/templates/research-workflow/agent.test.ts +60 -59
  126. package/dist/templates/research-workflow/tools/research_progress.ts +9 -12
  127. package/dist/templates/research-workflow/tools/research_status.ts +9 -11
  128. package/dist/templates/research-workflow/workflows/research.ts +44 -61
  129. package/dist/templates/retail/address.ts +87 -1
  130. package/dist/templates/retail/agent.eval.test.ts +314 -0
  131. package/dist/templates/retail/agent.test.ts +195 -112
  132. package/dist/templates/retail/agent.ts +35 -1
  133. package/dist/templates/retail/cancel.ts +116 -0
  134. package/dist/templates/retail/client.tsx +265 -117
  135. package/dist/templates/retail/payment.ts +107 -0
  136. package/dist/templates/retail/pending.test.ts +196 -0
  137. package/dist/templates/retail/pending.ts +137 -0
  138. package/dist/templates/retail/registry.test.ts +150 -7
  139. package/dist/templates/retail/returns.ts +102 -0
  140. package/dist/templates/retail/shared.test.ts +42 -0
  141. package/dist/templates/retail/shared.ts +35 -0
  142. package/dist/templates/retail/store.test.ts +84 -15
  143. package/dist/templates/retail/store.ts +231 -48
  144. package/dist/templates/retail/swap.test.ts +4 -4
  145. package/dist/templates/retail/swap.ts +248 -15
  146. package/dist/templates/retail/system-prompt.md +69 -28
  147. package/dist/templates/retail/tools/cancel_change.ts +33 -0
  148. package/dist/templates/retail/tools/cancel_pending_order.ts +16 -60
  149. package/dist/templates/retail/tools/confirm_change.ts +42 -0
  150. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -67
  151. package/dist/templates/retail/tools/find_user_id_by_email.ts +8 -5
  152. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +4 -5
  153. package/dist/templates/retail/tools/get_item_details.ts +3 -4
  154. package/dist/templates/retail/tools/get_order_details.ts +3 -4
  155. package/dist/templates/retail/tools/get_product_details.ts +3 -4
  156. package/dist/templates/retail/tools/get_user_details.ts +2 -2
  157. package/dist/templates/retail/tools/list_all_product_types.ts +5 -5
  158. package/dist/templates/retail/tools/modify_pending_order_address.ts +13 -25
  159. package/dist/templates/retail/tools/modify_pending_order_items.ts +23 -62
  160. package/dist/templates/retail/tools/modify_pending_order_payment.ts +16 -78
  161. package/dist/templates/retail/tools/modify_user_address.ts +13 -16
  162. package/dist/templates/retail/tools/return_delivered_order_items.ts +17 -72
  163. package/dist/templates/retail/tools/transfer_to_human_agents.ts +12 -6
  164. package/dist/templates/simple/agent.eval.test.ts +54 -0
  165. package/dist/templates/solo-rpg/agent.eval.test.ts +320 -0
  166. package/dist/templates/solo-rpg/agent.test.ts +227 -48
  167. package/dist/templates/solo-rpg/client.tsx +38 -37
  168. package/dist/templates/solo-rpg/shared.ts +145 -19
  169. package/dist/templates/solo-rpg/system-prompt.md +3 -2
  170. package/dist/templates/solo-rpg/tools/action_roll.ts +82 -63
  171. package/dist/templates/solo-rpg/tools/burn_momentum.ts +85 -50
  172. package/dist/templates/solo-rpg/tools/check_state.ts +24 -7
  173. package/dist/templates/solo-rpg/tools/load_game.ts +13 -1
  174. package/dist/templates/solo-rpg/tools/save_game.ts +16 -7
  175. package/dist/templates/solo-rpg/tools/setup_character.ts +22 -2
  176. package/dist/templates/solo-rpg/tools/update_state.ts +113 -100
  177. package/dist/templates/spoken-summary/agent.eval.test.ts +342 -0
  178. package/dist/templates/spoken-summary/agent.test.ts +68 -97
  179. package/dist/templates/spoken-summary/agent.ts +30 -17
  180. package/dist/templates/spoken-summary/client.tsx +10 -50
  181. package/dist/templates/spoken-summary/workflows/summarize.ts +17 -22
  182. package/dist/templates/spoken-summary/workflows/transcribe.ts +19 -26
  183. package/dist/templates/support-line/agent.eval.test.ts +273 -0
  184. package/dist/templates/support-line/agent.test.ts +11 -16
  185. package/dist/templates/support-line/agent.ts +1 -1
  186. package/dist/templates/support-line/client.tsx +9 -9
  187. package/dist/templates/support-line/nodes.ts +100 -0
  188. package/dist/templates/support-line/procedure.ts +407 -0
  189. package/dist/templates/support-line/system-prompt.md +2 -1
  190. package/dist/templates/support-line/tools/answer_question.ts +66 -4
  191. package/dist/templates/transcription-workflow/agent.eval.test.ts +234 -0
  192. package/dist/templates/transcription-workflow/agent.test.ts +94 -194
  193. package/dist/templates/transcription-workflow/agent.ts +1 -1
  194. package/dist/templates/transcription-workflow/client.tsx +17 -42
  195. package/dist/templates/transcription-workflow/workflows/batch.ts +19 -22
  196. package/dist/templates/transcription-workflow/workflows/normalize.ts +90 -186
  197. package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
  198. package/dist/templates/transcription-workflow/workflows/stream.ts +4 -5
  199. package/dist/templates/transcription-workflow/workflows/sync-api.ts +6 -6
  200. package/dist/templates/transcription-workflow/workflows/transcribe.ts +32 -26
  201. package/dist/templates/transcription-workflow/workflows/wav.ts +0 -31
  202. package/dist/templates/travel-concierge/agent.eval.test.ts +323 -0
  203. package/dist/templates/travel-concierge/agent.test.ts +174 -43
  204. package/dist/templates/travel-concierge/agent.ts +18 -9
  205. package/dist/templates/travel-concierge/client.tsx +11 -23
  206. package/dist/templates/travel-concierge/routing.ts +34 -15
  207. package/dist/templates/travel-concierge/shared.ts +180 -12
  208. package/dist/templates/travel-concierge/system-prompt.md +7 -1
  209. package/dist/templates/travel-concierge/tools/book_car_rental.ts +8 -5
  210. package/dist/templates/travel-concierge/tools/book_excursion.ts +8 -5
  211. package/dist/templates/travel-concierge/tools/book_hotel.ts +8 -5
  212. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +8 -5
  213. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +9 -5
  214. package/dist/templates/travel-concierge/tools/search_excursions.ts +9 -5
  215. package/dist/templates/travel-concierge/tools/search_flights.ts +9 -6
  216. package/dist/templates/travel-concierge/tools/search_hotels.ts +9 -5
  217. package/dist/templates/travel-concierge/tools/update_ticket.ts +8 -5
  218. package/dist/templates/web-researcher/agent.eval.test.ts +121 -0
  219. package/dist/templates/web-researcher/system-prompt.md +15 -4
  220. package/dist/{test-9kPKJI-w.mjs → test-BvyT6sAc.mjs} +24 -10
  221. package/dist/test.d.ts +29 -5
  222. package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-CCVEDjm1.mjs} +241 -3
  223. package/dist/worker-bundler.mjs +1 -1
  224. package/dist/{workflow-CFpxOFfQ.mjs → workflow-BflATMmU.mjs} +47 -12
  225. package/dist/workflow-bundler.d.ts +6 -1
  226. package/dist/workflow.d.ts +1 -1
  227. package/package.json +5 -4
  228. package/dist/eject-31gjtaHF.mjs +0 -116
  229. package/dist/eject.d.ts +0 -60
  230. package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
  231. package/dist/templates/infocom-adventure/tools/game_state_history.ts +0 -14
  232. package/dist/templates/support-line/graph.ts +0 -224
  233. package/dist/{_config-D_s09e7g.mjs → _config-CmJOFsAP.mjs} +1 -1
  234. /package/dist/templates/plan-and-execute/{graph.ts → procedure.ts} +0 -0
@@ -1,17 +1,21 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { OrderIdField, resolveOrder } from "../resolve.ts";
4
- import { authenticatedUser, money, retailTool, setFocus } from "../store.ts";
5
- import { assertCanCoverDiff, planItemSwap } from "../swap.ts";
3
+ import { stageAction } from "../pending.ts";
4
+ import { OrderIdField } from "../resolve.ts";
5
+ import { retailTool, setFocus } from "../store.ts";
6
+ import { planExchange } from "../swap.ts";
6
7
 
7
8
  export default retailTool({
8
9
  name: "exchange_delivered_order_items",
10
+ when: "serving",
11
+ send: { type: "STAGED" },
9
12
  description:
10
- "Request an exchange of items in a delivered order for different options of the SAME products. " +
11
- "Only a 'delivered' order can be exchanged, and only once. Remind the caller to name EVERY " +
12
- "item they want exchanged before you call this, read the whole list and the price difference " +
13
- "back, and get an explicit yes. No new order is needed the customer gets an email explaining " +
14
- "how to send the originals back.",
13
+ "STAGE an exchange of items in a delivered order for different options of the SAME products " +
14
+ "this does NOT request anything. Only a 'delivered' order can be exchanged, and only once, so " +
15
+ "remind the caller to name EVERY item they want exchanged before you call this. The whole " +
16
+ "list and the price difference come back as a sentence to read to them; nothing happens until " +
17
+ "you hear an explicit yes and call confirm_change. No new order is needed — the customer gets " +
18
+ "an email explaining how to send the originals back.",
15
19
  inputSchema: z.object({
16
20
  order_id: OrderIdField,
17
21
  item_ids: z
@@ -28,65 +32,16 @@ export default retailTool({
28
32
  .describe("Method to charge or refund the price difference"),
29
33
  }),
30
34
  execute: (args, state) => {
31
- const user = authenticatedUser(state);
32
- if (isToolFailure(user)) return user;
33
-
34
- const order = resolveOrder(state, args.order_id);
35
- if (isToolFailure(order)) return order;
36
- setFocus(state, { orderId: order.order_id });
37
-
38
- if (order.status !== "delivered") {
39
- return {
40
- error: `Order ${order.order_id} is ${order.status}. Only a delivered order can be exchanged, and only once.`,
41
- };
42
- }
43
-
44
- // requireDifferent is false: tau2 permits a same-item entry here, and a
45
- // zero-difference exchange is harmless on a delivered order.
46
- const plan = planItemSwap(state, order, args.item_ids, args.new_item_ids, {
47
- requireDifferent: false,
48
- });
35
+ const plan = planExchange(
36
+ state,
37
+ args.order_id,
38
+ args.item_ids,
39
+ args.new_item_ids,
40
+ args.payment_method_id,
41
+ );
49
42
  if (isToolFailure(plan)) return plan;
50
-
51
- const blocked = assertCanCoverDiff(user, args.payment_method_id, plan.diff);
52
- if (blocked) return blocked;
53
-
54
- // No balance moves and no items change: this records a REQUEST. The money
55
- // and the item swap settle when the originals come back.
56
- order.status = "exchange requested";
57
- // SORTED INDEPENDENTLY, verbatim from tau2 (`order.exchange_items =
58
- // sorted(item_ids)` / `sorted(new_item_ids)`, the same shape
59
- // `return_delivered_order_items` uses one list of) — kept because
60
- // `seed.json` is tau2's data and these fields are what a tau2 task's
61
- // expected end state is compared against. They are therefore two SETS of
62
- // ids, not a pairing: sorting each list separately permutes the second
63
- // against the first whenever the caller named more than one item.
64
- //
65
- // Which is why the ANSWER carries `exchanges` instead. `planItemSwap`
66
- // priced this positionally, so returning the two sorted lists as if they
67
- // lined up told the caller a pairing the quote was not for — the one thing
68
- // this result must not do, since the model reads it back down a phone.
69
- order.exchange_items = [...args.item_ids].sort();
70
- order.exchange_new_items = [...args.new_item_ids].sort();
71
- order.exchange_payment_method_id = args.payment_method_id;
72
- order.exchange_price_difference = plan.diff;
73
-
74
- return {
75
- order_id: order.order_id,
76
- status: order.status,
77
- price_difference: plan.diff,
78
- // The pairing AS PRICED, per line, in the order the caller gave it.
79
- exchanges: plan.pairs.map((pair) => ({
80
- item_id: pair.item.item_id,
81
- new_item_id: pair.newVariant.item_id,
82
- price_difference: money(pair.newVariant.price - pair.item.price),
83
- })),
84
- message:
85
- plan.diff > 0
86
- ? `Exchange requested on ${order.order_id}. $${plan.diff.toFixed(2)} will be charged to ${args.payment_method_id}. An email with return instructions is on its way.`
87
- : `Exchange requested on ${order.order_id}. $${Math.abs(plan.diff).toFixed(2)} will be refunded to ${args.payment_method_id}. An email with return instructions is on its way.`,
88
- };
43
+ setFocus(state, { orderId: plan.orderId });
44
+ return stageAction(state, { kind: "exchange_delivered_order_items", plan });
89
45
  },
90
- summary: (_args, result) =>
91
- isToolFailure(result) ? "exchange failed" : `exchange requested on ${result.order_id}`,
46
+ summary: (_args, result) => `staged: ${result.read_back}`,
92
47
  });
@@ -1,7 +1,6 @@
1
- import { isToolFailure } from "@alexkroman1/aai";
2
1
  import { z } from "zod";
3
2
  import { authenticateAs } from "../authenticate.ts";
4
- import { retailTool } from "../store.ts";
3
+ import { BEFORE_TRANSFER, retailTool } from "../store.ts";
5
4
 
6
5
  export default retailTool({
7
6
  name: "find_user_id_by_email",
@@ -12,7 +11,12 @@ export default retailTool({
12
11
  inputSchema: z.object({
13
12
  email: z.string().max(200).describe("The customer's email, e.g. 'something@example.com'"),
14
13
  }),
15
- requiresAuth: false,
14
+ // Legal in `serving` too: a caller repeating their email must not hit an
15
+ // error, and `authenticateAs` is what refuses a switch to a DIFFERENT
16
+ // customer. The `IDENTIFIED` event is not sent on that refusal — a flow tool
17
+ // sends nothing when its body answers a `ToolFailure`.
18
+ when: BEFORE_TRANSFER,
19
+ send: { type: "IDENTIFIED" },
16
20
  execute: (args, state) => {
17
21
  const target = args.email.trim().toLowerCase();
18
22
  const match = Object.values(state.store.users).find(
@@ -23,6 +27,5 @@ export default retailTool({
23
27
  }
24
28
  return authenticateAs(state, match);
25
29
  },
26
- summary: (_args, result) =>
27
- isToolFailure(result) ? "lookup failed" : `identified ${result.user_id}`,
30
+ summary: (_args, result) => `identified ${result.user_id}`,
28
31
  });
@@ -1,7 +1,6 @@
1
- import { isToolFailure } from "@alexkroman1/aai";
2
1
  import { z } from "zod";
3
2
  import { authenticateAs } from "../authenticate.ts";
4
- import { retailTool } from "../store.ts";
3
+ import { BEFORE_TRANSFER, retailTool } from "../store.ts";
5
4
 
6
5
  export default retailTool({
7
6
  name: "find_user_id_by_name_zip",
@@ -13,7 +12,8 @@ export default retailTool({
13
12
  last_name: z.string().max(100).describe("Last name, e.g. 'Doe'"),
14
13
  zip: z.string().max(20).describe("Zip code, e.g. '12345'"),
15
14
  }),
16
- requiresAuth: false,
15
+ when: BEFORE_TRANSFER,
16
+ send: { type: "IDENTIFIED" },
17
17
  execute: (args, state) => {
18
18
  const first = args.first_name.trim().toLowerCase();
19
19
  const last = args.last_name.trim().toLowerCase();
@@ -31,6 +31,5 @@ export default retailTool({
31
31
  }
32
32
  return authenticateAs(state, match);
33
33
  },
34
- summary: (_args, result) =>
35
- isToolFailure(result) ? "lookup failed" : `identified ${result.user_id}`,
34
+ summary: (_args, result) => `identified ${result.user_id}`,
36
35
  });
@@ -1,6 +1,6 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { findItem, retailTool } from "../store.ts";
3
+ import { BEFORE_TRANSFER, findItem, retailTool } from "../store.ts";
4
4
 
5
5
  export default retailTool({
6
6
  name: "get_item_details",
@@ -10,7 +10,7 @@ export default retailTool({
10
10
  inputSchema: z.object({
11
11
  item_id: z.string().max(60).describe("The item id, e.g. '1008292230'"),
12
12
  }),
13
- requiresAuth: false,
13
+ when: BEFORE_TRANSFER,
14
14
  execute: (args, state) => {
15
15
  const found = findItem(state, args.item_id);
16
16
  if (isToolFailure(found)) return found;
@@ -23,6 +23,5 @@ export default retailTool({
23
23
  available: found.variant.available,
24
24
  };
25
25
  },
26
- summary: (_args, result) =>
27
- isToolFailure(result) ? "item read failed" : `read item ${result.item_id}`,
26
+ summary: (_args, result) => `read item ${result.item_id}`,
28
27
  });
@@ -1,11 +1,11 @@
1
- import { isToolFailure } from "@alexkroman1/aai";
2
- import { omitUndefined } from "@alexkroman1/aai/utils";
1
+ import { isToolFailure, omitUndefined } from "@alexkroman1/aai";
3
2
  import { z } from "zod";
4
3
  import { OrderIdField, resolveOrder } from "../resolve.ts";
5
4
  import { retailTool, setFocus } from "../store.ts";
6
5
 
7
6
  export default retailTool({
8
7
  name: "get_order_details",
8
+ when: "serving",
9
9
  description:
10
10
  "Get the status and full details of one of the authenticated customer's orders. Accepts the " +
11
11
  "order id (e.g. '#W0000000' — note the leading '#'), or a spoken reference such as " +
@@ -33,6 +33,5 @@ export default retailTool({
33
33
  ...(order.cancel_reason ? { cancel_reason: order.cancel_reason } : {}),
34
34
  };
35
35
  },
36
- summary: (_args, result) =>
37
- isToolFailure(result) ? "order read failed" : `read ${result.order_id}`,
36
+ summary: (_args, result) => `read ${result.order_id}`,
38
37
  });
@@ -1,6 +1,6 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { findProduct, retailTool, setFocus } from "../store.ts";
3
+ import { BEFORE_TRANSFER, findProduct, retailTool, setFocus } from "../store.ts";
4
4
 
5
5
  export default retailTool({
6
6
  name: "get_product_details",
@@ -10,7 +10,7 @@ export default retailTool({
10
10
  inputSchema: z.object({
11
11
  product_id: z.string().max(60).describe("The product id, e.g. '6086499569'"),
12
12
  }),
13
- requiresAuth: false,
13
+ when: BEFORE_TRANSFER,
14
14
  execute: (args, state) => {
15
15
  const product = findProduct(state, args.product_id);
16
16
  if (isToolFailure(product)) return product;
@@ -26,6 +26,5 @@ export default retailTool({
26
26
  })),
27
27
  };
28
28
  },
29
- summary: (_args, result) =>
30
- isToolFailure(result) ? "product read failed" : `read product ${result.name}`,
29
+ summary: (_args, result) => `read product ${result.name}`,
31
30
  });
@@ -4,6 +4,7 @@ import { requireOwnUser, retailTool } from "../store.ts";
4
4
 
5
5
  export default retailTool({
6
6
  name: "get_user_details",
7
+ when: "serving",
7
8
  description:
8
9
  "Get the authenticated customer's profile: name, email, default address, payment methods " +
9
10
  "(with gift-card balances) and their order ids with each order's status.",
@@ -32,6 +33,5 @@ export default retailTool({
32
33
  })),
33
34
  };
34
35
  },
35
- summary: (_args, result) =>
36
- isToolFailure(result) ? "profile read failed" : `read profile ${result.user_id}`,
36
+ summary: (_args, result) => `read profile ${result.user_id}`,
37
37
  });
@@ -1,6 +1,5 @@
1
- import { isToolFailure } from "@alexkroman1/aai";
2
1
  import { z } from "zod";
3
- import { retailTool } from "../store.ts";
2
+ import { BEFORE_TRANSFER, retailTool } from "../store.ts";
4
3
 
5
4
  export default retailTool({
6
5
  name: "list_all_product_types",
@@ -10,13 +9,14 @@ export default retailTool({
10
9
  // Empty schema rather than omitting it: the wrapper has one code path, and
11
10
  // that is where the per-call UI-update invariant lives.
12
11
  inputSchema: z.object({}),
13
- requiresAuth: false,
12
+ // The catalogue is not customer data, so it needs no identified caller — but
13
+ // it is still off limits once the call belongs to a human.
14
+ when: BEFORE_TRANSFER,
14
15
  execute: (_args, state) => {
15
16
  const entries = Object.values(state.store.products)
16
17
  .map((product) => [product.name, product.product_id] as const)
17
18
  .sort(([a], [b]) => a.localeCompare(b));
18
19
  return { count: entries.length, products: Object.fromEntries(entries) };
19
20
  },
20
- summary: (_args, result) =>
21
- isToolFailure(result) ? "catalog read failed" : `listed ${result.count} product types`,
21
+ summary: (_args, result) => `listed ${result.count} product types`,
22
22
  });
@@ -1,39 +1,27 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { AddressFields, formatAddress, toAddress } from "../address.ts";
4
- import { OrderIdField, resolveOrder } from "../resolve.ts";
3
+ import { AddressFields, planOrderAddress } from "../address.ts";
4
+ import { stageAction } from "../pending.ts";
5
+ import { OrderIdField } from "../resolve.ts";
5
6
  import { retailTool, setFocus } from "../store.ts";
6
7
 
7
8
  export default retailTool({
8
9
  name: "modify_pending_order_address",
10
+ when: "serving",
11
+ send: { type: "STAGED" },
9
12
  description:
10
- "Change the shipping address of a pending order. Read the new address back to the caller and " +
11
- "get an explicit yes before calling this. This does not change the customer's default address.",
13
+ "STAGE a change to a pending order's shipping address this does NOT change it. The new " +
14
+ "address comes back as a sentence to read to the caller; nothing happens until you hear an " +
15
+ "explicit yes and call confirm_change. This does not touch the customer's default address.",
12
16
  inputSchema: z.object({
13
17
  order_id: OrderIdField,
14
18
  ...AddressFields,
15
19
  }),
16
20
  execute: (args, state) => {
17
- const order = resolveOrder(state, args.order_id);
18
- if (isToolFailure(order)) return order;
19
- setFocus(state, { orderId: order.order_id });
20
-
21
- // Any pending variant is fine here — unlike cancel and modify-items, which
22
- // require exactly 'pending'. Re-addressing a modified order is harmless.
23
- if (!order.status.startsWith("pending")) {
24
- return {
25
- error: `Order ${order.order_id} is ${order.status}, and only a pending order's address can be changed.`,
26
- };
27
- }
28
-
29
- order.address = toAddress(args);
30
- return {
31
- order_id: order.order_id,
32
- status: order.status,
33
- address: order.address,
34
- message: `Order ${order.order_id} now ships to ${formatAddress(order.address)}.`,
35
- };
21
+ const plan = planOrderAddress(state, args.order_id, args);
22
+ if (isToolFailure(plan)) return plan;
23
+ setFocus(state, { orderId: plan.orderId });
24
+ return stageAction(state, { kind: "modify_pending_order_address", plan });
36
25
  },
37
- summary: (_args, result) =>
38
- isToolFailure(result) ? "address change failed" : `re-addressed ${result.order_id}`,
26
+ summary: (_args, result) => `staged: ${result.read_back}`,
39
27
  });
@@ -1,17 +1,22 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { OrderIdField, resolveOrder } from "../resolve.ts";
4
- import { authenticatedUser, isGiftCard, money, retailTool, setFocus } from "../store.ts";
5
- import { applySwap, assertCanCoverDiff, planItemSwap } from "../swap.ts";
3
+ import { stageAction } from "../pending.ts";
4
+ import { OrderIdField } from "../resolve.ts";
5
+ import { retailTool, setFocus } from "../store.ts";
6
+ import { planModifyItems } from "../swap.ts";
6
7
 
7
8
  export default retailTool({
8
9
  name: "modify_pending_order_items",
10
+ when: "serving",
11
+ send: { type: "STAGED" },
9
12
  description:
10
- "Change items in a pending order to different options of the SAME products. This can be done " +
11
- "ONCE per order and is irreversible afterwards the order can no longer be cancelled or " +
12
- "modified at all. Collect EVERY item the caller wants changed into one call, read the whole " +
13
- "list and the price difference back, and get an explicit yes first. The item and replacement " +
14
- "lists are positional and must be the same length.",
13
+ "STAGE a change of items in a pending order to different options of the SAME products this " +
14
+ "does NOT change anything. It can only ever be done ONCE per order and is irreversible: " +
15
+ "afterwards the order can no longer be cancelled or modified at all. So collect EVERY item " +
16
+ "the caller wants changed into one call ask them 'is that everything you want to change?' " +
17
+ "explicitly first. The whole list and the price difference come back as a sentence to read " +
18
+ "to them; nothing happens until you hear an explicit yes and call confirm_change. The item " +
19
+ "and replacement lists are positional and must be the same length.",
15
20
  inputSchema: z.object({
16
21
  order_id: OrderIdField,
17
22
  item_ids: z
@@ -28,60 +33,16 @@ export default retailTool({
28
33
  .describe("Method to charge or refund the price difference, e.g. 'gift_card_0000000'"),
29
34
  }),
30
35
  execute: (args, state) => {
31
- const user = authenticatedUser(state);
32
- if (isToolFailure(user)) return user;
33
-
34
- const order = resolveOrder(state, args.order_id);
35
- if (isToolFailure(order)) return order;
36
- setFocus(state, { orderId: order.order_id });
37
-
38
- // Exactly 'pending'. A 'pending (item modified)' order has already used its
39
- // one modification, which is what makes this action terminal.
40
- if (order.status !== "pending") {
41
- return {
42
- error: `Order ${order.order_id} is ${order.status}. Items can only be changed while an order is exactly 'pending', and only once.`,
43
- };
44
- }
45
-
46
- const plan = planItemSwap(state, order, args.item_ids, args.new_item_ids, {
47
- requireDifferent: true,
48
- });
36
+ const plan = planModifyItems(
37
+ state,
38
+ args.order_id,
39
+ args.item_ids,
40
+ args.new_item_ids,
41
+ args.payment_method_id,
42
+ );
49
43
  if (isToolFailure(plan)) return plan;
50
-
51
- const blocked = assertCanCoverDiff(user, args.payment_method_id, plan.diff);
52
- if (blocked) return blocked;
53
-
54
- const method = user.payment_methods[args.payment_method_id];
55
- if (method && isGiftCard(method)) {
56
- method.balance = money(method.balance - plan.diff);
57
- }
58
- order.payment_history.push({
59
- transaction_type: plan.diff > 0 ? "payment" : "refund",
60
- amount: money(Math.abs(plan.diff)),
61
- payment_method_id: args.payment_method_id,
62
- });
63
-
64
- applySwap(order, plan);
65
- order.status = "pending (item modified)";
66
-
67
- return {
68
- order_id: order.order_id,
69
- status: order.status,
70
- price_difference: plan.diff,
71
- items: order.items.map((item) => ({
72
- name: item.name,
73
- item_id: item.item_id,
74
- options: item.options,
75
- price: item.price,
76
- })),
77
- message:
78
- plan.diff > 0
79
- ? `Done. $${plan.diff.toFixed(2)} was charged to ${args.payment_method_id}. This order can no longer be modified or cancelled.`
80
- : `Done. $${Math.abs(plan.diff).toFixed(2)} is being refunded to ${args.payment_method_id}. This order can no longer be modified or cancelled.`,
81
- };
44
+ setFocus(state, { orderId: plan.orderId });
45
+ return stageAction(state, { kind: "modify_pending_order_items", plan });
82
46
  },
83
- summary: (_args, result) =>
84
- isToolFailure(result)
85
- ? "item change failed"
86
- : `modified ${result.order_id} (${result.price_difference >= 0 ? "+" : ""}${result.price_difference})`,
47
+ summary: (_args, result) => `staged: ${result.read_back}`,
87
48
  });
@@ -1,21 +1,20 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { OrderIdField, resolveOrder } from "../resolve.ts";
4
- import {
5
- authenticatedUser,
6
- findPaymentMethod,
7
- isGiftCard,
8
- money,
9
- retailTool,
10
- setFocus,
11
- } from "../store.ts";
3
+ import { planPayment } from "../payment.ts";
4
+ import { stageAction } from "../pending.ts";
5
+ import { OrderIdField } from "../resolve.ts";
6
+ import { retailTool, setFocus } from "../store.ts";
12
7
 
13
8
  export default retailTool({
14
9
  name: "modify_pending_order_payment",
10
+ when: "serving",
11
+ send: { type: "STAGED" },
15
12
  description:
16
- "Change which payment method a pending order is charged to. The new method must be different " +
17
- "from the current one, and a gift card must hold enough to cover the whole order. The original " +
18
- "method is refunded. Read the change back and get an explicit yes before calling this.",
13
+ "STAGE a change to which payment method a pending order is charged to this does NOT change " +
14
+ "it. The new method must be different from the current one, and a gift card must hold enough " +
15
+ "to cover the whole order; the original method is refunded. The change comes back as a " +
16
+ "sentence to read to the caller; nothing happens until you hear an explicit yes and call " +
17
+ "confirm_change.",
19
18
  inputSchema: z.object({
20
19
  order_id: OrderIdField,
21
20
  payment_method_id: z
@@ -24,71 +23,10 @@ export default retailTool({
24
23
  .describe("The new payment method id, e.g. 'gift_card_0000000'"),
25
24
  }),
26
25
  execute: (args, state) => {
27
- const user = authenticatedUser(state);
28
- if (isToolFailure(user)) return user;
29
-
30
- const order = resolveOrder(state, args.order_id);
31
- if (isToolFailure(order)) return order;
32
- setFocus(state, { orderId: order.order_id });
33
-
34
- if (!order.status.startsWith("pending")) {
35
- return {
36
- error: `Order ${order.order_id} is ${order.status}, and only a pending order's payment method can be changed.`,
37
- };
38
- }
39
-
40
- const newMethod = findPaymentMethod(user, args.payment_method_id);
41
- if (isToolFailure(newMethod)) return newMethod;
42
-
43
- const original = order.payment_history[0];
44
- if (order.payment_history.length !== 1 || original?.transaction_type !== "payment") {
45
- return {
46
- error: `Order ${order.order_id} does not have exactly one payment on record, so its payment method cannot be changed.`,
47
- };
48
- }
49
- if (original.payment_method_id === args.payment_method_id) {
50
- return {
51
- error: `Order ${order.order_id} is already paid with ${args.payment_method_id}. The new method must be different.`,
52
- };
53
- }
54
-
55
- const amount = original.amount;
56
- if (isGiftCard(newMethod) && newMethod.balance < amount) {
57
- return {
58
- error: `Gift card ${args.payment_method_id}'s balance ($${newMethod.balance.toFixed(2)}) does not cover the $${amount.toFixed(2)} order total.`,
59
- };
60
- }
61
-
62
- order.payment_history.push(
63
- {
64
- transaction_type: "payment",
65
- amount,
66
- payment_method_id: args.payment_method_id,
67
- },
68
- {
69
- transaction_type: "refund",
70
- amount,
71
- payment_method_id: original.payment_method_id,
72
- },
73
- );
74
-
75
- if (isGiftCard(newMethod)) {
76
- newMethod.balance = money(newMethod.balance - amount);
77
- }
78
- const oldMethod = user.payment_methods[original.payment_method_id];
79
- if (oldMethod && isGiftCard(oldMethod)) {
80
- oldMethod.balance = money(oldMethod.balance + amount);
81
- }
82
-
83
- return {
84
- order_id: order.order_id,
85
- status: order.status,
86
- amount,
87
- paid_with: args.payment_method_id,
88
- refunded_to: original.payment_method_id,
89
- message: `Order ${order.order_id} is now charged to ${args.payment_method_id}, and $${amount.toFixed(2)} is going back to ${original.payment_method_id}.`,
90
- };
26
+ const plan = planPayment(state, args.order_id, args.payment_method_id);
27
+ if (isToolFailure(plan)) return plan;
28
+ setFocus(state, { orderId: plan.orderId });
29
+ return stageAction(state, { kind: "modify_pending_order_payment", plan });
91
30
  },
92
- summary: (_args, result) =>
93
- isToolFailure(result) ? "payment change failed" : `re-paid ${result.order_id}`,
31
+ summary: (_args, result) => `staged: ${result.read_back}`,
94
32
  });
@@ -1,29 +1,26 @@
1
1
  import { isToolFailure } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
- import { AddressFields, formatAddress, toAddress } from "../address.ts";
4
- import { requireOwnUser, retailTool } from "../store.ts";
3
+ import { AddressFields, planUserAddress } from "../address.ts";
4
+ import { stageAction } from "../pending.ts";
5
+ import { retailTool } from "../store.ts";
5
6
 
6
7
  export default retailTool({
7
8
  name: "modify_user_address",
9
+ when: "serving",
10
+ send: { type: "STAGED" },
8
11
  description:
9
- "Change the customer's default address, used for future orders. Read the new address back and " +
10
- "get an explicit yes before calling this. This does not change the address on any existing " +
11
- "order use modify_pending_order_address for that.",
12
+ "STAGE a change to the customer's default address, used for future orders this does NOT " +
13
+ "change it. The new address comes back as a sentence to read to the caller; nothing happens " +
14
+ "until you hear an explicit yes and call confirm_change. This does not change the address on " +
15
+ "any existing order — use modify_pending_order_address for that.",
12
16
  inputSchema: z.object({
13
17
  user_id: z.string().max(100).describe("The user id, e.g. 'sara_doe_496'"),
14
18
  ...AddressFields,
15
19
  }),
16
20
  execute: (args, state) => {
17
- const user = requireOwnUser(state, args.user_id);
18
- if (isToolFailure(user)) return user;
19
-
20
- user.address = toAddress(args);
21
- return {
22
- user_id: user.user_id,
23
- address: user.address,
24
- message: `Default address updated to ${formatAddress(user.address)}. Existing orders keep their own shipping addresses.`,
25
- };
21
+ const plan = planUserAddress(state, args.user_id, args);
22
+ if (isToolFailure(plan)) return plan;
23
+ return stageAction(state, { kind: "modify_user_address", plan });
26
24
  },
27
- summary: (_args, result) =>
28
- isToolFailure(result) ? "profile address change failed" : `re-addressed ${result.user_id}`,
25
+ summary: (_args, result) => `staged: ${result.read_back}`,
29
26
  });