@falai/agent 0.1.3 → 0.1.5

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 (160) hide show
  1. package/README.md +56 -1
  2. package/dist/cjs/constants/index.d.ts +5 -0
  3. package/dist/cjs/constants/index.d.ts.map +1 -0
  4. package/dist/cjs/constants/index.js +8 -0
  5. package/dist/cjs/constants/index.js.map +1 -0
  6. package/dist/cjs/core/Agent.d.ts +110 -0
  7. package/dist/cjs/core/Agent.d.ts.map +1 -0
  8. package/dist/cjs/core/Agent.js +290 -0
  9. package/dist/cjs/core/Agent.js.map +1 -0
  10. package/dist/cjs/core/DomainRegistry.d.ts +26 -0
  11. package/dist/cjs/core/DomainRegistry.d.ts.map +1 -0
  12. package/dist/cjs/core/DomainRegistry.js +47 -0
  13. package/dist/cjs/core/DomainRegistry.js.map +1 -0
  14. package/dist/cjs/core/Events.d.ts +19 -0
  15. package/dist/cjs/core/Events.d.ts.map +1 -0
  16. package/dist/cjs/core/Events.js +84 -0
  17. package/dist/cjs/core/Events.js.map +1 -0
  18. package/dist/cjs/core/Observation.d.ts +24 -0
  19. package/dist/cjs/core/Observation.d.ts.map +1 -0
  20. package/dist/cjs/core/Observation.js +39 -0
  21. package/dist/cjs/core/Observation.js.map +1 -0
  22. package/dist/cjs/core/PromptBuilder.d.ts +125 -0
  23. package/dist/cjs/core/PromptBuilder.d.ts.map +1 -0
  24. package/dist/cjs/core/PromptBuilder.js +374 -0
  25. package/dist/cjs/core/PromptBuilder.js.map +1 -0
  26. package/dist/cjs/core/Route.d.ts +46 -0
  27. package/dist/cjs/core/Route.d.ts.map +1 -0
  28. package/dist/cjs/core/Route.js +111 -0
  29. package/dist/cjs/core/Route.js.map +1 -0
  30. package/dist/cjs/core/State.d.ts +50 -0
  31. package/dist/cjs/core/State.d.ts.map +1 -0
  32. package/dist/cjs/core/State.js +112 -0
  33. package/dist/cjs/core/State.js.map +1 -0
  34. package/dist/cjs/core/Tool.d.ts +32 -0
  35. package/dist/cjs/core/Tool.d.ts.map +1 -0
  36. package/dist/cjs/core/Tool.js +37 -0
  37. package/dist/cjs/core/Tool.js.map +1 -0
  38. package/dist/cjs/core/Transition.d.ts +32 -0
  39. package/dist/cjs/core/Transition.d.ts.map +1 -0
  40. package/dist/cjs/core/Transition.js +59 -0
  41. package/dist/cjs/core/Transition.js.map +1 -0
  42. package/dist/cjs/index.d.ts +35 -0
  43. package/dist/cjs/index.d.ts.map +1 -0
  44. package/dist/cjs/index.js +55 -0
  45. package/dist/cjs/index.js.map +1 -0
  46. package/dist/cjs/package.json +1 -0
  47. package/dist/cjs/providers/GeminiProvider.d.ts +40 -0
  48. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -0
  49. package/dist/cjs/providers/GeminiProvider.js +142 -0
  50. package/dist/cjs/providers/GeminiProvider.js.map +1 -0
  51. package/dist/cjs/providers/OpenAIProvider.d.ts +42 -0
  52. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -0
  53. package/dist/cjs/providers/OpenAIProvider.js +242 -0
  54. package/dist/cjs/providers/OpenAIProvider.js.map +1 -0
  55. package/dist/cjs/providers/OpenRouterProvider.d.ts +46 -0
  56. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -0
  57. package/dist/cjs/providers/OpenRouterProvider.js +249 -0
  58. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -0
  59. package/dist/cjs/types/agent.d.ts +105 -0
  60. package/dist/cjs/types/agent.d.ts.map +1 -0
  61. package/dist/cjs/types/agent.js +21 -0
  62. package/dist/cjs/types/agent.js.map +1 -0
  63. package/dist/cjs/types/ai.d.ts +102 -0
  64. package/dist/cjs/types/ai.d.ts.map +1 -0
  65. package/dist/cjs/types/ai.js +6 -0
  66. package/dist/cjs/types/ai.js.map +1 -0
  67. package/dist/cjs/types/history.d.ts +112 -0
  68. package/dist/cjs/types/history.d.ts.map +1 -0
  69. package/dist/cjs/types/history.js +37 -0
  70. package/dist/cjs/types/history.js.map +1 -0
  71. package/dist/cjs/types/index.d.ts +14 -0
  72. package/dist/cjs/types/index.d.ts.map +1 -0
  73. package/dist/cjs/types/index.js +14 -0
  74. package/dist/cjs/types/index.js.map +1 -0
  75. package/dist/cjs/types/observation.d.ts +27 -0
  76. package/dist/cjs/types/observation.d.ts.map +1 -0
  77. package/dist/cjs/types/observation.js +6 -0
  78. package/dist/cjs/types/observation.js.map +1 -0
  79. package/dist/cjs/types/prompt.d.ts +46 -0
  80. package/dist/cjs/types/prompt.d.ts.map +1 -0
  81. package/dist/cjs/types/prompt.js +19 -0
  82. package/dist/cjs/types/prompt.js.map +1 -0
  83. package/dist/cjs/types/route.d.ts +61 -0
  84. package/dist/cjs/types/route.d.ts.map +1 -0
  85. package/dist/cjs/types/route.js +6 -0
  86. package/dist/cjs/types/route.js.map +1 -0
  87. package/dist/cjs/types/tool.d.ts +46 -0
  88. package/dist/cjs/types/tool.d.ts.map +1 -0
  89. package/dist/cjs/types/tool.js +6 -0
  90. package/dist/cjs/types/tool.js.map +1 -0
  91. package/dist/cjs/utils/id.d.ts +25 -0
  92. package/dist/cjs/utils/id.d.ts.map +1 -0
  93. package/dist/cjs/utils/id.js +71 -0
  94. package/dist/cjs/utils/id.js.map +1 -0
  95. package/dist/cjs/utils/retry.d.ts +13 -0
  96. package/dist/cjs/utils/retry.d.ts.map +1 -0
  97. package/dist/cjs/utils/retry.js +75 -0
  98. package/dist/cjs/utils/retry.js.map +1 -0
  99. package/dist/core/Agent.js +10 -11
  100. package/dist/core/Agent.js.map +1 -1
  101. package/dist/core/DomainRegistry.js +3 -1
  102. package/dist/core/DomainRegistry.js.map +1 -1
  103. package/dist/core/Events.d.ts +2 -2
  104. package/dist/core/Events.d.ts.map +1 -1
  105. package/dist/core/Events.js +4 -4
  106. package/dist/core/Events.js.map +1 -1
  107. package/dist/core/Observation.d.ts.map +1 -1
  108. package/dist/core/Observation.js +4 -5
  109. package/dist/core/Observation.js.map +1 -1
  110. package/dist/core/PromptBuilder.js +2 -3
  111. package/dist/core/PromptBuilder.js.map +1 -1
  112. package/dist/core/Route.d.ts.map +1 -1
  113. package/dist/core/Route.js +4 -10
  114. package/dist/core/Route.js.map +1 -1
  115. package/dist/core/State.d.ts +1 -1
  116. package/dist/core/State.d.ts.map +1 -1
  117. package/dist/core/State.js +6 -8
  118. package/dist/core/State.js.map +1 -1
  119. package/dist/core/Tool.d.ts +1 -0
  120. package/dist/core/Tool.d.ts.map +1 -1
  121. package/dist/core/Tool.js +3 -2
  122. package/dist/core/Tool.js.map +1 -1
  123. package/dist/core/Transition.js +0 -4
  124. package/dist/core/Transition.js.map +1 -1
  125. package/dist/index.d.ts +1 -0
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +2 -0
  128. package/dist/index.js.map +1 -1
  129. package/dist/providers/GeminiProvider.js +1 -6
  130. package/dist/providers/GeminiProvider.js.map +1 -1
  131. package/dist/providers/OpenAIProvider.js +1 -6
  132. package/dist/providers/OpenAIProvider.js.map +1 -1
  133. package/dist/providers/OpenRouterProvider.js +1 -6
  134. package/dist/providers/OpenRouterProvider.js.map +1 -1
  135. package/dist/types/agent.d.ts +2 -2
  136. package/dist/types/agent.d.ts.map +1 -1
  137. package/dist/types/observation.d.ts +2 -0
  138. package/dist/types/observation.d.ts.map +1 -1
  139. package/dist/types/route.d.ts +2 -0
  140. package/dist/types/route.d.ts.map +1 -1
  141. package/dist/utils/id.d.ts +25 -0
  142. package/dist/utils/id.d.ts.map +1 -0
  143. package/dist/utils/id.js +65 -0
  144. package/dist/utils/id.js.map +1 -0
  145. package/docs/API_REFERENCE.md +122 -6
  146. package/docs/CONSTRUCTOR_OPTIONS.md +43 -36
  147. package/docs/GETTING_STARTED.md +2 -0
  148. package/docs/PROVIDERS.md +3 -0
  149. package/examples/declarative-agent.ts +31 -7
  150. package/package.json +15 -5
  151. package/src/core/Events.ts +6 -4
  152. package/src/core/Observation.ts +3 -3
  153. package/src/core/Route.ts +3 -5
  154. package/src/core/State.ts +5 -4
  155. package/src/core/Tool.ts +4 -3
  156. package/src/index.ts +8 -0
  157. package/src/types/agent.ts +4 -2
  158. package/src/types/observation.ts +2 -0
  159. package/src/types/route.ts +2 -0
  160. package/src/utils/id.ts +74 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ID generation utilities
