effect-assistant 0.3.0 → 0.4.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 (73) hide show
  1. package/dist/Assistant.d.ts +5 -11
  2. package/dist/Assistant.d.ts.map +1 -1
  3. package/dist/Assistant.js.map +1 -1
  4. package/dist/AssistantEvent.d.ts +55 -7
  5. package/dist/AssistantEvent.d.ts.map +1 -1
  6. package/dist/AssistantEvent.js +61 -7
  7. package/dist/AssistantEvent.js.map +1 -1
  8. package/dist/AssistantLoop.d.ts +4 -2
  9. package/dist/AssistantLoop.d.ts.map +1 -1
  10. package/dist/AssistantLoop.js +4 -2
  11. package/dist/AssistantLoop.js.map +1 -1
  12. package/dist/Compaction.d.ts +1 -1
  13. package/dist/Compaction.d.ts.map +1 -1
  14. package/dist/Compaction.js +13 -6
  15. package/dist/Compaction.js.map +1 -1
  16. package/dist/Skill.d.ts +85 -43
  17. package/dist/Skill.d.ts.map +1 -1
  18. package/dist/Skill.js +93 -44
  19. package/dist/Skill.js.map +1 -1
  20. package/dist/Specialist.d.ts +6 -9
  21. package/dist/Specialist.d.ts.map +1 -1
  22. package/dist/Specialist.js.map +1 -1
  23. package/dist/Timeline.d.ts +40 -6
  24. package/dist/Timeline.d.ts.map +1 -1
  25. package/dist/Timeline.js +74 -15
  26. package/dist/Timeline.js.map +1 -1
  27. package/dist/ToolApproval.d.ts +146 -0
  28. package/dist/ToolApproval.d.ts.map +1 -0
  29. package/dist/ToolApproval.js +201 -0
  30. package/dist/ToolApproval.js.map +1 -0
  31. package/dist/ToolDiscovery.d.ts +6 -4
  32. package/dist/ToolDiscovery.d.ts.map +1 -1
  33. package/dist/ToolDiscovery.js +5 -4
  34. package/dist/ToolDiscovery.js.map +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.js +3 -2
  37. package/dist/internal/chat.js +4 -1
  38. package/dist/internal/chat.js.map +1 -1
  39. package/dist/internal/history.js +28 -3
  40. package/dist/internal/history.js.map +1 -1
  41. package/dist/internal/loop.d.ts +10 -5
  42. package/dist/internal/loop.d.ts.map +1 -1
  43. package/dist/internal/loop.js +54 -21
  44. package/dist/internal/loop.js.map +1 -1
  45. package/dist/internal/specialists.js +9 -16
  46. package/dist/internal/specialists.js.map +1 -1
  47. package/dist/internal/toolkit.d.ts +5 -7
  48. package/dist/internal/toolkit.d.ts.map +1 -1
  49. package/dist/internal/toolkit.js +28 -18
  50. package/dist/internal/toolkit.js.map +1 -1
  51. package/dist/internal/turns.d.ts.map +1 -1
  52. package/dist/internal/turns.js +18 -13
  53. package/dist/internal/turns.js.map +1 -1
  54. package/package.json +1 -1
  55. package/src/Assistant.ts +7 -13
  56. package/src/AssistantEvent.ts +60 -6
  57. package/src/AssistantLoop.ts +4 -2
  58. package/src/Compaction.ts +19 -2
  59. package/src/Skill.ts +134 -57
  60. package/src/Specialist.ts +8 -11
  61. package/src/Timeline.ts +104 -18
  62. package/src/ToolApproval.ts +249 -0
  63. package/src/ToolDiscovery.ts +8 -3
  64. package/src/index.ts +5 -0
  65. package/src/internal/chat.ts +11 -1
  66. package/src/internal/history.ts +58 -3
  67. package/src/internal/loop.ts +120 -34
  68. package/src/internal/specialists.ts +7 -13
  69. package/src/internal/toolkit.ts +47 -30
  70. package/src/internal/turns.ts +24 -7
  71. package/dist/internal/skills.js +0 -33
  72. package/dist/internal/skills.js.map +0 -1
  73. package/src/internal/skills.ts +0 -48
@@ -4,7 +4,6 @@ import { Instructions_d_exports, Resolved } from "./Instructions.js";
4
4
  import { AssistantLoop_d_exports, ChatHistory, Event, ToolkitServices } from "./AssistantLoop.js";
5
5
  import { CurrentChat } from "./CurrentChat.js";
6
6
  import { BlockKit, BlockKit_d_exports } from "./BlockKit.js";
7
- import { Skill } from "./Skill.js";
8
7
  import { Any as Any$1, CheckServices, HandlerOf, Runner, Specialist_d_exports, ToolHandlers } from "./Specialist.js";
9
8
  import { AssistantEngine } from "./AssistantEngine.js";
10
9
  import { Chats_d_exports, GetChatError } from "./Chats.js";
@@ -37,21 +36,16 @@ export type TurnServices = CurrentChat | Runner;
37
36
  * @category models
38
37
  * @since 0.1.0
39
38
  */
