@alexkroman1/aai-cli 7.0.0 → 8.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.
- package/README.md +1 -0
- package/dist/{_bundler-B4RqNF5Z.mjs → _bundler-CDuYl5Gb.mjs} +1 -1
- package/dist/{_dev-server-CiKFH9qw.mjs → _dev-server-Dt3TCjhj.mjs} +6 -2
- package/dist/{_init-CE4HKdgV.mjs → _init-BhEfQ6Yi.mjs} +1 -1
- package/dist/_resource-commands.d.ts +23 -0
- package/dist/{_slug-api-nRFaBEPJ.mjs → _slug-api-z-USxPtL.mjs} +14 -10
- package/dist/_slug-api.d.ts +5 -2
- package/dist/{_templates-FI3xPEMj.mjs → _templates-4WcKOjS5.mjs} +4 -5
- package/dist/_templates.d.ts +3 -4
- package/dist/{build-6jfaakgc.mjs → build-BBxf8vom.mjs} +2 -2
- package/dist/cli.mjs +338 -346
- package/dist/{deploy-DvYeD8ia.mjs → deploy-DRC8jWr8.mjs} +1 -1
- package/dist/{dev-B59JIfWO.mjs → dev-QuuOOUtv.mjs} +1 -1
- package/dist/eval-DfQ8cm5p.mjs +56 -0
- package/dist/eval.d.ts +43 -0
- package/dist/{init-CpJVjEg0.mjs → init-CUo87gcI.mjs} +2 -2
- package/dist/{logs-9q-psa4q.mjs → logs-CQ2T46dr.mjs} +1 -1
- package/dist/scaffold/CLAUDE.md +409 -68
- package/dist/scaffold/package.json +5 -4
- package/dist/scaffold/server.mjs +10 -3
- package/dist/scaffold/tsconfig.json +0 -1
- package/dist/{secret-CVvSLIDV.mjs → secret-Ct34kdgU.mjs} +1 -1
- package/dist/{storage-BvUrnvM3.mjs → storage-i_79ZwAR.mjs} +1 -1
- package/dist/{studio--MUV0cid.mjs → studio-CzTzPqe1.mjs} +1 -1
- 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/code-interpreter/agent.eval.test.ts +165 -0
- package/dist/templates/dispatch-center/agent.eval.test.ts +349 -0
- package/dist/templates/embedded-assets/agent.eval.test.ts +138 -0
- package/dist/templates/health-assistant/agent.eval.test.ts +167 -0
- package/dist/templates/infocom-adventure/agent.eval.test.ts +158 -0
- package/dist/templates/infocom-adventure/agent.test.ts +49 -18
- package/dist/templates/infocom-adventure/agent.ts +21 -0
- 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/math-buddy/agent.eval.test.ts +146 -0
- package/dist/templates/night-owl/agent.eval.test.ts +272 -0
- 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 +15 -0
- 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/podcast-digest/agent.eval.test.ts +411 -0
- package/dist/templates/podcast-digest/agent.test.ts +30 -59
- package/dist/templates/podcast-digest/agent.ts +1 -1
- package/dist/templates/podcast-digest/workflows/digest.ts +52 -19
- package/dist/templates/podcast-digest/workflows/slack.ts +48 -169
- package/dist/templates/recap-workflow/agent.eval.test.ts +366 -0
- package/dist/templates/redline/agent.eval.test.ts +209 -0
- package/dist/templates/research-workflow/agent.eval.test.ts +329 -0
- 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 +171 -91
- package/dist/templates/retail/agent.ts +35 -1
- package/dist/templates/retail/cancel.ts +116 -0
- package/dist/templates/retail/client.tsx +39 -0
- 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 +119 -8
- 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 +6 -4
- package/dist/templates/retail/store.ts +113 -57
- package/dist/templates/retail/swap.test.ts +4 -4
- package/dist/templates/retail/swap.ts +248 -15
- package/dist/templates/retail/system-prompt.md +62 -30
- package/dist/templates/retail/tools/cancel_change.ts +33 -0
- package/dist/templates/retail/tools/cancel_pending_order.ts +15 -59
- package/dist/templates/retail/tools/confirm_change.ts +42 -0
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +21 -66
- package/dist/templates/retail/tools/modify_pending_order_address.ts +12 -24
- package/dist/templates/retail/tools/modify_pending_order_items.ts +22 -60
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +15 -77
- package/dist/templates/retail/tools/modify_user_address.ts +12 -15
- package/dist/templates/retail/tools/return_delivered_order_items.ts +16 -71
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +4 -2
- package/dist/templates/simple/agent.eval.test.ts +54 -0
- package/dist/templates/solo-rpg/agent.eval.test.ts +320 -0
- package/dist/templates/spoken-summary/agent.eval.test.ts +342 -0
- package/dist/templates/support-line/agent.eval.test.ts +273 -0
- package/dist/templates/support-line/system-prompt.md +2 -1
- package/dist/templates/support-line/tools/answer_question.ts +56 -7
- package/dist/templates/transcription-workflow/agent.eval.test.ts +234 -0
- package/dist/templates/travel-concierge/agent.eval.test.ts +323 -0
- package/dist/templates/travel-concierge/agent.test.ts +110 -10
- package/dist/templates/travel-concierge/agent.ts +18 -9
- package/dist/templates/travel-concierge/shared.ts +110 -9
- package/dist/templates/travel-concierge/system-prompt.md +7 -1
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +6 -3
- package/dist/templates/travel-concierge/tools/book_excursion.ts +6 -3
- package/dist/templates/travel-concierge/tools/book_hotel.ts +6 -3
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +6 -3
- 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 +6 -3
- 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-COxnqstQ.mjs → worker-bundler-CCVEDjm1.mjs} +45 -25
- package/dist/worker-bundler.mjs +1 -1
- package/dist/{workflow-D2AQf2Pl.mjs → workflow-BflATMmU.mjs} +19 -11
- package/package.json +5 -5
- package/dist/eject-C9WJyyr2.mjs +0 -116
- package/dist/eject.d.ts +0 -60
- package/dist/templates/infocom-adventure/tools/game_state_history.ts +0 -14
|
@@ -3,9 +3,11 @@ import { isToolFailure } from "@alexkroman1/aai";
|
|
|
3
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
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";
|
|
@@ -34,6 +36,26 @@ async function authedCtx(email: string): Promise<ToolContext> {
|
|
|
34
36
|
return ctx;
|
|
35
37
|
}
|
|
36
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
|
+
|
|
37
59
|
describe("authentication", () => {
|
|
38
60
|
test("find_user_id_by_email is case-insensitive and authenticates the session", async () => {
|
|
39
61
|
const ctx = makeCtx();
|
|
@@ -43,7 +65,8 @@ describe("authentication", () => {
|
|
|
43
65
|
expect(result.user_id).toBe("olivia_ito_3591");
|
|
44
66
|
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
45
67
|
// Two facts, two homes: the store latches WHO, the flow holds WHETHER.
|
|
46
|
-
|
|
68
|
+
// `serving` is a parent now, so an identified call reports its child.
|
|
69
|
+
expect(callFlow.position(ctx).state).toBe("serving.helping");
|
|
47
70
|
});
|
|
48
71
|
|
|
49
72
|
test("an unknown email is refused and leaves the session unauthenticated", async () => {
|
|
@@ -107,7 +130,7 @@ describe("authentication", () => {
|
|
|
107
130
|
expect(retailSlot.get(ctx).authenticatedUserId).toBe("olivia_ito_3591");
|
|
108
131
|
// The tool is legal in `serving` — the refusal comes from `authenticateAs`,
|
|
109
132
|
// not from the gate — so the call stays exactly where it was.
|
|
110
|
-
expect(callFlow.position(ctx).state).toBe("serving");
|
|
133
|
+
expect(callFlow.position(ctx).state).toBe("serving.helping");
|
|
111
134
|
});
|
|
112
135
|
|
|
113
136
|
test("switching via name + zip is refused too — both doors, one lock", async () => {
|
|
@@ -224,11 +247,12 @@ interface CancelOrderResult {
|
|
|
224
247
|
describe("cancel_pending_order", () => {
|
|
225
248
|
test("cancels a pending order and credits a gift card immediately", async () => {
|
|
226
249
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
227
|
-
const result =
|
|
250
|
+
const result = await confirmed<CancelOrderResult>(
|
|
228
251
|
await cancelPendingOrder.execute(
|
|
229
252
|
{ order_id: "#W9300146", reason: "ordered by mistake" },
|
|
230
253
|
ctx,
|
|
231
254
|
),
|
|
255
|
+
ctx,
|
|
232
256
|
);
|
|
233
257
|
expect(result.status).toBe("cancelled");
|
|
234
258
|
expect(result.refund_immediate).toBe(true);
|
|
@@ -243,7 +267,10 @@ describe("cancel_pending_order", () => {
|
|
|
243
267
|
|
|
244
268
|
test("appends a matching refund to the payment history", async () => {
|
|
245
269
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
246
|
-
await
|
|
270
|
+
await confirmed(
|
|
271
|
+
await cancelPendingOrder.execute({ order_id: "#W9300146", reason: "no longer needed" }, ctx),
|
|
272
|
+
ctx,
|
|
273
|
+
);
|
|
247
274
|
const history = retailSlot.get(ctx).store.orders["#W9300146"]?.payment_history ?? [];
|
|
248
275
|
expect(history).toHaveLength(2);
|
|
249
276
|
expect(history[1]).toMatchObject({
|
|
@@ -255,8 +282,9 @@ describe("cancel_pending_order", () => {
|
|
|
255
282
|
|
|
256
283
|
test("a non-gift-card refund takes 5-7 days and moves no balance", async () => {
|
|
257
284
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
258
|
-
const result =
|
|
285
|
+
const result = await confirmed<CancelOrderResult>(
|
|
259
286
|
await cancelPendingOrder.execute({ order_id: "#W5442520", reason: "no longer needed" }, ctx),
|
|
287
|
+
ctx,
|
|
260
288
|
);
|
|
261
289
|
expect(result.refund_immediate).toBe(false);
|
|
262
290
|
expect(result.message).toContain("5 to 7");
|
|
@@ -298,7 +326,10 @@ describe("cancel_pending_order", () => {
|
|
|
298
326
|
|
|
299
327
|
test("refuses cancelling twice", async () => {
|
|
300
328
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
301
|
-
await
|
|
329
|
+
await confirmed(
|
|
330
|
+
await cancelPendingOrder.execute({ order_id: "#W9300146", reason: "no longer needed" }, ctx),
|
|
331
|
+
ctx,
|
|
332
|
+
);
|
|
302
333
|
const second = await cancelPendingOrder.execute(
|
|
303
334
|
{ order_id: "#W9300146", reason: "no longer needed" },
|
|
304
335
|
ctx,
|
|
@@ -311,11 +342,12 @@ describe("cancel_pending_order", () => {
|
|
|
311
342
|
|
|
312
343
|
test("resolves spoken shorthand to the single pending order", async () => {
|
|
313
344
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
314
|
-
const result =
|
|
345
|
+
const result = await confirmed<CancelOrderResult>(
|
|
315
346
|
await cancelPendingOrder.execute(
|
|
316
347
|
{ order_id: "my pending order", reason: "ordered by mistake" },
|
|
317
348
|
ctx,
|
|
318
349
|
),
|
|
350
|
+
ctx,
|
|
319
351
|
);
|
|
320
352
|
expect(result.order_id).toBe("#W9300146");
|
|
321
353
|
});
|
|
@@ -351,8 +383,9 @@ interface AddressToolResult {
|
|
|
351
383
|
describe("modify_pending_order_address", () => {
|
|
352
384
|
test("rewrites a pending order's shipping address", async () => {
|
|
353
385
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
354
|
-
const result =
|
|
386
|
+
const result = await confirmed<AddressToolResult>(
|
|
355
387
|
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
|
|
388
|
+
ctx,
|
|
356
389
|
);
|
|
357
390
|
expect(result.address).toEqual(NEW_ADDRESS);
|
|
358
391
|
expect(retailSlot.get(ctx).store.orders["#W9300146"]?.address).toEqual(NEW_ADDRESS);
|
|
@@ -360,7 +393,10 @@ describe("modify_pending_order_address", () => {
|
|
|
360
393
|
|
|
361
394
|
test("leaves the customer's default address untouched", async () => {
|
|
362
395
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
363
|
-
await
|
|
396
|
+
await confirmed(
|
|
397
|
+
await modifyPendingOrderAddress.execute({ order_id: "#W9300146", ...NEW_ADDRESS }, ctx),
|
|
398
|
+
ctx,
|
|
399
|
+
);
|
|
364
400
|
expect(retailSlot.get(ctx).store.users.aarav_anderson_8794?.address.zip).toBe("19031");
|
|
365
401
|
});
|
|
366
402
|
|
|
@@ -400,14 +436,20 @@ describe("modify_pending_order_address", () => {
|
|
|
400
436
|
describe("modify_user_address", () => {
|
|
401
437
|
test("rewrites the customer's default address", async () => {
|
|
402
438
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
403
|
-
|
|
439
|
+
await confirmed(
|
|
440
|
+
await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx),
|
|
441
|
+
ctx,
|
|
442
|
+
);
|
|
404
443
|
expect(retailSlot.get(ctx).store.users.emma_smith_8564?.address).toEqual(NEW_ADDRESS);
|
|
405
444
|
});
|
|
406
445
|
|
|
407
446
|
test("leaves existing orders' addresses untouched", async () => {
|
|
408
447
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
409
448
|
const before = structuredClone(retailSlot.get(ctx).store.orders["#W2417020"]?.address);
|
|
410
|
-
await
|
|
449
|
+
await confirmed(
|
|
450
|
+
await modifyUserAddress.execute({ user_id: "emma_smith_8564", ...NEW_ADDRESS }, ctx),
|
|
451
|
+
ctx,
|
|
452
|
+
);
|
|
411
453
|
expect(retailSlot.get(ctx).store.orders["#W2417020"]?.address).toEqual(before);
|
|
412
454
|
});
|
|
413
455
|
|
|
@@ -441,7 +483,7 @@ interface ModifyItemsResult {
|
|
|
441
483
|
describe("modify_pending_order_items", () => {
|
|
442
484
|
test("swaps an item, charges the difference to a gift card, and goes terminal", async () => {
|
|
443
485
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
444
|
-
const result =
|
|
486
|
+
const result = await confirmed<ModifyItemsResult>(
|
|
445
487
|
await modifyPendingOrderItems.execute(
|
|
446
488
|
{
|
|
447
489
|
order_id: "#W2417020",
|
|
@@ -451,6 +493,7 @@ describe("modify_pending_order_items", () => {
|
|
|
451
493
|
},
|
|
452
494
|
ctx,
|
|
453
495
|
),
|
|
496
|
+
ctx,
|
|
454
497
|
);
|
|
455
498
|
expect(result.price_difference).toBe(-382.03);
|
|
456
499
|
expect(result.status).toBe("pending (item modified)");
|
|
@@ -466,13 +509,16 @@ describe("modify_pending_order_items", () => {
|
|
|
466
509
|
|
|
467
510
|
test("records the difference as a refund entry when the new item is cheaper", async () => {
|
|
468
511
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
469
|
-
await
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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
|
+
),
|
|
476
522
|
ctx,
|
|
477
523
|
);
|
|
478
524
|
const history = retailSlot.get(ctx).store.orders["#W2417020"]?.payment_history ?? [];
|
|
@@ -485,13 +531,16 @@ describe("modify_pending_order_items", () => {
|
|
|
485
531
|
|
|
486
532
|
test("refuses a second modification — the status is terminal", async () => {
|
|
487
533
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
488
|
-
await
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
+
),
|
|
495
544
|
ctx,
|
|
496
545
|
);
|
|
497
546
|
const second = await modifyPendingOrderItems.execute(
|
|
@@ -508,13 +557,16 @@ describe("modify_pending_order_items", () => {
|
|
|
508
557
|
|
|
509
558
|
test("a modified order can no longer be cancelled", async () => {
|
|
510
559
|
const ctx = await authedCtx("emma.smith3991@example.com");
|
|
511
|
-
await
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
+
),
|
|
518
570
|
ctx,
|
|
519
571
|
);
|
|
520
572
|
const cancelled = await cancelPendingOrder.execute(
|
|
@@ -552,7 +604,7 @@ describe("modify_pending_order_items", () => {
|
|
|
552
604
|
|
|
553
605
|
test("the same upgrade succeeds on a payment method with no balance to run out of", async () => {
|
|
554
606
|
const ctx = await authedCtx("anya.garcia2061@example.com");
|
|
555
|
-
const result =
|
|
607
|
+
const result = await confirmed<ModifyItemsResult>(
|
|
556
608
|
await modifyPendingOrderItems.execute(
|
|
557
609
|
{
|
|
558
610
|
order_id: "#W6436609",
|
|
@@ -562,6 +614,7 @@ describe("modify_pending_order_items", () => {
|
|
|
562
614
|
},
|
|
563
615
|
ctx,
|
|
564
616
|
),
|
|
617
|
+
ctx,
|
|
565
618
|
);
|
|
566
619
|
expect(result.price_difference).toBe(167.37);
|
|
567
620
|
const history = retailSlot.get(ctx).store.orders["#W6436609"]?.payment_history ?? [];
|
|
@@ -615,11 +668,12 @@ describe("modify_pending_order_payment", () => {
|
|
|
615
668
|
test("moves an order to a different method, refunding the old one", async () => {
|
|
616
669
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
617
670
|
// #W5442520 is $663.85 on credit_card_9753331. Pay by PayPal instead.
|
|
618
|
-
const result =
|
|
671
|
+
const result = await confirmed<ModifyPaymentResult>(
|
|
619
672
|
await modifyPendingOrderPayment.execute(
|
|
620
673
|
{ order_id: "#W5442520", payment_method_id: "paypal_8049766" },
|
|
621
674
|
ctx,
|
|
622
675
|
),
|
|
676
|
+
ctx,
|
|
623
677
|
);
|
|
624
678
|
expect(result.status).toBe("pending");
|
|
625
679
|
|
|
@@ -640,11 +694,13 @@ describe("modify_pending_order_payment", () => {
|
|
|
640
694
|
test("refunds a gift card that was the original method", async () => {
|
|
641
695
|
const ctx = await authedCtx("aarav.gonzalez9269@example.com");
|
|
642
696
|
// #W9160732 is $1011.54 on gift_card_5979071 ($96.00). Move to PayPal.
|
|
643
|
-
|
|
644
|
-
|
|
697
|
+
await confirmed(
|
|
698
|
+
await modifyPendingOrderPayment.execute(
|
|
699
|
+
{ order_id: "#W9160732", payment_method_id: "paypal_6121064" },
|
|
700
|
+
ctx,
|
|
701
|
+
),
|
|
645
702
|
ctx,
|
|
646
703
|
);
|
|
647
|
-
expect(isToolFailure(result)).toBe(false);
|
|
648
704
|
const card =
|
|
649
705
|
retailSlot.get(ctx).store.users.aarav_gonzalez_5113?.payment_methods.gift_card_5979071;
|
|
650
706
|
expect(card?.source === "gift_card" && card.balance).toBe(1107.54);
|
|
@@ -731,7 +787,7 @@ interface ExchangeResult {
|
|
|
731
787
|
describe("return_delivered_order_items", () => {
|
|
732
788
|
test("requests a return refunded to the original method", async () => {
|
|
733
789
|
const ctx = await authedCtx("harper.brown3965@example.com");
|
|
734
|
-
const result =
|
|
790
|
+
const result = await confirmed<ReturnResult>(
|
|
735
791
|
await returnDeliveredOrderItems.execute(
|
|
736
792
|
{
|
|
737
793
|
order_id: "#W1840144",
|
|
@@ -740,6 +796,7 @@ describe("return_delivered_order_items", () => {
|
|
|
740
796
|
},
|
|
741
797
|
ctx,
|
|
742
798
|
),
|
|
799
|
+
ctx,
|
|
743
800
|
);
|
|
744
801
|
expect(result.status).toBe("return requested");
|
|
745
802
|
const order = retailSlot.get(ctx).store.orders["#W1840144"];
|
|
@@ -776,20 +833,25 @@ describe("return_delivered_order_items", () => {
|
|
|
776
833
|
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.status).toBe("delivered");
|
|
777
834
|
});
|
|
778
835
|
|
|
779
|
-
test("returns both copies of a duplicate item
|
|
836
|
+
test("returns both copies of a duplicate item, in the order they were named", async () => {
|
|
780
837
|
const ctx = await authedCtx("harper.brown3965@example.com");
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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
|
+
),
|
|
787
847
|
ctx,
|
|
788
848
|
);
|
|
789
|
-
|
|
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.
|
|
790
852
|
expect(retailSlot.get(ctx).store.orders["#W1840144"]?.return_items).toEqual([
|
|
791
|
-
"6534134392",
|
|
792
853
|
"8590708195",
|
|
854
|
+
"6534134392",
|
|
793
855
|
"8590708195",
|
|
794
856
|
]);
|
|
795
857
|
});
|
|
@@ -819,8 +881,11 @@ describe("return_delivered_order_items", () => {
|
|
|
819
881
|
);
|
|
820
882
|
expect(isToolFailure(pending)).toBe(true);
|
|
821
883
|
|
|
822
|
-
await
|
|
823
|
-
|
|
884
|
+
await confirmed(
|
|
885
|
+
await returnDeliveredOrderItems.execute(
|
|
886
|
+
{ order_id: "#W1840144", item_ids: ["8590708195"], payment_method_id: "paypal_2306935" },
|
|
887
|
+
ctx,
|
|
888
|
+
),
|
|
824
889
|
ctx,
|
|
825
890
|
);
|
|
826
891
|
const again = await returnDeliveredOrderItems.execute(
|
|
@@ -834,7 +899,7 @@ describe("return_delivered_order_items", () => {
|
|
|
834
899
|
describe("exchange_delivered_order_items", () => {
|
|
835
900
|
test("records an exchange with a positive price difference", async () => {
|
|
836
901
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
837
|
-
const result =
|
|
902
|
+
const result = await confirmed<ExchangeResult>(
|
|
838
903
|
await exchangeDeliveredOrderItems.execute(
|
|
839
904
|
{
|
|
840
905
|
order_id: "#W4316152",
|
|
@@ -844,6 +909,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
844
909
|
},
|
|
845
910
|
ctx,
|
|
846
911
|
),
|
|
912
|
+
ctx,
|
|
847
913
|
);
|
|
848
914
|
expect(result.price_difference).toBe(3.45);
|
|
849
915
|
expect(result.exchanges).toEqual([
|
|
@@ -856,16 +922,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
856
922
|
expect(order?.exchange_price_difference).toBe(3.45);
|
|
857
923
|
});
|
|
858
924
|
|
|
859
|
-
test("the
|
|
925
|
+
test("the stored fields hold the pairing that was priced, not two sorted sets", async () => {
|
|
860
926
|
// `#W5866402` is the case that makes the difference visible: the two
|
|
861
|
-
// requested replacements sort into the OTHER order
|
|
862
|
-
//
|
|
863
|
-
//
|
|
864
|
-
//
|
|
865
|
-
//
|
|
866
|
-
//
|
|
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.
|
|
867
933
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
868
|
-
const result =
|
|
934
|
+
const result = await confirmed<ExchangeResult>(
|
|
869
935
|
await exchangeDeliveredOrderItems.execute(
|
|
870
936
|
{
|
|
871
937
|
order_id: "#W5866402",
|
|
@@ -875,6 +941,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
875
941
|
},
|
|
876
942
|
ctx,
|
|
877
943
|
),
|
|
944
|
+
ctx,
|
|
878
945
|
);
|
|
879
946
|
|
|
880
947
|
expect(result.exchanges).toEqual([
|
|
@@ -888,22 +955,24 @@ describe("exchange_delivered_order_items", () => {
|
|
|
888
955
|
5,
|
|
889
956
|
);
|
|
890
957
|
|
|
891
|
-
//
|
|
892
|
-
// really is in the other order.
|
|
958
|
+
// Positionally aligned with each other AND with `result.exchanges`.
|
|
893
959
|
const order = retailSlot.get(ctx).store.orders["#W5866402"];
|
|
894
960
|
expect(order?.exchange_items).toEqual(["6242772310", "9727387530"]);
|
|
895
|
-
expect(order?.exchange_new_items).toEqual(["
|
|
961
|
+
expect(order?.exchange_new_items).toEqual(["7407838442", "2509076505"]);
|
|
896
962
|
});
|
|
897
963
|
|
|
898
964
|
test("does NOT move the gift-card balance — it is a request, not a settlement", async () => {
|
|
899
965
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
900
|
-
await
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
+
),
|
|
907
976
|
ctx,
|
|
908
977
|
);
|
|
909
978
|
const card =
|
|
@@ -913,13 +982,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
913
982
|
|
|
914
983
|
test("leaves the order's items unchanged until the exchange is fulfilled", async () => {
|
|
915
984
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
916
|
-
await
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
+
),
|
|
923
995
|
ctx,
|
|
924
996
|
);
|
|
925
997
|
expect(retailSlot.get(ctx).store.orders["#W4316152"]?.items.map((i) => i.item_id)).toEqual([
|
|
@@ -930,7 +1002,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
930
1002
|
|
|
931
1003
|
test("exchanges both copies of a duplicate and doubles the difference", async () => {
|
|
932
1004
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
933
|
-
const result =
|
|
1005
|
+
const result = await confirmed<ExchangeResult>(
|
|
934
1006
|
await exchangeDeliveredOrderItems.execute(
|
|
935
1007
|
{
|
|
936
1008
|
order_id: "#W4316152",
|
|
@@ -940,13 +1012,14 @@ describe("exchange_delivered_order_items", () => {
|
|
|
940
1012
|
},
|
|
941
1013
|
ctx,
|
|
942
1014
|
),
|
|
1015
|
+
ctx,
|
|
943
1016
|
);
|
|
944
1017
|
expect(result.price_difference).toBe(6.9);
|
|
945
1018
|
});
|
|
946
1019
|
|
|
947
1020
|
test("records a negative difference as a refund direction", async () => {
|
|
948
1021
|
const ctx = await authedCtx("olivia.ito5204@example.com");
|
|
949
|
-
const result =
|
|
1022
|
+
const result = await confirmed<ExchangeResult>(
|
|
950
1023
|
await exchangeDeliveredOrderItems.execute(
|
|
951
1024
|
{
|
|
952
1025
|
order_id: "#W5866402",
|
|
@@ -956,6 +1029,7 @@ describe("exchange_delivered_order_items", () => {
|
|
|
956
1029
|
},
|
|
957
1030
|
ctx,
|
|
958
1031
|
),
|
|
1032
|
+
ctx,
|
|
959
1033
|
);
|
|
960
1034
|
expect(result.price_difference).toBe(-40.86);
|
|
961
1035
|
});
|
|
@@ -1016,13 +1090,16 @@ describe("exchange_delivered_order_items", () => {
|
|
|
1016
1090
|
);
|
|
1017
1091
|
expect(isToolFailure(pending)).toBe(true);
|
|
1018
1092
|
|
|
1019
|
-
await
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
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
|
+
),
|
|
1026
1103
|
ctx,
|
|
1027
1104
|
);
|
|
1028
1105
|
const again = await exchangeDeliveredOrderItems.execute(
|
|
@@ -1039,12 +1116,15 @@ describe("exchange_delivered_order_items", () => {
|
|
|
1039
1116
|
|
|
1040
1117
|
test("a returned order can no longer be exchanged", async () => {
|
|
1041
1118
|
const ctx = await authedCtx("aarav.anderson9752@example.com");
|
|
1042
|
-
await
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
+
),
|
|
1048
1128
|
ctx,
|
|
1049
1129
|
);
|
|
1050
1130
|
const result = await exchangeDeliveredOrderItems.execute(
|
|
@@ -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
|
});
|