3
+ * Provides deterministic ID generation to ensure consistency across server restarts
4
+ */
5
+ /**
6
+ * Generate a deterministic ID from a string by creating a simple hash
7
+ * This ensures the same input always produces the same ID
8
+ */
9
+ function simpleHash(str) {
10
+ let hash = 0;
11
+ for (let i = 0; i < str.length; i++) {
12
+ const char = str.charCodeAt(i);
13
+ hash = (hash << 5) - hash + char;
14
+ hash = hash & hash; // Convert to 32-bit integer
15
+ }
16
+ return Math.abs(hash).toString(36);
17
+ }
18
+ /**
19
+ * Sanitize a string for use in an ID
20
+ */
21
+ function sanitize(str) {
22
+ return str.toLowerCase().replace(/[^a-z0-9]+/g, "_");
23
+ }
24
+ /**
25
+ * Generate a deterministic route ID
26
+ * Format: route_{sanitized_title}_{hash}
27
+ */
28
+ export function generateRouteId(title) {
29
+ const sanitized = sanitize(title);
30
+ const hash = simpleHash(title);
31
+ return `route_${sanitized}_${hash}`;
32
+ }
33
+ /**
34
+ * Generate a deterministic state ID
35
+ * Format: state_{sanitized_description}_{hash} or state_{routeId}_{index}
36
+ */
37
+ export function generateStateId(routeId, description, index) {
38
+ if (description) {
39
+ const sanitized = sanitize(description);
40
+ const hash = simpleHash(`${routeId}_${description}`);
41
+ return `state_${sanitized}_${hash}`;
42
+ }
43
+ // Fallback for states without descriptions
44
+ const suffix = index !== undefined ? index : simpleHash(routeId);
45
+ return `state_${routeId}_${suffix}`;
46
+ }
47
+ /**
48
+ * Generate a deterministic observation ID
49
+ * Format: observation_{sanitized_description}_{hash}
50
+ */
51
+ export function generateObservationId(description) {
52
+ const sanitized = sanitize(description.substring(0, 50)); // Limit length
53
+ const hash = simpleHash(description);
54
+ return `observation_${sanitized}_${hash}`;
55
+ }
56
+ /**
57
+ * Generate a deterministic tool ID
58
+ * Format: tool_{sanitized_name}_{hash}
59
+ */
60
+ export function generateToolId(name) {
61
+ const sanitized = sanitize(name);
62
+ const hash = simpleHash(name);
63
+ return `tool_${sanitized}_${hash}`;
64
+ }
65
+ //# sourceMappingURL=id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QACjC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,4BAA4B;IAClD,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,SAAS,SAAS,IAAI,IAAI,EAAE,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,WAAoB,EACpB,KAAc;IAEd,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC,CAAC;QACrD,OAAO,SAAS,SAAS,IAAI,IAAI,EAAE,CAAC;IACtC,CAAC;IACD,2CAA2C;IAC3C,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACjE,OAAO,SAAS,OAAO,IAAI,MAAM,EAAE,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe;IACzE,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACrC,OAAO,eAAe,SAAS,IAAI,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,QAAQ,SAAS,IAAI,IAAI,EAAE,CAAC;AACrC,CAAC"}
@@ -129,8 +129,18 @@ Represents a conversation flow with states and transitions.
129
129
 
