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
@@ -1 +1 @@
1
- {"version":3,"file":"Specialist.js","names":[],"sources":["../src/Specialist.ts"],"sourcesContent":["/**\n * Defines specialists, which are focused agents an assistant hands work to in the background.\n *\n * A specialist has a definition, created with {@link make}, that gives its name and when to use\n * it, and a handler, provided with its `toLayer`, that works on a {@link Brief} as a loop. The\n * {@link Runner} service starts, steers, and stops a chat's specialist runs.\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 Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport type * as AssistantLoop from \"./AssistantLoop.ts\"\nimport type { ChatId } from \"./ChatId.ts\"\nimport type { CurrentChat } from \"./CurrentChat.ts\"\nimport type * as Instructions from \"./Instructions.ts\"\nimport type { RunId } from \"./RunId.ts\"\nimport type { Skill } from \"./Skill.ts\"\n\nconst TypeId = \"~effect-assistant/Specialist\"\n\n/**\n * Represents a specialist's definition.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Specialist<Name extends string = string> {\n readonly [TypeId]: typeof TypeId\n readonly name: Name\n /** When to use the specialist. The assistant sees this. */\n readonly description: string\n /** The key of the specialist's handler, as `toLayer` provides it. */\n readonly Handler: Context.Key<SpecialistHandler<Name>, Built>\n /**\n * Provides the specialist's handler. The handler may use the {@link RunServices} and the\n * services its build Effect required (see {@link CheckServices}).\n */\n readonly toLayer: <R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE> | Effect.Effect<Handler<R, RE>, E, RX>) &\n CheckServices<Exclude<R | RE, RunServices | RX | Tool.Handler<string>>>\n ) => Layer.Layer<SpecialistHandler<Name>, never, Exclude<RX, Scope.Scope | CurrentChat> | ToolHandlers<R | RE>>\n}\n\n/**\n * Represents the check `toLayer` applies to the services a handler uses.\n *\n * **Details**\n *\n * Every service a handler uses must be one the run (or turn) provides, or one its build Effect\n * required, which the layer provides. A handler that uses any other service gets this type\n * error, which names those services.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type CheckServices<Undeclared> = [Undeclared] extends [never]\n ? unknown\n : {\n readonly \"~undeclaredServices\": {\n readonly error: \"The handler uses services that neither the turn provides nor its build Effect requires\"\n readonly services: Undeclared\n }\n }\n\n/**\n * Extracts the tool handlers (`Toolkit.toLayer`) that a handler's loop uses.\n *\n * They are requirements of the layer, so `Layer.provide(MyToolsLive)` provides them, and a\n * missing one is a type error.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type ToolHandlers<R> = Extract<R, Tool.Handler<string>>\n\n/**\n * Represents any specialist.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = Specialist<any>\n\n/**\n * Identifies a specialist's handler in a context.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface SpecialistHandler<Name extends string> {\n readonly _SpecialistHandler: Name\n}\n\n/**\n * Extracts the handlers that these specialists need.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type HandlerOf<S> = S extends Specialist<infer Name> ? SpecialistHandler<Name> : never\n\n/**\n * The services a run provides to its handler, which are its chat, the chat's runner, and the\n * model.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type RunServices = CurrentChat | Runner | LanguageModel.LanguageModel\n\n/**\n * Represents the options for `run.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> {\n readonly instructions: string | Instructions.Resolved\n /** The loop's tools, as a native toolkit whose handlers come from the layer. */\n readonly toolkit?: Toolkit.Toolkit<T> | undefined\n /**\n * Playbooks, listed as a catalog in the prompt and loaded with `load_skill` when one matches. A\n * skill made with `tools` is left out when the toolkit lacks one of them.\n */\n readonly skills?: ReadonlyArray<Skill<RS>> | undefined\n /** The model calls allowed per brief. Defaults to 8. */\n readonly maxSteps?: number | undefined\n}\n\n/**\n * Represents one run of a specialist on a brief, as its handler sees it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Run {\n readonly runId: RunId\n /** The chat the run belongs to, which is the chat of the turn that spawned it. */\n readonly chatId: ChatId\n readonly brief: Brief\n /**\n * The run's history, with the brief, what the run did so far, and the messages sent to it since\n * (follow-ups and steering). Read it before every model call, as the loop does.\n */\n readonly history: Effect.Effect<ReadonlyArray<Prompt.Message>>\n /** Runs the agent loop over the run's history, with the given instructions, tools, and skills. */\n readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>, RS = never>(\n options: LoopOptions<T, RS>\n ) => Stream.Stream<\n AssistantLoop.Event,\n AiError.AiError,\n LanguageModel.LanguageModel | AssistantLoop.ToolkitServices<T> | RS\n >\n}\n\n/**\n * Represents a specialist's handler, which works on a brief and returns a stream of what the loop\n * did.\n *\n * It may return the stream from an Effect. `R` is the services the stream needs, and `RE` the\n * services the Effect needs.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Handler<R = never, RE = R> = (\n run: Run\n) =>\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n\n/**\n * Represents a specialist's handler as its layer provides it.\n *\n * `open` runs the build for a chat (once per chat activation, see `CurrentChat`) and returns\n * the handler with the layer's services already provided.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Built {\n readonly open: Effect.Effect<\n (run: Run) => Stream.Stream<AssistantLoop.Event, unknown, RunServices>,\n never,\n Scope.Scope | CurrentChat\n >\n}\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 * Creates a specialist from its name and a description of when to use it.\n *\n * **Details**\n *\n * The specialist's `toLayer` provides its handler, which is usually `run.loop` with the\n * specialist's own instructions and tools. List the specialist in `Assistant.make`'s\n * `specialists`, and provide its layer to the assistant's.\n *\n * **Example** (Defining a research specialist)\n *\n * ```ts\n * import { Assistant, Specialist } from \"effect-assistant\"\n * import * as Layer from \"effect/Layer\"\n *\n * const Research = Specialist.make(\"research\", {\n * description: \"Researches past campaigns and orders. Give it the question and any context.\"\n * })\n *\n * const ResearchLive = Research.toLayer((run) =>\n * run.loop({\n * instructions: \"Answer with the findings and numbers the colleague needs, in a few lines.\",\n * toolkit: ResearchTools\n * })\n * ).pipe(Layer.provide(ResearchToolsLive))\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\", { specialists: [Research] })\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) => turn.loop({ instructions })).pipe(\n * Layer.provide(ResearchLive)\n * )\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <const Name extends string>(\n name: Name,\n options: {\n /** When to use it. The assistant sees this. */\n readonly description: string\n }\n): Specialist<Name> => {\n const Handler = Context.Service<SpecialistHandler<Name>, Built>(`effect-assistant/Specialist/${name}`)\n\n // The signature checks what the handler uses (`CheckServices`); the layer's services, which the\n // build required, are provided to it and to every run's stream, so what's left is the run\n // services.\n function toLayer<R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE> | Effect.Effect<Handler<R, RE>, E, RX>) &\n CheckServices<Exclude<R | RE, RunServices | RX | Tool.Handler<string>>>\n ): Layer.Layer<SpecialistHandler<Name>, never, Exclude<RX, Scope.Scope | CurrentChat> | ToolHandlers<R | RE>>\n function toLayer(\n build: Handler<any, any> | Effect.Effect<Handler<any, any>, unknown, any>\n ): Layer.Layer<SpecialistHandler<Name>, never, any> {\n return Layer.effect(\n Handler,\n Effect.gen(function* () {\n const context = yield* Effect.context()\n\n return {\n // The build runs per chat activation, in the chat; here it only keeps the layer's\n // services for it.\n open: Effect.gen(function* () {\n const handler = Effect.isEffect(build) ? yield* build : build\n\n return (run: Run) => Stream.provideContext(toStream(handler(run)), context)\n }).pipe(Effect.provideContext(context), Effect.orDie)\n }\n })\n )\n }\n\n return { [TypeId]: TypeId, name, description: options.description, Handler, toLayer }\n}\n\n/**\n * Schema for the brief the assistant gives a specialist.\n *\n * Only `goal` is required.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Brief = Schema.Struct({\n goal: Schema.String.annotate({ description: \"What to find out or do.\" }),\n context: Schema.optional(Schema.String).annotate({\n description: \"What it needs to know. It can't see this chat.\"\n }),\n successCriteria: Schema.optional(Schema.String).annotate({ description: \"When it is done.\" }),\n report: Schema.optional(Schema.String).annotate({\n description: \"What to put in the answer, and how.\"\n }),\n limits: Schema.optional(Schema.String).annotate({\n description: \"Hard limits for this job, like read-only or a deadline.\"\n })\n})\n\n/**\n * Represents the brief the assistant gives a specialist.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Brief = typeof Brief.Type\n\n/**\n * Represents a request to start a specialist run in the runner's chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface SpawnRequest {\n /** The specialist's name. */\n readonly specialist: string\n readonly brief: Brief\n}\n\n/**\n * Represents why a run request can't be carried out.\n *\n * **Details**\n *\n * The `reason` says which request failed: `\"UnknownSpecialist\"` when `spawn` names no specialist\n * of the assistant, `\"RunNotFound\"` when `message` names a run that is gone, and `\"NotRunning\"`\n * when `stop` names a run that isn't running. The `message` is written for the model.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class SpecialistError extends Schema.Error<SpecialistError>(\"effect-assistant/Specialist/SpecialistError\")({\n _tag: Schema.tag(\"SpecialistError\"),\n reason: Schema.Literals([\"UnknownSpecialist\", \"RunNotFound\", \"NotRunning\"]),\n message: Schema.String\n}) {}\n\n/**\n * Service that runs a chat's specialists and keeps finished runs warm for follow-ups.\n *\n * The engine provides it to every turn. The specialist tools use it, and a handler can too.\n *\n * @category services\n * @since 0.1.0\n */\nexport class Runner extends Context.Service<\n Runner,\n {\n /** Starts a run and returns its id. */\n readonly spawn: (request: SpawnRequest) => Effect.Effect<RunId, SpecialistError>\n /** Steers a running run, or continues a finished one. */\n readonly message: (runId: RunId, text: string) => Effect.Effect<\"steered\" | \"continued\", SpecialistError>\n /** Interrupts a running run. */\n readonly stop: (runId: RunId) => Effect.Effect<void, SpecialistError>\n }\n>()(\"effect-assistant/Specialist/Runner\") {}\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,SAAS;AA0Kf,MAAM,YACJ,WAGyD,OAAO,SAAS,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsC7G,MAAa,QACX,MACA,YAIqB;CACrB,MAAM,UAAU,QAAQ,QAAwC,+BAA+B,MAAM;CASrG,SAAS,QACP,OACkD;EAClD,OAAO,MAAM,OACX,SACA,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,OAAO,QAAQ;GAEtC,OAAO,EAGL,MAAM,OAAO,IAAI,aAAa;IAC5B,MAAM,UAAU,OAAO,SAAS,KAAK,IAAI,OAAO,QAAQ;IAExD,QAAQ,QAAa,OAAO,eAAe,SAAS,QAAQ,GAAG,CAAC,GAAG,OAAO;GAC5E,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,OAAO,GAAG,OAAO,KAAK,EACtD;EACF,CAAC,CACH;CACF;CAEA,OAAO;GAAG,SAAS;EAAQ;EAAM,aAAa,QAAQ;EAAa;EAAS;CAAQ;AACtF;;;;;;;;;AAUA,MAAa,QAAQ,OAAO,OAAO;CACjC,MAAM,OAAO,OAAO,SAAS,EAAE,aAAa,0BAA0B,CAAC;CACvE,SAAS,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC/C,aAAa,iDACf,CAAC;CACD,iBAAiB,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAAE,aAAa,mBAAmB,CAAC;CAC5F,QAAQ,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC9C,aAAa,sCACf,CAAC;CACD,QAAQ,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC9C,aAAa,0DACf,CAAC;AACH,CAAC;;;;;;;;;;;;;AAkCD,IAAa,kBAAb,cAAqC,OAAO,MAAuB,6CAA6C,CAAC,CAAC;CAChH,MAAM,OAAO,IAAI,iBAAiB;CAClC,QAAQ,OAAO,SAAS;EAAC;EAAqB;EAAe;CAAY,CAAC;CAC1E,SAAS,OAAO;AAClB,CAAC,CAAC,CAAC,CAAC;;;;;;;;;AAUJ,IAAa,SAAb,cAA4B,QAAQ,QAUlC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"Specialist.js","names":[],"sources":["../src/Specialist.ts"],"sourcesContent":["/**\n * Defines specialists, which are focused agents an assistant hands work to in the background.\n *\n * A specialist has a definition, created with {@link make}, that gives its name and when to use\n * it, and a handler, provided with its `toLayer`, that works on a {@link Brief} as a loop. The\n * {@link Runner} service starts, steers, and stops a chat's specialist runs.\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 Layer from \"effect/Layer\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport type * as AssistantLoop from \"./AssistantLoop.ts\"\nimport type { ChatId } from \"./ChatId.ts\"\nimport type { CurrentChat } from \"./CurrentChat.ts\"\nimport type * as Instructions from \"./Instructions.ts\"\nimport type { RunId } from \"./RunId.ts\"\n\nconst TypeId = \"~effect-assistant/Specialist\"\n\n/**\n * Represents a specialist's definition.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Specialist<Name extends string = string> {\n readonly [TypeId]: typeof TypeId\n readonly name: Name\n /** When to use the specialist. The assistant sees this. */\n readonly description: string\n /** The key of the specialist's handler, as `toLayer` provides it. */\n readonly Handler: Context.Key<SpecialistHandler<Name>, Built>\n /**\n * Provides the specialist's handler. The handler may use the {@link RunServices} and the\n * services its build Effect required (see {@link CheckServices}).\n */\n readonly toLayer: <R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE> | Effect.Effect<Handler<R, RE>, E, RX>) &\n CheckServices<Exclude<R | RE, RunServices | RX | Tool.Handler<string>>>\n ) => Layer.Layer<SpecialistHandler<Name>, never, Exclude<RX, Scope.Scope | CurrentChat> | ToolHandlers<R | RE>>\n}\n\n/**\n * Represents the check `toLayer` applies to the services a handler uses.\n *\n * **Details**\n *\n * Every service a handler uses must be one the run (or turn) provides, or one its build Effect\n * required, which the layer provides. A handler that uses any other service gets this type\n * error, which names those services.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type CheckServices<Undeclared> = [Undeclared] extends [never]\n ? unknown\n : {\n readonly \"~undeclaredServices\": {\n readonly error: \"The handler uses services that neither the turn provides nor its build Effect requires\"\n readonly services: Undeclared\n }\n }\n\n/**\n * Extracts the tool handlers (`Toolkit.toLayer`) that a handler's loop uses.\n *\n * They are requirements of the layer, so `Layer.provide(MyToolsLive)` provides them, and a\n * missing one is a type error.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type ToolHandlers<R> = Extract<R, Tool.Handler<string>>\n\n/**\n * Represents any specialist.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type Any = Specialist<any>\n\n/**\n * Identifies a specialist's handler in a context.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface SpecialistHandler<Name extends string> {\n readonly _SpecialistHandler: Name\n}\n\n/**\n * Extracts the handlers that these specialists need.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type HandlerOf<S> = S extends Specialist<infer Name> ? SpecialistHandler<Name> : never\n\n/**\n * The services a run provides to its handler, which are its chat, the chat's runner, and the\n * model.\n *\n * @category utility types\n * @since 0.1.0\n */\nexport type RunServices = CurrentChat | Runner | LanguageModel.LanguageModel\n\n/**\n * Represents the options for `run.loop`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface LoopOptions<T extends Record<string, Tool.Any> = Record<never, never>> {\n readonly instructions: string | Instructions.Resolved\n /**\n * The loop's tools, as a native toolkit whose handlers come from the layer. Merge in a\n * `Skill.toolkit(...)` for playbooks.\n */\n readonly toolkit?: Toolkit.Toolkit<T> | undefined\n /** The model calls allowed per brief. Defaults to 8. */\n readonly maxSteps?: number | undefined\n}\n\n/**\n * Represents one run of a specialist on a brief, as its handler sees it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Run {\n readonly runId: RunId\n /** The chat the run belongs to, which is the chat of the turn that spawned it. */\n readonly chatId: ChatId\n readonly brief: Brief\n /**\n * The run's history, with the brief, what the run did so far, and the messages sent to it since\n * (follow-ups and steering). Read it before every model call, as the loop does.\n */\n readonly history: Effect.Effect<ReadonlyArray<Prompt.Message>>\n /** Runs the agent loop over the run's history, with the given instructions and tools. */\n readonly loop: <T extends Record<string, Tool.Any> = Record<never, never>>(\n options: LoopOptions<T>\n ) => Stream.Stream<\n AssistantLoop.Event,\n AiError.AiError,\n LanguageModel.LanguageModel | AssistantLoop.ToolkitServices<T>\n >\n}\n\n/**\n * Represents a specialist's handler, which works on a brief and returns a stream of what the loop\n * did.\n *\n * It may return the stream from an Effect. `R` is the services the stream needs, and `RE` the\n * services the Effect needs.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Handler<R = never, RE = R> = (\n run: Run\n) =>\n | Stream.Stream<AssistantLoop.Event, unknown, R>\n | Effect.Effect<Stream.Stream<AssistantLoop.Event, unknown, R>, unknown, RE>\n\n/**\n * Represents a specialist's handler as its layer provides it.\n *\n * `open` runs the build for a chat (once per chat activation, see `CurrentChat`) and returns\n * the handler with the layer's services already provided.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Built {\n readonly open: Effect.Effect<\n (run: Run) => Stream.Stream<AssistantLoop.Event, unknown, RunServices>,\n never,\n Scope.Scope | CurrentChat\n >\n}\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 * Creates a specialist from its name and a description of when to use it.\n *\n * **Details**\n *\n * The specialist's `toLayer` provides its handler, which is usually `run.loop` with the\n * specialist's own instructions and tools. List the specialist in `Assistant.make`'s\n * `specialists`, and provide its layer to the assistant's.\n *\n * **Example** (Defining a research specialist)\n *\n * ```ts\n * import { Assistant, Specialist } from \"effect-assistant\"\n * import * as Layer from \"effect/Layer\"\n *\n * const Research = Specialist.make(\"research\", {\n * description: \"Researches past campaigns and orders. Give it the question and any context.\"\n * })\n *\n * const ResearchLive = Research.toLayer((run) =>\n * run.loop({\n * instructions: \"Answer with the findings and numbers the colleague needs, in a few lines.\",\n * toolkit: ResearchTools\n * })\n * ).pipe(Layer.provide(ResearchToolsLive))\n *\n * export const MyAssistant = Assistant.make(\"my-assistant\", { specialists: [Research] })\n *\n * const MyAssistantLive = MyAssistant.toLayer((turn) => turn.loop({ instructions })).pipe(\n * Layer.provide(ResearchLive)\n * )\n * ```\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = <const Name extends string>(\n name: Name,\n options: {\n /** When to use it. The assistant sees this. */\n readonly description: string\n }\n): Specialist<Name> => {\n const Handler = Context.Service<SpecialistHandler<Name>, Built>(`effect-assistant/Specialist/${name}`)\n\n // The signature checks what the handler uses (`CheckServices`); the layer's services, which the\n // build required, are provided to it and to every run's stream, so what's left is the run\n // services.\n function toLayer<R = never, RE = never, E = never, RX = never>(\n build: (Handler<R, RE> | Effect.Effect<Handler<R, RE>, E, RX>) &\n CheckServices<Exclude<R | RE, RunServices | RX | Tool.Handler<string>>>\n ): Layer.Layer<SpecialistHandler<Name>, never, Exclude<RX, Scope.Scope | CurrentChat> | ToolHandlers<R | RE>>\n function toLayer(\n build: Handler<any, any> | Effect.Effect<Handler<any, any>, unknown, any>\n ): Layer.Layer<SpecialistHandler<Name>, never, any> {\n return Layer.effect(\n Handler,\n Effect.gen(function* () {\n const context = yield* Effect.context()\n\n return {\n // The build runs per chat activation, in the chat; here it only keeps the layer's\n // services for it.\n open: Effect.gen(function* () {\n const handler = Effect.isEffect(build) ? yield* build : build\n\n return (run: Run) => Stream.provideContext(toStream(handler(run)), context)\n }).pipe(Effect.provideContext(context), Effect.orDie)\n }\n })\n )\n }\n\n return { [TypeId]: TypeId, name, description: options.description, Handler, toLayer }\n}\n\n/**\n * Schema for the brief the assistant gives a specialist.\n *\n * Only `goal` is required.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Brief = Schema.Struct({\n goal: Schema.String.annotate({ description: \"What to find out or do.\" }),\n context: Schema.optional(Schema.String).annotate({\n description: \"What it needs to know. It can't see this chat.\"\n }),\n successCriteria: Schema.optional(Schema.String).annotate({ description: \"When it is done.\" }),\n report: Schema.optional(Schema.String).annotate({\n description: \"What to put in the answer, and how.\"\n }),\n limits: Schema.optional(Schema.String).annotate({\n description: \"Hard limits for this job, like read-only or a deadline.\"\n })\n})\n\n/**\n * Represents the brief the assistant gives a specialist.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Brief = typeof Brief.Type\n\n/**\n * Represents a request to start a specialist run in the runner's chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface SpawnRequest {\n /** The specialist's name. */\n readonly specialist: string\n readonly brief: Brief\n}\n\n/**\n * Represents why a run request can't be carried out.\n *\n * **Details**\n *\n * The `reason` says which request failed: `\"UnknownSpecialist\"` when `spawn` names no specialist\n * of the assistant, `\"RunNotFound\"` when `message` names a run that is gone, and `\"NotRunning\"`\n * when `stop` names a run that isn't running. The `message` is written for the model.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class SpecialistError extends Schema.Error<SpecialistError>(\"effect-assistant/Specialist/SpecialistError\")({\n _tag: Schema.tag(\"SpecialistError\"),\n reason: Schema.Literals([\"UnknownSpecialist\", \"RunNotFound\", \"NotRunning\"]),\n message: Schema.String\n}) {}\n\n/**\n * Service that runs a chat's specialists and keeps finished runs warm for follow-ups.\n *\n * The engine provides it to every turn. The specialist tools use it, and a handler can too.\n *\n * @category services\n * @since 0.1.0\n */\nexport class Runner extends Context.Service<\n Runner,\n {\n /** Starts a run and returns its id. */\n readonly spawn: (request: SpawnRequest) => Effect.Effect<RunId, SpecialistError>\n /** Steers a running run, or continues a finished one. */\n readonly message: (runId: RunId, text: string) => Effect.Effect<\"steered\" | \"continued\", SpecialistError>\n /** Interrupts a running run. */\n readonly stop: (runId: RunId) => Effect.Effect<void, SpecialistError>\n }\n>()(\"effect-assistant/Specialist/Runner\") {}\n"],"mappings":";;;;;;;;;;;;;AA0BA,MAAM,SAAS;AAwKf,MAAM,YACJ,WAGyD,OAAO,SAAS,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsC7G,MAAa,QACX,MACA,YAIqB;CACrB,MAAM,UAAU,QAAQ,QAAwC,+BAA+B,MAAM;CASrG,SAAS,QACP,OACkD;EAClD,OAAO,MAAM,OACX,SACA,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,OAAO,QAAQ;GAEtC,OAAO,EAGL,MAAM,OAAO,IAAI,aAAa;IAC5B,MAAM,UAAU,OAAO,SAAS,KAAK,IAAI,OAAO,QAAQ;IAExD,QAAQ,QAAa,OAAO,eAAe,SAAS,QAAQ,GAAG,CAAC,GAAG,OAAO;GAC5E,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,OAAO,GAAG,OAAO,KAAK,EACtD;EACF,CAAC,CACH;CACF;CAEA,OAAO;GAAG,SAAS;EAAQ;EAAM,aAAa,QAAQ;EAAa;EAAS;CAAQ;AACtF;;;;;;;;;AAUA,MAAa,QAAQ,OAAO,OAAO;CACjC,MAAM,OAAO,OAAO,SAAS,EAAE,aAAa,0BAA0B,CAAC;CACvE,SAAS,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC/C,aAAa,iDACf,CAAC;CACD,iBAAiB,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAAE,aAAa,mBAAmB,CAAC;CAC5F,QAAQ,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC9C,aAAa,sCACf,CAAC;CACD,QAAQ,OAAO,SAAS,OAAO,MAAM,CAAC,CAAC,SAAS,EAC9C,aAAa,0DACf,CAAC;AACH,CAAC;;;;;;;;;;;;;AAkCD,IAAa,kBAAb,cAAqC,OAAO,MAAuB,6CAA6C,CAAC,CAAC;CAChH,MAAM,OAAO,IAAI,iBAAiB;CAClC,QAAQ,OAAO,SAAS;EAAC;EAAqB;EAAe;CAAY,CAAC;CAC1E,SAAS,OAAO;AAClB,CAAC,CAAC,CAAC,CAAC;;;;;;;;;AAUJ,IAAa,SAAb,cAA4B,QAAQ,QAUlC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC"}
@@ -3,7 +3,7 @@ import { Assistant_d_exports, Entry } from "./Assistant.js";
3
3
  import * as Schema from "effect/Schema";
4
4
  import * as Stream from "effect/Stream";
5
5
  declare namespace Timeline_d_exports {
6
- export { Activity, AssistantMessage, Block, BlockStatus, Change, ChangesOptions, Definition, EventItem, Item, ShowOptions, Shown, SpecialistWorking, Timeline, UserMessage, apply, changes, empty, fold, fromEntries, isIdle, snapshot };
6
+ export { Activity, ApprovalItem, AssistantMessage, Block, BlockStatus, Change, ChangesOptions, Definition, EventItem, Item, ShowOptions, Shown, SpecialistWorking, Timeline, UserMessage, apply, changes, empty, fold, fromEntries, isIdle, snapshot };
7
7
  }
8
8
  /**
9
9
  * Schema for the status of a block. A block is `"open"` while it accepts an answer, `"answered"`
@@ -115,8 +115,41 @@ export type EventItem<E> = E extends {
115
115
  readonly event: E;
116
116
  } : never;
117
117
  /**
118
- * Represents an item in the chat's timeline: a message the user or the assistant sent, or one of
119
- * the app's own events it shows (see {@link EventItem}).
118
+ * Schema for a tool call that waits, or waited, for the user's approval, as an item on the
119
+ * timeline.
120
+ *
121
+ * **Details**
122
+ *
123
+ * It is only the yes or no: the assistant's own message tells the user what it asks them to
124
+ * approve, so the item carries no params. `id` is the call's id, which the decision is for (see
125
+ * `ToolApproval.approve`), and `tool` the tool's name, for a label. `status` is where the call stands
126
+ * (`ToolApproval.Status`); `reason` is the user's, when they rejected it with one; `outcome` is how
127
+ * the approved call went, once it ran. `at` is when the call started waiting, in epoch
128
+ * milliseconds. A change updates the item in place as the call is decided and run.
129
+ *
130
+ * @category schemas
131
+ * @since 0.4.0
132
+ */
133
+ export declare const ApprovalItem: Schema.TaggedStruct<"ToolApproval", {
134
+ readonly id: Schema.String;
135
+ readonly tool: Schema.String;
136
+ readonly status: Schema.Literals<readonly ["pending", "approved", "rejected", "expired"]>;
137
+ readonly reason: Schema.optional<Schema.String>;
138
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["success", "failure"]>>;
139
+ readonly at: Schema.Finite;
140
+ }>;
141
+ /**
142
+ * Represents a tool call that waits, or waited, for the user's approval, as an item on the
143
+ * timeline.
144
+ *
145
+ * @category models
146
+ * @since 0.4.0
147
+ */
148
+ export type ApprovalItem = typeof ApprovalItem.Type;
149
+ /**
150
+ * Represents an item in the chat's timeline: a message the user or the assistant sent, one of the
151
+ * app's own events it shows (see {@link EventItem}), or a tool call waiting for the user's approval
152
+ * (see {@link ApprovalItem}).
120
153
  *
121
154
  * **Example** (Showing each item of a chat)
122
155
  *
@@ -138,7 +171,7 @@ export type EventItem<E> = E extends {
138
171
  * @category models
139
172
  * @since 0.3.0
140
173
  */
141
- export type Item<Events = never> = UserMessage | AssistantMessage | EventItem<Events>;
174
+ export type Item<Events = never> = UserMessage | AssistantMessage | EventItem<Events> | ApprovalItem;
142
175
  /**
143
176
  * Schema for a specialist that is working for the assistant. It holds the specialist's name and
144
177
  * when it started, in epoch milliseconds.
@@ -204,7 +237,7 @@ export interface Timeline<Events = never> {
204
237
  * **Details**
205
238
  *
206
239
  * A change holds each item that is new or changed (for example, a message whose block was
207
- * answered or closed), in full. It also holds the current activity, whether the last turn failed,
240
+ * answered or closed, or a call the user approved), in full. It also holds the current activity, whether the last turn failed,
208
241
  * and whether the chat was deleted. `seq` is the position in the chat's log that the change is up
209
242
  * to.
210
243
  *
@@ -225,7 +258,8 @@ export interface Change<Events = never> {
225
258
  * **Details**
226
259
  *
227
260
  * It is the success schema of the RPC that streams a chat's timeline: name the events the chat's
228
- * users see, the same ones the handler passes to {@link changes} as `show`. Each item decodes its
261
+ * users see, the same ones the handler passes to {@link changes} as `show`. Tool calls waiting for
262
+ * approval are always in it, as {@link ApprovalItem}s. Each item decodes its
229
263
  * event with that event's schema, so the client gets `Change<Events>` typed with them and folds it
230
264
  * with {@link fold}, no definition needed. `Change()` carries no app events.
231
265
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.d.ts","names":[],"sources":["../src/Timeline.ts"],"mappings":";;;;;;;;;;;;;;;qBAyCa,aAAW,OAAA;;;;;;;YAQZ,qBAAqB,YAAY;;;;;;;;;qBAUhC,OAAK,OAAA;;;;;;;;;;;;YAaN,eAAe,MAAM;;;;;;;;qBASpB,aAAW,OAAA;;;;;;;;;;;;;;;;;YAaZ,qBAAqB,YAAY;;;;;;;qBAQhC,kBAAgB,OAAA;;;;;;;;;;;;;;;;;YAajB,0BAA0B,iBAAiB;;;;;;;;;;;;;;;;YAiB3C,UAAU,KAAK;WAAqB,YAAY;;WAC7C,MAAM;WAAc;WAAqB;WAAqB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;YA2BxE,KAAK,kBAAkB,cAAc,mBAAmB,UAAU;;;;;;;;qBASjE,mBAAiB,OAAA;;;;;;;;;;YAWlB,2BAA2B,kBAAkB;;;;;;;;;;;qBAY5C,UAAQ,OAAA;;;;;;;;;;;;;YAWT,kBAAkB,SAAS;;;;;;;;;iBAUtB,SAAS;WACf;WACA,OAAO,cAAc,KAAK;WAC1B,UAAU;;WAEV;;WAEA;;;;;;;;;;;;;;;iBAgBM,OAAO;WACb;WACA,OAAO,cAAc,KAAK;WAC1B,UAAU;WACV;WACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkEE,eAAe,eAAe,cAAc,+BACvD,SAAS,WACN,OAAO,MAAM,OAAO,yBAAyB,OAAO;;;;;;;;qBAS5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA0CP,QAAS,QAAM,UAAY,SAAS,SAAO,QAAU,OAAO,YAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;qBAsC/E,OAAQ,QAAQ,GAAG,GAAC,SACtB,OAAO,OAAO,OAAO,SAAS,GAAG,OACzC,OAAO,OAAO,SAAS,SAAS,GAAG;;;;;;;;qBASzB,SAAU,QAAM,UAAY,SAAS;;;;;;;iBAyBjC,YAAY,aAAa,cAAc;;;;;WAK7C,OAAO;;;;;;;;YASN,MAAM,UAAU,cAAc;WAA2C,MAAM;;;;;;;;;;;;;;;;;qBAmC9E;GACV,SAAS,cAAc,YAA2B;GAClD,cAAc,aAAa,MAAM,uBAChC,SAAS,cAAc,WACvB,YAAY,WAAW,SACvB,UAAU,YAAY,QACrB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmDC;GACV,SAAS,cAAc,YAA2B;GAClD,cAAc,aAAa,MAAM,uBAChC,SAAS,cAAc,WACvB,YAAY,WAAW,SACvB,UAAU,YAAY,QACrB,SAAS;;;;;;;;;iBA4MG,WAAW;;WAEjB,QAAQ,cAAc;;WAEtB,OAAO,OAAO,MAAM,MAAgB,SAAS,OAAO;;;;;;;;iBAS9C,eACf,aAAa,cAAc,uCACnB,YAAY;;;;;WAKX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFE;GACV,cAAc,aAAa,MAAM,uBAChC,YAAY,WAAW,SACvB,UAAU,eAAe,SACvB,GAAG,GAAG,SAAS,OAAO,OAAO,UAAyB,GAAG,OAAO,OAAO,OAAO,OAAO,uBAAuB,GAAG;GAClH,GAAG,GAAG,cAAc,aAAa,MAAM,uBACtC,SAAS,OAAO,OAAO,UAAyB,GAAG,IACnD,YAAY,WAAW,SACvB,UAAU,eAAe,QACxB,OAAO,OAAO,OAAO,uBAAuB,GAAG"}
1
+ {"version":3,"file":"Timeline.d.ts","names":[],"sources":["../src/Timeline.ts"],"mappings":";;;;;;;;;;;;;;;qBA4Ca,aAAW,OAAA;;;;;;;YAQZ,qBAAqB,YAAY;;;;;;;;;qBAUhC,OAAK,OAAA;;;;;;;;;;;;YAaN,eAAe,MAAM;;;;;;;;qBASpB,aAAW,OAAA;;;;;;;;;;;;;;;;;YAaZ,qBAAqB,YAAY;;;;;;;qBAQhC,kBAAgB,OAAA;;;;;;;;;;;;;;;;;YAajB,0BAA0B,iBAAiB;;;;;;;;;;;;;;;;YAiB3C,UAAU,KAAK;WAAqB,YAAY;;WAC7C,MAAM;WAAc;WAAqB;WAAqB,OAAO;;;;;;;;;;;;;;;;;;qBAmBvE,cAAY,OAAA;;;;;;;;;;;;;;;YAgBb,sBAAsB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;YA2BnC,KAAK,kBAAkB,cAAc,mBAAmB,UAAU,UAAU;;;;;;;;qBAS3E,mBAAiB,OAAA;;;;;;;;;;YAWlB,2BAA2B,kBAAkB;;;;;;;;;;;qBAY5C,UAAQ,OAAA;;;;;;;;;;;;;YAWT,kBAAkB,SAAS;;;;;;;;;iBAUtB,SAAS;WACf;WACA,OAAO,cAAc,KAAK;WAC1B,UAAU;;WAEV;;WAEA;;;;;;;;;;;;;;;iBAgBM,OAAO;WACb;WACA,OAAO,cAAc,KAAK;WAC1B,UAAU;WACV;WACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsEE,eAAe,eAAe,cAAc,+BACvD,SAAS,WACN,OAAO,MAAM,OAAO,yBAAyB,OAAO;;;;;;;;qBAS5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA0CP,QAAS,QAAM,UAAY,SAAS,SAAO,QAAU,OAAO,YAAU,SAAS;;;;;;;;;;;;;;;;;;;;;;;qBAsC/E,OAAQ,QAAQ,GAAG,GAAC,SACtB,OAAO,OAAO,OAAO,SAAS,GAAG,OACzC,OAAO,OAAO,SAAS,SAAS,GAAG;;;;;;;;qBASzB,SAAU,QAAM,UAAY,SAAS;;;;;;;iBA2BjC,YAAY,aAAa,cAAc;;;;;WAK7C,OAAO;;;;;;;;YASN,MAAM,UAAU,cAAc;WAA2C,MAAM;;;;;;;;;;;;;;;;;qBAmC9E;GACV,SAAS,cAAc,YAA2B;GAClD,cAAc,aAAa,MAAM,uBAChC,SAAS,cAAc,WACvB,YAAY,WAAW,SACvB,UAAU,YAAY,QACrB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmDC;GACV,SAAS,cAAc,YAA2B;GAClD,cAAc,aAAa,MAAM,uBAChC,SAAS,cAAc,WACvB,YAAY,WAAW,SACvB,UAAU,YAAY,QACrB,SAAS;;;;;;;;;iBAsPG,WAAW;;WAEjB,QAAQ,cAAc;;WAEtB,OAAO,OAAO,MAAM,MAAgB,SAAS,OAAO;;;;;;;;iBAS9C,eACf,aAAa,cAAc,uCACnB,YAAY;;;;;WAKX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFE;GACV,cAAc,aAAa,MAAM,uBAChC,YAAY,WAAW,SACvB,UAAU,eAAe,SACvB,GAAG,GAAG,SAAS,OAAO,OAAO,UAAyB,GAAG,OAAO,OAAO,OAAO,OAAO,uBAAuB,GAAG;GAClH,GAAG,GAAG,cAAc,aAAa,MAAM,uBACtC,SAAS,OAAO,OAAO,UAAyB,GAAG,IACnD,YAAY,WAAW,SACvB,UAAU,eAAe,QACxB,OAAO,OAAO,OAAO,uBAAuB,GAAG"}
package/dist/Timeline.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
2
  import { InputId } from "./InputId.js";
3
- import { Attachment, isLibraryEvent } from "./AssistantEvent.js";
3
+ import { Attachment, ToolOutcome, isLibraryEvent } from "./AssistantEvent.js";
4
+ import { apply as apply$1 } from "./ToolApproval.js";
4
5
  import { decodeWith, tagOf } from "./internal/events.js";
5
6
  import { toJson } from "./internal/schema.js";
6
- import { apply as apply$1, empty as empty$1 } from "./AssistantActivity.js";
7
- import { apply as apply$2 } from "./BlockKit.js";
7
+ import { apply as apply$2, empty as empty$1 } from "./AssistantActivity.js";
8
+ import { apply as apply$3 } from "./BlockKit.js";
9
+ import * as Predicate from "effect/Predicate";
8
10
  import * as Schema from "effect/Schema";
9
11
  import * as Stream from "effect/Stream";
10
12
  import * as Option from "effect/Option";
@@ -16,8 +18,9 @@ import * as SchemaTransformation from "effect/SchemaTransformation";
16
18
  * This module provides a chat's timeline: the chat as its user sees it. A chat client receives the
17
19
  * timeline and nothing else.
18
20
  *
19
- * A {@link Timeline} holds the chat's items: the messages with their blocks, and the app's own
20
- * events it shows (each typed with its event, see {@link EventItem}). It also holds the chat's
21
+ * A {@link Timeline} holds the chat's items: the messages with their blocks, the app's own events
22
+ * it shows (each typed with its event, see {@link EventItem}), and the tool calls waiting for the
23
+ * user's approval ({@link ApprovalItem}). It also holds the chat's
21
24
  * {@link Activity}, and whether the last turn failed or the chat was deleted. It never contains the
22
25
  * model's output, tool calls or results, what the model read, or why a turn failed. The server
23
26
  * streams {@link Change}s by piping the assistant's `subscribe` through {@link changes}, and the
@@ -33,6 +36,7 @@ import * as SchemaTransformation from "effect/SchemaTransformation";
33
36
  */
34
37
  var Timeline_exports = /* @__PURE__ */ __exportAll({
35
38
  Activity: () => Activity,
39
+ ApprovalItem: () => ApprovalItem,
36
40
  AssistantMessage: () => AssistantMessage,
37
41
  Block: () => Block,
38
42
  BlockStatus: () => BlockStatus,
@@ -100,6 +104,35 @@ const AssistantMessage = Schema.TaggedStruct("AssistantMessage", {
100
104
  block: Schema.optional(Block)
101
105
  });
102
106
  /**
107
+ * Schema for a tool call that waits, or waited, for the user's approval, as an item on the
108
+ * timeline.
109
+ *
110
+ * **Details**
111
+ *
112
+ * It is only the yes or no: the assistant's own message tells the user what it asks them to
113
+ * approve, so the item carries no params. `id` is the call's id, which the decision is for (see
114
+ * `ToolApproval.approve`), and `tool` the tool's name, for a label. `status` is where the call stands
115
+ * (`ToolApproval.Status`); `reason` is the user's, when they rejected it with one; `outcome` is how
116
+ * the approved call went, once it ran. `at` is when the call started waiting, in epoch
117
+ * milliseconds. A change updates the item in place as the call is decided and run.
118
+ *
119
+ * @category schemas
120
+ * @since 0.4.0
121
+ */
122
+ const ApprovalItem = Schema.TaggedStruct("ToolApproval", {
123
+ id: Schema.String,
124
+ tool: Schema.String,
125
+ status: Schema.Literals([
126
+ "pending",
127
+ "approved",
128
+ "rejected",
129
+ "expired"
130
+ ]),
131
+ reason: Schema.optional(Schema.String),
132
+ outcome: Schema.optional(ToolOutcome),
133
+ at: Schema.Finite
134
+ });
135
+ /**
103
136
  * Schema for a specialist that is working for the assistant. It holds the specialist's name and
104
137
  * when it started, in epoch milliseconds.
105
138
  *
@@ -134,6 +167,7 @@ function makeChange(events = []) {
134
167
  const AnyItem = Schema.toCodecJson(Schema.Union([
135
168
  UserMessage,
136
169
  AssistantMessage,
170
+ ApprovalItem,
137
171
  ...events.map(eventItem)
138
172
  ]));
139
173
  const decodeItem = Schema.decodeUnknownOption(AnyItem);
@@ -156,7 +190,8 @@ function makeChange(events = []) {
156
190
  * **Details**
157
191
  *
158
192
  * It is the success schema of the RPC that streams a chat's timeline: name the events the chat's
159
- * users see, the same ones the handler passes to {@link changes} as `show`. Each item decodes its
193
+ * users see, the same ones the handler passes to {@link changes} as `show`. Tool calls waiting for
194
+ * approval are always in it, as {@link ApprovalItem}s. Each item decodes its
160
195
  * event with that event's schema, so the client gets `Change<Events>` typed with them and folds it
161
196
  * with {@link fold}, no definition needed. `Change()` carries no app events.
162
197
  *
@@ -345,6 +380,7 @@ const sameActivity = (a, b) => a.working === b.working && a.specialists.length =
345
380
  });
346
381
  const folding = (show) => {
347
382
  const shown = new Map(show.map((schema) => [tagOf(schema), schema]));
383
+ let approvals = /* @__PURE__ */ new Map();
348
384
  let seq = 0;
349
385
  let failed = false;
350
386
  let deleted = false;
@@ -405,6 +441,12 @@ const folding = (show) => {
405
441
  return [answer.messageId];
406
442
  },
407
443
  BlocksClosed: (closed) => closed.messageIds,
444
+ ApprovalRequested: () => [],
445
+ ApprovalDecision: () => {
446
+ failed = false;
447
+ return [];
448
+ },
449
+ ApprovalsExpired: () => [],
408
450
  TurnFailed: () => {
409
451
  failed = true;
410
452
  return [];
@@ -432,19 +474,34 @@ const folding = (show) => {
432
474
  event: decoded
433
475
  }));
434
476
  };
477
+ const callsOf = (event) => Predicate.isTagged(event, "ApprovalsExpired") ? event.callIds : Predicate.isTagged(event, "ApprovalRequested") || Predicate.isTagged(event, "ApprovalDecision") || Predicate.isTagged(event, "ToolResult") ? [event.callId] : [];
478
+ const approvalItems = (event, before) => callsOf(event).flatMap((callId) => {
479
+ const state = approvals.get(callId);
480
+ if (state === void 0 || state === before.get(callId)) return [];
481
+ const item = ApprovalItem.make({
482
+ id: callId,
483
+ tool: state.request.tool,
484
+ status: state.status,
485
+ reason: state.reason,
486
+ outcome: state.outcome,
487
+ at: state.at
488
+ });
489
+ items.set(callId, item);
490
+ return [item];
491
+ });
435
492
  const step = (entry) => {
436
- const before = {
493
+ const previous = {
437
494
  activity: activityOf(activity),
438
495
  failed,
439
496
  deleted
440
497
  };
441
498
  const { event } = entry;
442
499
  seq = entry.seq;
443
- activity = apply$1(activity, entry);
500
+ activity = apply$2(activity, entry);
444
501
  if (!isLibraryEvent(event)) {
445
502
  const now = activityOf(activity);
446
503
  return Option.match(eventItemOf(entry), {
447
- onNone: () => sameActivity(before.activity, now) ? void 0 : {
504
+ onNone: () => sameActivity(previous.activity, now) ? void 0 : {
448
505
  seq,
449
506
  items: [],
450
507
  activity: now,
@@ -463,19 +520,21 @@ const folding = (show) => {
463
520
  }
464
521
  });
465
522
  }
466
- blocks = apply$2(blocks, event);
467
- const changed = [...new Set(touched(event, entry.at))].flatMap((id) => {
523
+ blocks = apply$3(blocks, event);
524
+ const decidedBefore = approvals;
525
+ approvals = apply$1(approvals, entry);
526
+ const all = [...[...new Set(touched(event, entry.at))].flatMap((id) => {
468
527
  const message = sent.get(id);
469
528
  const current = items.get(id);
470
529
  if (message === void 0 || current === void 0) return current === void 0 ? [] : [current];
471
530
  const next = itemOf(message, current.at);
472
531
  items.set(id, next);
473
532
  return [next];
474
- });
533
+ }), ...approvalItems(event, decidedBefore)];
475
534
  const now = activityOf(activity);
476
- return changed.length === 0 && failed === before.failed && deleted === before.deleted && sameActivity(before.activity, now) ? void 0 : {
535
+ return all.length === 0 && failed === previous.failed && deleted === previous.deleted && sameActivity(previous.activity, now) ? void 0 : {
477
536
  seq,
478
- items: changed,
537
+ items: all,
479
538
  activity: now,
480
539
  failed,
481
540
  deleted
@@ -567,6 +626,6 @@ function changesWith(entries, _definition, options) {
567
626
  */
568
627
  const changes = dual((args) => Stream.isStream(args[0]), changesWith);
569
628
  //#endregion
570
- export { Activity, AssistantMessage, Block, BlockStatus, Change, SpecialistWorking, Timeline_exports, UserMessage, apply, changes, empty, fold, fromEntries, isIdle, snapshot };
629
+ export { Activity, ApprovalItem, AssistantMessage, Block, BlockStatus, Change, SpecialistWorking, Timeline_exports, UserMessage, apply, changes, empty, fold, fromEntries, isIdle, snapshot };
571
630
 
572
631
  //# sourceMappingURL=Timeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.js","names":["AssistantEvent.Attachment","AppEvents.tagOf","AssistantActivity.empty","AppEvents.decodeWith","AssistantActivity.apply","AssistantEvent.isLibraryEvent","BlockKit.apply"],"sources":["../src/Timeline.ts"],"sourcesContent":["/**\n * This module provides a chat's timeline: the chat as its user sees it. A chat client receives the\n * timeline and nothing else.\n *\n * A {@link Timeline} holds the chat's items: the messages with their blocks, and the app's own\n * events it shows (each typed with its event, see {@link EventItem}). It also holds the chat's\n * {@link Activity}, and whether the last turn failed or the chat was deleted. It never contains the\n * model's output, tool calls or results, what the model read, or why a turn failed. The server\n * streams {@link Change}s by piping the assistant's `subscribe` through {@link changes}, and the\n * client folds them with {@link fold} (or {@link apply}, from {@link empty}). On the server,\n * {@link fromEntries} builds a whole timeline from a chat's log, and {@link snapshot} the whole\n * timeline as one change.\n *\n * **Only the app events in `show` reach the timeline**, and none by default: an event that is only\n * for the assistant, like a summary of the chat, stays out. The RPC's schema, `Change([...])`,\n * names the same events and types the client's timeline with them.\n *\n * @since 0.3.0\n */\nimport { dual } from \"effect/Function\"\nimport * as Match from \"effect/Match\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaTransformation from \"effect/SchemaTransformation\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Assistant from \"./Assistant.ts\"\nimport * as AssistantActivity from \"./AssistantActivity.ts\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport * as BlockKit from \"./BlockKit.ts\"\nimport { InputId } from \"./InputId.ts\"\nimport * as AppEvents from \"./internal/events.ts\"\nimport { toJson } from \"./internal/schema.ts\"\n\n/**\n * Schema for the status of a block. A block is `\"open\"` while it accepts an answer, `\"answered\"`\n * once the user has used it, and `\"closed\"` when it closed without an answer because the user\n * answered in words or moved on.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const BlockStatus = Schema.Literals([\"open\", \"answered\", \"closed\"])\n\n/**\n * The status of a block.\n *\n * @category models\n * @since 0.1.0\n */\nexport type BlockStatus = typeof BlockStatus.Type\n\n/**\n * Schema for a block on an assistant's message. It holds the block's kind, its props (JSON, as\n * the block's props schema encodes them), its status, and the user's answer (JSON, as the block's\n * answer schema encodes it).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Block = Schema.Struct({\n kind: Schema.String,\n props: Schema.Json,\n status: BlockStatus,\n answer: Schema.optional(Schema.Json)\n})\n\n/**\n * Represents a block on an assistant's message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Block = typeof Block.Type\n\n/**\n * Schema for a message the user sent. `at` is when it was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserMessage = Schema.TaggedStruct(\"UserMessage\", {\n id: InputId,\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(AssistantEvent.Attachment)),\n at: Schema.Finite\n})\n\n/**\n * Represents a message the user sent.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserMessage = typeof UserMessage.Type\n\n/**\n * Schema for a message the assistant sent the user, including its block if it has one.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantMessage = Schema.TaggedStruct(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n at: Schema.Finite,\n block: Schema.optional(Block)\n})\n\n/**\n * Represents a message the assistant sent the user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantMessage = typeof AssistantMessage.Type\n\n/**\n * Represents one of the app's own events in the timeline: an item between the messages, in the\n * order the chat stored it.\n *\n * **Details**\n *\n * The item's `_tag` is the event's, so a match over a timeline's items takes a handler per event\n * beside `UserMessage` and `AssistantMessage`. The event, decoded with its schema, is in `event`,\n * and the item's own fields sit beside it rather than spread into it: an event may have an `id` or\n * an `at` of its own, and a class instance keeps its prototype. `id` is fixed per entry of the\n * chat's log, and `at` is when the event was stored, in epoch milliseconds.\n *\n * @category models\n * @since 0.3.0\n */\nexport type EventItem<E> = E extends { readonly _tag: infer Tag extends string }\n ? { readonly _tag: Tag; readonly id: string; readonly at: number; readonly event: E }\n : never\n\n/**\n * Represents an item in the chat's timeline: a message the user or the assistant sent, or one of\n * the app's own events it shows (see {@link EventItem}).\n *\n * **Example** (Showing each item of a chat)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n * import * as Match from \"effect/Match\"\n * import * as Schema from \"effect/Schema\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const line = (item: Timeline.Item<DailyBudgetReached>) =>\n * Match.valueTags(item, {\n * UserMessage: (sent) => `You: ${sent.text}`,\n * AssistantMessage: (sent) => `Assistant: ${sent.text}`,\n * DailyBudgetReached: () => \"You've reached today's budget.\"\n * })\n * ```\n *\n * @category models\n * @since 0.3.0\n */\nexport type Item<Events = never> = UserMessage | AssistantMessage | EventItem<Events>\n\n/**\n * Schema for a specialist that is working for the assistant. It holds the specialist's name and\n * when it started, in epoch milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const SpecialistWorking = Schema.Struct({\n specialist: Schema.String,\n since: Schema.Finite\n})\n\n/**\n * Represents a specialist that is working for the assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SpecialistWorking = typeof SpecialistWorking.Type\n\n/**\n * Schema for what is working in the chat.\n *\n * `working` is true while the assistant runs a turn, or when a turn is about to start for\n * something that arrived. `specialists` lists the specialists that are working. When the chat is\n * idle, nothing is working, even if a workflow is waiting on a timer or on the user.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Activity = Schema.Struct({\n working: Schema.Boolean,\n specialists: Schema.Array(SpecialistWorking)\n})\n\n/**\n * Represents what is working in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Activity = typeof Activity.Type\n\n/**\n * Represents a chat's timeline: the chat as its user sees it, typed with the app's events it shows.\n * It holds the chat's items in order, what is working in it, whether the last turn failed, and\n * whether the chat was deleted. `seq` is the position in the chat's log that the timeline is up to.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface Timeline<Events = never> {\n readonly seq: number\n readonly items: ReadonlyArray<Item<Events>>\n readonly activity: Activity\n /** Whether the last turn failed with no message after it. */\n readonly failed: boolean\n /** Whether the chat was deleted. A deleted chat accepts nothing more, and its stream ends. */\n readonly deleted: boolean\n}\n\n/**\n * Represents a change to a chat's timeline, typed with the app's events it shows.\n *\n * **Details**\n *\n * A change holds each item that is new or changed (for example, a message whose block was\n * answered or closed), in full. It also holds the current activity, whether the last turn failed,\n * and whether the chat was deleted. `seq` is the position in the chat's log that the change is up\n * to.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface Change<Events = never> {\n readonly seq: number\n readonly items: ReadonlyArray<Item<Events>>\n readonly activity: Activity\n readonly failed: boolean\n readonly deleted: boolean\n}\n\nconst eventItem = (schema: AssistantEvent.AppEventSchema) =>\n Schema.Struct({ _tag: Schema.Literal(AppEvents.tagOf(schema)), id: Schema.String, at: Schema.Finite, event: schema })\n\nfunction makeChange<const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []>(\n events?: Events\n): Schema.Codec<Change<Events[number][\"Type\"]>, Schema.Json>\n// The overload states the decoded type: an item per given schema, each typed with its own event.\nfunction makeChange(events: ReadonlyArray<AssistantEvent.AppEventSchema> = []): Schema.Top {\n const AnyItem = Schema.toCodecJson(Schema.Union([UserMessage, AssistantMessage, ...events.map(eventItem)]))\n const decodeItem = Schema.decodeUnknownOption(AnyItem)\n const encodeItem = Schema.encodeUnknownSync(AnyItem)\n\n return Schema.Struct({\n seq: Schema.Int,\n // An item for an event this schema doesn't name, or that doesn't decode (from a newer server, say), is left out\n // rather than failing the whole change.\n items: Schema.Array(Schema.Json).pipe(\n Schema.decodeTo(\n Schema.Array(Schema.toType(AnyItem)),\n SchemaTransformation.transform<ReadonlyArray<typeof AnyItem.Type>, ReadonlyArray<Schema.Json>>({\n decode: (json) => json.flatMap((item) => Option.toArray(decodeItem(item))),\n encode: (items) => items.map((item) => encodeItem(item))\n })\n )\n ),\n activity: Activity,\n failed: Schema.Boolean,\n deleted: Schema.Boolean\n })\n}\n\n/**\n * Creates the schema for the changes to a chat's timeline, with items for the given app events,\n * in JSON.\n *\n * **Details**\n *\n * It is the success schema of the RPC that streams a chat's timeline: name the events the chat's\n * users see, the same ones the handler passes to {@link changes} as `show`. Each item decodes its\n * event with that event's schema, so the client gets `Change<Events>` typed with them and folds it\n * with {@link fold}, no definition needed. `Change()` carries no app events.\n *\n * A handler whose changes show an event the schema doesn't name doesn't compile.\n *\n * **Example** (An RPC that streams a chat's timeline, with the budget notice)\n *\n * ```ts\n * import { ChatId, Timeline } from \"effect-assistant\"\n * import * as Rpc from \"effect/rpc/Rpc\"\n * import * as Schema from \"effect/Schema\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const Subscribe = Rpc.make(\"Subscribe\", {\n * payload: { chatId: ChatId.ChatId },\n * success: Timeline.Change([DailyBudgetReached]),\n * stream: true\n * })\n * ```\n *\n * @category schemas\n * @since 0.3.0\n */\nexport const Change: <const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []>(\n events?: Events\n) => Schema.Codec<Change<Events[number][\"Type\"]>, Schema.Json> = makeChange\n\n/**\n * The timeline of a chat before anything has happened in it, with no items and nothing working.\n * Use it as the starting point when folding changes with {@link apply}.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const empty: Timeline = {\n seq: 0,\n items: [],\n activity: { working: false, specialists: [] },\n failed: false,\n deleted: false\n}\n\n/**\n * Returns the timeline after a change.\n *\n * **Details**\n *\n * Each item in the change replaces the item with the same id, in place, and new items are added\n * after the rest. The change's activity, `failed`, and `deleted` replace the timeline's. Applying a\n * change twice changes nothing, so a client that resubscribes from the start can fold again and get\n * the same timeline.\n *\n * The timeline is typed with the change's events, as its RPC's schema decoded them (see\n * {@link Change}). To fold a stream of changes, use {@link fold}: in\n * `Stream.scan(() => Timeline.empty, Timeline.apply)`, TypeScript fixes the state's type from\n * `empty` before it sees the changes' events, so that form needs the timeline's type written out.\n *\n * **Example** (Folding changes into your own state)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n *\n * interface State<Events> {\n * readonly timeline: Timeline.Timeline<Events>\n * readonly unread: number\n * }\n *\n * const next = <Events>(state: State<Events>, change: Timeline.Change<Events>): State<Events> => ({\n * timeline: Timeline.apply(state.timeline, change),\n * unread: state.unread + change.items.length\n * })\n * ```\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const apply = <Events>(timeline: Timeline<Events>, change: Change<Events>): Timeline<Events> => {\n const changed = new Map(change.items.map((item) => [item.id, item]))\n const known = new Set(timeline.items.map((item) => item.id))\n\n return {\n seq: Math.max(timeline.seq, change.seq),\n items: [\n ...timeline.items.map((item) => changed.get(item.id) ?? item),\n ...change.items.filter((item) => !known.has(item.id))\n ],\n activity: change.activity,\n failed: change.failed,\n deleted: change.deleted\n }\n}\n\n/**\n * Returns the timelines a stream of changes folds to, from {@link empty}, typed with the changes'\n * events.\n *\n * **Example** (A chat's timeline on the client)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n * import * as Stream from \"effect/Stream\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * // The stream of an RPC whose success schema is `Timeline.Change([DailyBudgetReached])`.\n * declare const changes: Stream.Stream<Timeline.Change<DailyBudgetReached>>\n *\n * const timelines = changes.pipe(Timeline.fold) // Stream<Timeline.Timeline<DailyBudgetReached>>\n * ```\n *\n * @category combinators\n * @since 0.3.0\n */\nexport const fold = <Events, E, R>(\n changes: Stream.Stream<Change<Events>, E, R>\n): Stream.Stream<Timeline<Events>, E, R> => changes.pipe(Stream.scan((): Timeline<Events> => empty, apply))\n\n/**\n * Returns whether nothing is working in the chat: no turn is running or about to start, and no\n * specialist is working.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isIdle = <Events>(timeline: Timeline<Events>): boolean =>\n !timeline.activity.working && timeline.activity.specialists.length === 0\n\ninterface AnyEventItem {\n readonly _tag: string\n readonly id: string\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\n// What the fold below builds: the overloads that use it type its items with the events shown.\ninterface AnyChange {\n readonly seq: number\n readonly items: ReadonlyArray<UserMessage | AssistantMessage | AnyEventItem>\n readonly activity: Activity\n readonly failed: boolean\n readonly deleted: boolean\n}\n\n/**\n * Represents the options for building a chat's timeline from its log.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface ShowOptions<Show extends ReadonlyArray<AssistantEvent.AppEventSchema>> {\n /**\n * The schemas of the app's events the chat's users see. Each becomes an item in the timeline;\n * the app's other events are left out. Defaults to none.\n */\n readonly show?: Show | undefined\n}\n\n/**\n * Represents the schemas an assistant's timeline may show: some of its own events.\n *\n * @category utility types\n * @since 0.3.0\n */\nexport type Shown<Events> = ReadonlyArray<AssistantEvent.AppEventSchema & { readonly Type: Events }>\n\nfunction snapshotWith(entries: ReadonlyArray<AssistantEvent.AnyEntry>): Change\nfunction snapshotWith<Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n): Change<Show[number][\"Type\"]>\nfunction snapshotWith(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n _definition?: Schemas,\n options?: ShowOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): AnyChange {\n const timeline = folding(options?.show ?? [])\n\n for (const entry of entries) timeline.step(entry)\n\n return timeline.snapshot\n}\n\n/**\n * Returns a chat's whole timeline as one change, built from the entries of its log.\n *\n * **When to use**\n *\n * Use it on the server for an RPC that returns a whole chat at once: its success schema is\n * {@link Change} with the same events as `show`, and the client applies it to {@link empty}.\n *\n * **Details**\n *\n * Only the app's events in `show` become items; without it, the timeline has none.\n *\n * @category constructors\n * @since 0.3.0\n */\nexport const snapshot: {\n (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Change\n <Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n ): Change<Show[number][\"Type\"]>\n} = snapshotWith\n\nfunction fromEntriesWith(entries: ReadonlyArray<AssistantEvent.AnyEntry>): Timeline\nfunction fromEntriesWith<Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n): Timeline<Show[number][\"Type\"]>\nfunction fromEntriesWith(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n _definition?: Schemas,\n options?: ShowOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): AnyChange {\n const timeline = folding(options?.show ?? [])\n\n for (const entry of entries) timeline.step(entry)\n\n return timeline.snapshot\n}\n\n/**\n * Returns a chat's timeline, built from the entries of its log.\n *\n * **When to use**\n *\n * Use it in server-side code that reads a whole chat. A client gets the timeline as a stream from\n * {@link changes} instead, and an RPC that returns a whole chat sends a {@link snapshot}.\n *\n * **Details**\n *\n * Only the app's events in `show` become items, typed with their schemas; without it, the timeline\n * has none.\n *\n * **Example** (Building a chat's timeline from its log)\n *\n * ```ts\n * import { type ChatId, ChatLog, Timeline } from \"effect-assistant\"\n * import * as Effect from \"effect/Effect\"\n *\n * const timelineOf = (chatId: ChatId.ChatId) =>\n * ChatLog.ChatLog.use((log) => log.read(chatId)).pipe(\n * Effect.map((entries) => Timeline.fromEntries(entries))\n * )\n * ```\n *\n * @see {@link changes} for a stream of changes instead of a whole timeline\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const fromEntries: {\n (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Timeline\n <Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n ): Timeline<Show[number][\"Type\"]>\n} = fromEntriesWith\n\ninterface Schemas {\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n}\n\ninterface Folding {\n readonly step: (entry: AssistantEvent.AnyEntry) => AnyChange | undefined\n readonly snapshot: AnyChange\n}\n\nconst activityOf = (activity: AssistantActivity.State): Activity => ({\n working: activity.turnSince !== undefined || activity.queued,\n specialists: [...activity.specialists.values()].map((run) => ({\n specialist: run.specialist,\n since: run.since\n }))\n})\n\nconst sameActivity = (a: Activity, b: Activity) =>\n a.working === b.working &&\n a.specialists.length === b.specialists.length &&\n a.specialists.every((run, i) => {\n const other = b.specialists[i]\n\n return other !== undefined && run.specialist === other.specialist && run.since === other.since\n })\n\nconst folding = (show: ReadonlyArray<AssistantEvent.AppEventSchema>): Folding => {\n const shown = new Map(show.map((schema) => [AppEvents.tagOf(schema), schema]))\n let seq = 0\n let failed = false\n let deleted = false\n let activity = AssistantActivity.empty\n let blocks: ReadonlyMap<string, BlockKit.State> = new Map()\n // The items in order, and the assistant's messages as stored: their blocks change as the chat goes on.\n const items = new Map<string, UserMessage | AssistantMessage | AnyEventItem>()\n const sent = new Map<string, AssistantEvent.AssistantMessage>()\n\n const blockOf = (message: AssistantEvent.AssistantMessage): Block | undefined => {\n if (message.block === undefined) return undefined\n\n const state = blocks.get(message.id)\n const status = state === undefined || state.open ? \"open\" : state.answer === undefined ? \"closed\" : \"answered\"\n const block: Block = { kind: message.block.kind, props: toJson(message.block.props), status }\n\n return state?.answer === undefined ? block : { ...block, answer: toJson(state.answer.value) }\n }\n\n const itemOf = (message: AssistantEvent.AssistantMessage, at: number): AssistantMessage => {\n const block = blockOf(message)\n\n return block === undefined\n ? AssistantMessage.make({ id: message.id, text: message.text, at })\n : AssistantMessage.make({ id: message.id, text: message.text, at, block })\n }\n\n const touched = (event: AssistantEvent.AssistantEvent, at: number): ReadonlyArray<string> =>\n Match.value(event).pipe(\n Match.tagsExhaustive({\n UserMessage: (message) => {\n items.set(\n message.id,\n message.attachments === undefined\n ? UserMessage.make({ id: message.id, text: message.text, at })\n : UserMessage.make({\n id: message.id,\n text: message.text,\n attachments: message.attachments,\n at\n })\n )\n failed = false\n\n return [message.id]\n },\n AssistantMessage: (message) => {\n sent.set(message.id, message)\n items.set(message.id, itemOf(message, at))\n failed = false\n\n return [message.id, ...(message.closes ?? [])]\n },\n BlockAnswer: (answer) => {\n failed = false\n\n return [answer.messageId]\n },\n BlocksClosed: (closed) => closed.messageIds,\n TurnFailed: () => {\n failed = true\n\n return []\n },\n ModelOutput: () => [],\n ToolStarted: () => [],\n ToolResult: () => [],\n TurnStarted: () => [],\n TurnCompleted: () => [],\n SpecialistStarted: () => [],\n SpecialistFinished: () => [],\n ChatDeleted: () => {\n deleted = true\n\n return []\n }\n })\n )\n\n // Decoded as `subscribe` streams it, or as the log stores it; one that doesn't decode is left out.\n const eventItemOf = (entry: AssistantEvent.AnyEntry): Option.Option<AnyEventItem> => {\n const schema = shown.get(entry.event._tag)\n\n if (schema === undefined) return Option.none()\n\n const event = Schema.is(schema)(entry.event) ? Option.some(entry.event) : AppEvents.decodeWith(schema, entry.event)\n\n return Option.map(event, (decoded) => ({\n _tag: decoded._tag,\n id: `app-event:${entry.seq}`,\n at: entry.at,\n event: decoded\n }))\n }\n\n const step = (entry: AssistantEvent.AnyEntry): AnyChange | undefined => {\n const before = { activity: activityOf(activity), failed, deleted }\n const { event } = entry\n\n seq = entry.seq\n activity = AssistantActivity.apply(activity, entry)\n\n if (!AssistantEvent.isLibraryEvent(event)) {\n const now = activityOf(activity)\n\n return Option.match(eventItemOf(entry), {\n onNone: () =>\n sameActivity(before.activity, now) ? undefined : { seq, items: [], activity: now, failed, deleted },\n onSome: (item) => {\n items.set(item.id, item)\n\n return { seq, items: [item], activity: now, failed, deleted }\n }\n })\n }\n\n blocks = BlockKit.apply(blocks, event)\n\n const changed = [...new Set(touched(event, entry.at))].flatMap((id) => {\n const message = sent.get(id)\n const current = items.get(id)\n\n if (message === undefined || current === undefined) return current === undefined ? [] : [current]\n\n const next = itemOf(message, current.at)\n items.set(id, next)\n\n return [next]\n })\n\n const now = activityOf(activity)\n\n return changed.length === 0 &&\n failed === before.failed &&\n deleted === before.deleted &&\n sameActivity(before.activity, now)\n ? undefined\n : { seq, items: changed, activity: now, failed, deleted }\n }\n\n return {\n step,\n get snapshot() {\n return { seq, items: [...items.values()], activity: activityOf(activity), failed, deleted }\n }\n }\n}\n\nconst merge = (changes: ReadonlyArray<AnyChange>): AnyChange | undefined => {\n const last = changes.at(-1)\n\n if (last === undefined) return undefined\n\n const items = new Map<string, UserMessage | AssistantMessage | AnyEventItem>()\n\n for (const change of changes) for (const item of change.items) items.set(item.id, item)\n\n return {\n seq: last.seq,\n items: [...items.values()],\n activity: last.activity,\n failed: last.failed,\n deleted: last.deleted\n }\n}\n\n/**\n * Represents an assistant's definition as the timeline reads it. An assistant made with\n * `Assistant.make` fits it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Definition<Events = never> {\n /** The schemas of the assistant's events. */\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n /** The schema of the chat's entries, typed with the assistant's events: it types `show`. */\n readonly Entry: Schema.Codec<Assistant.Entry<Events>, Schema.Json>\n}\n\n/**\n * Represents the options accepted by {@link changes}.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface ChangesOptions<\n Show extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []\n> extends ShowOptions<Show> {\n /**\n * The `seq` up to which the client already has the timeline. The result contains only the\n * changes after it. Defaults to `0`, every change.\n */\n readonly after?: number | undefined\n}\n\nfunction changesWith<E, R, Events, const Show extends Shown<Events> = readonly []>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n): Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\nfunction changesWith<E, R>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n _definition: Schemas,\n options?: ChangesOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): Stream.Stream<AnyChange, E, R> {\n const after = options?.after ?? 0\n\n return entries.pipe(\n Stream.mapAccumArray(\n () => folding(options?.show ?? []),\n (timeline, chunk) => {\n const changed = chunk.flatMap((entry) => {\n const change = timeline.step(entry)\n\n return change === undefined || entry.seq <= after ? [] : [change]\n })\n\n const merged = merge(changed)\n\n return [timeline, merged === undefined ? [] : [merged]]\n }\n )\n )\n}\n\n/**\n * Returns the stream of changes to a chat's timeline that its entries produce, for a chat client.\n *\n * **Details**\n *\n * The entries must stream from the start of the chat's log, as `assistant.subscribe(chatId)` does\n * without `after`. The fold reads every entry, because a message depends on earlier entries: its\n * block is answered or closed later. `after` only skips emitting the changes up to that `seq`, so a\n * client that has the timeline up to a `seq` receives only what changed since. Pass `after` here,\n * not to `subscribe`: a subscription that starts after a `seq` gives the fold an incomplete log,\n * and the changes it produces are wrong.\n *\n * Each chunk of entries becomes one change; the stored entries, for example, arrive as one chunk.\n *\n * **Only the app's events in `show` reach the client.** Each one becomes an item between the\n * messages (see {@link EventItem}); the app's other events, like a summary of the chat for the\n * model, are left out. Without `show`, none are shown. The changes are typed with the events shown,\n * so the RPC's success schema names the same ones (`Timeline.Change([...])`), and a handler that\n * shows one the schema doesn't name doesn't compile. A timeline shows a turn as working once it\n * starts, or from the user's input.\n *\n * **Example** (Streaming a chat's timeline to a client, with the one app event its user sees)\n *\n * ```ts\n * import { Assistant, type ChatId, Timeline } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n * import * as Stream from \"effect/Stream\"\n *\n * class Summary extends Schema.TaggedClass<Summary>()(\"Summary\", { text: Schema.String, throughSeq: Schema.Int }) {}\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const MyAssistant = Assistant.make(\"my-assistant\", { events: [Summary, DailyBudgetReached] })\n *\n * // The summary is for the model: only the budget notice reaches the user.\n * const watch = (chatId: ChatId.ChatId, after: number) =>\n * Stream.unwrap(\n * MyAssistant.useSync((assistant) =>\n * assistant.subscribe(chatId).pipe(Timeline.changes(MyAssistant, { show: [DailyBudgetReached], after }))\n * )\n * )\n * ```\n *\n * @see {@link fold} for folding the changes into a timeline on the client\n *\n * @category utils\n * @since 0.3.0\n */\nexport const changes: {\n <Events, const Show extends Shown<Events> = readonly []>(\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n ): <E, R>(entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>) => Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\n <E, R, Events, const Show extends Shown<Events> = readonly []>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n ): Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\n} = dual((args) => Stream.isStream(args[0]), changesWith)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,cAAc,OAAO,SAAS;CAAC;CAAQ;CAAY;AAAQ,CAAC;;;;;;;;;AAkBzE,MAAa,QAAQ,OAAO,OAAO;CACjC,MAAM,OAAO;CACb,OAAO,OAAO;CACd,QAAQ;CACR,QAAQ,OAAO,SAAS,OAAO,IAAI;AACrC,CAAC;;;;;;;;AAiBD,MAAa,cAAc,OAAO,aAAa,eAAe;CAC5D,IAAI;CACJ,MAAM,OAAO;CACb,aAAa,OAAO,SAAS,OAAO,MAAMA,UAAyB,CAAC;CACpE,IAAI,OAAO;AACb,CAAC;;;;;;;AAgBD,MAAa,mBAAmB,OAAO,aAAa,oBAAoB;CACtE,IAAI,OAAO;CACX,MAAM,OAAO;CACb,IAAI,OAAO;CACX,OAAO,OAAO,SAAS,KAAK;AAC9B,CAAC;;;;;;;;AA8DD,MAAa,oBAAoB,OAAO,OAAO;CAC7C,YAAY,OAAO;CACnB,OAAO,OAAO;AAChB,CAAC;;;;;;;;;;;AAoBD,MAAa,WAAW,OAAO,OAAO;CACpC,SAAS,OAAO;CAChB,aAAa,OAAO,MAAM,iBAAiB;AAC7C,CAAC;AAiDD,MAAM,aAAa,WACjB,OAAO,OAAO;CAAE,MAAM,OAAO,QAAQC,MAAgB,MAAM,CAAC;CAAG,IAAI,OAAO;CAAQ,IAAI,OAAO;CAAQ,OAAO;AAAO,CAAC;AAMtH,SAAS,WAAW,SAAuD,CAAC,GAAe;CACzF,MAAM,UAAU,OAAO,YAAY,OAAO,MAAM;EAAC;EAAa;EAAkB,GAAG,OAAO,IAAI,SAAS;CAAC,CAAC,CAAC;CAC1G,MAAM,aAAa,OAAO,oBAAoB,OAAO;CACrD,MAAM,aAAa,OAAO,kBAAkB,OAAO;CAEnD,OAAO,OAAO,OAAO;EACnB,KAAK,OAAO;EAGZ,OAAO,OAAO,MAAM,OAAO,IAAI,CAAC,CAAC,KAC/B,OAAO,SACL,OAAO,MAAM,OAAO,OAAO,OAAO,CAAC,GACnC,qBAAqB,UAA0E;GAC7F,SAAS,SAAS,KAAK,SAAS,SAAS,OAAO,QAAQ,WAAW,IAAI,CAAC,CAAC;GACzE,SAAS,UAAU,MAAM,KAAK,SAAS,WAAW,IAAI,CAAC;EACzD,CAAC,CACH,CACF;EACA,UAAU;EACV,QAAQ,OAAO;EACf,SAAS,OAAO;CAClB,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,SAEoD;;;;;;;;AASjE,MAAa,QAAkB;CAC7B,KAAK;CACL,OAAO,CAAC;CACR,UAAU;EAAE,SAAS;EAAO,aAAa,CAAC;CAAE;CAC5C,QAAQ;CACR,SAAS;AACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAa,SAAiB,UAA4B,WAA6C;CACrG,MAAM,UAAU,IAAI,IAAI,OAAO,MAAM,KAAK,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;CACnE,MAAM,QAAQ,IAAI,IAAI,SAAS,MAAM,KAAK,SAAS,KAAK,EAAE,CAAC;CAE3D,OAAO;EACL,KAAK,KAAK,IAAI,SAAS,KAAK,OAAO,GAAG;EACtC,OAAO,CACL,GAAG,SAAS,MAAM,KAAK,SAAS,QAAQ,IAAI,KAAK,EAAE,KAAK,IAAI,GAC5D,GAAG,OAAO,MAAM,QAAQ,SAAS,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC,CACtD;EACA,UAAU,OAAO;EACjB,QAAQ,OAAO;EACf,SAAS,OAAO;CAClB;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,QACX,YAC0C,QAAQ,KAAK,OAAO,WAA6B,OAAO,KAAK,CAAC;;;;;;;;AAS1G,MAAa,UAAkB,aAC7B,CAAC,SAAS,SAAS,WAAW,SAAS,SAAS,YAAY,WAAW;AA8CzE,SAAS,aACP,SACA,aACA,SACW;CACX,MAAM,WAAW,QAAQ,SAAS,QAAQ,CAAC,CAAC;CAE5C,KAAK,MAAM,SAAS,SAAS,SAAS,KAAK,KAAK;CAEhD,OAAO,SAAS;AAClB;;;;;;;;;;;;;;;;AAiBA,MAAa,WAOT;AAQJ,SAAS,gBACP,SACA,aACA,SACW;CACX,MAAM,WAAW,QAAQ,SAAS,QAAQ,CAAC,CAAC;CAE5C,KAAK,MAAM,SAAS,SAAS,SAAS,KAAK,KAAK;CAEhD,OAAO,SAAS;AAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,cAOT;AAWJ,MAAM,cAAc,cAAiD;CACnE,SAAS,SAAS,cAAc,KAAA,KAAa,SAAS;CACtD,aAAa,CAAC,GAAG,SAAS,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS;EAC5D,YAAY,IAAI;EAChB,OAAO,IAAI;CACb,EAAE;AACJ;AAEA,MAAM,gBAAgB,GAAa,MACjC,EAAE,YAAY,EAAE,WAChB,EAAE,YAAY,WAAW,EAAE,YAAY,UACvC,EAAE,YAAY,OAAO,KAAK,MAAM;CAC9B,MAAM,QAAQ,EAAE,YAAY;CAE5B,OAAO,UAAU,KAAA,KAAa,IAAI,eAAe,MAAM,cAAc,IAAI,UAAU,MAAM;AAC3F,CAAC;AAEH,MAAM,WAAW,SAAgE;CAC/E,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK,WAAW,CAACA,MAAgB,MAAM,GAAG,MAAM,CAAC,CAAC;CAC7E,IAAI,MAAM;CACV,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,WAAWC;CACf,IAAI,yBAA8C,IAAI,IAAI;CAE1D,MAAM,wBAAQ,IAAI,IAA2D;CAC7E,MAAM,uBAAO,IAAI,IAA6C;CAE9D,MAAM,WAAW,YAAgE;EAC/E,IAAI,QAAQ,UAAU,KAAA,GAAW,OAAO,KAAA;EAExC,MAAM,QAAQ,OAAO,IAAI,QAAQ,EAAE;EACnC,MAAM,SAAS,UAAU,KAAA,KAAa,MAAM,OAAO,SAAS,MAAM,WAAW,KAAA,IAAY,WAAW;EACpG,MAAM,QAAe;GAAE,MAAM,QAAQ,MAAM;GAAM,OAAO,OAAO,QAAQ,MAAM,KAAK;GAAG;EAAO;EAE5F,OAAO,OAAO,WAAW,KAAA,IAAY,QAAQ;GAAE,GAAG;GAAO,QAAQ,OAAO,MAAM,OAAO,KAAK;EAAE;CAC9F;CAEA,MAAM,UAAU,SAA0C,OAAiC;EACzF,MAAM,QAAQ,QAAQ,OAAO;EAE7B,OAAO,UAAU,KAAA,IACb,iBAAiB,KAAK;GAAE,IAAI,QAAQ;GAAI,MAAM,QAAQ;GAAM;EAAG,CAAC,IAChE,iBAAiB,KAAK;GAAE,IAAI,QAAQ;GAAI,MAAM,QAAQ;GAAM;GAAI;EAAM,CAAC;CAC7E;CAEA,MAAM,WAAW,OAAsC,OACrD,MAAM,MAAM,KAAK,CAAC,CAAC,KACjB,MAAM,eAAe;EACnB,cAAc,YAAY;GACxB,MAAM,IACJ,QAAQ,IACR,QAAQ,gBAAgB,KAAA,IACpB,YAAY,KAAK;IAAE,IAAI,QAAQ;IAAI,MAAM,QAAQ;IAAM;GAAG,CAAC,IAC3D,YAAY,KAAK;IACf,IAAI,QAAQ;IACZ,MAAM,QAAQ;IACd,aAAa,QAAQ;IACrB;GACF,CAAC,CACP;GACA,SAAS;GAET,OAAO,CAAC,QAAQ,EAAE;EACpB;EACA,mBAAmB,YAAY;GAC7B,KAAK,IAAI,QAAQ,IAAI,OAAO;GAC5B,MAAM,IAAI,QAAQ,IAAI,OAAO,SAAS,EAAE,CAAC;GACzC,SAAS;GAET,OAAO,CAAC,QAAQ,IAAI,GAAI,QAAQ,UAAU,CAAC,CAAE;EAC/C;EACA,cAAc,WAAW;GACvB,SAAS;GAET,OAAO,CAAC,OAAO,SAAS;EAC1B;EACA,eAAe,WAAW,OAAO;EACjC,kBAAkB;GAChB,SAAS;GAET,OAAO,CAAC;EACV;EACA,mBAAmB,CAAC;EACpB,mBAAmB,CAAC;EACpB,kBAAkB,CAAC;EACnB,mBAAmB,CAAC;EACpB,qBAAqB,CAAC;EACtB,yBAAyB,CAAC;EAC1B,0BAA0B,CAAC;EAC3B,mBAAmB;GACjB,UAAU;GAEV,OAAO,CAAC;EACV;CACF,CAAC,CACH;CAGF,MAAM,eAAe,UAAgE;EACnF,MAAM,SAAS,MAAM,IAAI,MAAM,MAAM,IAAI;EAEzC,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,KAAK;EAE7C,MAAM,QAAQ,OAAO,GAAG,MAAM,CAAC,CAAC,MAAM,KAAK,IAAI,OAAO,KAAK,MAAM,KAAK,IAAIC,WAAqB,QAAQ,MAAM,KAAK;EAElH,OAAO,OAAO,IAAI,QAAQ,aAAa;GACrC,MAAM,QAAQ;GACd,IAAI,aAAa,MAAM;GACvB,IAAI,MAAM;GACV,OAAO;EACT,EAAE;CACJ;CAEA,MAAM,QAAQ,UAA0D;EACtE,MAAM,SAAS;GAAE,UAAU,WAAW,QAAQ;GAAG;GAAQ;EAAQ;EACjE,MAAM,EAAE,UAAU;EAElB,MAAM,MAAM;EACZ,WAAWC,QAAwB,UAAU,KAAK;EAElD,IAAI,CAACC,eAA8B,KAAK,GAAG;GACzC,MAAM,MAAM,WAAW,QAAQ;GAE/B,OAAO,OAAO,MAAM,YAAY,KAAK,GAAG;IACtC,cACE,aAAa,OAAO,UAAU,GAAG,IAAI,KAAA,IAAY;KAAE;KAAK,OAAO,CAAC;KAAG,UAAU;KAAK;KAAQ;IAAQ;IACpG,SAAS,SAAS;KAChB,MAAM,IAAI,KAAK,IAAI,IAAI;KAEvB,OAAO;MAAE;MAAK,OAAO,CAAC,IAAI;MAAG,UAAU;MAAK;MAAQ;KAAQ;IAC9D;GACF,CAAC;EACH;EAEA,SAASC,QAAe,QAAQ,KAAK;EAErC,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,QAAQ,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO;GACrE,MAAM,UAAU,KAAK,IAAI,EAAE;GAC3B,MAAM,UAAU,MAAM,IAAI,EAAE;GAE5B,IAAI,YAAY,KAAA,KAAa,YAAY,KAAA,GAAW,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,CAAC,OAAO;GAEhG,MAAM,OAAO,OAAO,SAAS,QAAQ,EAAE;GACvC,MAAM,IAAI,IAAI,IAAI;GAElB,OAAO,CAAC,IAAI;EACd,CAAC;EAED,MAAM,MAAM,WAAW,QAAQ;EAE/B,OAAO,QAAQ,WAAW,KACxB,WAAW,OAAO,UAClB,YAAY,OAAO,WACnB,aAAa,OAAO,UAAU,GAAG,IAC/B,KAAA,IACA;GAAE;GAAK,OAAO;GAAS,UAAU;GAAK;GAAQ;EAAQ;CAC5D;CAEA,OAAO;EACL;EACA,IAAI,WAAW;GACb,OAAO;IAAE;IAAK,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;IAAG,UAAU,WAAW,QAAQ;IAAG;IAAQ;GAAQ;EAC5F;CACF;AACF;AAEA,MAAM,SAAS,YAA6D;CAC1E,MAAM,OAAO,QAAQ,GAAG,EAAE;CAE1B,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAE/B,MAAM,wBAAQ,IAAI,IAA2D;CAE7E,KAAK,MAAM,UAAU,SAAS,KAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI;CAEtF,OAAO;EACL,KAAK,KAAK;EACV,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;EACzB,UAAU,KAAK;EACf,QAAQ,KAAK;EACb,SAAS,KAAK;CAChB;AACF;AAqCA,SAAS,YACP,SACA,aACA,SACgC;CAChC,MAAM,QAAQ,SAAS,SAAS;CAEhC,OAAO,QAAQ,KACb,OAAO,oBACC,QAAQ,SAAS,QAAQ,CAAC,CAAC,IAChC,UAAU,UAAU;EACnB,MAAM,UAAU,MAAM,SAAS,UAAU;GACvC,MAAM,SAAS,SAAS,KAAK,KAAK;GAElC,OAAO,WAAW,KAAA,KAAa,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM;EAClE,CAAC;EAED,MAAM,SAAS,MAAM,OAAO;EAE5B,OAAO,CAAC,UAAU,WAAW,KAAA,IAAY,CAAC,IAAI,CAAC,MAAM,CAAC;CACxD,CACF,CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,UAUT,MAAM,SAAS,OAAO,SAAS,KAAK,EAAE,GAAG,WAAW"}
1
+ {"version":3,"file":"Timeline.js","names":["AssistantEvent.Attachment","AssistantEvent.ToolOutcome","AppEvents.tagOf","AssistantActivity.empty","AppEvents.decodeWith","AssistantActivity.apply","AssistantEvent.isLibraryEvent","BlockKit.apply","ToolApproval.apply"],"sources":["../src/Timeline.ts"],"sourcesContent":["/**\n * This module provides a chat's timeline: the chat as its user sees it. A chat client receives the\n * timeline and nothing else.\n *\n * A {@link Timeline} holds the chat's items: the messages with their blocks, the app's own events\n * it shows (each typed with its event, see {@link EventItem}), and the tool calls waiting for the\n * user's approval ({@link ApprovalItem}). It also holds the chat's\n * {@link Activity}, and whether the last turn failed or the chat was deleted. It never contains the\n * model's output, tool calls or results, what the model read, or why a turn failed. The server\n * streams {@link Change}s by piping the assistant's `subscribe` through {@link changes}, and the\n * client folds them with {@link fold} (or {@link apply}, from {@link empty}). On the server,\n * {@link fromEntries} builds a whole timeline from a chat's log, and {@link snapshot} the whole\n * timeline as one change.\n *\n * **Only the app events in `show` reach the timeline**, and none by default: an event that is only\n * for the assistant, like a summary of the chat, stays out. The RPC's schema, `Change([...])`,\n * names the same events and types the client's timeline with them.\n *\n * @since 0.3.0\n */\nimport { dual } from \"effect/Function\"\nimport * as Match from \"effect/Match\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaTransformation from \"effect/SchemaTransformation\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Assistant from \"./Assistant.ts\"\nimport * as AssistantActivity from \"./AssistantActivity.ts\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport * as BlockKit from \"./BlockKit.ts\"\nimport { InputId } from \"./InputId.ts\"\nimport * as AppEvents from \"./internal/events.ts\"\nimport * as ToolApproval from \"./ToolApproval.ts\"\nimport { toJson } from \"./internal/schema.ts\"\n\n/**\n * Schema for the status of a block. A block is `\"open\"` while it accepts an answer, `\"answered\"`\n * once the user has used it, and `\"closed\"` when it closed without an answer because the user\n * answered in words or moved on.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const BlockStatus = Schema.Literals([\"open\", \"answered\", \"closed\"])\n\n/**\n * The status of a block.\n *\n * @category models\n * @since 0.1.0\n */\nexport type BlockStatus = typeof BlockStatus.Type\n\n/**\n * Schema for a block on an assistant's message. It holds the block's kind, its props (JSON, as\n * the block's props schema encodes them), its status, and the user's answer (JSON, as the block's\n * answer schema encodes it).\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Block = Schema.Struct({\n kind: Schema.String,\n props: Schema.Json,\n status: BlockStatus,\n answer: Schema.optional(Schema.Json)\n})\n\n/**\n * Represents a block on an assistant's message.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Block = typeof Block.Type\n\n/**\n * Schema for a message the user sent. `at` is when it was stored, in epoch\n * milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const UserMessage = Schema.TaggedStruct(\"UserMessage\", {\n id: InputId,\n text: Schema.String,\n attachments: Schema.optional(Schema.Array(AssistantEvent.Attachment)),\n at: Schema.Finite\n})\n\n/**\n * Represents a message the user sent.\n *\n * @category models\n * @since 0.1.0\n */\nexport type UserMessage = typeof UserMessage.Type\n\n/**\n * Schema for a message the assistant sent the user, including its block if it has one.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const AssistantMessage = Schema.TaggedStruct(\"AssistantMessage\", {\n id: Schema.String,\n text: Schema.String,\n at: Schema.Finite,\n block: Schema.optional(Block)\n})\n\n/**\n * Represents a message the assistant sent the user.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AssistantMessage = typeof AssistantMessage.Type\n\n/**\n * Represents one of the app's own events in the timeline: an item between the messages, in the\n * order the chat stored it.\n *\n * **Details**\n *\n * The item's `_tag` is the event's, so a match over a timeline's items takes a handler per event\n * beside `UserMessage` and `AssistantMessage`. The event, decoded with its schema, is in `event`,\n * and the item's own fields sit beside it rather than spread into it: an event may have an `id` or\n * an `at` of its own, and a class instance keeps its prototype. `id` is fixed per entry of the\n * chat's log, and `at` is when the event was stored, in epoch milliseconds.\n *\n * @category models\n * @since 0.3.0\n */\nexport type EventItem<E> = E extends { readonly _tag: infer Tag extends string }\n ? { readonly _tag: Tag; readonly id: string; readonly at: number; readonly event: E }\n : never\n\n/**\n * Schema for a tool call that waits, or waited, for the user's approval, as an item on the\n * timeline.\n *\n * **Details**\n *\n * It is only the yes or no: the assistant's own message tells the user what it asks them to\n * approve, so the item carries no params. `id` is the call's id, which the decision is for (see\n * `ToolApproval.approve`), and `tool` the tool's name, for a label. `status` is where the call stands\n * (`ToolApproval.Status`); `reason` is the user's, when they rejected it with one; `outcome` is how\n * the approved call went, once it ran. `at` is when the call started waiting, in epoch\n * milliseconds. A change updates the item in place as the call is decided and run.\n *\n * @category schemas\n * @since 0.4.0\n */\nexport const ApprovalItem = Schema.TaggedStruct(\"ToolApproval\", {\n id: Schema.String,\n tool: Schema.String,\n status: Schema.Literals([\"pending\", \"approved\", \"rejected\", \"expired\"]),\n reason: Schema.optional(Schema.String),\n outcome: Schema.optional(AssistantEvent.ToolOutcome),\n at: Schema.Finite\n})\n\n/**\n * Represents a tool call that waits, or waited, for the user's approval, as an item on the\n * timeline.\n *\n * @category models\n * @since 0.4.0\n */\nexport type ApprovalItem = typeof ApprovalItem.Type\n\n/**\n * Represents an item in the chat's timeline: a message the user or the assistant sent, one of the\n * app's own events it shows (see {@link EventItem}), or a tool call waiting for the user's approval\n * (see {@link ApprovalItem}).\n *\n * **Example** (Showing each item of a chat)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n * import * as Match from \"effect/Match\"\n * import * as Schema from \"effect/Schema\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const line = (item: Timeline.Item<DailyBudgetReached>) =>\n * Match.valueTags(item, {\n * UserMessage: (sent) => `You: ${sent.text}`,\n * AssistantMessage: (sent) => `Assistant: ${sent.text}`,\n * DailyBudgetReached: () => \"You've reached today's budget.\"\n * })\n * ```\n *\n * @category models\n * @since 0.3.0\n */\nexport type Item<Events = never> = UserMessage | AssistantMessage | EventItem<Events> | ApprovalItem\n\n/**\n * Schema for a specialist that is working for the assistant. It holds the specialist's name and\n * when it started, in epoch milliseconds.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const SpecialistWorking = Schema.Struct({\n specialist: Schema.String,\n since: Schema.Finite\n})\n\n/**\n * Represents a specialist that is working for the assistant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SpecialistWorking = typeof SpecialistWorking.Type\n\n/**\n * Schema for what is working in the chat.\n *\n * `working` is true while the assistant runs a turn, or when a turn is about to start for\n * something that arrived. `specialists` lists the specialists that are working. When the chat is\n * idle, nothing is working, even if a workflow is waiting on a timer or on the user.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Activity = Schema.Struct({\n working: Schema.Boolean,\n specialists: Schema.Array(SpecialistWorking)\n})\n\n/**\n * Represents what is working in the chat.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Activity = typeof Activity.Type\n\n/**\n * Represents a chat's timeline: the chat as its user sees it, typed with the app's events it shows.\n * It holds the chat's items in order, what is working in it, whether the last turn failed, and\n * whether the chat was deleted. `seq` is the position in the chat's log that the timeline is up to.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface Timeline<Events = never> {\n readonly seq: number\n readonly items: ReadonlyArray<Item<Events>>\n readonly activity: Activity\n /** Whether the last turn failed with no message after it. */\n readonly failed: boolean\n /** Whether the chat was deleted. A deleted chat accepts nothing more, and its stream ends. */\n readonly deleted: boolean\n}\n\n/**\n * Represents a change to a chat's timeline, typed with the app's events it shows.\n *\n * **Details**\n *\n * A change holds each item that is new or changed (for example, a message whose block was\n * answered or closed, or a call the user approved), in full. It also holds the current activity, whether the last turn failed,\n * and whether the chat was deleted. `seq` is the position in the chat's log that the change is up\n * to.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface Change<Events = never> {\n readonly seq: number\n readonly items: ReadonlyArray<Item<Events>>\n readonly activity: Activity\n readonly failed: boolean\n readonly deleted: boolean\n}\n\nconst eventItem = (schema: AssistantEvent.AppEventSchema) =>\n Schema.Struct({ _tag: Schema.Literal(AppEvents.tagOf(schema)), id: Schema.String, at: Schema.Finite, event: schema })\n\nfunction makeChange<const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []>(\n events?: Events\n): Schema.Codec<Change<Events[number][\"Type\"]>, Schema.Json>\n// The overload states the decoded type: an item per given schema, each typed with its own event.\nfunction makeChange(events: ReadonlyArray<AssistantEvent.AppEventSchema> = []): Schema.Top {\n const AnyItem = Schema.toCodecJson(\n Schema.Union([UserMessage, AssistantMessage, ApprovalItem, ...events.map(eventItem)])\n )\n\n const decodeItem = Schema.decodeUnknownOption(AnyItem)\n const encodeItem = Schema.encodeUnknownSync(AnyItem)\n\n return Schema.Struct({\n seq: Schema.Int,\n // An item for an event this schema doesn't name, or that doesn't decode (from a newer server, say), is left out\n // rather than failing the whole change.\n items: Schema.Array(Schema.Json).pipe(\n Schema.decodeTo(\n Schema.Array(Schema.toType(AnyItem)),\n SchemaTransformation.transform<ReadonlyArray<typeof AnyItem.Type>, ReadonlyArray<Schema.Json>>({\n decode: (json) => json.flatMap((item) => Option.toArray(decodeItem(item))),\n encode: (items) => items.map((item) => encodeItem(item))\n })\n )\n ),\n activity: Activity,\n failed: Schema.Boolean,\n deleted: Schema.Boolean\n })\n}\n\n/**\n * Creates the schema for the changes to a chat's timeline, with items for the given app events,\n * in JSON.\n *\n * **Details**\n *\n * It is the success schema of the RPC that streams a chat's timeline: name the events the chat's\n * users see, the same ones the handler passes to {@link changes} as `show`. Tool calls waiting for\n * approval are always in it, as {@link ApprovalItem}s. Each item decodes its\n * event with that event's schema, so the client gets `Change<Events>` typed with them and folds it\n * with {@link fold}, no definition needed. `Change()` carries no app events.\n *\n * A handler whose changes show an event the schema doesn't name doesn't compile.\n *\n * **Example** (An RPC that streams a chat's timeline, with the budget notice)\n *\n * ```ts\n * import { ChatId, Timeline } from \"effect-assistant\"\n * import * as Rpc from \"effect/rpc/Rpc\"\n * import * as Schema from \"effect/Schema\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const Subscribe = Rpc.make(\"Subscribe\", {\n * payload: { chatId: ChatId.ChatId },\n * success: Timeline.Change([DailyBudgetReached]),\n * stream: true\n * })\n * ```\n *\n * @category schemas\n * @since 0.3.0\n */\nexport const Change: <const Events extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []>(\n events?: Events\n) => Schema.Codec<Change<Events[number][\"Type\"]>, Schema.Json> = makeChange\n\n/**\n * The timeline of a chat before anything has happened in it, with no items and nothing working.\n * Use it as the starting point when folding changes with {@link apply}.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const empty: Timeline = {\n seq: 0,\n items: [],\n activity: { working: false, specialists: [] },\n failed: false,\n deleted: false\n}\n\n/**\n * Returns the timeline after a change.\n *\n * **Details**\n *\n * Each item in the change replaces the item with the same id, in place, and new items are added\n * after the rest. The change's activity, `failed`, and `deleted` replace the timeline's. Applying a\n * change twice changes nothing, so a client that resubscribes from the start can fold again and get\n * the same timeline.\n *\n * The timeline is typed with the change's events, as its RPC's schema decoded them (see\n * {@link Change}). To fold a stream of changes, use {@link fold}: in\n * `Stream.scan(() => Timeline.empty, Timeline.apply)`, TypeScript fixes the state's type from\n * `empty` before it sees the changes' events, so that form needs the timeline's type written out.\n *\n * **Example** (Folding changes into your own state)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n *\n * interface State<Events> {\n * readonly timeline: Timeline.Timeline<Events>\n * readonly unread: number\n * }\n *\n * const next = <Events>(state: State<Events>, change: Timeline.Change<Events>): State<Events> => ({\n * timeline: Timeline.apply(state.timeline, change),\n * unread: state.unread + change.items.length\n * })\n * ```\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const apply = <Events>(timeline: Timeline<Events>, change: Change<Events>): Timeline<Events> => {\n const changed = new Map(change.items.map((item) => [item.id, item]))\n const known = new Set(timeline.items.map((item) => item.id))\n\n return {\n seq: Math.max(timeline.seq, change.seq),\n items: [\n ...timeline.items.map((item) => changed.get(item.id) ?? item),\n ...change.items.filter((item) => !known.has(item.id))\n ],\n activity: change.activity,\n failed: change.failed,\n deleted: change.deleted\n }\n}\n\n/**\n * Returns the timelines a stream of changes folds to, from {@link empty}, typed with the changes'\n * events.\n *\n * **Example** (A chat's timeline on the client)\n *\n * ```ts\n * import { Timeline } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n * import * as Stream from \"effect/Stream\"\n *\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * // The stream of an RPC whose success schema is `Timeline.Change([DailyBudgetReached])`.\n * declare const changes: Stream.Stream<Timeline.Change<DailyBudgetReached>>\n *\n * const timelines = changes.pipe(Timeline.fold) // Stream<Timeline.Timeline<DailyBudgetReached>>\n * ```\n *\n * @category combinators\n * @since 0.3.0\n */\nexport const fold = <Events, E, R>(\n changes: Stream.Stream<Change<Events>, E, R>\n): Stream.Stream<Timeline<Events>, E, R> => changes.pipe(Stream.scan((): Timeline<Events> => empty, apply))\n\n/**\n * Returns whether nothing is working in the chat: no turn is running or about to start, and no\n * specialist is working.\n *\n * @category guards\n * @since 0.1.0\n */\nexport const isIdle = <Events>(timeline: Timeline<Events>): boolean =>\n !timeline.activity.working && timeline.activity.specialists.length === 0\n\ninterface AnyEventItem {\n readonly _tag: string\n readonly id: string\n readonly at: number\n readonly event: { readonly _tag: string }\n}\n\ntype AnyItem = UserMessage | AssistantMessage | AnyEventItem | ApprovalItem\n\n// What the fold below builds: the overloads that use it type its items with the events shown.\ninterface AnyChange {\n readonly seq: number\n readonly items: ReadonlyArray<AnyItem>\n readonly activity: Activity\n readonly failed: boolean\n readonly deleted: boolean\n}\n\n/**\n * Represents the options for building a chat's timeline from its log.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface ShowOptions<Show extends ReadonlyArray<AssistantEvent.AppEventSchema>> {\n /**\n * The schemas of the app's events the chat's users see. Each becomes an item in the timeline;\n * the app's other events are left out. Defaults to none.\n */\n readonly show?: Show | undefined\n}\n\n/**\n * Represents the schemas an assistant's timeline may show: some of its own events.\n *\n * @category utility types\n * @since 0.3.0\n */\nexport type Shown<Events> = ReadonlyArray<AssistantEvent.AppEventSchema & { readonly Type: Events }>\n\nfunction snapshotWith(entries: ReadonlyArray<AssistantEvent.AnyEntry>): Change\nfunction snapshotWith<Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n): Change<Show[number][\"Type\"]>\nfunction snapshotWith(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n _definition?: Schemas,\n options?: ShowOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): AnyChange {\n const timeline = folding(options?.show ?? [])\n\n for (const entry of entries) timeline.step(entry)\n\n return timeline.snapshot\n}\n\n/**\n * Returns a chat's whole timeline as one change, built from the entries of its log.\n *\n * **When to use**\n *\n * Use it on the server for an RPC that returns a whole chat at once: its success schema is\n * {@link Change} with the same events as `show`, and the client applies it to {@link empty}.\n *\n * **Details**\n *\n * Only the app's events in `show` become items; without it, the timeline has none.\n *\n * @category constructors\n * @since 0.3.0\n */\nexport const snapshot: {\n (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Change\n <Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n ): Change<Show[number][\"Type\"]>\n} = snapshotWith\n\nfunction fromEntriesWith(entries: ReadonlyArray<AssistantEvent.AnyEntry>): Timeline\nfunction fromEntriesWith<Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n): Timeline<Show[number][\"Type\"]>\nfunction fromEntriesWith(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n _definition?: Schemas,\n options?: ShowOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): AnyChange {\n const timeline = folding(options?.show ?? [])\n\n for (const entry of entries) timeline.step(entry)\n\n return timeline.snapshot\n}\n\n/**\n * Returns a chat's timeline, built from the entries of its log.\n *\n * **When to use**\n *\n * Use it in server-side code that reads a whole chat. A client gets the timeline as a stream from\n * {@link changes} instead, and an RPC that returns a whole chat sends a {@link snapshot}.\n *\n * **Details**\n *\n * Only the app's events in `show` become items, typed with their schemas; without it, the timeline\n * has none.\n *\n * **Example** (Building a chat's timeline from its log)\n *\n * ```ts\n * import { type ChatId, ChatLog, Timeline } from \"effect-assistant\"\n * import * as Effect from \"effect/Effect\"\n *\n * const timelineOf = (chatId: ChatId.ChatId) =>\n * ChatLog.ChatLog.use((log) => log.read(chatId)).pipe(\n * Effect.map((entries) => Timeline.fromEntries(entries))\n * )\n * ```\n *\n * @see {@link changes} for a stream of changes instead of a whole timeline\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const fromEntries: {\n (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Timeline\n <Events, const Show extends Shown<Events> = readonly []>(\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n definition: Definition<Events>,\n options?: ShowOptions<Show>\n ): Timeline<Show[number][\"Type\"]>\n} = fromEntriesWith\n\ninterface Schemas {\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n}\n\ninterface Folding {\n readonly step: (entry: AssistantEvent.AnyEntry) => AnyChange | undefined\n readonly snapshot: AnyChange\n}\n\nconst activityOf = (activity: AssistantActivity.State): Activity => ({\n working: activity.turnSince !== undefined || activity.queued,\n specialists: [...activity.specialists.values()].map((run) => ({\n specialist: run.specialist,\n since: run.since\n }))\n})\n\nconst sameActivity = (a: Activity, b: Activity) =>\n a.working === b.working &&\n a.specialists.length === b.specialists.length &&\n a.specialists.every((run, i) => {\n const other = b.specialists[i]\n\n return other !== undefined && run.specialist === other.specialist && run.since === other.since\n })\n\nconst folding = (show: ReadonlyArray<AssistantEvent.AppEventSchema>): Folding => {\n const shown = new Map(show.map((schema) => [AppEvents.tagOf(schema), schema]))\n let approvals: ReadonlyMap<string, ToolApproval.State> = new Map()\n let seq = 0\n let failed = false\n let deleted = false\n let activity = AssistantActivity.empty\n let blocks: ReadonlyMap<string, BlockKit.State> = new Map()\n // The items in order, and the assistant's messages as stored: their blocks change as the chat goes on.\n const items = new Map<string, AnyItem>()\n const sent = new Map<string, AssistantEvent.AssistantMessage>()\n\n const blockOf = (message: AssistantEvent.AssistantMessage): Block | undefined => {\n if (message.block === undefined) return undefined\n\n const state = blocks.get(message.id)\n const status = state === undefined || state.open ? \"open\" : state.answer === undefined ? \"closed\" : \"answered\"\n const block: Block = { kind: message.block.kind, props: toJson(message.block.props), status }\n\n return state?.answer === undefined ? block : { ...block, answer: toJson(state.answer.value) }\n }\n\n const itemOf = (message: AssistantEvent.AssistantMessage, at: number): AssistantMessage => {\n const block = blockOf(message)\n\n return block === undefined\n ? AssistantMessage.make({ id: message.id, text: message.text, at })\n : AssistantMessage.make({ id: message.id, text: message.text, at, block })\n }\n\n const touched = (event: AssistantEvent.AssistantEvent, at: number): ReadonlyArray<string> =>\n Match.value(event).pipe(\n Match.tagsExhaustive({\n UserMessage: (message) => {\n items.set(\n message.id,\n message.attachments === undefined\n ? UserMessage.make({ id: message.id, text: message.text, at })\n : UserMessage.make({\n id: message.id,\n text: message.text,\n attachments: message.attachments,\n at\n })\n )\n failed = false\n\n return [message.id]\n },\n AssistantMessage: (message) => {\n sent.set(message.id, message)\n items.set(message.id, itemOf(message, at))\n failed = false\n\n return [message.id, ...(message.closes ?? [])]\n },\n BlockAnswer: (answer) => {\n failed = false\n\n return [answer.messageId]\n },\n BlocksClosed: (closed) => closed.messageIds,\n ApprovalRequested: () => [],\n ApprovalDecision: () => {\n failed = false\n\n return []\n },\n ApprovalsExpired: () => [],\n TurnFailed: () => {\n failed = true\n\n return []\n },\n ModelOutput: () => [],\n ToolStarted: () => [],\n ToolResult: () => [],\n TurnStarted: () => [],\n TurnCompleted: () => [],\n SpecialistStarted: () => [],\n SpecialistFinished: () => [],\n ChatDeleted: () => {\n deleted = true\n\n return []\n }\n })\n )\n\n // Decoded as `subscribe` streams it, or as the log stores it; one that doesn't decode is left out.\n const eventItemOf = (entry: AssistantEvent.AnyEntry): Option.Option<AnyEventItem> => {\n const schema = shown.get(entry.event._tag)\n\n if (schema === undefined) return Option.none()\n\n const event = Schema.is(schema)(entry.event) ? Option.some(entry.event) : AppEvents.decodeWith(schema, entry.event)\n\n return Option.map(event, (decoded) => ({\n _tag: decoded._tag,\n id: `app-event:${entry.seq}`,\n at: entry.at,\n event: decoded\n }))\n }\n\n const callsOf = (event: AssistantEvent.AssistantEvent): ReadonlyArray<string> =>\n Predicate.isTagged(event, \"ApprovalsExpired\")\n ? event.callIds\n : Predicate.isTagged(event, \"ApprovalRequested\") ||\n Predicate.isTagged(event, \"ApprovalDecision\") ||\n Predicate.isTagged(event, \"ToolResult\")\n ? [event.callId]\n : []\n\n // The items of the calls this entry changed.\n const approvalItems = (event: AssistantEvent.AssistantEvent, before: ReadonlyMap<string, ToolApproval.State>) =>\n callsOf(event).flatMap((callId) => {\n const state = approvals.get(callId)\n\n if (state === undefined || state === before.get(callId)) return []\n\n const item = ApprovalItem.make({\n id: callId,\n tool: state.request.tool,\n status: state.status,\n reason: state.reason,\n outcome: state.outcome,\n at: state.at\n })\n\n items.set(callId, item)\n\n return [item]\n })\n\n const step = (entry: AssistantEvent.AnyEntry): AnyChange | undefined => {\n const previous = { activity: activityOf(activity), failed, deleted }\n const { event } = entry\n\n seq = entry.seq\n activity = AssistantActivity.apply(activity, entry)\n\n if (!AssistantEvent.isLibraryEvent(event)) {\n const now = activityOf(activity)\n\n return Option.match(eventItemOf(entry), {\n onNone: () =>\n sameActivity(previous.activity, now) ? undefined : { seq, items: [], activity: now, failed, deleted },\n onSome: (item) => {\n items.set(item.id, item)\n\n return { seq, items: [item], activity: now, failed, deleted }\n }\n })\n }\n\n blocks = BlockKit.apply(blocks, event)\n\n const decidedBefore = approvals\n approvals = ToolApproval.apply(approvals, entry)\n\n const changed = [...new Set(touched(event, entry.at))].flatMap((id) => {\n const message = sent.get(id)\n const current = items.get(id)\n\n if (message === undefined || current === undefined) return current === undefined ? [] : [current]\n\n const next = itemOf(message, current.at)\n items.set(id, next)\n\n return [next]\n })\n\n const all = [...changed, ...approvalItems(event, decidedBefore)]\n const now = activityOf(activity)\n\n return all.length === 0 &&\n failed === previous.failed &&\n deleted === previous.deleted &&\n sameActivity(previous.activity, now)\n ? undefined\n : { seq, items: all, activity: now, failed, deleted }\n }\n\n return {\n step,\n get snapshot() {\n return { seq, items: [...items.values()], activity: activityOf(activity), failed, deleted }\n }\n }\n}\n\nconst merge = (changes: ReadonlyArray<AnyChange>): AnyChange | undefined => {\n const last = changes.at(-1)\n\n if (last === undefined) return undefined\n\n const items = new Map<string, AnyItem>()\n\n for (const change of changes) for (const item of change.items) items.set(item.id, item)\n\n return {\n seq: last.seq,\n items: [...items.values()],\n activity: last.activity,\n failed: last.failed,\n deleted: last.deleted\n }\n}\n\n/**\n * Represents an assistant's definition as the timeline reads it. An assistant made with\n * `Assistant.make` fits it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Definition<Events = never> {\n /** The schemas of the assistant's events. */\n readonly events: ReadonlyArray<AssistantEvent.AppEventSchema>\n /** The schema of the chat's entries, typed with the assistant's events: it types `show`. */\n readonly Entry: Schema.Codec<Assistant.Entry<Events>, Schema.Json>\n}\n\n/**\n * Represents the options accepted by {@link changes}.\n *\n * @category models\n * @since 0.3.0\n */\nexport interface ChangesOptions<\n Show extends ReadonlyArray<AssistantEvent.AppEventSchema> = readonly []\n> extends ShowOptions<Show> {\n /**\n * The `seq` up to which the client already has the timeline. The result contains only the\n * changes after it. Defaults to `0`, every change.\n */\n readonly after?: number | undefined\n}\n\nfunction changesWith<E, R, Events, const Show extends Shown<Events> = readonly []>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n): Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\nfunction changesWith<E, R>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n _definition: Schemas,\n options?: ChangesOptions<ReadonlyArray<AssistantEvent.AppEventSchema>>\n): Stream.Stream<AnyChange, E, R> {\n const after = options?.after ?? 0\n\n return entries.pipe(\n Stream.mapAccumArray(\n () => folding(options?.show ?? []),\n (timeline, chunk) => {\n const changed = chunk.flatMap((entry) => {\n const change = timeline.step(entry)\n\n return change === undefined || entry.seq <= after ? [] : [change]\n })\n\n const merged = merge(changed)\n\n return [timeline, merged === undefined ? [] : [merged]]\n }\n )\n )\n}\n\n/**\n * Returns the stream of changes to a chat's timeline that its entries produce, for a chat client.\n *\n * **Details**\n *\n * The entries must stream from the start of the chat's log, as `assistant.subscribe(chatId)` does\n * without `after`. The fold reads every entry, because a message depends on earlier entries: its\n * block is answered or closed later. `after` only skips emitting the changes up to that `seq`, so a\n * client that has the timeline up to a `seq` receives only what changed since. Pass `after` here,\n * not to `subscribe`: a subscription that starts after a `seq` gives the fold an incomplete log,\n * and the changes it produces are wrong.\n *\n * Each chunk of entries becomes one change; the stored entries, for example, arrive as one chunk.\n *\n * **Only the app's events in `show` reach the client.** Each one becomes an item between the\n * messages (see {@link EventItem}); the app's other events, like a summary of the chat for the\n * model, are left out. Without `show`, none are shown. The changes are typed with the events shown,\n * so the RPC's success schema names the same ones (`Timeline.Change([...])`), and a handler that\n * shows one the schema doesn't name doesn't compile. A timeline shows a turn as working once it\n * starts, or from the user's input.\n *\n * **Example** (Streaming a chat's timeline to a client, with the one app event its user sees)\n *\n * ```ts\n * import { Assistant, type ChatId, Timeline } from \"effect-assistant\"\n * import * as Schema from \"effect/Schema\"\n * import * as Stream from \"effect/Stream\"\n *\n * class Summary extends Schema.TaggedClass<Summary>()(\"Summary\", { text: Schema.String, throughSeq: Schema.Int }) {}\n * class DailyBudgetReached extends Schema.TaggedClass<DailyBudgetReached>()(\"DailyBudgetReached\", {}) {}\n *\n * const MyAssistant = Assistant.make(\"my-assistant\", { events: [Summary, DailyBudgetReached] })\n *\n * // The summary is for the model: only the budget notice reaches the user.\n * const watch = (chatId: ChatId.ChatId, after: number) =>\n * Stream.unwrap(\n * MyAssistant.useSync((assistant) =>\n * assistant.subscribe(chatId).pipe(Timeline.changes(MyAssistant, { show: [DailyBudgetReached], after }))\n * )\n * )\n * ```\n *\n * @see {@link fold} for folding the changes into a timeline on the client\n *\n * @category utils\n * @since 0.3.0\n */\nexport const changes: {\n <Events, const Show extends Shown<Events> = readonly []>(\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n ): <E, R>(entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>) => Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\n <E, R, Events, const Show extends Shown<Events> = readonly []>(\n entries: Stream.Stream<AssistantEvent.AnyEntry, E, R>,\n definition: Definition<Events>,\n options?: ChangesOptions<Show>\n ): Stream.Stream<Change<Show[number][\"Type\"]>, E, R>\n} = dual((args) => Stream.isStream(args[0]), changesWith)\n"],"mappings":"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"}