effect-assistant 0.2.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 (113) hide show
  1. package/dist/Assistant.d.ts +77 -37
  2. package/dist/Assistant.d.ts.map +1 -1
  3. package/dist/Assistant.js +11 -4
  4. package/dist/Assistant.js.map +1 -1
  5. package/dist/AssistantActivity.d.ts +10 -16
  6. package/dist/AssistantActivity.d.ts.map +1 -1
  7. package/dist/AssistantActivity.js +11 -4
  8. package/dist/AssistantActivity.js.map +1 -1
  9. package/dist/AssistantEngine.d.ts +39 -1
  10. package/dist/AssistantEngine.d.ts.map +1 -1
  11. package/dist/AssistantEngine.js +81 -28
  12. package/dist/AssistantEngine.js.map +1 -1
  13. package/dist/AssistantEvent.d.ts +104 -11
  14. package/dist/AssistantEvent.d.ts.map +1 -1
  15. package/dist/AssistantEvent.js +88 -13
  16. package/dist/AssistantEvent.js.map +1 -1
  17. package/dist/AssistantLoop.d.ts +4 -2
  18. package/dist/AssistantLoop.d.ts.map +1 -1
  19. package/dist/AssistantLoop.js +4 -2
  20. package/dist/AssistantLoop.js.map +1 -1
  21. package/dist/BlockKit.d.ts +4 -4
  22. package/dist/BlockKit.js +1 -1
  23. package/dist/BlockKit.js.map +1 -1
  24. package/dist/ChatLog.js +1 -1
  25. package/dist/Compaction.d.ts +7 -3
  26. package/dist/Compaction.d.ts.map +1 -1
  27. package/dist/Compaction.js +19 -8
  28. package/dist/Compaction.js.map +1 -1
  29. package/dist/CurrentChat.d.ts +2 -1
  30. package/dist/CurrentChat.d.ts.map +1 -1
  31. package/dist/CurrentChat.js.map +1 -1
  32. package/dist/Instructions.js +1 -1
  33. package/dist/Skill.d.ts +86 -11
  34. package/dist/Skill.d.ts.map +1 -1
  35. package/dist/Skill.js +93 -18
  36. package/dist/Skill.js.map +1 -1
  37. package/dist/Specialist.d.ts +7 -7
  38. package/dist/Specialist.d.ts.map +1 -1
  39. package/dist/Specialist.js.map +1 -1
  40. package/dist/Timeline.d.ts +516 -0
  41. package/dist/Timeline.d.ts.map +1 -0
  42. package/dist/Timeline.js +631 -0
  43. package/dist/Timeline.js.map +1 -0
  44. package/dist/ToolApproval.d.ts +146 -0
  45. package/dist/ToolApproval.d.ts.map +1 -0
  46. package/dist/ToolApproval.js +201 -0
  47. package/dist/ToolApproval.js.map +1 -0
  48. package/dist/ToolDiscovery.d.ts +6 -4
  49. package/dist/ToolDiscovery.d.ts.map +1 -1
  50. package/dist/ToolDiscovery.js +5 -4
  51. package/dist/ToolDiscovery.js.map +1 -1
  52. package/dist/index.d.ts +4 -4
  53. package/dist/index.js +4 -4
  54. package/dist/internal/chat.js +9 -7
  55. package/dist/internal/chat.js.map +1 -1
  56. package/dist/internal/events.d.ts +15 -16
  57. package/dist/internal/events.d.ts.map +1 -1
  58. package/dist/internal/events.js +29 -28
  59. package/dist/internal/events.js.map +1 -1
  60. package/dist/internal/history.js +53 -22
  61. package/dist/internal/history.js.map +1 -1
  62. package/dist/internal/input.js +7 -0
  63. package/dist/internal/input.js.map +1 -0
  64. package/dist/internal/loop.d.ts +10 -5
  65. package/dist/internal/loop.d.ts.map +1 -1
  66. package/dist/internal/loop.js +55 -22
  67. package/dist/internal/loop.js.map +1 -1
  68. package/dist/internal/specialists.js +10 -16
  69. package/dist/internal/specialists.js.map +1 -1
  70. package/dist/internal/toolkit.d.ts +5 -7
  71. package/dist/internal/toolkit.d.ts.map +1 -1
  72. package/dist/internal/toolkit.js +28 -18
  73. package/dist/internal/toolkit.js.map +1 -1
  74. package/dist/internal/tools.js +1 -1
  75. package/dist/internal/turns.d.ts.map +1 -1
  76. package/dist/internal/turns.js +35 -42
  77. package/dist/internal/turns.js.map +1 -1
  78. package/package.json +1 -1
  79. package/src/Assistant.ts +80 -40
  80. package/src/AssistantActivity.ts +11 -18
  81. package/src/AssistantEngine.ts +110 -43
  82. package/src/AssistantEvent.ts +106 -14
  83. package/src/AssistantLoop.ts +4 -2
  84. package/src/BlockKit.ts +6 -6
  85. package/src/Compaction.ts +25 -4
  86. package/src/CurrentChat.ts +2 -1
  87. package/src/Skill.ts +133 -23
  88. package/src/Specialist.ts +9 -9
  89. package/src/Timeline.ts +947 -0
  90. package/src/ToolApproval.ts +249 -0
  91. package/src/ToolDiscovery.ts +8 -3
  92. package/src/index.ts +10 -10
  93. package/src/internal/chat.ts +15 -5
  94. package/src/internal/events.ts +43 -43
  95. package/src/internal/history.ts +94 -32
  96. package/src/internal/input.ts +3 -0
  97. package/src/internal/loop.ts +120 -34
  98. package/src/internal/specialists.ts +7 -12
  99. package/src/internal/toolkit.ts +47 -30
  100. package/src/internal/turns.ts +52 -58
  101. package/dist/AppEvent.d.ts +0 -111
  102. package/dist/AppEvent.d.ts.map +0 -1
  103. package/dist/AppEvent.js +0 -71
  104. package/dist/AppEvent.js.map +0 -1
  105. package/dist/ChatView.d.ts +0 -408
  106. package/dist/ChatView.d.ts.map +0 -1
  107. package/dist/ChatView.js +0 -443
  108. package/dist/ChatView.js.map +0 -1
  109. package/dist/internal/skills.js +0 -28
  110. package/dist/internal/skills.js.map +0 -1
  111. package/src/AppEvent.ts +0 -134
  112. package/src/ChatView.ts +0 -587
  113. package/src/internal/skills.ts +0 -37
