effect-assistant 0.4.0 → 0.6.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 (102) hide show
  1. package/dist/Assistant.d.ts +10 -10
  2. package/dist/Assistant.d.ts.map +1 -1
  3. package/dist/Assistant.js +1 -1
  4. package/dist/Assistant.js.map +1 -1
  5. package/dist/AssistantActivity.d.ts +2 -2
  6. package/dist/AssistantActivity.d.ts.map +1 -1
  7. package/dist/AssistantActivity.js +5 -5
  8. package/dist/AssistantActivity.js.map +1 -1
  9. package/dist/AssistantEngine.d.ts +3 -3
  10. package/dist/AssistantEngine.js +2 -2
  11. package/dist/AssistantEngine.js.map +1 -1
  12. package/dist/AssistantEvent.d.ts +19 -8
  13. package/dist/AssistantEvent.d.ts.map +1 -1
  14. package/dist/AssistantEvent.js +20 -9
  15. package/dist/AssistantEvent.js.map +1 -1
  16. package/dist/AssistantLoop.d.ts +2 -2
  17. package/dist/AssistantLoop.js.map +1 -1
  18. package/dist/Block.d.ts +63 -1
  19. package/dist/Block.d.ts.map +1 -1
  20. package/dist/Block.js +63 -2
  21. package/dist/Block.js.map +1 -1
  22. package/dist/BlockKit.d.ts +25 -3
  23. package/dist/BlockKit.d.ts.map +1 -1
  24. package/dist/BlockKit.js +14 -12
  25. package/dist/BlockKit.js.map +1 -1
  26. package/dist/ChatLog.d.ts +9 -9
  27. package/dist/ChatLog.d.ts.map +1 -1
  28. package/dist/ChatLog.js +21 -21
  29. package/dist/ChatLog.js.map +1 -1
  30. package/dist/Compaction.d.ts +10 -10
  31. package/dist/Compaction.d.ts.map +1 -1
  32. package/dist/Compaction.js +19 -19
  33. package/dist/Compaction.js.map +1 -1
  34. package/dist/Memory.d.ts +1 -1
  35. package/dist/Memory.js +1 -1
  36. package/dist/Memory.js.map +1 -1
  37. package/dist/Skill.d.ts +1 -1
  38. package/dist/Skill.js +1 -1
  39. package/dist/Skill.js.map +1 -1
  40. package/dist/Specialist.d.ts +1 -1
  41. package/dist/Specialist.js +1 -1
  42. package/dist/Specialist.js.map +1 -1
  43. package/dist/Timeline.d.ts +13 -9
  44. package/dist/Timeline.d.ts.map +1 -1
  45. package/dist/Timeline.js +35 -23
  46. package/dist/Timeline.js.map +1 -1
  47. package/dist/ToolApproval.d.ts +1 -1
  48. package/dist/ToolApproval.js +1 -1
  49. package/dist/ToolApproval.js.map +1 -1
  50. package/dist/ToolDiscovery.d.ts +1 -1
  51. package/dist/ToolDiscovery.js +1 -1
  52. package/dist/ToolDiscovery.js.map +1 -1
  53. package/dist/ToolResult.d.ts +56 -0
  54. package/dist/ToolResult.d.ts.map +1 -0
  55. package/dist/ToolResult.js +49 -0
  56. package/dist/ToolResult.js.map +1 -0
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +4 -3
  59. package/dist/internal/chat.js +4 -4
  60. package/dist/internal/chat.js.map +1 -1
  61. package/dist/internal/events.js +1 -1
  62. package/dist/internal/events.js.map +1 -1
  63. package/dist/internal/history.js +23 -23
  64. package/dist/internal/history.js.map +1 -1
  65. package/dist/internal/loop.d.ts +1 -1
  66. package/dist/internal/loop.d.ts.map +1 -1
  67. package/dist/internal/loop.js +12 -6
  68. package/dist/internal/loop.js.map +1 -1
  69. package/dist/internal/toolkit.d.ts +7 -1
  70. package/dist/internal/toolkit.d.ts.map +1 -1
  71. package/dist/internal/toolkit.js +47 -4
  72. package/dist/internal/toolkit.js.map +1 -1
  73. package/dist/internal/tools.js +39 -6
  74. package/dist/internal/tools.js.map +1 -1
  75. package/dist/internal/turns.d.ts.map +1 -1
  76. package/dist/internal/turns.js +24 -21
  77. package/dist/internal/turns.js.map +1 -1
  78. package/package.json +1 -1
  79. package/src/Assistant.ts +11 -11
  80. package/src/AssistantActivity.ts +9 -7
  81. package/src/AssistantEngine.ts +3 -3
  82. package/src/AssistantEvent.ts +21 -10
  83. package/src/AssistantLoop.ts +3 -3
  84. package/src/Block.ts +82 -1
  85. package/src/BlockKit.ts +37 -4
  86. package/src/ChatLog.ts +31 -26
  87. package/src/Compaction.ts +21 -21
  88. package/src/Memory.ts +1 -1
  89. package/src/Skill.ts +1 -1
  90. package/src/Specialist.ts +1 -1
  91. package/src/Timeline.ts +38 -25
  92. package/src/ToolApproval.ts +1 -1
  93. package/src/ToolDiscovery.ts +1 -1
  94. package/src/ToolResult.ts +80 -0
  95. package/src/index.ts +5 -0
  96. package/src/internal/chat.ts +4 -4
  97. package/src/internal/events.ts +1 -1
  98. package/src/internal/history.ts +32 -28
  99. package/src/internal/loop.ts +24 -7
  100. package/src/internal/toolkit.ts +55 -5
  101. package/src/internal/tools.ts +81 -5
  102. package/src/internal/turns.ts +35 -29
