@alexkroman1/aai 0.7.11 → 0.8.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 (92) hide show
  1. package/README.md +1 -1
  2. package/dist/aai.js +1 -1
  3. package/dist/cli.js +693 -349
  4. package/dist/sdk/_internal_types.d.ts +0 -1
  5. package/dist/sdk/_internal_types.d.ts.map +1 -1
  6. package/dist/sdk/_internal_types.js.map +1 -1
  7. package/dist/sdk/_render_check.d.ts +7 -0
  8. package/dist/sdk/_render_check.d.ts.map +1 -0
  9. package/dist/sdk/_render_check.js +38 -0
  10. package/dist/sdk/_render_check.js.map +1 -0
  11. package/dist/sdk/builtin_tools.d.ts.map +1 -1
  12. package/dist/sdk/builtin_tools.js +21 -0
  13. package/dist/sdk/builtin_tools.js.map +1 -1
  14. package/dist/sdk/define_agent.d.ts.map +1 -1
  15. package/dist/sdk/define_agent.js +0 -1
  16. package/dist/sdk/define_agent.js.map +1 -1
  17. package/dist/sdk/direct_executor.d.ts.map +1 -1
  18. package/dist/sdk/direct_executor.js +0 -1
  19. package/dist/sdk/direct_executor.js.map +1 -1
  20. package/dist/sdk/memory_tools.d.ts +38 -0
  21. package/dist/sdk/memory_tools.d.ts.map +1 -0
  22. package/dist/sdk/memory_tools.js +77 -0
  23. package/dist/sdk/memory_tools.js.map +1 -0
  24. package/dist/sdk/mod.d.ts +3 -1
  25. package/dist/sdk/mod.d.ts.map +1 -1
  26. package/dist/sdk/mod.js +2 -0
  27. package/dist/sdk/mod.js.map +1 -1
  28. package/dist/sdk/protocol.d.ts +9 -3
  29. package/dist/sdk/protocol.d.ts.map +1 -1
  30. package/dist/sdk/protocol.js +2 -1
  31. package/dist/sdk/protocol.js.map +1 -1
  32. package/dist/sdk/s2s.d.ts.map +1 -1
  33. package/dist/sdk/s2s.js +9 -3
  34. package/dist/sdk/s2s.js.map +1 -1
  35. package/dist/sdk/session.d.ts.map +1 -1
  36. package/dist/sdk/session.js +5 -0
  37. package/dist/sdk/session.js.map +1 -1
  38. package/dist/sdk/types.d.ts +23 -14
  39. package/dist/sdk/types.d.ts.map +1 -1
  40. package/dist/sdk/types.js +29 -0
  41. package/dist/sdk/types.js.map +1 -1
  42. package/dist/ui/_components/app.d.ts.map +1 -1
  43. package/dist/ui/_components/app.js +6 -7
  44. package/dist/ui/_components/app.js.map +1 -1
  45. package/dist/ui/_components/message_list.d.ts.map +1 -1
  46. package/dist/ui/_components/message_list.js +2 -1
  47. package/dist/ui/_components/message_list.js.map +1 -1
  48. package/dist/ui/_components/sidebar_layout.d.ts +19 -0
  49. package/dist/ui/_components/sidebar_layout.d.ts.map +1 -0
  50. package/dist/ui/_components/sidebar_layout.js +21 -0
  51. package/dist/ui/_components/sidebar_layout.js.map +1 -0
  52. package/dist/ui/_components/start_screen.d.ts +24 -0
  53. package/dist/ui/_components/start_screen.d.ts.map +1 -0
  54. package/dist/ui/_components/start_screen.js +25 -0
  55. package/dist/ui/_components/start_screen.js.map +1 -0
  56. package/dist/ui/_hooks.d.ts +21 -0
  57. package/dist/ui/_hooks.d.ts.map +1 -0
  58. package/dist/ui/_hooks.js +35 -0
  59. package/dist/ui/_hooks.js.map +1 -0
  60. package/dist/ui/components.d.ts +20 -0
  61. package/dist/ui/components.d.ts.map +1 -1
  62. package/dist/ui/components.js +12 -0
  63. package/dist/ui/components.js.map +1 -1
  64. package/dist/ui/components_mod.d.ts +3 -2
  65. package/dist/ui/components_mod.d.ts.map +1 -1
  66. package/dist/ui/components_mod.js +3 -2
  67. package/dist/ui/components_mod.js.map +1 -1
  68. package/dist/ui/mod.d.ts +4 -3
  69. package/dist/ui/mod.d.ts.map +1 -1
  70. package/dist/ui/mod.js +3 -2
  71. package/dist/ui/mod.js.map +1 -1
  72. package/dist/ui/session.d.ts +7 -0
  73. package/dist/ui/session.d.ts.map +1 -1
  74. package/dist/ui/session.js +21 -3
  75. package/dist/ui/session.js.map +1 -1
  76. package/dist/ui/signals.d.ts +14 -0
  77. package/dist/ui/signals.d.ts.map +1 -1
  78. package/dist/ui/signals.js +55 -3
  79. package/dist/ui/signals.js.map +1 -1
  80. package/package.json +19 -2
  81. package/templates/_shared/CLAUDE.md +305 -116
  82. package/templates/_shared/package.json +4 -1
  83. package/templates/dispatch-center/agent.ts +43 -72
  84. package/templates/embedded-assets/agent.ts +4 -5
  85. package/templates/health-assistant/agent.ts +7 -7
  86. package/templates/infocom-adventure/agent.ts +20 -20
  87. package/templates/memory-agent/agent.ts +1 -55
  88. package/templates/night-owl/agent.ts +4 -4
  89. package/templates/night-owl/client.tsx +6 -23
  90. package/templates/pizza-ordering/agent.ts +214 -0
  91. package/templates/pizza-ordering/client.tsx +264 -0
  92. package/templates/smart-research/agent.ts +10 -10
