@alexkroman1/aai-cli 5.1.1 → 5.3.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.
Files changed (99) hide show
  1. package/dist/_agent.d.ts +39 -0
  2. package/dist/_api-client.d.ts +37 -0
  3. package/dist/_bundler.d.ts +46 -0
  4. package/dist/_config.d.ts +57 -0
  5. package/dist/_default-html.d.ts +12 -0
  6. package/dist/_deploy.d.ts +28 -0
  7. package/dist/_dev-server.d.ts +74 -0
  8. package/dist/{_init-B-LEVTHx.mjs → _init-DU-sXH6S.mjs} +44 -65
  9. package/dist/_init.d.ts +7 -0
  10. package/dist/_mock-api.d.ts +22 -0
  11. package/dist/_mock-registry.d.ts +17 -0
  12. package/dist/_output.d.ts +50 -0
  13. package/dist/_server-common.d.ts +17 -0
  14. package/dist/_slug-api.d.ts +15 -0
  15. package/dist/_templates.d.ts +21 -0
  16. package/dist/_typecheck-gate.d.ts +7 -0
  17. package/dist/_ui.d.ts +22 -0
  18. package/dist/_utils.d.ts +48 -0
  19. package/dist/_vite-env.d.ts +8 -0
  20. package/dist/build.d.ts +16 -0
  21. package/dist/cli.d.ts +1 -0
  22. package/dist/cli.mjs +1 -1
  23. package/dist/client-bundler.d.ts +21 -0
  24. package/dist/delete.d.ts +18 -0
  25. package/dist/deploy.d.ts +15 -0
  26. package/dist/dev.d.ts +13 -0
  27. package/dist/{init-tc3Rbgrf.mjs → init-BjeK8crW.mjs} +1 -1
  28. package/dist/init.d.ts +24 -0
  29. package/dist/scaffold/.env.example +11 -0
  30. package/dist/scaffold/CLAUDE.md +938 -0
  31. package/dist/scaffold/global.d.ts +1 -0
  32. package/dist/scaffold/package.json +32 -0
  33. package/dist/scaffold/pnpm-workspace.yaml +13 -0
  34. package/dist/scaffold/tsconfig.json +19 -0
  35. package/dist/scaffold/vite.config.ts +14 -0
  36. package/dist/scaffold/vitest.config.ts +24 -0
  37. package/dist/secret.d.ts +25 -0
  38. package/dist/storage.d.ts +22 -0
  39. package/dist/templates/code-interpreter/agent.ts +11 -0
  40. package/dist/templates/code-interpreter/system-prompt.md +18 -0
  41. package/dist/templates/dispatch-center/agent.test.ts +153 -0
  42. package/dist/templates/dispatch-center/agent.ts +50 -0
  43. package/dist/templates/dispatch-center/client.tsx +378 -0
  44. package/dist/templates/dispatch-center/shared.ts +631 -0
  45. package/dist/templates/dispatch-center/system-prompt.md +39 -0
  46. package/dist/templates/dispatch-center/tools/incident_add_note.ts +27 -0
  47. package/dist/templates/dispatch-center/tools/incident_create.ts +79 -0
  48. package/dist/templates/dispatch-center/tools/incident_escalate.ts +88 -0
  49. package/dist/templates/dispatch-center/tools/incident_get.ts +36 -0
  50. package/dist/templates/dispatch-center/tools/incident_triage.ts +78 -0
  51. package/dist/templates/dispatch-center/tools/incident_update_status.ts +68 -0
  52. package/dist/templates/dispatch-center/tools/ops_dashboard.ts +52 -0
  53. package/dist/templates/dispatch-center/tools/ops_protocols.ts +27 -0
  54. package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +149 -0
  55. package/dist/templates/dispatch-center/tools/resources_dispatch.ts +97 -0
  56. package/dist/templates/dispatch-center/tools/resources_get_available.ts +36 -0
  57. package/dist/templates/dispatch-center/tools/resources_update_status.ts +62 -0
  58. package/dist/templates/embedded-assets/agent.ts +55 -0
  59. package/dist/templates/embedded-assets/knowledge.json +20 -0
  60. package/dist/templates/health-assistant/agent.ts +174 -0
  61. package/dist/templates/health-assistant/system-prompt.md +17 -0
  62. package/dist/templates/infocom-adventure/agent.ts +119 -0
  63. package/dist/templates/infocom-adventure/client.tsx +279 -0
  64. package/dist/templates/infocom-adventure/shared.ts +38 -0
  65. package/dist/templates/infocom-adventure/system-prompt.md +43 -0
  66. package/dist/templates/math-buddy/agent.ts +16 -0
  67. package/dist/templates/math-buddy/system-prompt.md +12 -0
  68. package/dist/templates/night-owl/agent.ts +76 -0
  69. package/dist/templates/night-owl/client.tsx +130 -0
  70. package/dist/templates/night-owl/shared.ts +5 -0
  71. package/dist/templates/personal-finance/agent.ts +11 -0
  72. package/dist/templates/personal-finance/system-prompt.md +17 -0
  73. package/dist/templates/pipeline-simple/agent.test.ts +32 -0
  74. package/dist/templates/pipeline-simple/agent.ts +13 -0
  75. package/dist/templates/pizza-ordering/agent.test.ts +235 -0
  76. package/dist/templates/pizza-ordering/agent.ts +174 -0
  77. package/dist/templates/pizza-ordering/client.tsx +120 -0
  78. package/dist/templates/pizza-ordering/shared.ts +142 -0
  79. package/dist/templates/pizza-ordering/system-prompt.md +17 -0
  80. package/dist/templates/simple/agent.ts +5 -0
  81. package/dist/templates/solo-rpg/agent.test.ts +477 -0
  82. package/dist/templates/solo-rpg/agent.ts +37 -0
  83. package/dist/templates/solo-rpg/client.tsx +809 -0
  84. package/dist/templates/solo-rpg/shared.ts +747 -0
  85. package/dist/templates/solo-rpg/system-prompt.md +115 -0
  86. package/dist/templates/solo-rpg/tools/action_roll.ts +98 -0
  87. package/dist/templates/solo-rpg/tools/burn_momentum.ts +69 -0
  88. package/dist/templates/solo-rpg/tools/check_state.ts +11 -0
  89. package/dist/templates/solo-rpg/tools/load_game.ts +25 -0
  90. package/dist/templates/solo-rpg/tools/oracle.ts +248 -0
  91. package/dist/templates/solo-rpg/tools/save_game.ts +20 -0
  92. package/dist/templates/solo-rpg/tools/setup_character.ts +208 -0
  93. package/dist/templates/solo-rpg/tools/update_state.ts +170 -0
  94. package/dist/templates/web-researcher/agent.ts +11 -0
  95. package/dist/test.d.ts +39 -0
  96. package/dist/tsdown.config.d.ts +2 -0
  97. package/dist/typecheck.d.ts +26 -0
  98. package/dist/worker-bundler.d.ts +29 -0
  99. package/package.json +9 -6
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,32 @@
1
+ {
2
+ "type": "module",
3
+ "packageManager": "pnpm@10.29.3",
4
+ "engines": {
5
+ "node": ">=24 <27"
6
+ },
7
+ "scripts": {
8
+ "dev": "aai dev",
9
+ "build": "aai build",
10
+ "test": "aai test",
11
+ "deploy": "aai deploy"
12
+ },
13
+ "dependencies": {
14
+ "@alexkroman1/aai": "^5.3.0",
15
+ "@alexkroman1/aai-ui": "^5.3.0",
16
+ "react": "^19.2.8",
17
+ "react-dom": "^19.2.8",
18
+ "tailwindcss": "^4.0.0",
19
+ "zod": "^4.4.3"
20
+ },
21
+ "devDependencies": {
22
+ "@alexkroman1/aai-cli": "^5.3.0",
23
+ "@tailwindcss/vite": "^4.3.3",
24
+ "@types/node": "^26.1.1",
25
+ "@types/react": "^19.2.17",
26
+ "@types/react-dom": "^19.2.3",
27
+ "@vitejs/plugin-react": "^6.0.4",
28
+ "typescript": "^7.0.2",
29
+ "vite": "^8.1.5",
30
+ "vitest": "^4.1.10"
31
+ }
32
+ }
@@ -0,0 +1,13 @@
1
+ # esbuild's postinstall must stay approved or pnpm fails the install
2
+ # (unapproved build scripts). @alexkroman1/aai-cli no longer depends on it
3
+ # directly (its dev bundler runs on Rolldown, prebuilt binaries, no
4
+ # postinstall), but esbuild is an optional peer of vite: any project whose
5
+ # lockfile ever resolved it (an upgrade from an older CLI, or a reinstall
6
+ # over an existing lockfile) keeps it installed, and pnpm then blocks its
7
+ # build script without this entry. pnpm 10 reads `onlyBuiltDependencies`;
8
+ # pnpm 11 renamed it to `allowBuilds` and ignores the old key, so both are
9
+ # declared. This file also marks the project as its own pnpm workspace root.
10
+ onlyBuiltDependencies:
11
+ - esbuild
12
+ allowBuilds:
13
+ esbuild: true
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noImplicitAny": false,
8
+ "useUnknownInCatchVariables": false,
9
+ "verbatimModuleSyntax": true,
10
+ "allowImportingTsExtensions": true,
11
+ "noEmit": true,
12
+ "skipLibCheck": true,
13
+ "types": ["vitest/globals"],
14
+ "resolveJsonModule": true,
15
+ "jsx": "react-jsx",
16
+ "jsxImportSource": "react",
17
+ "lib": ["ES2024", "DOM", "DOM.Iterable"]
18
+ }
19
+ }
@@ -0,0 +1,14 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import tailwindcss from "@tailwindcss/vite";
3
+ import { defineConfig } from "vite";
4
+
5
+ export default defineConfig({
6
+ plugins: [react(), tailwindcss()],
7
+ build: {
8
+ target: "es2022",
9
+ minify: true,
10
+ },
11
+ ssr: {
12
+ noExternal: true,
13
+ },
14
+ });
@@ -0,0 +1,24 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ /**
4
+ * Test config, deliberately separate from vite.config.ts.
5
+ *
6
+ * Vitest prefers this file, which is the point. `vite.config.ts` imports the
7
+ * React and Tailwind plugins for the CLIENT build — neither has anything to
8
+ * do with running an agent's tests, but loading them is a way for the test
9
+ * run to fail. Measured in the studio: an agent's own test suite failed to
10
+ * load because `defineConfig` came back undefined from an unresolved plugin
11
+ * import, reported as `TypeError: default is not a function` with zero tests
12
+ * collected. The agent then spent a build round "fixing" a test that was
13
+ * fine.
14
+ *
15
+ * `globals: true` so `describe`/`test`/`expect` work with or without an
16
+ * explicit `import { test } from "vitest"`. Both spellings are common, both
17
+ * are correct, and tsconfig's `types: ["vitest/globals"]` already promises
18
+ * the un-imported one — this makes the runtime match the types.
19
+ */
20
+ export default defineConfig({
21
+ test: {
22
+ globals: true,
23
+ },
24
+ });
@@ -0,0 +1,25 @@
1
+ import { type CommandResult } from "./_output.ts";
2
+ /**
3
+ * The one `no_input` failure for `secret put`, shared by the JSON-mode stdin
4
+ * path (cli.ts) and the TTY prompt path below so the two can't drift.
5
+ */
6
+ export declare const NO_INPUT: readonly ["no_input", "No value provided", "Pipe secret value to stdin"];
7
+ /** Read secret value from stdin (for non-TTY / piped input). */
8
+ export declare function readStdin(): Promise<string>;
9
+ type SecretPutData = {
10
+ name: string;
11
+ };
12
+ type SecretDeleteData = {
13
+ name: string;
14
+ };
15
+ type SecretListData = {
16
+ secrets: string[];
17
+ };
18
+ /**
19
+ * Execute secret put. If `value` is provided, use it directly (non-TTY path).
20
+ * If not provided, prompt interactively (TTY path).
21
+ */
22
+ export declare function executeSecretPut(cwd: string, name: string, value: string | undefined, server: string | undefined): Promise<CommandResult<SecretPutData>>;
23
+ export declare function executeSecretDelete(cwd: string, name: string, server: string | undefined): Promise<CommandResult<SecretDeleteData>>;
24
+ export declare function executeSecretList(cwd: string, server: string | undefined): Promise<CommandResult<SecretListData>>;
25
+ export {};
@@ -0,0 +1,22 @@
1
+ import { type CommandResult } from "./_output.ts";
2
+ type StorageStatusData = {
3
+ slug: string;
4
+ enabled: boolean;
5
+ };
6
+ export declare function executeStorageStatus(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
7
+ export declare function executeStorageEnable(cwd: string, server: string | undefined): Promise<CommandResult<StorageStatusData>>;
8
+ export type StorageDisableOpts = {
9
+ server?: string | undefined;
10
+ /** Skip the confirmation prompt (required in non-interactive runs). */
11
+ force?: boolean | undefined;
12
+ /** TTY override for testing. Defaults to real stdin+stdout TTY state. */
13
+ isTTY?: boolean | undefined;
14
+ };
15
+ /**
16
+ * Disable storage — destructive: the server DROPS the app's database schema
17
+ * and all its data. On a TTY this requires interactive confirmation; without
18
+ * a TTY it refuses unless `--force` is passed, so a script can never drop
19
+ * data by accident.
20
+ */
21
+ export declare function executeStorageDisable(cwd: string, opts?: StorageDisableOpts): Promise<CommandResult<StorageStatusData>>;
22
+ export {};
@@ -0,0 +1,11 @@
1
+ import { agent, assemblyAIPipeline } from "@alexkroman1/aai";
2
+ import systemPrompt from "./system-prompt.md?raw";
3
+
4
+ export default agent({
5
+ name: "Coda",
6
+ ...assemblyAIPipeline(),
7
+ systemPrompt,
8
+ greeting:
9
+ "Hey, I'm Coda. I solve problems by writing and running code. Try asking me something like, what's the 50th fibonacci number, or what day of the week was January 1st 2000.",
10
+ builtinTools: ["run_code"],
11
+ });
@@ -0,0 +1,18 @@
1
+ You are Coda, a problem-solving assistant who answers questions by writing and running JavaScript code.
2
+
3
+ CRITICAL RULES:
4
+ - You MUST use the run_code tool for ANY question involving math, counting, string manipulation, data processing, logic, or anything that benefits from exact computation.
5
+ - NEVER do mental math or estimate. Always write code and report the exact result.
6
+ - Use console.log() to output intermediate steps. The last expression is captured automatically.
7
+ - If the code throws an error, fix it and try again.
8
+ - Explain what the code does briefly, then give the answer.
9
+ - Keep your spoken responses short — just say what the code found.
10
+
11
+ Examples of questions you MUST use code for:
12
+ - "What is 127 times 849?" → run_code
13
+ - "How many prime numbers are there below 1000?" → run_code
14
+ - "Reverse the string 'hello world'" → run_code
15
+ - "What's the 50th fibonacci number?" → run_code
16
+ - "Sort these numbers: 42, 17, 93, 8, 55" → run_code
17
+ - "What day of the week was January 1st, 2000?" → run_code
18
+ - "Convert 255 to binary" → run_code
@@ -0,0 +1,153 @@
1
+ import type { ToolContext } from "@alexkroman1/aai";
2
+ import { describe, expect, test } from "vitest";
3
+ import { getState } from "./shared.ts";
4
+ import { incidentCreate } from "./tools/incident_create.ts";
5
+ import { incidentEscalate } from "./tools/incident_escalate.ts";
6
+ import { incidentTriage } from "./tools/incident_triage.ts";
7
+ import { incidentUpdateStatus } from "./tools/incident_update_status.ts";
8
+ import { resourcesDispatch } from "./tools/resources_dispatch.ts";
9
+ import { resourcesUpdateStatus } from "./tools/resources_update_status.ts";
10
+
11
+ let sessionCounter = 0;
12
+
13
+ /** ToolContext stub — the dispatch board lives in ctx.state, one per session. */
14
+ function makeCtx(): { ctx: ToolContext } {
15
+ const sessionId = `test-session-${++sessionCounter}`;
16
+ const ctx = {
17
+ sessionId,
18
+ send: () => {},
19
+ env: {},
20
+ state: {},
21
+ messages: [],
22
+ } as unknown as ToolContext;
23
+ return { ctx };
24
+ }
25
+
26
+ async function createIncidentFor(
27
+ ctx: ToolContext,
28
+ description = "structure fire with heavy smoke",
29
+ ): Promise<string> {
30
+ const result = (await incidentCreate.execute(
31
+ { location: "400 Oak Street", description },
32
+ ctx,
33
+ )) as { incidentId: string };
34
+ return result.incidentId;
35
+ }
36
+
37
+ describe("dispatch-center template", () => {
38
+ test("resolving an incident does not yank a reassigned unit off its new incident", async () => {
39
+ const { ctx } = makeCtx();
40
+
41
+ const inc1 = await createIncidentFor(ctx);
42
+ await resourcesDispatch.execute({ incidentId: inc1, callsigns: ["Medic-1"] }, ctx);
43
+
44
+ // Medic-1 radios available, then is dispatched to a second incident.
45
+ await resourcesUpdateStatus.execute({ callsign: "Medic-1", status: "available" }, ctx);
46
+ const inc2 = await createIncidentFor(ctx, "cardiac arrest, not breathing");
47
+ await resourcesDispatch.execute({ incidentId: inc2, callsigns: ["Medic-1"] }, ctx);
48
+
49
+ // Resolving the first incident must not touch Medic-1 anymore.
50
+ await incidentUpdateStatus.execute({ incidentId: inc1, status: "resolved" }, ctx);
51
+
52
+ const state = getState(ctx);
53
+ const medic1 = state.resources.find((r) => r.callsign === "Medic-1");
54
+ expect(medic1?.status).toBe("dispatched");
55
+ expect(medic1?.assignedIncident).toBe(inc2);
56
+ expect(state.incidents[inc2]?.assignedResources).toContain("R1");
57
+ expect(state.incidents[inc1]?.assignedResources).not.toContain("R1");
58
+ });
59
+
60
+ test("callsigns: ['auto'] triggers auto-dispatch as the description promises", async () => {
61
+ const { ctx } = makeCtx();
62
+ const incidentId = await createIncidentFor(ctx, "cardiac arrest, patient not breathing");
63
+
64
+ const result = (await resourcesDispatch.execute({ incidentId, callsigns: ["auto"] }, ctx)) as {
65
+ dispatched: { callsign: string }[];
66
+ failed?: { callsign: string; reason: string }[];
67
+ };
68
+
69
+ expect(result.failed).toBeUndefined();
70
+ expect(result.dispatched.length).toBeGreaterThan(0);
71
+ });
72
+
73
+ test("concurrent tool calls are serialized — no lost updates", async () => {
74
+ const { ctx } = makeCtx();
75
+
76
+ // Parallel tool calls in one LLM turn run concurrently. The per-session
77
+ // mutex in updateState makes each one run against the previous one's
78
+ // finished state, so neither incident's changes are half-applied when
79
+ // the other's mutator runs.
80
+ const [a, b] = (await Promise.all([
81
+ incidentCreate.execute({ location: "1 First St", description: "gas leak" }, ctx),
82
+ incidentCreate.execute({ location: "2 Second St", description: "vehicle crash" }, ctx),
83
+ ])) as { incidentId: string }[];
84
+
85
+ const state = getState(ctx);
86
+ expect(a?.incidentId).not.toBe(b?.incidentId);
87
+ expect(state.incidentCounter).toBe(2);
88
+ expect(Object.keys(state.incidents)).toHaveLength(2);
89
+ });
90
+
91
+ test("negative casualty counts are rejected by the parameter schemas", () => {
92
+ expect(
93
+ incidentTriage.parameters?.safeParse({ incidentId: "INC-0001", casualtyUpdate: -5 }).success,
94
+ ).toBe(false);
95
+ expect(
96
+ incidentCreate.parameters?.safeParse({
97
+ location: "1 First St",
98
+ description: "fire",
99
+ estimatedCasualties: -1,
100
+ }).success,
101
+ ).toBe(false);
102
+ expect(
103
+ incidentUpdateStatus.parameters?.safeParse({
104
+ incidentId: "INC-0001",
105
+ status: "on_scene",
106
+ casualtyUpdate: { confirmed: -2 },
107
+ }).success,
108
+ ).toBe(false);
109
+ });
110
+
111
+ test("mutual-aid units get unique ids and callsigns across escalations", async () => {
112
+ const { ctx } = makeCtx();
113
+ const incidentId = await createIncidentFor(ctx);
114
+
115
+ await incidentEscalate.execute(
116
+ { incidentId, reason: "spreading", requestMutualAid: true },
117
+ ctx,
118
+ );
119
+ await incidentEscalate.execute(
120
+ { incidentId, reason: "still spreading", requestMutualAid: true },
121
+ ctx,
122
+ );
123
+
124
+ const state = getState(ctx);
125
+ const mutualAid = state.resources.filter((r) => r.id.startsWith("MA-"));
126
+ expect(mutualAid).toHaveLength(4);
127
+ expect(new Set(mutualAid.map((r) => r.id)).size).toBe(4);
128
+ expect(new Set(mutualAid.map((r) => r.callsign)).size).toBe(4);
129
+ });
130
+
131
+ test("resolved is terminal: no escalation, re-resolution, or dispatch", async () => {
132
+ const { ctx } = makeCtx();
133
+ const incidentId = await createIncidentFor(ctx);
134
+ await incidentUpdateStatus.execute({ incidentId, status: "resolved" }, ctx);
135
+
136
+ const escalated = (await incidentEscalate.execute({ incidentId, reason: "flare-up" }, ctx)) as {
137
+ error?: string;
138
+ };
139
+ expect(escalated.error).toMatch(/resolved/);
140
+
141
+ const reResolved = (await incidentUpdateStatus.execute(
142
+ { incidentId, status: "resolved" },
143
+ ctx,
144
+ )) as { error?: string };
145
+ expect(reResolved.error).toMatch(/resolved/);
146
+
147
+ const dispatchedTo = (await resourcesDispatch.execute(
148
+ { incidentId, callsigns: ["Medic-1"] },
149
+ ctx,
150
+ )) as { error?: string };
151
+ expect(dispatchedTo.error).toMatch(/resolved/);
152
+ });
153
+ });
@@ -0,0 +1,50 @@
1
+ import { agent, assemblyAIPipeline } from "@alexkroman1/aai";
2
+ import { assemblyAI } from "@alexkroman1/aai/stt";
3
+ import { dashboardView } from "./shared.ts";
4
+ import systemPrompt from "./system-prompt.md?raw";
5
+ import { incidentAddNote } from "./tools/incident_add_note.ts";
6
+ import { incidentCreate } from "./tools/incident_create.ts";
7
+ import { incidentEscalate } from "./tools/incident_escalate.ts";
8
+ import { incidentGet } from "./tools/incident_get.ts";
9
+ import { incidentTriage } from "./tools/incident_triage.ts";
10
+ import { incidentUpdateStatus } from "./tools/incident_update_status.ts";
11
+ import { opsDashboard } from "./tools/ops_dashboard.ts";
12
+ import { opsProtocols } from "./tools/ops_protocols.ts";
13
+ import { opsRunScenario } from "./tools/ops_run_scenario.ts";
14
+ import { resourcesDispatch } from "./tools/resources_dispatch.ts";
15
+ import { resourcesGetAvailable } from "./tools/resources_get_available.ts";
16
+ import { resourcesUpdateStatus } from "./tools/resources_update_status.ts";
17
+
18
+ export default agent({
19
+ name: "Dispatch Command Center",
20
+ ...assemblyAIPipeline(),
21
+ // One projection replaces eleven `ctx.send("incidents", ...)` calls, and
22
+ // is the single place that decides caller PII stays server-side.
23
+ syncState: dashboardView,
24
+ // Overriding the STT stage: a dispatcher reads addresses and unit numbers
25
+ // in bursts with pauses inside one message, so end-of-turn silence has to
26
+ // be longer than the default or "unit twelve … respond to" splits in two.
27
+ stt: assemblyAI({ minTurnSilenceMs: 2200 }),
28
+ systemPrompt,
29
+ greeting:
30
+ "Dispatch Command Center online. Restoring operational state. I'm ready to take incoming calls, manage active incidents, or run dispatch operations. Say 'dashboard' for a full status report. What do we have.",
31
+
32
+ // The system prompt instructs the model to use web_search and run_code, so
33
+ // they must be enabled here — the default builtin set does not include them.
34
+ builtinTools: ["think", "remember", "recall", "calculate", "web_search", "run_code"],
35
+
36
+ tools: {
37
+ incident_add_note: incidentAddNote,
38
+ incident_create: incidentCreate,
39
+ incident_escalate: incidentEscalate,
40
+ incident_get: incidentGet,
41
+ incident_triage: incidentTriage,
42
+ incident_update_status: incidentUpdateStatus,
43
+ ops_dashboard: opsDashboard,
44
+ ops_protocols: opsProtocols,
45
+ ops_run_scenario: opsRunScenario,
46
+ resources_dispatch: resourcesDispatch,
47
+ resources_get_available: resourcesGetAvailable,
48
+ resources_update_status: resourcesUpdateStatus,
49
+ },
50
+ });