@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
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { ToolContext } from "@alexkroman1/aai";
|
|
2
2
|
import { isToolFailure } from "@alexkroman1/aai";
|
|
3
|
-
import { createToolContext } from "@alexkroman1/aai/testing";
|
|
3
|
+
import { createToolContext, ok } from "@alexkroman1/aai/testing";
|
|
4
4
|
import { describe, expect, test } from "vitest";
|
|
5
5
|
import type { AuthResult } from "./authenticate.ts";
|
|
6
|
+
import type { StagedResult } from "./pending.ts";
|
|
6
7
|
import type { Address } from "./shared.ts";
|
|
7
|
-
import { retailSlot } from "./store.ts";
|
|
8
|
+
import { callFlow, retailSlot } from "./store.ts";
|
|
8
9
|
import cancelPendingOrder from "./tools/cancel_pending_order.ts";
|
|
10
|
+
import confirmChange from "./tools/confirm_change.ts";
|
|
9
11
|
import exchangeDeliveredOrderItems from "./tools/exchange_delivered_order_items.ts";
|
|
10
12
|
import findUserIdByEmail from "./tools/find_user_id_by_email.ts";
|
|
11
13
|
import findUserIdByNameZip from "./tools/find_user_id_by_name_zip.ts";
|
|
@@ -27,27 +29,6 @@ function makeCtx(): ToolContext {
|
|
|
27
29
|
return createToolContext();
|
|
28
30
|
}
|
|
29
31
|
|
|
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;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
32
|
/** A context already authenticated as `userId`, via the real tool. */
|
|
52
33
|
async function authedCtx(email: string): Promise<ToolContext> {
|
|
53
34
|
const ctx = makeCtx();
|
|
@@ -55,6 +36,26 @@ async function authedCtx(email: string): Promise<ToolContext> {
|
|
|
55
36
|
return ctx;
|
|
56
37
|
}
|
|
57
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Stage a change and say yes — the two calls one caller turn now produces.
|
|
41
|
+
*
|
|
42
|
+
* Nothing in this template mutates before `confirm_change`, so every spec about
|
|
43
|
+
* an APPLIED change has to run both halves. That is not helper ceremony: it is
|
|
44
|
+
* the property under test, and a spec that forgot the second call would find
|
|
45
|
+
* the store untouched. It asserts the readback exists on the way through,
|
|
46
|
+
* because a staged change the agent has no sentence for is the one shape that
|
|
47
|
+
* would let this gate pass while carrying nothing to confirm.
|
|
48
|
+
*/
|
|
49
|
+
async function confirmed<R = unknown>(staged: unknown, ctx: ToolContext): Promise<R> {
|
|
50
|
+
const stage = ok<StagedResult>(staged);
|
|
51
|
+
// A throw rather than an `expect`, which biome's `noMisplacedAssertion`
|
|
52
|
+
// rightly refuses outside a test body. A staged change with no sentence to
|
|
53
|
+
// read back is the one shape that would let the gate pass while carrying
|
|
54
|
+
// nothing to confirm, so it stops the spec here rather than downstream.
|
|
55
|
+
if (stage.read_back.length === 0) throw new Error(`${stage.staged} staged with no readback`);
|
|
56
|
+
return ok<R>(await confirmChange.execute({}, ctx));
|
|
57
|
+
}
|
|
58
|
+
|
|
58
59
|
describe("authentication", () => {
|
|
59
60
|
test("find_user_id_by_email is case-insensitive and authenticates the session", async () => {
|
|
60
61
|
const ctx = makeCtx();
|
|
@@ -63,6 +64,9 @@ describe("authentication", () => {
|
|
|
63
64
|
);
|
|
64
65
|
expect(result.user_id).toBe("olivia_ito_3591");
|
|
65
66
|
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
67
|
+
// Two facts, two homes: the store latches WHO, the flow holds WHETHER.
|
|
68
|
+
// `serving` is a parent now, so an identified call reports its child.
|
|
69
|
+
expect(callFlow.position(ctx).state).toBe("serving.helping");
|
|
66
70
|
});
|
|
67
71
|
|
|
68
72
|
test("an unknown email is refused and leaves the session unauthenticated", async () => {
|
|
@@ -70,6 +74,9 @@ describe("authentication", () => {
|
|
|
70
74
|
const result = await findUserIdByEmail.execute({ email: "nobody@example.com" }, ctx);
|
|
71
75
|
expect(isToolFailure(result)).toBe(true);
|
|
72
76
|
expect(retailSlot.get(ctx).authenticatedUserId).toBeNull();
|
|
77
|
+
// `IDENTIFIED` is not sent when the body answers a `ToolFailure`, so a
|
|
78
|
+
// lookup that found nobody cannot leave the call one step ahead of itself.
|
|
79
|
+
expect(callFlow.position(ctx).state).toBe("identifying");
|
|
73
80
|
});
|
|
74
81
|
|
|
75
82
|
test("find_user_id_by_name_zip is case-insensitive on names and exact on zip", async () => {
|
|
@@ -121,6 +128,9 @@ describe("authentication", () => {
|
|
|
121
128
|
);
|
|
122
129
|
expect(isToolFailure(switched) && switched.error.toLowerCase()).toContain("one customer");
|
|
123
130
|
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
131
|
+
// The tool is legal in `serving` — the refusal comes from `authenticateAs`,
|
|
132
|
+
// not from the gate — so the call stays exactly where it was.
|
|
133
|
+
expect(callFlow.position(ctx).state).toBe("serving.helping");
|
|
124
134
|
});
|
|
125
135
|
|
|
126
136
|
test("switching via name + zip is refused too — both doors, one lock", async () => {
|
|
@@ -237,11 +247,12 @@ interface CancelOrderResult {
|
|
|
237
247
|
describe("cancel_pending_order", () => {
|
|
238
248
|
test("cancels a pending order and credits a gift card immediately", async () => {
|
|
239
249
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
240
|
-
const result =
|
|
250
|
+
const result = await confirmed<CancelOrderResult>(
|
|
241
251
|
await cancelPendingOrder.execute(
|
|
242
252
|
{ order_id: "#W9300146", reason: "ordered by mistake" },
|
|
243
253
|
ctx,
|
|
244
254
|
),
|
|
255
|
+
ctx,
|
|
245
256
|
);
|
|
246
257
|
expect(result.status).toBe("cancelled");
|
|
247
258
|
expect(result.refund_immediate).toBe(true);
|
|
@@ -256,7 +267,10 @@ describe("cancel_pending_order", () => {
|
|
|
256
267
|
|
|
257
268
|
test("appends a matching refund to the payment history", async () => {
|
|
258
269
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
259
|
-
await
|
|
270
|
+
await confirmed(
|
|
271
|
+
await cancelPendingOrder.execute({ order_id: "#W9300146", reason: "no longer needed" }, ctx),
|
|
272
|
+
ctx,
|
|
273
|
+
);
|
|
260
274
|
const history = retailSlot.get(ctx).store.orders["#W9300146"]?.payment_history ?? [];
|
|
261
275
|
expect(history).toHaveLength(2);
|
|
262
276
|
expect(history[1]).toMatchObject({
|
|
@@ -268,8 +282,9 @@ describe("cancel_pending_order", () => {
|
|
|
268
282
|
|
|
269
283
|
test("a non-gift-card refund takes 5-7 days and moves no balance", async () => {
|
|
270
284
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
271
|
-
const result =
|
|
285
|
+
const result = await confirmed<CancelOrderResult>(
|
|
272
286
|
await cancelPendingOrder.execute({ order_id: "#W5442520", reason: "no longer needed" }, ctx),
|
|
287
|
+
ctx,
|
|
273
288
|
);
|
|
274
289
|
expect(result.refund_immediate).toBe(false);
|
|
275
290
|
expect(result.message).toContain("5 to 7");
|
|
@@ -311,7 +326,10 @@ describe("cancel_pending_order", () => {
|
|
|
311
326
|
|
|
312
327
|
test("refuses cancelling twice", async () => {
|
|
313
328
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
314
|
-
await
|
|
329
|
+
await confirmed(
|
|
330
|
+
await cancelPendingOrder.execute({ order_id: "#W9300146", reason: "no longer needed" }, ctx),
|
|
331
|
+
ctx,
|
|
332
|
+
);
|
|
315
333
|
const second = await cancelPendingOrder.execute(
|
|
316
334
|
{ order_id: "#W9300146", reason: "no longer needed" },
|
|
317
335
|
ctx,
|
|
@@ -324,11 +342,12 @@ describe("cancel_pending_order", () => {
|
|
|
324
342
|
|
|
325
343
|
test("resolves spoken shorthand to the single pending order", async () => {
|
|
326
344
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
327
|
-
const result =
|
|
345
|
+
const result = await confirmed<CancelOrderResult>(
|
|
328
346
|
await cancelPendingOrder.execute(
|
|
329
347
|
{ order_id: "my pending order", reason: "ordered by mistake" },
|
|
330
348
|
ctx,
|
|
331
349
|
),
|
|
350
|
+
ctx,
|
|
332
351
|
);
|
|
333
352
|
expect(result.order_id).toBe("#W9300146");
|
|
334
353
|
});
|
|
@@ -364,8 +383,9 @@ interface AddressToolResult {
|
|
|
364
383
|
describe("modify_pending_order_address", () => {
|
|
365
384
|
test("rewrites a pending order's shipping address", async () => {
|
|
366
385
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
367
|
-
const result =
|
|
386
|
+
const result = await confirmed<AddressToolResult>(
|
|
368
387
|
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
|
|
388
|
+
ctx,
|
|
369
389
|
);
|
|
370
390
|
expect(result.address).toEqual(NEW_ADDRESS);
|
|
371
391
|
expect(retailSlot.get(ctx).store.orders["#W9300146"]?.address).toEqual(NEW_ADDRESS);
|
|
@@ -373,7 +393,10 @@ describe("modify_pending_order_address", () => {
|
|
|
373
393
|
|
|
374
394
|
test("leaves the customer's default address untouched", async () => {
|
|
375
395
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
376
|
-
await
|
|
396
|
+
await confirmed(
|
|
397
|
+
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
|
|
398
|
+
ctx,
|
|
399
|
+
);
|
|
377
400
|
expect(retailSlot.get(ctx).store.users.aarav_anderson_8794?.address.zip).toBe("19031");
|
|
378
401
|
});
|
|
379
402
|
|
|
@@ -413,14 +436,20 @@ describe("modify_pending_order_address", () => {
|
|
|
413
436
|
describe("modify_user_address", () => {
|
|
414
437
|
test("rewrites the customer's default address", async () => {
|
|
415
438
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
416
|
-
|
|
439
|
+
await confirmed(
|
|
440
|
+
await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx),
|
|
441
|
+
ctx,
|
|
442
|
+
);
|
|
417
443
|
expect(retailSlot.get(ctx).store.users.emma_smith_8564?.address).toEqual(NEW_ADDRESS);
|
|
418
444
|
});
|
|
419
445
|
|
|
420
446
|
test("leaves existing orders' addresses untouched", async () => {
|
|
421
447
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
422
448
|
const before = structuredClone(retailSlot.get(ctx).store.orders["#W2417020"]?.address);
|
|
423
|
-
await
|
|
449
|
+
await confirmed(
|
|
450
|
+
await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx),
|
|
451
|
+
ctx,
|
|
452
|
+
);
|
|
424
453
|
expect(retailSlot.get(ctx).store.orders["#W2417020"]?.address).toEqual(before);
|
|
425
454
|
});
|
|
426
455
|
|
|
@@ -454,7 +483,7 @@ interface ModifyItemsResult {
|
|
|
454
483
|
describe("modify_pending_order_items", () => {
|
|
455
484
|
test("swaps an item, charges the difference to a gift card, and goes terminal", async () => {
|
|
456
485
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
457
|
-
const result =
|
|
486
|
+
const result = await confirmed<ModifyItemsResult>(
|
|
458
487
|
await modifyPendingOrderItems.execute(
|
|
459
488
|
{
|
|
460
489
|
order_id: "#W2417020",
|
|
@@ -464,6 +493,7 @@ describe("modify_pending_order_items", () => {
|
|
|
464
493
|
},
|
|
465
494
|
ctx,
|
|
466
495
|
),
|
|
496
|
+
ctx,
|
|
467
497
|
);
|
|
468
498
|
expect(result.price_difference).toBe(-382.03);
|
|
469
499
|
expect(result.status).toBe("pending (item modified)");
|
|
@@ -479,13 +509,16 @@ describe("modify_pending_order_items", () => {
|
|
|
479
509
|
|
|
480
510
|
test("records the difference as a refund entry when the new item is cheaper", async () => {
|
|
481
511
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
482
|
-
await
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
512
|
+
await confirmed(
|
|
513
|
+
await modifyPendingOrderItems.execute(
|
|
514
|
+
{
|
|
515
|
+
order_id: "#W2417020",
|
|
516
|
+
item_ids: ["8997785118"],
|
|
517
|
+
new_item_ids: ["6017636844"],
|
|
518
|
+
payment_method_id: "gift_card_8541487",
|
|
519
|
+
},
|
|
520
|
+
ctx,
|
|
521
|
+
),
|
|
489
522
|
ctx,
|
|
490
523
|
);
|
|
491
524
|
const history = retailSlot.get(ctx).store.orders["#W2417020"]?.payment_history ?? [];
|
|
@@ -498,13 +531,16 @@ describe("modify_pending_order_items", () => {
|
|
|
498
531
|
|
|
499
532
|
test("refuses a second modification — the status is terminal", async () => {
|
|
500
533
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
501
|
-
await
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
534
|
+
await confirmed(
|
|
535
|
+
await modifyPendingOrderItems.execute(
|
|
536
|
+
{
|
|
537
|
+
order_id: "#W2417020",
|
|
538
|
+
item_ids: ["8997785118"],
|
|
539
|
+
new_item_ids: ["6017636844"],
|
|
540
|
+
payment_method_id: "gift_card_8541487",
|
|
541
|
+
},
|
|
542
|
+
ctx,
|
|
543
|
+
),
|
|
508
544
|
ctx,
|
|
509
545
|
);
|
|
510
546
|
const second = await modifyPendingOrderItems.execute(
|
|
@@ -521,13 +557,16 @@ describe("modify_pending_order_items", () => {
|
|
|
521
557
|
|
|
522
558
|
test("a modified order can no longer be cancelled", async () => {
|
|
523
559
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
524
|
-
await
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
560
|
+
await confirmed(
|
|
561
|
+
await modifyPendingOrderItems.execute(
|
|
562
|
+
{
|
|
563
|
+
order_id: "#W2417020",
|
|
564
|
+
item_ids: ["8997785118"],
|
|
565
|
+
new_item_ids: ["6017636844"],
|
|
566
|
+
payment_method_id: "gift_card_8541487",
|
|
567
|
+
},
|
|
568
|
+
ctx,
|
|
569
|
+
),
|
|
531
570
|
ctx,
|
|
532
571
|
);
|
|
533
572
|
const cancelled = await cancelPendingOrder.execute(
|
|
@@ -565,7 +604,7 @@ describe("modify_pending_order_items", () => {
|
|
|
565
604
|
|
|
566
605
|
test("the same upgrade succeeds on a payment method with no balance to run out of", async () => {
|
|
567
606
|
const ctx = await authedCtx("anya.garcia2061@example.com");
|
|
568
|
-
const result =
|
|
607
|
+
const result = await confirmed<ModifyItemsResult>(
|
|
569
608
|
await modifyPendingOrderItems.execute(
|
|
570
609
|
{
|
|
571
610
|
order_id: "#W6436609",
|
|
@@ -575,6 +614,7 @@ describe("modify_pending_order_items", () => {
|
|
|
575
614
|
},
|
|
576
615
|
ctx,
|
|
577
616
|
),
|
|
617
|
+
ctx,
|
|
578
618
|
);
|
|
579
619
|
expect(result.price_difference).toBe(167.37);
|
|
580
620
|
const history = retailSlot.get(ctx).store.orders["#W6436609"]?.payment_history ?? [];
|
|
@@ -628,11 +668,12 @@ describe("modify_pending_order_payment", () => {
|
|
|
628
668
|
test("moves an order to a different method, refunding the old one", async () => {
|
|
629
669
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
630
670
|
// #W5442520 is $663.85 on credit_card_9753331. Pay by PayPal instead.
|
|
631
|
-
const result =
|
|
671
|
+
const result = await confirmed<ModifyPaymentResult>(
|
|
632
672
|
await modifyPendingOrderPayment.execute(
|
|
633
673
|
{ order_id: "#W5442520", payment_method_id: "paypal_8049766" },
|
|
634
674
|
ctx,
|
|
635
675
|
),
|
|
676
|
+
ctx,
|
|
636
677
|
);
|
|
637
678
|
expect(result.status).toBe("pending");
|
|
638
679
|
|
|
@@ -653,11 +694,13 @@ describe("modify_pending_order_payment", () => {
|
|
|
653
694
|
test("refunds a gift card that was the original method", async () => {
|
|
654
695
|
const ctx = await authedCtx("aarav.gonzalez9269@example.com");
|
|
655
696
|
// #W9160732 is $1011.54 on gift_card_5979071 ($96.00). Move to PayPal.
|
|
656
|
-
|
|
657
|
-
|
|
697
|
+
await confirmed(
|
|
698
|
+
await modifyPendingOrderPayment.execute(
|
|
699
|
+
{ order_id: "#W9160732", payment_method_id: "paypal_6121064" },
|
|
700
|
+
ctx,
|
|
701
|
+
),
|
|
658
702
|
ctx,
|
|
659
703
|
);
|
|
660
|
-
expect(isToolFailure(result)).toBe(false);
|
|
661
704
|
const card =
|
|
662
705
|
retailSlot.get(ctx).store.users.aarav_gonzalez_5113?.payment_methods.gift_card_5979071;
|
|
663
706
|
expect(card?.source === "gift_card" && card.balance).toBe(1107.54);
|
|
@@ -744,7 +787,7 @@ interface ExchangeResult {
|
|
|
744
787
|
describe("return_delivered_order_items", () => {
|
|
745
788
|
test("requests a return refunded to the original method", async () => {
|
|
746
789
|
const ctx = await authedCtx("harper.brown3965@example.com");
|
|
747
|
-
const result =
|
|
790
|
+
const result = await confirmed<ReturnResult>(
|
|
748
791
|
await returnDeliveredOrderItems.execute(
|
|
749
792
|
{
|
|
750
793
|
order_id: "#W1840144",
|
|
@@ -753,6 +796,7 @@ describe("return_delivered_order_items", () => {
|
|
|
753
796
|
},
|
|
754
797
|
ctx,
|
|
755
798
|
),
|
|
799
|
+
ctx,
|
|
756
800
|
);
|
|
757
801
|
expect(result.status).toBe("return requested");
|
|
758
802
|
const order = retailSlot.get(ctx).store.orders["#W1840144"];
|
|
@@ -789,20 +833,25 @@ describe("return_delivered_order_items", () => {
|
|
|
789
833
|
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.status).toBe("delivered");
|
|
790
834
|
});
|
|
791
835
|
|
|
792
|
-
test("returns both copies of a duplicate item
|
|
836
|
+
test("returns both copies of a duplicate item, in the order they were named", async () => {
|
|
793
837
|
const ctx = await authedCtx("harper.brown3965@example.com");
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
838
|
+
await confirmed(
|
|
839
|
+
await returnDeliveredOrderItems.execute(
|
|
840
|
+
{
|
|
841
|
+
order_id: "#W1840144",
|
|
842
|
+
item_ids: ["8590708195", "6534134392", "8590708195"],
|
|
843
|
+
payment_method_id: "paypal_2306935",
|
|
844
|
+
},
|
|
845
|
+
ctx,
|
|
846
|
+
),
|
|
800
847
|
ctx,
|
|
801
848
|
);
|
|
802
|
-
|
|
849
|
+
// Not sorted. tau2 sorted this list to match an expected end state and
|
|
850
|
+
// nothing compares against one any more, so it holds the caller's own
|
|
851
|
+
// order — which is the order the readback said them in.
|
|
803
852
|
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.return_items).toEqual([
|
|
804
|
-
"6534134392",
|
|
805
853
|
"8590708195",
|
|
854
|
+
"6534134392",
|
|
806
855
|
"8590708195",
|
|
807
856
|
]);
|
|
808
857
|
});
|
|
@@ -832,8 +881,11 @@ describe("return_delivered_order_items", () => {
|
|
|
832
881
|
);
|
|
833
882
|
expect(isToolFailure(pending)).toBe(true);
|
|
834
883
|
|
|
835
|
-
await
|
|
836
|
-
|
|
884
|
+
await confirmed(
|
|
885
|
+
await returnDeliveredOrderItems.execute(
|
|
886
|
+
{ order_id: "#W1840144", item_ids: ["8590708195"], payment_method_id: "paypal_2306935" },
|
|
887
|
+
ctx,
|
|
888
|
+
),
|
|
837
889
|
ctx,
|
|
838
890
|
);
|
|
839
891
|
const again = await returnDeliveredOrderItems.execute(
|
|
@@ -847,7 +899,7 @@ describe("return_delivered_order_items", () => {
|
|
|
847
899
|
describe("exchange_delivered_order_items", () => {
|
|
848
900
|
test("records an exchange with a positive price difference", async () => {
|
|
849
901
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
850
|
-
const result =
|
|
902
|
+
const result = await confirmed<ExchangeResult>(
|
|
851
903
|
await exchangeDeliveredOrderItems.execute(
|
|
852
904
|
{
|
|
853
905
|
order_id: "#W4316152",
|
|
@@ -857,6 +909,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
857
909
|
},
|
|
858
910
|
ctx,
|
|
859
911
|
),
|
|
912
|
+
ctx,
|
|
860
913
|
);
|
|
861
914
|
expect(result.price_difference).toBe(3.45);
|
|
862
915
|
expect(result.exchanges).toEqual([
|
|
@@ -869,16 +922,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
869
922
|
expect(order?.exchange_price_difference).toBe(3.45);
|
|
870
923
|
});
|
|
871
924
|
|
|
872
|
-
test("the
|
|
925
|
+
test("the stored fields hold the pairing that was priced, not two sorted sets", async () => {
|
|
873
926
|
// `#W5866402` is the case that makes the difference visible: the two
|
|
874
|
-
// requested replacements sort into the OTHER order
|
|
875
|
-
//
|
|
876
|
-
//
|
|
877
|
-
//
|
|
878
|
-
//
|
|
879
|
-
//
|
|
927
|
+
// requested replacements sort into the OTHER order. tau2 stored
|
|
928
|
+
// `sorted(item_ids)` / `sorted(new_item_ids)`, which read as
|
|
929
|
+
// espresso -> sneaker if anything treated them as a pairing — so the result
|
|
930
|
+
// had to carry the real pairing separately, and the stored fields were a
|
|
931
|
+
// trap for the next reader. Nothing is compared against a tau2 end state
|
|
932
|
+
// any more, so both hold what `planItemSwap` actually priced.
|
|
880
933
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
881
|
-
const result =
|
|
934
|
+
const result = await confirmed<ExchangeResult>(
|
|
882
935
|
await exchangeDeliveredOrderItems.execute(
|
|
883
936
|
{
|
|
884
937
|
order_id: "#W5866402",
|
|
@@ -888,6 +941,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
888
941
|
},
|
|
889
942
|
ctx,
|
|
890
943
|
),
|
|
944
|
+
ctx,
|
|
891
945
|
);
|
|
892
946
|
|
|
893
947
|
expect(result.exchanges).toEqual([
|
|
@@ -901,22 +955,24 @@ describe("exchange_delivered_order_items", () => {
|
|
|
901
955
|
5,
|
|
902
956
|
);
|
|
903
957
|
|
|
904
|
-
//
|
|
905
|
-
// really is in the other order.
|
|
958
|
+
// Positionally aligned with each other AND with `result.exchanges`.
|
|
906
959
|
const order = retailSlot.get(ctx).store.orders["#W5866402"];
|
|
907
960
|
expect(order?.exchange_items).toEqual(["6242772310", "9727387530"]);
|
|
908
|
-
expect(order?.exchange_new_items).toEqual(["
|
|
961
|
+
expect(order?.exchange_new_items).toEqual(["7407838442", "2509076505"]);
|
|
909
962
|
});
|
|
910
963
|
|
|
911
964
|
test("does NOT move the gift-card balance — it is a request, not a settlement", async () => {
|
|
912
965
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
913
|
-
await
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
966
|
+
await confirmed(
|
|
967
|
+
await exchangeDeliveredOrderItems.execute(
|
|
968
|
+
{
|
|
969
|
+
order_id: "#W4316152",
|
|
970
|
+
item_ids: ["7292993796"],
|
|
971
|
+
new_item_ids: ["3909406921"],
|
|
972
|
+
payment_method_id: "gift_card_7245904",
|
|
973
|
+
},
|
|
974
|
+
ctx,
|
|
975
|
+
),
|
|
920
976
|
ctx,
|
|
921
977
|
);
|
|
922
978
|
const card =
|
|
@@ -926,13 +982,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
926
982
|
|
|
927
983
|
test("leaves the order's items unchanged until the exchange is fulfilled", async () => {
|
|
928
984
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
929
|
-
await
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
985
|
+
await confirmed(
|
|
986
|
+
await exchangeDeliveredOrderItems.execute(
|
|
987
|
+
{
|
|
988
|
+
order_id: "#W4316152",
|
|
989
|
+
item_ids: ["7292993796"],
|
|
990
|
+
new_item_ids: ["3909406921"],
|
|
991
|
+
payment_method_id: "gift_card_7245904",
|
|
992
|
+
},
|
|
993
|
+
ctx,
|
|
994
|
+
),
|
|
936
995
|
ctx,
|
|
937
996
|
);
|
|
938
997
|
expect(retailSlot.get(ctx).store.orders["#W4316152"]?.items.map((i) => i.item_id)).toEqual([
|
|
@@ -943,7 +1002,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
943
1002
|
|
|
944
1003
|
test("exchanges both copies of a duplicate and doubles the difference", async () => {
|
|
945
1004
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
946
|
-
const result =
|
|
1005
|
+
const result = await confirmed<ExchangeResult>(
|
|
947
1006
|
await exchangeDeliveredOrderItems.execute(
|
|
948
1007
|
{
|
|
949
1008
|
order_id: "#W4316152",
|
|
@@ -953,13 +1012,14 @@ describe("exchange_delivered_order_items", () => {
|
|
|
953
1012
|
},
|
|
954
1013
|
ctx,
|
|
955
1014
|
),
|
|
1015
|
+
ctx,
|
|
956
1016
|
);
|
|
957
1017
|
expect(result.price_difference).toBe(6.9);
|
|
958
1018
|
});
|
|
959
1019
|
|
|
960
1020
|
test("records a negative difference as a refund direction", async () => {
|
|
961
1021
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
962
|
-
const result =
|
|
1022
|
+
const result = await confirmed<ExchangeResult>(
|
|
963
1023
|
await exchangeDeliveredOrderItems.execute(
|
|
964
1024
|
{
|
|
965
1025
|
order_id: "#W5866402",
|
|
@@ -969,6 +1029,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
969
1029
|
},
|
|
970
1030
|
ctx,
|
|
971
1031
|
),
|
|
1032
|
+
ctx,
|
|
972
1033
|
);
|
|
973
1034
|
expect(result.price_difference).toBe(-40.86);
|
|
974
1035
|
});
|
|
@@ -1029,13 +1090,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
1029
1090
|
);
|
|
1030
1091
|
expect(isToolFailure(pending)).toBe(true);
|
|
1031
1092
|
|
|
1032
|
-
await
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1093
|
+
await confirmed(
|
|
1094
|
+
await exchangeDeliveredOrderItems.execute(
|
|
1095
|
+
{
|
|
1096
|
+
order_id: "#W4316152",
|
|
1097
|
+
item_ids: ["7292993796"],
|
|
1098
|
+
new_item_ids: ["3909406921"],
|
|
1099
|
+
payment_method_id: "gift_card_7245904",
|
|
1100
|
+
},
|
|
1101
|
+
ctx,
|
|
1102
|
+
),
|
|
1039
1103
|
ctx,
|
|
1040
1104
|
);
|
|
1041
1105
|
const again = await exchangeDeliveredOrderItems.execute(
|
|
@@ -1052,12 +1116,15 @@ describe("exchange_delivered_order_items", () => {
|
|
|
1052
1116
|
|
|
1053
1117
|
test("a returned order can no longer be exchanged", async () => {
|
|
1054
1118
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
1055
|
-
await
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1119
|
+
await confirmed(
|
|
1120
|
+
await returnDeliveredOrderItems.execute(
|
|
1121
|
+
{
|
|
1122
|
+
order_id: "#W4316152",
|
|
1123
|
+
item_ids: ["7292993796"],
|
|
1124
|
+
payment_method_id: "gift_card_7245904",
|
|
1125
|
+
},
|
|
1126
|
+
ctx,
|
|
1127
|
+
),
|
|
1061
1128
|
ctx,
|
|
1062
1129
|
);
|
|
1063
1130
|
const result = await exchangeDeliveredOrderItems.execute(
|
|
@@ -1090,4 +1157,20 @@ describe("transfer_to_human_agents", () => {
|
|
|
1090
1157
|
expect(isToolFailure(result)).toBe(false);
|
|
1091
1158
|
expect(result.transferred).toBe(true);
|
|
1092
1159
|
});
|
|
1160
|
+
|
|
1161
|
+
test("the handoff is terminal, so the call cannot be worked afterwards", async () => {
|
|
1162
|
+
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
1163
|
+
ok(await transferToHumanAgents.execute({ summary: "wants a human" }, ctx));
|
|
1164
|
+
|
|
1165
|
+
const at = callFlow.position(ctx);
|
|
1166
|
+
expect(at.state).toBe("transferred");
|
|
1167
|
+
expect(at.done).toBe(true);
|
|
1168
|
+
|
|
1169
|
+
// The policy's "say nothing else after that" used to be enforced by
|
|
1170
|
+
// nothing: every tool stayed callable, so a model that kept going kept
|
|
1171
|
+
// acting on a call it had given away.
|
|
1172
|
+
const refused = await getUserDetails.execute({ user_id: "olivia_ito_3591" }, ctx);
|
|
1173
|
+
expect(isToolFailure(refused)).toBe(true);
|
|
1174
|
+
expect(isToolFailure(refused) && refused.error).toContain('"transferred"');
|
|
1175
|
+
});
|
|
1093
1176
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agent } from "@alexkroman1/aai";
|
|
2
2
|
import { storeView } from "./shared.ts";
|
|
3
|
-
import { retailSlot } from "./store.ts";
|
|
3
|
+
import { callFlow, gateFor, record, retailSlot } from "./store.ts";
|
|
4
4
|
|
|
5
5
|
export default agent({
|
|
6
6
|
name: "Retail Support",
|
|
@@ -26,4 +26,38 @@ export default agent({
|
|
|
26
26
|
greeting:
|
|
27
27
|
"Thanks for calling. Before I can look anything up I'll need to find your account — " +
|
|
28
28
|
"what's the email address on it?",
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The activity feed's blocked lines, which no tool wrapper could write.
|
|
32
|
+
*
|
|
33
|
+
* `retailTool` records a line per call from INSIDE the body, so it sees every
|
|
34
|
+
* call the caller's own logic answered — and none that `callFlow` refused,
|
|
35
|
+
* because a gated refusal short-circuits before the body. The sidebar simply
|
|
36
|
+
* stopped showing the most interesting calls the model makes: the ones it
|
|
37
|
+
* tried too early.
|
|
38
|
+
*
|
|
39
|
+
* `tool.called` is emitted by the runtime for every call the model makes, so a
|
|
40
|
+
* hook is the only place that observation is available at all. It is also the
|
|
41
|
+
* shape of hook this template is here to show: it WRITES the session's state
|
|
42
|
+
* and it cannot speak — nothing here changes what the agent says, or stops the
|
|
43
|
+
* refusal still reaching the model. It does neither.
|
|
44
|
+
*
|
|
45
|
+
* The gate is evaluated a second time rather than reported by the first,
|
|
46
|
+
* because the refusal happens later and inside the body. That is one fact read
|
|
47
|
+
* twice from one source (`gateFor`, which `retailTool` fills), not two copies
|
|
48
|
+
* of it — and the only cost of the two evaluations disagreeing is a duplicate
|
|
49
|
+
* or missing sidebar line, never a wrong store.
|
|
50
|
+
*/
|
|
51
|
+
events: {
|
|
52
|
+
"tool.called": (event, ctx) => {
|
|
53
|
+
const when = gateFor(event.toolName);
|
|
54
|
+
// Not a retail tool, or one this call may run: either way the wrapper owns
|
|
55
|
+
// the line, and writing one here would double it.
|
|
56
|
+
if (!when || when.some((state) => callFlow.matches(ctx, state))) return;
|
|
57
|
+
const at = callFlow.position(ctx);
|
|
58
|
+
retailSlot.update(ctx, (state) => {
|
|
59
|
+
record(state, event.toolName, `blocked: call is at ${at.state}`);
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
},
|
|
29
63
|
});
|