@alexkroman1/aai-cli 5.14.0 → 6.1.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 (231) hide show
  1. package/dist/{_agent-C8Jk-ZcP.mjs → _agent-vGW5SBnX.mjs} +23 -5
  2. package/dist/_agent.d.ts +18 -0
  3. package/dist/{_api-client-BX8Qv51n.mjs → _api-client-LgLksMhN.mjs} +45 -8
  4. package/dist/_api-client.d.ts +39 -0
  5. package/dist/{_bundler-BuIyDdvs.mjs → _bundler-BapCUUJN.mjs} +42 -14
  6. package/dist/_bundler.d.ts +43 -5
  7. package/dist/_cli-common.d.ts +67 -3
  8. package/dist/{_config-CzwAWOWr.mjs → _config-D_s09e7g.mjs} +36 -18
  9. package/dist/_deploy.d.ts +2 -5
  10. package/dist/_dev-restart.d.ts +6 -1
  11. package/dist/{_dev-server-Ch7K8e8i.mjs → _dev-server-DQ1t9sbw.mjs} +94 -38
  12. package/dist/_dev-server.d.ts +30 -2
  13. package/dist/_e2e-test-utils.d.ts +80 -0
  14. package/dist/_fault-mode.d.ts +175 -0
  15. package/dist/{_init-CbkrE2wq.mjs → _init-CpISqoR8.mjs} +3 -3
  16. package/dist/{_output-CC300DzW.mjs → _output-CKkmWs7i.mjs} +8 -1
  17. package/dist/_output.d.ts +9 -2
  18. package/dist/{_server-common-61fL__39.mjs → _server-common-6e0QI4mq.mjs} +1 -1
  19. package/dist/{_slug-api-D30zPmsn.mjs → _slug-api-D2heILuC.mjs} +2 -2
  20. package/dist/{_studio-D2ciIhbi.mjs → _studio-zAJycZ8j.mjs} +3 -3
  21. package/dist/{_templates-B1kt3BGt.mjs → _templates-iDGjGiPf.mjs} +4 -6
  22. package/dist/{_typecheck-gate-4v9UBZHh.mjs → _typecheck-gate-BXvUNnfs.mjs} +1 -1
  23. package/dist/{_utils-0DHbcqUb.mjs → _utils-B8QmtFhK.mjs} +21 -7
  24. package/dist/_utils.d.ts +18 -2
  25. package/dist/{_vite-env-Dg_QlVv0.mjs → _vite-env-BNveawd1.mjs} +31 -1
  26. package/dist/_vite-env.d.ts +30 -0
  27. package/dist/build-BO1Ni6oJ.mjs +62 -0
  28. package/dist/build.d.ts +19 -0
  29. package/dist/cli-workflow.d.ts +10 -0
  30. package/dist/cli.d.ts +1 -2
  31. package/dist/cli.mjs +406 -224
  32. package/dist/{client-bundler-C9qXL8A2.mjs → client-bundler-B8Q9F8gJ.mjs} +3 -22
  33. package/dist/client-bundler.mjs +1 -1
  34. package/dist/{delete-DCRX3d96.mjs → delete-4I0uNR4a.mjs} +6 -7
  35. package/dist/delete.d.ts +2 -8
  36. package/dist/{deploy-DjsEV-40.mjs → deploy-DJ3HRcia.mjs} +13 -14
  37. package/dist/{dev-CvPH5dbF.mjs → dev-C6SWaAZP.mjs} +3 -3
  38. package/dist/{eject-LgBRfnbH.mjs → eject-Zrzk5KV3.mjs} +41 -15
  39. package/dist/eject.d.ts +30 -3
  40. package/dist/{init-BppxMR8r.mjs → init-BTRGiPQr.mjs} +61 -26
  41. package/dist/init.d.ts +11 -0
  42. package/dist/{login-BQQaxJhL.mjs → login-k0Z3VQu9.mjs} +5 -5
  43. package/dist/project-config.mjs +1 -1
  44. package/dist/scaffold/CLAUDE.md +498 -91
  45. package/dist/scaffold/package.json +11 -8
  46. package/dist/scaffold/pnpm-workspace.yaml +8 -0
  47. package/dist/scaffold/server.mjs +53 -57
  48. package/dist/{secret-Ds9dvqAj.mjs → secret-DzSQx1Ds.mjs} +6 -3
  49. package/dist/{storage-DbrQSHFk.mjs → storage-C-Do0mIA.mjs} +18 -6
  50. package/dist/{studio-GSFd4s5O.mjs → studio-BGVmCA2G.mjs} +10 -10
  51. package/dist/templates/code-interpreter/agent.ts +0 -2
  52. package/dist/templates/dispatch-center/agent.test.ts +17 -19
  53. package/dist/templates/dispatch-center/agent.ts +2 -29
  54. package/dist/templates/dispatch-center/client.tsx +33 -16
  55. package/dist/templates/dispatch-center/shared.ts +53 -19
  56. package/dist/templates/dispatch-center/tools/incident_add_note.ts +12 -14
  57. package/dist/templates/dispatch-center/tools/incident_create.ts +41 -44
  58. package/dist/templates/dispatch-center/tools/incident_escalate.ts +59 -61
  59. package/dist/templates/dispatch-center/tools/incident_get.ts +3 -4
  60. package/dist/templates/dispatch-center/tools/incident_triage.ts +43 -45
  61. package/dist/templates/dispatch-center/tools/incident_update_status.ts +39 -41
  62. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +2 -5
  63. package/dist/templates/dispatch-center/tools/ops_protocols.ts +1 -1
  64. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +20 -23
  65. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +52 -54
  66. package/dist/templates/dispatch-center/tools/resources_get_available.ts +2 -4
  67. package/dist/templates/dispatch-center/tools/resources_update_status.ts +39 -42
  68. package/dist/templates/embedded-assets/agent.test.ts +83 -0
  69. package/dist/templates/embedded-assets/agent.ts +1 -48
  70. package/dist/templates/embedded-assets/shared.ts +17 -0
  71. package/dist/templates/embedded-assets/system-prompt.md +13 -0
  72. package/dist/templates/embedded-assets/tools/list_topics.ts +9 -0
  73. package/dist/templates/embedded-assets/tools/search_knowledge.ts +26 -0
  74. package/dist/templates/health-assistant/agent.test.ts +169 -0
  75. package/dist/templates/health-assistant/agent.ts +1 -166
  76. package/dist/templates/health-assistant/fda.ts +100 -0
  77. package/dist/templates/health-assistant/tools/check_drug_interaction.ts +59 -0
  78. package/dist/templates/health-assistant/tools/medication_lookup.ts +31 -0
  79. package/dist/templates/infocom-adventure/agent.test.ts +205 -0
  80. package/dist/templates/infocom-adventure/agent.ts +3 -106
  81. package/dist/templates/infocom-adventure/client.tsx +28 -12
  82. package/dist/templates/infocom-adventure/shared.ts +28 -11
  83. package/dist/templates/infocom-adventure/tools/game_state_drop.ts +13 -0
  84. package/dist/templates/infocom-adventure/tools/game_state_flag.ts +13 -0
  85. package/dist/templates/infocom-adventure/tools/game_state_get.ts +16 -0
  86. package/dist/templates/infocom-adventure/tools/game_state_history.ts +14 -0
  87. package/dist/templates/infocom-adventure/tools/game_state_move.ts +14 -0
  88. package/dist/templates/infocom-adventure/tools/game_state_restart.ts +13 -0
  89. package/dist/templates/infocom-adventure/tools/game_state_score.ts +13 -0
  90. package/dist/templates/infocom-adventure/tools/game_state_take.ts +13 -0
  91. package/dist/templates/link-digest/agent.test.ts +223 -0
  92. package/dist/templates/link-digest/agent.ts +79 -0
  93. package/dist/templates/link-digest/client.tsx +146 -0
  94. package/dist/templates/link-digest/workflows/digest.ts +248 -0
  95. package/dist/templates/math-buddy/agent.ts +0 -2
  96. package/dist/templates/night-owl/agent.test.ts +80 -0
  97. package/dist/templates/night-owl/agent.ts +1 -68
  98. package/dist/templates/night-owl/system-prompt.md +10 -0
  99. package/dist/templates/night-owl/tools/recommend.ts +64 -0
  100. package/dist/templates/personal-finance/agent.ts +0 -2
  101. package/dist/templates/pizza-ordering/agent.test.ts +52 -47
  102. package/dist/templates/pizza-ordering/agent.ts +9 -158
  103. package/dist/templates/pizza-ordering/shared.ts +35 -12
  104. package/dist/templates/pizza-ordering/tools/add_pizza.ts +31 -0
  105. package/dist/templates/pizza-ordering/tools/place_order.ts +22 -0
  106. package/dist/templates/pizza-ordering/tools/remove_pizza.ts +22 -0
  107. package/dist/templates/pizza-ordering/tools/set_customer_name.ts +13 -0
  108. package/dist/templates/pizza-ordering/tools/update_pizza.ts +28 -0
  109. package/dist/templates/pizza-ordering/tools/view_order.ts +21 -0
  110. package/dist/templates/plan-and-execute/agent.test.ts +411 -0
  111. package/dist/templates/plan-and-execute/agent.ts +23 -0
  112. package/dist/templates/plan-and-execute/client.tsx +115 -0
  113. package/dist/templates/plan-and-execute/graph.ts +178 -0
  114. package/dist/templates/plan-and-execute/prompts.ts +110 -0
  115. package/dist/templates/plan-and-execute/shared.ts +161 -0
  116. package/dist/templates/plan-and-execute/system-prompt.md +18 -0
  117. package/dist/templates/plan-and-execute/tools/plan_status.ts +20 -0
  118. package/dist/templates/plan-and-execute/tools/revise_plan.ts +63 -0
  119. package/dist/templates/plan-and-execute/tools/start_plan.ts +51 -0
  120. package/dist/templates/plan-and-execute/tools/work_next_step.ts +101 -0
  121. package/dist/templates/recap-workflow/agent.test.ts +762 -0
  122. package/dist/templates/recap-workflow/agent.ts +95 -0
  123. package/dist/templates/recap-workflow/shared.ts +43 -0
  124. package/dist/templates/recap-workflow/tools/cancel_recap.ts +23 -0
  125. package/dist/templates/recap-workflow/tools/keep_transcript.ts +31 -0
  126. package/dist/templates/recap-workflow/tools/recap_progress.ts +27 -0
  127. package/dist/templates/recap-workflow/tools/recap_status.ts +46 -0
  128. package/dist/templates/recap-workflow/tools/request_recap.ts +43 -0
  129. package/dist/templates/recap-workflow/workflows/recap.ts +544 -0
  130. package/dist/templates/recap-workflow/workflows/tokens.ts +26 -0
  131. package/dist/templates/redline/agent.test.ts +222 -0
  132. package/dist/templates/redline/agent.ts +89 -0
  133. package/dist/templates/redline/client.tsx +184 -0
  134. package/dist/templates/redline/workflows/prompts.ts +68 -0
  135. package/dist/templates/redline/workflows/redline.ts +258 -0
  136. package/dist/templates/research-workflow/agent.test.ts +525 -0
  137. package/dist/templates/research-workflow/agent.ts +94 -0
  138. package/dist/templates/research-workflow/shared.ts +35 -0
  139. package/dist/templates/research-workflow/tools/file_it_now.ts +22 -0
  140. package/dist/templates/research-workflow/tools/request_research.ts +33 -0
  141. package/dist/templates/research-workflow/tools/research_progress.ts +26 -0
  142. package/dist/templates/research-workflow/tools/research_status.ts +40 -0
  143. package/dist/templates/research-workflow/workflows/prompts.ts +143 -0
  144. package/dist/templates/research-workflow/workflows/research.ts +558 -0
  145. package/dist/templates/retail/agent.test.ts +236 -172
  146. package/dist/templates/retail/agent.ts +5 -42
  147. package/dist/templates/retail/client.tsx +33 -11
  148. package/dist/templates/retail/registry.test.ts +31 -16
  149. package/dist/templates/retail/resolve.ts +28 -64
  150. package/dist/templates/retail/shared.test.ts +4 -3
  151. package/dist/templates/retail/shared.ts +24 -7
  152. package/dist/templates/retail/store.test.ts +12 -10
  153. package/dist/templates/retail/store.ts +69 -30
  154. package/dist/templates/retail/tools/cancel_pending_order.ts +4 -9
  155. package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -11
  156. package/dist/templates/retail/tools/find_user_id_by_email.ts +5 -9
  157. package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +5 -7
  158. package/dist/templates/retail/tools/get_item_details.ts +5 -7
  159. package/dist/templates/retail/tools/get_order_details.ts +5 -7
  160. package/dist/templates/retail/tools/get_product_details.ts +4 -7
  161. package/dist/templates/retail/tools/get_user_details.ts +4 -7
  162. package/dist/templates/retail/tools/list_all_product_types.ts +5 -7
  163. package/dist/templates/retail/tools/modify_pending_order_address.ts +4 -9
  164. package/dist/templates/retail/tools/modify_pending_order_items.ts +4 -16
  165. package/dist/templates/retail/tools/modify_pending_order_payment.ts +3 -9
  166. package/dist/templates/retail/tools/modify_user_address.ts +4 -9
  167. package/dist/templates/retail/tools/return_delivered_order_items.ts +3 -9
  168. package/dist/templates/retail/tools/transfer_to_human_agents.ts +1 -1
  169. package/dist/templates/solo-rpg/agent.test.ts +152 -29
  170. package/dist/templates/solo-rpg/agent.ts +3 -21
  171. package/dist/templates/solo-rpg/client.tsx +19 -6
  172. package/dist/templates/solo-rpg/shared.ts +19 -11
  173. package/dist/templates/solo-rpg/tools/action_roll.ts +2 -4
  174. package/dist/templates/solo-rpg/tools/burn_momentum.ts +2 -4
  175. package/dist/templates/solo-rpg/tools/check_state.ts +6 -6
  176. package/dist/templates/solo-rpg/tools/load_game.ts +1 -1
  177. package/dist/templates/solo-rpg/tools/oracle.ts +18 -11
  178. package/dist/templates/solo-rpg/tools/save_game.ts +10 -7
  179. package/dist/templates/solo-rpg/tools/setup_character.ts +1 -1
  180. package/dist/templates/solo-rpg/tools/update_state.ts +3 -4
  181. package/dist/templates/support-line/agent.test.ts +343 -0
  182. package/dist/templates/support-line/agent.ts +27 -0
  183. package/dist/templates/support-line/client.tsx +122 -0
  184. package/dist/templates/support-line/graph.ts +224 -0
  185. package/dist/templates/support-line/knowledge.json +65 -0
  186. package/dist/templates/support-line/prompts.ts +95 -0
  187. package/dist/templates/support-line/shared.ts +240 -0
  188. package/dist/templates/support-line/system-prompt.md +17 -0
  189. package/dist/templates/support-line/tools/answer_question.ts +72 -0
  190. package/dist/templates/support-line/tools/list_topics.ts +22 -0
  191. package/dist/templates/support-line/tools/log_ticket.ts +30 -0
  192. package/dist/templates/transcription-workflow/agent.test.ts +563 -0
  193. package/dist/templates/transcription-workflow/agent.ts +92 -0
  194. package/dist/templates/transcription-workflow/client.tsx +234 -0
  195. package/dist/templates/transcription-workflow/workflows/transcribe.ts +416 -0
  196. package/dist/templates/transcription-workflow/workflows/wav.ts +314 -0
  197. package/dist/templates/travel-concierge/agent.test.ts +338 -0
  198. package/dist/templates/travel-concierge/agent.ts +29 -0
  199. package/dist/templates/travel-concierge/client.tsx +161 -0
  200. package/dist/templates/travel-concierge/routing.ts +138 -0
  201. package/dist/templates/travel-concierge/shared.ts +547 -0
  202. package/dist/templates/travel-concierge/system-prompt.md +24 -0
  203. package/dist/templates/travel-concierge/tools/book_car_rental.ts +20 -0
  204. package/dist/templates/travel-concierge/tools/book_excursion.ts +18 -0
  205. package/dist/templates/travel-concierge/tools/book_hotel.ts +20 -0
  206. package/dist/templates/travel-concierge/tools/cancel_action.ts +7 -0
  207. package/dist/templates/travel-concierge/tools/cancel_ticket.ts +16 -0
  208. package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +7 -0
  209. package/dist/templates/travel-concierge/tools/confirm_action.ts +7 -0
  210. package/dist/templates/travel-concierge/tools/lookup_booking.ts +35 -0
  211. package/dist/templates/travel-concierge/tools/search_car_rentals.ts +33 -0
  212. package/dist/templates/travel-concierge/tools/search_excursions.ts +37 -0
  213. package/dist/templates/travel-concierge/tools/search_flights.ts +43 -0
  214. package/dist/templates/travel-concierge/tools/search_hotels.ts +36 -0
  215. package/dist/templates/travel-concierge/tools/to_car_rental_assistant.ts +11 -0
  216. package/dist/templates/travel-concierge/tools/to_excursion_assistant.ts +11 -0
  217. package/dist/templates/travel-concierge/tools/to_flight_assistant.ts +11 -0
  218. package/dist/templates/travel-concierge/tools/to_hotel_assistant.ts +11 -0
  219. package/dist/templates/travel-concierge/tools/update_ticket.ts +22 -0
  220. package/dist/templates/web-researcher/agent.ts +0 -2
  221. package/dist/templates/web-researcher/system-prompt.md +10 -0
  222. package/dist/{test-2LWBcXvY.mjs → test-B6ZBxSpk.mjs} +2 -2
  223. package/dist/typecheck.mjs +17 -15
  224. package/dist/worker-bundler-CIdDhZ0c.mjs +398 -0
  225. package/dist/worker-bundler.d.ts +14 -0
  226. package/dist/worker-bundler.mjs +1 -108
  227. package/dist/workflow-B-O97AqI.mjs +125 -0
  228. package/dist/workflow-bundler.d.ts +133 -0
  229. package/dist/workflow.d.ts +67 -0
  230. package/package.json +13 -9
  231. package/dist/build-DQJ1ZSm9.mjs +0 -39