@@ -1,6 +1,6 @@
1
1
  import type * as LanguageModel from "effect/ai/LanguageModel"
2
+ import * as Response from "effect/ai/Response"
2
3
  import type * as Tool from "effect/ai/Tool"
3
- import type * as Prompt from "effect/ai/Prompt"
4
4
  import * as Cause from "effect/Cause"
5
5
  import * as Context from "effect/Context"
6
6
  import * as Effect from "effect/Effect"
@@ -8,6 +8,7 @@ import * as Exit from "effect/Exit"
8
8
  import { identity } from "effect/Function"
9
9
  import * as Option from "effect/Option"
10
10
  import * as Predicate from "effect/Predicate"
11
+ import * as Ref from "effect/Ref"
11
12
  import * as Schema from "effect/Schema"
12
13
  import type * as Scope from "effect/Scope"
13
14
  import * as Stream from "effect/Stream"
@@ -19,8 +20,7 @@ import * as Loop from "./loop.ts"
19
20
  import type * as BlockKit from "../BlockKit.ts"
20
21
  import type { CurrentChat } from "../CurrentChat.ts"
21
22
  import * as Instructions from "../Instructions.ts"
22
- import * as Skill from "../Skill.ts"
23
- import * as Skills from "./skills.ts"
23
+ import * as ToolApproval from "../ToolApproval.ts"
24
24
  import type * as Specialist from "../Specialist.ts"
25
25
  import * as AppEvents from "./events.ts"
26
26
  import * as History from "./history.ts"
@@ -125,35 +125,22 @@ const failureMessage = (cause: Cause.Cause<unknown>): string =>
125
125
  onSome: ({ _tag, raw }) => `${_tag ?? "Error"}: ${Predicate.isString(raw) ? raw : toJsonText(raw)}`
126
126
  })
127
127
 