130
130
  ```typescript
131
131
  new Route(options: RouteOptions)
132
+
133
+ interface RouteOptions {
134
+ id?: string; // Optional custom ID (deterministic ID generated from title if not provided)
135
+ title: string; // Route title
136
+ description?: string; // Route description
137
+ conditions?: string[]; // Conditions that activate this route
138
+ guidelines?: Guideline[]; // Initial guidelines for this route
139
+ }
132
140
  ```
133
141
 
142
+ **Note on IDs:** Route IDs are deterministic by default, generated from the title using a hash function. This ensures consistency across server restarts. You can provide a custom ID if you need specific control over the identifier.
143
+
134
144
  #### Methods
135
145
 
136
146
  ##### `createGuideline(guideline: Guideline): this`
@@ -223,8 +233,16 @@ Handles disambiguation between multiple routes.
223
233
 
224
234
  ```typescript
225
235
  new Observation(options: ObservationOptions)
236
+
237
+ interface ObservationOptions {
238
+ id?: string; // Optional custom ID (deterministic ID generated from description if not provided)
239
+ description: string; // The observation description
240
+ routeRefs?: string[]; // Route IDs or titles to disambiguate between
241
+ }
226
242
  ```
227
243
 
244
+ **Note on IDs:** Observation IDs are deterministic by default, generated from the description using a hash function. This ensures consistency across server restarts.
245
+
228
246
  #### Methods