40
- export interface LoopOptions<T extends Record<string, Tool.Any> = Record<never, never>, RS = never, Events = never> {
39
+ export interface LoopOptions<T extends Record<string, Tool.Any> = Record<never, never>, Events = never> {
41
40
  /** The assistant's role and rules. The loop places `Instructions.protocol` before them. */
42
41
  readonly instructions: string | Resolved;
43
42
  /**
44
43
  * Tools the model can use in addition to the assistant's own. This is an `effect/ai` toolkit,
45
44
  * and its handlers come from the layer. Combine several toolkits with `Toolkit.merge`, for
46
- * example `Memory.Tools` or a `ToolDiscovery.make(...)` toolkit together with your own.
45
+ * example `Memory.Tools`, a `Skill.toolkit(...)`, or a `ToolDiscovery.make(...)` toolkit together
46
+ * with your own.
47
47
  */
48
48
  readonly toolkit?: Toolkit.Toolkit<T> | undefined;
49
- /**
50
- * Playbooks the model can follow. The prompt lists them as a catalog, and the model loads one
51
- * with `load_skill` when a task matches it. A skill made with `tools` is left out when the turn's
52
- * tools lack one of them.
53
- */
54
- readonly skills?: ReadonlyArray<Skill<RS>> | undefined;
55
49
  /** The maximum number of model calls per turn. Defaults to 16. */
56
50
  readonly maxSteps?: number | undefined;
57
51
  /** The message the reply pass gives the model when the turn would end without a reply. */
@@ -165,10 +159,10 @@ export interface Turn<Events = never> {
165
159
  readonly instructions: (instructions: string | Resolved) => Resolved;
166
160
  /**
167
161
  * Runs the agent loop over the chat. The loop uses `Instructions.protocol`, the assistant's own
168
- * tools, and the instructions, tools, and skills in `options`. Every step is a tool call, and
162
+ * tools, and the instructions and tools in `options`. Every step is a tool call, and
169
163
  * the loop ends with `replyIfOwed`.
170
164
  */
171
- readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>, RS = never>(options: LoopOptions<T, RS, Events>) => Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | TurnServices | ToolkitServices<T> | RS>;
165
+ readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>>(options: LoopOptions<T, Events>) => Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | TurnServices | ToolkitServices<T>>;
172
166
  /**
173
167
  * Runs one more pass when the turn would otherwise end without a reply to the user's latest
174
168
  * message. In this pass the model can only reply.
@@ -1 +1 @@
1
- {"version":3,"file":"Assistant.d.ts","names":[],"sources":["../src/Assistant.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAuCM;;;;;;;;YASM,eAAe,cAAc;;;;;;;iBAQxB,YAAY,UAAU,eAAe,KAAK,OAAO,sBAAsB,YAAY;;WAEzF,uBAAuB;;;;;;WAMvB,UAAU,QAAQ,QAAQ;;;;;;WAM1B,SAAS,cAAc,MAAM;;WAE7B;;WAEA;;;;;;WAMA,UAAU,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAkDrB,YAAY,mBACtB,SAAS,cAAc,MAAM,aAC1B,cAAc,MAAM,UAAU,OAAO;;;;;;;iBAQzB,MAAM;WACZ,QAAQ;WACR;WACA;WACA,OAAO,iBAAgC;;;;;;;;iBASjC;;WAEN,uBAAuB;;;;;WAKvB;;;;;;;;;;;iBAYM,KAAK;WACX,QAAQ;;;;;;;;;WASR,UAAU,OAAO,YAAY,YAAY,OAAO,OAAO;;;;;WAKvD,eAAe,uBAAuB,aAA0B;;;;;;WAMhE,OAAO,UAAU,eAAe,KAAK,OAAO,sBAAsB,YACzE,SAAS,YAAY,GAAG,IAAI,YACzB,OAAO,OACV,OACA,QAAQ,SACR,cAAc,gBAAgB,eAAe,gBAA8B,KAAK;;;;;;;;;WAUzE,cACP,SAAS,iBACN,OAAO,OAAO,OAAqB,QAAQ,SAAS,cAAc,gBAAgB;;;;;;;;;;;;YAa7E,QAAQ,WAAW,KAAK,GAAG,mBACrC,MAAM,KAAK,YAET,OAAO,OAAO,gBAA8B,KAC5C,OAAO,OAAO,OAAO,OAAO,gBAA8B,aAAa;;;;;;;;;YAU/D,gBAAgB,KAAK,QAAQ,GAAG,eAAe,cAAc,gBAAgB,KAAK;;;;;;;;iBAe7E,WAAW;WACjB,+BAA+B;;;;;;;;iBASzB,eAAe;WACrB,oCAAoC;;;;;;;;;;;;;iBAc9B,KAAK;WACX,QAAQ;;;;;WAKR,SAAS,OAAO,OAAO,cAAc,MAAM;;;;;;EAMpD,UAAU,QAAQ,cAAc,iBAAgC,UAAU,OAAO;;;;;;;;;;iBAWlE,QAAQ;;;;;;;;;;;;EAavB,OAAO,QAAQ,QAAQ,OAAO,QAAuB,SAAS,OAAO,aAAa;;;;;;;;;;;WAWzE,YACP,QAAQ,QACR;aAAqB;QAClB,OAAO,OAAO,MAAM;;;;;;;;;;;;;;WAchB,SAAS,QAAQ,WAAW,OAAO;;;;;;;;;;;;iBAa7B,UACf,8BACA,cAAc,eACd,wBACQ,QAAQ,QAAQ,WAAW,OAAO,QAAQ;YACxC,gBAAgB;WACjB,MAAM;WACN,QAAQ;;WAER,aAAa,cAAc;;WAE3B,QAAQ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAmCtB,UAAU,WAAW,YAAY,WAAW,YACnD,QAAQ,QAAQ,GAAG,IAAI,UAAU,OAAO,OAAO,QAAQ,GAAG,IAAI,SAAS,GAAG,OACxE,cAAyB,QAAQ,gBAAgB,IAAI,KAAK,KAAK,eAAe,YAC7E,MAAM,MACT,WAAW,cAET,kBACA,cAAc,gBACd,UAAqB,SACrB,QAAQ,IAAI,MAAM,QAAQ,cAAc,eAAe,SACvD,aAAwB,IAAI;;;;;;;;;;;;;;;;;;;;;;;WAwBvB,aAAa,MAAM,MAAM,WAAW,cAAc;;;;;WAKlD,MAAM,QAAQ,QAAQ,eAAe,OAAO,KAAK;;WAEjD,OAAO,OAAO,MAAM,MAAM,SAAS,OAAO;;;;;;;;YASzC,MAAM;;;;;;;qBAQL,cAAW,eAAiB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAqDjC,aACL,2BACA,cAAc,cAAc,4BAC5B,eAAe,cAAc,+BAA4C,MAEzE,MAAI;;WAGC,SAAS;;;;;WAKT,cAAc;;;;;;;WAOd,SAAS;MAEnB,UAAU,MAAM,eAAe"}
1
+ {"version":3,"file":"Assistant.d.ts","names":[],"sources":["../src/Assistant.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;cAsCM;;;;;;;;YASM,eAAe,cAAc;;;;;;;iBAQxB,YAAY,UAAU,eAAe,KAAK,OAAO,sBAAsB;;WAE7E,uBAAuB;;;;;;;WAOvB,UAAU,QAAQ,QAAQ;;WAE1B;;WAEA;;;;;;WAMA,UAAU,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAkDrB,YAAY,mBACtB,SAAS,cAAc,MAAM,aAC1B,cAAc,MAAM,UAAU,OAAO;;;;;;;iBAQzB,MAAM;WACZ,QAAQ;WACR;WACA;WACA,OAAO,iBAAgC;;;;;;;;iBASjC;;WAEN,uBAAuB;;;;;WAKvB;;;;;;;;;;;iBAYM,KAAK;WACX,QAAQ;;;;;;;;;WASR,UAAU,OAAO,YAAY,YAAY,OAAO,OAAO;;;;;WAKvD,eAAe,uBAAuB,aAA0B;;;;;;WAMhE,OAAO,UAAU,eAAe,KAAK,OAAO,sBACnD,SAAS,YAAY,GAAG,YACrB,OAAO,OACV,OACA,QAAQ,SACR,cAAc,gBAAgB,eAAe,gBAA8B;;;;;;;;;WAUpE,cACP,SAAS,iBACN,OAAO,OAAO,OAAqB,QAAQ,SAAS,cAAc,gBAAgB;;;;;;;;;;;;YAa7E,QAAQ,WAAW,KAAK,GAAG,mBACrC,MAAM,KAAK,YAET,OAAO,OAAO,gBAA8B,KAC5C,OAAO,OAAO,OAAO,OAAO,gBAA8B,aAAa;;;;;;;;;YAU/D,gBAAgB,KAAK,QAAQ,GAAG,eAAe,cAAc,gBAAgB,KAAK;;;;;;;;iBAe7E,WAAW;WACjB,+BAA+B;;;;;;;;iBASzB,eAAe;WACrB,oCAAoC;;;;;;;;;;;;;iBAc9B,KAAK;WACX,QAAQ;;;;;WAKR,SAAS,OAAO,OAAO,cAAc,MAAM;;;;;;EAMpD,UAAU,QAAQ,cAAc,iBAAgC,UAAU,OAAO;;;;;;;;;;iBAWlE,QAAQ;;;;;;;;;;;;EAavB,OAAO,QAAQ,QAAQ,OAAO,QAAuB,SAAS,OAAO,aAAa;;;;;;;;;;;WAWzE,YACP,QAAQ,QACR;aAAqB;QAClB,OAAO,OAAO,MAAM;;;;;;;;;;;;;;WAchB,SAAS,QAAQ,WAAW,OAAO;;;;;;;;;;;;iBAa7B,UACf,8BACA,cAAc,eACd,wBACQ,QAAQ,QAAQ,WAAW,OAAO,QAAQ;YACxC,gBAAgB;WACjB,MAAM;WACN,QAAQ;;WAER,aAAa,cAAc;;WAE3B,QAAQ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAmCtB,UAAU,WAAW,YAAY,WAAW,YACnD,QAAQ,QAAQ,GAAG,IAAI,UAAU,OAAO,OAAO,QAAQ,GAAG,IAAI,SAAS,GAAG,OACxE,cAAyB,QAAQ,gBAAgB,IAAI,KAAK,KAAK,eAAe,YAC7E,MAAM,MACT,WAAW,cAET,kBACA,cAAc,gBACd,UAAqB,SACrB,QAAQ,IAAI,MAAM,QAAQ,cAAc,eAAe,SACvD,aAAwB,IAAI;;;;;;;;;;;;;;;;;;;;;;;WAwBvB,aAAa,MAAM,MAAM,WAAW,cAAc;;;;;WAKlD,MAAM,QAAQ,QAAQ,eAAe,OAAO,KAAK;;WAEjD,OAAO,OAAO,MAAM,MAAM,SAAS,OAAO;;;;;;;;YASzC,MAAM;;;;;;;qBAQL,cAAW,eAAiB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAqDjC,aACL,2BACA,cAAc,cAAc,4BAC5B,eAAe,cAAc,+BAA4C,MAEzE,MAAI;;WAGC,SAAS;;;;;WAKT,cAAc;;;;;;;WAOd,SAAS;MAEnB,UAAU,MAAM,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"Assistant.js","names":["AssistantEvent.AssistantEvent","AppEvents.make","AssistantEvent.isInput","AppEvents.encode","Chats.Chats","AppEvents.decodeEntry","Turns.register"],"sources":["../src/Assistant.ts"],"sourcesContent":["/**\n * This module provides `Assistant.make`, which defines an assistant that works in a chat the way a\n * colleague does: the user messages it, it works in the background with the app's tools, and it\n * messages the user back.\n *\n * A definition is what the rest of an app imports. Yielding it (`yield* MyAssistant`) returns the\n * assistant's {@link Service}, which submits inputs to chats and streams them back. Its `toLayer`\n * registers the handler that runs each {@link Turn} with the `AssistantEngine`, and its\n * `layerClient` provides the service without a handler. Code that runs inside a chat reads and\n * extends that chat through the definition's {@link Chat} key.\n *\n * @since 0.1.0\n */\nimport type * as AiError from \"effect/ai/AiError\"\nimport type * as LanguageModel from \"effect/ai/LanguageModel\"\nimport type * as Prompt from \"effect/ai/Prompt\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport type * as Toolkit from \"effect/ai/Toolkit\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Filter from \"effect/Filter\"\nimport * as Layer from \"effect/Layer\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport { AssistantEngine } from \"./AssistantEngine.ts\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport type * as AssistantLoop from \"./AssistantLoop.ts\"\nimport type * as BlockKit from \"./BlockKit.ts\"\nimport { ChatId } from \"./ChatId.ts\"\nimport * as Chats from \"./Chats.ts\"\nimport type { CurrentChat } from \"./CurrentChat.ts\"\nimport type * as Instructions from \"./Instructions.ts\"\nimport * as AppEvents from \"./internal/events.ts\"\nimport * as Turns from \"./internal/turns.ts\"\nimport type { Skill } from \"./Skill.ts\"\nimport type * as Specialist from \"./Specialist.ts\"\n\nconst TypeId = \"~effect-assistant/Assistant\" as const\n\n/**\n * Represents the services that a turn provides to its tools, its loop, and its specialists. These\n * are the turn's chat (`CurrentChat`) and the chat's specialist runner (`Specialist.Runner`).\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type TurnServices = CurrentChat | Specialist.Runner\n\n/**\n * Represents the options accepted by `turn.loop`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface LoopOptions<T extends Record<string, Tool.Any> = Record<never, never>, RS = never, Events = never> {\n /** The assistant's role and rules. The loop places `Instructions.protocol` before them. */\n readonly instructions: string | Instructions.Resolved\n /**\n * Tools the model can use in addition to the assistant's own. This is an `effect/ai` toolkit,\n * and its handlers come from the layer. Combine several toolkits with `Toolkit.merge`, for\n * example `Memory.Tools` or a `ToolDiscovery.make(...)` toolkit together with your own.\n */\n readonly toolkit?: Toolkit.Toolkit<T> | undefined\n /**\n * Playbooks the model can follow. The prompt lists them as a catalog, and the model loads one\n * with `load_skill` when a task matches it. A skill made with `tools` is left out when the turn's\n * tools lack one of them.\n */\n readonly skills?: ReadonlyArray<Skill<RS>> | undefined\n /** The maximum number of model calls per turn. Defaults to 16. */\n readonly maxSteps?: number | undefined\n /** The message the reply pass gives the model when the turn would end without a reply. */\n readonly replyNudge?: string | undefined\n /**\n * Selects which of the chat's entries the model reads, for example only those since the last\n * summary, and maps the app's own events it should read into prompt messages. Defaults to all\n * entries, without the app's events.\n */\n readonly history?: HistoryView<Events> | undefined\n}\n\n/**\n * Represents a function that selects which of a chat's entries the model reads, and what else it\n * reads with them.\n *\n * **Details**\n *\n * The function receives the chat's entries, the app's own events among them, and returns, in\n * order, a mix of entries and `Prompt.Message`s. The model reads the returned entries the same way\n * it reads the full history, and reads each prompt message unchanged, where the view put it. The\n * model reads none of the app's events by itself: map those it should read to a message, and an\n * app event's entry left in the result is dropped.\n *\n * A message stands right after the entry before it: one that came after the model's last read, or\n * between a tool call and its result, moves after them, as an input does.\n *\n * **Example** (Telling the model about an order the user placed)\n *\n * ```ts\n * import { Assistant, InputId } from \"effect-assistant\"\n * import * as Prompt from \"effect/ai/Prompt\"\n * import * as Schema from \"effect/Schema\"\n *\n * class OrderPlaced extends Schema.TaggedClass<OrderPlaced>()(\"OrderPlaced\", {\n * id: InputId.InputId,\n * reference: Schema.String\n * }) {}\n *\n * const MyAssistant = Assistant.make(\"my-assistant\", { events: [OrderPlaced] })\n *\n * const withOrders: Assistant.HistoryView<OrderPlaced> = (entries) =>\n * entries.map((entry) =>\n * entry.event instanceof OrderPlaced\n * ? Prompt.userMessage({\n * content: [Prompt.textPart({ text: `The user placed order ${entry.event.reference}.` })]\n * })\n * : entry\n * )\n *\n * // An `OrderPlaced` that the API submits wakes a turn, which reads it as this note.\n * const MyAssistantLive = MyAssistant.toLayer((turn) =>\n * turn.loop({ instructions: \"Be brief.\", history: withOrders })\n * )\n * ```\n *\n * @category models\n * @since 0.1.0\n */\nexport type HistoryView<Events = never> = (\n entries: ReadonlyArray<Entry<Events>>\n) => ReadonlyArray<Entry<Events> | Prompt.Message>\n\n/**\n * Represents an entry in a chat's log, with the assistant's own events decoded to their types.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Entry<Events = never> {\n readonly chatId: ChatId\n readonly seq: number\n readonly at: number\n readonly event: AssistantEvent.AssistantEvent | Events\n}\n\n/**\n * Represents the options accepted by `turn.replyIfOwed`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ReplyOptions {\n /** The assistant's instructions, as passed to `turn.loop`. `Instructions.protocol` is added. */\n readonly instructions: string | Instructions.Resolved\n /**\n * The message given to the model after the history. Defaults to a message saying that it\n * hasn't replied yet and should reply now.\n */\n readonly nudge?: string | undefined\n}\n\n/**\n * Represents one turn, as the assistant's handler receives it.\n *\n * `Events` is the type of the app's own events that the assistant registered with its `events`\n * option.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Turn<Events = never> {\n readonly chatId: ChatId\n /**\n * Returns the model's history, built from the chat's entries (all of them, or those a view\n * keeps), together with how far into the chat it reaches.\n *\n * Read it before every model call, as the loop does. Each step records how far its model call\n * read, and the turn ends once it has handled what the model saw. An input that arrived after\n * that is handled by the next turn.\n */\n readonly history: (view?: HistoryView<Events>) => Effect.Effect<AssistantLoop.ChatHistory>\n /**\n * Returns the full system prompt for the given instructions. It contains\n * `Instructions.protocol`, the instructions, and the specialist runs that are working now.\n */\n readonly instructions: (instructions: string | Instructions.Resolved) => Instructions.Resolved\n /**\n * Runs the agent loop over the chat. The loop uses `Instructions.protocol`, the assistant's own\n * tools, and the instructions, tools, and skills in `options`. Every step is a tool call, and\n * the loop ends with `replyIfOwed`.\n */\n readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>, RS = never>(\n options: LoopOptions<T, RS, Events>\n ) => Stream.Stream<\n AssistantLoop.Event,\n AiError.AiError,\n LanguageModel.LanguageModel | TurnServices | AssistantLoop.ToolkitServices<T> | RS\n >\n /**\n * Runs one more pass when the turn would otherwise end without a reply to the user's latest\n * message. In this pass the model can only reply.\n *\n * The stream is empty when no reply is owed, or when an input arrived that the next turn will\n * read. `loop` runs this pass with its own prompt and tools, so the pass reuses the cached\n * prompt.\n */\n readonly replyIfOwed: (\n options: ReplyOptions\n ) => Stream.Stream<AssistantLoop.Event, AiError.AiError, LanguageModel.LanguageModel | TurnServices>\n}\n\n/**\n * Represents an assistant's handler, which runs a turn and returns it as a stream of the loop's\n * events.\n *\n * The handler may return the stream directly or from an Effect. `R` is the services the stream\n * requires, and `RE` is the services the Effect requires.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Handler<R = never, RE = R, Events = never> = (\n turn: Turn<Events>\n) =>\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n\n/**\n * Computes the services an assistant's handler needs beyond those its turns provide (the turn\n * services and `LanguageModel`) and its tools' handlers (which its layer requires). These are the\n * services that its build Effect must require.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type HandlerServices<R> = Exclude<R, TurnServices | LanguageModel.LanguageModel | Tool.Handler<string>>\n\nconst toStream = <R, RE>(\n result:\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n): Stream.Stream<AssistantLoop.Event, unknown, R | RE> => (Effect.isEffect(result) ? Stream.unwrap(result) : result)\n\n/**\n * Identifies an assistant's service in a context. Each assistant has its own identifier, keyed by\n * its name.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Identifier<Name extends string> {\n readonly \"~effect-assistant/Assistant\": Name\n}\n\n/**\n * Identifies, in a context, the chat that an assistant's turns run in.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ChatIdentifier<Name extends string> {\n readonly \"~effect-assistant/Assistant/Chat\": Name\n}\n\n/**\n * Represents the chat that a turn of this assistant runs in, typed with the assistant's events.\n *\n * Use `yield* MyAssistant.Chat` in the handler, in the handler's build, or in a tool that its\n * turns run. Code that doesn't know the assistant uses `CurrentChat` instead.\n *\n * @see {@link CurrentChat} for the same chat without the assistant's event types\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Chat<Events = never> {\n readonly chatId: ChatId\n /**\n * The chat's entries so far, in order. They include the library's events and the assistant's\n * own events, typed.\n */\n readonly entries: Effect.Effect<ReadonlyArray<Entry<Events>>>\n /**\n * Appends events after the chat's last entry and sends them to the chat's subscribers. The\n * events can be the library's or the assistant's own, such as a summary. None of them wakes a\n * turn: only what comes in through the assistant's `submit` does.\n */\n append(...events: ReadonlyArray<AssistantEvent.AssistantEvent | Events>): Effect.Effect<void>\n}\n\n/**\n * Represents the service that `yield* MyAssistant` returns. It gives code outside a chat, such as\n * a server or a workflow, access to the assistant's chats. Code that runs inside a chat uses\n * `CurrentChat` instead.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service<Events = never> {\n /**\n * Stores an input or one of the assistant's own events, then returns. Examples are a user's\n * message or block answer, a workflow's progress, or the order the user placed. Each one is an\n * input: storing it wakes a turn, which the assistant runs in the background. The handler decides\n * what the turn does with it, and an app event the model should read needs a history view that\n * maps it (see {@link HistoryView}). To store an app event without waking a turn, append it from\n * within the chat instead (`yield* MyAssistant.Chat`).\n *\n * Fails with `GetChatError` when the app's `Chats` service reports that the chat can't be used,\n * because the app doesn't know the chat or has deleted it.\n */\n // Method syntax: a service for these events is one for any (`Service<any>`), for helpers over assistants.\n submit(chatId: ChatId, input: AssistantEvent.Input | Events): Effect.Effect<void, Chats.GetChatError>\n /**\n * Streams the chat's stored entries, then live ones, with the assistant's own events decoded.\n * A reader that already has the earlier entries passes `after` and receives only the entries\n * after that `seq`.\n *\n * For a chat client, pipe the whole log into `Timeline.changes(MyAssistant, { after })`, which\n * streams the chat's timeline, as its user sees it. Its `after` goes there, not here: a message\n * on the timeline depends on earlier entries. The timeline shows only the app events named in its\n * `show`, none by default (see `Timeline.changes`).\n */\n readonly subscribe: (\n chatId: ChatId,\n options?: { readonly after?: number | undefined }\n ) => Stream.Stream<Entry<Events>>\n /**\n * Deletes a chat by storing a `ChatDeleted` event and stopping everything that works in it.\n *\n * **Details**\n *\n * A turn or specialist run working in the chat stops, and subscribers' streams end with the\n * `ChatDeleted` event. After that the chat stores nothing more: a later input, a run's report,\n * or a workflow's event for it is dropped without an error. The log is kept, and removing it is\n * up to the app. Deleting a chat a second time does nothing.\n *\n * When the app's `Chats` service reports a chat as deleted, the assistant deletes it in the same\n * way the next time it checks. It checks before an input is stored and before each model call.\n */\n readonly delete: (chatId: ChatId) => Effect.Effect<void>\n}\n\n/**\n * Represents an assistant's definition.\n *\n * The definition holds what callers and the UI need to know about the assistant, builds its\n * handler's layer, and is the key of its service (`yield* MyAssistant`). `Events` is the type of\n * the app's own events that the assistant registered.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Assistant<\n Name extends string = string,\n Specs extends Specialist.Any = never,\n Events = never\n> extends Context.Service<Identifier<Name>, Service<Events>> {\n readonly [TypeId]: typeof TypeId\n readonly name: Name\n readonly blocks: BlockKit.BlockKit | undefined\n /** The specialists the assistant can hand work to. */\n readonly specialists: ReadonlyArray<Specs>\n /** The schemas of the app's own events the assistant stores. */\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n /**\n * Creates a layer that registers the assistant's handler with the engine and provides the\n * assistant's service.\n *\n * **Details**\n *\n * `build` is the handler, or an Effect that returns it. The handler may use the turn services,\n * `LanguageModel`, its tools' handlers (which the layer then requires), and services that its\n * build Effect required (see `Specialist.CheckServices`). The layer requires the\n * `AssistantEngine`, the `LanguageModel`, and each specialist's handler\n * (`Research.toLayer(...)`).\n *\n * **Example** (Providing an assistant's handler)\n *\n * ```ts\n * import { Assistant, AssistantEngine, ChatLog } from \"effect-assistant\"\n * import type * as LanguageModel from \"effect/ai/LanguageModel\"\n * import * as Layer from \"effect/Layer\"\n *\n * declare const ModelLive: Layer.Layer<LanguageModel.LanguageModel>\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) =>\n * turn.loop({ instructions: \"You help a small team. Be brief.\" })\n * )\n *\n * const AppLive = MyAssistantLive.pipe(\n * Layer.provide(ModelLive),\n * Layer.provideMerge(AssistantEngine.layerMemory),\n * Layer.provideMerge(ChatLog.layerMemory)\n * )\n * ```\n */\n readonly toLayer: <R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE, Events> | Effect.Effect<Handler<R, RE, Events>, E, RX>) &\n Specialist.CheckServices<Exclude<HandlerServices<R | RE>, RX | ChatIdentifier<Name>>>\n ) => Layer.Layer<\n Identifier<Name>,\n never,\n | AssistantEngine\n | LanguageModel.LanguageModel\n | Specialist.HandlerOf<Specs>\n | Exclude<RX, Scope.Scope | CurrentChat | ChatIdentifier<Name>>\n | Specialist.ToolHandlers<R | RE>\n >\n /**\n * A layer that provides the assistant's service without its handler.\n *\n * **When to use**\n *\n * Use it in a process that submits to chats that another process runs, such as a web server in\n * front of a cluster.\n *\n * **Example** (Serving clients in front of a cluster)\n *\n * ```ts\n * import { Assistant, AssistantEngine } from \"effect-assistant\"\n * import * as Layer from \"effect/Layer\"\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * // Requires `Sharding`. The cluster's runners run the chats, with `AssistantEngine.layerCluster`.\n * const MyAssistantClient = MyAssistant.layerClient.pipe(\n * Layer.provide(AssistantEngine.layerClusterClient)\n * )\n * ```\n */\n readonly layerClient: Layer.Layer<Identifier<Name>, never, AssistantEngine>\n /**\n * The key of the chat that the assistant's turns run in, typed with its events. See\n * {@link Chat}.\n */\n readonly Chat: Context.Service<ChatIdentifier<Name>, Chat<Events>>\n /** A schema that encodes the chat's entries to JSON, including the assistant's own events. */\n readonly Entry: Schema.Codec<Entry<Events>, Schema.Json>\n}\n\n/**\n * Represents any assistant, for use in generic constraints.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = Assistant<any, any, any>\n\n/**\n * Returns `true` when the value is an assistant's definition, created with {@link make}.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isAssistant = (u: unknown): u is Any => Predicate.hasProperty(u, TypeId)\n\nfunction entrySchema<E>(events: ReadonlyArray<AssistantEvent.AppEventSchema>): Schema.Codec<Entry<E>, Schema.Json>\n// The overload states the decoded type: the library's events, or one of these schemas' types.\nfunction entrySchema(events: ReadonlyArray<AssistantEvent.AppEventSchema>): Schema.Top {\n return Schema.toCodecJson(\n Schema.Struct({\n chatId: ChatId,\n seq: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent.AssistantEvent, ...events])\n })\n )\n}\n\n/**\n * Creates an assistant definition from its name, its UI blocks, its specialists, and the schemas of\n * the app's own events it stores.\n *\n * **Details**\n *\n * The definition holds no code that runs turns. Provide a handler with its `toLayer`, then yield\n * the definition to get the assistant's {@link Service}.\n *\n * The definition only says which of the app's events a chat stores. What happens with one is up\n * to the places that use it. One that comes in through the service's `submit`, from outside a\n * turn, is an input and wakes a turn, like a user's message; one a turn appends to its chat never\n * does. The handler decides what a turn does, a history view says how the model reads an event\n * ({@link HistoryView}), and the chat's timeline says which its user sees\n * (`Timeline.changes`).\n *\n * **Example** (Defining an assistant and submitting a message)\n *\n * ```ts\n * import { Assistant, AssistantEvent, ChatId } from \"effect-assistant\"\n * import * as Effect from \"effect/Effect\"\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * const program = Effect.gen(function* () {\n * const assistant = yield* MyAssistant\n * const chatId = yield* ChatId.make()\n *\n * yield* assistant.submit(chatId, AssistantEvent.UserMessage.make({ text: \"Weather in Oslo?\" }))\n * })\n * ```\n *\n * @see {@link Assistant} for the definition's members\n * @see {@link Service} for what `yield* MyAssistant` returns\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <\n const Name extends string,\n const Specs extends ReadonlyArray<Specialist.Any> = readonly [],\n const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []\n>(\n name: Name,\n options?: {\n /** UI blocks the assistant can add to its messages. */\n readonly blocks?: BlockKit.BlockKit | undefined\n /**\n * The specialists the assistant can hand work to. Each specialist's `toLayer` must be\n * provided to the assistant's layer.\n */\n readonly specialists?: Specs | undefined\n /**\n * The schemas of the app's own events, each a tagged struct or class. `submit` and the chat's\n * `append` store them, in their JSON form, and the chat's `entries` and `subscribe` read them\n * back, decoded. Each needs a tag of its own that isn't one of the library's: `make` throws\n * otherwise.\n */\n readonly events?: Events | undefined\n }\n): Assistant<Name, Specs[number], Events[number][\"Type\"]> => {\n const events = options?.events ?? []\n\n const definition: Turns.Definition = {\n name,\n blocks: options?.blocks,\n specialists: options?.specialists ?? [],\n app: AppEvents.make(events)\n }\n\n type OwnEvents = Events[number][\"Type\"]\n\n const Key = Context.Service<Identifier<Name>, Service<OwnEvents>>(`effect-assistant/Assistant/${name}`)\n\n // The app's own events go to the engine encoded, as they're stored; the library's go unchanged.\n const stored = (\n input: AssistantEvent.Input | OwnEvents\n ): Effect.Effect<AssistantEvent.Input | AssistantEvent.StoredAppEvent> =>\n AssistantEvent.isInput(input) ? Effect.succeed(input) : AppEvents.encode(definition.app, input)\n\n const ChatKey = Context.Service<ChatIdentifier<Name>, Chat<OwnEvents>>(`effect-assistant/Assistant/${name}/Chat`)\n\n // On the engine and the app's `Chats` the layer was given, so every caller sees the same ones.\n const makeService = Effect.gen(function* () {\n const engine = yield* AssistantEngine\n const chats = yield* Chats.Chats\n\n return Key.of({\n submit: Effect.fn(\"Assistant.submit\")(function* (chatId: ChatId, input: AssistantEvent.Input | OwnEvents) {\n // Checked here, where the caller can handle it: the chat checks again when it stores the input.\n yield* chats.get(chatId)\n yield* engine.submit(name, chatId, yield* stored(input))\n }),\n subscribe: (chatId, options) =>\n engine\n .subscribe(name, chatId, options?.after ?? 0)\n .pipe(\n Stream.filterMap(\n Filter.fromPredicateOption((entry: AssistantEvent.Entry) => AppEvents.decodeEntry(definition.app, entry))\n )\n ),\n delete: (chatId) => engine.delete(name, chatId)\n })\n })\n\n // The signature checks what the handler uses (`CheckServices`); the layer's services, which the build\n // required, are provided to it and to every turn's stream, so what's left is the turn services.\n function toLayer<R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE, OwnEvents> | Effect.Effect<Handler<R, RE, OwnEvents>, E, RX>) &\n Specialist.CheckServices<Exclude<HandlerServices<R | RE>, RX | ChatIdentifier<Name>>>\n ): Layer.Layer<\n Identifier<Name>,\n never,\n | AssistantEngine\n | LanguageModel.LanguageModel\n | Specialist.HandlerOf<Specs[number]>\n | Exclude<RX, Scope.Scope | CurrentChat | ChatIdentifier<Name>>\n | Specialist.ToolHandlers<R | RE>\n >\n function toLayer(\n build: Handler<any, any, any> | Effect.Effect<Handler<any, any, any>, unknown, any>\n ): Layer.Layer<Identifier<Name>, never, any> {\n return Layer.effect(\n Key,\n Effect.gen(function* () {\n const context = yield* Effect.context()\n\n const registration = yield* Turns.register(definition, {\n // The build runs per chat activation, in the chat; here it only keeps the layer's services for it.\n open: (typed) =>\n Effect.gen(function* () {\n // The chat's entries are already decoded with this assistant's events.\n const chat = ChatKey.of({ chatId: typed.chatId, entries: typed.entries, append: typed.append })\n const handler = Effect.isEffect(build) ? yield* Effect.provideService(build, ChatKey, chat) : build\n\n return (turn: Turn<any>) =>\n Stream.provideContext(toStream(handler(turn)), context).pipe(Stream.provideService(ChatKey, chat))\n }).pipe(Effect.provideContext(context), Effect.orDie)\n })\n\n yield* (yield* AssistantEngine).register(registration)\n\n return yield* makeService\n })\n )\n }\n\n // The key is a function, whose own `name` is read-only: the assistant's name replaces it.\n const named = Object.defineProperty(Key, \"name\", { value: name, writable: true })\n\n return Object.assign(named, {\n [TypeId]: TypeId,\n name,\n blocks: options?.blocks,\n specialists: options?.specialists ?? [],\n events,\n toLayer,\n layerClient: Layer.effect(Key, makeService),\n Chat: ChatKey,\n Entry: entrySchema<OwnEvents>(events)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuCA,MAAM,SAAS;AAwMf,MAAM,YACJ,WAGyD,OAAO,SAAS,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI;;;;;;;AAuN7G,MAAa,eAAe,MAAyB,UAAU,YAAY,GAAG,MAAM;AAIpF,SAAS,YAAY,QAAkE;CACrF,OAAO,OAAO,YACZ,OAAO,OAAO;EACZ,QAAQ;EACR,KAAK,OAAO;EACZ,IAAI,OAAO;EACX,OAAO,OAAO,MAAM,CAACA,gBAA+B,GAAG,MAAM,CAAC;CAChE,CAAC,CACH;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,QAKX,MACA,YAgB2D;CAC3D,MAAM,SAAS,SAAS,UAAU,CAAC;CAEnC,MAAM,aAA+B;EACnC;EACA,QAAQ,SAAS;EACjB,aAAa,SAAS,eAAe,CAAC;EACtC,KAAKC,OAAe,MAAM;CAC5B;CAIA,MAAM,MAAM,QAAQ,QAA8C,8BAA8B,MAAM;CAGtG,MAAM,UACJ,UAEAC,QAAuB,KAAK,IAAI,OAAO,QAAQ,KAAK,IAAIC,OAAiB,WAAW,KAAK,KAAK;CAEhG,MAAM,UAAU,QAAQ,QAA+C,8BAA8B,KAAK,MAAM;CAGhH,MAAM,cAAc,OAAO,IAAI,aAAa;EAC1C,MAAM,SAAS,OAAO;EACtB,MAAM,QAAQ,OAAOC;EAErB,OAAO,IAAI,GAAG;GACZ,QAAQ,OAAO,GAAG,kBAAkB,CAAC,CAAC,WAAW,QAAgB,OAAyC;IAExG,OAAO,MAAM,IAAI,MAAM;IACvB,OAAO,OAAO,OAAO,MAAM,QAAQ,OAAO,OAAO,KAAK,CAAC;GACzD,CAAC;GACD,YAAY,QAAQ,YAClB,OACG,UAAU,MAAM,QAAQ,SAAS,SAAS,CAAC,CAAC,CAC5C,KACC,OAAO,UACL,OAAO,qBAAqB,UAAgCC,YAAsB,WAAW,KAAK,KAAK,CAAC,CAC1G,CACF;GACJ,SAAS,WAAW,OAAO,OAAO,MAAM,MAAM;EAChD,CAAC;CACH,CAAC;CAgBD,SAAS,QACP,OAC2C;EAC3C,OAAO,MAAM,OACX,KACA,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,OAAO,QAAQ;GAEtC,MAAM,eAAe,OAAOC,SAAe,YAAY,EAErD,OAAO,UACL,OAAO,IAAI,aAAa;IAEtB,MAAM,OAAO,QAAQ,GAAG;KAAE,QAAQ,MAAM;KAAQ,SAAS,MAAM;KAAS,QAAQ,MAAM;IAAO,CAAC;IAC9F,MAAM,UAAU,OAAO,SAAS,KAAK,IAAI,OAAO,OAAO,eAAe,OAAO,SAAS,IAAI,IAAI;IAE9F,QAAQ,SACN,OAAO,eAAe,SAAS,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,OAAO,eAAe,SAAS,IAAI,CAAC;GACrG,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,OAAO,GAAG,OAAO,KAAK,EACxD,CAAC;GAED,QAAQ,OAAO,gBAAA,CAAiB,SAAS,YAAY;GAErD,OAAO,OAAO;EAChB,CAAC,CACH;CACF;CAGA,MAAM,QAAQ,OAAO,eAAe,KAAK,QAAQ;EAAE,OAAO;EAAM,UAAU;CAAK,CAAC;CAEhF,OAAO,OAAO,OAAO,OAAO;GACzB,SAAS;EACV;EACA,QAAQ,SAAS;EACjB,aAAa,SAAS,eAAe,CAAC;EACtC;EACA;EACA,aAAa,MAAM,OAAO,KAAK,WAAW;EAC1C,MAAM;EACN,OAAO,YAAuB,MAAM;CACtC,CAAC;AACH"}
1
+ {"version":3,"file":"Assistant.js","names":["AssistantEvent.AssistantEvent","AppEvents.make","AssistantEvent.isInput","AppEvents.encode","Chats.Chats","AppEvents.decodeEntry","Turns.register"],"sources":["../src/Assistant.ts"],"sourcesContent":["/**\n * This module provides `Assistant.make`, which defines an assistant that works in a chat the way a\n * colleague does: the user messages it, it works in the background with the app's tools, and it\n * messages the user back.\n *\n * A definition is what the rest of an app imports. Yielding it (`yield* MyAssistant`) returns the\n * assistant's {@link Service}, which submits inputs to chats and streams them back. Its `toLayer`\n * registers the handler that runs each {@link Turn} with the `AssistantEngine`, and its\n * `layerClient` provides the service without a handler. Code that runs inside a chat reads and\n * extends that chat through the definition's {@link Chat} key.\n *\n * @since 0.1.0\n */\nimport type * as AiError from \"effect/ai/AiError\"\nimport type * as LanguageModel from \"effect/ai/LanguageModel\"\nimport type * as Prompt from \"effect/ai/Prompt\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport type * as Toolkit from \"effect/ai/Toolkit\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Filter from \"effect/Filter\"\nimport * as Layer from \"effect/Layer\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport { AssistantEngine } from \"./AssistantEngine.ts\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport type * as AssistantLoop from \"./AssistantLoop.ts\"\nimport type * as BlockKit from \"./BlockKit.ts\"\nimport { ChatId } from \"./ChatId.ts\"\nimport * as Chats from \"./Chats.ts\"\nimport type { CurrentChat } from \"./CurrentChat.ts\"\nimport type * as Instructions from \"./Instructions.ts\"\nimport * as AppEvents from \"./internal/events.ts\"\nimport * as Turns from \"./internal/turns.ts\"\nimport type * as Specialist from \"./Specialist.ts\"\n\nconst TypeId = \"~effect-assistant/Assistant\" as const\n\n/**\n * Represents the services that a turn provides to its tools, its loop, and its specialists. These\n * are the turn's chat (`CurrentChat`) and the chat's specialist runner (`Specialist.Runner`).\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type TurnServices = CurrentChat | Specialist.Runner\n\n/**\n * Represents the options accepted by `turn.loop`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface LoopOptions<T extends Record<string, Tool.Any> = Record<never, never>, Events = never> {\n /** The assistant's role and rules. The loop places `Instructions.protocol` before them. */\n readonly instructions: string | Instructions.Resolved\n /**\n * Tools the model can use in addition to the assistant's own. This is an `effect/ai` toolkit,\n * and its handlers come from the layer. Combine several toolkits with `Toolkit.merge`, for\n * example `Memory.Tools`, a `Skill.toolkit(...)`, or a `ToolDiscovery.make(...)` toolkit together\n * with your own.\n */\n readonly toolkit?: Toolkit.Toolkit<T> | undefined\n /** The maximum number of model calls per turn. Defaults to 16. */\n readonly maxSteps?: number | undefined\n /** The message the reply pass gives the model when the turn would end without a reply. */\n readonly replyNudge?: string | undefined\n /**\n * Selects which of the chat's entries the model reads, for example only those since the last\n * summary, and maps the app's own events it should read into prompt messages. Defaults to all\n * entries, without the app's events.\n */\n readonly history?: HistoryView<Events> | undefined\n}\n\n/**\n * Represents a function that selects which of a chat's entries the model reads, and what else it\n * reads with them.\n *\n * **Details**\n *\n * The function receives the chat's entries, the app's own events among them, and returns, in\n * order, a mix of entries and `Prompt.Message`s. The model reads the returned entries the same way\n * it reads the full history, and reads each prompt message unchanged, where the view put it. The\n * model reads none of the app's events by itself: map those it should read to a message, and an\n * app event's entry left in the result is dropped.\n *\n * A message stands right after the entry before it: one that came after the model's last read, or\n * between a tool call and its result, moves after them, as an input does.\n *\n * **Example** (Telling the model about an order the user placed)\n *\n * ```ts\n * import { Assistant, InputId } from \"effect-assistant\"\n * import * as Prompt from \"effect/ai/Prompt\"\n * import * as Schema from \"effect/Schema\"\n *\n * class OrderPlaced extends Schema.TaggedClass<OrderPlaced>()(\"OrderPlaced\", {\n * id: InputId.InputId,\n * reference: Schema.String\n * }) {}\n *\n * const MyAssistant = Assistant.make(\"my-assistant\", { events: [OrderPlaced] })\n *\n * const withOrders: Assistant.HistoryView<OrderPlaced> = (entries) =>\n * entries.map((entry) =>\n * entry.event instanceof OrderPlaced\n * ? Prompt.userMessage({\n * content: [Prompt.textPart({ text: `The user placed order ${entry.event.reference}.` })]\n * })\n * : entry\n * )\n *\n * // An `OrderPlaced` that the API submits wakes a turn, which reads it as this note.\n * const MyAssistantLive = MyAssistant.toLayer((turn) =>\n * turn.loop({ instructions: \"Be brief.\", history: withOrders })\n * )\n * ```\n *\n * @category models\n * @since 0.1.0\n */\nexport type HistoryView<Events = never> = (\n entries: ReadonlyArray<Entry<Events>>\n) => ReadonlyArray<Entry<Events> | Prompt.Message>\n\n/**\n * Represents an entry in a chat's log, with the assistant's own events decoded to their types.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Entry<Events = never> {\n readonly chatId: ChatId\n readonly seq: number\n readonly at: number\n readonly event: AssistantEvent.AssistantEvent | Events\n}\n\n/**\n * Represents the options accepted by `turn.replyIfOwed`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ReplyOptions {\n /** The assistant's instructions, as passed to `turn.loop`. `Instructions.protocol` is added. */\n readonly instructions: string | Instructions.Resolved\n /**\n * The message given to the model after the history. Defaults to a message saying that it\n * hasn't replied yet and should reply now.\n */\n readonly nudge?: string | undefined\n}\n\n/**\n * Represents one turn, as the assistant's handler receives it.\n *\n * `Events` is the type of the app's own events that the assistant registered with its `events`\n * option.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Turn<Events = never> {\n readonly chatId: ChatId\n /**\n * Returns the model's history, built from the chat's entries (all of them, or those a view\n * keeps), together with how far into the chat it reaches.\n *\n * Read it before every model call, as the loop does. Each step records how far its model call\n * read, and the turn ends once it has handled what the model saw. An input that arrived after\n * that is handled by the next turn.\n */\n readonly history: (view?: HistoryView<Events>) => Effect.Effect<AssistantLoop.ChatHistory>\n /**\n * Returns the full system prompt for the given instructions. It contains\n * `Instructions.protocol`, the instructions, and the specialist runs that are working now.\n */\n readonly instructions: (instructions: string | Instructions.Resolved) => Instructions.Resolved\n /**\n * Runs the agent loop over the chat. The loop uses `Instructions.protocol`, the assistant's own\n * tools, and the instructions and tools in `options`. Every step is a tool call, and\n * the loop ends with `replyIfOwed`.\n */\n readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>>(\n options: LoopOptions<T, Events>\n ) => Stream.Stream<\n AssistantLoop.Event,\n AiError.AiError,\n LanguageModel.LanguageModel | TurnServices | AssistantLoop.ToolkitServices<T>\n >\n /**\n * Runs one more pass when the turn would otherwise end without a reply to the user's latest\n * message. In this pass the model can only reply.\n *\n * The stream is empty when no reply is owed, or when an input arrived that the next turn will\n * read. `loop` runs this pass with its own prompt and tools, so the pass reuses the cached\n * prompt.\n */\n readonly replyIfOwed: (\n options: ReplyOptions\n ) => Stream.Stream<AssistantLoop.Event, AiError.AiError, LanguageModel.LanguageModel | TurnServices>\n}\n\n/**\n * Represents an assistant's handler, which runs a turn and returns it as a stream of the loop's\n * events.\n *\n * The handler may return the stream directly or from an Effect. `R` is the services the stream\n * requires, and `RE` is the services the Effect requires.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Handler<R = never, RE = R, Events = never> = (\n turn: Turn<Events>\n) =>\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n\n/**\n * Computes the services an assistant's handler needs beyond those its turns provide (the turn\n * services and `LanguageModel`) and its tools' handlers (which its layer requires). These are the\n * services that its build Effect must require.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type HandlerServices<R> = Exclude<R, TurnServices | LanguageModel.LanguageModel | Tool.Handler<string>>\n\nconst toStream = <R, RE>(\n result:\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n): Stream.Stream<AssistantLoop.Event, unknown, R | RE> => (Effect.isEffect(result) ? Stream.unwrap(result) : result)\n\n/**\n * Identifies an assistant's service in a context. Each assistant has its own identifier, keyed by\n * its name.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Identifier<Name extends string> {\n readonly \"~effect-assistant/Assistant\": Name\n}\n\n/**\n * Identifies, in a context, the chat that an assistant's turns run in.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ChatIdentifier<Name extends string> {\n readonly \"~effect-assistant/Assistant/Chat\": Name\n}\n\n/**\n * Represents the chat that a turn of this assistant runs in, typed with the assistant's events.\n *\n * Use `yield* MyAssistant.Chat` in the handler, in the handler's build, or in a tool that its\n * turns run. Code that doesn't know the assistant uses `CurrentChat` instead.\n *\n * @see {@link CurrentChat} for the same chat without the assistant's event types\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Chat<Events = never> {\n readonly chatId: ChatId\n /**\n * The chat's entries so far, in order. They include the library's events and the assistant's\n * own events, typed.\n */\n readonly entries: Effect.Effect<ReadonlyArray<Entry<Events>>>\n /**\n * Appends events after the chat's last entry and sends them to the chat's subscribers. The\n * events can be the library's or the assistant's own, such as a summary. None of them wakes a\n * turn: only what comes in through the assistant's `submit` does.\n */\n append(...events: ReadonlyArray<AssistantEvent.AssistantEvent | Events>): Effect.Effect<void>\n}\n\n/**\n * Represents the service that `yield* MyAssistant` returns. It gives code outside a chat, such as\n * a server or a workflow, access to the assistant's chats. Code that runs inside a chat uses\n * `CurrentChat` instead.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service<Events = never> {\n /**\n * Stores an input or one of the assistant's own events, then returns. Examples are a user's\n * message or block answer, a workflow's progress, or the order the user placed. Each one is an\n * input: storing it wakes a turn, which the assistant runs in the background. The handler decides\n * what the turn does with it, and an app event the model should read needs a history view that\n * maps it (see {@link HistoryView}). To store an app event without waking a turn, append it from\n * within the chat instead (`yield* MyAssistant.Chat`).\n *\n * Fails with `GetChatError` when the app's `Chats` service reports that the chat can't be used,\n * because the app doesn't know the chat or has deleted it.\n */\n // Method syntax: a service for these events is one for any (`Service<any>`), for helpers over assistants.\n submit(chatId: ChatId, input: AssistantEvent.Input | Events): Effect.Effect<void, Chats.GetChatError>\n /**\n * Streams the chat's stored entries, then live ones, with the assistant's own events decoded.\n * A reader that already has the earlier entries passes `after` and receives only the entries\n * after that `seq`.\n *\n * For a chat client, pipe the whole log into `Timeline.changes(MyAssistant, { after })`, which\n * streams the chat's timeline, as its user sees it. Its `after` goes there, not here: a message\n * on the timeline depends on earlier entries. The timeline shows only the app events named in its\n * `show`, none by default (see `Timeline.changes`).\n */\n readonly subscribe: (\n chatId: ChatId,\n options?: { readonly after?: number | undefined }\n ) => Stream.Stream<Entry<Events>>\n /**\n * Deletes a chat by storing a `ChatDeleted` event and stopping everything that works in it.\n *\n * **Details**\n *\n * A turn or specialist run working in the chat stops, and subscribers' streams end with the\n * `ChatDeleted` event. After that the chat stores nothing more: a later input, a run's report,\n * or a workflow's event for it is dropped without an error. The log is kept, and removing it is\n * up to the app. Deleting a chat a second time does nothing.\n *\n * When the app's `Chats` service reports a chat as deleted, the assistant deletes it in the same\n * way the next time it checks. It checks before an input is stored and before each model call.\n */\n readonly delete: (chatId: ChatId) => Effect.Effect<void>\n}\n\n/**\n * Represents an assistant's definition.\n *\n * The definition holds what callers and the UI need to know about the assistant, builds its\n * handler's layer, and is the key of its service (`yield* MyAssistant`). `Events` is the type of\n * the app's own events that the assistant registered.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Assistant<\n Name extends string = string,\n Specs extends Specialist.Any = never,\n Events = never\n> extends Context.Service<Identifier<Name>, Service<Events>> {\n readonly [TypeId]: typeof TypeId\n readonly name: Name\n readonly blocks: BlockKit.BlockKit | undefined\n /** The specialists the assistant can hand work to. */\n readonly specialists: ReadonlyArray<Specs>\n /** The schemas of the app's own events the assistant stores. */\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n /**\n * Creates a layer that registers the assistant's handler with the engine and provides the\n * assistant's service.\n *\n * **Details**\n *\n * `build` is the handler, or an Effect that returns it. The handler may use the turn services,\n * `LanguageModel`, its tools' handlers (which the layer then requires), and services that its\n * build Effect required (see `Specialist.CheckServices`). The layer requires the\n * `AssistantEngine`, the `LanguageModel`, and each specialist's handler\n * (`Research.toLayer(...)`).\n *\n * **Example** (Providing an assistant's handler)\n *\n * ```ts\n * import { Assistant, AssistantEngine, ChatLog } from \"effect-assistant\"\n * import type * as LanguageModel from \"effect/ai/LanguageModel\"\n * import * as Layer from \"effect/Layer\"\n *\n * declare const ModelLive: Layer.Layer<LanguageModel.LanguageModel>\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) =>\n * turn.loop({ instructions: \"You help a small team. Be brief.\" })\n * )\n *\n * const AppLive = MyAssistantLive.pipe(\n * Layer.provide(ModelLive),\n * Layer.provideMerge(AssistantEngine.layerMemory),\n * Layer.provideMerge(ChatLog.layerMemory)\n * )\n * ```\n */\n readonly toLayer: <R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE, Events> | Effect.Effect<Handler<R, RE, Events>, E, RX>) &\n Specialist.CheckServices<Exclude<HandlerServices<R | RE>, RX | ChatIdentifier<Name>>>\n ) => Layer.Layer<\n Identifier<Name>,\n never,\n | AssistantEngine\n | LanguageModel.LanguageModel\n | Specialist.HandlerOf<Specs>\n | Exclude<RX, Scope.Scope | CurrentChat | ChatIdentifier<Name>>\n | Specialist.ToolHandlers<R | RE>\n >\n /**\n * A layer that provides the assistant's service without its handler.\n *\n * **When to use**\n *\n * Use it in a process that submits to chats that another process runs, such as a web server in\n * front of a cluster.\n *\n * **Example** (Serving clients in front of a cluster)\n *\n * ```ts\n * import { Assistant, AssistantEngine } from \"effect-assistant\"\n * import * as Layer from \"effect/Layer\"\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * // Requires `Sharding`. The cluster's runners run the chats, with `AssistantEngine.layerCluster`.\n * const MyAssistantClient = MyAssistant.layerClient.pipe(\n * Layer.provide(AssistantEngine.layerClusterClient)\n * )\n * ```\n */\n readonly layerClient: Layer.Layer<Identifier<Name>, never, AssistantEngine>\n /**\n * The key of the chat that the assistant's turns run in, typed with its events. See\n * {@link Chat}.\n */\n readonly Chat: Context.Service<ChatIdentifier<Name>, Chat<Events>>\n /** A schema that encodes the chat's entries to JSON, including the assistant's own events. */\n readonly Entry: Schema.Codec<Entry<Events>, Schema.Json>\n}\n\n/**\n * Represents any assistant, for use in generic constraints.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = Assistant<any, any, any>\n\n/**\n * Returns `true` when the value is an assistant's definition, created with {@link make}.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isAssistant = (u: unknown): u is Any => Predicate.hasProperty(u, TypeId)\n\nfunction entrySchema<E>(events: ReadonlyArray<AssistantEvent.AppEventSchema>): Schema.Codec<Entry<E>, Schema.Json>\n// The overload states the decoded type: the library's events, or one of these schemas' types.\nfunction entrySchema(events: ReadonlyArray<AssistantEvent.AppEventSchema>): Schema.Top {\n return Schema.toCodecJson(\n Schema.Struct({\n chatId: ChatId,\n seq: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent.AssistantEvent, ...events])\n })\n )\n}\n\n/**\n * Creates an assistant definition from its name, its UI blocks, its specialists, and the schemas of\n * the app's own events it stores.\n *\n * **Details**\n *\n * The definition holds no code that runs turns. Provide a handler with its `toLayer`, then yield\n * the definition to get the assistant's {@link Service}.\n *\n * The definition only says which of the app's events a chat stores. What happens with one is up\n * to the places that use it. One that comes in through the service's `submit`, from outside a\n * turn, is an input and wakes a turn, like a user's message; one a turn appends to its chat never\n * does. The handler decides what a turn does, a history view says how the model reads an event\n * ({@link HistoryView}), and the chat's timeline says which its user sees\n * (`Timeline.changes`).\n *\n * **Example** (Defining an assistant and submitting a message)\n *\n * ```ts\n * import { Assistant, AssistantEvent, ChatId } from \"effect-assistant\"\n * import * as Effect from \"effect/Effect\"\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\")\n *\n * const program = Effect.gen(function* () {\n * const assistant = yield* MyAssistant\n * const chatId = yield* ChatId.make()\n *\n * yield* assistant.submit(chatId, AssistantEvent.UserMessage.make({ text: \"Weather in Oslo?\" }))\n * })\n * ```\n *\n * @see {@link Assistant} for the definition's members\n * @see {@link Service} for what `yield* MyAssistant` returns\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <\n const Name extends string,\n const Specs extends ReadonlyArray<Specialist.Any> = readonly [],\n const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []\n>(\n name: Name,\n options?: {\n /** UI blocks the assistant can add to its messages. */\n readonly blocks?: BlockKit.BlockKit | undefined\n /**\n * The specialists the assistant can hand work to. Each specialist's `toLayer` must be\n * provided to the assistant's layer.\n */\n readonly specialists?: Specs | undefined\n /**\n * The schemas of the app's own events, each a tagged struct or class. `submit` and the chat's\n * `append` store them, in their JSON form, and the chat's `entries` and `subscribe` read them\n * back, decoded. Each needs a tag of its own that isn't one of the library's: `make` throws\n * otherwise.\n */\n readonly events?: Events | undefined\n }\n): Assistant<Name, Specs[number], Events[number][\"Type\"]> => {\n const events = options?.events ?? []\n\n const definition: Turns.Definition = {\n name,\n blocks: options?.blocks,\n specialists: options?.specialists ?? [],\n app: AppEvents.make(events)\n }\n\n type OwnEvents = Events[number][\"Type\"]\n\n const Key = Context.Service<Identifier<Name>, Service<OwnEvents>>(`effect-assistant/Assistant/${name}`)\n\n // The app's own events go to the engine encoded, as they're stored; the library's go unchanged.\n const stored = (\n input: AssistantEvent.Input | OwnEvents\n ): Effect.Effect<AssistantEvent.Input | AssistantEvent.StoredAppEvent> =>\n AssistantEvent.isInput(input) ? Effect.succeed(input) : AppEvents.encode(definition.app, input)\n\n const ChatKey = Context.Service<ChatIdentifier<Name>, Chat<OwnEvents>>(`effect-assistant/Assistant/${name}/Chat`)\n\n // On the engine and the app's `Chats` the layer was given, so every caller sees the same ones.\n const makeService = Effect.gen(function* () {\n const engine = yield* AssistantEngine\n const chats = yield* Chats.Chats\n\n return Key.of({\n submit: Effect.fn(\"Assistant.submit\")(function* (chatId: ChatId, input: AssistantEvent.Input | OwnEvents) {\n // Checked here, where the caller can handle it: the chat checks again when it stores the input.\n yield* chats.get(chatId)\n yield* engine.submit(name, chatId, yield* stored(input))\n }),\n subscribe: (chatId, options) =>\n engine\n .subscribe(name, chatId, options?.after ?? 0)\n .pipe(\n Stream.filterMap(\n Filter.fromPredicateOption((entry: AssistantEvent.Entry) => AppEvents.decodeEntry(definition.app, entry))\n )\n ),\n delete: (chatId) => engine.delete(name, chatId)\n })\n })\n\n // The signature checks what the handler uses (`CheckServices`); the layer's services, which the build\n // required, are provided to it and to every turn's stream, so what's left is the turn services.\n function toLayer<R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE, OwnEvents> | Effect.Effect<Handler<R, RE, OwnEvents>, E, RX>) &\n Specialist.CheckServices<Exclude<HandlerServices<R | RE>, RX | ChatIdentifier<Name>>>\n ): Layer.Layer<\n Identifier<Name>,\n never,\n | AssistantEngine\n | LanguageModel.LanguageModel\n | Specialist.HandlerOf<Specs[number]>\n | Exclude<RX, Scope.Scope | CurrentChat | ChatIdentifier<Name>>\n | Specialist.ToolHandlers<R | RE>\n >\n function toLayer(\n build: Handler<any, any, any> | Effect.Effect<Handler<any, any, any>, unknown, any>\n ): Layer.Layer<Identifier<Name>, never, any> {\n return Layer.effect(\n Key,\n Effect.gen(function* () {\n const context = yield* Effect.context()\n\n const registration = yield* Turns.register(definition, {\n // The build runs per chat activation, in the chat; here it only keeps the layer's services for it.\n open: (typed) =>\n Effect.gen(function* () {\n // The chat's entries are already decoded with this assistant's events.\n const chat = ChatKey.of({ chatId: typed.chatId, entries: typed.entries, append: typed.append })\n const handler = Effect.isEffect(build) ? yield* Effect.provideService(build, ChatKey, chat) : build\n\n return (turn: Turn<any>) =>\n Stream.provideContext(toStream(handler(turn)), context).pipe(Stream.provideService(ChatKey, chat))\n }).pipe(Effect.provideContext(context), Effect.orDie)\n })\n\n yield* (yield* AssistantEngine).register(registration)\n\n return yield* makeService\n })\n )\n }\n\n // The key is a function, whose own `name` is read-only: the assistant's name replaces it.\n const named = Object.defineProperty(Key, \"name\", { value: name, writable: true })\n\n return Object.assign(named, {\n [TypeId]: TypeId,\n name,\n blocks: options?.blocks,\n specialists: options?.specialists ?? [],\n events,\n toLayer,\n layerClient: Layer.effect(Key, makeService),\n Chat: ChatKey,\n Entry: entrySchema<OwnEvents>(events)\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAsCA,MAAM,SAAS;AAmMf,MAAM,YACJ,WAGyD,OAAO,SAAS,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI;;;;;;;AAuN7G,MAAa,eAAe,MAAyB,UAAU,YAAY,GAAG,MAAM;AAIpF,SAAS,YAAY,QAAkE;CACrF,OAAO,OAAO,YACZ,OAAO,OAAO;EACZ,QAAQ;EACR,KAAK,OAAO;EACZ,IAAI,OAAO;EACX,OAAO,OAAO,MAAM,CAACA,gBAA+B,GAAG,MAAM,CAAC;CAChE,CAAC,CACH;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,QAKX,MACA,YAgB2D;CAC3D,MAAM,SAAS,SAAS,UAAU,CAAC;CAEnC,MAAM,aAA+B;EACnC;EACA,QAAQ,SAAS;EACjB,aAAa,SAAS,eAAe,CAAC;EACtC,KAAKC,OAAe,MAAM;CAC5B;CAIA,MAAM,MAAM,QAAQ,QAA8C,8BAA8B,MAAM;CAGtG,MAAM,UACJ,UAEAC,QAAuB,KAAK,IAAI,OAAO,QAAQ,KAAK,IAAIC,OAAiB,WAAW,KAAK,KAAK;CAEhG,MAAM,UAAU,QAAQ,QAA+C,8BAA8B,KAAK,MAAM;CAGhH,MAAM,cAAc,OAAO,IAAI,aAAa;EAC1C,MAAM,SAAS,OAAO;EACtB,MAAM,QAAQ,OAAOC;EAErB,OAAO,IAAI,GAAG;GACZ,QAAQ,OAAO,GAAG,kBAAkB,CAAC,CAAC,WAAW,QAAgB,OAAyC;IAExG,OAAO,MAAM,IAAI,MAAM;IACvB,OAAO,OAAO,OAAO,MAAM,QAAQ,OAAO,OAAO,KAAK,CAAC;GACzD,CAAC;GACD,YAAY,QAAQ,YAClB,OACG,UAAU,MAAM,QAAQ,SAAS,SAAS,CAAC,CAAC,CAC5C,KACC,OAAO,UACL,OAAO,qBAAqB,UAAgCC,YAAsB,WAAW,KAAK,KAAK,CAAC,CAC1G,CACF;GACJ,SAAS,WAAW,OAAO,OAAO,MAAM,MAAM;EAChD,CAAC;CACH,CAAC;CAgBD,SAAS,QACP,OAC2C;EAC3C,OAAO,MAAM,OACX,KACA,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,OAAO,QAAQ;GAEtC,MAAM,eAAe,OAAOC,SAAe,YAAY,EAErD,OAAO,UACL,OAAO,IAAI,aAAa;IAEtB,MAAM,OAAO,QAAQ,GAAG;KAAE,QAAQ,MAAM;KAAQ,SAAS,MAAM;KAAS,QAAQ,MAAM;IAAO,CAAC;IAC9F,MAAM,UAAU,OAAO,SAAS,KAAK,IAAI,OAAO,OAAO,eAAe,OAAO,SAAS,IAAI,IAAI;IAE9F,QAAQ,SACN,OAAO,eAAe,SAAS,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,OAAO,eAAe,SAAS,IAAI,CAAC;GACrG,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,OAAO,GAAG,OAAO,KAAK,EACxD,CAAC;GAED,QAAQ,OAAO,gBAAA,CAAiB,SAAS,YAAY;GAErD,OAAO,OAAO;EAChB,CAAC,CACH;CACF;CAGA,MAAM,QAAQ,OAAO,eAAe,KAAK,QAAQ;EAAE,OAAO;EAAM,UAAU;CAAK,CAAC;CAEhF,OAAO,OAAO,OAAO,OAAO;GACzB,SAAS;EACV;EACA,QAAQ,SAAS;EACjB,aAAa,SAAS,eAAe,CAAC;EACtC;EACA;EACA,aAAa,MAAM,OAAO,KAAK,WAAW;EAC1C,MAAM;EACN,OAAO,YAAuB,MAAM;CACtC,CAAC;AACH"}
@@ -3,7 +3,7 @@ import * as Schema from "effect/Schema";
3
3
  import * as Response from "effect/ai/Response";
4
4
  import * as SchemaAST from "effect/SchemaAST";
5
5
  declare namespace AssistantEvent_d_exports {
6
- export { AnyEntry, AppEventSchema, AppTag, AssistantEvent, AssistantMessage, Attachment, BlockAnswer, BlocksClosed, ChatDeleted, Entry, Input, MessageBlock, ModelOutput, ModelPart, SpecialistFinished, SpecialistStarted, StoredAppEvent, ToolCallPart, ToolOutcome, ToolResult, ToolStarted, TurnCompleted, TurnFailed, TurnStarted, UserInput, UserMessage, assistantTexts, attachmentLine, formatSize, isAppInput, isInput, isLibraryEvent, isTurnEnd, isUserInput, isWaking, maxAttachments, textWithAttachments };
6
+ export { AnyEntry, AppEventSchema, AppTag, ApprovalDecision, ApprovalRequested, ApprovalsExpired, AssistantEvent, AssistantMessage, Attachment, BlockAnswer, BlocksClosed, ChatDeleted, Entry, Input, MessageBlock, ModelOutput, ModelPart, SpecialistFinished, SpecialistStarted, StoredAppEvent, ToolCallPart, ToolOutcome, ToolResult, ToolStarted, TurnCompleted, TurnFailed, TurnStarted, UserInput, UserMessage, assistantTexts, attachmentLine, formatSize, isAppInput, isInput, isLibraryEvent, isTurnEnd, isUserInput, isWaking, maxAttachments, textWithAttachments };
7
7
  }
8
8
  /**
9
9
  * Schema for a file the user attached to a message, kept by reference.
@@ -117,13 +117,30 @@ declare const SpecialistFinished_base: Schema.Class<SpecialistFinished, Schema.T
117
117
  * @since 0.1.0
118
118
  */
119
119
  export declare class SpecialistFinished extends SpecialistFinished_base {}
120
+ declare const ApprovalDecision_base: Schema.Class<ApprovalDecision, Schema.TaggedStruct<"ApprovalDecision", {
121
+ readonly id: Schema.brand<Schema.String, "~effect-assistant/InputId">;
122
+ readonly callId: Schema.String;
123
+ readonly decision: Schema.Literals<readonly ["approved", "rejected"]>;
124
+ readonly reason: Schema.optional<Schema.String>;
125
+ }>, {}>;
126
+ /**
127
+ * Represents the user's decision on a tool call that waits for their approval: `"approved"`, so
128
+ * the call runs, or `"rejected"`, with an optional short reason the model reads.
129
+ *
130
+ * Its id is fixed per call (see `ToolApproval.approve`), so a decision is stored once. A decision on
131
+ * a call that isn't waiting any more (decided, or expired) isn't stored.
132
+ *
133
+ * @category schemas
134
+ * @since 0.4.0
135
+ */
136
+ export declare class ApprovalDecision extends ApprovalDecision_base {}
120
137
  /**
121
138
  * Schema for every input `Assistant.submit` accepts.
122
139
  *
123
140
  * @category schemas
124
141
  * @since 0.1.0
125
142
  */
126
- export declare const Input: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof SpecialistFinished]>;
143
+ export declare const Input: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof SpecialistFinished, typeof ApprovalDecision]>;
127
144
  /**
128
145
  * Represents an input to an assistant.
129
146
  *
@@ -132,7 +149,8 @@ export declare const Input: Schema.Union<readonly [typeof UserMessage, typeof Bl
132
149
  */
133
150
  export type Input = typeof Input.Type;
134
151
  /**
135
- * Schema for the inputs from the chat's user, which are messages and block answers.
152
+ * Schema for the inputs from the chat's user, which are messages, block answers, and decisions on
153
+ * tool calls that wait for their approval.
136
154
  *
137
155
  * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's
138
156
  * events come from the server.
@@ -140,7 +158,7 @@ export type Input = typeof Input.Type;
140
158
  * @category schemas
141
159
  * @since 0.1.0
142
160
  */
143
- export declare const UserInput: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer]>;
161
+ export declare const UserInput: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof ApprovalDecision]>;
144
162
  /**
145
163
  * Represents an input from the chat's user.
146
164
  *
@@ -408,13 +426,43 @@ declare const BlocksClosed_base: Schema.Class<BlocksClosed, Schema.TaggedStruct<
408
426
  * @since 0.1.0
409
427
  */