@@ -1,4 +1,4 @@
1
- import { defineAgent } from "@alexkroman1/aai";
1
+ import { defineAgent, tool } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
3
  import type { HookContext, ToolContext } from "@alexkroman1/aai";
4
4
 
@@ -619,7 +619,7 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
619
619
  },
620
620
 
621
621
  tools: {
622
- incident_create: {
622
+ incident_create: tool({
623
623
  description: "Create a new incident from an incoming emergency call.",
624
624
  parameters: z.object({
625
625
  location: z.string().describe("Address or location description"),
@@ -643,17 +643,10 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
643
643
  callerPhone,
644
644
  estimatedCasualties,
645
645
  hazards,
646
- }: {
647
- location: string;
648
- description: string;
649
- callerName?: string;
650
- callerPhone?: string;
651
- estimatedCasualties?: number;
652
- hazards?: string[];
653
646
  },
654
647
  ctx,
655
648
  ) => {
656
- const state = ctx.state;
649
+ const state = ctx.state as DispatchState;
657
650
  state.incidentCounter++;
658
651
  const id = `INC-${String(state.incidentCounter).padStart(4, "0")}`;
659
652
 
@@ -722,9 +715,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
722
715
  : `${id} created. Triage score ${triageScore}. ${recommended.length} resource(s) recommended.`,
723
716
  };
724
717
  },
725
- },
718
+ }),
726
719
 