128
- const lastSeq = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) => entries.at(-1)?.seq ?? otherwise
129
-
130
- const toHistory = (
131
- viewed: ReadonlyArray<Assistant.Entry<any> | Prompt.Message>,
132
- app: AppEvents.AppEvents
133
- ): ReadonlyArray<Prompt.Message> => {
134
- const out: Array<Prompt.Message> = []
135
- let run: Array<AssistantEvent.AnyEntry> = []
136
-
137
- const flush = () => {
138
- out.push(...History.fromEntries(run, (event) => AppEvents.read(app, event)))
139
- run = []
140
- }
141
-
142
- for (const item of viewed) {
143
- if (isEntry(item)) run.push(item)
144
- else {
145
- flush()
146
- out.push(item)
147
- }
148
- }
149
-
150
- flush()
151
-
152
- return out
153
- }
128
+ // Approved, and not run yet: also one a crash cut off while it ran, which runs again with the same id.
129
+ const approvedCalls = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyArray<AssistantEvent.ToolCallPart> =>
130
+ [...ToolApproval.fromEntries(entries).values()].flatMap(({ request, status, outcome }) =>
131
+ status === "approved" && outcome === undefined
132
+ ? [
133
+ Response.makePart("tool-call", {
134
+ id: request.callId,
135
+ name: request.tool,
136
+ params: request.params,
137
+ providerExecuted: false
138
+ })
139
+ ]
140
+ : []
141
+ )
154
142
 
155
- const isEntry = (item: Assistant.Entry<any> | Prompt.Message): item is Assistant.Entry<any> =>
156
- Predicate.hasProperty(item, "seq")
143
+ const lastSeq = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) => entries.at(-1)?.seq ?? otherwise
157
144
 
158
145
  // The handler's stream can end between a step's output and its tools, or while they run. An interrupted turn (a
159
146
  // shutdown) leaves its calls open instead: the next turn runs them again.
@@ -164,7 +151,7 @@ const closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSeq: numb
164
151
  entries.flatMap(({ event }) => (Predicate.isTagged(event, "ToolStarted") ? [event.callId] : []))
165
152
  )
166
153
 
167
- const open = History.unansweredCalls(History.fromEntries(entries, () => undefined))
154
+ const open = History.unansweredCalls(History.fromEntries(entries))
168
155
 
169
156
  if (open.length === 0) return
170
157
 
@@ -196,13 +183,13 @@ const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, startSe
196
183
  startSeq
197
184
  )
198
185
 
199
- // Fails visibly when the user's latest input got no reply; an input that arrived after what its model read gets the
200
- // next turn, which replies.
201
- const finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, wakes: ReadonlySet<string>) {
186
+ // A turn that ran the loop fails visibly when the user's latest input got no reply; an input that arrived after what
187
+ // its model read gets the next turn, which replies. A handler that ended without the loop decided that itself.
188
+ const finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, ranLoop: boolean) {
202
189
  const entries = yield* chat.entries
203
190
 
204
191
  const end =
205
- History.owesReply(entries, read) && !History.unreadAfter(entries, read, wakes)
192
+ ranLoop && History.owesReply(entries, read) && !History.unreadAfter(entries, read)
206
193
  ? AssistantEvent.TurnFailed.make({ throughSeq: read, message: silentTurnMessage })
207
194
  : AssistantEvent.TurnCompleted.make({ throughSeq: read })
208
195
 
@@ -222,11 +209,15 @@ const makeTurnApi = (
222
209
  registration: Registration,
223
210
  chat: Writer,
224
211
  startSeq: number,
225
- checkOpen: Effect.Effect<void>
212
+ checkOpen: Effect.Effect<void>,
213
+ ranLoop: Ref.Ref<boolean>
226
214
  ): Assistant.Turn<any> => {
227
215
  const { definition, builtins } = registration
228
216
  const { app } = definition
229
217
 
218
+ const running = <A, E, R>(stream: Stream.Stream<A, E, R>) =>
219
+ Stream.unwrap(Ref.set(ranLoop, true).pipe(Effect.as(stream)))
220
+
230
221
  // With how far into the chat it goes: the step records it on the model's output.
231
222
  // Read before every model call: a chat the app deleted meanwhile stops here.
232
223
  const history = Effect.fnUntraced(function* (view: Assistant.HistoryView<any> = identity) {
@@ -235,7 +226,7 @@ const makeTurnApi = (
235
226
  const all = yield* chat.entries
236
227
 
237
228
  const read: AssistantLoop.ChatHistory = {
238
- messages: toHistory(view(AppEvents.decodeEntries(app, all)), app),
229
+ messages: History.fromEntries(view(AppEvents.decodeEntries(app, all))),
239
230
  throughSeq: lastSeq(all, startSeq)
240
231
  }
241
232
 
@@ -258,7 +249,7 @@ const makeTurnApi = (
258
249
  const through = handledThrough(all, startSeq)
259
250
 
260
251
  // Something arrived after what the loop's model read: the next turn reads it with the full loop, and replies.
261
- if (History.unreadAfter(all, through, app.wakes)) return Stream.empty
252
+ if (History.unreadAfter(all, through)) return Stream.empty
262
253
 
263
254
  const owed = History.owesReply(all, through)
264
255
  const undelivered = !owed && History.owesDelivery(all, through)
@@ -282,7 +273,7 @@ const makeTurnApi = (
282
273
 
283
274
  return {
284
275
  messages: [
285
- ...toHistory(options.view(AppEvents.decodeEntries(app, entries)), app),
276
+ ...History.fromEntries(options.view(AppEvents.decodeEntries(app, entries))),
286
277
  History.userText(nudge)
287
278
  ],
288
279
  throughSeq: lastSeq(entries, startSeq)
@@ -290,27 +281,27 @@ const makeTurnApi = (
290
281
  }),
291
282
  // A second step, for a reply after a call it was refused.
292
283
  maxSteps: 2
293
- })
284
+ // The loop ran the approved calls; a pass that can only reply runs none.
285
+ }).pipe(Stream.provideService(Loop.TurnApprovals, Option.none()))
294
286
  })
295
287
  )