410
428
  export declare class BlocksClosed extends BlocksClosed_base {}
429
+ declare const ApprovalRequested_base: Schema.Class<ApprovalRequested, Schema.TaggedStruct<"ApprovalRequested", {
430
+ readonly callId: Schema.String;
431
+ readonly tool: Schema.String;
432
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
433
+ }>, {}>;
434
+ /**
435
+ * Represents a tool call that waits for the user's approval instead of running: its tool needs
436
+ * approval (Effect's `needsApproval`).
437
+ *
438
+ * **Details**
439
+ *
440
+ * It holds the call's id, the tool, and its params, in JSON, as the model wrote them. The model
441
+ * reads it as the call's result, saying it waits for the user; the user's
442
+ * {@link ApprovalDecision} decides it, and an approved call runs then, with the same id.
443
+ *
444
+ * @category schemas
445
+ * @since 0.4.0
446
+ */
447
+ export declare class ApprovalRequested extends ApprovalRequested_base {}
448
+ declare const ApprovalsExpired_base: Schema.Class<ApprovalsExpired, Schema.TaggedStruct<"ApprovalsExpired", {
449
+ readonly callIds: Schema.$Array<Schema.String>;
450
+ }>, {}>;
451
+ /**
452
+ * Represents tool calls that waited for the user's approval and won't run: the user moved on, like
453
+ * the ones `ToolApproval.closeStale` expires. The model reads that they weren't approved.
454
+ *
455
+ * @category schemas
456
+ * @since 0.4.0
457
+ */
458
+ export declare class ApprovalsExpired extends ApprovalsExpired_base {}
411
459
  /**
412
460
  * Schema for every event the library defines.
413
461
  *
414
462
  * @category schemas
415
463
  * @since 0.1.0
416
464
  */