727
- incident_triage: {
720
+ incident_triage: tool({
728
721
  description:
729
722
  "Triage an incident — confirm or override severity, type, hazards, and casualty count.",
730
723
  parameters: z.object({
@@ -756,17 +749,10 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
756
749
  additionalHazards,
757
750
  casualtyUpdate,
758
751
  notes,
759
- }: {
760
- incidentId: string;
761
- severity?: "critical" | "urgent" | "moderate" | "minor";
762
- type?: "medical" | "fire" | "hazmat" | "traffic" | "crime" | "natural_disaster" | "utility" | "other";
763
- additionalHazards?: string[];
764
- casualtyUpdate?: number;
765
- notes?: string;
766
752
  },
767
753
  ctx,
768
754
  ) => {
769
- const state = ctx.state;
755
+ const state = ctx.state as DispatchState;
770
756
  const inc = state.incidents[incidentId];
771
757
  if (!inc) return { error: `Incident ${incidentId} not found` };
772
758
 
@@ -821,9 +807,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
821
807
  systemAlertLevel: state.alertLevel,
822
808
  };
823
809
  },
824
- },
810
+ }),
825
811
 
826
- incident_update_status: {
812
+ incident_update_status: tool({
827
813
  description:
828
814
  "Update an incident's status (en_route, on_scene, resolved, escalated).",
829
815
  parameters: z.object({
@@ -837,15 +823,10 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
837
823
  }).describe("Updated casualty numbers").optional(),
838
824
  }),
839
825
  execute: async (
840
- { incidentId, status, notes, casualtyUpdate }: {
841
- incidentId: string;
842
- status: "en_route" | "on_scene" | "resolved" | "escalated";
843
- notes?: string;
844
- casualtyUpdate?: { confirmed?: number; treated?: number };
845
- },
826
+ { incidentId, status, notes, casualtyUpdate },
846
827
  ctx,
847
828
  ) => {
848
- const state = ctx.state;
829
+ const state = ctx.state as DispatchState;
849
830
  const inc = state.incidents[incidentId];
850
831
  if (!inc) return { error: `Incident ${incidentId} not found` };
851
832
 
@@ -905,9 +886,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
905
886
  systemAlertLevel: state.alertLevel,
906
887
  };
907
888
  },
908
- },
889
+ }),
909
890
 
910
- incident_escalate: {
891
+ incident_escalate: tool({
911
892
  description:
912
893
  "Escalate an incident when it exceeds current capacity or severity increases.",
913
894
  parameters: z.object({
@@ -921,15 +902,10 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
921
902
  ).optional(),
922
903
  }),
923
904
  execute: async (
924
- { incidentId, reason, requestMutualAid, newSeverity }: {
925
- incidentId: string;
926
- reason: string;
927
- requestMutualAid?: boolean;
928
- newSeverity?: "critical" | "urgent";
929
- },
905
+ { incidentId, reason, requestMutualAid, newSeverity },
930
906
  ctx,
931
907
  ) => {
932
- const state = ctx.state;
908
+ const state = ctx.state as DispatchState;
933
909
  const inc = state.incidents[incidentId];
934
910
  if (!inc) return { error: `Incident ${incidentId} not found` };
935
911
 
@@ -1004,16 +980,16 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1004
980
  `ESCALATION CONFIRMED — ${incidentId} now Level ${inc.escalationLevel}. ${additionalResources.length} additional resource(s) available for dispatch.`,
1005
981
  };
1006
982
  },
1007
- },
983
+ }),
1008
984
 
1009
- incident_get: {
985
+ incident_get: tool({
1010
986
  description:
1011
987
  "Get full details on a specific incident including timeline and assigned resources.",
1012
988
  parameters: z.object({
1013
989
  incidentId: z.string().describe("The incident ID"),
1014
990
  }),
1015
- execute: ({ incidentId }: { incidentId: string }, ctx) => {
1016
- const state = ctx.state;
991
+ execute: ({ incidentId }, ctx) => {
992
+ const state = ctx.state as DispatchState;
1017
993
  const inc = state.incidents[incidentId];
1018
994
  if (!inc) return { error: `Incident ${incidentId} not found` };
1019
995
 
@@ -1044,9 +1020,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1044
1020
  .map((p) => p.name),
1045
1021
  };
1046
1022
  },
1047
- },
1023
+ }),
1048
1024
 