229
247
 
230
248
  ##### `disambiguate(routes: (Route | RouteRef)[]): this`
@@ -326,12 +344,15 @@ defineTool<TContext, TArgs extends unknown[], TReturn>(
326
344
  name: string,
327
345
  handler: ToolHandler<TContext, TArgs, TReturn>,
328
346
  options?: {
347
+ id?: string; // Optional custom ID (deterministic ID generated from name if not provided)
329
348
  description?: string;
330
- metadata?: Record<string, unknown>;
349
+ parameters?: unknown;
331
350
  }
332
351
  ): ToolRef<TContext, TArgs, TReturn>
333
352
  ```
334
353
 
354
+ **Note on IDs:** Tool IDs are deterministic by default, generated from the name using a hash function. This ensures consistency across server restarts.
355
+
335
356
  **Example:**
336
357
 
337
358
  ```typescript
@@ -340,7 +361,10 @@ const getTool = defineTool<MyContext, [id: string], Data>(
340
361
  async ({ context }, id) => {
341
362
  return { data: await fetchData(id) };
342
363
  },
343
- { description: "Fetches data by ID" }
364
+ {
365
+ id: "custom_get_data_tool", // Optional: provide your own ID
366
+ description: "Fetches data by ID",
367
+ }
344
368
  );
345
369
  ```
346
370
 
@@ -353,11 +377,27 @@ Creates a message event for conversation history.
353
377
  ```typescript
354
378
  createMessageEvent(
355
379
  source: EventSource,
356
- name: string,
357
- message: string
380
+ participantName: string,
381
+ message: string,
382
+ timestamp?: string // Optional: provide custom timestamp (ISO 8601 format)
358
383
  ): Event
359
384
  ```
360
385
 
386
+ **Example:**
387
+
388
+ ```typescript
389
+ // With auto-generated timestamp
390
+ createMessageEvent(EventSource.CUSTOMER, "Alice", "Hello!");
391
+
392
+ // With custom timestamp (useful for historical data)
393
+ createMessageEvent(
394
+ EventSource.CUSTOMER,
395
+ "Alice",
396
+ "Hello!",
397
+ "2025-10-13T10:30:00Z"
398
+ );
399
+ ```
400
+
361
401
  ---
362
402
 
363
403
  ### `createToolEvent()`
@@ -366,11 +406,28 @@ Creates a tool execution event.
366
406
 
367
407
  ```typescript
368
408
  createToolEvent(
369
- toolName: string,
370
- data: unknown
409
+ source: EventSource,
410
+ toolCalls: ToolCall[],
411
+ timestamp?: string // Optional: provide custom timestamp (ISO 8601 format)
371
412
  ): Event
