@alexkroman1/aai-cli 6.10.1 → 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 +93 -14
- package/dist/scaffold/package.json +5 -3
- package/dist/scaffold/server.mjs +13 -4
- package/dist/scaffold/vite.config.ts +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 +818 -0
- package/dist/templates/call-audit/agent.ts +171 -0
- package/dist/templates/call-audit/client.tsx +198 -0
- package/dist/templates/call-audit/workflows/audit.ts +298 -0
- package/dist/templates/call-audit/workflows/ingest.ts +251 -0
- package/dist/templates/call-audit/workflows/media.ts +637 -0
- package/dist/templates/call-audit/workflows/summarize.ts +201 -0
- package/dist/templates/call-audit/workflows/sync-api.ts +44 -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.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 +107 -96
- 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 +48 -41
- 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 +314 -0
- package/dist/templates/spoken-summary/agent.ts +155 -0
- package/dist/templates/spoken-summary/client.tsx +185 -0
- package/dist/templates/spoken-summary/workflows/summarize.ts +237 -0
- package/dist/templates/spoken-summary/workflows/transcribe.ts +138 -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/tools/answer_question.ts +17 -4
- package/dist/templates/transcription-workflow/agent.test.ts +246 -123
- package/dist/templates/transcription-workflow/agent.ts +21 -7
- package/dist/templates/transcription-workflow/client.tsx +17 -42
- package/dist/templates/transcription-workflow/workflows/batch.ts +79 -180
- package/dist/templates/transcription-workflow/workflows/normalize.ts +247 -0
- package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
- package/dist/templates/transcription-workflow/workflows/stream.ts +10 -9
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +26 -94
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +55 -40
- package/dist/templates/transcription-workflow/workflows/wav.ts +31 -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/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,14 +3,22 @@ import { z } from "zod";
|
|
|
3
3
|
import {
|
|
4
4
|
assertNotResolved,
|
|
5
5
|
calculateTriageScore,
|
|
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 `ESCALATED` puts the call back in `dispatching`: an
|
|
15
|
+
* incident that outgrew its response needs more units, which is exactly that
|
|
16
|
+
* state's instruction.
|
|
17
|
+
*/
|
|
18
|
+
export default callFlow.tool({
|
|
13
19
|
description: "Escalate an incident when it exceeds current capacity or severity increases.",
|
|
20
|
+
when: "working",
|
|
21
|
+
send: { type: "ESCALATED" },
|
|
14
22
|
inputSchema: z.object({
|
|
15
23
|
incidentId: z.string().max(20).describe("The incident ID"),
|
|
16
24
|
reason: z.string().max(1000).describe("Reason for escalation"),
|
|
@@ -20,67 +28,68 @@ export default dispatchSlot.updateTool({
|
|
|
20
28
|
.optional(),
|
|
21
29
|
newSeverity: z.enum(["critical", "urgent"]).describe("Escalated severity level").optional(),
|
|
22
30
|
}),
|
|
23
|
-
execute(args,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
execute: (args, ctx) =>
|
|
32
|
+
dispatchSlot.update(ctx, (state) => {
|
|
33
|
+
const inc = findIncident(state, args.incidentId);
|
|
34
|
+
if (isToolFailure(inc)) return inc;
|
|
35
|
+
const blocked = assertNotResolved(inc, "escalate");
|
|
36
|
+
if (blocked) return blocked;
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
inc.escalationLevel++;
|
|
39
|
+
if (args.newSeverity) inc.severity = args.newSeverity;
|
|
40
|
+
inc.status = "escalated";
|
|
41
|
+
logEvent(inc, `ESCALATED (Level ${inc.escalationLevel}): ${args.reason}`);
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
if (args.requestMutualAid) {
|
|
44
|
+
logEvent(inc, "Mutual aid requested from neighboring jurisdictions");
|
|
45
|
+
// Counter-based ids/callsigns: Date.now() collides across rapid
|
|
46
|
+
// escalations, and duplicate callsigns break dispatch-by-callsign.
|
|
47
|
+
const medicN = ++state.mutualAidCounter;
|
|
48
|
+
const engineN = ++state.mutualAidCounter;
|
|
49
|
+
state.resources.push(
|
|
50
|
+
{
|
|
51
|
+
id: `MA-${medicN}`,
|
|
52
|
+
type: "ambulance",
|
|
53
|
+
callsign: `Mutual-Aid-Medic-${medicN}`,
|
|
54
|
+
status: "available",
|
|
55
|
+
assignedIncident: null,
|
|
56
|
+
eta: null,
|
|
57
|
+
capabilities: ["als"],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: `MA-${engineN}`,
|
|
61
|
+
type: "fire_engine",
|
|
62
|
+
callsign: `Mutual-Aid-Engine-${engineN}`,
|
|
63
|
+
status: "available",
|
|
64
|
+
assignedIncident: null,
|
|
65
|
+
eta: null,
|
|
66
|
+
capabilities: ["structural"],
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
inc.triageScore = calculateTriageScore(
|
|
72
|
+
inc.severity,
|
|
73
|
+
inc.type,
|
|
74
|
+
inc.casualties.estimated,
|
|
75
|
+
inc.hazards.length,
|
|
76
|
+
);
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
const additionalResources = recommendResources(inc.type, inc.severity, state).filter(
|
|
79
|
+
(r) => !inc.assignedResources.includes(r.id),
|
|
80
|
+
);
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
return {
|
|
83
|
+
incidentId: args.incidentId,
|
|
84
|
+
escalationLevel: inc.escalationLevel,
|
|
85
|
+
newSeverity: inc.severity,
|
|
86
|
+
newTriageScore: inc.triageScore,
|
|
87
|
+
mutualAidRequested: args.requestMutualAid ?? false,
|
|
88
|
+
additionalResourcesAvailable: additionalResources.map((r) => ({
|
|
89
|
+
callsign: r.callsign,
|
|
90
|
+
type: r.type,
|
|
91
|
+
})),
|
|
92
|
+
message: `ESCALATION CONFIRMED — ${args.incidentId} now Level ${inc.escalationLevel}. ${additionalResources.length} additional resource(s) available for dispatch.`,
|
|
93
|
+
};
|
|
94
|
+
}),
|
|
86
95
|
});
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import {
|
|
4
4
|
assertNotResolved,
|
|
5
5
|
calculateTriageScore,
|
|
6
|
+
callFlow,
|
|
6
7
|
dispatchSlot,
|
|
7
8
|
findIncident,
|
|
8
9
|
getApplicableProtocols,
|
|
@@ -12,9 +13,21 @@ import {
|
|
|
12
13
|
SEVERITIES,
|
|
13
14
|
} from "../shared.ts";
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Gated on `working`: there is nothing to triage until something has been
|
|
18
|
+
* logged, and the flow's refusal says so in the dispatcher's own terms where
|
|
19
|
+
* `findIncident` would have answered `Incident INC-0001 not found` — a data
|
|
20
|
+
* answer to a positional question.
|
|
21
|
+
*
|
|
22
|
+
* `TRIAGED` moves the call to `dispatching`, whose instruction is "assign
|
|
23
|
+
* units", so the sequencing the system prompt used to spell out tool by tool
|
|
24
|
+
* arrives in this tool's own result.
|
|
25
|
+
*/
|
|
26
|
+
export default callFlow.tool({
|
|
16
27
|
description:
|
|
17
28
|
"Triage an incident — confirm or override severity, type, hazards, and casualty count.",
|
|
29
|
+
when: "working",
|
|
30
|
+
send: { type: "TRIAGED" },
|
|
18
31
|
inputSchema: z.object({
|
|
19
32
|
incidentId: z.string().max(20).describe("The incident ID"),
|
|
20
33
|
severity: z.enum(SEVERITIES).describe("Confirmed severity after triage").optional(),
|
|
@@ -27,50 +40,51 @@ export default dispatchSlot.updateTool({
|
|
|
27
40
|
casualtyUpdate: z.number().int().nonnegative().describe("Updated casualty count").optional(),
|
|
28
41
|
notes: z.string().max(1000).describe("Triage notes").optional(),
|
|
29
42
|
}),
|
|
30
|
-
execute(args,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
execute: (args, ctx) =>
|
|
44
|
+
dispatchSlot.update(ctx, (state) => {
|
|
45
|
+
const inc = findIncident(state, args.incidentId);
|
|
46
|
+
if (isToolFailure(inc)) return inc;
|
|
47
|
+
const blocked = assertNotResolved(inc, "triage");
|
|
48
|
+
if (blocked) return blocked;
|
|
35
49
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
if (args.severity) inc.severity = args.severity;
|
|
51
|
+
if (args.type) inc.type = args.type;
|
|
52
|
+
for (const hazard of args.additionalHazards ?? []) {
|
|
53
|
+
if (!inc.hazards.includes(hazard)) inc.hazards.push(hazard);
|
|
54
|
+
}
|
|
55
|
+
if (args.casualtyUpdate !== undefined) {
|
|
56
|
+
inc.casualties.estimated = args.casualtyUpdate;
|
|
57
|
+
}
|
|
58
|
+
if (args.notes) logEvent(inc, `Triage note: ${args.notes}`);
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
inc.triageScore = calculateTriageScore(
|
|
61
|
+
inc.severity,
|
|
62
|
+
inc.type,
|
|
63
|
+
inc.casualties.estimated,
|
|
64
|
+
inc.hazards.length,
|
|
65
|
+
);
|
|
66
|
+
inc.status = "triaged";
|
|
67
|
+
logEvent(inc, `Triaged: ${inc.severity} ${inc.type}, score ${inc.triageScore}`);
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
const protocols = getApplicableProtocols(inc.type, inc.severity);
|
|
70
|
+
const recommended = recommendResources(inc.type, inc.severity, state);
|
|
57
71
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
return {
|
|
73
|
+
incidentId: args.incidentId,
|
|
74
|
+
severity: inc.severity,
|
|
75
|
+
type: inc.type,
|
|
76
|
+
triageScore: inc.triageScore,
|
|
77
|
+
hazards: inc.hazards,
|
|
78
|
+
estimatedCasualties: inc.casualties.estimated,
|
|
79
|
+
protocols: protocols.map((p) => ({
|
|
80
|
+
name: p.name,
|
|
81
|
+
steps: p.steps,
|
|
82
|
+
requiredResources: p.requiredResources,
|
|
83
|
+
})),
|
|
84
|
+
recommendedResources: recommended.map((r) => ({
|
|
85
|
+
callsign: r.callsign,
|
|
86
|
+
type: r.type,
|
|
87
|
+
})),
|
|
88
|
+
};
|
|
89
|
+
}),
|
|
76
90
|
});
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { isToolFailure } from "@alexkroman1/aai";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { assertNotResolved, dispatchSlot, findIncident, logEvent } from "../shared.ts";
|
|
3
|
+
import { assertNotResolved, callFlow, dispatchSlot, findIncident, logEvent } from "../shared.ts";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Gated on `working`; sends nothing. A status update moves the INCIDENT, not
|
|
7
|
+
* the call — the dispatcher is still in `monitoring` afterwards, and closing
|
|
8
|
+
* one incident says nothing about the others still on the board. Omitting both
|
|
9
|
+
* `send` and `sendFrom` is how a flow tool reads without advancing, and the
|
|
10
|
+
* result still carries the position it read.
|
|
11
|
+
*/
|
|
12
|
+
export default callFlow.tool({
|
|
6
13
|
description: "Update an incident's status (en_route, on_scene, resolved, escalated).",
|
|
14
|
+
when: "working",
|
|
7
15
|
inputSchema: z.object({
|
|
8
16
|
incidentId: z.string().max(20).describe("The incident ID"),
|
|
9
17
|
status: z.enum(["en_route", "on_scene", "resolved", "escalated"]).describe("New status"),
|
|
@@ -16,51 +24,52 @@ export default dispatchSlot.updateTool({
|
|
|
16
24
|
.describe("Updated casualty numbers")
|
|
17
25
|
.optional(),
|
|
18
26
|
}),
|
|
19
|
-
execute(args,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
execute: (args, ctx) =>
|
|
28
|
+
dispatchSlot.update(ctx, (state) => {
|
|
29
|
+
const inc = findIncident(state, args.incidentId);
|
|
30
|
+
if (isToolFailure(inc)) return inc;
|
|
31
|
+
const blocked = assertNotResolved(inc, `set status to ${args.status}`);
|
|
32
|
+
if (blocked) return blocked;
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
inc.status = args.status;
|
|
35
|
+
logEvent(inc, `Status → ${args.status}${args.notes ? `: ${args.notes}` : ""}`);
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
if (args.casualtyUpdate) {
|
|
38
|
+
if (args.casualtyUpdate.confirmed !== undefined) {
|
|
39
|
+
inc.casualties.confirmed = args.casualtyUpdate.confirmed;
|
|
40
|
+
}
|
|
41
|
+
if (args.casualtyUpdate.treated !== undefined) {
|
|
42
|
+
inc.casualties.treated = args.casualtyUpdate.treated;
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
|
-
if (args.casualtyUpdate.treated !== undefined) {
|
|
33
|
-
inc.casualties.treated = args.casualtyUpdate.treated;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
// Only touch resources still assigned to THIS incident — a unit that
|
|
47
|
+
// radioed available and was re-dispatched elsewhere belongs to its
|
|
48
|
+
// new incident now.
|
|
49
|
+
const ownResources = state.resources.filter((r) => r.assignedIncident === inc.id);
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
if (args.status === "resolved") {
|
|
52
|
+
for (const r of ownResources) {
|
|
53
|
+
r.status = "returning";
|
|
54
|
+
r.assignedIncident = null;
|
|
55
|
+
r.eta = null;
|
|
56
|
+
}
|
|
57
|
+
inc.assignedResources = [];
|
|
58
|
+
logEvent(inc, "All resources released — incident closed");
|
|
47
59
|
}
|
|
48
|
-
inc.assignedResources = [];
|
|
49
|
-
logEvent(inc, "All resources released — incident closed");
|
|
50
|
-
}
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
if (args.status === "en_route" || args.status === "on_scene") {
|
|
62
|
+
for (const r of ownResources) {
|
|
63
|
+
r.status = args.status;
|
|
64
|
+
if (args.status === "on_scene") r.eta = null;
|
|
65
|
+
}
|
|
56
66
|
}
|
|
57
|
-
}
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
return {
|
|
69
|
+
incidentId: args.incidentId,
|
|
70
|
+
newStatus: args.status,
|
|
71
|
+
timeline: inc.timeline.slice(-5).map((t) => t.event),
|
|
72
|
+
casualties: inc.casualties,
|
|
73
|
+
};
|
|
74
|
+
}),
|
|
66
75
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { Incident, IncidentType, Severity } from "../shared.ts";
|
|
3
|
-
import { calculateTriageScore, createIncident, dispatchSlot } from "../shared.ts";
|
|
3
|
+
import { calculateTriageScore, callFlow, createIncident, dispatchSlot } from "../shared.ts";
|
|
4
4
|
|
|
5
5
|
type ScenarioIncident = Pick<Incident, "location" | "description" | "type" | "severity">;
|
|
6
6
|
type ScenarioDef = { narrative: string; incidents: ScenarioIncident[] };
|
|
@@ -116,12 +116,15 @@ const scenarios = {
|
|
|
116
116
|
type ScenarioName = keyof typeof scenarios;
|
|
117
117
|
const SCENARIO_NAMES = Object.keys(scenarios) as [ScenarioName, ...ScenarioName[]];
|
|
118
118
|
|
|
119
|
+
/** Logs incidents like a real call does, so it sends `LOGGED` for the same
|
|
120
|
+
* reason `incident_create` does — and is ungated for the same reason too: a
|
|
121
|
+
* drill may be started at any point in a shift. */
|
|
119
122
|
export default dispatchSlot.updateTool({
|
|
120
123
|
description: "Run a training scenario that creates simulated incidents for dispatch practice.",
|
|
121
124
|
inputSchema: z.object({
|
|
122
125
|
scenario: z.enum(SCENARIO_NAMES).describe("Scenario type to simulate"),
|
|
123
126
|
}),
|
|
124
|
-
execute(args, state) {
|
|
127
|
+
execute(args, state, ctx) {
|
|
125
128
|
const s = scenarios[args.scenario];
|
|
126
129
|
|
|
127
130
|
const created: string[] = [];
|
|
@@ -140,6 +143,9 @@ export default dispatchSlot.updateTool({
|
|
|
140
143
|
scenario: args.scenario,
|
|
141
144
|
narrative: s.narrative,
|
|
142
145
|
incidentsCreated: created,
|
|
146
|
+
// As `incident_create`: the position spread verbatim, so an ungated tool
|
|
147
|
+
// reports it under the same keys a gated one does.
|
|
148
|
+
...callFlow.send(ctx, { type: "LOGGED" }),
|
|
143
149
|
message: `SCENARIO ACTIVE: ${s.narrative}. ${created.length} incidents created. Awaiting dispatch orders.`,
|
|
144
150
|
};
|
|
145
151
|
},
|
|
@@ -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
|
});
|