1049
- incident_add_note: {
1025
+ incident_add_note: tool({
1050
1026
  description: "Add a situational update note to an incident.",
1051
1027
  parameters: z.object({
1052
1028
  incidentId: z.string().describe("The incident ID"),
@@ -1055,8 +1031,8 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1055
1031
  "Who reported this — unit callsign or caller",
1056
1032
  ).optional(),
1057
1033
  }),
1058
- execute: async ({ incidentId, note, source }: { incidentId: string; note: string; source?: string }, ctx) => {
1059
- const state = ctx.state;
1034
+ execute: async ({ incidentId, note, source }, ctx) => {
1035
+ const state = ctx.state as DispatchState;
1060
1036
  const inc = state.incidents[incidentId];
1061
1037
  if (!inc) return { error: `Incident ${incidentId} not found` };
1062
1038
 
@@ -1072,9 +1048,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1072
1048
  totalNotes: inc.notes.length,
1073
1049
  };
1074
1050
  },
1075
- },
1051
+ }),
1076
1052
 
1077
- resources_dispatch: {
1053
+ resources_dispatch: tool({
1078
1054
  description:
1079
1055
  "Dispatch units to an incident. Can auto-dispatch recommended resources or manually specify callsigns.",
1080
1056
  parameters: z.object({
@@ -1090,15 +1066,10 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1090
1066
  ).optional(),
1091
1067
  }),
1092
1068
  execute: async (
1093
- { incidentId, callsigns, autoDispatch, priority }: {
1094
- incidentId: string;
1095
- callsigns?: string[];
1096
- autoDispatch?: boolean;
1097
- priority?: "routine" | "priority" | "emergency";
1098
- },
1069
+ { incidentId, callsigns, autoDispatch, priority },
1099
1070
  ctx,
1100
1071
  ) => {
1101
- const state = ctx.state;
1072
+ const state = ctx.state as DispatchState;
1102
1073
  const inc = state.incidents[incidentId];
1103
1074
  if (!inc) return { error: `Incident ${incidentId} not found` };
1104
1075
 
@@ -1180,9 +1151,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1180
1151
  : undefined,
1181
1152
  };
1182
1153
  },
1183
- },
1154
+ }),
1184
1155
 
1185
- resources_get_available: {
1156
+ resources_get_available: tool({
1186
1157
  description: "List available resources, optionally filtered by type.",
1187
1158
  parameters: z.object({
1188
1159
  type: z.enum([
@@ -1197,8 +1168,8 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1197
1168
  "all",
1198
1169
  ]).describe("Filter by resource type, or 'all'").optional(),
1199
1170
  }),
1200
- execute: ({ type }: { type?: "ambulance" | "fire_engine" | "police" | "hazmat_team" | "helicopter" | "k9_unit" | "swat" | "ems_supervisor" | "all" }, ctx) => {
1201
- const state = ctx.state;
1171
+ execute: ({ type }, ctx) => {
1172
+ const state = ctx.state as DispatchState;
1202
1173
  let resources = state.resources;
1203
1174
  if (type && type !== "all") {
1204
1175
  resources = resources.filter((r) => r.type === type);
@@ -1222,9 +1193,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1222
1193
  },
1223
1194
  };
1224
1195
  },
1225
- },
1196
+ }),
1226
1197
 
1227
- resources_update_status: {
1198
+ resources_update_status: tool({
1228
1199
  description: "Update a resource unit's status when it radios in.",
1229
1200
  parameters: z.object({
1230
1201
  callsign: z.string().describe("The resource callsign"),
@@ -1237,8 +1208,8 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1237
1208
  ]).describe("New status"),
1238
1209
  notes: z.string().describe("Status notes").optional(),
1239
1210
  }),
1240
- execute: async ({ callsign, status, notes }: { callsign: string; status: "available" | "dispatched" | "en_route" | "on_scene" | "returning"; notes?: string }, ctx) => {
1241
- const state = ctx.state;
1211
+ execute: async ({ callsign, status, notes }, ctx) => {
1212
+ const state = ctx.state as DispatchState;
1242
1213
  const resource = state.resources.find((r) =>
1243
1214
  r.callsign.toLowerCase() === callsign.toLowerCase()
1244
1215
  );
@@ -1279,13 +1250,13 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1279
1250
  systemAlertLevel: state.alertLevel,
1280
1251
  };
1281
1252
  },
1282
- },
1253
+ }),
1283
1254
 