372
413
  ```
373
414
 
415
+ **Example:**
416
+
417
+ ```typescript
418
+ // With auto-generated timestamp
419
+ createToolEvent(EventSource.AI_AGENT, [
420
+ { tool_id: "get_data", arguments: { id: "123" }, result: { data: {...} } }
421
+ ]);
422
+
423
+ // With custom timestamp
424
+ createToolEvent(
425
+ EventSource.AI_AGENT,
426
+ [{ tool_id: "get_data", arguments: { id: "123" }, result: { data: {...} } }],
427
+ "2025-10-13T10:30:00Z"
428
+ );
429
+ ```
430
+
374
431
  ---
375
432
 
376
433
  ### `adaptEvent()`
@@ -501,6 +558,65 @@ This type represents the structured JSON output that AI providers return when us
501
558
 
502
559
  ---
503
560
 
561
+ ### ID Generation Utilities
562
+
563
+ Generate deterministic IDs for consistency across server restarts.
564
+
565
+ #### `generateRouteId(title: string): string`
566
+
567
+ Generates a deterministic route ID from a title.
568
+
569
+ ```typescript
570
+ import { generateRouteId } from "@falai/agent";
571
+
572
+ const routeId = generateRouteId("User Onboarding");
573
+ // Returns: "route_user_onboarding_{hash}"
574
+ ```
575
+
576
+ #### `generateStateId(routeId: string, description?: string, index?: number): string`
577
+
578
+ Generates a deterministic state ID.
579
+
580
+ ```typescript
581
+ import { generateStateId } from "@falai/agent";
582
+
583
+ const stateId = generateStateId("route_123", "Ask for name");
584
+ // Returns: "state_ask_for_name_{hash}"
585
+ ```
586
+
587
+ #### `generateObservationId(description: string): string`
588
+
589
+ Generates a deterministic observation ID from a description.
590
+
591
+ ```typescript
592
+ import { generateObservationId } from "@falai/agent";
593
+
594
+ const obsId = generateObservationId("User intent is unclear");
595
+ // Returns: "observation_user_intent_is_unclear_{hash}"
596
+ ```
597
+
598
+ #### `generateToolId(name: string): string`
599
+
600
+ Generates a deterministic tool ID from a name.
601
+
602
+ ```typescript
603
+ import { generateToolId } from "@falai/agent";
604
+
605
+ const toolId = generateToolId("get_user_data");
606
+ // Returns: "tool_get_user_data_{hash}"
607
+ ```
608
+
609
+ **Why Deterministic IDs?**
610
+
611
+ All IDs are generated deterministically using a hash function of their content (title, name, description). This ensures:
612
+
613
+ - **Consistency** - Same input always produces the same ID
614
+ - **Server Restart Safe** - IDs remain stable across application restarts
615
+ - **Persistence Friendly** - Safe to store in databases and reference later
616
+ - **Custom Control** - You can always provide your own IDs when needed
617
+
618
+ ---
619
+
504
620
  ## Constants
505
621
 
506
622
  ### `END_ROUTE`
@@ -18,16 +18,16 @@ interface AgentOptions<TContext = unknown> {
18
18
  // Required
19
19
  name: string;
20
20
  ai: AiProvider;
21
-
21
+
22
22
  // Optional metadata
23
23
  description?: string;
24
24
  goal?: string;
25
25
  context?: TContext;
26
-
26
+
27
27
  // Configuration
28
28
  maxEngineIterations?: number;
29
29
  compositionMode?: CompositionMode;
30
-
30
+
31
31
  // Declarative initialization (NEW!)
32
32
  terms?: Term[];
33
33
  guidelines?: Guideline[];
@@ -44,43 +44,47 @@ const agent = new Agent({
44
44
  name: "SupportBot",
45
45
  description: "Helpful customer support",
46
46
  goal: "Resolve issues efficiently",
47
- ai: new GeminiProvider({ apiKey: "..." }),
47
+ ai: new GeminiProvider({ apiKey: "...", model: "..." }),
48
48
  context: { userId: "123" },
49
-
49
+
50
50
  terms: [
51
- { name: "SLA", description: "Service Level Agreement", synonyms: ["response time"] }
51
+ {
52
+ name: "SLA",
53
+ description: "Service Level Agreement",
54
+ synonyms: ["response time"],
55
+ },
52
56
  ],
53
-
57
+
54
58
  guidelines: [
55
59
  {
56
60
  condition: "User is frustrated",
57
61
  action: "Show empathy and offer escalation",
58
62
  tags: ["support"],
59
- enabled: true
60
- }
63
+ enabled: true,
64
+ },
61
65
  ],
62
-
66
+
63
67
  capabilities: [
64
- { title: "Ticket Management", description: "Create and track tickets" }
68
+ { title: "Ticket Management", description: "Create and track tickets" },
65
69
  ],
66
-
70
+
67
71
  routes: [
68
72
  {
69
73
  title: "Create Ticket",
70
74
  description: "Help user create a support ticket",
71
75
  conditions: ["User wants to report an issue"],
72
76
  guidelines: [
73
- { condition: "Issue is urgent", action: "Prioritize immediately" }
74
- ]
75
- }
77
+ { condition: "Issue is urgent", action: "Prioritize immediately" },
78
+ ],
79
+ },
76
80
  ],
77
-
81
+
78
82
  observations: [
79
83
  {
80
84
  description: "User mentions problem but unclear what kind",
81
- routeRefs: ["Create Ticket", "Check Ticket Status"] // By title!
82
- }
83
- ]
85
+ routeRefs: ["Create Ticket", "Check Ticket Status"], // By title!
86
+ },
87
+ ],
84
88
  });
85
89
  ```