@@ -1 +1 @@
1
- {"version":3,"file":"chat.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.isInput","Chats.Chats","AppEvents.isRegistered","AppEvents.encode","AppEvents.decodeEntries","ToolApproval.fromEntries","AppEvents.markInput","History.needsTurn","Turns.run","Specialists.makeRunner","SpecialistService.Runner"],"sources":["../../src/internal/chat.ts"],"sourcesContent":["import * as Cause from \"effect/Cause\"\nimport * as Context from \"effect/Context\"\nimport * as Deferred from \"effect/Deferred\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport * as Fiber from \"effect/Fiber\"\nimport * as FiberSet from \"effect/FiberSet\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as PubSub from \"effect/PubSub\"\nimport * as Ref from \"effect/Ref\"\nimport * as Scope from \"effect/Scope\"\nimport * as Semaphore from \"effect/Semaphore\"\nimport * as Stream from \"effect/Stream\"\nimport * as SynchronizedRef from \"effect/SynchronizedRef\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type { ChatId } from \"../ChatId.ts\"\nimport { ChatLog } from \"../ChatLog.ts\"\nimport { CurrentChat } from \"../CurrentChat.ts\"\nimport * as Chats from \"../Chats.ts\"\nimport * as SpecialistService from \"../Specialist.ts\"\nimport * as ToolApproval from \"../ToolApproval.ts\"\nimport * as AppEvents from \"./events.ts\"\nimport * as History from \"./history.ts\"\nimport * as Specialists from \"./specialists.ts\"\nimport * as Turns from \"./turns.ts\"\nimport type { Writer } from \"./writer.ts\"\n\nconst idOf = (event: { readonly _tag: string }): string | undefined =>\n AssistantEvent.isLibraryEvent(event)\n ? AssistantEvent.isInput(event)\n ? event.id\n : undefined\n : Predicate.hasProperty(event, \"id\") && Predicate.isString(event.id)\n ? event.id\n : undefined\n\nconst makeChannelPubSub = Effect.acquireRelease(PubSub.unbounded<AssistantEvent.Entry>(), PubSub.shutdown)\n\nconst makeHolding = Effect.fnUntraced(function* (keepAlive: (enabled: boolean) => Effect.Effect<void>) {\n const holds = yield* SynchronizedRef.make(0)\n\n const acquire = SynchronizedRef.updateEffect(\n holds,\n Effect.fnUntraced(function* (n: number) {\n if (n !== 0) return n + 1\n\n yield* keepAlive(true)\n\n return 1\n })\n )\n\n const release = SynchronizedRef.updateEffect(\n holds,\n Effect.fnUntraced(function* (n: number) {\n if (n !== 1) return n - 1\n\n yield* keepAlive(false)\n\n return 0\n })\n )\n\n return (effect: Effect.Effect<void>) =>\n Effect.acquireUseRelease(\n acquire,\n () => effect,\n // An interrupted effect (a shutdown) keeps its hold, so the chat starts again and picks up where it was cut off.\n (_, exit) => (Exit.hasInterrupts(exit) ? Effect.void : release)\n )\n})\n\n/** @internal */\nexport interface Chat {\n readonly submit: (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) => Effect.Effect<void>\n readonly submitInBackground: (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) => Effect.Effect<void>\n // For inputs no turn has read, like a report stored during a shutdown.\n readonly resume: Effect.Effect<void>\n readonly stream: (after: number) => Stream.Stream<AssistantEvent.Entry>\n // Stops what works in the chat and ends its subscribers' streams; its log stays.\n readonly delete: Effect.Effect<void>\n}\n\nconst isChatDeleted = (entry: AssistantEvent.Entry) => Predicate.isTagged(entry.event, \"ChatDeleted\")\n\nconst deletedChat = (entries: ReadonlyArray<AssistantEvent.Entry>): Chat => ({\n submit: () => Effect.void,\n submitInBackground: () => Effect.void,\n resume: Effect.void,\n stream: (after) => Stream.fromIterable(entries.filter((entry) => entry.seq > after)),\n delete: Effect.void\n})\n\nconst refused = (input: { readonly _tag: string }, why: string) =>\n Effect.logDebug(`assistant chat ${why}: ${input._tag} not stored`)\n\n/** @internal */\nexport const make = Effect.fnUntraced(function* (options: {\n readonly registration: Turns.Registration\n readonly chatId: ChatId\n readonly keepAlive: (enabled: boolean) => Effect.Effect<void>\n}) {\n const { registration, chatId } = options\n const { app } = registration.definition\n const storage = yield* ChatLog\n const chats = yield* Chats.Chats\n const initial = yield* storage.read(chatId)\n\n if (initial.some(isChatDeleted)) return deletedChat(initial)\n\n const channel = yield* makeChannelPubSub\n const writes = yield* Semaphore.make(1)\n const turnsLock = yield* Semaphore.make(1)\n const holding = yield* makeHolding(options.keepAlive)\n const deleted = yield* Deferred.make<undefined>()\n // So deleting the chat stops all its work: its turns, runs, and the builds' resources.\n const working = yield* Scope.fork(yield* Effect.scope)\n const inWorking = Effect.provideService(Scope.Scope, working)\n const background = yield* inWorking(FiberSet.make())\n const requests = yield* inWorking(FiberSet.make())\n\n const isDeleted = Deferred.isDone(deleted)\n\n // The chat is its log's only writer, so it reads the log once and turns read memory.\n const entries = yield* Ref.make(initial)\n\n // Every writer goes through here, one at a time, so each append is numbered after the last. After a delete it\n // stores nothing.\n const write = Effect.fnUntraced(function* (\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) {\n const unknown = events.find((event) => !AssistantEvent.isLibraryEvent(event) && !AppEvents.isRegistered(app, event))\n\n if (unknown !== undefined) {\n return yield* Effect.die(`${unknown._tag} isn't one of the ${registration.definition.name} assistant's events.`)\n }\n\n if (yield* isDeleted) return []\n\n return yield* stored(events)\n })\n\n // Uninterruptible: a stored append must reach memory and the subscribers too, or a turn misses it and replies twice.\n const stored = Effect.fnUntraced(function* (\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) {\n const stored = yield* storage.append(chatId, events)\n\n yield* Ref.update(entries, (all) => [...all, ...stored])\n yield* PubSub.publishAll(channel, stored)\n\n return stored\n }, Effect.uninterruptible)\n\n const chat: Writer = {\n chatId,\n entries: Ref.get(entries),\n append: (events) => Semaphore.withPermit(writes)(write(events))\n }\n\n const toStored = (event: {\n readonly _tag: string\n }): Effect.Effect<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent> =>\n AssistantEvent.isLibraryEvent(event)\n ? Effect.succeed(event)\n : AppEvents.isRegistered(app, event)\n ? AppEvents.encode(app, event)\n : Effect.die(`${event._tag} isn't one of the ${registration.definition.name} assistant's events.`)\n\n // What handlers, tools, and runs see: the assistant's own events decoded, and encoded when appended.\n const handle: Turns.TypedChat = {\n chatId,\n entries: Effect.map(chat.entries, (all) => AppEvents.decodeEntries(app, all)),\n append: (...events) => Effect.asVoid(Effect.flatMap(Effect.forEach(events, toStored), chat.append))\n }\n\n // A chat the app deleted is deleted here too, which stops what works in it: a turn that checks stops there.\n const checkAvailable: Effect.Effect<boolean> = chats.get(chatId).pipe(\n Effect.as(true),\n Effect.catchTag(\"GetChatError\", (unavailable) =>\n unavailable.reason === \"Deleted\"\n ? Effect.as(\n Effect.suspend(() => remove),\n false\n )\n : Effect.succeed(false)\n )\n )\n\n const store = Effect.fnUntraced(function* (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) {\n if (yield* isDeleted) return yield* refused(input, \"is deleted\")\n\n if (!(yield* checkAvailable)) return yield* refused(input, \"is unavailable\")\n\n // Checked under the write lock: a retry that arrives while the first submit is storing is stored once.\n yield* Semaphore.withPermit(writes)(\n Effect.gen(function* () {\n if (yield* isDeleted) return yield* refused(input, \"is deleted\")\n\n const id = idOf(input)\n const all = yield* Ref.get(entries)\n\n if (id !== undefined && all.some(({ event }) => idOf(event) === id)) return\n\n // A decision counts only for a call that still waits: not one decided, or expired, or unknown.\n if (\n Predicate.isTagged(input, \"ApprovalDecision\") &&\n ToolApproval.fromEntries(all).get(input.callId)?.status !== \"pending\"\n ) {\n return yield* refused(input, \"has no call waiting for that decision\")\n }\n\n // Stored as an input: it came in from outside a turn, so it wakes one, also after a restart.\n yield* write([AssistantEvent.isLibraryEvent(input) ? input : AppEvents.markInput(input)])\n })\n )\n })\n\n // The handlers' builds run in the chat's working scope: what they set up lives until the chat closes or is deleted.\n const handler = yield* inWorking(Effect.provideService(registration.open(handle), CurrentChat, handle))\n\n const specialists = yield* Effect.forEach(\n registration.specialists,\n Effect.fnUntraced(function* ({ specialist, built }) {\n const handler = yield* inWorking(Effect.provideService(built.open, CurrentChat, handle))\n\n return { specialist, handler }\n })\n )\n\n const inBackground = (effect: Effect.Effect<void>) => FiberSet.run(background, holding(effect)).pipe(Effect.asVoid)\n\n // A request's turns run in the working scope too, so a delete stops them; the request ends quietly then.\n const inRequest = Effect.fnUntraced(function* (effect: Effect.Effect<void>) {\n const fiber = yield* FiberSet.run(requests, effect)\n const exit = yield* Fiber.await(fiber).pipe(Effect.onInterrupt(() => Fiber.interrupt(fiber)))\n\n if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {\n return yield* Effect.failCause(exit.cause)\n }\n }, Effect.orDie)\n\n const drain: Effect.Effect<void> = Semaphore.withPermit(turnsLock)(\n Effect.gen(function* () {\n while (!(yield* isDeleted) && History.needsTurn(yield* chat.entries)) {\n yield* Turns.run({ registration, handler, chat, services, checkOpen: Effect.asVoid(checkAvailable) })\n }\n })\n )\n\n const runner = yield* inWorking(\n Specialists.makeRunner({\n chat,\n handle,\n specialists,\n model: registration.model,\n holding,\n report: Effect.fnUntraced(function* (finished) {\n yield* store(finished)\n yield* inBackground(drain)\n })\n })\n )\n\n const services = Context.make(CurrentChat, handle).pipe(\n Context.add(SpecialistService.Runner, runner),\n Context.merge(registration.model)\n )\n\n const remove = Effect.gen(function* () {\n // Under the write lock: an append in flight is stored before the `ChatDeleted`; none comes after.\n const first = yield* Semaphore.withPermit(writes)(\n Effect.gen(function* () {\n if (yield* isDeleted) return false\n\n yield* stored([AssistantEvent.ChatDeleted.make({})])\n\n return yield* Deferred.succeed(deleted, undefined)\n })\n ).pipe(Effect.uninterruptible)\n\n if (!first) return\n\n // Its own fiber: a tool that deletes its own chat is in the working scope, and closing it interrupts that tool.\n // Interrupted work keeps its hold (see `makeHolding`), so the chat lets go of it here.\n const closing = yield* Effect.forkDetach(\n Effect.gen(function* () {\n yield* Scope.close(working, Exit.void)\n yield* options.keepAlive(false)\n })\n )\n\n yield* Fiber.await(closing)\n })\n\n // A chat the app deleted while it was closed is deleted now, so its subscribers hear it.\n yield* checkAvailable\n\n const chatApi: Chat = {\n submit: Effect.fnUntraced(function* (input) {\n yield* store(input)\n yield* inRequest(drain)\n }),\n submitInBackground: Effect.fnUntraced(function* (input) {\n yield* store(input)\n yield* inBackground(drain)\n }),\n resume: Effect.gen(function* () {\n if (History.needsTurn(yield* chat.entries)) yield* inBackground(drain)\n }),\n stream: (after) =>\n Stream.unwrap(\n Effect.gen(function* () {\n // Subscribes before it reads, so nothing appended in between is missed.\n const subscription = yield* PubSub.subscribe(channel)\n const all = yield* chat.entries\n const stored = all.filter((entry) => entry.seq > after)\n const last = stored.at(-1)?.seq ?? after\n\n // Deleted: what the reader hasn't seen, then the end. Otherwise the stream ends with the `ChatDeleted`.\n if (all.some(isChatDeleted)) return Stream.fromIterable(stored)\n\n return Stream.concat(\n Stream.fromIterable(stored),\n Stream.fromSubscription(subscription).pipe(Stream.filter((entry) => entry.seq > last))\n ).pipe(Stream.takeUntil(isChatDeleted))\n })\n ),\n delete: remove\n }\n\n return chatApi\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.isInput","Chats.Chats","AppEvents.isRegistered","AppEvents.encode","AppEvents.decodeEntries","ToolApproval.fromEntries","AppEvents.markInput","History.needsTurn","Turns.run","Specialists.makeRunner","SpecialistService.Runner"],"sources":["../../src/internal/chat.ts"],"sourcesContent":["import * as Cause from \"effect/Cause\"\nimport * as Context from \"effect/Context\"\nimport * as Deferred from \"effect/Deferred\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport * as Fiber from \"effect/Fiber\"\nimport * as FiberSet from \"effect/FiberSet\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as PubSub from \"effect/PubSub\"\nimport * as Ref from \"effect/Ref\"\nimport * as Scope from \"effect/Scope\"\nimport * as Semaphore from \"effect/Semaphore\"\nimport * as Stream from \"effect/Stream\"\nimport * as SynchronizedRef from \"effect/SynchronizedRef\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type { ChatId } from \"../ChatId.ts\"\nimport { ChatLog } from \"../ChatLog.ts\"\nimport { CurrentChat } from \"../CurrentChat.ts\"\nimport * as Chats from \"../Chats.ts\"\nimport * as SpecialistService from \"../Specialist.ts\"\nimport * as ToolApproval from \"../ToolApproval.ts\"\nimport * as AppEvents from \"./events.ts\"\nimport * as History from \"./history.ts\"\nimport * as Specialists from \"./specialists.ts\"\nimport * as Turns from \"./turns.ts\"\nimport type { Writer } from \"./writer.ts\"\n\nconst idOf = (event: { readonly _tag: string }): string | undefined =>\n AssistantEvent.isLibraryEvent(event)\n ? AssistantEvent.isInput(event)\n ? event.id\n : undefined\n : Predicate.hasProperty(event, \"id\") && Predicate.isString(event.id)\n ? event.id\n : undefined\n\nconst makeChannelPubSub = Effect.acquireRelease(PubSub.unbounded<AssistantEvent.Entry>(), PubSub.shutdown)\n\nconst makeHolding = Effect.fnUntraced(function* (keepAlive: (enabled: boolean) => Effect.Effect<void>) {\n const holds = yield* SynchronizedRef.make(0)\n\n const acquire = SynchronizedRef.updateEffect(\n holds,\n Effect.fnUntraced(function* (n: number) {\n if (n !== 0) return n + 1\n\n yield* keepAlive(true)\n\n return 1\n })\n )\n\n const release = SynchronizedRef.updateEffect(\n holds,\n Effect.fnUntraced(function* (n: number) {\n if (n !== 1) return n - 1\n\n yield* keepAlive(false)\n\n return 0\n })\n )\n\n return (effect: Effect.Effect<void>) =>\n Effect.acquireUseRelease(\n acquire,\n () => effect,\n // An interrupted effect (a shutdown) keeps its hold, so the chat starts again and picks up where it was cut off.\n (_, exit) => (Exit.hasInterrupts(exit) ? Effect.void : release)\n )\n})\n\n/** @internal */\nexport interface Chat {\n readonly submit: (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) => Effect.Effect<void>\n readonly submitInBackground: (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) => Effect.Effect<void>\n // For inputs no turn has read, like a report stored during a shutdown.\n readonly resume: Effect.Effect<void>\n readonly stream: (after: number) => Stream.Stream<AssistantEvent.Entry>\n // Stops what works in the chat and ends its subscribers' streams; its log stays.\n readonly delete: Effect.Effect<void>\n}\n\nconst isChatDeleted = (entry: AssistantEvent.Entry) => Predicate.isTagged(entry.event, \"ChatDeleted\")\n\nconst deletedChat = (entries: ReadonlyArray<AssistantEvent.Entry>): Chat => ({\n submit: () => Effect.void,\n submitInBackground: () => Effect.void,\n resume: Effect.void,\n stream: (after) => Stream.fromIterable(entries.filter((entry) => entry.sequence > after)),\n delete: Effect.void\n})\n\nconst refused = (input: { readonly _tag: string }, why: string) =>\n Effect.logDebug(`assistant chat ${why}: ${input._tag} not stored`)\n\n/** @internal */\nexport const make = Effect.fnUntraced(function* (options: {\n readonly registration: Turns.Registration\n readonly chatId: ChatId\n readonly keepAlive: (enabled: boolean) => Effect.Effect<void>\n}) {\n const { registration, chatId } = options\n const { app } = registration.definition\n const storage = yield* ChatLog\n const chats = yield* Chats.Chats\n const initial = yield* storage.read(chatId)\n\n if (initial.some(isChatDeleted)) return deletedChat(initial)\n\n const channel = yield* makeChannelPubSub\n const writes = yield* Semaphore.make(1)\n const turnsLock = yield* Semaphore.make(1)\n const holding = yield* makeHolding(options.keepAlive)\n const deleted = yield* Deferred.make<undefined>()\n // So deleting the chat stops all its work: its turns, runs, and the builds' resources.\n const working = yield* Scope.fork(yield* Effect.scope)\n const inWorking = Effect.provideService(Scope.Scope, working)\n const background = yield* inWorking(FiberSet.make())\n const requests = yield* inWorking(FiberSet.make())\n\n const isDeleted = Deferred.isDone(deleted)\n\n // The chat is its log's only writer, so it reads the log once and turns read memory.\n const entries = yield* Ref.make(initial)\n\n // Every writer goes through here, one at a time, so each append is numbered after the last. After a delete it\n // stores nothing.\n const write = Effect.fnUntraced(function* (\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) {\n const unknown = events.find((event) => !AssistantEvent.isLibraryEvent(event) && !AppEvents.isRegistered(app, event))\n\n if (unknown !== undefined) {\n return yield* Effect.die(`${unknown._tag} isn't one of the ${registration.definition.name} assistant's events.`)\n }\n\n if (yield* isDeleted) return []\n\n return yield* stored(events)\n })\n\n // Uninterruptible: a stored append must reach memory and the subscribers too, or a turn misses it and replies twice.\n const stored = Effect.fnUntraced(function* (\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) {\n const stored = yield* storage.append(chatId, events)\n\n yield* Ref.update(entries, (all) => [...all, ...stored])\n yield* PubSub.publishAll(channel, stored)\n\n return stored\n }, Effect.uninterruptible)\n\n const chat: Writer = {\n chatId,\n entries: Ref.get(entries),\n append: (events) => Semaphore.withPermit(writes)(write(events))\n }\n\n const toStored = (event: {\n readonly _tag: string\n }): Effect.Effect<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent> =>\n AssistantEvent.isLibraryEvent(event)\n ? Effect.succeed(event)\n : AppEvents.isRegistered(app, event)\n ? AppEvents.encode(app, event)\n : Effect.die(`${event._tag} isn't one of the ${registration.definition.name} assistant's events.`)\n\n // What handlers, tools, and runs see: the assistant's own events decoded, and encoded when appended.\n const handle: Turns.TypedChat = {\n chatId,\n entries: Effect.map(chat.entries, (all) => AppEvents.decodeEntries(app, all)),\n append: (...events) => Effect.asVoid(Effect.flatMap(Effect.forEach(events, toStored), chat.append))\n }\n\n // A chat the app deleted is deleted here too, which stops what works in it: a turn that checks stops there.\n const checkAvailable: Effect.Effect<boolean> = chats.get(chatId).pipe(\n Effect.as(true),\n Effect.catchTag(\"GetChatError\", (unavailable) =>\n unavailable.reason === \"Deleted\"\n ? Effect.as(\n Effect.suspend(() => remove),\n false\n )\n : Effect.succeed(false)\n )\n )\n\n const store = Effect.fnUntraced(function* (input: AssistantEvent.Input | AssistantEvent.StoredAppEvent) {\n if (yield* isDeleted) return yield* refused(input, \"is deleted\")\n\n if (!(yield* checkAvailable)) return yield* refused(input, \"is unavailable\")\n\n // Checked under the write lock: a retry that arrives while the first submit is storing is stored once.\n yield* Semaphore.withPermit(writes)(\n Effect.gen(function* () {\n if (yield* isDeleted) return yield* refused(input, \"is deleted\")\n\n const id = idOf(input)\n const all = yield* Ref.get(entries)\n\n if (id !== undefined && all.some(({ event }) => idOf(event) === id)) return\n\n // A decision counts only for a call that still waits: not one decided, or expired, or unknown.\n if (\n Predicate.isTagged(input, \"ApprovalDecision\") &&\n ToolApproval.fromEntries(all).get(input.callId)?.status !== \"pending\"\n ) {\n return yield* refused(input, \"has no call waiting for that decision\")\n }\n\n // Stored as an input: it came in from outside a turn, so it wakes one, also after a restart.\n yield* write([AssistantEvent.isLibraryEvent(input) ? input : AppEvents.markInput(input)])\n })\n )\n })\n\n // The handlers' builds run in the chat's working scope: what they set up lives until the chat closes or is deleted.\n const handler = yield* inWorking(Effect.provideService(registration.open(handle), CurrentChat, handle))\n\n const specialists = yield* Effect.forEach(\n registration.specialists,\n Effect.fnUntraced(function* ({ specialist, built }) {\n const handler = yield* inWorking(Effect.provideService(built.open, CurrentChat, handle))\n\n return { specialist, handler }\n })\n )\n\n const inBackground = (effect: Effect.Effect<void>) => FiberSet.run(background, holding(effect)).pipe(Effect.asVoid)\n\n // A request's turns run in the working scope too, so a delete stops them; the request ends quietly then.\n const inRequest = Effect.fnUntraced(function* (effect: Effect.Effect<void>) {\n const fiber = yield* FiberSet.run(requests, effect)\n const exit = yield* Fiber.await(fiber).pipe(Effect.onInterrupt(() => Fiber.interrupt(fiber)))\n\n if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {\n return yield* Effect.failCause(exit.cause)\n }\n }, Effect.orDie)\n\n const drain: Effect.Effect<void> = Semaphore.withPermit(turnsLock)(\n Effect.gen(function* () {\n while (!(yield* isDeleted) && History.needsTurn(yield* chat.entries)) {\n yield* Turns.run({ registration, handler, chat, services, checkOpen: Effect.asVoid(checkAvailable) })\n }\n })\n )\n\n const runner = yield* inWorking(\n Specialists.makeRunner({\n chat,\n handle,\n specialists,\n model: registration.model,\n holding,\n report: Effect.fnUntraced(function* (finished) {\n yield* store(finished)\n yield* inBackground(drain)\n })\n })\n )\n\n const services = Context.make(CurrentChat, handle).pipe(\n Context.add(SpecialistService.Runner, runner),\n Context.merge(registration.model)\n )\n\n const remove = Effect.gen(function* () {\n // Under the write lock: an append in flight is stored before the `ChatDeleted`; none comes after.\n const first = yield* Semaphore.withPermit(writes)(\n Effect.gen(function* () {\n if (yield* isDeleted) return false\n\n yield* stored([AssistantEvent.ChatDeleted.make({})])\n\n return yield* Deferred.succeed(deleted, undefined)\n })\n ).pipe(Effect.uninterruptible)\n\n if (!first) return\n\n // Its own fiber: a tool that deletes its own chat is in the working scope, and closing it interrupts that tool.\n // Interrupted work keeps its hold (see `makeHolding`), so the chat lets go of it here.\n const closing = yield* Effect.forkDetach(\n Effect.gen(function* () {\n yield* Scope.close(working, Exit.void)\n yield* options.keepAlive(false)\n })\n )\n\n yield* Fiber.await(closing)\n })\n\n // A chat the app deleted while it was closed is deleted now, so its subscribers hear it.\n yield* checkAvailable\n\n const chatApi: Chat = {\n submit: Effect.fnUntraced(function* (input) {\n yield* store(input)\n yield* inRequest(drain)\n }),\n submitInBackground: Effect.fnUntraced(function* (input) {\n yield* store(input)\n yield* inBackground(drain)\n }),\n resume: Effect.gen(function* () {\n if (History.needsTurn(yield* chat.entries)) yield* inBackground(drain)\n }),\n stream: (after) =>\n Stream.unwrap(\n Effect.gen(function* () {\n // Subscribes before it reads, so nothing appended in between is missed.\n const subscription = yield* PubSub.subscribe(channel)\n const all = yield* chat.entries\n const stored = all.filter((entry) => entry.sequence > after)\n const last = stored.at(-1)?.sequence ?? after\n\n // Deleted: what the reader hasn't seen, then the end. Otherwise the stream ends with the `ChatDeleted`.\n if (all.some(isChatDeleted)) return Stream.fromIterable(stored)\n\n return Stream.concat(\n Stream.fromIterable(stored),\n Stream.fromSubscription(subscription).pipe(Stream.filter((entry) => entry.sequence > last))\n ).pipe(Stream.takeUntil(isChatDeleted))\n })\n ),\n delete: remove\n }\n\n return chatApi\n})\n"],"mappings":"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"}
@@ -57,7 +57,7 @@ const decodeEntry = (app, entry) => {
57
57
  const decodeEntries = (app, entries) => entries.flatMap((entry) => Option.toArray(decodeEntry(app, entry)));
58
58
  const toEntry = (entry) => ({
59
59
  chatId: entry.chatId,
60
- seq: entry.seq,
60
+ sequence: entry.sequence,
61
61
  at: entry.at
62
62
  });
63
63
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.StoredAppEvent"],"sources":["../../src/internal/events.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Record from \"effect/Record\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport type * as Assistant from \"../Assistant.ts\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport { inputKey } from \"./input.ts\"\n\n/** @internal */\nexport interface AppEvents {\n readonly byTag: ReadonlyMap<string, AssistantEvent.AppEventSchema>\n}\n\n/** @internal */\nexport const tagOf = (schema: AssistantEvent.AppEventSchema): string => {\n const ast = schema.fields._tag.ast\n\n if (SchemaAST.isLiteral(ast) && Predicate.isString(ast.literal)) return ast.literal\n\n throw new Error(\"An app event is a tagged struct or class.\")\n}\n\n/** @internal */\nexport const make = (events: ReadonlyArray<AssistantEvent.AppEventSchema>): AppEvents => {\n const byTag = new Map(events.map((schema): [string, AssistantEvent.AppEventSchema] => [tagOf(schema), schema]))\n\n for (const tag of byTag.keys()) {\n if (AssistantEvent.isLibraryEvent({ _tag: tag })) throw new Error(`${tag} is one of the library's events.`)\n }\n\n if (byTag.size !== events.length) throw new Error(\"An assistant's events have different tags.\")\n\n return { byTag }\n}\n\n/** @internal */\nexport const encode = Effect.fnUntraced(function* (app: AppEvents, event: { readonly _tag: string }) {\n const schema = app.byTag.get(event._tag)\n\n // The types only let registered events through, so one with no schema here is a defect.\n if (schema === undefined) return yield* Effect.die(`${event._tag} isn't one of this assistant's events.`)\n\n const json = yield* Schema.encodeUnknownEffect(Schema.toCodecJson(schema))(event)\n\n return yield* Schema.decodeUnknownEffect(AssistantEvent.StoredAppEvent)(json)\n}, Effect.orDie)\n\n/** @internal */\nexport const isRegistered = (app: AppEvents, event: { readonly _tag: string }): boolean => app.byTag.has(event._tag)\n\n// The app's schema decodes the event as the app wrote it.\nconst unmarked = (event: { readonly _tag: string }) =>\n Predicate.hasProperty(event, inputKey) ? Record.remove(event, inputKey) : event\n\n// An event as the log stores it, decoded with its schema.\n/** @internal */\nexport const decodeWith = (\n schema: AssistantEvent.AppEventSchema,\n event: { readonly _tag: string }\n): Option.Option<{ readonly _tag: string }> => Schema.decodeUnknownOption(Schema.toCodecJson(schema))(unmarked(event))\n\nconst decodeEvent = (app: AppEvents, event: { readonly _tag: string }): Option.Option<{ readonly _tag: string }> => {\n const schema = app.byTag.get(event._tag)\n\n return schema === undefined ? Option.none() : decodeWith(schema, event)\n}\n\n// What came in through `submit`, from outside a turn, is an input: it wakes a turn.\n/** @internal */\nexport const markInput = (event: AssistantEvent.StoredAppEvent): AssistantEvent.StoredAppEvent => ({\n ...event,\n [inputKey]: true\n})\n\n// An app event that doesn't decode (say, one from an older version of its schema) is left out.\n/** @internal */\nexport const decodeEntry = (app: AppEvents, entry: AssistantEvent.AnyEntry): Option.Option<Assistant.Entry<any>> => {\n const { event } = entry\n\n if (AssistantEvent.isLibraryEvent(event)) return Option.some({ ...toEntry(entry), event })\n\n return Option.map(decodeEvent(app, event), (decoded) => ({ ...toEntry(entry), event: decoded }))\n}\n\n/** @internal */\nexport const decodeEntries = (\n app: AppEvents,\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<Assistant.Entry<any>> => entries.flatMap((entry) => Option.toArray(decodeEntry(app, entry)))\n\n/** @internal */\nexport type DecodedEntries = ReturnType<typeof decodeEntries>\n\nconst toEntry = (entry: AssistantEvent.AnyEntry) => ({\n chatId: entry.chatId,\n seq: entry.seq,\n at: entry.at\n})\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,SAAS,WAAkD;CACtE,MAAM,MAAM,OAAO,OAAO,KAAK;CAE/B,IAAI,UAAU,UAAU,GAAG,KAAK,UAAU,SAAS,IAAI,OAAO,GAAG,OAAO,IAAI;CAE5E,MAAM,IAAI,MAAM,2CAA2C;AAC7D;;AAGA,MAAa,QAAQ,WAAoE;CACvF,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,WAAoD,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC;CAE9G,KAAK,MAAM,OAAO,MAAM,KAAK,GAC3B,IAAIA,eAA8B,EAAE,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,iCAAiC;CAG5G,IAAI,MAAM,SAAS,OAAO,QAAQ,MAAM,IAAI,MAAM,4CAA4C;CAE9F,OAAO,EAAE,MAAM;AACjB;;AAGA,MAAa,SAAS,OAAO,WAAW,WAAW,KAAgB,OAAkC;CACnG,MAAM,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI;CAGvC,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,OAAO,IAAI,GAAG,MAAM,KAAK,uCAAuC;CAExG,MAAM,OAAO,OAAO,OAAO,oBAAoB,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK;CAEhF,OAAO,OAAO,OAAO,oBAAoBC,cAA6B,CAAC,CAAC,IAAI;AAC9E,GAAG,OAAO,KAAK;;AAGf,MAAa,gBAAgB,KAAgB,UAA8C,IAAI,MAAM,IAAI,MAAM,IAAI;AAGnH,MAAM,YAAY,UAChB,UAAU,YAAY,OAAA,yBAAe,IAAI,OAAO,OAAO,OAAO,QAAQ,IAAI;;AAI5E,MAAa,cACX,QACA,UAC6C,OAAO,oBAAoB,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC;AAErH,MAAM,eAAe,KAAgB,UAA+E;CAClH,MAAM,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI;CAEvC,OAAO,WAAW,KAAA,IAAY,OAAO,KAAK,IAAI,WAAW,QAAQ,KAAK;AACxE;;AAIA,MAAa,aAAa,WAAyE;CACjG,GAAG;EACF,WAAW;AACd;;AAIA,MAAa,eAAe,KAAgB,UAAwE;CAClH,MAAM,EAAE,UAAU;CAElB,IAAID,eAA8B,KAAK,GAAG,OAAO,OAAO,KAAK;EAAE,GAAG,QAAQ,KAAK;EAAG;CAAM,CAAC;CAEzF,OAAO,OAAO,IAAI,YAAY,KAAK,KAAK,IAAI,aAAa;EAAE,GAAG,QAAQ,KAAK;EAAG,OAAO;CAAQ,EAAE;AACjG;;AAGA,MAAa,iBACX,KACA,YACwC,QAAQ,SAAS,UAAU,OAAO,QAAQ,YAAY,KAAK,KAAK,CAAC,CAAC;AAK5G,MAAM,WAAW,WAAoC;CACnD,QAAQ,MAAM;CACd,KAAK,MAAM;CACX,IAAI,MAAM;AACZ"}
1
+ {"version":3,"file":"events.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.StoredAppEvent"],"sources":["../../src/internal/events.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Record from \"effect/Record\"\nimport * as Schema from \"effect/Schema\"\nimport * as SchemaAST from \"effect/SchemaAST\"\nimport type * as Assistant from \"../Assistant.ts\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport { inputKey } from \"./input.ts\"\n\n/** @internal */\nexport interface AppEvents {\n readonly byTag: ReadonlyMap<string, AssistantEvent.AppEventSchema>\n}\n\n/** @internal */\nexport const tagOf = (schema: AssistantEvent.AppEventSchema): string => {\n const ast = schema.fields._tag.ast\n\n if (SchemaAST.isLiteral(ast) && Predicate.isString(ast.literal)) return ast.literal\n\n throw new Error(\"An app event is a tagged struct or class.\")\n}\n\n/** @internal */\nexport const make = (events: ReadonlyArray<AssistantEvent.AppEventSchema>): AppEvents => {\n const byTag = new Map(events.map((schema): [string, AssistantEvent.AppEventSchema] => [tagOf(schema), schema]))\n\n for (const tag of byTag.keys()) {\n if (AssistantEvent.isLibraryEvent({ _tag: tag })) throw new Error(`${tag} is one of the library's events.`)\n }\n\n if (byTag.size !== events.length) throw new Error(\"An assistant's events have different tags.\")\n\n return { byTag }\n}\n\n/** @internal */\nexport const encode = Effect.fnUntraced(function* (app: AppEvents, event: { readonly _tag: string }) {\n const schema = app.byTag.get(event._tag)\n\n // The types only let registered events through, so one with no schema here is a defect.\n if (schema === undefined) return yield* Effect.die(`${event._tag} isn't one of this assistant's events.`)\n\n const json = yield* Schema.encodeUnknownEffect(Schema.toCodecJson(schema))(event)\n\n return yield* Schema.decodeUnknownEffect(AssistantEvent.StoredAppEvent)(json)\n}, Effect.orDie)\n\n/** @internal */\nexport const isRegistered = (app: AppEvents, event: { readonly _tag: string }): boolean => app.byTag.has(event._tag)\n\n// The app's schema decodes the event as the app wrote it.\nconst unmarked = (event: { readonly _tag: string }) =>\n Predicate.hasProperty(event, inputKey) ? Record.remove(event, inputKey) : event\n\n// An event as the log stores it, decoded with its schema.\n/** @internal */\nexport const decodeWith = (\n schema: AssistantEvent.AppEventSchema,\n event: { readonly _tag: string }\n): Option.Option<{ readonly _tag: string }> => Schema.decodeUnknownOption(Schema.toCodecJson(schema))(unmarked(event))\n\nconst decodeEvent = (app: AppEvents, event: { readonly _tag: string }): Option.Option<{ readonly _tag: string }> => {\n const schema = app.byTag.get(event._tag)\n\n return schema === undefined ? Option.none() : decodeWith(schema, event)\n}\n\n// What came in through `submit`, from outside a turn, is an input: it wakes a turn.\n/** @internal */\nexport const markInput = (event: AssistantEvent.StoredAppEvent): AssistantEvent.StoredAppEvent => ({\n ...event,\n [inputKey]: true\n})\n\n// An app event that doesn't decode (say, one from an older version of its schema) is left out.\n/** @internal */\nexport const decodeEntry = (app: AppEvents, entry: AssistantEvent.AnyEntry): Option.Option<Assistant.Entry<any>> => {\n const { event } = entry\n\n if (AssistantEvent.isLibraryEvent(event)) return Option.some({ ...toEntry(entry), event })\n\n return Option.map(decodeEvent(app, event), (decoded) => ({ ...toEntry(entry), event: decoded }))\n}\n\n/** @internal */\nexport const decodeEntries = (\n app: AppEvents,\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<Assistant.Entry<any>> => entries.flatMap((entry) => Option.toArray(decodeEntry(app, entry)))\n\n/** @internal */\nexport type DecodedEntries = ReturnType<typeof decodeEntries>\n\nconst toEntry = (entry: AssistantEvent.AnyEntry) => ({\n chatId: entry.chatId,\n sequence: entry.sequence,\n at: entry.at\n})\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,SAAS,WAAkD;CACtE,MAAM,MAAM,OAAO,OAAO,KAAK;CAE/B,IAAI,UAAU,UAAU,GAAG,KAAK,UAAU,SAAS,IAAI,OAAO,GAAG,OAAO,IAAI;CAE5E,MAAM,IAAI,MAAM,2CAA2C;AAC7D;;AAGA,MAAa,QAAQ,WAAoE;CACvF,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,WAAoD,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC;CAE9G,KAAK,MAAM,OAAO,MAAM,KAAK,GAC3B,IAAIA,eAA8B,EAAE,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,GAAG,IAAI,iCAAiC;CAG5G,IAAI,MAAM,SAAS,OAAO,QAAQ,MAAM,IAAI,MAAM,4CAA4C;CAE9F,OAAO,EAAE,MAAM;AACjB;;AAGA,MAAa,SAAS,OAAO,WAAW,WAAW,KAAgB,OAAkC;CACnG,MAAM,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI;CAGvC,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,OAAO,IAAI,GAAG,MAAM,KAAK,uCAAuC;CAExG,MAAM,OAAO,OAAO,OAAO,oBAAoB,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK;CAEhF,OAAO,OAAO,OAAO,oBAAoBC,cAA6B,CAAC,CAAC,IAAI;AAC9E,GAAG,OAAO,KAAK;;AAGf,MAAa,gBAAgB,KAAgB,UAA8C,IAAI,MAAM,IAAI,MAAM,IAAI;AAGnH,MAAM,YAAY,UAChB,UAAU,YAAY,OAAA,yBAAe,IAAI,OAAO,OAAO,OAAO,QAAQ,IAAI;;AAI5E,MAAa,cACX,QACA,UAC6C,OAAO,oBAAoB,OAAO,YAAY,MAAM,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC;AAErH,MAAM,eAAe,KAAgB,UAA+E;CAClH,MAAM,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI;CAEvC,OAAO,WAAW,KAAA,IAAY,OAAO,KAAK,IAAI,WAAW,QAAQ,KAAK;AACxE;;AAIA,MAAa,aAAa,WAAyE;CACjG,GAAG;EACF,WAAW;AACd;;AAIA,MAAa,eAAe,KAAgB,UAAwE;CAClH,MAAM,EAAE,UAAU;CAElB,IAAID,eAA8B,KAAK,GAAG,OAAO,OAAO,KAAK;EAAE,GAAG,QAAQ,KAAK;EAAG;CAAM,CAAC;CAEzF,OAAO,OAAO,IAAI,YAAY,KAAK,KAAK,IAAI,aAAa;EAAE,GAAG,QAAQ,KAAK;EAAG,OAAO;CAAQ,EAAE;AACjG;;AAGA,MAAa,iBACX,KACA,YACwC,QAAQ,SAAS,UAAU,OAAO,QAAQ,YAAY,KAAK,KAAK,CAAC,CAAC;AAK5G,MAAM,WAAW,WAAoC;CACnD,QAAQ,MAAM;CACd,UAAU,MAAM;CAChB,IAAI,MAAM;AACZ"}
@@ -66,7 +66,7 @@ const awaitingMessage = (request) => Prompt.toolMessage({ content: [Prompt.toolR
66
66
  result: awaitingApprovalOutput,
67
67
  providerExecuted: false
68
68
  })] });