1284
1255
  ops_dashboard: {
1285
1256
  description:
1286
1257
  "Get the full operational dashboard: alert level, resource utilization, active incidents, and available resources.",
1287
1258
  execute: (_args, ctx) => {
1288
- const state = ctx.state;
1259
+ const state = ctx.state as DispatchState;
1289
1260
 
1290
1261
  const activeIncidents = Object.values(state.incidents)
1291
1262
  .filter((i) => i.status !== "resolved")
@@ -1342,7 +1313,7 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1342
1313
  },
1343
1314
  },
1344
1315
 
1345
- ops_protocols: {
1316
+ ops_protocols: tool({
1346
1317
  description:
1347
1318
  "Look up step-by-step response protocols for a given incident type and severity.",
1348
1319
  parameters: z.object({
@@ -1359,7 +1330,7 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1359
1330
  severity: z.enum(["critical", "urgent", "moderate", "minor"])
1360
1331
  .describe("Severity level"),
1361
1332
  }),
1362
- execute: ({ incidentType, severity }: { incidentType: IncidentType; severity: Severity }) => {
1333
+ execute: ({ incidentType, severity }) => {
1363
1334
  const protocols = getApplicableProtocols(
1364
1335
  incidentType,
1365
1336
  severity,
@@ -1379,9 +1350,9 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1379
1350
  })),
1380
1351
  };
1381
1352
  },
1382
- },
1353
+ }),
1383
1354
 
1384
- ops_run_scenario: {
1355
+ ops_run_scenario: tool({
1385
1356
  description:
1386
1357
  "Run a training scenario that creates simulated incidents for dispatch practice.",
1387
1358
  parameters: z.object({
@@ -1393,8 +1364,8 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1393
1364
  "highway_pileup",
1394
1365
  ]).describe("Scenario type to simulate"),
1395
1366
  }),
1396
- execute: async ({ scenario }: { scenario: string }, ctx) => {
1397
- const state = ctx.state;
1367
+ execute: async ({ scenario }, ctx) => {
1368
+ const state = ctx.state as DispatchState;
1398
1369
  const scenarios: Record<
1399
1370
  string,
1400
1371
  { incidents: Partial<Incident>[]; narrative: string }
@@ -1559,6 +1530,6 @@ Radio style: "Medic-1, respond priority one to 400 Oak Street, report of cardiac
1559
1530
  `SCENARIO ACTIVE: ${s.narrative}. ${created.length} incidents created. Awaiting dispatch orders.`,
1560
1531
  };
1561
1532
  },
1562
- },
1533
+ }),
1563
1534
  },
1564
1535
  });
@@ -1,4 +1,4 @@
1
- import { defineAgent } from "@alexkroman1/aai";
1
+ import { defineAgent, tool } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
3
  import knowledge from "./knowledge.json" with { type: "json" };
4
4
 