417
- export declare const AssistantEvent: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof AssistantMessage, typeof ModelOutput, typeof ToolStarted, typeof ToolResult, typeof TurnStarted, typeof TurnCompleted, typeof TurnFailed, typeof SpecialistStarted, typeof SpecialistFinished, typeof BlocksClosed, typeof ChatDeleted]>;
465
+ export declare const AssistantEvent: Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof AssistantMessage, typeof ModelOutput, typeof ToolStarted, typeof ToolResult, typeof TurnStarted, typeof TurnCompleted, typeof TurnFailed, typeof SpecialistStarted, typeof SpecialistFinished, typeof BlocksClosed, typeof ApprovalRequested, typeof ApprovalDecision, typeof ApprovalsExpired, typeof ChatDeleted]>;
418
466
  /**
419
467
  * Represents an event the library defines.
420
468
  *
@@ -535,7 +583,7 @@ export declare const Entry: Schema.Struct<{
535
583
  readonly chatId: Schema.brand<Schema.TemplateLiteral<readonly ["chat:", Schema.String]>, "~effect-assistant/ChatId">;
536
584
  readonly seq: Schema.Int;
537
585
  readonly at: Schema.Finite;
538
- readonly event: Schema.Union<readonly [Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof AssistantMessage, typeof ModelOutput, typeof ToolStarted, typeof ToolResult, typeof TurnStarted, typeof TurnCompleted, typeof TurnFailed, typeof SpecialistStarted, typeof SpecialistFinished, typeof BlocksClosed, typeof ChatDeleted]>, Schema.StructWithRest<Schema.Struct<{
586
+ readonly event: Schema.Union<readonly [Schema.Union<readonly [typeof UserMessage, typeof BlockAnswer, typeof AssistantMessage, typeof ModelOutput, typeof ToolStarted, typeof ToolResult, typeof TurnStarted, typeof TurnCompleted, typeof TurnFailed, typeof SpecialistStarted, typeof SpecialistFinished, typeof BlocksClosed, typeof ApprovalRequested, typeof ApprovalDecision, typeof ApprovalsExpired, typeof ChatDeleted]>, Schema.StructWithRest<Schema.Struct<{
539
587
  readonly _tag: Schema.brand<Schema.String, "~effect-assistant/AppTag">;
540
588
  readonly "~effect-assistant/input": Schema.optionalKey<Schema.Literal<true>>;
541
589
  }>, readonly [Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>]>]>;
@@ -549,7 +597,7 @@ export declare const Entry: Schema.Struct<{
549
597
  export type Entry = typeof Entry.Type;
550
598
  /**
551
599
  * Returns whether an event is one of the library's inputs, which are a user message, a block
552
- * answer, or a finished specialist run.
600
+ * answer, a finished specialist run, or a decision on a tool call waiting for approval.
553
601
  *
554
602
  * The application's events that came in through the assistant's `submit` are inputs too (see
555
603
  * {@link isAppInput}).
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantEvent.d.ts","names":[],"sources":["../src/AssistantEvent.ts"],"mappings":";;;;;;;;;;;;;;;qBA2Ba,YAAU,OAAA;;;;;;;;;;;;;;;qBAgBV;;;;;;;YAQD,oBAAoB,WAAW;;;;;;;qBAQ9B,aAAU;;;;;;;;;qBAiBV,iBAAc,YAAgB;;;;;;;qBAS9B,sBAAmB,cAAgB,aAAe,cAAc;;;;;;;;;;;;;;;;;;;;;qBAchE,oBAAoB;;;;;;;;;;;;;;;;qBAepB,oBAAoB;;;;;;;;;;;;;;;;;;;;;qBAgBpB,2BAA2B;;;;;;;qBAkB3B,OAAK,OAAA,uBAAA,oBAAA,oBAAA;;;;;;;YAQN,eAAe,MAAM;;;;;;;;;;qBAWpB,WAAS,OAAA,uBAAA,oBAAA;;;;;;;YAQV,mBAAmB,UAAU;;;;;;;;;;;;qBAS5B,qBAAqB;;;;;;;;;;;;;;;qBAYrB,yBAAyB;;;;;;;;;;qBAiBzB,cAAY,OAAA;;;;;;;;;;;;;;;YAWb,sBAAsB,aAAa;;;;;;;;;;qBAWlC,WAAS,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;YAQV,mBAAmB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAU5B,oBAAoB;;;;;;;;;;;;;;qBA2BpB,oBAAoB;;;;;;;;qBAYpB,aAAW,OAAA;;;;;;;YAQZ,qBAAqB,YAAY;;;;;;;;;;;;;;;;;;qBAYhC,mBAAmB;;;;;;;;;;;;;;;qBAcnB,oBAAoB;;;;;;;;;;;;qBAiBpB,sBAAsB;;;;;;;;;;;;;;qBAatB,mBAAmB;;;;;;;;;;;;;;;;;qBAenB,0BAA0B;;;;;;;;;;;qBAgB1B,oBAAoB;;;;;;;;;;;;;qBAWpB,qBAAqB;;;;;;;qBAUrB,gBAAc,OAAA,uBAAA,oBAAA,oBAAA,yBAAA,oBAAA,oBAAA,mBAAA,oBAAA,sBAAA,mBAAA,0BAAA,2BAAA,qBAAA;;;;;;;YAsBf,wBAAwB,eAAe;;;;;;;;;;;qBAgBtC,QAAM,OAAA,MAAA,OAAA;;;;;;;;;;;;;;YAeP,iBAAiB,OAAO;WAAiB;;WAA2B;;WACrE;aAAmB;eAAiB,KAAK,UAAU;;;;;;;;;;;;;;;;;;;;qBAmBjD,gBAAc,OAAA,eAAA,OAAA;;;cAG0F,OAAA,QAAA,OAAA,QAAA,OAAA,MAAA,OAAA,MAAA,OAAA;;;;;;;YAQzG,wBAAwB,eAAe;;;;;;;qBAQtC,iBAAc;WAAsB;MAAiB,SAAS;;;;;;;;;;qBAW9D,aAAU;WAAsB;;;;;;;;;;;iBAY5B;WACN,QAAQ;WACR;WACA;WACA;aAAkB;;;;;;;;;;;;qBAYhB,OAAK,OAAA;;;;;;;;;;;;;;;YAaN,eAAe,MAAM;;;;;;;;;;;qBAYpB,UAAO;WAAsB;MAAiB,SAAS;;;;;;;;qBAYvD,WAAQ,OAAW;;;;;;;qBASnB,cAAW,OAAW,UAAQ,SAAS;;;;;;;qBASvC,YAAS;WAAsB;MAAiB,SAAS,gBAAgB;;;;;;;qBASzE,iBAAc,SAAa,cAAc,cAAY"}
1
+ {"version":3,"file":"AssistantEvent.d.ts","names":[],"sources":["../src/AssistantEvent.ts"],"mappings":";;;;;;;;;;;;;;;qBA2Ba,YAAU,OAAA;;;;;;;;;;;;;;;qBAgBV;;;;;;;YAQD,oBAAoB,WAAW;;;;;;;qBAQ9B,aAAU;;;;;;;;;qBAiBV,iBAAc,YAAgB;;;;;;;qBAS9B,sBAAmB,cAAgB,aAAe,cAAc;;;;;;;;;;;;;;;;;;;;;qBAchE,oBAAoB;;;;;;;;;;;;;;;;qBAepB,oBAAoB;;;;;;;;;;;;;;;;;;;;;qBAgBpB,2BAA2B;;;;;;;;;;;;;;;;;qBAsB3B,yBAAyB;;;;;;;qBAazB,OAAK,OAAA,uBAAA,oBAAA,oBAAA,2BAAA;;;;;;;YAQN,eAAe,MAAM;;;;;;;;;;;qBAYpB,WAAS,OAAA,uBAAA,oBAAA,oBAAA;;;;;;;YAQV,mBAAmB,UAAU;;;;;;;;;;;;qBAS5B,qBAAqB;;;;;;;;;;;;;;;qBAYrB,yBAAyB;;;;;;;;;;qBAiBzB,cAAY,OAAA;;;;;;;;;;;;;;;YAWb,sBAAsB,aAAa;;;;;;;;;;qBAWlC,WAAS,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;YAQV,mBAAmB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAU5B,oBAAoB;;;;;;;;;;;;;;qBA2BpB,oBAAoB;;;;;;;;qBAYpB,aAAW,OAAA;;;;;;;YAQZ,qBAAqB,YAAY;;;;;;;;;;;;;;;;;;qBAYhC,mBAAmB;;;;;;;;;;;;;;;qBAcnB,oBAAoB;;;;;;;;;;;;qBAiBpB,sBAAsB;;;;;;;;;;;;;;qBAatB,mBAAmB;;;;;;;;;;;;;;;;;qBAenB,0BAA0B;;;;;;;;;;;qBAgB1B,oBAAoB;;;;;;;;;;;;;qBAWpB,qBAAqB;;;;;;;;;;;;;;;;;;;qBAiBrB,0BAA0B;;;;;;;;;;;qBAa1B,yBAAyB;;;;;;;qBAUzB,gBAAc,OAAA,uBAAA,oBAAA,oBAAA,yBAAA,oBAAA,oBAAA,mBAAA,oBAAA,sBAAA,mBAAA,0BAAA,2BAAA,qBAAA,0BAAA,yBAAA,yBAAA;;;;;;;YAyBf,wBAAwB,eAAe;;;;;;;;;;;qBAgBtC,QAAM,OAAA,MAAA,OAAA;;;;;;;;;;;;;;YAeP,iBAAiB,OAAO;WAAiB;;WAA2B;;WACrE;aAAmB;eAAiB,KAAK,UAAU;;;;;;;;;;;;;;;;;;;;qBAmBjD,gBAAc,OAAA,eAAA,OAAA;;;cAG0F,OAAA,QAAA,OAAA,QAAA,OAAA,MAAA,OAAA,MAAA,OAAA;;;;;;;YAQzG,wBAAwB,eAAe;;;;;;;qBAQtC,iBAAc;WAAsB;MAAiB,SAAS;;;;;;;;;;qBAW9D,aAAU;WAAsB;;;;;;;;;;;iBAY5B;WACN,QAAQ;WACR;WACA;WACA;aAAkB;;;;;;;;;;;;qBAYhB,OAAK,OAAA;;;;;;;;;;;;;;;YAaN,eAAe,MAAM;;;;;;;;;;;qBAYpB,UAAO;WAAsB;MAAiB,SAAS;;;;;;;;qBAavD,WAAQ,OAAW;;;;;;;qBASnB,cAAW,OAAW,UAAQ,SAAS;;;;;;;qBAWvC,YAAS;WAAsB;MAAiB,SAAS,gBAAgB;;;;;;;qBASzE,iBAAc,SAAa,cAAc,cAAY"}
@@ -19,6 +19,9 @@ import * as Response from "effect/ai/Response";
19
19
  */
