@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,7 +1,7 @@
|
|
|
1
1
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
2
2
|
|
|
3
3
|
import type { DeepReadonly, ToolFailure } from "@alexkroman1/aai";
|
|
4
|
-
import { pushCapped, sessionSlot } from "@alexkroman1/aai";
|
|
4
|
+
import { dialog, pushCapped, sessionSlot } from "@alexkroman1/aai";
|
|
5
5
|
|
|
6
6
|
export const SEVERITIES = ["critical", "urgent", "moderate", "minor"] as const;
|
|
7
7
|
export type Severity = (typeof SEVERITIES)[number];
|
|
@@ -218,6 +218,104 @@ export const dispatchSlot = sessionSlot("dispatch", createDefaultState, {
|
|
|
218
218
|
/** The projection BOTH ends use: `syncState` on the agent, `useAgentState` in the client. */
|
|
219
219
|
export const dashboardProjection = dispatchSlot.projection(dashboardView);
|
|
220
220
|
|
|
221
|
+
// ─── The call in hand ────────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Where the dispatcher is on the call in hand, as a plain state map.
|
|
225
|
+
*
|
|
226
|
+
* Declared as a {@link DialogSpec} — `{ initial, states }`, each state carrying
|
|
227
|
+
* its own `instruction` and `on` map — rather than an XState machine. This
|
|
228
|
+
* dialog used exactly the four things a spec can say, and the `setup({ types:
|
|
229
|
+
* {} as { events: … } })` block it used to carry restated the four event names
|
|
230
|
+
* already written in the `on` maps below. The instruction is a TYPED field now
|
|
231
|
+
* instead of an untyped `meta` bag, which matters because a misspelling there
|
|
232
|
+
* produced a refusal with no recovery text and no error anywhere.
|
|
233
|
+
*
|
|
234
|
+
* The system prompt used to carry this as prose — "incident_triage: After
|
|
235
|
+
* creating, assess severity", "Critical incidents get immediate dispatch",
|
|
236
|
+
* "Never leave a critical incident without at least one resource dispatched" —
|
|
237
|
+
* which is advice to a model rather than a property of the agent. Two things
|
|
238
|
+
* change by declaring it.
|
|
239
|
+
*
|
|
240
|
+
* **The gate is real.** Six mutating tools take an incident id and did their own
|
|
241
|
+
* "does this exist" check through {@link findIncident}, whose answer on an empty
|
|
242
|
+
* board is `Incident INC-0001 not found` — a data answer to a positional
|
|
243
|
+
* question, and the wrong sentence for a dispatcher who has logged nothing yet.
|
|
244
|
+
* `when: "working"` is that check now, and the SDK's refusal names where the
|
|
245
|
+
* shift actually is and quotes the state's instruction.
|
|
246
|
+
*
|
|
247
|
+
* **The instruction rides every result.** A flow tool answers with the position
|
|
248
|
+
* it landed in, so `incident_create`'s result now ends with "confirm the
|
|
249
|
+
* severity and type with incident_triage" and `incident_triage`'s with "assign
|
|
250
|
+
* units" — the tool-by-tool sequencing the prompt was spending paragraphs on,
|
|
251
|
+
* arriving in the last thing the model reads before it speaks.
|
|
252
|
+
*
|
|
253
|
+
* **What this position is NOT is per-incident.** A flow is bound to a session
|
|
254
|
+
* and holds one position, and this board holds many incidents at once — so
|
|
255
|
+
* `working.monitoring` means "the incident this dispatcher last touched has
|
|
256
|
+
* units on it", not "every incident does". Per-incident status stays where it
|
|
257
|
+
* belongs, on {@link Incident.status}, and the tools stay addressed by id. That
|
|
258
|
+
* is also why `working` is a PARENT state: what every gated tool actually needs
|
|
259
|
+
* is "something has been logged on this shift", and the three children exist to
|
|
260
|
+
* carry the instruction for the step in front of the dispatcher rather than to
|
|
261
|
+
* gate anything.
|
|
262
|
+
*
|
|
263
|
+
* `LOGGED` is accepted from every state because a new 911 call is always legal —
|
|
264
|
+
* the one transition that is not a progression.
|
|
265
|
+
*
|
|
266
|
+
* `as const` is what keeps the `on` keys literal, so `DialogEvent` synthesizes
|
|
267
|
+
* the event union from them and a misspelled `send` is a compile error.
|
|
268
|
+
*/
|
|
269
|
+
const callSpec = {
|
|
270
|
+
initial: "standby",
|
|
271
|
+
states: {
|
|
272
|
+
standby: {
|
|
273
|
+
instruction:
|
|
274
|
+
"Nothing is logged on this shift. Take the call — location first, then the " +
|
|
275
|
+
"nature of the emergency, then the caller's name and callback — and log it " +
|
|
276
|
+
"with incident_create.",
|
|
277
|
+
on: { LOGGED: "working" },
|
|
278
|
+
},
|
|
279
|
+
working: {
|
|
280
|
+
initial: "triaging",
|
|
281
|
+
// Declared on the PARENT so all three children inherit it, which is what
|
|
282
|
+
// makes a new call legal mid-incident without repeating the transition
|
|
283
|
+
// three times. The children re-target each other below.
|
|
284
|
+
on: { LOGGED: ".triaging" },
|
|
285
|
+
states: {
|
|
286
|
+
triaging: {
|
|
287
|
+
instruction:
|
|
288
|
+
"Logged, not yet triaged. Confirm or override the severity and type with " +
|
|
289
|
+
"incident_triage. For a critical call, dispatch first and triage alongside it.",
|
|
290
|
+
on: { TRIAGED: "dispatching", DISPATCHED: "monitoring", ESCALATED: "dispatching" },
|
|
291
|
+
},
|
|
292
|
+
dispatching: {
|
|
293
|
+
instruction:
|
|
294
|
+
"Triaged, nothing rolling. Assign units with resources_dispatch — never " +
|
|
295
|
+
"leave a critical incident without at least one.",
|
|
296
|
+
on: { DISPATCHED: "monitoring", TRIAGED: "dispatching", ESCALATED: "dispatching" },
|
|
297
|
+
},
|
|
298
|
+
monitoring: {
|
|
299
|
+
instruction:
|
|
300
|
+
"Units are assigned. Work it — status updates as they radio in, notes as " +
|
|
301
|
+
"the picture changes, escalate if it outgrows the response, and close it " +
|
|
302
|
+
"with incident_update_status.",
|
|
303
|
+
on: { TRIAGED: "dispatching", DISPATCHED: "monitoring", ESCALATED: "dispatching" },
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
} as const;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* The flow. Its own slot key beside {@link dispatchSlot}: the flow holds the
|
|
312
|
+
* POSITION and the board holds the incidents, because the position is one fact
|
|
313
|
+
* about the conversation and the board is many facts about the world. One tool
|
|
314
|
+
* call moves both — every converted tool opens `dispatchSlot.update` inside its
|
|
315
|
+
* `execute` and lets the flow's own `send` follow it.
|
|
316
|
+
*/
|
|
317
|
+
export const callFlow = dialog("call", callSpec);
|
|
318
|
+
|
|
221
319
|
/**
|
|
222
320
|
* The board as a READ hands it out: deep-frozen, and typed to say so.
|
|
223
321
|
*
|
|
@@ -2,11 +2,13 @@ You are the AI-powered Emergency Dispatch Command Center. You coordinate emergen
|
|
|
2
2
|
|
|
3
3
|
Your role combines call-taker, dispatcher, and incident commander. You speak like an experienced dispatcher: calm, precise, and authoritative. Never panic. Use brevity codes and dispatch terminology naturally.
|
|
4
4
|
|
|
5
|
+
WHERE THE CALL IS: every incident and resource tool answers with the position the call is in and what that position expects next — read it. Those tools refuse outright until something has been logged, and the refusal tells you what to do first. Nothing here has to be remembered between turns.
|
|
6
|
+
|
|
5
7
|
Your tools:
|
|
6
8
|
|
|
7
9
|
INCIDENT MANAGEMENT:
|
|
8
10
|
- incident_create: Log a new incident. Ask for location first, then nature of emergency, then caller info. Speed matters for critical calls.
|
|
9
|
-
- incident_triage:
|
|
11
|
+
- incident_triage: Assess severity. The system recommends severity, type, and protocols. Review and confirm or override.
|
|
10
12
|
- incident_update_status: Move incidents through the workflow (en_route, on_scene, resolved, escalated).
|
|
11
13
|
- incident_get: Get details on a specific incident.
|
|
12
14
|
- incident_escalate: Escalate when an incident exceeds current capacity or severity increases.
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import { isToolFailure } from "@alexkroman1/aai";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { dispatchSlot, findIncident, logEvent } from "../shared.ts";
|
|
3
|
+
import { callFlow, dispatchSlot, findIncident, logEvent } from "../shared.ts";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** Gated on `working` — there is nothing to annotate before anything is logged. */
|
|
6
|
+
export default callFlow.tool({
|
|
6
7
|
description: "Add a situational update note to an incident's timeline.",
|
|
8
|
+
when: "working",
|
|
7
9
|
inputSchema: z.object({
|
|
8
10
|
incidentId: z.string().max(20).describe("The incident ID"),
|
|
9
11
|
note: z.string().max(1000).describe("The note to add"),
|
|
10
12
|
source: z.string().max(100).describe("Who reported this — unit callsign or caller").optional(),
|
|
11
13
|
}),
|
|
12
|
-
execute(args,
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
execute: (args, ctx) =>
|
|
15
|
+
dispatchSlot.update(ctx, (state) => {
|
|
16
|
+
const inc = findIncident(state, args.incidentId);
|
|
17
|
+
if (isToolFailure(inc)) return inc;
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
const entry = args.source ? `[${args.source}] ${args.note}` : args.note;
|
|
20
|
+
logEvent(inc, entry);
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
return {
|
|
23
|
+
incidentId: args.incidentId,
|
|
24
|
+
noteAdded: entry,
|
|
25
|
+
timelineEntries: inc.timeline.length,
|
|
26
|
+
};
|
|
27
|
+
}),
|
|
25
28
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import {
|
|
3
3
|
calculateTriageScore,
|
|
4
|
+
callFlow,
|
|
4
5
|
createIncident,
|
|
5
6
|
dispatchSlot,
|
|
6
7
|
getApplicableProtocols,
|
|
@@ -10,6 +11,18 @@ import {
|
|
|
10
11
|
resourceBrief,
|
|
11
12
|
} from "../shared.ts";
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* **Not a `callFlow.tool`, deliberately.** A new 911 call is legal in every
|
|
16
|
+
* state, so a `when` listing all of them would be a gate that gates nothing —
|
|
17
|
+
* the same call `plan-and-execute`'s `start_plan` makes. It sends `LOGGED`
|
|
18
|
+
* itself, which is what `dialog.send` is public for, and reports the position it
|
|
19
|
+
* landed in so the model reads "confirm the severity and type" as part of this
|
|
20
|
+
* result rather than having to remember it from the prompt.
|
|
21
|
+
*
|
|
22
|
+
* The `send` sits INSIDE the board's mutation window, which is safe because it
|
|
23
|
+
* is a different slot: the re-entrancy guard is per slot, and neither window
|
|
24
|
+
* spans an await, so the whole call commits atomically.
|
|
25
|
+
*/
|
|
13
26
|
export default dispatchSlot.updateTool({
|
|
14
27
|
description: "Create a new incident from an incoming emergency call.",
|
|
15
28
|
inputSchema: z.object({
|
|
@@ -29,7 +42,7 @@ export default dispatchSlot.updateTool({
|
|
|
29
42
|
.describe("Known hazards: fire, chemical, electrical, structural, weapons")
|
|
30
43
|
.optional(),
|
|
31
44
|
}),
|
|
32
|
-
execute(args, state) {
|
|
45
|
+
execute(args, state, ctx) {
|
|
33
46
|
const recSeverity = recommendSeverity(args.description);
|
|
34
47
|
const recType = recommendType(args.description);
|
|
35
48
|
const triageScore = calculateTriageScore(
|
|
@@ -62,6 +75,11 @@ export default dispatchSlot.updateTool({
|
|
|
62
75
|
|
|
63
76
|
return {
|
|
64
77
|
incidentId: id,
|
|
78
|
+
// Spread the position rather than renaming its fields: this tool is
|
|
79
|
+
// ungated, so the `{ state, instruction }` a gated tool's result carries
|
|
80
|
+
// has to come from here — and under two different key sets the model
|
|
81
|
+
// reads its own position two different ways within one agent.
|
|
82
|
+
...callFlow.send(ctx, { type: "LOGGED" }),
|
|
65
83
|
recommendedSeverity: recSeverity,
|
|
66
84
|
recommendedType: recType,
|
|
67
85
|
triageScore,
|
|
@@ -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
|
},
|