@@ -22,15 +22,14 @@ Rules:
22
22
  - Always be helpful and polite`,
23
23
  greeting:
24
24
  "Hi! I'm your FAQ assistant. Ask me anything about the AAI agent framework and I'll look it up in my knowledge base.",
25
- voice: "156fb8d2-335b-4950-9cb3-a2d33befec77", // Helpful Woman
26
25
  tools: {
27
- search_knowledge: {
26
+ search_knowledge: tool({
28
27
  description:
29
28
  "Search the embedded FAQ knowledge base for an answer matching the user's question.",
30
29
  parameters: z.object({
31
30
  query: z.string().describe("The user's question to search for"),
32
31
  }),
33
- execute: ({ query }: { query: string }) => {
32
+ execute: ({ query }) => {
34
33
  const q = query.toLowerCase();
35
34
  const match = faqs.find((f) =>
36
35
  f.question.toLowerCase().includes(q) ||
@@ -39,7 +38,7 @@ Rules:
39
38
  );
40
39
  return match ?? { result: "No matching FAQ found." };
41
40
  },
42
- },
41
+ }),
43
42
  list_topics: {
44
43
  description:
45
44
  "List all available topics in the embedded FAQ knowledge base.",
@@ -1,4 +1,4 @@
1
- import { defineAgent } from "@alexkroman1/aai";
1
+ import { defineAgent, tool } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
3
 
4
4
  function first(field: unknown): string | undefined {
@@ -136,7 +136,7 @@ Use run_code for health calculations:
136
136
  "Hey, I'm Dr. Sage. Try asking me something like, what are the side effects of ibuprofen, can I take aspirin and warfarin together, or calculate my BMI. Just remember, I'm not a real doctor, so always check with your healthcare provider.",
137
137
  builtinTools: ["web_search", "run_code"],
138
138
  tools: {
139
- medication_lookup: {
139
+ medication_lookup: tool({
140
140
  description:
141
141
  "Look up detailed information about a single medication, including purpose, warnings, dosage, side effects, and manufacturer. Works with both generic and brand names.",
142
142
  parameters: z.object({
@@ -144,9 +144,9 @@ Use run_code for health calculations:
144
144
  "Medication name (generic or brand, e.g. 'ibuprofen' or 'Advil')",
145
145
  ),
146
146
  }),
147
- execute: ({ name }: { name: string }) => lookupDrug(name),
148
- },
149
- check_drug_interaction: {
147
+ execute: ({ name }) => lookupDrug(name),
148
+ }),
149
+ check_drug_interaction: tool({
150
150
  description:
151
151
  "Check for known interactions between two or more medications. Resolves drug names via RxNorm and returns interaction details with severity levels.",
152
152
  parameters: z.object({
@@ -154,7 +154,7 @@ Use run_code for health calculations:
154
154
  "Comma-separated medication names (e.g. 'ibuprofen, warfarin')",
155
155
  ),
156
156
  }),
157
- execute: ({ drugs }: { drugs: string }) => checkInteractions(drugs),
158
- },
157
+ execute: ({ drugs }) => checkInteractions(drugs),
158
+ }),
159
159
  },
160
160
  });
@@ -1,6 +1,6 @@
1
- import { defineAgent } from "@alexkroman1/aai";
2
- import { z } from "zod";
1
+ import { defineAgent, tool } from "@alexkroman1/aai";
3
2
  import type { ToolContext } from "@alexkroman1/aai";
3
+ import { z } from "zod";
4
4
 
5
5
  type GameState = {
6
6
  inventory: string[];
@@ -89,76 +89,76 @@ ATMOSPHERE:
89
89
  };
90
90
  },
91
91
  },
92
- game_state_move: {
92
+ game_state_move: tool({
93
93
  description:
94
94
  "Move the player to a new room and increment the move counter.",
95
95
  parameters: z.object({
96
96
  value: z.string().describe("Room name to move to"),
97
97
  }),
98
- execute: ({ value }: { value: string }, ctx) => {
98
+ execute: ({ value }, ctx) => {
99
99
  const g = s(ctx);
100
100
  g.currentRoom = value;
101
101
  g.moves++;
102
102
  return { currentRoom: g.currentRoom, moves: g.moves };
103
103
  },
104
- },
105
- game_state_take: {
104
+ }),
105
+ game_state_take: tool({
106
106
  description: "Add an item to the player's inventory.",
107
107
  parameters: z.object({
108
108
  value: z.string().describe("Item name to take"),
109
109
  }),
110
- execute: ({ value }: { value: string }, ctx) => {
110
+ execute: ({ value }, ctx) => {
111
111
  const g = s(ctx);
112
112
  if (!g.inventory.includes(value)) g.inventory.push(value);
113
113
  return { inventory: g.inventory };
114
114
  },
115
- },
116
- game_state_drop: {
115
+ }),
116
+ game_state_drop: tool({
117
117
  description: "Remove an item from the player's inventory.",
118
118
  parameters: z.object({
119
119
  value: z.string().describe("Item name to drop"),
120
120
  }),
121
- execute: ({ value }: { value: string }, ctx) => {
121
+ execute: ({ value }, ctx) => {
122
122
  const g = s(ctx);
123
123
  g.inventory = g.inventory.filter((i) => i !== value);
124
124
  return { inventory: g.inventory };
125
125
  },
126
- },
127
- game_state_score: {
126
+ }),
127
+ game_state_score: tool({
128
128
  description: "Add points to the player's score.",
129
129
  parameters: z.object({
130
130
  value: z.number().describe("Points to add"),
131
131
  }),
132
- execute: ({ value }: { value: number }, ctx) => {
132
+ execute: ({ value }, ctx) => {
133
133
  const g = s(ctx);
134
134
  g.score += value;
135
135
  return { score: g.score };
136
136
  },
137
- },
138
- game_state_flag: {
137
+ }),
138
+ game_state_flag: tool({
139
139
  description:
140
140
  "Set a game flag to true, used for tracking puzzle and event state.",
141
141
  parameters: z.object({
142
142
  value: z.string().describe("Flag name to set"),
143
143
  }),
144
- execute: ({ value }: { value: string }, ctx) => {
144
+ execute: ({ value }, ctx) => {
145
145
  const g = s(ctx);
146
146
  g.flags[value] = true;
147
147
  return { flags: g.flags };
148
148
  },
149
- },
150
- game_state_history: {
149
+ }),
150
+ game_state_history: tool({
151
151
  description:
152
152
  "Log a player command to the history and increment the move counter.",
153
153
  parameters: z.object({
154
154
  value: z.string().describe("Command text to log"),
155
155
  }),
156
- execute: ({ value }: { value: string }, ctx) => {
156
+ execute: ({ value }, ctx) => {
157
157
  const g = s(ctx);
158
158
  g.history.push(value);
159
159
  g.moves++;
160
160
  return { moves: g.moves, recentHistory: g.history.slice(-5) };
161
161
  },
162
- },
162
+ }),
163
163
  },
164
164
  });
@@ -1,5 +1,4 @@
1
1
  import { defineAgent } from "@alexkroman1/aai";
2
- import { z } from "zod";
3
2
 
4
3
  export default defineAgent({
5
4
  name: "Memory Agent",
@@ -17,58 +16,5 @@ Keep responses concise and conversational. Never say "I saved that to my \
17
16
  database" — just confirm naturally, like "Got it, I'll remember that."`,