20
20
  var AssistantEvent_exports = /* @__PURE__ */ __exportAll({
21
21
  AppTag: () => AppTag,
22
+ ApprovalDecision: () => ApprovalDecision,
23
+ ApprovalRequested: () => ApprovalRequested,
24
+ ApprovalsExpired: () => ApprovalsExpired,
22
25
  AssistantEvent: () => AssistantEvent,
23
26
  AssistantMessage: () => AssistantMessage,
24
27
  Attachment: () => Attachment,
@@ -156,6 +159,22 @@ var SpecialistFinished = class extends Schema.TaggedClass()("SpecialistFinished"
156
159
  error: Schema.optional(Schema.String)
157
160
  }) {};
158
161
  /**
162
+ * Represents the user's decision on a tool call that waits for their approval: `"approved"`, so
163
+ * the call runs, or `"rejected"`, with an optional short reason the model reads.
164
+ *
165
+ * Its id is fixed per call (see `ToolApproval.approve`), so a decision is stored once. A decision on
166
+ * a call that isn't waiting any more (decided, or expired) isn't stored.
167
+ *
168
+ * @category schemas
169
+ * @since 0.4.0
170
+ */
171
+ var ApprovalDecision = class extends Schema.TaggedClass()("ApprovalDecision", {
172
+ id: InputId,
173
+ callId: Schema.String,
174
+ decision: Schema.Literals(["approved", "rejected"]),
175
+ reason: Schema.optional(Schema.String.check(Schema.isMaxLength(500)))
176
+ }) {};
177
+ /**
159
178
  * Schema for every input `Assistant.submit` accepts.
160
179
  *
161
180
  * @category schemas
@@ -164,10 +183,12 @@ var SpecialistFinished = class extends Schema.TaggedClass()("SpecialistFinished"
164
183
  const Input = Schema.Union([
165
184
  UserMessage,
166
185
  BlockAnswer,
167
- SpecialistFinished
186
+ SpecialistFinished,
187
+ ApprovalDecision
168
188
  ]);
169
189
  /**
170
- * Schema for the inputs from the chat's user, which are messages and block answers.
190
+ * Schema for the inputs from the chat's user, which are messages, block answers, and decisions on
191
+ * tool calls that wait for their approval.
171
192
  *
172
193
  * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's
173
194
  * events come from the server.
@@ -175,7 +196,11 @@ const Input = Schema.Union([
175
196
  * @category schemas
176
197
  * @since 0.1.0
177
198
  */
178
- const UserInput = Schema.Union([UserMessage, BlockAnswer]);
199
+ const UserInput = Schema.Union([
200
+ UserMessage,
201
+ BlockAnswer,
202
+ ApprovalDecision
203
+ ]);
179
204
  /**
180
205
  * Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its
181
206
  * props.
@@ -364,6 +389,32 @@ var ChatDeleted = class extends Schema.TaggedClass()("ChatDeleted", {}) {};
364
389
  */
365
390
  var BlocksClosed = class extends Schema.TaggedClass()("BlocksClosed", { messageIds: Schema.Array(Schema.String) }) {};
366
391
  /**
392
+ * Represents a tool call that waits for the user's approval instead of running: its tool needs
393
+ * approval (Effect's `needsApproval`).
394
+ *
395
+ * **Details**
396
+ *
397
+ * It holds the call's id, the tool, and its params, in JSON, as the model wrote them. The model
398
+ * reads it as the call's result, saying it waits for the user; the user's
399
+ * {@link ApprovalDecision} decides it, and an approved call runs then, with the same id.
400
+ *
401
+ * @category schemas
402
+ * @since 0.4.0
403
+ */
404
+ var ApprovalRequested = class extends Schema.TaggedClass()("ApprovalRequested", {
405
+ callId: Schema.String,
406
+ tool: Schema.String,
407
+ params: Schema.Json
408
+ }) {};
409
+ /**
410
+ * Represents tool calls that waited for the user's approval and won't run: the user moved on, like
411
+ * the ones `ToolApproval.closeStale` expires. The model reads that they weren't approved.
412
+ *
413
+ * @category schemas
414
+ * @since 0.4.0
415
+ */
416
+ var ApprovalsExpired = class extends Schema.TaggedClass()("ApprovalsExpired", { callIds: Schema.Array(Schema.String) }) {};
417
+ /**
367
418
  * Schema for every event the library defines.
368
419
  *
369
420
  * @category schemas
@@ -382,6 +433,9 @@ const AssistantEvent = Schema.Union([
382
433
  SpecialistStarted,
383
434
  SpecialistFinished,
384
435
  BlocksClosed,
436
+ ApprovalRequested,
437
+ ApprovalDecision,
438
+ ApprovalsExpired,
385
439
  ChatDeleted
386
440
  ]);
387
441
  const libraryTags = new Set(AssistantEvent.members.map((member) => member.fields._tag.schema.literal));
@@ -450,7 +504,7 @@ const Entry = Schema.Struct({
450
504
  });
451
505
  /**
452
506
  * Returns whether an event is one of the library's inputs, which are a user message, a block
453
- * answer, or a finished specialist run.
507
+ * answer, a finished specialist run, or a decision on a tool call waiting for approval.
454
508
  *
455
509
  * The application's events that came in through the assistant's `submit` are inputs too (see
456
510
  * {@link isAppInput}).
@@ -458,7 +512,7 @@ const Entry = Schema.Struct({
458
512
  * @category guards
459
513
  * @since 0.1.0
460
514
  */
461
- const isInput = (event) => Predicate.isTagged(event, "UserMessage") || Predicate.isTagged(event, "BlockAnswer") || Predicate.isTagged(event, "SpecialistFinished");
515
+ const isInput = (event) => Predicate.isTagged(event, "UserMessage") || Predicate.isTagged(event, "BlockAnswer") || Predicate.isTagged(event, "SpecialistFinished") || Predicate.isTagged(event, "ApprovalDecision");
462
516
  /**
463
517
  * Returns whether an input starts a turn, which every input does except a run the assistant
464
518
  * stopped itself.
@@ -473,7 +527,7 @@ const isWaking = (input) => !(Predicate.isTagged(input, "SpecialistFinished") &&
473
527
  * @category guards
474
528
  * @since 0.1.0
475
529
  */
476
- const isUserInput = (input) => Predicate.isTagged(input, "UserMessage") || Predicate.isTagged(input, "BlockAnswer");
530
+ const isUserInput = (input) => Predicate.isTagged(input, "UserMessage") || Predicate.isTagged(input, "BlockAnswer") || Predicate.isTagged(input, "ApprovalDecision");
477
531
  /**
478
532
  * Returns whether an event ends a turn, and so says how far the assistant has read.
479
533
  *
@@ -489,6 +543,6 @@ const isTurnEnd = (event) => Predicate.isTagged(event, "TurnCompleted") || Predi
489
543
  */
490
544
  const assistantTexts = (entries) => entries.flatMap(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "AssistantMessage") ? [event.text] : []);