86
90
 
@@ -92,7 +96,7 @@ const agent = new Agent({
92
96
  interface RouteOptions {
93
97
  // Required
94
98
  title: string;
95
-
99
+
96
100
  // Optional
97
101
  description?: string;
98
102
  conditions?: string[];
@@ -115,16 +119,16 @@ const agent = new Agent({
115
119
  {
116
120
  condition: "User skips a step",
117
121
  action: "Gently remind them it's important",
118
- tags: ["onboarding"]
122
+ tags: ["onboarding"],
119
123
  },
120
124
  {
121
125
  condition: "User seems confused",
122
126
  action: "Offer a quick tutorial video",
123
- tags: ["help"]
124
- }
125
- ]
126
- }
127
- ]
127
+ tags: ["help"],
128
+ },
129
+ ],
130
+ },
131
+ ],
128
132
  });
129
133
  ```
130
134
 
@@ -187,18 +191,21 @@ obs.disambiguate([route1, route2]);
187
191
  ## 🎨 Best Practices
188
192
 
189
193
  ### Use Declarative When:
194
+
190
195
  - ✅ Configuration is **static** and known upfront
191
196
  - ✅ Loading config from **JSON/YAML files**
192
197
  - ✅ Building **reusable agent templates**
193
198
  - ✅ You want **clean, readable initialization**
194
199
 
195
200
  ### Use Fluent When:
201
+
196
202
  - ✅ Logic is **dynamic** or **conditional**
197
203
  - ✅ Building routes with **complex state machines**
198
204
  - ✅ Adding features **based on runtime conditions**
199
205
  - ✅ You prefer **step-by-step construction**
200
206
 
201
207
  ### Mix Both!
208
+
202
209
  ```typescript
203
210
  // Start with static config