@@ -1,53 +1,67 @@
1
- import type { ToolContext, ToolFailure } from "@alexkroman1/aai";
1
+ import type { ToolContext } from "@alexkroman1/aai";
2
2
  import { isToolFailure } from "@alexkroman1/aai";
3
3
  import { createToolContext } from "@alexkroman1/aai/testing";
4
4
  import { describe, expect, test } from "vitest";
5
5
  import type { AuthResult } from "./authenticate.ts";
6
6
  import type { Address } from "./shared.ts";
7
- import { retailSlot, type StateSlot } from "./store.ts";
8
- import { cancelPendingOrder } from "./tools/cancel_pending_order.ts";
9
- import { exchangeDeliveredOrderItems } from "./tools/exchange_delivered_order_items.ts";
10
- import { findUserIdByEmail } from "./tools/find_user_id_by_email.ts";
11
- import { findUserIdByNameZip } from "./tools/find_user_id_by_name_zip.ts";
12
- import { getItemDetails } from "./tools/get_item_details.ts";
13
- import { getOrderDetails } from "./tools/get_order_details.ts";
14
- import { getProductDetails } from "./tools/get_product_details.ts";
15
- import { getUserDetails } from "./tools/get_user_details.ts";
16
- import { listAllProductTypes } from "./tools/list_all_product_types.ts";
17
- import { modifyPendingOrderAddress } from "./tools/modify_pending_order_address.ts";
18
- import { modifyPendingOrderItems } from "./tools/modify_pending_order_items.ts";
19
- import { modifyPendingOrderPayment } from "./tools/modify_pending_order_payment.ts";
20
- import { modifyUserAddress } from "./tools/modify_user_address.ts";
21
- import { returnDeliveredOrderItems } from "./tools/return_delivered_order_items.ts";
22
- import { transferToHumanAgents } from "./tools/transfer_to_human_agents.ts";
7
+ import { retailSlot } from "./store.ts";
8
+ import cancelPendingOrder from "./tools/cancel_pending_order.ts";
9
+ import exchangeDeliveredOrderItems from "./tools/exchange_delivered_order_items.ts";
10
+ import findUserIdByEmail from "./tools/find_user_id_by_email.ts";
11
+ import findUserIdByNameZip from "./tools/find_user_id_by_name_zip.ts";
12
+ import getItemDetails from "./tools/get_item_details.ts";
13
+ import getOrderDetails from "./tools/get_order_details.ts";
14
+ import getProductDetails from "./tools/get_product_details.ts";
15
+ import getUserDetails from "./tools/get_user_details.ts";
16
+ import listAllProductTypes from "./tools/list_all_product_types.ts";
17
+ import modifyPendingOrderAddress from "./tools/modify_pending_order_address.ts";
18
+ import modifyPendingOrderItems from "./tools/modify_pending_order_items.ts";
19
+ import modifyPendingOrderPayment from "./tools/modify_pending_order_payment.ts";
20
+ import modifyUserAddress from "./tools/modify_user_address.ts";
21
+ import returnDeliveredOrderItems from "./tools/return_delivered_order_items.ts";
22
+ import transferToHumanAgents from "./tools/transfer_to_human_agents.ts";
23
23
 