296
288
 
297
289
  const replyIfOwed = (options: Assistant.ReplyOptions) =>
298
- replyPass({
299
- instructions: instructions(options.instructions),
300
- toolkit: builtins,
301
- view: identity,
302
- nudge: options.nudge
303
- })
290
+ running(
291
+ replyPass({
292
+ instructions: instructions(options.instructions),
293
+ toolkit: builtins,
294
+ view: identity,
295
+ nudge: options.nudge
296
+ })
297
+ )
304
298
 
305
- const prepare = <T extends Record<string, Tool.Any>, RS>(options: Assistant.LoopOptions<T, RS, any>) => {
306
- const skills = options.skills ?? []
299
+ const prepare = <T extends Record<string, Tool.Any>>(options: Assistant.LoopOptions<T, any>) => {
300
+ const own = Toolkits.fromToolkitOrEmpty(options.toolkit)
307
301
 
308
302
  return {
309
- system: mapText(instructions(options.instructions), (text) => Skill.withCatalog(text, skills)),
310
- toolkit: Toolkits.merge(
311
- Toolkits.merge(builtins, Skills.toTools(skills)),
312
- Toolkits.fromToolkitOrEmpty(options.toolkit)
313
- ),
303
+ system: instructions(options.instructions),
304
+ toolkit: Toolkits.merge(builtins, own),
314
305
  view: options.history ?? identity
315
306
  }
316
307
  }
@@ -334,7 +325,7 @@ const makeTurnApi = (
334
325
  maxSteps: options.maxSteps ?? 16
335
326
  })
336
327
 
337
- return Stream.concat(loop, replyPass({ instructions: system, toolkit, view, nudge: options.replyNudge }))
328
+ return running(Stream.concat(loop, replyPass({ instructions: system, toolkit, view, nudge: options.replyNudge })))
338
329
  }
339
330
  }
340
331
  }
@@ -353,7 +344,7 @@ export const run = Effect.fnUntraced(
353
344
  const { chat } = options
354
345
  const before = yield* chat.entries
355
346
 
356
- yield* Telemetry.agentInput(History.unhandledInputs(before, options.registration.definition.app.wakes))
347
+ yield* Telemetry.agentInput(History.unhandledInputs(before))
357
348
 
358
349
  const assistant = options.registration.definition.name
359
350
  const traceId = yield* Telemetry.currentTraceId
@@ -368,7 +359,8 @@ export const run = Effect.fnUntraced(
368
359
  ])
369
360
 
370
361
  const startSeq = lastSeq(started, 0)
371
- const turn = makeTurnApi(options.registration, chat, startSeq, options.checkOpen)
362
+ const ranLoop = yield* Ref.make(false)
363
+ const turn = makeTurnApi(options.registration, chat, startSeq, options.checkOpen, ranLoop)
372
364
 
373
365
  const record = (event: AssistantLoop.Event) => chat.append([event])