204
211
  const agent = new Agent({
@@ -212,7 +219,7 @@ const agent = new Agent({
212
219
  if (user.isPremium) {
213
220
  agent.createGuideline({
214
221
  condition: "User asks for priority support",
215
- action: "Escalate immediately to premium team"
222
+ action: "Escalate immediately to premium team",
216
223
  });
217
224
  }
218
225
  ```
@@ -221,15 +228,15 @@ if (user.isPremium) {
221
228
 
222
229
  ## 📊 Complete Comparison
223
230
 
224
- | Feature | Declarative (Constructor) | Fluent (Methods) |
225
- |---------|--------------------------|------------------|
226
- | **Terms** | `terms: Term[]` | `agent.createTerm(...)` |
227
- | **Guidelines** | `guidelines: Guideline[]` | `agent.createGuideline(...)` |
228
- | **Capabilities** | `capabilities: Capability[]` | `agent.createCapability(...)` |
229
- | **Routes** | `routes: RouteOptions[]` | `agent.createRoute(...)` |
230
- | **Route Guidelines** | `route.guidelines: Guideline[]` | `route.createGuideline(...)` |
231
- | **Observations** | `observations: ObservationOptions[]` | `agent.createObservation(...)` |
232
- | **Disambiguation** | `routeRefs: string[]` | `obs.disambiguate([...])` |
231
+ | Feature | Declarative (Constructor) | Fluent (Methods) |
232
+ | -------------------- | ------------------------------------ | ------------------------------ |
233
+ | **Terms** | `terms: Term[]` | `agent.createTerm(...)` |
234
+ | **Guidelines** | `guidelines: Guideline[]` | `agent.createGuideline(...)` |
235
+ | **Capabilities** | `capabilities: Capability[]` | `agent.createCapability(...)` |
236
+ | **Routes** | `routes: RouteOptions[]` | `agent.createRoute(...)` |
237
+ | **Route Guidelines** | `route.guidelines: Guideline[]` | `route.createGuideline(...)` |
238
+ | **Observations** | `observations: ObservationOptions[]` | `agent.createObservation(...)` |
239
+ | **Disambiguation** | `routeRefs: string[]` | `obs.disambiguate([...])` |
233
240
 
234
241
  ---
235
242
 
@@ -58,6 +58,7 @@ interface MyContext {
58
58
  // Create AI provider
59
59
  const ai = new GeminiProvider({
60
60
  apiKey: process.env.GEMINI_API_KEY!,
61
+ model: "models/gemini-2.5-pro",
61
62
  });
62
63
 
63
64
  // Create your agent
@@ -307,6 +308,7 @@ Increase timeout in provider config:
307
308
  ```typescript
308
309
  new GeminiProvider({
309
310
  apiKey: "...",
311
+ model: "models/gemini-2.5-flash",
310
312
  retryConfig: {
311
313
  timeout: 120000, // 2 minutes
312
314
  retries: 5,
package/docs/PROVIDERS.md CHANGED
@@ -281,6 +281,7 @@ const geminiAgent = new Agent({
281
281
  name: "Gemini Assistant",
282
282
  ai: new GeminiProvider({
283
283
  apiKey: process.env.GEMINI_API_KEY!,
284
+ model: "models/gemini-2.5-flash",
284
285
  }),
285
286
  });
286
287
 
@@ -315,10 +316,12 @@ config();
315
316
 
316
317
  const geminiProvider = new GeminiProvider({
317
318
  apiKey: process.env.GEMINI_API_KEY!,
319
+ model: "models/gemini-2.5-flash",
318
320
  });
319
321
 
320
322
  const openaiProvider = new OpenAIProvider({
321
323
  apiKey: process.env.OPENAI_API_KEY!,
324
+ model: "gpt-5",
322
325
  });
323
326
  ```
324
327
 
@@ -6,8 +6,9 @@
6
6
  * - Terms (glossary)
7
7
  * - Guidelines (behavior rules)
8
8
  * - Capabilities
9
- * - Routes with nested guidelines
10
- * - Observations with route references
9
+ * - Routes with nested guidelines and custom IDs
10
+ * - Observations with route references and custom IDs
11
+ * - Custom timestamps for events
11
12
  */
12
13
 
13
14
  import {
@@ -29,13 +30,16 @@ interface HealthcareContext {
29
30
  patientName: string;
30
31
  }
31
32
 
32
- // Define tools
33
+ // Define tools with custom IDs (optional - IDs are deterministic by default)
33
34
  const getInsuranceProviders = defineTool<HealthcareContext, [], string[]>(
34
35
  "get_insurance_providers",
35
36
  async () => {
36
37
  return { data: ["MegaCare Insurance", "HealthFirst", "WellnessPlus"] };
37
38
  },
38
- { description: "Retrieves list of accepted insurance providers" }
39
+ {
40
+ id: "healthcare_insurance_providers", // Custom ID for persistence
41
+ description: "Retrieves list of accepted insurance providers",
42
+ }
39
43
  );
40
44
 
41
45
  const getAvailableSlots = defineTool<
@@ -53,7 +57,10 @@ const getAvailableSlots = defineTool<
53
57
  ],
54
58
  };
55
59
  },
56
- { description: "Gets available appointment slots" }
60
+ {
61
+ id: "healthcare_available_slots", // Custom ID
62
+ description: "Gets available appointment slots",
63
+ }
57
64
  );
58
65
 
59
66
  const getLabResults = defineTool<
@@ -70,7 +77,10 @@ const getLabResults = defineTool<
70
77
  },
71
78
  };
72
79
  },
73
- { description: "Retrieves patient lab results" }
80
+ {
81
+ id: "healthcare_lab_results", // Custom ID
82
+ description: "Retrieves patient lab results",
83
+ }
74
84
  );
75
85
 
76
86
  // Declarative configuration
@@ -126,6 +136,7 @@ const capabilities: Capability[] = [
126
136
 
127
137
  const routes: RouteOptions[] = [
128
138
  {
139
+ id: "route_schedule_appointment", // Custom ID ensures consistency across restarts
129
140
  title: "Schedule Appointment",
130
141
  description: "Helps the patient schedule an appointment",
131
142
  conditions: ["The patient wants to schedule an appointment"],
@@ -139,6 +150,7 @@ const routes: RouteOptions[] = [
139
150
  ],
140
151
  },
141
152
  {
153
+ id: "route_check_lab_results", // Custom ID
142
154
  title: "Check Lab Results",
143
155
  description: "Retrieves and explains patient lab results",
144
156
  conditions: ["The patient wants to see their lab results"],
@@ -155,6 +167,7 @@ const routes: RouteOptions[] = [
155
167
 
156
168
  const observations: ObservationOptions[] = [
157
169
  {
170
+ id: "obs_visit_followup", // Custom ID for tracking
158
171
  description:
159
172
  "The patient asks to follow up on their visit, but it's not clear in which way",
160
173
  routeRefs: ["Schedule Appointment", "Check Lab Results"], // Reference by title
@@ -172,6 +185,7 @@ const agent = new Agent<HealthcareContext>({
172
185
  },
173
186
  ai: new GeminiProvider({
174
187
  apiKey: process.env.GEMINI_API_KEY || "demo-key",
188
+ model: "models/gemini-2.5-flash",
175
189
  }),
176
190
  // Declarative initialization
177
191
  terms,
@@ -196,19 +210,29 @@ agent
196
210
 
197
211
  // Example usage
198
212
  async function main() {
213
+ // Create events with custom timestamps (useful for historical data)
199
214
  const history = [
200
215
  createMessageEvent(
201
216
  EventSource.CUSTOMER,
202
217
  "Alice",
203
- "Hi, I need to follow up on my recent visit"
218
+ "Hi, I need to follow up on my recent visit",
219
+ "2025-10-13T14:30:00Z" // Optional custom timestamp
204
220
  ),
205
221
  ];
206
222
 
207
223
  const response = await agent.respond({ history });
208
224
  console.log("Agent:", response.message);
225
+ console.log("Route chosen:", response.route?.title);
226
+ console.log("Route ID:", response.route?.id); // Custom ID is preserved
209
227
 
210
228
  // The agent will use the observation to disambiguate
211
229
  // and ask which type of follow-up the patient needs
230
+
231
+ // Note: Custom IDs ensure consistency across server restarts
232
+ // This is crucial for:
233
+ // - Storing conversation state in databases
234
+ // - Tracking metrics and analytics
235
+ // - Referencing routes in external systems
212
236
  }
213
237
 
214
238
  // Uncomment to run:
package/package.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Standalone, strongly-typed AI Agent framework with route DSL and AI provider strategy",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
6
+ "main": "./dist/cjs/index.js",
7
+ "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/cjs/index.d.ts",
17
+ "default": "./dist/cjs/index.js"
18
+ }
12
19
  }
13
20
  },
14
21
  "files": [
@@ -31,7 +38,10 @@
31
38
  },
32
39
  "homepage": "https://github.com/gusnips/falai#readme",
33
40
  "scripts": {
34
- "build": "tsc",
41
+ "build": "npm run build:esm && npm run build:cjs && npm run fix:cjs",
42
+ "build:esm": "tsc",
43
+ "build:cjs": "tsc --project tsconfig.cjs.json",
44
+ "fix:cjs": "echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
35
45
  "dev": "tsc --watch",
36
46
  "typecheck": "tsc --noEmit",
37
47
  "lint": "eslint src/**/*.ts",
@@ -70,7 +70,8 @@ export function adaptEvent(e: Event | EmittedEvent): string {
70
70
  export function createMessageEvent(
71
71
  source: EventSource,
72
72
  participantName: string,
73
- message: string
73
+ message: string,
74
+ timestamp?: string
74
75
  ): Event<MessageEventData> {
75
76
  return {
76
77
  kind: EventKind.MESSAGE,
@@ -79,7 +80,7 @@ export function createMessageEvent(
79
80
  participant: { display_name: participantName },
80
81
  message,
81
82
  },
82
- timestamp: new Date().toISOString(),
83
+ timestamp: timestamp || new Date().toISOString(),
83
84
  };
84
85
  }
85
86
 
@@ -88,7 +89,8 @@ export function createMessageEvent(
88
89
  */
89
90
  export function createToolEvent(
90
91
  source: EventSource,
91
- toolCalls: ToolCall[]
92
+ toolCalls: ToolCall[],
93
+ timestamp?: string
92
94
  ): Event<ToolEventData> {
93
95
  return {
94
96
  kind: EventKind.TOOL,
@@ -96,6 +98,6 @@ export function createToolEvent(
96
98
  data: {
97
99
  tool_calls: toolCalls,
98
100
  },
99
- timestamp: new Date().toISOString(),
101
+ timestamp: timestamp || new Date().toISOString(),
100
102
  };
101
103
  }
@@ -8,8 +8,7 @@ import type {
8
8
  } from "../types/observation";
9
9
  import type { RouteRef } from "../types/route";
10
10
  import type { Route } from "./Route";
11
-
12
- let observationIdCounter = 0;
11
+ import { generateObservationId } from "../utils/id";
13
12
 
14
13
  /**
15
14
  * An observation that can trigger disambiguation between routes
@@ -20,7 +19,8 @@ export class Observation implements IObservation {
20
19
  public routes: RouteRef[] = [];
21
20
 
22
21
  constructor(options: ObservationOptions) {
23
- this.id = `observation_${++observationIdCounter}`;
22
+ // Use provided ID or generate a deterministic one from the description
23
+ this.id = options.id || generateObservationId(options.description);
24
24
  this.description = options.description;
25
25
  }
26
26