69
- const isEntry = (item) => Predicate.hasProperty(item, "seq");
69
+ const isEntry = (item) => Predicate.hasProperty(item, "sequence");
70
70
  /** @internal */
71
71
  const fromEntries = (items) => {
72
72
  const entries = items.filter(isEntry);
@@ -81,26 +81,26 @@ const fromEntries = (items) => {
81
81
  };
82
82
  const nextSeen = (from) => {
83
83
  const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, "ModelOutput"))?.event;
84
- return next !== void 0 && isLibraryEvent(next) && Predicate.isTagged(next, "ModelOutput") ? next.seenThroughSeq : void 0;
84
+ return next !== void 0 && isLibraryEvent(next) && Predicate.isTagged(next, "ModelOutput") ? next.seenThroughSequence : void 0;
85
85
  };
86
- const input = (index, seq, item) => {
86
+ const input = (index, sequence, item) => {
87
87
  const seen = nextSeen(index);
88
- if (open.size > 0 || seen !== void 0 && seen < seq) held.push(item);
88
+ if (open.size > 0 || seen !== void 0 && seen < sequence) held.push(item);
89
89
  else history.push(item);
90
90
  };
91
91
  let index = -1;
92
92
  for (const item of items) {
93
93
  if (!isEntry(item)) {
94
- input(index, (entries[index]?.seq ?? 0) + .5, item);
94
+ input(index, (entries[index]?.sequence ?? 0) + .5, item);
95
95
  continue;
96
96
  }
97
97
  index += 1;
98
- const { event, seq } = item;
98
+ const { event, sequence } = item;
99
99
  const at = index;
100
100
  if (!isLibraryEvent(event)) continue;
101
101
  Match.value(event).pipe(Match.tagsExhaustive({
102
- UserMessage: (message) => input(at, seq, userText(textWithAttachments(message.text, message.attachments))),
103
- BlockAnswer: (answer) => input(at, seq, userText(answerText(answer))),
102
+ UserMessage: (message) => input(at, sequence, userText(textWithAttachments(message.text, message.attachments))),
103
+ BlockAnswer: (answer) => input(at, sequence, userText(answerText(answer))),
104
104
  ModelOutput: (output) => {
105
105
  history.push(...Prompt.fromResponseParts(output.parts).content);
106
106
  for (const part of output.parts) if (part.type === "tool-call") {
@@ -110,8 +110,8 @@ const fromEntries = (items) => {
110
110
  flushIfClosed();
111
111
  },
112
112
  ToolResult: (result) => {
113
- if (requests.has(result.callId)) return input(at, seq, userText(approvedText(result)));
114
- if (!called.has(result.callId)) return input(at, seq, userText(resultText(result)));
113
+ if (requests.has(result.callId)) return input(at, sequence, userText(approvedText(result)));
114
+ if (!called.has(result.callId)) return input(at, sequence, userText(resultText(result)));
115
115
  history.push(toolMessage(result));
116
116
  open.delete(result.callId);
117
117
  flushIfClosed();
@@ -121,18 +121,18 @@ const fromEntries = (items) => {
121
121
  TurnStarted: () => void 0,
122
122
  TurnCompleted: () => void 0,
123
123
  TurnFailed: () => void 0,
124
- SpecialistFinished: (finished) => input(at, seq, userText(finishedText(entries, at, finished))),
124
+ SpecialistFinished: (finished) => input(at, sequence, userText(finishedText(entries, at, finished))),
125
125
  SpecialistStarted: () => void 0,
126
126
  BlocksClosed: () => void 0,
127
127
  ApprovalRequested: (request) => {
128
128
  requests.set(request.callId, request);
129
- if (!called.has(request.callId)) return input(at, seq, userText(awaitingText(request)));
129
+ if (!called.has(request.callId)) return input(at, sequence, userText(awaitingText(request)));
130
130
  history.push(awaitingMessage(request));
131
131
  open.delete(request.callId);
132
132
  flushIfClosed();
133
133
  },
134
- ApprovalDecision: (decision) => decision.decision === "rejected" ? input(at, seq, userText(rejectedText(decision, toolOf(decision.callId)))) : void 0,
135
- ApprovalsExpired: (expired) => expired.callIds.forEach((callId) => input(at, seq, userText(expiredText(callId, toolOf(callId))))),
134
+ ApprovalDecision: (decision) => decision.decision === "rejected" ? input(at, sequence, userText(rejectedText(decision, toolOf(decision.callId)))) : void 0,
135
+ ApprovalsExpired: (expired) => expired.callIds.forEach((callId) => input(at, sequence, userText(expiredText(callId, toolOf(callId))))),
136
136
  ChatDeleted: () => void 0
137
137
  }));
138
138
  }
@@ -143,27 +143,27 @@ const isInput = (event) => isInput$1(event) || isAppInput(event);
143
143
  /** @internal */
144
144
  const handledThrough = (entries) => {
145
145
  const last = entries.findLast(({ event }) => isTurnEnd(event))?.event;
146
- return last !== void 0 && isTurnEnd(last) ? last.throughSeq : 0;
146
+ return last !== void 0 && isTurnEnd(last) ? last.throughSequence : 0;
147
147
  };
148
148
  /** @internal */
149
149
  const unhandledInputs = (entries) => {
150
150
  const seen = handledThrough(entries);
151
- return entries.flatMap(({ event, seq }) => seq > seen && isInput(event) && !(isInput$1(event) && !isWaking(event)) ? [event] : []);
151
+ return entries.flatMap(({ event, sequence }) => sequence > seen && isInput(event) && !(isInput$1(event) && !isWaking(event)) ? [event] : []);
152
152
  };
153
153
  /** @internal */
154
154
  const needsTurn = (entries) => unhandledInputs(entries).length > 0;
155
155
  /** @internal */
156
- const owesReply = (entries, throughSeq) => {
157
- const latest = entries.findLast(({ event, seq }) => seq <= throughSeq && isInput$1(event) && isUserInput(event));
158
- return latest !== void 0 && !entries.some(({ event, seq }) => seq > latest.seq && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed") || Predicate.isTagged(event, "ApprovalRequested")));
156
+ const owesReply = (entries, throughSequence) => {
157
+ const latest = entries.findLast(({ event, sequence }) => sequence <= throughSequence && isInput$1(event) && isUserInput(event));
158
+ return latest !== void 0 && !entries.some(({ event, sequence }) => sequence > latest.sequence && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed") || Predicate.isTagged(event, "ApprovalRequested")));
159
159
  };
160
160
  const sentToWaitOn = Schema.decodeUnknownOption(Schema.Struct({ wait: Schema.Literal(false) }));
161
161
  const progressMessages = (entries) => new Set(entries.flatMap(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "ModelOutput") ? event.parts.flatMap((part) => part.type === "tool-call" && part.name === "send_user_message" && Option.isSome(sentToWaitOn(part.params)) ? [fromKey(`message:${part.id}`)] : []) : []));
162
162
  /** @internal */
163
- const owesDelivery = (entries, throughSeq) => {
164
- const latest = entries.findLast(({ event, seq }) => seq <= throughSeq && isInput$1(event) && isUserInput(event));
163
+ const owesDelivery = (entries, throughSequence) => {
164
+ const latest = entries.findLast(({ event, sequence }) => sequence <= throughSequence && isInput$1(event) && isUserInput(event));
165
165
  if (latest === void 0) return false;
166
- const after = entries.flatMap(({ seq, event }) => seq > latest.seq && isLibraryEvent(event) ? [event] : []);
166
+ const after = entries.flatMap(({ sequence, event }) => sequence > latest.sequence && isLibraryEvent(event) ? [event] : []);
167
167
  const messages = after.flatMap((event) => Predicate.isTagged(event, "AssistantMessage") ? [event] : []);
168
168
  const progress = progressMessages(entries);
169
169
  const started = after.flatMap((event) => Predicate.isTagged(event, "SpecialistStarted") ? [event.runId] : []);
@@ -171,7 +171,7 @@ const owesDelivery = (entries, throughSeq) => {
171
171
  return messages.length > 0 && messages.every((message) => message.block === void 0 && progress.has(message.id)) && !after.some((event) => Predicate.isTagged(event, "TurnFailed") || Predicate.isTagged(event, "ApprovalRequested")) && started.every((runId) => finished.has(runId));
172
172
  };
173
173
  /** @internal */
174
- const unreadAfter = (entries, throughSeq) => entries.some(({ event, seq }) => seq > throughSeq && isInput(event));
174
+ const unreadAfter = (entries, throughSequence) => entries.some(({ event, sequence }) => sequence > throughSequence && isInput(event));
175
175
  //#endregion
176
176
  export { awaitingApprovalOutput, fromEntries, handledThrough, isInput, mayHaveRunOutput, needsTurn, notRunOutput, owesDelivery, owesReply, toolMessage, unansweredCalls, unhandledInputs, unreadAfter, userText };
177
177
 
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.textWithAttachments","AssistantEvent.isInput","AssistantEvent.isAppInput","AssistantEvent.isTurnEnd","AssistantEvent.isWaking","AssistantEvent.isUserInput","Uuid.fromKey"],"sources":["../../src/internal/history.ts"],"sourcesContent":["import * as Prompt from \"effect/ai/Prompt\"\nimport * as Response from \"effect/ai/Response\"\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 AssistantEvent from \"../AssistantEvent.ts\"\nimport { toJson, toJsonText } from \"./schema.ts\"\nimport * as Uuid from \"./uuid.ts\"\n\n/** @internal */\nexport const userText = (text: string): Prompt.UserMessage =>\n Prompt.userMessage({ content: [Prompt.textPart({ text })] })\n\n/** @internal */\nexport const toolMessage = (result: AssistantEvent.ToolResult): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: result.callId,\n name: result.tool,\n isFailure: result.outcome === \"failure\",\n result: result.result,\n providerExecuted: false\n })\n ]\n })\n\n// A call with no result is one a crash cut off.\n/** @internal */\nexport const unansweredCalls = (history: ReadonlyArray<Prompt.Message>): ReadonlyArray<AssistantEvent.ToolCallPart> => {\n const answered = new Set(\n history.flatMap((message) =>\n message.role === \"tool\" ? message.content.flatMap((part) => (part.type === \"tool-result\" ? [part.id] : [])) : []\n )\n )\n\n return history.flatMap((message) =>\n message.role === \"assistant\"\n ? message.content.flatMap((part) =>\n part.type === \"tool-call\" && !part.providerExecuted && !answered.has(part.id)\n ? [\n Response.makePart(\"tool-call\", {\n id: part.id,\n name: part.name,\n params: toJson(part.params),\n providerExecuted: false,\n metadata: part.options\n })\n ]\n : []\n )\n : []\n )\n}\n\n/** @internal */\nexport const notRunOutput = \"Not run: the turn ended before this call ran.\"\n\n/** @internal */\nexport const mayHaveRunOutput =\n \"Stopped: the turn ended while this call ran. It may have done its work; check before doing it again.\"\n\nconst ago = (millis: number) => {\n const seconds = Math.max(0, Math.round(millis / 1000))\n\n return seconds < 90 ? `${seconds} seconds` : `${Math.round(seconds / 60)} minutes`\n}\n\nconst finishedText = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n index: number,\n finished: AssistantEvent.SpecialistFinished\n) => {\n const at = entries[index]!\n\n const isStart = ({ event }: AssistantEvent.AnyEntry) =>\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"SpecialistStarted\") &&\n event.runId === finished.runId\n\n const from = entries.slice(0, index).findLastIndex(isStart)\n const started = from === -1 ? undefined : entries[from]\n const since = started === undefined ? [] : entries.slice(from + 1, index)\n const wrote = since.some(({ event }) => Predicate.isTagged(event, \"UserMessage\"))\n\n const lines = [\n \"A report from a specialist you started. The user hasn't seen it.\",\n started === undefined\n ? `You started it for: ${finished.goal}`\n : `You started it ${ago(at.at - started.at)} ago for: ${finished.goal}`,\n wrote ? \"The user has written since you started it.\" : \"The user hasn't written since you started it.\",\n Match.value(finished.status).pipe(\n Match.when(\"answered\", () => `Report:\\n${finished.report ?? \"\"}`),\n Match.when(\"failed\", () => `It failed: ${finished.error ?? \"\"}`),\n Match.when(\"cut_off\", () => \"A restart cut it off before it finished. Spawn it again if it's still needed.\"),\n Match.when(\"stopped\", () => \"You stopped it.\"),\n Match.exhaustive\n )\n ]\n\n return `<specialist name=\"${finished.specialist}\" run=\"${finished.runId}\" status=\"${finished.status}\">\\n${lines.join(\n \"\\n\"\n )}\\n</specialist>`\n}\n\nconst answerText = (answer: AssistantEvent.BlockAnswer) =>\n `<answer message=\"${answer.messageId}\" kind=\"${answer.kind}\">\\nThe user answered with the block on your message ${answer.messageId}.\\n${toJsonText(answer.value)}\\n</answer>`\n\nconst resultText = (result: AssistantEvent.ToolResult) =>\n `<result tool=\"${result.tool}\" call=\"${result.callId}\" outcome=\"${result.outcome}\">\\nThe result of a call from earlier in this chat.\\n${toJsonText(result.result)}\\n</result>`\n\n/** @internal */\nexport const awaitingApprovalOutput =\n \"Waiting for the user's approval: they see this call and approve or reject it. Its outcome arrives later in this chat; don't call it again meanwhile.\"\n\nconst awaitingText = (request: AssistantEvent.ApprovalRequested) =>\n `<approval tool=\"${request.tool}\" call=\"${request.callId}\" status=\"pending\">\\nYour call to ${request.tool} from earlier in this chat waits for the user's approval.\\n${toJsonText(request.params)}\\n</approval>`\n\nconst approvedText = (result: AssistantEvent.ToolResult) =>\n `<result tool=\"${result.tool}\" call=\"${result.callId}\" outcome=\"${result.outcome}\">\\nThe user approved your call to ${result.tool} from earlier in this chat, and it ran.\\n${toJsonText(result.result)}\\n</result>`\n\nconst rejectedText = (decision: AssistantEvent.ApprovalDecision, tool: string) =>\n `<approval tool=\"${tool}\" call=\"${decision.callId}\" decision=\"rejected\">\\nThe user rejected your call to ${tool}; it didn't run.${decision.reason === undefined ? \"\" : `\\nTheir reason: ${decision.reason}`}\\n</approval>`\n\nconst expiredText = (callId: string, tool: string) =>\n `<approval tool=\"${tool}\" call=\"${callId}\" status=\"expired\">\\nThe user moved on without approving your call to ${tool}; it didn't run.\\n</approval>`\n\nconst awaitingMessage = (request: AssistantEvent.ApprovalRequested): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: request.callId,\n name: request.tool,\n isFailure: false,\n result: awaitingApprovalOutput,\n providerExecuted: false\n })\n ]\n })\n\nconst isEntry = (item: AssistantEvent.AnyEntry | Prompt.Message): item is AssistantEvent.AnyEntry =>\n Predicate.hasProperty(item, \"seq\")\n\n// In the order the model saw the entries, not the order they were stored. A message a history view put in stands\n// where it is, right after the entry before it, and waits like an input for a call's result or for the model's next\n// read; the app's own events reach the model only as such messages.\n/** @internal */\nexport const fromEntries = (\n items: ReadonlyArray<AssistantEvent.AnyEntry | Prompt.Message>\n): ReadonlyArray<Prompt.Message> => {\n const entries = items.filter(isEntry)\n const history: Array<Prompt.Message> = []\n const held: Array<Prompt.Message> = []\n const open = new Set<string>()\n const called = new Set<string>()\n // Calls that waited for approval: the placeholder answered them, so what came of them later is an input.\n const requests = new Map<string, AssistantEvent.ApprovalRequested>()\n const toolOf = (callId: string) => requests.get(callId)?.tool ?? \"a tool\"\n\n const flushIfClosed = () => {\n if (open.size === 0) history.push(...held.splice(0))\n }\n\n const nextSeen = (from: number) => {\n const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, \"ModelOutput\"))?.event\n\n return next !== undefined && AssistantEvent.isLibraryEvent(next) && Predicate.isTagged(next, \"ModelOutput\")\n ? next.seenThroughSeq\n : undefined\n }\n\n // `index` is the last entry at or before the item, and `seq` where the item stands.\n const input = (index: number, seq: number, item: Prompt.Message) => {\n const seen = nextSeen(index)\n\n if (open.size > 0 || (seen !== undefined && seen < seq)) held.push(item)\n else history.push(item)\n }\n\n let index = -1\n\n for (const item of items) {\n if (!isEntry(item)) {\n input(index, (entries[index]?.seq ?? 0) + 0.5, item)\n\n continue\n }\n\n index += 1\n\n const { event, seq } = item\n const at = index\n\n if (!AssistantEvent.isLibraryEvent(event)) continue\n\n Match.value(event).pipe(\n Match.tagsExhaustive({\n UserMessage: (message) =>\n input(at, seq, userText(AssistantEvent.textWithAttachments(message.text, message.attachments))),\n BlockAnswer: (answer) => input(at, seq, userText(answerText(answer))),\n ModelOutput: (output) => {\n history.push(...Prompt.fromResponseParts(output.parts).content)\n\n for (const part of output.parts) {\n if (part.type === \"tool-call\") {\n open.add(part.id)\n called.add(part.id)\n }\n }\n\n flushIfClosed()\n },\n ToolResult: (result) => {\n if (requests.has(result.callId)) return input(at, seq, userText(approvedText(result)))\n\n // A view that starts after the call (since a summary, say) keeps its result as text: a tool result needs its\n // call right before it.\n if (!called.has(result.callId)) return input(at, seq, userText(resultText(result)))\n\n history.push(toolMessage(result))\n open.delete(result.callId)\n flushIfClosed()\n },\n AssistantMessage: () => undefined,\n ToolStarted: () => undefined,\n TurnStarted: () => undefined,\n TurnCompleted: () => undefined,\n TurnFailed: () => undefined,\n SpecialistFinished: (finished) => input(at, seq, userText(finishedText(entries, at, finished))),\n // Every run starts in a call of the model's own (a spawn or a follow-up), whose result says so.\n SpecialistStarted: () => undefined,\n BlocksClosed: () => undefined,\n // The call's result, for the provider's pairing; what the user decides comes later.\n ApprovalRequested: (request) => {\n requests.set(request.callId, request)\n\n if (!called.has(request.callId)) return input(at, seq, userText(awaitingText(request)))\n\n history.push(awaitingMessage(request))\n open.delete(request.callId)\n flushIfClosed()\n },\n // An approved call's result says so.\n ApprovalDecision: (decision) =>\n decision.decision === \"rejected\"\n ? input(at, seq, userText(rejectedText(decision, toolOf(decision.callId))))\n : undefined,\n ApprovalsExpired: (expired) =>\n expired.callIds.forEach((callId) => input(at, seq, userText(expiredText(callId, toolOf(callId))))),\n ChatDeleted: () => undefined\n })\n )\n }\n\n return [...history, ...held]\n}\n\n/** @internal */\nexport const isInput = (event: { readonly _tag: string }): boolean =>\n AssistantEvent.isInput(event) || AssistantEvent.isAppInput(event)\n\n/** @internal */\nexport const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): number => {\n const last = entries.findLast(({ event }) => AssistantEvent.isTurnEnd(event))?.event\n\n return last !== undefined && AssistantEvent.isTurnEnd(last) ? last.throughSeq : 0\n}\n\n// A run the assistant stopped isn't an input here: it wakes nothing.\n/** @internal */\nexport const unhandledInputs = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<{ readonly _tag: string }> => {\n const seen = handledThrough(entries)\n\n return entries.flatMap(({ event, seq }) =>\n seq > seen && isInput(event) && !(AssistantEvent.isInput(event) && !AssistantEvent.isWaking(event)) ? [event] : []\n )\n}\n\n/** @internal */\nexport const needsTurn = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): boolean =>\n unhandledInputs(entries).length > 0\n\n// A failure the user was shown counts as a reply, and so does a call they're asked to approve.\n/** @internal */\nexport const owesReply = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {\n const latest = entries.findLast(\n ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n return (\n latest !== undefined &&\n !entries.some(\n ({ event, seq }) =>\n seq > latest.seq &&\n (Predicate.isTagged(event, \"AssistantMessage\") ||\n Predicate.isTagged(event, \"TurnFailed\") ||\n Predicate.isTagged(event, \"ApprovalRequested\"))\n )\n )\n}\n\nconst sentToWaitOn = Schema.decodeUnknownOption(Schema.Struct({ wait: Schema.Literal(false) }))\n\n// Messages sent with `wait: false` are progress while the model kept working, like \"On it\".\nconst progressMessages = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlySet<string> =>\n new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"ModelOutput\")\n ? event.parts.flatMap((part) =>\n part.type === \"tool-call\" && part.name === \"send_user_message\" && Option.isSome(sentToWaitOn(part.params))\n ? [Uuid.fromKey(`message:${part.id}`)]\n : []\n )\n : []\n )\n )\n\n// The model said it was on it and stopped without the result: only progress messages since the user's latest input.\n// A specialist still working doesn't count, since its report is what comes next.\n/** @internal */\nexport const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {\n const latest = entries.findLast(\n ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n if (latest === undefined) return false\n\n const after = entries.flatMap(({ seq, event }) =>\n seq > latest.seq && AssistantEvent.isLibraryEvent(event) ? [event] : []\n )\n\n const messages = after.flatMap((event) => (Predicate.isTagged(event, \"AssistantMessage\") ? [event] : []))\n const progress = progressMessages(entries)\n const started = after.flatMap((event) => (Predicate.isTagged(event, \"SpecialistStarted\") ? [event.runId] : []))\n\n const finished = new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"SpecialistFinished\") ? [event.runId] : []\n )\n )\n\n // A call waiting for the user's approval is what comes next, like a specialist's report.\n return (\n messages.length > 0 &&\n messages.every((message) => message.block === undefined && progress.has(message.id)) &&\n !after.some((event) => Predicate.isTagged(event, \"TurnFailed\") || Predicate.isTagged(event, \"ApprovalRequested\")) &&\n started.every((runId) => finished.has(runId))\n )\n}\n\n/** @internal */\nexport const unreadAfter = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean =>\n entries.some(({ event, seq }) => seq > throughSeq && isInput(event))\n"],"mappings":"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"}
1
+ {"version":3,"file":"history.js","names":["AssistantEvent.isLibraryEvent","AssistantEvent.textWithAttachments","AssistantEvent.isInput","AssistantEvent.isAppInput","AssistantEvent.isTurnEnd","AssistantEvent.isWaking","AssistantEvent.isUserInput","Uuid.fromKey"],"sources":["../../src/internal/history.ts"],"sourcesContent":["import * as Prompt from \"effect/ai/Prompt\"\nimport * as Response from \"effect/ai/Response\"\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 AssistantEvent from \"../AssistantEvent.ts\"\nimport { toJson, toJsonText } from \"./schema.ts\"\nimport * as Uuid from \"./uuid.ts\"\n\n/** @internal */\nexport const userText = (text: string): Prompt.UserMessage =>\n Prompt.userMessage({ content: [Prompt.textPart({ text })] })\n\n/** @internal */\nexport const toolMessage = (result: AssistantEvent.ToolResult): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: result.callId,\n name: result.tool,\n isFailure: result.outcome === \"failure\",\n result: result.result,\n providerExecuted: false\n })\n ]\n })\n\n// A call with no result is one a crash cut off.\n/** @internal */\nexport const unansweredCalls = (history: ReadonlyArray<Prompt.Message>): ReadonlyArray<AssistantEvent.ToolCallPart> => {\n const answered = new Set(\n history.flatMap((message) =>\n message.role === \"tool\" ? message.content.flatMap((part) => (part.type === \"tool-result\" ? [part.id] : [])) : []\n )\n )\n\n return history.flatMap((message) =>\n message.role === \"assistant\"\n ? message.content.flatMap((part) =>\n part.type === \"tool-call\" && !part.providerExecuted && !answered.has(part.id)\n ? [\n Response.makePart(\"tool-call\", {\n id: part.id,\n name: part.name,\n params: toJson(part.params),\n providerExecuted: false,\n metadata: part.options\n })\n ]\n : []\n )\n : []\n )\n}\n\n/** @internal */\nexport const notRunOutput = \"Not run: the turn ended before this call ran.\"\n\n/** @internal */\nexport const mayHaveRunOutput =\n \"Stopped: the turn ended while this call ran. It may have done its work; check before doing it again.\"\n\nconst ago = (millis: number) => {\n const seconds = Math.max(0, Math.round(millis / 1000))\n\n return seconds < 90 ? `${seconds} seconds` : `${Math.round(seconds / 60)} minutes`\n}\n\nconst finishedText = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>,\n index: number,\n finished: AssistantEvent.SpecialistFinished\n) => {\n const at = entries[index]!\n\n const isStart = ({ event }: AssistantEvent.AnyEntry) =>\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"SpecialistStarted\") &&\n event.runId === finished.runId\n\n const from = entries.slice(0, index).findLastIndex(isStart)\n const started = from === -1 ? undefined : entries[from]\n const since = started === undefined ? [] : entries.slice(from + 1, index)\n const wrote = since.some(({ event }) => Predicate.isTagged(event, \"UserMessage\"))\n\n const lines = [\n \"A report from a specialist you started. The user hasn't seen it.\",\n started === undefined\n ? `You started it for: ${finished.goal}`\n : `You started it ${ago(at.at - started.at)} ago for: ${finished.goal}`,\n wrote ? \"The user has written since you started it.\" : \"The user hasn't written since you started it.\",\n Match.value(finished.status).pipe(\n Match.when(\"answered\", () => `Report:\\n${finished.report ?? \"\"}`),\n Match.when(\"failed\", () => `It failed: ${finished.error ?? \"\"}`),\n Match.when(\"cut_off\", () => \"A restart cut it off before it finished. Spawn it again if it's still needed.\"),\n Match.when(\"stopped\", () => \"You stopped it.\"),\n Match.exhaustive\n )\n ]\n\n return `<specialist name=\"${finished.specialist}\" run=\"${finished.runId}\" status=\"${finished.status}\">\\n${lines.join(\n \"\\n\"\n )}\\n</specialist>`\n}\n\nconst answerText = (answer: AssistantEvent.BlockAnswer) =>\n `<answer message=\"${answer.messageId}\" kind=\"${answer.kind}\">\\nThe user answered with the block on your message ${answer.messageId}.\\n${toJsonText(answer.value)}\\n</answer>`\n\nconst resultText = (result: AssistantEvent.ToolResult) =>\n `<result tool=\"${result.tool}\" call=\"${result.callId}\" outcome=\"${result.outcome}\">\\nThe result of a call from earlier in this chat.\\n${toJsonText(result.result)}\\n</result>`\n\n/** @internal */\nexport const awaitingApprovalOutput =\n \"Waiting for the user's approval: they see this call and approve or reject it. Its outcome arrives later in this chat; don't call it again meanwhile.\"\n\nconst awaitingText = (request: AssistantEvent.ApprovalRequested) =>\n `<approval tool=\"${request.tool}\" call=\"${request.callId}\" status=\"pending\">\\nYour call to ${request.tool} from earlier in this chat waits for the user's approval.\\n${toJsonText(request.params)}\\n</approval>`\n\nconst approvedText = (result: AssistantEvent.ToolResult) =>\n `<result tool=\"${result.tool}\" call=\"${result.callId}\" outcome=\"${result.outcome}\">\\nThe user approved your call to ${result.tool} from earlier in this chat, and it ran.\\n${toJsonText(result.result)}\\n</result>`\n\nconst rejectedText = (decision: AssistantEvent.ApprovalDecision, tool: string) =>\n `<approval tool=\"${tool}\" call=\"${decision.callId}\" decision=\"rejected\">\\nThe user rejected your call to ${tool}; it didn't run.${decision.reason === undefined ? \"\" : `\\nTheir reason: ${decision.reason}`}\\n</approval>`\n\nconst expiredText = (callId: string, tool: string) =>\n `<approval tool=\"${tool}\" call=\"${callId}\" status=\"expired\">\\nThe user moved on without approving your call to ${tool}; it didn't run.\\n</approval>`\n\nconst awaitingMessage = (request: AssistantEvent.ApprovalRequested): Prompt.ToolMessage =>\n Prompt.toolMessage({\n content: [\n Prompt.toolResultPart({\n id: request.callId,\n name: request.tool,\n isFailure: false,\n result: awaitingApprovalOutput,\n providerExecuted: false\n })\n ]\n })\n\nconst isEntry = (item: AssistantEvent.AnyEntry | Prompt.Message): item is AssistantEvent.AnyEntry =>\n Predicate.hasProperty(item, \"sequence\")\n\n// In the order the model saw the entries, not the order they were stored. A message a history view put in stands\n// where it is, right after the entry before it, and waits like an input for a call's result or for the model's next\n// read; the app's own events reach the model only as such messages.\n/** @internal */\nexport const fromEntries = (\n items: ReadonlyArray<AssistantEvent.AnyEntry | Prompt.Message>\n): ReadonlyArray<Prompt.Message> => {\n const entries = items.filter(isEntry)\n const history: Array<Prompt.Message> = []\n const held: Array<Prompt.Message> = []\n const open = new Set<string>()\n const called = new Set<string>()\n // Calls that waited for approval: the placeholder answered them, so what came of them later is an input.\n const requests = new Map<string, AssistantEvent.ApprovalRequested>()\n const toolOf = (callId: string) => requests.get(callId)?.tool ?? \"a tool\"\n\n const flushIfClosed = () => {\n if (open.size === 0) history.push(...held.splice(0))\n }\n\n const nextSeen = (from: number) => {\n const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, \"ModelOutput\"))?.event\n\n return next !== undefined && AssistantEvent.isLibraryEvent(next) && Predicate.isTagged(next, \"ModelOutput\")\n ? next.seenThroughSequence\n : undefined\n }\n\n // `index` is the last entry at or before the item, and `sequence` where the item stands.\n const input = (index: number, sequence: number, item: Prompt.Message) => {\n const seen = nextSeen(index)\n\n if (open.size > 0 || (seen !== undefined && seen < sequence)) held.push(item)\n else history.push(item)\n }\n\n let index = -1\n\n for (const item of items) {\n if (!isEntry(item)) {\n input(index, (entries[index]?.sequence ?? 0) + 0.5, item)\n\n continue\n }\n\n index += 1\n\n const { event, sequence } = item\n const at = index\n\n if (!AssistantEvent.isLibraryEvent(event)) continue\n\n Match.value(event).pipe(\n Match.tagsExhaustive({\n UserMessage: (message) =>\n input(at, sequence, userText(AssistantEvent.textWithAttachments(message.text, message.attachments))),\n BlockAnswer: (answer) => input(at, sequence, userText(answerText(answer))),\n ModelOutput: (output) => {\n history.push(...Prompt.fromResponseParts(output.parts).content)\n\n for (const part of output.parts) {\n if (part.type === \"tool-call\") {\n open.add(part.id)\n called.add(part.id)\n }\n }\n\n flushIfClosed()\n },\n ToolResult: (result) => {\n if (requests.has(result.callId)) return input(at, sequence, userText(approvedText(result)))\n\n // A view that starts after the call (since a summary, say) keeps its result as text: a tool result needs its\n // call right before it.\n if (!called.has(result.callId)) return input(at, sequence, userText(resultText(result)))\n\n history.push(toolMessage(result))\n open.delete(result.callId)\n flushIfClosed()\n },\n AssistantMessage: () => undefined,\n ToolStarted: () => undefined,\n TurnStarted: () => undefined,\n TurnCompleted: () => undefined,\n TurnFailed: () => undefined,\n SpecialistFinished: (finished) => input(at, sequence, userText(finishedText(entries, at, finished))),\n // Every run starts in a call of the model's own (a spawn or a follow-up), whose result says so.\n SpecialistStarted: () => undefined,\n BlocksClosed: () => undefined,\n // The call's result, for the provider's pairing; what the user decides comes later.\n ApprovalRequested: (request) => {\n requests.set(request.callId, request)\n\n if (!called.has(request.callId)) return input(at, sequence, userText(awaitingText(request)))\n\n history.push(awaitingMessage(request))\n open.delete(request.callId)\n flushIfClosed()\n },\n // An approved call's result says so.\n ApprovalDecision: (decision) =>\n decision.decision === \"rejected\"\n ? input(at, sequence, userText(rejectedText(decision, toolOf(decision.callId))))\n : undefined,\n ApprovalsExpired: (expired) =>\n expired.callIds.forEach((callId) => input(at, sequence, userText(expiredText(callId, toolOf(callId))))),\n ChatDeleted: () => undefined\n })\n )\n }\n\n return [...history, ...held]\n}\n\n/** @internal */\nexport const isInput = (event: { readonly _tag: string }): boolean =>\n AssistantEvent.isInput(event) || AssistantEvent.isAppInput(event)\n\n/** @internal */\nexport const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): number => {\n const last = entries.findLast(({ event }) => AssistantEvent.isTurnEnd(event))?.event\n\n return last !== undefined && AssistantEvent.isTurnEnd(last) ? last.throughSequence : 0\n}\n\n// A run the assistant stopped isn't an input here: it wakes nothing.\n/** @internal */\nexport const unhandledInputs = (\n entries: ReadonlyArray<AssistantEvent.AnyEntry>\n): ReadonlyArray<{ readonly _tag: string }> => {\n const seen = handledThrough(entries)\n\n return entries.flatMap(({ event, sequence }) =>\n sequence > seen && isInput(event) && !(AssistantEvent.isInput(event) && !AssistantEvent.isWaking(event))\n ? [event]\n : []\n )\n}\n\n/** @internal */\nexport const needsTurn = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): boolean =>\n unhandledInputs(entries).length > 0\n\n// A failure the user was shown counts as a reply, and so does a call they're asked to approve.\n/** @internal */\nexport const owesReply = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean => {\n const latest = entries.findLast(\n ({ event, sequence }) =>\n sequence <= throughSequence && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n return (\n latest !== undefined &&\n !entries.some(\n ({ event, sequence }) =>\n sequence > latest.sequence &&\n (Predicate.isTagged(event, \"AssistantMessage\") ||\n Predicate.isTagged(event, \"TurnFailed\") ||\n Predicate.isTagged(event, \"ApprovalRequested\"))\n )\n )\n}\n\nconst sentToWaitOn = Schema.decodeUnknownOption(Schema.Struct({ wait: Schema.Literal(false) }))\n\n// Messages sent with `wait: false` are progress while the model kept working, like \"On it\".\nconst progressMessages = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlySet<string> =>\n new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"ModelOutput\")\n ? event.parts.flatMap((part) =>\n part.type === \"tool-call\" && part.name === \"send_user_message\" && Option.isSome(sentToWaitOn(part.params))\n ? [Uuid.fromKey(`message:${part.id}`)]\n : []\n )\n : []\n )\n )\n\n// The model said it was on it and stopped without the result: only progress messages since the user's latest input.\n// A specialist still working doesn't count, since its report is what comes next.\n/** @internal */\nexport const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean => {\n const latest = entries.findLast(\n ({ event, sequence }) =>\n sequence <= throughSequence && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)\n )\n\n if (latest === undefined) return false\n\n const after = entries.flatMap(({ sequence, event }) =>\n sequence > latest.sequence && AssistantEvent.isLibraryEvent(event) ? [event] : []\n )\n\n const messages = after.flatMap((event) => (Predicate.isTagged(event, \"AssistantMessage\") ? [event] : []))\n const progress = progressMessages(entries)\n const started = after.flatMap((event) => (Predicate.isTagged(event, \"SpecialistStarted\") ? [event.runId] : []))\n\n const finished = new Set(\n entries.flatMap(({ event }) =>\n AssistantEvent.isLibraryEvent(event) && Predicate.isTagged(event, \"SpecialistFinished\") ? [event.runId] : []\n )\n )\n\n // A call waiting for the user's approval is what comes next, like a specialist's report.\n return (\n messages.length > 0 &&\n messages.every((message) => message.block === undefined && progress.has(message.id)) &&\n !after.some((event) => Predicate.isTagged(event, \"TurnFailed\") || Predicate.isTagged(event, \"ApprovalRequested\")) &&\n started.every((runId) => finished.has(runId))\n )\n}\n\n/** @internal */\nexport const unreadAfter = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean =>\n entries.some(({ event, sequence }) => sequence > throughSequence && isInput(event))\n"],"mappings":"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"}
@@ -23,7 +23,7 @@ export declare const TurnTools: Context.Reference<Option.Option<Tools<never>>>;
23
23
  /** @internal */
24
24
  export interface ChatHistory {
25
25
  readonly messages: ReadonlyArray<Prompt.Message>;
26
- readonly throughSeq: number;
26
+ readonly throughSequence: number;
27
27
  }
28
28
  /** @internal */
29
29
  export interface StepOptions<R = never> {
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","names":[],"sources":["../../src/internal/loop.ts"],"mappings":";;;;;;;;;;;;;;;YAuBY,QACR,cACA,cACA,aACA;;qBAKS,eAAa,QAAA,UAAA,OAAA;WACW,UAAA,OAAO,OAAO,cAAc;;;qBAMpD,WAAS,QAAA,UAAA,OAAA,OAAA;;iBAKL;WACN,UAAU,cAAc,OAAO;WAC/B;;;iBAUM,YAAY;WAClB,uBAAuB;WACvB,UAAU,MAAe;WACzB,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;WACvD,aAAa,cAAc;;;iBAIrB,QAAQ,mBAAmB,YAAY;WAC7C;;;iBAKM;;WAEN,QAAQ;;;;;;WAMR;;;;WAIA;;;YA4HC,KAAK,GAAG,KAAK,KAAK;EAC5B;aAAkB,OAAO;;EACzB;aAAiB,OAAO;;EACxB,MAAM;;;qBAUK,QAAS,GAAC,OAAS,MAAI,KAAK;;qBAG5B,OAAQ,GAAC,OAAS,MAAI,OAAO,OAAO,YAAY;;qBAIhD,OAAQ,gBAAc,OAAO,OAAO,YAAY;;qBAOhD,OAAQ,GAAG,GAAG,GAAG,GAAC,SACpB,GAAC,OACH,OAAO,MAAM,OAAO,OAAO,KAAK,GAAG,IAAI,GAAG,OAChD,OAAO,OAAO,GAAG,GAAG;;qBAmBV,OAAQ,GAAG,GAAG,YAAY,YAAU,SACtC,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,IAAI;;qBA6E5E,MAAO,WAAS,SAClB,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB"}
1
+ {"version":3,"file":"loop.d.ts","names":[],"sources":["../../src/internal/loop.ts"],"mappings":";;;;;;;;;;;;;;;YAuBY,QACR,cACA,cACA,aACA;;qBAKS,eAAa,QAAA,UAAA,OAAA;WACW,UAAA,OAAO,OAAO,cAAc;;;qBAMpD,WAAS,QAAA,UAAA,OAAA,OAAA;;iBAKL;WACN,UAAU,cAAc,OAAO;WAC/B;;;iBAUM,YAAY;WAClB,uBAAuB;WACvB,UAAU,MAAe;WACzB,SAAS,OAAO,OAAO,cAAc,OAAO,WAAW;WACvD,aAAa,cAAc;;;iBAIrB,QAAQ,mBAAmB,YAAY;WAC7C;;;iBAKM;;WAEN,QAAQ;;;;;;WAMR;;;;WAIA;;;YA6IC,KAAK,GAAG,KAAK,KAAK;EAC5B;aAAkB,OAAO;;EACzB;aAAiB,OAAO;;EACxB,MAAM;;;qBAUK,QAAS,GAAC,OAAS,MAAI,KAAK;;qBAG5B,OAAQ,GAAC,OAAS,MAAI,OAAO,OAAO,YAAY;;qBAIhD,OAAQ,gBAAc,OAAO,OAAO,YAAY;;qBAOhD,OAAQ,GAAG,GAAG,GAAG,GAAC,SACpB,GAAC,OACH,OAAO,MAAM,OAAO,OAAO,KAAK,GAAG,IAAI,GAAG,OAChD,OAAO,OAAO,GAAG,GAAG;;qBAmBV,OAAQ,GAAG,GAAG,YAAY,YAAU,SACtC,YAAY,IAAE,OAChB,QAAQ,eAAe,OAAO,OAAO,YAAY,IAAI,IAAI,QAC/D,OAAO,OAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,IAAI,cAAc,gBAAgB,IAAI;;qBA6E5E,MAAO,WAAS,SAClB,QAAQ,OAChB,OAAO,OAAO,OAAO,QAAQ,SAAS,cAAc,gBAAgB"}
@@ -2,7 +2,7 @@ import { ApprovalRequested, ModelOutput, ModelPart, ToolResult, ToolStarted } fr
2
2
  import { unansweredCalls } from "./history.js";
3
3
  import { chatSpan, toolOutput, toolSpan } from "./telemetry.js";
4
4
  import { empty, failed } from "./toolkit.js";
5
- import { isTurnEnding } from "./tools.js";
5
+ import { isTurnEnding, isUserMessage } from "./tools.js";
6
6
  import * as Context from "effect/Context";
7
7
  import * as Effect from "effect/Effect";
8
8
  import * as Predicate from "effect/Predicate";
@@ -21,10 +21,10 @@ const TurnApprovals = Context.Reference("effect-assistant/TurnApprovals", { defa
21
21
  const TurnTools = Context.Reference("effect-assistant/TurnTools", { defaultValue: Option.none });
22
22
  const readHistory = (read) => "messages" in read ? {
23
23
  messages: read.messages,
24
- throughSeq: read.throughSeq
24
+ throughSequence: read.throughSequence
25
25
  } : {
26
26
  messages: read,
27
- throughSeq: void 0
27
+ throughSequence: void 0
28
28
  };
29
29
  const requestToolkit = (definitions) => ({
30
30
  tools: Object.fromEntries(definitions.map((tool) => [tool.name, tool.needsApproval === void 0 || tool.needsApproval === false ? tool : tool.setNeedsApproval(false)])),
@@ -62,6 +62,11 @@ const awaitApproval = Effect.fnUntraced(function* (call) {
62
62
  params: call.params
63
63
  });
64
64
  });
65
+ const unexplained = (call) => ToolResult.make({
66
+ callId: call.id,
67
+ tool: call.name,
68
+ ...failed(`Not sent for approval: the user would see buttons without knowing what they approve. In one step, send them a message with send_user_message saying what ${call.name} will do (what, to whom, how many), and call ${call.name} again.`, "ApprovalNeedsMessage")
69
+ });
65
70
  const runCalls = (tools, pending, history, then) => Stream.unwrap(Effect.gen(function* () {
66
71
  const waits = yield* Effect.forEach(pending, ({ call, approved }) => approved ? Effect.succeed(false) : tools.needsApproval(call, history));
67
72
  const started = pending.flatMap(({ call }, i) => waits[i] === true ? [] : [value(ToolStarted.make({
@@ -69,7 +74,8 @@ const runCalls = (tools, pending, history, then) => Stream.unwrap(Effect.gen(fun
69
74
  tool: call.name
70
75
  }))]);
71
76
  return Stream.fromIterable(started).pipe(Stream.concat(Stream.unwrap(Effect.gen(function* () {
72
- const done = yield* Effect.forEach(pending, ({ call }, i) => waits[i] === true ? awaitApproval(call) : runCall(tools, call), { concurrency: "unbounded" });
77
+ const explained = pending.some(({ call }) => isUserMessage(call));
78
+ const done = yield* Effect.forEach(pending, ({ call }, i) => waits[i] !== true ? runCall(tools, call) : explained ? awaitApproval(call) : Effect.succeed(unexplained(call)), { concurrency: "unbounded" });
73
79
  return Stream.fromIterable(done.map(value)).pipe(Stream.concat(then({
74
80
  failed: done.some(isFailure),
75
81
  waiting: done.some((event) => Predicate.isTagged(event, "ApprovalRequested"))
@@ -102,7 +108,7 @@ const step = (options, then) => {
102
108
  const instructions = Predicate.isString(options.instructions) ? { text: options.instructions } : options.instructions;
103
109
  const after = (result) => Stream.suspend(() => then(result));
104
110
  return Stream.unwrap(Effect.gen(function* () {
105
- const { messages: history, throughSeq } = readHistory(yield* options.history);
111
+ const { messages: history, throughSequence } = readHistory(yield* options.history);
106
112
  const cutOff = unansweredCalls(history);
107
113
  const approvals = yield* TurnApprovals;
108
114
  const approved = Option.isNone(approvals) ? [] : (yield* approvals.value.approved).filter((call) => toolkit.definitions.some((tool) => tool.name === call.name) && !cutOff.some((cut) => cut.id === call.id));
@@ -127,7 +133,7 @@ const step = (options, then) => {
127
133
  const metadata = response.content.find((part) => part.type === "response-metadata");
128
134
  const output = ModelOutput.make({
129
135
  parts: modelParts(response.content),
130
- seenThroughSeq: throughSeq,
136
+ seenThroughSequence: throughSequence,
131
137
  model: metadata?.modelId ?? Option.getOrUndefined(name),
132
138
  responseId: metadata?.id,
133
139
  usage: response.usage,
@@ -1 +1 @@
1
- {"version":3,"file":"loop.js","names":["AssistantEvent.ModelPart","Telemetry.toolOutput","Telemetry.toolSpan","Toolkits.failed","Toolkits.empty","History.unansweredCalls","Telemetry.chatSpan"],"sources":["../../src/internal/loop.ts"],"sourcesContent":["import type * as AiError from \"effect/ai/AiError\"\nimport * as LanguageModel from \"effect/ai/LanguageModel\"\nimport * as Model from \"effect/ai/Model\"\nimport * as Prompt from \"effect/ai/Prompt\"\nimport type * as Response from \"effect/ai/Response\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport type * as Toolkit from \"effect/ai/Toolkit\"\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Data from \"effect/Data\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type * as Instructions from \"../Instructions.ts\"\nimport * as History from \"./history.ts\"\nimport * as Telemetry from \"./telemetry.ts\"\nimport * as Toolkits from \"./toolkit.ts\"\nimport { isTurnEnding } from \"./tools.ts\"\n\n/** @internal */\nexport type Event =\n | AssistantEvent.ModelOutput\n | AssistantEvent.ToolStarted\n | AssistantEvent.ToolResult\n | AssistantEvent.ApprovalRequested\n\n// Only a turn's loop can wait for the user's approval, and runs the calls they approved. A turn's specialists inherit\n// its context, so their runs set it back to none.\n/** @internal */\nexport const TurnApprovals = Context.Reference<\n Option.Option<{ readonly approved: Effect.Effect<ReadonlyArray<AssistantEvent.ToolCallPart>> }>\n>(\"effect-assistant/TurnApprovals\", { defaultValue: Option.none })\n\n// The assistant's own tools with the turn's services, so an app's own loop in a turn can message the user. A\n// specialist's run has none.\n/** @internal */\nexport const TurnTools = Context.Reference<Option.Option<Toolkits.Tools>>(\"effect-assistant/TurnTools\", {\n defaultValue: Option.none\n})\n\n/** @internal */\nexport interface ChatHistory {\n readonly messages: ReadonlyArray<Prompt.Message>\n readonly throughSeq: number\n}\n\nconst readHistory = (read: ReadonlyArray<Prompt.Message> | ChatHistory) =>\n \"messages\" in read\n ? { messages: read.messages, throughSeq: read.throughSeq }\n : { messages: read, throughSeq: undefined }\n\n// `AssistantLoop` takes native toolkits; turns and specialist runs come here with their tools already merged.\n/** @internal */\nexport interface StepOptions<R = never> {\n readonly instructions: string | Instructions.Resolved\n readonly toolkit?: Toolkits.Tools<R> | undefined\n readonly history: Effect.Effect<ReadonlyArray<Prompt.Message> | ChatHistory>\n readonly toolChoice?: LanguageModel.ToolChoice<string> | undefined\n}\n\n/** @internal */\nexport interface Options<R = never> extends StepOptions<R> {\n readonly maxSteps?: number | undefined\n}\n\n// Its member docs show through `AssistantLoop.StepResult`.\n/** @internal */\nexport interface StepResult {\n /** The model's output; unset when the step only ran calls a crash cut off. */\n readonly output: AssistantEvent.ModelOutput | undefined\n /**\n * Whether the model is done: it made no tool calls, one of its calls waits for the user's\n * approval, or it ended its turn (`wait`, or a message with `wait: true`) and none of the step's\n * calls failed.\n */\n readonly done: boolean\n /**\n * The history's length with this step's output and results: a longer one got something new meanwhile.\n */\n readonly historyLength: number\n}\n\n// The loop runs the calls itself, after storing each as started, so the model call runs none. It also decides which\n// calls wait for approval: the model call would ask for approvals of its own.\nconst requestToolkit = (definitions: ReadonlyArray<Tool.Any>): Toolkit.WithHandler<Record<string, Tool.Any>> => ({\n tools: Object.fromEntries(\n definitions.map((tool) => [\n tool.name,\n tool.needsApproval === undefined || tool.needsApproval === false ? tool : tool.setNeedsApproval(false)\n ])\n ),\n handle: () => Effect.succeed(Stream.empty)\n})\n\n// Tool call resolution stays on with a toolkit that runs nothing: with it off, a call whose params don't fit its\n// tool's schema fails the whole model call, and here the model reads why instead.\nconst generate = (\n prompt: Prompt.Prompt,\n definitions: ReadonlyArray<Tool.Any>,\n toolChoice: LanguageModel.ToolChoice<string> | undefined\n): Effect.Effect<\n LanguageModel.GenerateTextResponse<Record<string, Tool.Any>, \"opaque\">,\n AiError.AiError,\n LanguageModel.LanguageModel\n> => LanguageModel.generateText({ prompt, toolkit: requestToolkit(definitions), toolChoice })\n\nconst decodePart = Schema.decodeUnknownOption(AssistantEvent.ModelPart)\n\nconst modelParts = (content: ReadonlyArray<Response.AllParts<Record<string, Tool.Any>>>) =>\n content.flatMap((part) =>\n Option.toArray(decodePart(part)).filter((decoded) => !(decoded.type === \"tool-call\" && decoded.providerExecuted))\n )\n\nconst toolCalls = (parts: ReadonlyArray<AssistantEvent.ModelPart>): ReadonlyArray<AssistantEvent.ToolCallPart> =>\n parts.flatMap((part) => (part.type === \"tool-call\" ? [part] : []))\n\nconst runCall = Effect.fnUntraced(function* <R>(tools: Toolkits.Tools<R>, call: AssistantEvent.ToolCallPart) {\n const result = yield* Effect.gen(function* () {\n const result = yield* tools.run(call)\n\n yield* Telemetry.toolOutput(result.result)\n\n return result\n }).pipe(Telemetry.toolSpan(call))\n\n return AssistantEvent.ToolResult.make({ callId: call.id, tool: call.name, ...result })\n})\n\n// A call that needs approval waits for the user in a turn. A specialist's run can't ask them, so its call fails.\nconst awaitApproval = Effect.fnUntraced(function* (call: AssistantEvent.ToolCallPart) {\n if (Option.isNone(yield* TurnApprovals)) {\n return AssistantEvent.ToolResult.make({\n callId: call.id,\n tool: call.name,\n ...Toolkits.failed(\n `${call.name} needs the user's approval, and only the assistant can ask for it. Don't run it: say in your answer what needs approving, with the details.`,\n \"NeedsApproval\"\n )\n })\n }\n\n return AssistantEvent.ApprovalRequested.make({ callId: call.id, tool: call.name, params: call.params })\n})\n\ninterface Pending {\n readonly call: AssistantEvent.ToolCallPart\n /** The user approved it: it runs without asking again. */\n readonly approved: boolean\n}\n\ninterface Ran {\n /** One of the calls failed, so the model has to read the results. */\n readonly failed: boolean\n /** One of the calls waits for the user's approval, so the turn ends. */\n readonly waiting: boolean\n}\n\n// Every `ToolStarted` is stored before any call runs; a call that waits for approval doesn't start.\nconst runCalls = <R, S, E, R2>(\n tools: Toolkits.Tools<R>,\n pending: ReadonlyArray<Pending>,\n history: ReadonlyArray<Prompt.Message>,\n then: (ran: Ran) => Stream.Stream<Step<never, S>, E, R2>\n): Stream.Stream<Step<Event, S>, E, R | R2> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const waits = yield* Effect.forEach(pending, ({ call, approved }) =>\n approved ? Effect.succeed(false) : tools.needsApproval(call, history)\n )\n\n const started = pending.flatMap(({ call }, i) =>\n waits[i] === true ? [] : [value(AssistantEvent.ToolStarted.make({ callId: call.id, tool: call.name }))]\n )\n\n return Stream.fromIterable(started).pipe(\n Stream.concat(\n Stream.unwrap(\n Effect.gen(function* () {\n const done = yield* Effect.forEach(\n pending,\n ({ call }, i): Effect.Effect<Event, never, R> =>\n waits[i] === true ? awaitApproval(call) : runCall(tools, call),\n { concurrency: \"unbounded\" }\n )\n\n return Stream.fromIterable(done.map(value)).pipe(\n Stream.concat(\n then({\n failed: done.some(isFailure),\n waiting: done.some((event) => Predicate.isTagged(event, \"ApprovalRequested\"))\n })\n )\n )\n })\n )\n )\n )\n })\n )\n\nconst isFailure = (event: Event) => Predicate.isTagged(event, \"ToolResult\") && event.outcome === \"failure\"\n\n/** @internal */\nexport type Step<A, S> = Data.TaggedEnum<{\n Value: { readonly value: A }\n Next: { readonly state: S }\n Stop: Record<never, never>\n}>\n\ninterface StepDefinition extends Data.TaggedEnum.WithGenerics<2> {\n readonly taggedEnum: Step<this[\"A\"], this[\"B\"]>\n}\n\nconst Steps = Data.taggedEnum<StepDefinition>()\n\n/** @internal */\nexport const value = <A>(value: A): Step<A, never> => Steps.Value({ value })\n\n/** @internal */\nexport const next = <S>(state: S): Stream.Stream<Step<never, S>> => Stream.succeed(Steps.Next({ state }))\n\n/** @internal */\n// oxlint-disable-next-line no-unnecessary-type-parameters -- `stop<number>()` types a loop's state where inference can't.\nexport const stop = <S = never>(): Stream.Stream<Step<never, S>> => Stream.succeed(Steps.Stop())\n\nconst isValue = <A, S>(step: Step<A, S>): step is Data.TaggedEnum.Value<Step<A, S>, \"Value\"> =>\n Predicate.isTagged(step, \"Value\")\n\n// A body that ends without `next` or `stop` stops too.\n/** @internal */\nexport const loop = <S, A, E, R>(\n initial: S,\n body: (state: S) => Stream.Stream<Step<A, S>, E, R>\n): Stream.Stream<A, E, R> => {\n const go = (state: S): Stream.Stream<A, E, R> =>\n Stream.suspend(() => {\n let after: Option.Option<S> = Option.none()\n\n return body(state).pipe(\n Stream.tap((step) =>\n Effect.sync(() => (after = Predicate.isTagged(step, \"Next\") ? Option.some(step.state) : after))\n ),\n Stream.filter(isValue),\n Stream.map((step) => step.value),\n Stream.concat(Stream.suspend(() => Option.match(after, { onNone: () => Stream.empty, onSome: go })))\n )\n })\n\n return go(initial)\n}\n\n/** @internal */\nexport const step = <R, S, E2 = never, R2 = never>(\n options: StepOptions<R>,\n then: (result: StepResult) => Stream.Stream<Step<never, S>, E2, R2>\n): Stream.Stream<Step<Event, S>, AiError.AiError | E2, LanguageModel.LanguageModel | R | R2> => {\n const toolkit = options.toolkit ?? Toolkits.empty\n const instructions = Predicate.isString(options.instructions) ? { text: options.instructions } : options.instructions\n\n const after = (result: StepResult) => Stream.suspend(() => then(result))\n\n return Stream.unwrap(\n Effect.gen(function* () {\n const { messages: history, throughSeq } = readHistory(yield* options.history)\n // Calls a crash cut off, with no result: they run again, with the same ids, before the model goes on.\n const cutOff = History.unansweredCalls(history)\n const approvals = yield* TurnApprovals\n\n // Calls the user approved: they run as the model made them, with the same ids, before the model goes on. Not in\n // a loop without their tools.\n const approved = Option.isNone(approvals)\n ? []\n : (yield* approvals.value.approved).filter(\n (call) =>\n toolkit.definitions.some((tool) => tool.name === call.name) && !cutOff.some((cut) => cut.id === call.id)\n )\n\n if (cutOff.length > 0 || approved.length > 0) {\n const pending = [\n ...cutOff.map((call) => ({ call, approved: false })),\n ...approved.map((call) => ({ call, approved: true }))\n ]\n\n return runCalls(toolkit, pending, history, ({ waiting }) =>\n after({ output: undefined, done: waiting, historyLength: history.length + pending.length })\n )\n }\n\n const prompt = Prompt.fromMessages([Prompt.systemMessage({ content: instructions.text }), ...history])\n const name = yield* Effect.serviceOption(Model.ModelName)\n const started = yield* Clock.currentTimeMillis\n\n const response = yield* generate(prompt, toolkit.definitions, options.toolChoice).pipe(\n Telemetry.chatSpan(prompt, instructions.attributes)\n )\n\n const metadata = response.content.find((part) => part.type === \"response-metadata\")\n\n const output = AssistantEvent.ModelOutput.make({\n parts: modelParts(response.content),\n seenThroughSeq: throughSeq,\n model: metadata?.modelId ?? Option.getOrUndefined(name),\n responseId: metadata?.id,\n usage: response.usage,\n durationMillis: (yield* Clock.currentTimeMillis) - started\n })\n\n const calls = toolCalls(output.parts)\n const historyLength = history.length + Prompt.fromResponseParts(output.parts).content.length + calls.length\n\n // A failed call doesn't end the turn: a message that failed never reached the user, so the model reads why. A call\n // that waits for approval does: the turn waits for the user.\n return Stream.make(value(output)).pipe(\n Stream.concat(\n runCalls(\n toolkit,\n calls.map((call) => ({ call, approved: false })),\n history,\n ({ failed, waiting }) =>\n after({\n output,\n done: calls.length === 0 || waiting || (!failed && calls.some(isTurnEnding)),\n historyLength\n })\n )\n )\n )\n })\n )\n}\n\n/** @internal */\nexport const run = <R = never>(\n options: Options<R>\n): Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | R> => {\n const maxSteps = options.maxSteps ?? 16\n\n // Done: stop, unless something arrived while the model was working; then it reads that first.\n const afterDone = (steps: number, historyLength: number) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const read = yield* options.history\n\n return readHistory(read).messages.length > historyLength ? next(steps) : stop<number>()\n })\n )\n\n return loop(0, (steps: number) =>\n step(options, (result) => {\n const taken = result.output === undefined ? steps : steps + 1\n\n if (taken >= maxSteps) return stop<number>()\n\n return result.done ? afterDone(taken, result.historyLength) : next(taken)\n })\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"loop.js","names":["AssistantEvent.ModelPart","Telemetry.toolOutput","Telemetry.toolSpan","Toolkits.failed","Toolkits.empty","History.unansweredCalls","Telemetry.chatSpan"],"sources":["../../src/internal/loop.ts"],"sourcesContent":["import type * as AiError from \"effect/ai/AiError\"\nimport * as LanguageModel from \"effect/ai/LanguageModel\"\nimport * as Model from \"effect/ai/Model\"\nimport * as Prompt from \"effect/ai/Prompt\"\nimport type * as Response from \"effect/ai/Response\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport type * as Toolkit from \"effect/ai/Toolkit\"\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Data from \"effect/Data\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type * as Instructions from \"../Instructions.ts\"\nimport * as History from \"./history.ts\"\nimport * as Telemetry from \"./telemetry.ts\"\nimport * as Toolkits from \"./toolkit.ts\"\nimport { isTurnEnding, isUserMessage } from \"./tools.ts\"\n\n/** @internal */\nexport type Event =\n | AssistantEvent.ModelOutput\n | AssistantEvent.ToolStarted\n | AssistantEvent.ToolResult\n | AssistantEvent.ApprovalRequested\n\n// Only a turn's loop can wait for the user's approval, and runs the calls they approved. A turn's specialists inherit\n// its context, so their runs set it back to none.\n/** @internal */\nexport const TurnApprovals = Context.Reference<\n Option.Option<{ readonly approved: Effect.Effect<ReadonlyArray<AssistantEvent.ToolCallPart>> }>\n>(\"effect-assistant/TurnApprovals\", { defaultValue: Option.none })\n\n// The assistant's own tools with the turn's services, so an app's own loop in a turn can message the user. A\n// specialist's run has none.\n/** @internal */\nexport const TurnTools = Context.Reference<Option.Option<Toolkits.Tools>>(\"effect-assistant/TurnTools\", {\n defaultValue: Option.none\n})\n\n/** @internal */\nexport interface ChatHistory {\n readonly messages: ReadonlyArray<Prompt.Message>\n readonly throughSequence: number\n}\n\nconst readHistory = (read: ReadonlyArray<Prompt.Message> | ChatHistory) =>\n \"messages\" in read\n ? { messages: read.messages, throughSequence: read.throughSequence }\n : { messages: read, throughSequence: undefined }\n\n// `AssistantLoop` takes native toolkits; turns and specialist runs come here with their tools already merged.\n/** @internal */\nexport interface StepOptions<R = never> {\n readonly instructions: string | Instructions.Resolved\n readonly toolkit?: Toolkits.Tools<R> | undefined\n readonly history: Effect.Effect<ReadonlyArray<Prompt.Message> | ChatHistory>\n readonly toolChoice?: LanguageModel.ToolChoice<string> | undefined\n}\n\n/** @internal */\nexport interface Options<R = never> extends StepOptions<R> {\n readonly maxSteps?: number | undefined\n}\n\n// Its member docs show through `AssistantLoop.StepResult`.\n/** @internal */\nexport interface StepResult {\n /** The model's output; unset when the step only ran calls a crash cut off. */\n readonly output: AssistantEvent.ModelOutput | undefined\n /**\n * Whether the model is done: it made no tool calls, one of its calls waits for the user's\n * approval, or it ended its turn (`wait`, or a message with `wait: true`) and none of the step's\n * calls failed.\n */\n readonly done: boolean\n /**\n * The history's length with this step's output and results: a longer one got something new meanwhile.\n */\n readonly historyLength: number\n}\n\n// The loop runs the calls itself, after storing each as started, so the model call runs none. It also decides which\n// calls wait for approval: the model call would ask for approvals of its own.\nconst requestToolkit = (definitions: ReadonlyArray<Tool.Any>): Toolkit.WithHandler<Record<string, Tool.Any>> => ({\n tools: Object.fromEntries(\n definitions.map((tool) => [\n tool.name,\n tool.needsApproval === undefined || tool.needsApproval === false ? tool : tool.setNeedsApproval(false)\n ])\n ),\n handle: () => Effect.succeed(Stream.empty)\n})\n\n// Tool call resolution stays on with a toolkit that runs nothing: with it off, a call whose params don't fit its\n// tool's schema fails the whole model call, and here the model reads why instead.\nconst generate = (\n prompt: Prompt.Prompt,\n definitions: ReadonlyArray<Tool.Any>,\n toolChoice: LanguageModel.ToolChoice<string> | undefined\n): Effect.Effect<\n LanguageModel.GenerateTextResponse<Record<string, Tool.Any>, \"opaque\">,\n AiError.AiError,\n LanguageModel.LanguageModel\n> => LanguageModel.generateText({ prompt, toolkit: requestToolkit(definitions), toolChoice })\n\nconst decodePart = Schema.decodeUnknownOption(AssistantEvent.ModelPart)\n\nconst modelParts = (content: ReadonlyArray<Response.AllParts<Record<string, Tool.Any>>>) =>\n content.flatMap((part) =>\n Option.toArray(decodePart(part)).filter((decoded) => !(decoded.type === \"tool-call\" && decoded.providerExecuted))\n )\n\nconst toolCalls = (parts: ReadonlyArray<AssistantEvent.ModelPart>): ReadonlyArray<AssistantEvent.ToolCallPart> =>\n parts.flatMap((part) => (part.type === \"tool-call\" ? [part] : []))\n\nconst runCall = Effect.fnUntraced(function* <R>(tools: Toolkits.Tools<R>, call: AssistantEvent.ToolCallPart) {\n const result = yield* Effect.gen(function* () {\n const result = yield* tools.run(call)\n\n yield* Telemetry.toolOutput(result.result)\n\n return result\n }).pipe(Telemetry.toolSpan(call))\n\n return AssistantEvent.ToolResult.make({ callId: call.id, tool: call.name, ...result })\n})\n\n// A call that needs approval waits for the user in a turn. A specialist's run can't ask them, so its call fails.\nconst awaitApproval = Effect.fnUntraced(function* (call: AssistantEvent.ToolCallPart) {\n if (Option.isNone(yield* TurnApprovals)) {\n return AssistantEvent.ToolResult.make({\n callId: call.id,\n tool: call.name,\n ...Toolkits.failed(\n `${call.name} needs the user's approval, and only the assistant can ask for it. Don't run it: say in your answer what needs approving, with the details.`,\n \"NeedsApproval\"\n )\n })\n }\n\n return AssistantEvent.ApprovalRequested.make({ callId: call.id, tool: call.name, params: call.params })\n})\n\ninterface Pending {\n readonly call: AssistantEvent.ToolCallPart\n /** The user approved it: it runs without asking again. */\n readonly approved: boolean\n}\n\ninterface Ran {\n /** One of the calls failed, so the model has to read the results. */\n readonly failed: boolean\n /** One of the calls waits for the user's approval, so the turn ends. */\n readonly waiting: boolean\n}\n\n// The approval shows the call, not what it does: the user learns that from the assistant's message beside it.\nconst unexplained = (call: AssistantEvent.ToolCallPart) =>\n AssistantEvent.ToolResult.make({\n callId: call.id,\n tool: call.name,\n ...Toolkits.failed(\n `Not sent for approval: the user would see buttons without knowing what they approve. In one step, send them a message with send_user_message saying what ${call.name} will do (what, to whom, how many), and call ${call.name} again.`,\n \"ApprovalNeedsMessage\"\n )\n })\n\n// Every `ToolStarted` is stored before any call runs; a call that waits for approval doesn't start.\nconst runCalls = <R, S, E, R2>(\n tools: Toolkits.Tools<R>,\n pending: ReadonlyArray<Pending>,\n history: ReadonlyArray<Prompt.Message>,\n then: (ran: Ran) => Stream.Stream<Step<never, S>, E, R2>\n): Stream.Stream<Step<Event, S>, E, R | R2> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const waits = yield* Effect.forEach(pending, ({ call, approved }) =>\n approved ? Effect.succeed(false) : tools.needsApproval(call, history)\n )\n\n const started = pending.flatMap(({ call }, i) =>\n waits[i] === true ? [] : [value(AssistantEvent.ToolStarted.make({ callId: call.id, tool: call.name }))]\n )\n\n return Stream.fromIterable(started).pipe(\n Stream.concat(\n Stream.unwrap(\n Effect.gen(function* () {\n const explained = pending.some(({ call }) => isUserMessage(call))\n\n const done = yield* Effect.forEach(\n pending,\n ({ call }, i): Effect.Effect<Event, never, R> =>\n waits[i] !== true\n ? runCall(tools, call)\n : explained\n ? awaitApproval(call)\n : Effect.succeed(unexplained(call)),\n { concurrency: \"unbounded\" }\n )\n\n return Stream.fromIterable(done.map(value)).pipe(\n Stream.concat(\n then({\n failed: done.some(isFailure),\n waiting: done.some((event) => Predicate.isTagged(event, \"ApprovalRequested\"))\n })\n )\n )\n })\n )\n )\n )\n })\n )\n\nconst isFailure = (event: Event) => Predicate.isTagged(event, \"ToolResult\") && event.outcome === \"failure\"\n\n/** @internal */\nexport type Step<A, S> = Data.TaggedEnum<{\n Value: { readonly value: A }\n Next: { readonly state: S }\n Stop: Record<never, never>\n}>\n\ninterface StepDefinition extends Data.TaggedEnum.WithGenerics<2> {\n readonly taggedEnum: Step<this[\"A\"], this[\"B\"]>\n}\n\nconst Steps = Data.taggedEnum<StepDefinition>()\n\n/** @internal */\nexport const value = <A>(value: A): Step<A, never> => Steps.Value({ value })\n\n/** @internal */\nexport const next = <S>(state: S): Stream.Stream<Step<never, S>> => Stream.succeed(Steps.Next({ state }))\n\n/** @internal */\n// oxlint-disable-next-line no-unnecessary-type-parameters -- `stop<number>()` types a loop's state where inference can't.\nexport const stop = <S = never>(): Stream.Stream<Step<never, S>> => Stream.succeed(Steps.Stop())\n\nconst isValue = <A, S>(step: Step<A, S>): step is Data.TaggedEnum.Value<Step<A, S>, \"Value\"> =>\n Predicate.isTagged(step, \"Value\")\n\n// A body that ends without `next` or `stop` stops too.\n/** @internal */\nexport const loop = <S, A, E, R>(\n initial: S,\n body: (state: S) => Stream.Stream<Step<A, S>, E, R>\n): Stream.Stream<A, E, R> => {\n const go = (state: S): Stream.Stream<A, E, R> =>\n Stream.suspend(() => {\n let after: Option.Option<S> = Option.none()\n\n return body(state).pipe(\n Stream.tap((step) =>\n Effect.sync(() => (after = Predicate.isTagged(step, \"Next\") ? Option.some(step.state) : after))\n ),\n Stream.filter(isValue),\n Stream.map((step) => step.value),\n Stream.concat(Stream.suspend(() => Option.match(after, { onNone: () => Stream.empty, onSome: go })))\n )\n })\n\n return go(initial)\n}\n\n/** @internal */\nexport const step = <R, S, E2 = never, R2 = never>(\n options: StepOptions<R>,\n then: (result: StepResult) => Stream.Stream<Step<never, S>, E2, R2>\n): Stream.Stream<Step<Event, S>, AiError.AiError | E2, LanguageModel.LanguageModel | R | R2> => {\n const toolkit = options.toolkit ?? Toolkits.empty\n const instructions = Predicate.isString(options.instructions) ? { text: options.instructions } : options.instructions\n\n const after = (result: StepResult) => Stream.suspend(() => then(result))\n\n return Stream.unwrap(\n Effect.gen(function* () {\n const { messages: history, throughSequence } = readHistory(yield* options.history)\n // Calls a crash cut off, with no result: they run again, with the same ids, before the model goes on.\n const cutOff = History.unansweredCalls(history)\n const approvals = yield* TurnApprovals\n\n // Calls the user approved: they run as the model made them, with the same ids, before the model goes on. Not in\n // a loop without their tools.\n const approved = Option.isNone(approvals)\n ? []\n : (yield* approvals.value.approved).filter(\n (call) =>\n toolkit.definitions.some((tool) => tool.name === call.name) && !cutOff.some((cut) => cut.id === call.id)\n )\n\n if (cutOff.length > 0 || approved.length > 0) {\n const pending = [\n ...cutOff.map((call) => ({ call, approved: false })),\n ...approved.map((call) => ({ call, approved: true }))\n ]\n\n return runCalls(toolkit, pending, history, ({ waiting }) =>\n after({ output: undefined, done: waiting, historyLength: history.length + pending.length })\n )\n }\n\n const prompt = Prompt.fromMessages([Prompt.systemMessage({ content: instructions.text }), ...history])\n const name = yield* Effect.serviceOption(Model.ModelName)\n const started = yield* Clock.currentTimeMillis\n\n const response = yield* generate(prompt, toolkit.definitions, options.toolChoice).pipe(\n Telemetry.chatSpan(prompt, instructions.attributes)\n )\n\n const metadata = response.content.find((part) => part.type === \"response-metadata\")\n\n const output = AssistantEvent.ModelOutput.make({\n parts: modelParts(response.content),\n seenThroughSequence: throughSequence,\n model: metadata?.modelId ?? Option.getOrUndefined(name),\n responseId: metadata?.id,\n usage: response.usage,\n durationMillis: (yield* Clock.currentTimeMillis) - started\n })\n\n const calls = toolCalls(output.parts)\n const historyLength = history.length + Prompt.fromResponseParts(output.parts).content.length + calls.length\n\n // A failed call doesn't end the turn: a message that failed never reached the user, so the model reads why. A call\n // that waits for approval does: the turn waits for the user.\n return Stream.make(value(output)).pipe(\n Stream.concat(\n runCalls(\n toolkit,\n calls.map((call) => ({ call, approved: false })),\n history,\n ({ failed, waiting }) =>\n after({\n output,\n done: calls.length === 0 || waiting || (!failed && calls.some(isTurnEnding)),\n historyLength\n })\n )\n )\n )\n })\n )\n}\n\n/** @internal */\nexport const run = <R = never>(\n options: Options<R>\n): Stream.Stream<Event, AiError.AiError, LanguageModel.LanguageModel | R> => {\n const maxSteps = options.maxSteps ?? 16\n\n // Done: stop, unless something arrived while the model was working; then it reads that first.\n const afterDone = (steps: number, historyLength: number) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const read = yield* options.history\n\n return readHistory(read).messages.length > historyLength ? next(steps) : stop<number>()\n })\n )\n\n return loop(0, (steps: number) =>\n step(options, (result) => {\n const taken = result.output === undefined ? steps : steps + 1\n\n if (taken >= maxSteps) return stop<number>()\n\n return result.done ? afterDone(taken, result.historyLength) : next(taken)\n })\n )\n}\n"],"mappings":"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"}
@@ -7,13 +7,15 @@ import * as Prompt from "effect/ai/Prompt";
7
7
  import * as Tool from "effect/ai/Tool";
8
8
  import * as Toolkit from "effect/ai/Toolkit";
9
9
  declare namespace toolkit_d_exports {
10
- export { CallResult, ToolServices, Tools, duplicateNames, empty, failed, fromToolkit, fromToolkitOrEmpty, fromWithHandler, merge, needingApproval, provide, runNative, runOnly };
10
+ export { CallResult, ToolServices, Tools, duplicateNames, empty, failed, fromToolkit, fromToolkitOrEmpty, fromWithHandler, merge, needingApproval, provide, resultIdOf, runNative, runOnly, showing };
11
11
  }
12
12
  /** @internal */
13
13
  export interface CallResult {
14
14
  readonly outcome: ToolOutcome;
15
15
  readonly result: Schema.Json;
16
16
  readonly reason?: string | undefined;
17
+ readonly full?: Schema.Json | undefined;
18
+ readonly resultId?: string | undefined;
17
19
  }
18
20
  /** @internal */
19
21
  export interface Tools<out R = never> {
@@ -50,6 +52,10 @@ export declare const empty: Tools;
50
52
  /** @internal */
51
53
  export declare const merge: <A, B>(self: Tools<A>, that: Tools<B>) => Tools<A | B>;
52
54
  /** @internal */
55
+ export declare const resultIdOf: (callId: string) => string;
56
+ /** @internal */
57
+ export declare const showing: <R>(tools: Tools<R>, names: ReadonlySet<string>) => Tools<R>;
58
+ /** @internal */
53
59
  export declare const provide: <R>(tools: Tools<R>, context: Context.Context<R>) => Tools;
54
60
  /** @internal */
55
61
  export declare const duplicateNames: (tools: Tools<unknown>) => ReadonlyArray<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"toolkit.d.ts","names":[],"sources":["../../src/internal/toolkit.ts"],"mappings":";;;;;;;;;;;;iBAgBiB;WACN,SAAS;WACT,QAAQ,OAAO;WACf;;;iBAKM,UAAU;WAChB,aAAa,cAAc,KAAK;WAChC,MAAM,MAAM,iBAAgC,OAAO,OAAO,mBAAmB;WAE7E,gBACP,MAAM,cACN,SAAS,cAAc,OAAO,aAC3B,OAAO,uBAAuB;;;YAIzB,aAAa,UAAU,eAAe,KAAK,QACnD,QAAQ,KAAK,gBAAgB,QAAQ,KAAK,qBAE1C,QAAQ;;qBAKC,SAAM,iBAAmB,mBAAmB;;qBA2B5C,kBAAe,aAAiB,cAAc,KAAK,SAAO;;qBAiC1D,YAAyC,UAAU,eAAe,KAAK,MAAI,SAAA,QAAA,YAAA,IAAA;;;;;;;;;;MAiCtF,OAAA,OAAA,mBAAA,aAAA;;qBAGW,kBAAmB,UAAU,eAAe,KAAK,MAAI,SACvD,QAAQ,YAAY,OAC5B,MAAM,aAAa;;qBAOT,cAAe,UAAU,eAAe,KAAK,MAAI,SACnD,QAAQ,QAAQ,OACxB,MAAM,aAAa,KAAK,KAAK,YAAY;;qBAS/B,qBAAsB,UAAU,eAAe,KAAK,MAAI,SAC1D,QAAQ,QAAQ,mBACxB,MAAM,aAAa,KAAK,KAAK,YAAY;;qBAG/B,OAAO;;qBAOP,QAAS,GAAG,GAAC,MAAQ,MAAM,IAAE,MAAQ,MAAM,OAAK,MAAM,IAAI;;qBAa1D,UAAW,GAAC,OAAS,MAAM,IAAE,SAAW,QAAQ,QAAQ,OAAK;;qBAO7D,iBAAc,OAAW,mBAAiB;;qBAQ1C,UAAW,GAAC,OAAS,MAAM,IAAE,OAAS,uBAAqB,oBAAoB,MAAM"}
1
+ {"version":3,"file":"toolkit.d.ts","names":[],"sources":["../../src/internal/toolkit.ts"],"mappings":";;;;;;;;;;;;iBAkBiB;WACN,SAAS;WACT,QAAQ,OAAO;WACf;WACA,OAAO,OAAO;WACd;;;iBAKM,UAAU;WAChB,aAAa,cAAc,KAAK;WAChC,MAAM,MAAM,iBAAgC,OAAO,OAAO,mBAAmB;WAE7E,gBACP,MAAM,cACN,SAAS,cAAc,OAAO,aAC3B,OAAO,uBAAuB;;;YAIzB,aAAa,UAAU,eAAe,KAAK,QACnD,QAAQ,KAAK,gBAAgB,QAAQ,KAAK,qBAE1C,QAAQ;;qBAKC,SAAM,iBAAmB,mBAAmB;;qBA2B5C,kBAAe,aAAiB,cAAc,KAAK,SAAO;;qBAiC1D,YAAyC,UAAU,eAAe,KAAK,MAAI,SAAA,QAAA,YAAA,IAAA;;;;;;;;;;MA2CtF,OAAA,OAAA,mBAAA,aAAA;;qBAGW,kBAAmB,UAAU,eAAe,KAAK,MAAI,SACvD,QAAQ,YAAY,OAC5B,MAAM,aAAa;;qBAOT,cAAe,UAAU,eAAe,KAAK,MAAI,SACnD,QAAQ,QAAQ,OACxB,MAAM,aAAa,KAAK,KAAK,YAAY;;qBAS/B,qBAAsB,UAAU,eAAe,KAAK,MAAI,SAC1D,QAAQ,QAAQ,mBACxB,MAAM,aAAa,KAAK,KAAK,YAAY;;qBAG/B,OAAO;;qBAOP,QAAS,GAAG,GAAC,MAAQ,MAAM,IAAE,MAAQ,MAAM,OAAK,MAAM,IAAI;;qBAa1D,aAAU;;qBAuBV,UAAW,GAAC,OAAS,MAAM,IAAE,OAAS,wBAAsB,MAAM;;qBAalE,UAAW,GAAC,OAAS,MAAM,IAAE,SAAW,QAAQ,QAAQ,OAAK;;qBAO7D,iBAAc,OAAW,mBAAiB;;qBAQ1C,UAAW,GAAC,OAAS,MAAM,IAAE,OAAS,uBAAqB,oBAAoB,MAAM"}