18
17
  greeting:
19
18
  "Hey there. I'm an assistant with a long-term memory. Tell me things you want me to remember, and I'll recall them in future conversations.",
20
- builtinTools: ["web_search"],
21
- tools: {
22
- save_memory: {
23
- description:
24
- "Save a piece of information to persistent memory. Use a descriptive key like 'user:name' or 'project:status'.",
25
- parameters: z.object({
26
- key: z.string().describe(
27
- "A descriptive key for this memory (e.g. 'user:name', 'preference:color')",
28
- ),
29
- value: z.string().describe("The information to remember"),
30
- }),
31
- execute: async (
32
- { key, value }: { key: string; value: string },
33
- ctx,
34
- ) => {
35
- await ctx.kv.set(key, value);
36
- return { saved: key };
37
- },
38
- },
39
- recall_memory: {
40
- description: "Retrieve a previously saved memory by its key.",
41
- parameters: z.object({
42
- key: z.string().describe("The key to look up"),
43
- }),
44
- execute: async ({ key }: { key: string }, ctx) => {
45
- const value = await ctx.kv.get(key);
46
- if (value === null) return { found: false, key };
47
- return { found: true, key, value };
48
- },
49
- },
50
- list_memories: {
51
- description:
52
- "List all saved memory keys, optionally filtered by a prefix (e.g. 'user:').",
53
- parameters: z.object({
54
- prefix: z.string().describe(
55
- "Prefix to filter keys (e.g. 'user:'). Use empty string for all.",
56
- ).optional(),
57
- }),
58
- execute: async ({ prefix }: { prefix?: string }, ctx) => {
59
- const entries = await ctx.kv.list(prefix ?? "");
60
- return { count: entries.length, keys: entries.map((e) => e.key) };
61
- },
62
- },
63
- forget_memory: {
64
- description: "Delete a previously saved memory by its key.",
65
- parameters: z.object({
66
- key: z.string().describe("The key to delete"),
67
- }),
68
- execute: async ({ key }: { key: string }, ctx) => {
69
- await ctx.kv.delete(key);
70
- return { deleted: key };
71
- },
72
- },
73
- },
19
+ builtinTools: ["web_search", "memory"],
74
20
  });