374
366
 
@@ -380,12 +372,14 @@ export const run = Effect.fnUntraced(
380
372
  Loop.TurnTools,
381
373
  Option.some(Toolkits.provide(options.registration.builtins, options.services))
382
374
  ),
375
+ // A turn's loop waits for the user's approval, and runs the calls they approved.
376
+ Effect.provideService(Loop.TurnApprovals, Option.some({ approved: Effect.map(chat.entries, approvedCalls) })),
383
377
  Effect.provideContext(options.services),
384
378
  Effect.matchCauseEffect({
385
379
  onSuccess: Effect.fnUntraced(function* () {
386
380
  const all = yield* chat.entries
387
381
 
388
- yield* finishTurn(chat, handledThrough(all, startSeq), options.registration.definition.app.wakes)
382
+ yield* finishTurn(chat, handledThrough(all, startSeq), yield* Ref.get(ranLoop))
389
383
  }),
390
384
  onFailure: (cause) => failTurn(chat, cause)
391
385
  })
@@ -1,111 +0,0 @@
1
- import * as SchemaAST from "effect/SchemaAST";
2
- import * as Schema from "effect/Schema";
3
- declare namespace AppEvent_d_exports {
4
- export { Any, AppEvent, EventSchema, Render, Type, make };
5
- }
6
- /**
7
- * Schema for one of the application's own events.
8
- *
9
- * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) that needs no
10
- * services.
11
- *
12
- * @category models
13
- * @since 0.1.0
14
- */
15
- export type EventSchema = Schema.Codec<{
16
- readonly _tag: string;
17
- }, {
18
- readonly _tag: string;
19
- }> & {
20
- readonly fields: {
21
- readonly _tag: {
22
- readonly ast: SchemaAST.AST;
23
- };
24
- };
25
- };
26
- /**
27
- * Describes how the model reads an event.
28
- *
29
- * `"json"` shows the event's tag and fields as JSON. A function shows the text it returns for the
30
- * event.
31
- *
32
- * @category models
33
- * @since 0.1.0
34
- */
35
- export type Render<A> = "json" | ((event: A) => string);
36
- /**
37
- * Represents one of the application's events, registered with an assistant.
38
- *
39
- * It holds the event's schema and tag, whether storing it wakes a turn, and how the model reads
40
- * it. A `render` of `undefined` means the model doesn't read it.
41
- *
42
- * @category models
43
- * @since 0.1.0
44
- */
45
- export interface AppEvent<S extends EventSchema = EventSchema> {
46
- readonly schema: S;
47
- readonly tag: string;
48
- readonly wakes: boolean;
49
- readonly render: Render<S["Type"]> | undefined;
50
- }
51
- /**
52
- * Represents any registered event, for generic constraints.
53
- *
54
- * @category utility types
55
- * @since 0.1.0
56
- */
57
- export type Any = AppEvent<any>;
58
- /**
59
- * Extracts the type of a registered event's values.
60
- *
61
- * @category utility types
62
- * @since 0.1.0
63
- */
64
- export type Type<E extends Any> = E["schema"]["Type"];
65
- /**
66
- * Registers one of the application's events with an assistant.
67
- *
68
- * **Details**
69
- *
70
- * Storing the event wakes a turn only when `wakes` is `true`; it defaults to `false`. `render`
71
- * defaults to `"json"` for an event that wakes a turn. An event that doesn't wake a turn and has
72
- * no `render` isn't read by the model.
73
- *
74
- * The schema must be a tagged struct or class whose `_tag` is a string literal; `make` throws
75
- * otherwise.
76
- *
77
- * **Example** (Registering an event that wakes a turn)
78
- *
79
- * ```ts
80
- * import { AppEvent, Assistant, InputId } from "effect-assistant"
81
- * import * as Schema from "effect/Schema"
82
- *
83
- * class OrderShipped extends Schema.TaggedClass<OrderShipped>()("OrderShipped", {
84
- * id: InputId.InputId,
85
- * orderId: Schema.String,
86
- * carrier: Schema.String
87
- * }) {}
88
- *
89
- * export const MyAssistant = Assistant.make("my-assistant", {
90
- * events: [
91
- * AppEvent.make(OrderShipped, {
92
- * wakes: true,
93
- * render: (shipped) =>
94
- * `Order ${shipped.orderId} shipped with ${shipped.carrier}. Tell the user if it matters.`
95
- * })
96
- * ]
97
- * })
98
- * ```
99
- *
100
- * @category constructors
101
- * @since 0.1.0
102
- */
103
- export declare const make: <S extends EventSchema>(schema: S, options?: {
104
- /** Whether storing it starts a turn, like a user's message. Default false. */
105
- readonly wakes?: boolean | undefined;
106
- /** How the model reads it in its history. */
107
- readonly render?: Render<S["Type"]> | undefined;
108
- }) => AppEvent<S>;
109
- //#endregion
110
- export { AppEvent_d_exports };
111
- //# sourceMappingURL=AppEvent.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AppEvent.d.ts","names":[],"sources":["../src/AppEvent.ts"],"mappings":";;;;;;;;;;;;;;YAuBY,cAAc,OAAO;WAAiB;;WAA2B;;WAClE;aAAmB;eAAiB,KAAK,UAAU;;;;;;;;;;;;;YAYlD,OAAO,gBAAgB,OAAO;;;;;;;;;;iBAWzB,SAAS,UAAU,cAAc;WACvC,QAAQ;WACR;WACA;WACA,QAAQ,OAAO;;;;;;;;YASd,MAAM;;;;;;;YAQN,KAAK,UAAU,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgDrB,OAAQ,UAAU,aAAW,QAChC,GAAC;;WAGE;;WAEA,SAAS,OAAO;MAE1B,SAAS"}
package/dist/AppEvent.js DELETED
@@ -1,71 +0,0 @@
1
- import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
- import * as Predicate from "effect/Predicate";
3
- import * as SchemaAST from "effect/SchemaAST";
4
- //#region src/AppEvent.ts
5
- /**
6
- * Registers an application's own events with an assistant.
7
- *
8
- * An app event is a tagged schema for something the application reports into a chat, such as a
9
- * workflow's progress, a webhook, a summary for compaction, or the draft the user opened.
10
- * {@link make} registers one with two choices: whether storing it wakes a turn (`wakes`), and
11
- * how the model reads it (`render`).
12
- *
13
- * @since 0.1.0
14
- */
15
- var AppEvent_exports = /* @__PURE__ */ __exportAll({ make: () => make });
16
- const tagOf = (schema) => {
17
- const ast = schema.fields._tag.ast;
18
- if (SchemaAST.isLiteral(ast) && Predicate.isString(ast.literal)) return ast.literal;
19
- throw new Error("An app event is a tagged struct or class.");
20
- };
21
- /**
22
- * Registers one of the application's events with an assistant.
23
- *
24
- * **Details**
25
- *
26
- * Storing the event wakes a turn only when `wakes` is `true`; it defaults to `false`. `render`
27
- * defaults to `"json"` for an event that wakes a turn. An event that doesn't wake a turn and has
28
- * no `render` isn't read by the model.
29
- *
30
- * The schema must be a tagged struct or class whose `_tag` is a string literal; `make` throws
31
- * otherwise.
32
- *
33
- * **Example** (Registering an event that wakes a turn)
34
- *
35
- * ```ts
36
- * import { AppEvent, Assistant, InputId } from "effect-assistant"
37
- * import * as Schema from "effect/Schema"
38
- *
39
- * class OrderShipped extends Schema.TaggedClass<OrderShipped>()("OrderShipped", {
40
- * id: InputId.InputId,
41
- * orderId: Schema.String,
42
- * carrier: Schema.String
43
- * }) {}
44
- *
45
- * export const MyAssistant = Assistant.make("my-assistant", {
46
- * events: [
47
- * AppEvent.make(OrderShipped, {
48
- * wakes: true,
49
- * render: (shipped) =>
50
- * `Order ${shipped.orderId} shipped with ${shipped.carrier}. Tell the user if it matters.`
51
- * })
52
- * ]
53
- * })
54
- * ```
55
- *
56
- * @category constructors
57
- * @since 0.1.0
58
- */
59
- const make = (schema, options) => {
60
- const wakes = options?.wakes ?? false;
61
- return {
62
- schema,
63
- tag: tagOf(schema),
64
- wakes,
65
- render: options?.render ?? (wakes ? "json" : void 0)
66
- };
67
- };
68
- //#endregion
69
- export { AppEvent_exports, make };
70
-
71
- //# sourceMappingURL=AppEvent.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AppEvent.js","names":[],"sources":["../src/AppEvent.ts"],"sourcesContent":["/**\n * Registers an application's own events with an assistant.\n *\n * An app event is a tagged schema for something the application reports into a chat, such as a\n * workflow's progress, a webhook, a summary for compaction, or the draft the user opened.\n * {@link make} registers one with two choices: whether storing it wakes a turn (`wakes`), and\n * how the model reads it (`render`).\n *\n * @since 0.1.0\n */\nimport * as Predicate from \"effect/Predicate\"\nimport type * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\n\n/**\n * Schema for one of the application's own events.\n *\n * It is a tagged struct or class (`Schema.TaggedStruct`, `Schema.TaggedClass`) that needs no\n * services.\n *\n * @category models\n * @since 0.1.0\n */\nexport type EventSchema = Schema.Codec<{ readonly _tag: string }, { readonly _tag: string }> & {\n readonly fields: { readonly _tag: { readonly ast: SchemaAST.AST } }\n}\n\n/**\n * Describes how the model reads an event.\n *\n * `\"json\"` shows the event's tag and fields as JSON. A function shows the text it returns for the\n * event.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Render<A> = \"json\" | ((event: A) => string)\n\n/**\n * Represents one of the application's events, registered with an assistant.\n *\n * It holds the event's schema and tag, whether storing it wakes a turn, and how the model reads\n * it. A `render` of `undefined` means the model doesn't read it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AppEvent<S extends EventSchema = EventSchema> {\n readonly schema: S\n readonly tag: string\n readonly wakes: boolean\n readonly render: Render<S[\"Type\"]> | undefined\n}\n\n/**\n * Represents any registered event, for generic constraints.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = AppEvent<any>\n\n/**\n * Extracts the type of a registered event's values.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Type<E extends Any> = E[\"schema\"][\"Type\"]\n\nconst tagOf = (schema: EventSchema): string => {\n const ast = schema.fields._tag.ast\n\n if (SchemaAST.isLiteral(ast) && Predicate.isString(ast.literal)) return ast.literal\n\n throw new Error(\"An app event is a tagged struct or class.\")\n}\n\n/**\n * Registers one of the application's events with an assistant.\n *\n * **Details**\n *\n * Storing the event wakes a turn only when `wakes` is `true`; it defaults to `false`. `render`\n * defaults to `\"json\"` for an event that wakes a turn. An event that doesn't wake a turn and has\n * no `render` isn't read by the model.\n *\n * The schema must be a tagged struct or class whose `_tag` is a string literal; `make` throws\n * otherwise.\n *\n * **Example** (Registering an event that wakes a turn)\n *\n * ```ts\n * import { AppEvent, Assistant, InputId } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n *\n * class OrderShipped extends Schema.TaggedClass<OrderShipped>()(\"OrderShipped\", {\n * id: InputId.InputId,\n * orderId: Schema.String,\n * carrier: Schema.String\n * }) {}\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\", {\n * events: [\n * AppEvent.make(OrderShipped, {\n * wakes: true,\n * render: (shipped) =>\n * `Order ${shipped.orderId} shipped with ${shipped.carrier}. Tell the user if it matters.`\n * })\n * ]\n * })\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <S extends EventSchema>(\n schema: S,\n options?: {\n /** Whether storing it starts a turn, like a user's message. Default false. */\n readonly wakes?: boolean | undefined\n /** How the model reads it in its history. */\n readonly render?: Render<S[\"Type\"]> | undefined\n }\n): AppEvent<S> => {\n const wakes = options?.wakes ?? false\n\n return {\n schema,\n tag: tagOf(schema),\n wakes,\n render: options?.render ?? (wakes ? \"json\" : undefined)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsEA,MAAM,SAAS,WAAgC;CAC7C,MAAM,MAAM,OAAO,OAAO,KAAK;CAE/B,IAAI,UAAU,UAAU,GAAG,KAAK,UAAU,SAAS,IAAI,OAAO,GAAG,OAAO,IAAI;CAE5E,MAAM,IAAI,MAAM,2CAA2C;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,QACX,QACA,YAMgB;CAChB,MAAM,QAAQ,SAAS,SAAS;CAEhC,OAAO;EACL;EACA,KAAK,MAAM,MAAM;EACjB;EACA,QAAQ,SAAS,WAAW,QAAQ,SAAS,KAAA;CAC/C;AACF"}