24
24
  /** Each call is its own session, so two contexts are two independent stores —
25
25
  * which is what the isolation tests below rest on. */
26
- function makeCtx(): ToolContext<StateSlot> {
27
- return createToolContext<StateSlot>();
26
+ function makeCtx(): ToolContext {
27
+ return createToolContext();
28
+ }
29
+
30
+ /**
31
+ * The success half of a tool result, or a thrown failure carrying the tool's
32
+ * own message.
33
+ *
34
+ * `ToolDef["execute"]`'s public signature always returns `unknown` — the wire
35
+ * type is fixed so any tool is assignable to `ToolDef`, whatever its body
36
+ * really returns — so every test that reads a field back off a result needed a
37
+ * cast. There were EIGHTEEN of them here, each the same three lines, which is
38
+ * the shape the repo's guides call a missing typed seam: a concentration of
39
+ * identical casts is one narrowing that belongs in one helper, not a cast per
40
+ * assertion. This is that helper, and it is the file's only cast.
41
+ *
42
+ * It also removes the second half of the boilerplate: a `ToolFailure` fails the
43
+ * test HERE, naming what the tool refused, rather than surfacing three lines
44
+ * later as `undefined` on a field nobody assigned.
45
+ */
46
+ function ok<T>(result: unknown): T {
47
+ if (isToolFailure(result)) throw new Error(`tool refused: ${result.error}`);
48
+ return result as T;
28
49
  }
29
50
 
30
51
  /** A context already authenticated as `userId`, via the real tool. */
31
- async function authedCtx(email: string): Promise<ToolContext<StateSlot>> {
52
+ async function authedCtx(email: string): Promise<ToolContext> {
32
53
  const ctx = makeCtx();
33
- // `ToolDef["execute"]`'s public signature always returns `unknown` (the
34
- // wire type is fixed so any tool is assignable to `ToolDef`, regardless of
35
- // what its own `execute` body actually returns), so a cast is needed
36
- // anywhere a test reads a success field back off the result — matching the
37
- // `as {...}` convention other templates already use for the same reason.
38
- const result = (await findUserIdByEmail.execute({ email }, ctx)) as AuthResult | ToolFailure;
39
- if (isToolFailure(result)) throw new Error(`fixture failed to authenticate: ${result.error}`);
54
+ ok<AuthResult>(await findUserIdByEmail.execute({ email }, ctx));
40
55
  return ctx;
41
56
  }
42
57
 
43
58
  describe("authentication", () => {
44
59
  test("find_user_id_by_email is case-insensitive and authenticates the session", async () => {
45
60
  const ctx = makeCtx();
46
- const result = (await findUserIdByEmail.execute(
47
- { email: "OLIVIA.ITO5204@EXAMPLE.COM" },
48
- ctx,
49
- )) as AuthResult | ToolFailure;
50
- expect(isToolFailure(result) ? null : result.user_id).toBe("olivia_ito_3591");
61
+ const result = ok<AuthResult>(
62
+ await findUserIdByEmail.execute({ email: "OLIVIA.ITO5204@EXAMPLE.COM" }, ctx),
63
+ );
64
+ expect(result.user_id).toBe("olivia_ito_3591");
51
65
  expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
52
66
  });
53
67
 
@@ -60,11 +74,13 @@ describe("authentication", () => {
60
74
 
61
75
  test("find_user_id_by_name_zip is case-insensitive on names and exact on zip", async () => {
62
76
  const ctx = makeCtx();
63
- const ok = (await findUserIdByNameZip.execute(
64
- { first_name: "aarav", last_name: "ANDERSON", zip: "19031" },
65
- ctx,
66
- )) as AuthResult | ToolFailure;
67
- expect(isToolFailure(ok) ? null : ok.user_id).toBe("aarav_anderson_8794");
77
+ const found = ok<AuthResult>(
78
+ await findUserIdByNameZip.execute(
79
+ { first_name: "aarav", last_name: "ANDERSON", zip: "19031" },
80
+ ctx,
81
+ ),
82
+ );
83
+ expect(found.user_id).toBe("aarav_anderson_8794");
68
84
 
69
85
  const wrongZip = await findUserIdByNameZip.execute(
70
86
  { first_name: "Aarav", last_name: "Anderson", zip: "78268" },
@@ -74,16 +90,20 @@ describe("authentication", () => {
74
90
  });
75
91
 
76
92
  test("two customers share a first name — the zip is what separates them", async () => {
77
- const a = (await findUserIdByNameZip.execute(
78
- { first_name: "Aarav", last_name: "Anderson", zip: "19031" },
79
- makeCtx(),
80
- )) as AuthResult | ToolFailure;
81
- const b = (await findUserIdByNameZip.execute(
82
- { first_name: "Aarav", last_name: "Gonzalez", zip: "78268" },
83
- makeCtx(),
84
- )) as AuthResult | ToolFailure;
85
- expect(isToolFailure(a) ? null : a.user_id).toBe("aarav_anderson_8794");
86
- expect(isToolFailure(b) ? null : b.user_id).toBe("aarav_gonzalez_5113");
93
+ const a = ok<AuthResult>(
94
+ await findUserIdByNameZip.execute(
95
+ { first_name: "Aarav", last_name: "Anderson", zip: "19031" },
96
+ makeCtx(),
97
+ ),
98
+ );
99
+ const b = ok<AuthResult>(
100
+ await findUserIdByNameZip.execute(
101
+ { first_name: "Aarav", last_name: "Gonzalez", zip: "78268" },
102
+ makeCtx(),
103
+ ),
104
+ );
105
+ expect(a.user_id).toBe("aarav_anderson_8794");
106
+ expect(b.user_id).toBe("aarav_gonzalez_5113");
87
107
  });
88
108
 
89
109
  test("re-authenticating as the same customer is a no-op success", async () => {
@@ -142,10 +162,9 @@ interface ProductTypesResult {
142
162
  describe("read tools", () => {
143
163
  test("get_user_details returns the caller's profile with gift-card balances", async () => {
144
164
  const ctx = await authedCtx("olivia.ito5204@example.com");
145
- const result = (await getUserDetails.execute({ user_id: "olivia_ito_3591" }, ctx)) as
146
- | UserDetailsResult
147
- | ToolFailure;
148
- if (isToolFailure(result)) throw new Error(result.error);
165
+ const result = ok<UserDetailsResult>(
166
+ await getUserDetails.execute({ user_id: "olivia_ito_3591" }, ctx),
167
+ );
149
168
  expect(result.email).toBe("olivia.ito5204@example.com");
150
169
  expect(result.orders).toHaveLength(5);
151
170
  const card = result.payment_methods.find((m) => m.payment_method_id === "gift_card_7794233");
@@ -160,10 +179,9 @@ describe("read tools", () => {
160
179
 
161
180
  test("get_order_details resolves shorthand and sets focus", async () => {
162
181
  const ctx = await authedCtx("olivia.ito5204@example.com");
163
- const result = (await getOrderDetails.execute({ order_id: "the delivered one" }, ctx)) as
164
- | OrderDetailsResult
165
- | ToolFailure;
166
- if (isToolFailure(result)) throw new Error(result.error);
182
+ const result = ok<OrderDetailsResult>(
183
+ await getOrderDetails.execute({ order_id: "the delivered one" }, ctx),
184
+ );
167
185
  expect(result.order_id).toBe("#W5866402");
168
186
  expect(retailSlot.get(ctx).focus.orderId).toBe("#W5866402");
169
187
  });
@@ -175,10 +193,9 @@ describe("read tools", () => {
175
193
  });
176
194
 
177
195
  test("get_product_details lists variants and needs no authentication", async () => {
178
- const result = (await getProductDetails.execute({ product_id: "9832717871" }, makeCtx())) as
179
- | ProductDetailsResult
180
- | ToolFailure;
181
- if (isToolFailure(result)) throw new Error(result.error);
196
+ const result = ok<ProductDetailsResult>(
197
+ await getProductDetails.execute({ product_id: "9832717871" }, makeCtx()),
198
+ );
182
199
  expect(result.name).toBe("Tea Kettle");
183
200
  expect(result.variants.length).toBeGreaterThan(1);
184
201
  expect(result.variants[0]).toHaveProperty("item_id");
@@ -190,19 +207,15 @@ describe("read tools", () => {
190
207
  });
191
208
 
192
209
  test("get_item_details resolves an item without knowing its product", async () => {
193
- const result = (await getItemDetails.execute({ item_id: "3909406921" }, makeCtx())) as
194
- | ItemDetailsResult
195
- | ToolFailure;
196
- if (isToolFailure(result)) throw new Error(result.error);
210
+ const result = ok<ItemDetailsResult>(
211
+ await getItemDetails.execute({ item_id: "3909406921" }, makeCtx()),
212
+ );
197
213
  expect(result.price).toBe(98.25);
198
214
  expect(result.product_name).toBe("Tea Kettle");
199
215
  });
200
216
 
201
217
  test("list_all_product_types returns all 50, sorted by name", async () => {
202
- const result = (await listAllProductTypes.execute({}, makeCtx())) as
203
- | ProductTypesResult
204
- | ToolFailure;
205
- if (isToolFailure(result)) throw new Error(result.error);
218
+ const result = ok<ProductTypesResult>(await listAllProductTypes.execute({}, makeCtx()));
206
219
  expect(Object.keys(result.products)).toHaveLength(50);
207
220
  const names = Object.keys(result.products);
208
221
  // localeCompare, not the default lexicographic sort — the tool sorts for a
@@ -224,11 +237,12 @@ interface CancelOrderResult {
224
237
  describe("cancel_pending_order", () => {
225
238
  test("cancels a pending order and credits a gift card immediately", async () => {
226
239
  const ctx = await authedCtx("aarav.anderson9752@example.com");
227
- const result = (await cancelPendingOrder.execute(
228
- { order_id: "#W9300146", reason: "ordered by mistake" },
229
- ctx,
230
- )) as CancelOrderResult | ToolFailure;
231
- if (isToolFailure(result)) throw new Error(result.error);
240
+ const result = ok<CancelOrderResult>(
241
+ await cancelPendingOrder.execute(
242
+ { order_id: "#W9300146", reason: "ordered by mistake" },
243
+ ctx,
244
+ ),
245
+ );
232
246
  expect(result.status).toBe("cancelled");
233
247
  expect(result.refund_immediate).toBe(true);
234
248
 
@@ -254,11 +268,9 @@ describe("cancel_pending_order", () => {
254
268
 
255
269
  test("a non-gift-card refund takes 5-7 days and moves no balance", async () => {
256
270
  const ctx = await authedCtx("olivia.ito5204@example.com");
257
- const result = (await cancelPendingOrder.execute(
258
- { order_id: "#W5442520", reason: "no longer needed" },
259
- ctx,
260
- )) as CancelOrderResult | ToolFailure;
261
- if (isToolFailure(result)) throw new Error(result.error);
271
+ const result = ok<CancelOrderResult>(
272
+ await cancelPendingOrder.execute({ order_id: "#W5442520", reason: "no longer needed" }, ctx),
273
+ );
262
274
  expect(result.refund_immediate).toBe(false);
263
275
  expect(result.message).toContain("5 to 7");
264
276
  const card = retailSlot.get(ctx).store.users.olivia_ito_3591?.payment_methods.gift_card_7794233;
@@ -312,11 +324,13 @@ describe("cancel_pending_order", () => {
312
324
 
313
325
  test("resolves spoken shorthand to the single pending order", async () => {
314
326
  const ctx = await authedCtx("aarav.anderson9752@example.com");
315
- const result = (await cancelPendingOrder.execute(
316
- { order_id: "my pending order", reason: "ordered by mistake" },
317
- ctx,
318
- )) as CancelOrderResult | ToolFailure;
319
- expect(isToolFailure(result) ? null : result.order_id).toBe("#W9300146");
327
+ const result = ok<CancelOrderResult>(
328
+ await cancelPendingOrder.execute(
329
+ { order_id: "my pending order", reason: "ordered by mistake" },
330
+ ctx,
331
+ ),
332
+ );
333
+ expect(result.order_id).toBe("#W9300146");
320
334
  });
321
335
 
322
336
  test("refuses ambiguous shorthand rather than cancelling the wrong order", async () => {
@@ -350,11 +364,9 @@ interface AddressToolResult {
350
364
  describe("modify_pending_order_address", () => {
351
365
  test("rewrites a pending order's shipping address", async () => {
352
366
  const ctx = await authedCtx("aarav.anderson9752@example.com");
353
- const result = (await modifyPendingOrderAddress.execute(
354
- { order_id: "#W9300146", ...NEW_ADDRESS },
355
- ctx,
356
- )) as AddressToolResult | ToolFailure;
357
- if (isToolFailure(result)) throw new Error(result.error);
367
+ const result = ok<AddressToolResult>(
368
+ await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
369
+ );
358
370
  expect(result.address).toEqual(NEW_ADDRESS);
359
371
  expect(retailSlot.get(ctx).store.orders["#W9300146"]?.address).toEqual(NEW_ADDRESS);
360
372
  });
@@ -367,9 +379,11 @@ describe("modify_pending_order_address", () => {
367
379
 
368
380
  test("accepts a 'pending (item modified)' order — unlike cancel", async () => {
369
381
  const ctx = await authedCtx("aarav.anderson9752@example.com");
370
- const order = retailSlot.get(ctx).store.orders["#W9300146"];
371
- if (!order) throw new Error("fixture missing");
372
- order.status = "pending (item modified)";
382
+ retailSlot.update(ctx, (state) => {
383
+ const order = state.store.orders["#W9300146"];
384
+ if (!order) throw new Error("fixture missing");
385
+ order.status = "pending (item modified)";
386
+ });
373
387
  const result = await modifyPendingOrderAddress.execute(
374
388
  { order_id: "#W9300146", ...NEW_ADDRESS },
375
389
  ctx,
@@ -399,11 +413,7 @@ describe("modify_pending_order_address", () => {
399
413
  describe("modify_user_address", () => {
400
414
  test("rewrites the customer's default address", async () => {
401
415
  const ctx = await authedCtx("emma.smith3991@example.com");
402
- const result = await modifyUserAddress.execute(
403
- { user_id: "emma_smith_8564", ...NEW_ADDRESS },
404
- ctx,
405
- );
406
- if (isToolFailure(result)) throw new Error(result.error);
416
+ ok(await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx));
407
417
  expect(retailSlot.get(ctx).store.users.emma_smith_8564?.address).toEqual(NEW_ADDRESS);
408
418
  });
409
419
 
@@ -444,16 +454,17 @@ interface ModifyItemsResult {
444
454
  describe("modify_pending_order_items", () => {
445
455
  test("swaps an item, charges the difference to a gift card, and goes terminal", async () => {
446
456
  const ctx = await authedCtx("emma.smith3991@example.com");
447
- const result = (await modifyPendingOrderItems.execute(
448
- {
449
- order_id: "#W2417020",
450
- item_ids: ["8997785118"],
451
- new_item_ids: ["6017636844"],
452
- payment_method_id: "gift_card_8541487",
453
- },
454
- ctx,
455
- )) as ModifyItemsResult | ToolFailure;
456
- if (isToolFailure(result)) throw new Error(result.error);
457
+ const result = ok<ModifyItemsResult>(
458
+ await modifyPendingOrderItems.execute(
459
+ {
460
+ order_id: "#W2417020",
461
+ item_ids: ["8997785118"],
462
+ new_item_ids: ["6017636844"],
463
+ payment_method_id: "gift_card_8541487",
464
+ },
465
+ ctx,
466
+ ),
467
+ );
457
468
  expect(result.price_difference).toBe(-382.03);
458
469
  expect(result.status).toBe("pending (item modified)");
459
470
 
@@ -554,16 +565,17 @@ describe("modify_pending_order_items", () => {
554
565
 
555
566
  test("the same upgrade succeeds on a payment method with no balance to run out of", async () => {
556
567
  const ctx = await authedCtx("anya.garcia2061@example.com");
557
- const result = (await modifyPendingOrderItems.execute(
558
- {
559
- order_id: "#W6436609",
560
- item_ids: ["6017636844"],
561
- new_item_ids: ["9844888101"],
562
- payment_method_id: "credit_card_8955149",
563
- },
564
- ctx,
565
- )) as ModifyItemsResult | ToolFailure;
566
- if (isToolFailure(result)) throw new Error(result.error);
568
+ const result = ok<ModifyItemsResult>(
569
+ await modifyPendingOrderItems.execute(
570
+ {
571
+ order_id: "#W6436609",
572
+ item_ids: ["6017636844"],
573
+ new_item_ids: ["9844888101"],
574
+ payment_method_id: "credit_card_8955149",
575
+ },
576
+ ctx,
577
+ ),
578
+ );
567
579
  expect(result.price_difference).toBe(167.37);
568
580
  const history = retailSlot.get(ctx).store.orders["#W6436609"]?.payment_history ?? [];
569
581
  expect(history[1]).toMatchObject({
@@ -616,11 +628,12 @@ describe("modify_pending_order_payment", () => {
616
628
  test("moves an order to a different method, refunding the old one", async () => {
617
629
  const ctx = await authedCtx("olivia.ito5204@example.com");
618
630
  // #W5442520 is $663.85 on credit_card_9753331. Pay by PayPal instead.
619
- const result = (await modifyPendingOrderPayment.execute(
620
- { order_id: "#W5442520", payment_method_id: "paypal_8049766" },
621
- ctx,
622
- )) as ModifyPaymentResult | ToolFailure;
623
- if (isToolFailure(result)) throw new Error(result.error);
631
+ const result = ok<ModifyPaymentResult>(
632
+ await modifyPendingOrderPayment.execute(
633
+ { order_id: "#W5442520", payment_method_id: "paypal_8049766" },
634
+ ctx,
635
+ ),
636
+ );
624
637
  expect(result.status).toBe("pending");
625
638
 
626
639
  const history = retailSlot.get(ctx).store.orders["#W5442520"]?.payment_history ?? [];
@@ -675,14 +688,16 @@ describe("modify_pending_order_payment", () => {
675
688
 
676
689
  test("refuses an order whose payment history is not a single payment", async () => {
677
690
  const ctx = await authedCtx("olivia.ito5204@example.com");
678
- const order = retailSlot.get(ctx).store.orders["#W5442520"];
679
- if (!order) throw new Error("fixture missing");
680
691
  // No seeded pending order has a second history entry, so construct one —
681
692
  // tau2 guards this case and the guard should still be covered.
682
- order.payment_history.push({
683
- transaction_type: "refund",
684
- amount: 10,
685
- payment_method_id: "credit_card_9753331",
693
+ retailSlot.update(ctx, (state) => {
694
+ const order = state.store.orders["#W5442520"];
695
+ if (!order) throw new Error("fixture missing");
696
+ order.payment_history.push({
697
+ transaction_type: "refund",
698
+ amount: 10,
699
+ payment_method_id: "credit_card_9753331",
700
+ });
686
701
  });
687
702
  const result = await modifyPendingOrderPayment.execute(
688
703
  { order_id: "#W5442520", payment_method_id: "paypal_8049766" },
@@ -722,23 +737,23 @@ interface ExchangeResult {
722
737
  order_id: string;
723
738
  status: string;
724
739
  price_difference: number;
725
- exchange_items: string[];
726
- exchange_new_items: string[];
740
+ exchanges: { item_id: string; new_item_id: string; price_difference: number }[];
727
741
  message: string;
728
742
  }
729
743
 
730
744
  describe("return_delivered_order_items", () => {
731
745
  test("requests a return refunded to the original method", async () => {
732
746
  const ctx = await authedCtx("harper.brown3965@example.com");
733
- const result = (await returnDeliveredOrderItems.execute(
734
- {
735
- order_id: "#W1840144",
736
- item_ids: ["8590708195"],
737
- payment_method_id: "paypal_2306935",
738
- },
739
- ctx,
740
- )) as ReturnResult | ToolFailure;
741
- if (isToolFailure(result)) throw new Error(result.error);
747
+ const result = ok<ReturnResult>(
748
+ await returnDeliveredOrderItems.execute(
749
+ {
750
+ order_id: "#W1840144",
751
+ item_ids: ["8590708195"],
752
+ payment_method_id: "paypal_2306935",
753
+ },
754
+ ctx,
755
+ ),
756
+ );
742
757
  expect(result.status).toBe("return requested");
743
758
  const order = retailSlot.get(ctx).store.orders["#W1840144"];
744
759
  expect(order?.return_items).toEqual(["8590708195"]);
@@ -832,17 +847,21 @@ describe("return_delivered_order_items", () => {
832
847
  describe("exchange_delivered_order_items", () => {
833
848
  test("records an exchange with a positive price difference", async () => {
834
849
  const ctx = await authedCtx("aarav.anderson9752@example.com");
835
- const result = (await exchangeDeliveredOrderItems.execute(
836
- {
837
- order_id: "#W4316152",
838
- item_ids: ["7292993796"],
839
- new_item_ids: ["3909406921"],
840
- payment_method_id: "gift_card_7245904",
841
- },
842
- ctx,
843
- )) as ExchangeResult | ToolFailure;
844
- if (isToolFailure(result)) throw new Error(result.error);
850
+ const result = ok<ExchangeResult>(
851
+ await exchangeDeliveredOrderItems.execute(
852
+ {
853
+ order_id: "#W4316152",
854
+ item_ids: ["7292993796"],
855
+ new_item_ids: ["3909406921"],
856
+ payment_method_id: "gift_card_7245904",
857
+ },
858
+ ctx,
859
+ ),
860
+ );
845
861
  expect(result.price_difference).toBe(3.45);
862
+ expect(result.exchanges).toEqual([
863
+ { item_id: "7292993796", new_item_id: "3909406921", price_difference: 3.45 },
864
+ ]);
846
865
  const order = retailSlot.get(ctx).store.orders["#W4316152"];
847
866
  expect(order?.status).toBe("exchange requested");
848
867
  expect(order?.exchange_items).toEqual(["7292993796"]);
@@ -850,6 +869,45 @@ describe("exchange_delivered_order_items", () => {
850
869
  expect(order?.exchange_price_difference).toBe(3.45);
851
870
  });
852
871
 
872
+ test("the ANSWER pairs by position even where the stored fields sort separately", async () => {
873
+ // `#W5866402` is the case that makes the difference visible: the two
874
+ // requested replacements sort into the OTHER order, so the two stored lists
875
+ // — tau2's `sorted(item_ids)` / `sorted(new_item_ids)`, kept for fidelity
876
+ // with its expected end states — read as espresso→sneaker if anything
877
+ // treats them as a pairing. `planItemSwap` priced this positionally, so the
878
+ // result has to report the pairing that was priced, not the one the sort
879
+ // produced.
880
+ const ctx = await authedCtx("olivia.ito5204@example.com");
881
+ const result = ok<ExchangeResult>(
882
+ await exchangeDeliveredOrderItems.execute(
883
+ {
884
+ order_id: "#W5866402",
885
+ item_ids: ["6242772310", "9727387530"],
886
+ new_item_ids: ["7407838442", "2509076505"],
887
+ payment_method_id: "paypal_8049766",
888
+ },
889
+ ctx,
890
+ ),
891
+ );
892
+
893
+ expect(result.exchanges).toEqual([
894
+ { item_id: "6242772310", new_item_id: "7407838442", price_difference: 85.88 },
895
+ { item_id: "9727387530", new_item_id: "2509076505", price_difference: -18.25 },
896
+ ]);
897
+ // The per-line differences add up to the quoted total — which is what makes
898
+ // the answer internally consistent rather than two numbers side by side.
899
+ expect(result.exchanges.reduce((sum, one) => sum + one.price_difference, 0)).toBeCloseTo(
900
+ result.price_difference,
901
+ 5,
902
+ );
903
+
904
+ // The stored fields keep tau2's shape: two sorted SETS, and the second one
905
+ // really is in the other order.
906
+ const order = retailSlot.get(ctx).store.orders["#W5866402"];
907
+ expect(order?.exchange_items).toEqual(["6242772310", "9727387530"]);
908
+ expect(order?.exchange_new_items).toEqual(["2509076505", "7407838442"]);
909
+ });
910
+
853
911
  test("does NOT move the gift-card balance — it is a request, not a settlement", async () => {
854
912
  const ctx = await authedCtx("aarav.anderson9752@example.com");
855
913
  await exchangeDeliveredOrderItems.execute(
@@ -885,30 +943,34 @@ describe("exchange_delivered_order_items", () => {
885
943
 
886
944
  test("exchanges both copies of a duplicate and doubles the difference", async () => {
887
945
  const ctx = await authedCtx("aarav.anderson9752@example.com");
888
- const result = (await exchangeDeliveredOrderItems.execute(
889
- {
890
- order_id: "#W4316152",
891
- item_ids: ["7292993796", "7292993796"],
892
- new_item_ids: ["3909406921", "3909406921"],
893
- payment_method_id: "gift_card_7245904",
894
- },
895
- ctx,
896
- )) as ExchangeResult | ToolFailure;
897
- expect(isToolFailure(result) ? null : result.price_difference).toBe(6.9);
946
+ const result = ok<ExchangeResult>(
947
+ await exchangeDeliveredOrderItems.execute(
948
+ {
949
+ order_id: "#W4316152",
950
+ item_ids: ["7292993796", "7292993796"],
951
+ new_item_ids: ["3909406921", "3909406921"],
952
+ payment_method_id: "gift_card_7245904",
953
+ },
954
+ ctx,
955
+ ),
956
+ );
957
+ expect(result.price_difference).toBe(6.9);
898
958
  });
899
959
 
900
960
  test("records a negative difference as a refund direction", async () => {
901
961
  const ctx = await authedCtx("olivia.ito5204@example.com");
902
- const result = (await exchangeDeliveredOrderItems.execute(
903
- {
904
- order_id: "#W5866402",
905
- item_ids: ["6242772310"],
906
- new_item_ids: ["6200867091"],
907
- payment_method_id: "gift_card_7794233",
908
- },
909
- ctx,
910
- )) as ExchangeResult | ToolFailure;
911
- expect(isToolFailure(result) ? null : result.price_difference).toBe(-40.86);
962
+ const result = ok<ExchangeResult>(
963
+ await exchangeDeliveredOrderItems.execute(
964
+ {
965
+ order_id: "#W5866402",
966
+ item_ids: ["6242772310"],
967
+ new_item_ids: ["6200867091"],
968
+ payment_method_id: "gift_card_7794233",
969
+ },
970
+ ctx,
971
+ ),
972
+ );
973
+ expect(result.price_difference).toBe(-40.86);
912
974
  });
913
975
 
914
976
  test("refuses a gift card that cannot cover the difference", async () => {
@@ -1019,11 +1081,13 @@ interface TransferResult {
1019
1081
 
1020
1082
  describe("transfer_to_human_agents", () => {
1021
1083
  test("works without authentication — the escape hatch cannot be gated", async () => {
1022
- const result = (await transferToHumanAgents.execute(
1023
- { summary: "Caller wants to dispute a charge from 2019." },
1024
- makeCtx(),
1025
- )) as TransferResult | ToolFailure;
1084
+ const result = ok<TransferResult>(
1085
+ await transferToHumanAgents.execute(
1086
+ { summary: "Caller wants to dispute a charge from 2019." },
1087
+ makeCtx(),
1088
+ ),
1089
+ );
1026
1090
  expect(isToolFailure(result)).toBe(false);
1027
- expect(isToolFailure(result) ? null : result.transferred).toBe(true);
1091
+ expect(result.transferred).toBe(true);
1028
1092
  });
1029
1093
  });