491
545
  //#endregion
492
- export { AppTag, AssistantEvent, AssistantEvent_exports, AssistantMessage, Attachment, BlockAnswer, BlocksClosed, ChatDeleted, Entry, Input, MessageBlock, ModelOutput, ModelPart, SpecialistFinished, SpecialistStarted, StoredAppEvent, ToolCallPart, ToolOutcome, ToolResult, ToolStarted, TurnCompleted, TurnFailed, TurnStarted, UserInput, UserMessage, assistantTexts, attachmentLine, formatSize, isAppInput, isInput, isLibraryEvent, isTurnEnd, isUserInput, isWaking, maxAttachments, textWithAttachments };
546
+ export { AppTag, ApprovalDecision, ApprovalRequested, ApprovalsExpired, AssistantEvent, AssistantEvent_exports, AssistantMessage, Attachment, BlockAnswer, BlocksClosed, ChatDeleted, Entry, Input, MessageBlock, ModelOutput, ModelPart, SpecialistFinished, SpecialistStarted, StoredAppEvent, ToolCallPart, ToolOutcome, ToolResult, ToolStarted, TurnCompleted, TurnFailed, TurnStarted, UserInput, UserMessage, assistantTexts, attachmentLine, formatSize, isAppInput, isInput, isLibraryEvent, isTurnEnd, isUserInput, isWaking, maxAttachments, textWithAttachments };
493
547
 
