@alexkroman1/aai-cli 6.11.0 → 7.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/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
- package/dist/{_bundler-C2BDwU7r.mjs → _bundler-B4RqNF5Z.mjs} +2 -2
- package/dist/_dev-env.d.ts +22 -2
- package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-CiKFH9qw.mjs} +38 -47
- package/dist/_dev-server.d.ts +0 -17
- package/dist/{_init-C57bWAQ8.mjs → _init-CE4HKdgV.mjs} +3 -2
- package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
- package/dist/_preflight.d.ts +1 -1
- package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-nRFaBEPJ.mjs} +2 -2
- package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
- package/dist/{_templates-Dxb_P2Wz.mjs → _templates-FI3xPEMj.mjs} +1 -1
- package/dist/_workflow-scan.d.ts +108 -0
- package/dist/{build-Dfza2pRI.mjs → build-6jfaakgc.mjs} +5 -2
- package/dist/cli.mjs +30 -25
- 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-DvYeD8ia.mjs} +5 -5
- package/dist/{dev-Uq5ujP8a.mjs → dev-B59JIfWO.mjs} +1 -1
- package/dist/{eject-31gjtaHF.mjs → eject-C9WJyyr2.mjs} +1 -1
- package/dist/{init-l6xfU4xX.mjs → init-CpJVjEg0.mjs} +3 -3
- package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
- package/dist/{logs-BWVGSjLR.mjs → logs-9q-psa4q.mjs} +2 -2
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +40 -19
- package/dist/scaffold/package.json +5 -3
- package/dist/scaffold/server.mjs +1 -1
- package/dist/{secret-BuMuFR4B.mjs → secret-CVvSLIDV.mjs} +2 -2
- package/dist/{storage-DzRZ-eCw.mjs → storage-BvUrnvM3.mjs} +2 -2
- package/dist/{studio-BCNUpDgP.mjs → studio--MUV0cid.mjs} +4 -4
- 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/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.test.ts +17 -14
- package/dist/templates/health-assistant/agent.test.ts +22 -4
- package/dist/templates/infocom-adventure/agent.test.ts +4 -6
- package/dist/templates/infocom-adventure/client.tsx +246 -164
- 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/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/pizza-ordering/agent.test.ts +27 -27
- package/dist/templates/pizza-ordering/client.tsx +9 -26
- 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.test.ts +746 -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 +411 -0
- package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
- package/dist/templates/podcast-digest/workflows/slack.ts +209 -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.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.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/agent.test.ts +26 -23
- package/dist/templates/retail/client.tsx +226 -117
- package/dist/templates/retail/registry.test.ts +38 -6
- package/dist/templates/retail/store.test.ts +82 -15
- package/dist/templates/retail/store.ts +174 -47
- package/dist/templates/retail/system-prompt.md +11 -2
- package/dist/templates/retail/tools/cancel_pending_order.ts +2 -2
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +2 -2
- 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 +2 -2
- package/dist/templates/retail/tools/modify_pending_order_items.ts +2 -3
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +2 -2
- package/dist/templates/retail/tools/modify_user_address.ts +2 -2
- package/dist/templates/retail/tools/return_delivered_order_items.ts +2 -2
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +8 -4
- 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.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.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/tools/answer_question.ts +17 -4
- 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.test.ts +64 -33
- 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 +70 -3
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +2 -2
- package/dist/templates/travel-concierge/tools/book_excursion.ts +2 -2
- package/dist/templates/travel-concierge/tools/book_hotel.ts +2 -2
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +2 -2
- package/dist/templates/travel-concierge/tools/update_ticket.ts +2 -2
- package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-COxnqstQ.mjs} +221 -3
- package/dist/worker-bundler.mjs +1 -1
- package/dist/{workflow-CFpxOFfQ.mjs → workflow-D2AQf2Pl.mjs} +28 -1
- package/dist/workflow-bundler.d.ts +6 -1
- package/dist/workflow.d.ts +1 -1
- package/package.json +5 -4
- package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
- 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
|
@@ -3,15 +3,22 @@ import { z } from "zod";
|
|
|
3
3
|
import type { Resource } from "../shared.ts";
|
|
4
4
|
import {
|
|
5
5
|
assertNotResolved,
|
|
6
|
+
callFlow,
|
|
6
7
|
dispatchSlot,
|
|
7
8
|
findIncident,
|
|
8
9
|
logEvent,
|
|
9
10
|
recommendResources,
|
|
10
11
|
} from "../shared.ts";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Gated on `working`, and legal from `triaging` as well as `dispatching` — the
|
|
15
|
+
* prompt's "critical incidents get immediate dispatch, triage can happen
|
|
16
|
+
* simultaneously" is why the gate is the parent state rather than one child.
|
|
17
|
+
*/
|
|
18
|
+
export default callFlow.tool({
|
|
13
19
|
description:
|
|
14
20
|
"Dispatch units to an incident. Can auto-dispatch recommended resources or manually specify callsigns.",
|
|
21
|
+
when: "working",
|
|
15
22
|
inputSchema: z.object({
|
|
16
23
|
incidentId: z.string().max(20).describe("The incident ID"),
|
|
17
24
|
callsigns: z
|
|
@@ -30,66 +37,76 @@ export default dispatchSlot.updateTool({
|
|
|
30
37
|
.describe("Dispatch priority — affects simulated ETA")
|
|
31
38
|
.optional(),
|
|
32
39
|
}),
|
|
33
|
-
execute(args,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
execute: (args, ctx) =>
|
|
41
|
+
dispatchSlot.update(ctx, (state) => {
|
|
42
|
+
const inc = findIncident(state, args.incidentId);
|
|
43
|
+
if (isToolFailure(inc)) return inc;
|
|
44
|
+
const blocked = assertNotResolved(inc, "dispatch resources");
|
|
45
|
+
if (blocked) return blocked;
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const dispatched: { callsign: string; type: string; eta: number }[] = [];
|
|
48
|
+
const failed: { callsign: string; reason: string }[] = [];
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
// The literal "auto" callsign means the same thing as autoDispatch.
|
|
51
|
+
const wantsAuto =
|
|
52
|
+
args.autoDispatch || args.callsigns?.some((cs) => cs.toLowerCase() === "auto");
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
let resourcesToDispatch: Resource[] = [];
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
if (wantsAuto) {
|
|
57
|
+
resourcesToDispatch = recommendResources(inc.type, inc.severity, state);
|
|
58
|
+
} else if (args.callsigns) {
|
|
59
|
+
for (const cs of args.callsigns) {
|
|
60
|
+
const r = state.resources.find((r) => r.callsign.toLowerCase() === cs.toLowerCase());
|
|
61
|
+
if (!r) {
|
|
62
|
+
failed.push({ callsign: cs, reason: "Not found" });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (r.status !== "available") {
|
|
66
|
+
failed.push({ callsign: cs, reason: `Currently ${r.status}` });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
resourcesToDispatch.push(r);
|
|
56
70
|
}
|
|
57
|
-
if (r.status !== "available") {
|
|
58
|
-
failed.push({ callsign: cs, reason: `Currently ${r.status}` });
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
resourcesToDispatch.push(r);
|
|
62
71
|
}
|
|
63
|
-
}
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
const etaBase = args.priority === "emergency" ? 3 : args.priority === "priority" ? 6 : 10;
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
for (const r of resourcesToDispatch) {
|
|
76
|
+
const eta = etaBase + Math.floor(Math.random() * 5);
|
|
77
|
+
r.status = "dispatched";
|
|
78
|
+
r.assignedIncident = args.incidentId;
|
|
79
|
+
r.eta = eta;
|
|
80
|
+
inc.assignedResources.push(r.id);
|
|
81
|
+
dispatched.push({ callsign: r.callsign, type: r.type, eta });
|
|
82
|
+
logEvent(inc, `Dispatched ${r.callsign} — ETA ${eta} min`);
|
|
83
|
+
}
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
if (dispatched.length > 0) {
|
|
86
|
+
inc.status = "dispatched";
|
|
87
|
+
}
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
const availableCount = state.resources.filter((r) => r.status === "available").length;
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
return {
|
|
92
|
+
incidentId: args.incidentId,
|
|
93
|
+
dispatched,
|
|
94
|
+
failed: failed.length > 0 ? failed : undefined,
|
|
95
|
+
totalAssignedToIncident: inc.assignedResources.length,
|
|
96
|
+
remainingAvailableResources: availableCount,
|
|
97
|
+
capacityWarning:
|
|
98
|
+
availableCount <= 3
|
|
99
|
+
? "WARNING: Resource capacity critically low. Consider mutual aid."
|
|
100
|
+
: undefined,
|
|
101
|
+
};
|
|
102
|
+
}),
|
|
103
|
+
// Only when units actually rolled: a dispatch whose every requested callsign
|
|
104
|
+
// was busy answers `dispatched: []`, which is not a refusal but is not
|
|
105
|
+
// progress either, so the call stays where it is.
|
|
106
|
+
//
|
|
107
|
+
// `result` is the SUCCESS type — `sendFrom` takes `Exclude<R, ToolFailure>`,
|
|
108
|
+
// so the `"dispatched" in result` guard that used to stand in for the failure
|
|
109
|
+
// arm leaking into `R` is a plain property read.
|
|
110
|
+
sendFrom: (result) =>
|
|
111
|
+
result.dispatched.length > 0 ? { type: "DISPATCHED" as const } : undefined,
|
|
95
112
|
});
|
|
@@ -1,59 +1,63 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { dispatchSlot, logEvent, RESOURCE_STATUSES } from "../shared.ts";
|
|
2
|
+
import { callFlow, dispatchSlot, logEvent, RESOURCE_STATUSES } from "../shared.ts";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/** Gated on `working`: a unit radios in about an incident, so there has to be
|
|
5
|
+
* one. Sends nothing — a unit changing status does not move the call. */
|
|
6
|
+
export default callFlow.tool({
|
|
5
7
|
description: "Update a resource unit's status when it radios in.",
|
|
8
|
+
when: "working",
|
|
6
9
|
inputSchema: z.object({
|
|
7
10
|
callsign: z.string().max(50).describe("The resource callsign"),
|
|
8
11
|
status: z.enum(RESOURCE_STATUSES).describe("New status"),
|
|
9
12
|
notes: z.string().max(1000).describe("Status notes").optional(),
|
|
10
13
|
}),
|
|
11
|
-
execute(args,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const previousStatus = resource.status;
|
|
20
|
-
|
|
21
|
-
// Log to the incident timeline BEFORE unassigning, so the
|
|
22
|
-
// return-to-available transition is recorded too.
|
|
23
|
-
if (resource.assignedIncident) {
|
|
24
|
-
const inc = state.incidents[resource.assignedIncident];
|
|
25
|
-
if (inc) {
|
|
26
|
-
logEvent(
|
|
27
|
-
inc,
|
|
28
|
-
`${resource.callsign}: ${previousStatus} → ${args.status}${args.notes ? ` (${args.notes})` : ""}`,
|
|
29
|
-
);
|
|
14
|
+
execute: (args, ctx) =>
|
|
15
|
+
dispatchSlot.update(ctx, (state) => {
|
|
16
|
+
const resource = state.resources.find(
|
|
17
|
+
(r) => r.callsign.toLowerCase() === args.callsign.toLowerCase(),
|
|
18
|
+
);
|
|
19
|
+
if (!resource) {
|
|
20
|
+
return { error: `Resource ${args.callsign} not found` };
|
|
30
21
|
}
|
|
31
|
-
}
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
const previousStatus = resource.status;
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
// assignedResources lets that incident later yank a unit that has
|
|
38
|
-
// been re-dispatched elsewhere.
|
|
25
|
+
// Log to the incident timeline BEFORE unassigning, so the
|
|
26
|
+
// return-to-available transition is recorded too.
|
|
39
27
|
if (resource.assignedIncident) {
|
|
40
28
|
const inc = state.incidents[resource.assignedIncident];
|
|
41
29
|
if (inc) {
|
|
42
|
-
|
|
30
|
+
logEvent(
|
|
31
|
+
inc,
|
|
32
|
+
`${resource.callsign}: ${previousStatus} → ${args.status}${args.notes ? ` (${args.notes})` : ""}`,
|
|
33
|
+
);
|
|
43
34
|
}
|
|
44
35
|
}
|
|
45
|
-
resource.assignedIncident = null;
|
|
46
|
-
resource.eta = null;
|
|
47
|
-
}
|
|
48
|
-
if (args.status === "on_scene") {
|
|
49
|
-
resource.eta = null;
|
|
50
|
-
}
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
resource.status = args.status;
|
|
38
|
+
|
|
39
|
+
if (args.status === "available") {
|
|
40
|
+
// Detach from the old incident on BOTH sides — leaving the id in
|
|
41
|
+
// assignedResources lets that incident later yank a unit that has
|
|
42
|
+
// been re-dispatched elsewhere.
|
|
43
|
+
if (resource.assignedIncident) {
|
|
44
|
+
const inc = state.incidents[resource.assignedIncident];
|
|
45
|
+
if (inc) {
|
|
46
|
+
inc.assignedResources = inc.assignedResources.filter((id) => id !== resource.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
resource.assignedIncident = null;
|
|
50
|
+
resource.eta = null;
|
|
51
|
+
}
|
|
52
|
+
if (args.status === "on_scene") {
|
|
53
|
+
resource.eta = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
callsign: resource.callsign,
|
|
58
|
+
previousStatus,
|
|
59
|
+
newStatus: args.status,
|
|
60
|
+
assignedIncident: resource.assignedIncident,
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
59
63
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { createToolContext, runTool, withDiscoveredTools } from "@alexkroman1/aai/testing";
|
|
3
|
+
import { toolRunner, withDiscoveredTools } from "@alexkroman1/aai/testing";
|
|
5
4
|
import { describe, expect, test } from "vitest";
|
|
6
5
|
import authoredAgent from "./agent.ts";
|
|
7
6
|
import { faqs, searchable } from "./shared.ts";
|
|
@@ -18,8 +17,14 @@ const agentDef = withDiscoveredTools(
|
|
|
18
17
|
import.meta.glob("./tools/*.ts", { eager: true }),
|
|
19
18
|
);
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Both tools here are stateless, so no call passes a context: `runTool` builds
|
|
22
|
+
* a fresh one, which is a distinct session with empty slots — right for a tool
|
|
23
|
+
* that reads nothing but its arguments, and never what two calls sharing state
|
|
24
|
+
* want. `list_topics` takes no arguments either, and may say so by leaving them
|
|
25
|
+
* out rather than passing a `{}` between the two values a reader cares about.
|
|
26
|
+
*/
|
|
27
|
+
const run = toolRunner(agentDef);
|
|
23
28
|
|
|
24
29
|
describe("embedded-assets template", () => {
|
|
25
30
|
test("the JSON asset really is bundled, and the index is built from it", () => {
|
|
@@ -44,7 +49,7 @@ describe("embedded-assets template", () => {
|
|
|
44
49
|
|
|
45
50
|
describe("list_topics", () => {
|
|
46
51
|
test("answers with every question in the knowledge base", async () => {
|
|
47
|
-
const topics = await run("list_topics"
|
|
52
|
+
const topics = await run("list_topics");
|
|
48
53
|
expect(topics).toEqual(faqs.map((f) => f.question));
|
|
49
54
|
});
|
|
50
55
|
});
|
|
@@ -59,25 +64,23 @@ describe("search_knowledge", () => {
|
|
|
59
64
|
.toLowerCase()
|
|
60
65
|
.split(/\W+/)
|
|
61
66
|
.filter((w) => w.length > 2);
|
|
62
|
-
const result = await run(
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
createToolContext(),
|
|
66
|
-
);
|
|
67
|
+
const result = await run("search_knowledge", {
|
|
68
|
+
query: `hey, could you tell me ${words.join(" ")}?`,
|
|
69
|
+
});
|
|
67
70
|
expect(result).toEqual(target);
|
|
68
71
|
});
|
|
69
72
|
|
|
70
73
|
test("a query with no words longer than two characters is refused early", async () => {
|
|
71
74
|
// The guard before the scan: with no scoreable words every entry ties at
|
|
72
75
|
// zero, and returning the first one would be an answer to nothing.
|
|
73
|
-
expect(await run("search_knowledge", { query: "is it a" }
|
|
76
|
+
expect(await run("search_knowledge", { query: "is it a" })).toEqual({
|
|
74
77
|
result: "No matching FAQ found.",
|
|
75
78
|
});
|
|
76
79
|
});
|
|
77
80
|
|
|
78
81
|
test("a query that overlaps nothing reports no match rather than guessing", async () => {
|
|
79
|
-
expect(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
expect(await run("search_knowledge", { query: "zzzqqq wibbleflange" })).toEqual({
|
|
83
|
+
result: "No matching FAQ found.",
|
|
84
|
+
});
|
|
82
85
|
});
|
|
83
86
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
|
-
import { isToolFailure
|
|
4
|
-
import {
|
|
3
|
+
import { isToolFailure } from "@alexkroman1/aai";
|
|
4
|
+
import { toolInputIssues, toolRunner, withDiscoveredTools } from "@alexkroman1/aai/testing";
|
|
5
5
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
6
6
|
import authoredAgent from "./agent.ts";
|
|
7
7
|
import { excerptAround, type FdaLabel, toDrugInfo } from "./fda.ts";
|
|
@@ -35,8 +35,13 @@ const agentDef = withDiscoveredTools(
|
|
|
35
35
|
import.meta.glob("./tools/*.ts", { eager: true }),
|
|
36
36
|
);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Every tool here takes arguments and none of them touches session state, so no
|
|
40
|
+
* call passes a context: `runTool` builds a fresh one per call, which is a
|
|
41
|
+
* distinct session — right for a stateless tool, and never what two calls
|
|
42
|
+
* sharing state want.
|
|
43
|
+
*/
|
|
44
|
+
const run = toolRunner(agentDef);
|
|
40
45
|
|
|
41
46
|
const IBUPROFEN: FdaLabel = {
|
|
42
47
|
openfda: { generic_name: ["IBUPROFEN"], brand_name: ["Advil"], manufacturer_name: ["Acme"] },
|
|
@@ -150,6 +155,19 @@ describe("check_drug_interaction", () => {
|
|
|
150
155
|
expect(isToolFailure(result) && result.error).toContain("at least two");
|
|
151
156
|
expect(label).not.toHaveBeenCalled();
|
|
152
157
|
});
|
|
158
|
+
|
|
159
|
+
test("the schema itself accepts those names, which is why the body re-checks", async () => {
|
|
160
|
+
// The other half of the claim above, asked of the schema directly rather
|
|
161
|
+
// than through `~standard`: `min(2)` counts ENTRIES and `min(1)` counts
|
|
162
|
+
// CHARACTERS, so `" "` is a valid entry and the refusal is the body's.
|
|
163
|
+
expect(
|
|
164
|
+
await toolInputIssues(agentDef, "check_drug_interaction", { drugs: ["ibuprofen", " "] }),
|
|
165
|
+
).toBeUndefined();
|
|
166
|
+
// And the schema is still doing its own half — one drug is not a check.
|
|
167
|
+
expect(
|
|
168
|
+
await toolInputIssues(agentDef, "check_drug_interaction", { drugs: ["ibuprofen"] }),
|
|
169
|
+
).toBeDefined();
|
|
170
|
+
});
|
|
153
171
|
});
|
|
154
172
|
|
|
155
173
|
describe("fda.ts helpers", () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { createToolContext, runTool, withDiscoveredTools } from "@alexkroman1/aai/testing";
|
|
3
|
+
import { createToolContext, toolRunner, withDiscoveredTools } from "@alexkroman1/aai/testing";
|
|
5
4
|
import { describe, expect, test } from "vitest";
|
|
6
5
|
import authoredAgent from "./agent.ts";
|
|
7
6
|
|
|
@@ -21,8 +20,7 @@ const agentDef = withDiscoveredTools(
|
|
|
21
20
|
import { DEFAULT_GAME_STATE, gameSlot, MAX_HISTORY, REPORTED_HISTORY } from "./shared.ts";
|
|
22
21
|
|
|
23
22
|
/** A tool by the name the model calls it by, bound to this agent. */
|
|
24
|
-
const run = (
|
|
25
|
-
runTool(agentDef, name, args, ctx);
|
|
23
|
+
const run = toolRunner(agentDef);
|
|
26
24
|
|
|
27
25
|
/** Each context owns its OWN slot store, which is what makes two playthroughs
|
|
28
26
|
* independent by construction. */
|
|
@@ -153,7 +151,7 @@ describe("the adventure's tools", () => {
|
|
|
153
151
|
await run("game_state_history", { value: `command ${i}` }, ctx);
|
|
154
152
|
}
|
|
155
153
|
|
|
156
|
-
expect(await run("game_state_get",
|
|
154
|
+
expect(await run("game_state_get", ctx)).toEqual({
|
|
157
155
|
currentRoom: "Echo Chamber",
|
|
158
156
|
inventory: ["lantern"],
|
|
159
157
|
score: 7,
|
|
@@ -169,7 +167,7 @@ describe("the adventure's tools", () => {
|
|
|
169
167
|
await run("game_state_score", { value: 30 }, ctx);
|
|
170
168
|
await run("game_state_move", { value: "Echo Chamber" }, ctx);
|
|
171
169
|
|
|
172
|
-
const restarted = (await run("game_state_restart",
|
|
170
|
+
const restarted = (await run("game_state_restart", ctx)) as {
|
|
173
171
|
restarted: boolean;
|
|
174
172
|
currentRoom: string;
|
|
175
173
|
};
|