@@ -1,4 +1,4 @@
1
- import { defineAgent } from "@alexkroman1/aai";
1
+ import { defineAgent, tool } from "@alexkroman1/aai";
2
2
  import { z } from "zod";
3
3
 
4
4
  const PICKS: Record<string, Record<string, string[]>> = {
@@ -79,20 +79,20 @@ Use run_code for sleep calculations:
79
79
  "Hey there, night owl. Try asking me for a cozy movie recommendation, or tell me what time you need to wake up and I'll calculate the best time to fall asleep.",
80
80
  builtinTools: ["run_code"],
81
81
  tools: {
82
- recommend: {
82
+ recommend: tool({
83
83
  description:
84
84
  "Get recommendations for movies, music, or books based on mood.",
85
85
  parameters: z.object({
86
86
  category: z.enum(["movie", "music", "book"]),
87
87
  mood: z.enum(["chill", "intense", "cozy", "spooky", "funny"]),
88
88
  }),
89
- execute: ({ category, mood }: { category: string; mood: string }) => {
89
+ execute: ({ category, mood }) => {
90
90
  return {
91
91
  category,
92
92
  mood,
93
93
  picks: PICKS[category]?.[mood] ?? [],
94
94
  };
95
95
  },
96
- },
96
+ }),
97
97
  },
98
98
  });
@@ -1,29 +1,12 @@
1
1
  import "@alexkroman1/aai/ui/styles.css";
2
- import { ChatView, mount, useSession } from "@alexkroman1/aai/ui";
2
+ import { ChatView, StartScreen, mount } from "@alexkroman1/aai/ui";
3
3
 
4
4
  function NightOwl() {
5
- const { started, start } = useSession();
6
-
7
- if (!started.value) {
8
- return (
9
- <div class="flex items-center justify-center h-screen bg-aai-bg font-aai">
10
- <div class="flex flex-col items-center gap-6 bg-aai-surface border border-aai-border rounded-lg px-12 py-10">
11
- <div class="text-5xl">&#x1F989;</div>
12
- <h1 class="text-xl font-semibold text-aai-text m-0">Night Owl</h1>
13
- <p class="text-sm text-aai-text-muted m-0">your evening companion</p>
14
- <button
15
- type="button"
16
- class="mt-2 px-8 py-3 rounded-aai text-sm font-medium cursor-pointer bg-aai-primary text-white border-none"
17
- onClick={start}
18
- >
19
- Start Conversation
20
- </button>
21
- </div>
22
- </div>
23
- );
24
- }
25
-
26
- return <ChatView />;
5
+ return (
6
+ <StartScreen icon={<span>&#x1F989;</span>} title="Night Owl" subtitle="your evening companion" buttonText="Start Conversation">
7
+ <ChatView />
8
+ </StartScreen>
9
+ );
27
10
  }
28
11
 
29
12
  mount(NightOwl);