494
548
  //# sourceMappingURL=AssistantEvent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantEvent.js","names":["makeInputId"],"sources":["../src/AssistantEvent.ts"],"sourcesContent":["/**\n * Defines the events that can happen in a chat.\n *\n * A chat is an append-only log of these events, and the log is the source of truth. A chat client\n * receives the part of the log its user sees (its `Timeline`), and the model's history is rebuilt\n * from the log before every model call. Besides the library's events ({@link AssistantEvent}), a\n * log holds the application's own events ({@link StoredAppEvent}).\n *\n * @since 0.1.0\n */\nimport * as Response from \"effect/ai/Response\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as SchemaAST from \"effect/SchemaAST\"\nimport { ChatId } from \"./ChatId.ts\"\nimport { InputId, make as makeInputId } from \"./InputId.ts\"\nimport { inputKey } from \"./internal/input.ts\"\nimport { RunId } from \"./RunId.ts\"\n\n/**\n * Schema for a file the user attached to a message, kept by reference.\n *\n * The file itself is stored elsewhere, so the log never holds its bytes.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Attachment = Schema.Struct({\n fileId: Schema.String.check(Schema.isMaxLength(64)),\n name: Schema.String.check(Schema.isMaxLength(255)),\n mediaType: Schema.String.check(Schema.isMaxLength(127)),\n /** In bytes. */\n size: Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))\n})\n\n/**\n * The maximum number of files one message can carry.\n *\n * Each attached file is a line in every prompt after the message.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const maxAttachments = 10\n\n/**\n * Represents a file the user attached to a message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Attachment = typeof Attachment.Type\n\n/**\n * Formats a byte count for people and the model, such as `2.1 MB`.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const formatSize = (bytes: number): string =>\n bytes < 1024\n ? `${bytes} B`\n : bytes < 1024 * 1024\n ? `${(bytes / 1024).toFixed(1)} KB`\n : bytes < 1024 * 1024 * 1024\n ? `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n : `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n\n/**\n * Returns the line the model reads for an attachment.\n *\n * The line names the file and never holds its content.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const attachmentLine = (attachment: Attachment): string =>\n `[attached: ${attachment.name}, ${attachment.mediaType}, ${formatSize(attachment.size)}, id ${attachment.fileId}]`\n\n/**\n * Returns a message's text followed by a line for each attached file.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const textWithAttachments = (text: string, attachments: ReadonlyArray<Attachment> | undefined): string =>\n attachments === undefined || attachments.length === 0\n ? text\n : [text, ...attachments.map(attachmentLine)].filter((part) => part.length > 0).join(\"\\n\")\n\n/**\n * Represents text the user typed, and the files they attached.\n *\n * `id` defaults to a new one. Pass the client's own id so that a retried send stores the message\n * once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class UserMessage extends Schema.TaggedClass<UserMessage>()(\"UserMessage\", {\n id: InputId.pipe(Schema.withConstructorDefault(makeInputId())),\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(Attachment).check(Schema.isMaxLength(maxAttachments)))\n}) {}\n\n/**\n * Represents the user's answer to a block.\n *\n * It holds the id of the message the block was on, and the value in the block's answer schema\n * (in its JSON form). A chat UI shows it inside the block rather than as a message.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlockAnswer extends Schema.TaggedClass<BlockAnswer>()(\"BlockAnswer\", {\n id: InputId,\n messageId: Schema.String,\n kind: Schema.String,\n value: Schema.Unknown\n}) {}\n\n/**\n * Represents a specialist run that finished working on a brief.\n *\n * Unless the assistant stopped the run, it is an input, and a turn reads its report and decides\n * what to tell the user. Its id is fixed per run and brief, so it is stored once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistFinished extends Schema.TaggedClass<SpecialistFinished>()(\"SpecialistFinished\", {\n id: InputId,\n runId: RunId,\n specialist: Schema.String,\n /** Which brief this answers: 1 for the first, then one more per follow-up. */\n brief: Schema.Int,\n goal: Schema.String,\n status: Schema.Literals([\"answered\", \"failed\", \"stopped\", \"cut_off\"]),\n report: Schema.optional(Schema.String),\n error: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Schema for every input `Assistant.submit` accepts.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Input = Schema.Union([UserMessage, BlockAnswer, SpecialistFinished])\n\n/**\n * Represents an input to an assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Schema for the inputs from the chat's user, which are messages and block answers.\n *\n * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's\n * events come from the server.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserInput = Schema.Union([UserMessage, BlockAnswer])\n\n/**\n * Represents an input from the chat's user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserInput = typeof UserInput.Type\n\n/**\n * Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its\n * props.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class MessageBlock extends Schema.Class<MessageBlock>(\"effect-assistant/MessageBlock\")({\n kind: Schema.String,\n props: Schema.Unknown\n}) {}\n\n/**\n * Represents a message the assistant sent the user with `send_user_message`, optionally with a\n * block.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class AssistantMessage extends Schema.TaggedClass<AssistantMessage>()(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n block: Schema.optional(MessageBlock),\n /** Earlier messages whose block the user answered by typing instead. Their blocks close. */\n closes: Schema.optional(Schema.Array(Schema.String))\n}) {}\n\n/**\n * Schema for a tool call the model made.\n *\n * It is `effect/ai`'s tool call part (`Response.ToolCallPart`) for any tool, with its params in\n * JSON, as the model wrote them.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolCallPart = Schema.Struct({\n ...Response.ToolCallPart(\"\", Schema.Json).fields,\n name: Schema.String\n}).annotate({ identifier: \"ToolCallPart\" })\n\n/**\n * Represents a tool call the model made.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolCallPart = typeof ToolCallPart.Type\n\n/**\n * Schema for what the log keeps of a model call.\n *\n * It uses `effect/ai`'s response parts for text, reasoning (with the provider's metadata, such as\n * a thinking block's signature), and tool calls.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ModelPart = Schema.Union([Response.TextPart, Response.ReasoningPart, ToolCallPart])\n\n/**\n * Represents a part of a model call's output.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ModelPart = typeof ModelPart.Type\n\n/**\n * Represents what one model call wrote, as response parts for text, reasoning, and tool calls.\n *\n * `Prompt.fromResponseParts` turns the parts into history.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ModelOutput extends Schema.TaggedClass<ModelOutput>()(\"ModelOutput\", {\n parts: Schema.Array(ModelPart),\n /**\n * The last entry the model had read when it wrote this. Inputs after it\n * arrived while the model was working. A turn has handled what its\n * outputs had read.\n */\n seenThroughSeq: Schema.optional(Schema.Int),\n /** The model that wrote it, as the provider's id for it or the name the context gave it. */\n model: Schema.optional(Schema.String),\n /** The provider's id for the response, when it gives one. */\n responseId: Schema.optional(Schema.String),\n /** The call's token usage, with input (including the cache's reads and writes) and output. */\n usage: Schema.optional(Response.Usage),\n /** How long the call took, in milliseconds. */\n durationMillis: Schema.optional(Schema.Finite)\n}) {}\n\n/**\n * Represents a tool call that is about to run.\n *\n * It is stored before the tool runs, so after a crash the log says which calls may have done\n * something. Those calls run again with the same call id (see `AssistantToolCall`).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolStarted extends Schema.TaggedClass<ToolStarted>()(\"ToolStarted\", {\n callId: Schema.String,\n tool: Schema.String\n}) {}\n\n/**\n * Schema for how a tool call went, either `\"success\"` or `\"failure\"`, which the model reads as an\n * error.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolOutcome = Schema.Literals([\"success\", \"failure\"])\n\n/**\n * Represents how a tool call went.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolOutcome = typeof ToolOutcome.Type\n\n/**\n * Represents the result of one tool call, as the model reads it.\n *\n * It holds the tool's success or failure in its encoded (JSON) form, and which of the two it is. A\n * failure includes its reason when it has one, which is a `ToolFailure`'s `reason` or the\n * failure's tag.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolResult extends Schema.TaggedClass<ToolResult>()(\"ToolResult\", {\n callId: Schema.String,\n tool: Schema.String,\n outcome: ToolOutcome,\n result: Schema.Json,\n reason: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the start of a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnStarted extends Schema.TaggedClass<TurnStarted>()(\"TurnStarted\", {\n /** The assistant that runs it. */\n assistant: Schema.String,\n /**\n * The id of the turn's trace, when a tracer records it (not Effect's own, which exports nothing).\n */\n traceId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents a turn that finished.\n *\n * `throughSeq` is the last entry the turn read. Inputs after it still need a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnCompleted extends Schema.TaggedClass<TurnCompleted>()(\"TurnCompleted\", {\n throughSeq: Schema.Int\n}) {}\n\n/**\n * Represents a turn that failed.\n *\n * `throughSeq` covers everything stored when the turn failed, so a failing input isn't retried\n * forever.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnFailed extends Schema.TaggedClass<TurnFailed>()(\"TurnFailed\", {\n throughSeq: Schema.Int,\n message: Schema.String\n}) {}\n\n/**\n * Represents a specialist run that started working, either spawned or continued by a follow-up\n * message.\n *\n * The model doesn't see it, since the call that started the run says so, and the run's report\n * arrives as a {@link SpecialistFinished}.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistStarted extends Schema.TaggedClass<SpecialistStarted>()(\"SpecialistStarted\", {\n runId: RunId,\n specialist: Schema.String,\n /** The goal it's working on. */\n goal: Schema.String\n}) {}\n\n/**\n * Represents the deletion of a chat.\n *\n * The chat stores nothing after it, the work running in the chat stopped, and its subscribers'\n * streams end here. Its log stays.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ChatDeleted extends Schema.TaggedClass<ChatDeleted>()(\"ChatDeleted\", {}) {}\n\n/**\n * Represents blocks that closed without an answer, like the ones `BlockKit.closeStale` closes\n * from a turn once the user moved on.\n *\n * The model doesn't see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlocksClosed extends Schema.TaggedClass<BlocksClosed>()(\"BlocksClosed\", {\n messageIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Schema for every event the library defines.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantEvent = Schema.Union([\n UserMessage,\n BlockAnswer,\n AssistantMessage,\n ModelOutput,\n ToolStarted,\n ToolResult,\n TurnStarted,\n TurnCompleted,\n TurnFailed,\n SpecialistStarted,\n SpecialistFinished,\n BlocksClosed,\n ChatDeleted\n])\n\n/**\n * Represents an event the library defines.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantEvent = typeof AssistantEvent.Type\n\nconst libraryTags: ReadonlySet<string> = new Set(\n AssistantEvent.members.map((member) => member.fields._tag.schema.literal)\n)\n\n/**\n * Schema for the tag of one of the application's own events.\n *\n * It accepts any tag but the library's, and it is branded, so narrowing an entry's event to one\n * of the library's events (`Predicate.isTagged(event, \"UserMessage\")`) never leaves an app event\n * in.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AppTag = Schema.String.pipe(Schema.brand(\"~effect-assistant/AppTag\"))\n\n/**\n * Represents the schema of one of the application's own events, as `Assistant.make`'s `events`\n * takes it.\n *\n * **Details**\n *\n * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) whose `_tag` is a\n * string literal other than the library's, and that needs no services. The log stores the event in\n * the schema's JSON form.\n *\n * @category models\n * @since 0.3.0\n */\nexport type AppEventSchema = Schema.Codec<{ readonly _tag: string }, { readonly _tag: string }> & {\n readonly fields: { readonly _tag: { readonly ast: SchemaAST.AST } }\n}\n\n/**\n * Schema for one of the application's own events as it is stored, with its `_tag` and its fields\n * in JSON.\n *\n * **Details**\n *\n * An assistant made with `events: [Summary]` decodes it to a `Summary` in `CurrentChat`'s\n * `entries` and in its `subscribe`.\n *\n * An event that came in through the assistant's `submit`, from outside a turn, is an input, and the\n * log marks it with a `\"~effect-assistant/input\": true` key (see {@link isAppInput}); one a turn\n * appended has none. Decoding drops the key, so the app's schemas never see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const StoredAppEvent = Schema.StructWithRest(\n Schema.Struct({ _tag: AppTag, [inputKey]: Schema.optionalKey(Schema.Literal(true)) }),\n [Schema.Record(Schema.String, Schema.Json)]\n).check(Schema.makeFilter((event) => !libraryTags.has(event._tag) || `${event._tag} is one of the library's events`))\n\n/**\n * Represents one of the application's own events, as it is stored.\n *\n * @category models\n * @since 0.1.0\n */\nexport type StoredAppEvent = typeof StoredAppEvent.Type\n\n/**\n * Returns whether an event is one of the library's, not one of the app's own.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isLibraryEvent = (event: { readonly _tag: string }): event is AssistantEvent => libraryTags.has(event._tag)\n\n/**\n * Returns whether one of the application's events, as the log stores it, is an input: it came in\n * through the assistant's `submit`, from outside a turn, so storing it woke a turn. An event a turn\n * appended (`CurrentChat`'s `append`) isn't, and neither is a decoded one, which no longer has the\n * mark.\n *\n * @category guards\n * @since 0.3.0\n */\nexport const isAppInput = (event: { readonly _tag: string }): boolean =>\n !isLibraryEvent(event) && Predicate.hasProperty(event, inputKey) && event[inputKey] === true\n\n/**\n * Represents a chat's entry with any event in it, either the library's or the application's own\n * (decoded or not).\n *\n * The folds over a chat take this type, so a client's typed entries fit too.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AnyEntry {\n readonly chatId: ChatId\n readonly seq: number\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\n/**\n * Schema for a stored event with its position in the chat.\n *\n * `seq` starts at 1 and has no gaps. `at` is the time the event was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Entry = Schema.Struct({\n chatId: ChatId,\n seq: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent, StoredAppEvent])\n})\n\n/**\n * Represents a stored event with its position in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Entry = typeof Entry.Type\n\n/**\n * Returns whether an event is one of the library's inputs, which are a user message, a block\n * answer, or a finished specialist run.\n *\n * The application's events that came in through the assistant's `submit` are inputs too (see\n * {@link isAppInput}).\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isInput = (event: { readonly _tag: string }): event is Input =>\n Predicate.isTagged(event, \"UserMessage\") ||\n Predicate.isTagged(event, \"BlockAnswer\") ||\n Predicate.isTagged(event, \"SpecialistFinished\")\n\n/**\n * Returns whether an input starts a turn, which every input does except a run the assistant\n * stopped itself.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isWaking = (input: Input): boolean =>\n !(Predicate.isTagged(input, \"SpecialistFinished\") && input.status === \"stopped\")\n\n/**\n * Returns whether an input came from the chat's user, and so is owed a reply.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isUserInput = (input: Input): input is UserInput =>\n Predicate.isTagged(input, \"UserMessage\") || Predicate.isTagged(input, \"BlockAnswer\")\n\n/**\n * Returns whether an event ends a turn, and so says how far the assistant has read.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isTurnEnd = (event: { readonly _tag: string }): event is TurnCompleted | TurnFailed =>\n Predicate.isTagged(event, \"TurnCompleted\") || Predicate.isTagged(event, \"TurnFailed\")\n\n/**\n * Returns the text of every message the user was shown, in order.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const assistantTexts = (entries: ReadonlyArray<AnyEntry>): ReadonlyArray<string> =>\n entries.flatMap(({ event }) =>\n isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") ? [event.text] : []\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssistantEvent.js","names":["makeInputId"],"sources":["../src/AssistantEvent.ts"],"sourcesContent":["/**\n * Defines the events that can happen in a chat.\n *\n * A chat is an append-only log of these events, and the log is the source of truth. A chat client\n * receives the part of the log its user sees (its `Timeline`), and the model's history is rebuilt\n * from the log before every model call. Besides the library's events ({@link AssistantEvent}), a\n * log holds the application's own events ({@link StoredAppEvent}).\n *\n * @since 0.1.0\n */\nimport * as Response from \"effect/ai/Response\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport type * as SchemaAST from \"effect/SchemaAST\"\nimport { ChatId } from \"./ChatId.ts\"\nimport { InputId, make as makeInputId } from \"./InputId.ts\"\nimport { inputKey } from \"./internal/input.ts\"\nimport { RunId } from \"./RunId.ts\"\n\n/**\n * Schema for a file the user attached to a message, kept by reference.\n *\n * The file itself is stored elsewhere, so the log never holds its bytes.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Attachment = Schema.Struct({\n fileId: Schema.String.check(Schema.isMaxLength(64)),\n name: Schema.String.check(Schema.isMaxLength(255)),\n mediaType: Schema.String.check(Schema.isMaxLength(127)),\n /** In bytes. */\n size: Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))\n})\n\n/**\n * The maximum number of files one message can carry.\n *\n * Each attached file is a line in every prompt after the message.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const maxAttachments = 10\n\n/**\n * Represents a file the user attached to a message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Attachment = typeof Attachment.Type\n\n/**\n * Formats a byte count for people and the model, such as `2.1 MB`.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const formatSize = (bytes: number): string =>\n bytes < 1024\n ? `${bytes} B`\n : bytes < 1024 * 1024\n ? `${(bytes / 1024).toFixed(1)} KB`\n : bytes < 1024 * 1024 * 1024\n ? `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n : `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n\n/**\n * Returns the line the model reads for an attachment.\n *\n * The line names the file and never holds its content.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const attachmentLine = (attachment: Attachment): string =>\n `[attached: ${attachment.name}, ${attachment.mediaType}, ${formatSize(attachment.size)}, id ${attachment.fileId}]`\n\n/**\n * Returns a message's text followed by a line for each attached file.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const textWithAttachments = (text: string, attachments: ReadonlyArray<Attachment> | undefined): string =>\n attachments === undefined || attachments.length === 0\n ? text\n : [text, ...attachments.map(attachmentLine)].filter((part) => part.length > 0).join(\"\\n\")\n\n/**\n * Represents text the user typed, and the files they attached.\n *\n * `id` defaults to a new one. Pass the client's own id so that a retried send stores the message\n * once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class UserMessage extends Schema.TaggedClass<UserMessage>()(\"UserMessage\", {\n id: InputId.pipe(Schema.withConstructorDefault(makeInputId())),\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(Attachment).check(Schema.isMaxLength(maxAttachments)))\n}) {}\n\n/**\n * Represents the user's answer to a block.\n *\n * It holds the id of the message the block was on, and the value in the block's answer schema\n * (in its JSON form). A chat UI shows it inside the block rather than as a message.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlockAnswer extends Schema.TaggedClass<BlockAnswer>()(\"BlockAnswer\", {\n id: InputId,\n messageId: Schema.String,\n kind: Schema.String,\n value: Schema.Unknown\n}) {}\n\n/**\n * Represents a specialist run that finished working on a brief.\n *\n * Unless the assistant stopped the run, it is an input, and a turn reads its report and decides\n * what to tell the user. Its id is fixed per run and brief, so it is stored once.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistFinished extends Schema.TaggedClass<SpecialistFinished>()(\"SpecialistFinished\", {\n id: InputId,\n runId: RunId,\n specialist: Schema.String,\n /** Which brief this answers: 1 for the first, then one more per follow-up. */\n brief: Schema.Int,\n goal: Schema.String,\n status: Schema.Literals([\"answered\", \"failed\", \"stopped\", \"cut_off\"]),\n report: Schema.optional(Schema.String),\n error: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the user's decision on a tool call that waits for their approval: `\"approved\"`, so\n * the call runs, or `\"rejected\"`, with an optional short reason the model reads.\n *\n * Its id is fixed per call (see `ToolApproval.approve`), so a decision is stored once. A decision on\n * a call that isn't waiting any more (decided, or expired) isn't stored.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalDecision extends Schema.TaggedClass<ApprovalDecision>()(\"ApprovalDecision\", {\n id: InputId,\n callId: Schema.String,\n decision: Schema.Literals([\"approved\", \"rejected\"]),\n reason: Schema.optional(Schema.String.check(Schema.isMaxLength(500)))\n}) {}\n\n/**\n * Schema for every input `Assistant.submit` accepts.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Input = Schema.Union([UserMessage, BlockAnswer, SpecialistFinished, ApprovalDecision])\n\n/**\n * Represents an input to an assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Schema for the inputs from the chat's user, which are messages, block answers, and decisions on\n * tool calls that wait for their approval.\n *\n * A turn owes each one a reply. It is also what a chat client usually may submit, since the app's\n * events come from the server.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserInput = Schema.Union([UserMessage, BlockAnswer, ApprovalDecision])\n\n/**\n * Represents an input from the chat's user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserInput = typeof UserInput.Type\n\n/**\n * Represents the UI shown with a message, as a block kind from the assistant's `BlockKit` and its\n * props.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class MessageBlock extends Schema.Class<MessageBlock>(\"effect-assistant/MessageBlock\")({\n kind: Schema.String,\n props: Schema.Unknown\n}) {}\n\n/**\n * Represents a message the assistant sent the user with `send_user_message`, optionally with a\n * block.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class AssistantMessage extends Schema.TaggedClass<AssistantMessage>()(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n block: Schema.optional(MessageBlock),\n /** Earlier messages whose block the user answered by typing instead. Their blocks close. */\n closes: Schema.optional(Schema.Array(Schema.String))\n}) {}\n\n/**\n * Schema for a tool call the model made.\n *\n * It is `effect/ai`'s tool call part (`Response.ToolCallPart`) for any tool, with its params in\n * JSON, as the model wrote them.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolCallPart = Schema.Struct({\n ...Response.ToolCallPart(\"\", Schema.Json).fields,\n name: Schema.String\n}).annotate({ identifier: \"ToolCallPart\" })\n\n/**\n * Represents a tool call the model made.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolCallPart = typeof ToolCallPart.Type\n\n/**\n * Schema for what the log keeps of a model call.\n *\n * It uses `effect/ai`'s response parts for text, reasoning (with the provider's metadata, such as\n * a thinking block's signature), and tool calls.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ModelPart = Schema.Union([Response.TextPart, Response.ReasoningPart, ToolCallPart])\n\n/**\n * Represents a part of a model call's output.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ModelPart = typeof ModelPart.Type\n\n/**\n * Represents what one model call wrote, as response parts for text, reasoning, and tool calls.\n *\n * `Prompt.fromResponseParts` turns the parts into history.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ModelOutput extends Schema.TaggedClass<ModelOutput>()(\"ModelOutput\", {\n parts: Schema.Array(ModelPart),\n /**\n * The last entry the model had read when it wrote this. Inputs after it\n * arrived while the model was working. A turn has handled what its\n * outputs had read.\n */\n seenThroughSeq: Schema.optional(Schema.Int),\n /** The model that wrote it, as the provider's id for it or the name the context gave it. */\n model: Schema.optional(Schema.String),\n /** The provider's id for the response, when it gives one. */\n responseId: Schema.optional(Schema.String),\n /** The call's token usage, with input (including the cache's reads and writes) and output. */\n usage: Schema.optional(Response.Usage),\n /** How long the call took, in milliseconds. */\n durationMillis: Schema.optional(Schema.Finite)\n}) {}\n\n/**\n * Represents a tool call that is about to run.\n *\n * It is stored before the tool runs, so after a crash the log says which calls may have done\n * something. Those calls run again with the same call id (see `AssistantToolCall`).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolStarted extends Schema.TaggedClass<ToolStarted>()(\"ToolStarted\", {\n callId: Schema.String,\n tool: Schema.String\n}) {}\n\n/**\n * Schema for how a tool call went, either `\"success\"` or `\"failure\"`, which the model reads as an\n * error.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ToolOutcome = Schema.Literals([\"success\", \"failure\"])\n\n/**\n * Represents how a tool call went.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ToolOutcome = typeof ToolOutcome.Type\n\n/**\n * Represents the result of one tool call, as the model reads it.\n *\n * It holds the tool's success or failure in its encoded (JSON) form, and which of the two it is. A\n * failure includes its reason when it has one, which is a `ToolFailure`'s `reason` or the\n * failure's tag.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ToolResult extends Schema.TaggedClass<ToolResult>()(\"ToolResult\", {\n callId: Schema.String,\n tool: Schema.String,\n outcome: ToolOutcome,\n result: Schema.Json,\n reason: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents the start of a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnStarted extends Schema.TaggedClass<TurnStarted>()(\"TurnStarted\", {\n /** The assistant that runs it. */\n assistant: Schema.String,\n /**\n * The id of the turn's trace, when a tracer records it (not Effect's own, which exports nothing).\n */\n traceId: Schema.optional(Schema.String)\n}) {}\n\n/**\n * Represents a turn that finished.\n *\n * `throughSeq` is the last entry the turn read. Inputs after it still need a turn.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnCompleted extends Schema.TaggedClass<TurnCompleted>()(\"TurnCompleted\", {\n throughSeq: Schema.Int\n}) {}\n\n/**\n * Represents a turn that failed.\n *\n * `throughSeq` covers everything stored when the turn failed, so a failing input isn't retried\n * forever.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class TurnFailed extends Schema.TaggedClass<TurnFailed>()(\"TurnFailed\", {\n throughSeq: Schema.Int,\n message: Schema.String\n}) {}\n\n/**\n * Represents a specialist run that started working, either spawned or continued by a follow-up\n * message.\n *\n * The model doesn't see it, since the call that started the run says so, and the run's report\n * arrives as a {@link SpecialistFinished}.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class SpecialistStarted extends Schema.TaggedClass<SpecialistStarted>()(\"SpecialistStarted\", {\n runId: RunId,\n specialist: Schema.String,\n /** The goal it's working on. */\n goal: Schema.String\n}) {}\n\n/**\n * Represents the deletion of a chat.\n *\n * The chat stores nothing after it, the work running in the chat stopped, and its subscribers'\n * streams end here. Its log stays.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ChatDeleted extends Schema.TaggedClass<ChatDeleted>()(\"ChatDeleted\", {}) {}\n\n/**\n * Represents blocks that closed without an answer, like the ones `BlockKit.closeStale` closes\n * from a turn once the user moved on.\n *\n * The model doesn't see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class BlocksClosed extends Schema.TaggedClass<BlocksClosed>()(\"BlocksClosed\", {\n messageIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Represents a tool call that waits for the user's approval instead of running: its tool needs\n * approval (Effect's `needsApproval`).\n *\n * **Details**\n *\n * It holds the call's id, the tool, and its params, in JSON, as the model wrote them. The model\n * reads it as the call's result, saying it waits for the user; the user's\n * {@link ApprovalDecision} decides it, and an approved call runs then, with the same id.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalRequested extends Schema.TaggedClass<ApprovalRequested>()(\"ApprovalRequested\", {\n callId: Schema.String,\n tool: Schema.String,\n params: Schema.Json\n}) {}\n\n/**\n * Represents tool calls that waited for the user's approval and won't run: the user moved on, like\n * the ones `ToolApproval.closeStale` expires. The model reads that they weren't approved.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport class ApprovalsExpired extends Schema.TaggedClass<ApprovalsExpired>()(\"ApprovalsExpired\", {\n callIds: Schema.Array(Schema.String)\n}) {}\n\n/**\n * Schema for every event the library defines.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantEvent = Schema.Union([\n UserMessage,\n BlockAnswer,\n AssistantMessage,\n ModelOutput,\n ToolStarted,\n ToolResult,\n TurnStarted,\n TurnCompleted,\n TurnFailed,\n SpecialistStarted,\n SpecialistFinished,\n BlocksClosed,\n ApprovalRequested,\n ApprovalDecision,\n ApprovalsExpired,\n ChatDeleted\n])\n\n/**\n * Represents an event the library defines.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantEvent = typeof AssistantEvent.Type\n\nconst libraryTags: ReadonlySet<string> = new Set(\n AssistantEvent.members.map((member) => member.fields._tag.schema.literal)\n)\n\n/**\n * Schema for the tag of one of the application's own events.\n *\n * It accepts any tag but the library's, and it is branded, so narrowing an entry's event to one\n * of the library's events (`Predicate.isTagged(event, \"UserMessage\")`) never leaves an app event\n * in.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AppTag = Schema.String.pipe(Schema.brand(\"~effect-assistant/AppTag\"))\n\n/**\n * Represents the schema of one of the application's own events, as `Assistant.make`'s `events`\n * takes it.\n *\n * **Details**\n *\n * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) whose `_tag` is a\n * string literal other than the library's, and that needs no services. The log stores the event in\n * the schema's JSON form.\n *\n * @category models\n * @since 0.3.0\n */\nexport type AppEventSchema = Schema.Codec<{ readonly _tag: string }, { readonly _tag: string }> & {\n readonly fields: { readonly _tag: { readonly ast: SchemaAST.AST } }\n}\n\n/**\n * Schema for one of the application's own events as it is stored, with its `_tag` and its fields\n * in JSON.\n *\n * **Details**\n *\n * An assistant made with `events: [Summary]` decodes it to a `Summary` in `CurrentChat`'s\n * `entries` and in its `subscribe`.\n *\n * An event that came in through the assistant's `submit`, from outside a turn, is an input, and the\n * log marks it with a `\"~effect-assistant/input\": true` key (see {@link isAppInput}); one a turn\n * appended has none. Decoding drops the key, so the app's schemas never see it.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const StoredAppEvent = Schema.StructWithRest(\n Schema.Struct({ _tag: AppTag, [inputKey]: Schema.optionalKey(Schema.Literal(true)) }),\n [Schema.Record(Schema.String, Schema.Json)]\n).check(Schema.makeFilter((event) => !libraryTags.has(event._tag) || `${event._tag} is one of the library's events`))\n\n/**\n * Represents one of the application's own events, as it is stored.\n *\n * @category models\n * @since 0.1.0\n */\nexport type StoredAppEvent = typeof StoredAppEvent.Type\n\n/**\n * Returns whether an event is one of the library's, not one of the app's own.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isLibraryEvent = (event: { readonly _tag: string }): event is AssistantEvent => libraryTags.has(event._tag)\n\n/**\n * Returns whether one of the application's events, as the log stores it, is an input: it came in\n * through the assistant's `submit`, from outside a turn, so storing it woke a turn. An event a turn\n * appended (`CurrentChat`'s `append`) isn't, and neither is a decoded one, which no longer has the\n * mark.\n *\n * @category guards\n * @since 0.3.0\n */\nexport const isAppInput = (event: { readonly _tag: string }): boolean =>\n !isLibraryEvent(event) && Predicate.hasProperty(event, inputKey) && event[inputKey] === true\n\n/**\n * Represents a chat's entry with any event in it, either the library's or the application's own\n * (decoded or not).\n *\n * The folds over a chat take this type, so a client's typed entries fit too.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AnyEntry {\n readonly chatId: ChatId\n readonly seq: number\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\n/**\n * Schema for a stored event with its position in the chat.\n *\n * `seq` starts at 1 and has no gaps. `at` is the time the event was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Entry = Schema.Struct({\n chatId: ChatId,\n seq: Schema.Int,\n at: Schema.Finite,\n event: Schema.Union([AssistantEvent, StoredAppEvent])\n})\n\n/**\n * Represents a stored event with its position in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Entry = typeof Entry.Type\n\n/**\n * Returns whether an event is one of the library's inputs, which are a user message, a block\n * answer, a finished specialist run, or a decision on a tool call waiting for approval.\n *\n * The application's events that came in through the assistant's `submit` are inputs too (see\n * {@link isAppInput}).\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isInput = (event: { readonly _tag: string }): event is Input =>\n Predicate.isTagged(event, \"UserMessage\") ||\n Predicate.isTagged(event, \"BlockAnswer\") ||\n Predicate.isTagged(event, \"SpecialistFinished\") ||\n Predicate.isTagged(event, \"ApprovalDecision\")\n\n/**\n * Returns whether an input starts a turn, which every input does except a run the assistant\n * stopped itself.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isWaking = (input: Input): boolean =>\n !(Predicate.isTagged(input, \"SpecialistFinished\") && input.status === \"stopped\")\n\n/**\n * Returns whether an input came from the chat's user, and so is owed a reply.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isUserInput = (input: Input): input is UserInput =>\n Predicate.isTagged(input, \"UserMessage\") ||\n Predicate.isTagged(input, \"BlockAnswer\") ||\n Predicate.isTagged(input, \"ApprovalDecision\")\n\n/**\n * Returns whether an event ends a turn, and so says how far the assistant has read.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isTurnEnd = (event: { readonly _tag: string }): event is TurnCompleted | TurnFailed =>\n Predicate.isTagged(event, \"TurnCompleted\") || Predicate.isTagged(event, \"TurnFailed\")\n\n/**\n * Returns the text of every message the user was shown, in order.\n *\n * @category utils\n * @since 0.1.0\n */\nexport const assistantTexts = (entries: ReadonlyArray<AnyEntry>): ReadonlyArray<string> =>\n entries.flatMap(({ event }) =>\n isLibraryEvent(event) && Predicate.isTagged(event, \"AssistantMessage\") ? [event.text] : []\n )\n"],"mappings":"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"}
@@ -130,8 +130,10 @@ export interface Options<T extends Record<string, Tool.Any> = NoTools> extends S
130
130
  * the step's result and decides what comes next, with `AssistantLoop.next(state)` or
131
131
  * `AssistantLoop.stop()`.
132
132
  *
133
- * A tool with Effect's `needsApproval` set fails the loop before the model is called: the
134
- * library doesn't run calls that wait for approval (see the guide on involving the user).
133
+ * In a turn's loop, a call to a tool with Effect's `needsApproval` doesn't run: the step emits an
134
+ * `ApprovalRequested` for it, and ends the turn, which waits for the user (see `ToolApproval`). A
135
+ * call the user approved runs at the start of the next step, before the model is called. A
136
+ * specialist's run can't wait for the user: such a call fails, and the run reads why.
135
137
  *
136
138
  * @category constructors
137
139
  * @since 0.1.0
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantLoop.d.ts","names":[],"sources":["../src/AssistantLoop.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAkCY,QAAQ;;;;;;;;;YAUR,cAAc;;;;;;;YAQd,aAAa;;;;;;;YAQb,KAAK,GAAG,KAAK,OAAU,GAAG;;;;;;;qBAQzB,cAAK;;;;;;;qBAQL,aAAI;;;;;;;qBAQJ,aAAI;;;;;;;;;;qBAWJ,aAAI;KAEZ,UAAU;;;;;;;;YASH,gBAAgB,UAAU,eAAe,KAAK,QAAQ,KAAK,YAAY,KAAK,aAAsB;;;;;;;iBAQ7F,YAAY,UAAU,eAAe,KAAK,OAAO;;WAEvD,uBAAuB;;;;;WAKvB,UAAU,QAAQ,QAAQ;;;;;;WAM1B,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;;;;;WAKvD,aAAa,cAAc;;;;;;;;iBASrB,QAAQ,UAAU,eAAe,KAAK,OAAO,iBAAiB,YAAY;;WAEhF;;;;;;;;;;;;;;;;;;qBA0BE,OAAQ,UAAU,eAAe,KAAK,OAAO,SAAS,WAAW,YAAY,YAAU,SACzF,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,gBAAgB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiC7F,MAAO,UAAU,eAAe,KAAK,OAAO,SAAO,SACrD,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB,gBAAgB"}
1
+ {"version":3,"file":"AssistantLoop.d.ts","names":[],"sources":["../src/AssistantLoop.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAkCY,QAAQ;;;;;;;;;YAUR,cAAc;;;;;;;YAQd,aAAa;;;;;;;YAQb,KAAK,GAAG,KAAK,OAAU,GAAG;;;;;;;qBAQzB,cAAK;;;;;;;qBAQL,aAAI;;;;;;;qBAQJ,aAAI;;;;;;;;;;qBAWJ,aAAI;KAEZ,UAAU;;;;;;;;YASH,gBAAgB,UAAU,eAAe,KAAK,QAAQ,KAAK,YAAY,KAAK,aAAsB;;;;;;;iBAQ7F,YAAY,UAAU,eAAe,KAAK,OAAO;;WAEvD,uBAAuB;;;;;WAKvB,UAAU,QAAQ,QAAQ;;;;;;WAM1B,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;;;;;WAKvD,aAAa,cAAc;;;;;;;;iBASrB,QAAQ,UAAU,eAAe,KAAK,OAAO,iBAAiB,YAAY;;WAEhF;;;;;;;;;;;;;;;;;;;;qBA4BE,OAAQ,UAAU,eAAe,KAAK,OAAO,SAAS,WAAW,YAAY,YAAU,SACzF,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,gBAAgB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAiC7F,MAAO,UAAU,eAAe,KAAK,OAAO,SAAO,SACrD,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB,gBAAgB"}