effect-assistant 0.5.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 (73) 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 +8 -8
  13. package/dist/AssistantEvent.js +7 -7
  14. package/dist/AssistantEvent.js.map +1 -1
  15. package/dist/AssistantLoop.d.ts +2 -2
  16. package/dist/AssistantLoop.js.map +1 -1
  17. package/dist/ChatLog.d.ts +9 -9
  18. package/dist/ChatLog.d.ts.map +1 -1
  19. package/dist/ChatLog.js +21 -21
  20. package/dist/ChatLog.js.map +1 -1
  21. package/dist/Compaction.d.ts +10 -10
  22. package/dist/Compaction.d.ts.map +1 -1
  23. package/dist/Compaction.js +16 -16
  24. package/dist/Compaction.js.map +1 -1
  25. package/dist/Memory.d.ts +1 -1
  26. package/dist/Memory.js +1 -1
  27. package/dist/Memory.js.map +1 -1
  28. package/dist/Skill.d.ts +1 -1
  29. package/dist/Skill.js +1 -1
  30. package/dist/Skill.js.map +1 -1
  31. package/dist/Specialist.d.ts +1 -1
  32. package/dist/Specialist.js +1 -1
  33. package/dist/Specialist.js.map +1 -1
  34. package/dist/Timeline.d.ts +9 -9
  35. package/dist/Timeline.js +16 -16
  36. package/dist/Timeline.js.map +1 -1
  37. package/dist/ToolApproval.d.ts +1 -1
  38. package/dist/ToolApproval.js +1 -1
  39. package/dist/ToolApproval.js.map +1 -1
  40. package/dist/ToolDiscovery.d.ts +1 -1
  41. package/dist/ToolDiscovery.js +1 -1
  42. package/dist/ToolDiscovery.js.map +1 -1
  43. package/dist/internal/chat.js +4 -4
  44. package/dist/internal/chat.js.map +1 -1
  45. package/dist/internal/events.js +1 -1
  46. package/dist/internal/events.js.map +1 -1
  47. package/dist/internal/history.js +23 -23
  48. package/dist/internal/history.js.map +1 -1
  49. package/dist/internal/loop.d.ts +1 -1
  50. package/dist/internal/loop.js +4 -4
  51. package/dist/internal/loop.js.map +1 -1
  52. package/dist/internal/turns.d.ts.map +1 -1
  53. package/dist/internal/turns.js +18 -18
  54. package/dist/internal/turns.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/Assistant.ts +11 -11
  57. package/src/AssistantActivity.ts +9 -7
  58. package/src/AssistantEngine.ts +3 -3
  59. package/src/AssistantEvent.ts +8 -8
  60. package/src/AssistantLoop.ts +3 -3
  61. package/src/ChatLog.ts +31 -26
  62. package/src/Compaction.ts +21 -21
  63. package/src/Memory.ts +1 -1
  64. package/src/Skill.ts +1 -1
  65. package/src/Specialist.ts +1 -1
  66. package/src/Timeline.ts +22 -22
  67. package/src/ToolApproval.ts +1 -1
  68. package/src/ToolDiscovery.ts +1 -1
  69. package/src/internal/chat.ts +4 -4
  70. package/src/internal/events.ts +1 -1
  71. package/src/internal/history.ts +32 -28
  72. package/src/internal/loop.ts +5 -5
  73. package/src/internal/turns.ts +28 -26
@@ -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> {
@@ -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)])),
@@ -108,7 +108,7 @@ const step = (options, then) => {
108
108
  const instructions = Predicate.isString(options.instructions) ? { text: options.instructions } : options.instructions;
109
109
  const after = (result) => Stream.suspend(() => then(result));
110
110
  return Stream.unwrap(Effect.gen(function* () {
111
- const { messages: history, throughSeq } = readHistory(yield* options.history);
111
+ const { messages: history, throughSequence } = readHistory(yield* options.history);
112
112
  const cutOff = unansweredCalls(history);
113
113
  const approvals = yield* TurnApprovals;
114
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));
@@ -133,7 +133,7 @@ const step = (options, then) => {
133
133
  const metadata = response.content.find((part) => part.type === "response-metadata");
134
134
  const output = ModelOutput.make({
135
135
  parts: modelParts(response.content),
136
- seenThroughSeq: throughSeq,
136
+ seenThroughSequence: throughSequence,
137
137
  model: metadata?.modelId ?? Option.getOrUndefined(name),
138
138
  responseId: metadata?.id,
139
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, 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 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// 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, 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":";;;;;;;;;;;;;;;;;;AAgCA,MAAa,gBAAgB,QAAQ,UAEnC,kCAAkC,EAAE,cAAc,OAAO,KAAK,CAAC;;AAKjE,MAAa,YAAY,QAAQ,UAAyC,8BAA8B,EACtG,cAAc,OAAO,KACvB,CAAC;AAQD,MAAM,eAAe,SACnB,cAAc,OACV;CAAE,UAAU,KAAK;CAAU,iBAAiB,KAAK;AAAgB,IACjE;CAAE,UAAU;CAAM,iBAAiB,KAAA;AAAU;AAmCnD,MAAM,kBAAkB,iBAAyF;CAC/G,OAAO,OAAO,YACZ,YAAY,KAAK,SAAS,CACxB,KAAK,MACL,KAAK,kBAAkB,KAAA,KAAa,KAAK,kBAAkB,QAAQ,OAAO,KAAK,iBAAiB,KAAK,CACvG,CAAC,CACH;CACA,cAAc,OAAO,QAAQ,OAAO,KAAK;AAC3C;AAIA,MAAM,YACJ,QACA,aACA,eAKG,cAAc,aAAa;CAAE;CAAQ,SAAS,eAAe,WAAW;CAAG;AAAW,CAAC;AAE5F,MAAM,aAAa,OAAO,oBAAoBA,SAAwB;AAEtE,MAAM,cAAc,YAClB,QAAQ,SAAS,SACf,OAAO,QAAQ,WAAW,IAAI,CAAC,CAAC,CAAC,QAAQ,YAAY,EAAE,QAAQ,SAAS,eAAe,QAAQ,iBAAiB,CAClH;AAEF,MAAM,aAAa,UACjB,MAAM,SAAS,SAAU,KAAK,SAAS,cAAc,CAAC,IAAI,IAAI,CAAC,CAAE;AAEnE,MAAM,UAAU,OAAO,WAAW,WAAc,OAA0B,MAAmC;CAC3G,MAAM,SAAS,OAAO,OAAO,IAAI,aAAa;EAC5C,MAAM,SAAS,OAAO,MAAM,IAAI,IAAI;EAEpC,OAAOC,WAAqB,OAAO,MAAM;EAEzC,OAAO;CACT,CAAC,CAAC,CAAC,KAAKC,SAAmB,IAAI,CAAC;CAEhC,OAAA,WAAiC,KAAK;EAAE,QAAQ,KAAK;EAAI,MAAM,KAAK;EAAM,GAAG;CAAO,CAAC;AACvF,CAAC;AAGD,MAAM,gBAAgB,OAAO,WAAW,WAAW,MAAmC;CACpF,IAAI,OAAO,OAAO,OAAO,aAAa,GACpC,OAAA,WAAiC,KAAK;EACpC,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,GAAGC,OACD,GAAG,KAAK,KAAK,8IACb,eACF;CACF,CAAC;CAGH,OAAA,kBAAwC,KAAK;EAAE,QAAQ,KAAK;EAAI,MAAM,KAAK;EAAM,QAAQ,KAAK;CAAO,CAAC;AACxG,CAAC;AAgBD,MAAM,eAAe,SAAA,WACO,KAAK;CAC7B,QAAQ,KAAK;CACb,MAAM,KAAK;CACX,GAAGA,OACD,4JAA4J,KAAK,KAAK,+CAA+C,KAAK,KAAK,UAC/N,sBACF;AACF,CAAC;AAGH,MAAM,YACJ,OACA,SACA,SACA,SAEA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,OAAO,QAAQ,UAAU,EAAE,MAAM,eACpD,WAAW,OAAO,QAAQ,KAAK,IAAI,MAAM,cAAc,MAAM,OAAO,CACtE;CAEA,MAAM,UAAU,QAAQ,SAAS,EAAE,QAAQ,MACzC,MAAM,OAAO,OAAO,CAAC,IAAI,CAAC,MAAA,YAAiC,KAAK;EAAE,QAAQ,KAAK;EAAI,MAAM,KAAK;CAAK,CAAC,CAAC,CAAC,CACxG;CAEA,OAAO,OAAO,aAAa,OAAO,CAAC,CAAC,KAClC,OAAO,OACL,OAAO,OACL,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,QAAQ,MAAM,EAAE,WAAW,cAAc,IAAI,CAAC;EAEhE,MAAM,OAAO,OAAO,OAAO,QACzB,UACC,EAAE,QAAQ,MACT,MAAM,OAAO,OACT,QAAQ,OAAO,IAAI,IACnB,YACE,cAAc,IAAI,IAClB,OAAO,QAAQ,YAAY,IAAI,CAAC,GACxC,EAAE,aAAa,YAAY,CAC7B;EAEA,OAAO,OAAO,aAAa,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAC1C,OAAO,OACL,KAAK;GACH,QAAQ,KAAK,KAAK,SAAS;GAC3B,SAAS,KAAK,MAAM,UAAU,UAAU,SAAS,OAAO,mBAAmB,CAAC;EAC9E,CAAC,CACH,CACF;CACF,CAAC,CACH,CACF,CACF;AACF,CAAC,CACH;AAEF,MAAM,aAAa,UAAiB,UAAU,SAAS,OAAO,YAAY,KAAK,MAAM,YAAY;AAajG,MAAM,QAAQ,KAAK,WAA2B;;AAG9C,MAAa,SAAY,UAA6B,MAAM,MAAM,EAAE,MAAM,CAAC;;AAG3E,MAAa,QAAW,UAA4C,OAAO,QAAQ,MAAM,KAAK,EAAE,MAAM,CAAC,CAAC;;AAIxG,MAAa,aAAuD,OAAO,QAAQ,MAAM,KAAK,CAAC;AAE/F,MAAM,WAAiB,SACrB,UAAU,SAAS,MAAM,OAAO;;AAIlC,MAAa,QACX,SACA,SAC2B;CAC3B,MAAM,MAAM,UACV,OAAO,cAAc;EACnB,IAAI,QAA0B,OAAO,KAAK;EAE1C,OAAO,KAAK,KAAK,CAAC,CAAC,KACjB,OAAO,KAAK,SACV,OAAO,WAAY,QAAQ,UAAU,SAAS,MAAM,MAAM,IAAI,OAAO,KAAK,KAAK,KAAK,IAAI,KAAM,CAChG,GACA,OAAO,OAAO,OAAO,GACrB,OAAO,KAAK,SAAS,KAAK,KAAK,GAC/B,OAAO,OAAO,OAAO,cAAc,OAAO,MAAM,OAAO;GAAE,cAAc,OAAO;GAAO,QAAQ;EAAG,CAAC,CAAC,CAAC,CACrG;CACF,CAAC;CAEH,OAAO,GAAG,OAAO;AACnB;;AAGA,MAAa,QACX,SACA,SAC8F;CAC9F,MAAM,UAAU,QAAQ,WAAWC;CACnC,MAAM,eAAe,UAAU,SAAS,QAAQ,YAAY,IAAI,EAAE,MAAM,QAAQ,aAAa,IAAI,QAAQ;CAEzG,MAAM,SAAS,WAAuB,OAAO,cAAc,KAAK,MAAM,CAAC;CAEvE,OAAO,OAAO,OACZ,OAAO,IAAI,aAAa;EACtB,MAAM,EAAE,UAAU,SAAS,oBAAoB,YAAY,OAAO,QAAQ,OAAO;EAEjF,MAAM,SAASC,gBAAwB,OAAO;EAC9C,MAAM,YAAY,OAAO;EAIzB,MAAM,WAAW,OAAO,OAAO,SAAS,IACpC,CAAC,KACA,OAAO,UAAU,MAAM,SAAA,CAAU,QAC/B,SACC,QAAQ,YAAY,MAAM,SAAS,KAAK,SAAS,KAAK,IAAI,KAAK,CAAC,OAAO,MAAM,QAAQ,IAAI,OAAO,KAAK,EAAE,CAC3G;EAEJ,IAAI,OAAO,SAAS,KAAK,SAAS,SAAS,GAAG;GAC5C,MAAM,UAAU,CACd,GAAG,OAAO,KAAK,UAAU;IAAE;IAAM,UAAU;GAAM,EAAE,GACnD,GAAG,SAAS,KAAK,UAAU;IAAE;IAAM,UAAU;GAAK,EAAE,CACtD;GAEA,OAAO,SAAS,SAAS,SAAS,UAAU,EAAE,cAC5C,MAAM;IAAE,QAAQ,KAAA;IAAW,MAAM;IAAS,eAAe,QAAQ,SAAS,QAAQ;GAAO,CAAC,CAC5F;EACF;EAEA,MAAM,SAAS,OAAO,aAAa,CAAC,OAAO,cAAc,EAAE,SAAS,aAAa,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC;EACrG,MAAM,OAAO,OAAO,OAAO,cAAc,MAAM,SAAS;EACxD,MAAM,UAAU,OAAO,MAAM;EAE7B,MAAM,WAAW,OAAO,SAAS,QAAQ,QAAQ,aAAa,QAAQ,UAAU,CAAC,CAAC,KAChFC,SAAmB,QAAQ,aAAa,UAAU,CACpD;EAEA,MAAM,WAAW,SAAS,QAAQ,MAAM,SAAS,KAAK,SAAS,mBAAmB;EAElF,MAAM,SAAA,YAAoC,KAAK;GAC7C,OAAO,WAAW,SAAS,OAAO;GAClC,qBAAqB;GACrB,OAAO,UAAU,WAAW,OAAO,eAAe,IAAI;GACtD,YAAY,UAAU;GACtB,OAAO,SAAS;GAChB,iBAAiB,OAAO,MAAM,qBAAqB;EACrD,CAAC;EAED,MAAM,QAAQ,UAAU,OAAO,KAAK;EACpC,MAAM,gBAAgB,QAAQ,SAAS,OAAO,kBAAkB,OAAO,KAAK,CAAC,CAAC,QAAQ,SAAS,MAAM;EAIrG,OAAO,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC,CAAC,KAChC,OAAO,OACL,SACE,SACA,MAAM,KAAK,UAAU;GAAE;GAAM,UAAU;EAAM,EAAE,GAC/C,UACC,EAAE,QAAQ,cACT,MAAM;GACJ;GACA,MAAM,MAAM,WAAW,KAAK,WAAY,CAAC,UAAU,MAAM,KAAK,YAAY;GAC1E;EACF,CAAC,CACL,CACF,CACF;CACF,CAAC,CACH;AACF;;AAGA,MAAa,OACX,YAC2E;CAC3E,MAAM,WAAW,QAAQ,YAAY;CAGrC,MAAM,aAAa,OAAe,kBAChC,OAAO,OACL,OAAO,IAAI,aAAa;EACtB,MAAM,OAAO,OAAO,QAAQ;EAE5B,OAAO,YAAY,IAAI,CAAC,CAAC,SAAS,SAAS,gBAAgB,KAAK,KAAK,IAAI,KAAa;CACxF,CAAC,CACH;CAEF,OAAO,KAAK,IAAI,UACd,KAAK,UAAU,WAAW;EACxB,MAAM,QAAQ,OAAO,WAAW,KAAA,IAAY,QAAQ,QAAQ;EAE5D,IAAI,SAAS,UAAU,OAAO,KAAa;EAE3C,OAAO,OAAO,OAAO,UAAU,OAAO,OAAO,aAAa,IAAI,KAAK,KAAK;CAC1E,CAAC,CACH;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"turns.d.ts","names":[],"sources":["../../src/internal/turns.ts"],"mappings":";;;;;;;;;;;;;;;;;;qBAmCa;;qBAGA;;qBAIA;;iBAII;WACN;WACA,QAAQ;WACR,aAAa,cAAc;WAC3B,KAAK;;;YAKJ,eACV,MAAM,cACH,OAAO,OAAO,gBAA8B,eAAyB,cAAc;;iBAKvE;WACN,QAAQ;WACR,SAAS,OAAO,OAAO;WACvB,QAAQ;;;iBAIF;WACN,YAAY;WACZ,OAAO,QAAQ,QAAQ,cAAc;WACrC,OAAO,MAAM,cAAc,OAAO,OAAO,oBAAoB,MAAM,QAAQ;WAC3E,aAAa;aACX,YAAY;aACZ,OAAO;;WAET,UAAU,MAAe;;;qBAIvB,WAAQ,YAAA,YAAA;WAEO,MAAA;MAiC1B,OAAA,OAAA,qBAAA,cAAA;;qBA8NW,MAAG;WAEW,cAAA;WACL,SAAA;WACH,MAAA;WACI,UAAA,QAAQ,QAAQ,eAAyB,cAAc;WAEtD,WAAA,OAAO;MA8D9B,OAAA"}
1
+ {"version":3,"file":"turns.d.ts","names":[],"sources":["../../src/internal/turns.ts"],"mappings":";;;;;;;;;;;;;;;;;;qBAmCa;;qBAGA;;qBAIA;;iBAII;WACN;WACA,QAAQ;WACR,aAAa,cAAc;WAC3B,KAAK;;;YAKJ,eACV,MAAM,cACH,OAAO,OAAO,gBAA8B,eAAyB,cAAc;;iBAKvE;WACN,QAAQ;WACR,SAAS,OAAO,OAAO;WACvB,QAAQ;;;iBAIF;WACN,YAAY;WACZ,OAAO,QAAQ,QAAQ,cAAc;WACrC,OAAO,MAAM,cAAc,OAAO,OAAO,oBAAoB,MAAM,QAAQ;WAC3E,aAAa;aACX,YAAY;aACZ,OAAO;;WAET,UAAU,MAAe;;;qBAIvB,WAAQ,YAAA,YAAA;WAEO,MAAA;MAiC1B,OAAA,OAAA,qBAAA,cAAA;;qBA+NW,MAAG;WAEW,cAAA;WACL,SAAA;WACH,MAAA;WACI,UAAA,QAAQ,QAAQ,eAAyB,cAAc;WAEtD,WAAA,OAAO;MA+D9B,OAAA"}
@@ -63,9 +63,9 @@ const approvedCalls = (entries) => [...fromEntries(entries).values()].flatMap(({
63
63
  params: request.params,
64
64
  providerExecuted: false
65
65
  })] : []);
66
- const lastSeq = (entries, otherwise) => entries.at(-1)?.seq ?? otherwise;
67
- const closeOpenCalls = Effect.fnUntraced(function* (chat, startSeq) {
68
- const entries = (yield* chat.entries).filter(({ seq }) => seq > startSeq);
66
+ const lastSequence = (entries, otherwise) => entries.at(-1)?.sequence ?? otherwise;
67
+ const closeOpenCalls = Effect.fnUntraced(function* (chat, startSequence) {
68
+ const entries = (yield* chat.entries).filter(({ sequence }) => sequence > startSequence);
69
69
  const started = new Set(entries.flatMap(({ event }) => Predicate.isTagged(event, "ToolStarted") ? [event.callId] : []));
70
70
  const open = unansweredCalls(fromEntries$1(entries));
71
71
  if (open.length === 0) return;
@@ -82,24 +82,24 @@ const closeOpenCalls = Effect.fnUntraced(function* (chat, startSeq) {
82
82
  }
83
83
  })));
84
84
  });
85
- const handledThrough = (entries, startSeq) => entries.reduce((through, { seq, event }) => seq > startSeq && isLibraryEvent(event) && Predicate.isTagged(event, "ModelOutput") && event.seenThroughSeq !== void 0 ? Math.max(through, event.seenThroughSeq) : through, startSeq);
85
+ const handledThrough = (entries, startSequence) => entries.reduce((through, { sequence, event }) => sequence > startSequence && isLibraryEvent(event) && Predicate.isTagged(event, "ModelOutput") && event.seenThroughSequence !== void 0 ? Math.max(through, event.seenThroughSequence) : through, startSequence);
86
86
  const finishTurn = Effect.fnUntraced(function* (chat, read, ranLoop) {
87
87
  const entries = yield* chat.entries;
88
88
  const end = ranLoop && owesReply(entries, read) && !unreadAfter(entries, read) ? TurnFailed.make({
89
- throughSeq: read,
89
+ throughSequence: read,
90
90
  message: silentTurnMessage
91
- }) : TurnCompleted.make({ throughSeq: read });
91
+ }) : TurnCompleted.make({ throughSequence: read });
92
92
  yield* chat.append([end]);
93
93
  });
94
94
  const failTurn = Effect.fnUntraced(function* (chat, cause) {
95
95
  yield* Effect.logError("assistant turn failed", cause);
96
- const last = lastSeq(yield* chat.entries, 0);
96
+ const last = lastSequence(yield* chat.entries, 0);
97
97
  yield* chat.append([TurnFailed.make({
98
- throughSeq: last,
98
+ throughSequence: last,
99
99
  message: failureMessage(cause)
100
100
  })]);
101
101
  });
102
- const makeTurnApi = (registration, chat, startSeq, checkOpen, ranLoop) => {
102
+ const makeTurnApi = (registration, chat, startSequence, checkOpen, ranLoop) => {
103
103
  const { definition, builtins } = registration;
104
104
  const { app } = definition;
105
105
  const shownTools = toolNames(definition.blocks);
@@ -109,13 +109,13 @@ const makeTurnApi = (registration, chat, startSeq, checkOpen, ranLoop) => {
109
109
  const all = yield* chat.entries;
110
110
  return {
111
111
  messages: fromEntries$1(view(decodeEntries(app, all))),
112
- throughSeq: lastSeq(all, startSeq)
112
+ throughSequence: lastSequence(all, startSequence)
113
113
  };
114
114
  });
115
115
  const instructions = (given) => mapText(given, (text) => `${protocol}\n\n${text}`);
116
116
  const replyPass = (options) => Stream.unwrap(Effect.gen(function* () {
117
117
  const all = yield* chat.entries;
118
- const through = handledThrough(all, startSeq);
118
+ const through = handledThrough(all, startSequence);
119
119
  if (unreadAfter(all, through)) return Stream.empty;
120
120
  const owed = owesReply(all, through);
121
121
  const undelivered = !owed && owesDelivery(all, through);
@@ -129,7 +129,7 @@ const makeTurnApi = (registration, chat, startSeq, checkOpen, ranLoop) => {
129
129
  const entries = yield* chat.entries;
130
130
  return {
131
131
  messages: [...fromEntries$1(options.view(decodeEntries(app, entries))), userText(nudge)],
132
- throughSeq: lastSeq(entries, startSeq)
132
+ throughSequence: lastSequence(entries, startSequence)
133
133
  };
134
134
  }),
135
135
  maxSteps: 2,
@@ -155,7 +155,7 @@ const makeTurnApi = (registration, chat, startSeq, checkOpen, ranLoop) => {
155
155
  history,
156
156
  instructions,
157
157
  replyIfOwed,
158
- loop: (options) => {
158
+ run: (options) => {
159
159
  const { system, toolkit, view } = prepare(options);
160
160
  const twice = duplicateNames(toolkit);
161
161
  const loop = twice.length > 0 ? Stream.die(`Tool names used twice, like one named after a built-in: ${twice.join(", ")}`) : run$1({
@@ -187,18 +187,18 @@ const run = Effect.fnUntraced(function* (options) {
187
187
  traceId: id
188
188
  })
189
189
  }))]);
190
- const startSeq = lastSeq(started, 0);
190
+ const startSequence = lastSequence(started, 0);
191
191
  const ranLoop = yield* Ref.make(false);
192
- const turn = makeTurnApi(options.registration, chat, startSeq, options.checkOpen, ranLoop);
192
+ const turn = makeTurnApi(options.registration, chat, startSequence, options.checkOpen, ranLoop);
193
193
  const record = (event) => chat.append([event]);
194
- yield* options.handler(turn).pipe(Stream.runForEach(record), Effect.onExit((exit) => Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSeq)), Effect.provideService(TurnTools, Option.some(provide(options.registration.builtins, options.services))), Effect.provideService(TurnApprovals, Option.some({ approved: Effect.map(chat.entries, approvedCalls) })), Effect.provideContext(options.services), Effect.matchCauseEffect({
194
+ yield* options.handler(turn).pipe(Stream.runForEach(record), Effect.onExit((exit) => Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSequence)), Effect.provideService(TurnTools, Option.some(provide(options.registration.builtins, options.services))), Effect.provideService(TurnApprovals, Option.some({ approved: Effect.map(chat.entries, approvedCalls) })), Effect.provideContext(options.services), Effect.matchCauseEffect({
195
195
  onSuccess: Effect.fnUntraced(function* () {
196
196
  const all = yield* chat.entries;
197
- yield* finishTurn(chat, handledThrough(all, startSeq), yield* Ref.get(ranLoop));
197
+ yield* finishTurn(chat, handledThrough(all, startSequence), yield* Ref.get(ranLoop));
198
198
  }),
199
199
  onFailure: (cause) => failTurn(chat, cause)
200
200
  }));
201
- const shown = (yield* chat.entries).flatMap(({ event, seq }) => seq > startSeq && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed")) ? [event] : []);
201
+ const shown = (yield* chat.entries).flatMap(({ event, sequence }) => sequence > startSequence && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed")) ? [event] : []);
202
202
  yield* agentOutput(shown);
203
203
  }, (effect, options) => effect.pipe(agentSpan(options.registration.definition.name, {
204
204
  chatId: options.chat.chatId,
@@ -1 +1 @@
1
- {"version":3,"file":"turns.js","names":["Toolkits.merge","Tools.makeMessaging","Specialists.toolkit","ToolApproval.fromEntries","History.unansweredCalls","History.fromEntries","History.mayHaveRunOutput","History.notRunOutput","AssistantEvent.isLibraryEvent","History.owesReply","History.unreadAfter","BlockKit.toolNames","AppEvents.decodeEntries","Instructions.protocol","History.owesDelivery","Loop.run","Toolkits.runOnly","Tools.sendUserMessageName","Tools.waitToolName","History.userText","Loop.TurnApprovals","Toolkits.showing","Toolkits.fromToolkitOrEmpty","Toolkits.duplicateNames","Telemetry.agentInput","History.unhandledInputs","Telemetry.currentTraceId","Loop.TurnTools","Toolkits.provide","Telemetry.agentOutput","Telemetry.agentSpan"],"sources":["../../src/internal/turns.ts"],"sourcesContent":["import type * as LanguageModel from \"effect/ai/LanguageModel\"\nimport * as Response from \"effect/ai/Response\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport * as Cause from \"effect/Cause\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport { identity } from \"effect/Function\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Ref from \"effect/Ref\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Assistant from \"../Assistant.ts\"\nimport type { ChatId } from \"../ChatId.ts\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type * as AssistantLoop from \"../AssistantLoop.ts\"\nimport * as Loop from \"./loop.ts\"\nimport * as BlockKit from \"../BlockKit.ts\"\nimport type { CurrentChat } from \"../CurrentChat.ts\"\nimport * as Instructions from \"../Instructions.ts\"\nimport * as ToolApproval from \"../ToolApproval.ts\"\nimport type * as Specialist from \"../Specialist.ts\"\nimport * as AppEvents from \"./events.ts\"\nimport * as History from \"./history.ts\"\nimport { mapText } from \"./instructions.ts\"\nimport { toJsonText } from \"./schema.ts\"\nimport * as Specialists from \"./specialists.ts\"\nimport * as Telemetry from \"./telemetry.ts\"\nimport * as Toolkits from \"./toolkit.ts\"\nimport type { Writer } from \"./writer.ts\"\nimport * as Tools from \"./tools.ts\"\n\n/** @internal */\nexport const silentTurnMessage = \"I finished without sending a reply.\"\n\n/** @internal */\nexport const defaultReplyNudge =\n \"You haven't replied to the user's latest message yet. Reply now with send_user_message.\"\n\n/** @internal */\nexport const deliveryNudge =\n \"You told the user you're on it, but stopped without giving them the result. Send it now with send_user_message; if part of it is still running, say what and that you'll report back. If they already have everything, call wait.\"\n\n/** @internal */\nexport interface Definition {\n readonly name: string\n readonly blocks: BlockKit.BlockKit | undefined\n readonly specialists: ReadonlyArray<Specialist.Any>\n readonly app: AppEvents.AppEvents\n}\n\n// Once its build ran the layer's services are provided, so its streams need only what a turn provides.\n/** @internal */\nexport type TurnHandler = (\n turn: Assistant.Turn<any>\n) => Stream.Stream<AssistantLoop.Event, unknown, Assistant.TurnServices | LanguageModel.LanguageModel>\n\n// The chat `CurrentChat` gives, with its entries typed as the assistant's decode returns them: the assistant's typed\n// chat is this one, so it needs no check of its own.\n/** @internal */\nexport interface TypedChat {\n readonly chatId: ChatId\n readonly entries: Effect.Effect<AppEvents.DecodedEntries>\n readonly append: CurrentChat[\"Service\"][\"append\"]\n}\n\n/** @internal */\nexport interface Registration {\n readonly definition: Definition\n readonly model: Context.Context<LanguageModel.LanguageModel>\n readonly open: (chat: TypedChat) => Effect.Effect<TurnHandler, never, Scope.Scope | CurrentChat>\n readonly specialists: ReadonlyArray<{\n readonly specialist: Specialist.Any\n readonly built: Specialist.Built\n }>\n readonly builtins: Toolkits.Tools<Assistant.TurnServices>\n}\n\n/** @internal */\nexport const register = Effect.fnUntraced(function* (\n definition: Definition,\n options: { readonly open: Registration[\"open\"] }\n) {\n const model = yield* Effect.context<LanguageModel.LanguageModel>()\n\n const specialists = yield* Effect.forEach(\n definition.specialists,\n Effect.fnUntraced(function* (specialist) {\n const built = yield* Effect.serviceOption(specialist.Handler)\n\n if (Option.isNone(built)) {\n return yield* Effect.die(\n `No handler for the ${specialist.name} specialist: provide ${specialist.name}.toLayer.`\n )\n }\n\n return { specialist, built: built.value }\n })\n )\n\n const builtins = Toolkits.merge(\n yield* Tools.makeMessaging(definition.blocks),\n yield* Specialists.toolkit(definition.specialists)\n )\n\n const registration: Registration = {\n definition,\n model,\n open: options.open,\n specialists,\n builtins\n }\n\n return registration\n})\n\nconst ProviderError = Schema.Struct({ _tag: Schema.optional(Schema.String), raw: Schema.Unknown })\n\n// Provider errors keep their response body.\nconst failureMessage = (cause: Cause.Cause<unknown>): string =>\n Option.match(Schema.decodeUnknownOption(ProviderError)(Cause.squash(cause)), {\n onNone: () => Cause.pretty(cause),\n onSome: ({ _tag, raw }) => `${_tag ?? \"Error\"}: ${Predicate.isString(raw) ? raw : toJsonText(raw)}`\n })\n\n// Approved, and not run yet: also one a crash cut off while it ran, which runs again with the same id.\nconst approvedCalls = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyArray<AssistantEvent.ToolCallPart> =>\n [...ToolApproval.fromEntries(entries).values()].flatMap(({ request, status, outcome }) =>\n status === \"approved\" && outcome === undefined\n ? [\n Response.makePart(\"tool-call\", {\n id: request.callId,\n name: request.tool,\n params: request.params,\n providerExecuted: false\n })\n ]\n : []\n )\n\nconst lastSeq = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) => entries.at(-1)?.seq ?? otherwise\n\n// The handler's stream can end between a step's output and its tools, or while they run. An interrupted turn (a\n// shutdown) leaves its calls open instead: the next turn runs them again.\nconst closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSeq: number) {\n const entries = (yield* chat.entries).filter(({ seq }) => seq > startSeq)\n\n const started = new Set(\n entries.flatMap(({ event }) => (Predicate.isTagged(event, \"ToolStarted\") ? [event.callId] : []))\n )\n\n const open = History.unansweredCalls(History.fromEntries(entries))\n\n if (open.length === 0) return\n\n yield* chat.append(\n open.map((call) =>\n AssistantEvent.ToolResult.make({\n callId: call.id,\n tool: call.name,\n outcome: \"failure\",\n ...(started.has(call.id)\n ? { result: History.mayHaveRunOutput, reason: \"MayHaveRun\" }\n : { result: History.notRunOutput, reason: \"NotRun\" })\n })\n )\n )\n})\n\n// The inputs before the turn's `TurnStarted` (at `startSeq`) woke it; past that, what its model outputs record\n// reading.\nconst handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, startSeq: number) =>\n entries.reduce(\n (through, { seq, event }) =>\n seq > startSeq &&\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"ModelOutput\") &&\n event.seenThroughSeq !== undefined\n ? Math.max(through, event.seenThroughSeq)\n : through,\n startSeq\n )\n\n// A turn that ran the loop fails visibly when the user's latest input got no reply; an input that arrived after what\n// its model read gets the next turn, which replies. A handler that ended without the loop decided that itself.\nconst finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, ranLoop: boolean) {\n const entries = yield* chat.entries\n\n const end =\n ranLoop && History.owesReply(entries, read) && !History.unreadAfter(entries, read)\n ? AssistantEvent.TurnFailed.make({ throughSeq: read, message: silentTurnMessage })\n : AssistantEvent.TurnCompleted.make({ throughSeq: read })\n\n yield* chat.append([end])\n})\n\n// Everything stored so far counts as read, so a failing input isn't retried forever.\nconst failTurn = Effect.fnUntraced(function* (chat: Writer, cause: Cause.Cause<unknown>) {\n yield* Effect.logError(\"assistant turn failed\", cause)\n\n const last = lastSeq(yield* chat.entries, 0)\n\n yield* chat.append([AssistantEvent.TurnFailed.make({ throughSeq: last, message: failureMessage(cause) })])\n})\n\nconst makeTurnApi = (\n registration: Registration,\n chat: Writer,\n startSeq: number,\n checkOpen: Effect.Effect<void>,\n ranLoop: Ref.Ref<boolean>\n): Assistant.Turn<any> => {\n const { definition, builtins } = registration\n const { app } = definition\n const shownTools = BlockKit.toolNames(definition.blocks)\n\n const running = <A, E, R>(stream: Stream.Stream<A, E, R>) =>\n Stream.unwrap(Ref.set(ranLoop, true).pipe(Effect.as(stream)))\n\n // With how far into the chat it goes: the step records it on the model's output.\n // Read before every model call: a chat the app deleted meanwhile stops here.\n const history = Effect.fnUntraced(function* (view: Assistant.HistoryView<any> = identity) {\n yield* checkOpen\n\n const all = yield* chat.entries\n\n const read: AssistantLoop.ChatHistory = {\n messages: History.fromEntries(view(AppEvents.decodeEntries(app, all))),\n throughSeq: lastSeq(all, startSeq)\n }\n\n return read\n })\n\n const instructions = (given: string | Instructions.Resolved) =>\n mapText(given, (text) => `${Instructions.protocol}\\n\\n${text}`)\n\n // The loop's own prompt and tools, so the prompt stays cached, with the nudge at the end.\n const replyPass = <R>(options: {\n readonly instructions: Instructions.Resolved\n readonly toolkit: Toolkits.Tools<R>\n readonly view: Assistant.HistoryView<any>\n readonly nudge: string | undefined\n }) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const all = yield* chat.entries\n const through = handledThrough(all, startSeq)\n\n // Something arrived after what the loop's model read: the next turn reads it with the full loop, and replies.\n if (History.unreadAfter(all, through)) return Stream.empty\n\n const owed = History.owesReply(all, through)\n const undelivered = !owed && History.owesDelivery(all, through)\n\n if (!owed && !undelivered) return Stream.empty\n\n const nudge = undelivered ? deliveryNudge : (options.nudge ?? defaultReplyNudge)\n\n return Loop.run({\n instructions: options.instructions,\n // A reply that's owed can only be sent; a result it may have given may also be for nobody.\n toolkit: Toolkits.runOnly(\n options.toolkit,\n owed ? [Tools.sendUserMessageName] : [Tools.sendUserMessageName, Tools.waitToolName],\n \"Not now: reply to the user with send_user_message.\"\n ),\n history: Effect.gen(function* () {\n yield* checkOpen\n\n const entries = yield* chat.entries\n\n return {\n messages: [\n ...History.fromEntries(options.view(AppEvents.decodeEntries(app, entries))),\n History.userText(nudge)\n ],\n throughSeq: lastSeq(entries, startSeq)\n }\n }),\n // A second step, for a reply after a call it was refused.\n maxSteps: 2,\n // The pass runs because the model wrote text of its own instead of replying, which reaches nobody: it has to\n // call a tool now. Changing the choice costs a read of the history at full price, on these rare turns only.\n toolChoice: \"required\"\n // The loop ran the approved calls; a pass that can only reply runs none.\n }).pipe(Stream.provideService(Loop.TurnApprovals, Option.none()))\n })\n )\n\n const replyIfOwed = (options: Assistant.ReplyOptions) =>\n running(\n replyPass({\n instructions: instructions(options.instructions),\n toolkit: builtins,\n view: identity,\n nudge: options.nudge\n })\n )\n\n const prepare = <T extends Record<string, Tool.Any>>(options: Assistant.LoopOptions<T, any>) => {\n const own = Toolkits.showing(Toolkits.fromToolkitOrEmpty(options.toolkit), shownTools)\n\n return {\n system: instructions(options.instructions),\n toolkit: Toolkits.merge(builtins, own),\n view: options.history ?? identity\n }\n }\n\n return {\n chatId: chat.chatId,\n history,\n instructions,\n replyIfOwed,\n loop: (options) => {\n const { system, toolkit, view } = prepare(options)\n const twice = Toolkits.duplicateNames(toolkit)\n\n const loop =\n twice.length > 0\n ? Stream.die(`Tool names used twice, like one named after a built-in: ${twice.join(\", \")}`)\n : Loop.run({\n instructions: system,\n toolkit,\n history: history(view),\n maxSteps: options.maxSteps ?? 16\n })\n\n return running(Stream.concat(loop, replyPass({ instructions: system, toolkit, view, nudge: options.replyNudge })))\n }\n }\n}\n\n// Every event the handler's loop emits is recorded before the loop is pulled again.\n/** @internal */\nexport const run = Effect.fnUntraced(\n function* (options: {\n readonly registration: Registration\n readonly handler: TurnHandler\n readonly chat: Writer\n readonly services: Context.Context<Assistant.TurnServices | LanguageModel.LanguageModel>\n // A chat the app deleted is deleted here, and the turn stops.\n readonly checkOpen: Effect.Effect<void>\n }) {\n const { chat } = options\n const before = yield* chat.entries\n\n yield* Telemetry.agentInput(History.unhandledInputs(before))\n\n const assistant = options.registration.definition.name\n const traceId = yield* Telemetry.currentTraceId\n\n const started = yield* chat.append([\n AssistantEvent.TurnStarted.make(\n Option.match(traceId, {\n onNone: () => ({ assistant }),\n onSome: (id) => ({ assistant, traceId: id })\n })\n )\n ])\n\n const startSeq = lastSeq(started, 0)\n const ranLoop = yield* Ref.make(false)\n const turn = makeTurnApi(options.registration, chat, startSeq, options.checkOpen, ranLoop)\n\n const record = (event: AssistantLoop.Event) => chat.append([event])\n\n yield* options.handler(turn).pipe(\n Stream.runForEach(record),\n Effect.onExit((exit) => (Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSeq))),\n // A loop the app writes gets the assistant's own tools too, with this turn's services.\n Effect.provideService(\n Loop.TurnTools,\n Option.some(Toolkits.provide(options.registration.builtins, options.services))\n ),\n // A turn's loop waits for the user's approval, and runs the calls they approved.\n Effect.provideService(Loop.TurnApprovals, Option.some({ approved: Effect.map(chat.entries, approvedCalls) })),\n Effect.provideContext(options.services),\n Effect.matchCauseEffect({\n onSuccess: Effect.fnUntraced(function* () {\n const all = yield* chat.entries\n\n yield* finishTurn(chat, handledThrough(all, startSeq), yield* Ref.get(ranLoop))\n }),\n onFailure: (cause) => failTurn(chat, cause)\n })\n )\n\n // The trace's output is what the user saw this turn, and a failure if any.\n const shown = (yield* chat.entries).flatMap(({ event, seq }) =>\n seq > startSeq && (Predicate.isTagged(event, \"AssistantMessage\") || Predicate.isTagged(event, \"TurnFailed\"))\n ? [event]\n : []\n )\n\n yield* Telemetry.agentOutput(shown)\n },\n (effect, options) =>\n effect.pipe(\n Telemetry.agentSpan(options.registration.definition.name, {\n chatId: options.chat.chatId,\n root: true\n })\n )\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"turns.js","names":["Toolkits.merge","Tools.makeMessaging","Specialists.toolkit","ToolApproval.fromEntries","History.unansweredCalls","History.fromEntries","History.mayHaveRunOutput","History.notRunOutput","AssistantEvent.isLibraryEvent","History.owesReply","History.unreadAfter","BlockKit.toolNames","AppEvents.decodeEntries","Instructions.protocol","History.owesDelivery","Loop.run","Toolkits.runOnly","Tools.sendUserMessageName","Tools.waitToolName","History.userText","Loop.TurnApprovals","Toolkits.showing","Toolkits.fromToolkitOrEmpty","Toolkits.duplicateNames","Telemetry.agentInput","History.unhandledInputs","Telemetry.currentTraceId","Loop.TurnTools","Toolkits.provide","Telemetry.agentOutput","Telemetry.agentSpan"],"sources":["../../src/internal/turns.ts"],"sourcesContent":["import type * as LanguageModel from \"effect/ai/LanguageModel\"\nimport * as Response from \"effect/ai/Response\"\nimport type * as Tool from \"effect/ai/Tool\"\nimport * as Cause from \"effect/Cause\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport { identity } from \"effect/Function\"\nimport * as Option from \"effect/Option\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Ref from \"effect/Ref\"\nimport * as Schema from \"effect/Schema\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Assistant from \"../Assistant.ts\"\nimport type { ChatId } from \"../ChatId.ts\"\nimport * as AssistantEvent from \"../AssistantEvent.ts\"\nimport type * as AssistantLoop from \"../AssistantLoop.ts\"\nimport * as Loop from \"./loop.ts\"\nimport * as BlockKit from \"../BlockKit.ts\"\nimport type { CurrentChat } from \"../CurrentChat.ts\"\nimport * as Instructions from \"../Instructions.ts\"\nimport * as ToolApproval from \"../ToolApproval.ts\"\nimport type * as Specialist from \"../Specialist.ts\"\nimport * as AppEvents from \"./events.ts\"\nimport * as History from \"./history.ts\"\nimport { mapText } from \"./instructions.ts\"\nimport { toJsonText } from \"./schema.ts\"\nimport * as Specialists from \"./specialists.ts\"\nimport * as Telemetry from \"./telemetry.ts\"\nimport * as Toolkits from \"./toolkit.ts\"\nimport type { Writer } from \"./writer.ts\"\nimport * as Tools from \"./tools.ts\"\n\n/** @internal */\nexport const silentTurnMessage = \"I finished without sending a reply.\"\n\n/** @internal */\nexport const defaultReplyNudge =\n \"You haven't replied to the user's latest message yet. Reply now with send_user_message.\"\n\n/** @internal */\nexport const deliveryNudge =\n \"You told the user you're on it, but stopped without giving them the result. Send it now with send_user_message; if part of it is still running, say what and that you'll report back. If they already have everything, call wait.\"\n\n/** @internal */\nexport interface Definition {\n readonly name: string\n readonly blocks: BlockKit.BlockKit | undefined\n readonly specialists: ReadonlyArray<Specialist.Any>\n readonly app: AppEvents.AppEvents\n}\n\n// Once its build ran the layer's services are provided, so its streams need only what a turn provides.\n/** @internal */\nexport type TurnHandler = (\n turn: Assistant.Turn<any>\n) => Stream.Stream<AssistantLoop.Event, unknown, Assistant.TurnServices | LanguageModel.LanguageModel>\n\n// The chat `CurrentChat` gives, with its entries typed as the assistant's decode returns them: the assistant's typed\n// chat is this one, so it needs no check of its own.\n/** @internal */\nexport interface TypedChat {\n readonly chatId: ChatId\n readonly entries: Effect.Effect<AppEvents.DecodedEntries>\n readonly append: CurrentChat[\"Service\"][\"append\"]\n}\n\n/** @internal */\nexport interface Registration {\n readonly definition: Definition\n readonly model: Context.Context<LanguageModel.LanguageModel>\n readonly open: (chat: TypedChat) => Effect.Effect<TurnHandler, never, Scope.Scope | CurrentChat>\n readonly specialists: ReadonlyArray<{\n readonly specialist: Specialist.Any\n readonly built: Specialist.Built\n }>\n readonly builtins: Toolkits.Tools<Assistant.TurnServices>\n}\n\n/** @internal */\nexport const register = Effect.fnUntraced(function* (\n definition: Definition,\n options: { readonly open: Registration[\"open\"] }\n) {\n const model = yield* Effect.context<LanguageModel.LanguageModel>()\n\n const specialists = yield* Effect.forEach(\n definition.specialists,\n Effect.fnUntraced(function* (specialist) {\n const built = yield* Effect.serviceOption(specialist.Handler)\n\n if (Option.isNone(built)) {\n return yield* Effect.die(\n `No handler for the ${specialist.name} specialist: provide ${specialist.name}.toLayer.`\n )\n }\n\n return { specialist, built: built.value }\n })\n )\n\n const builtins = Toolkits.merge(\n yield* Tools.makeMessaging(definition.blocks),\n yield* Specialists.toolkit(definition.specialists)\n )\n\n const registration: Registration = {\n definition,\n model,\n open: options.open,\n specialists,\n builtins\n }\n\n return registration\n})\n\nconst ProviderError = Schema.Struct({ _tag: Schema.optional(Schema.String), raw: Schema.Unknown })\n\n// Provider errors keep their response body.\nconst failureMessage = (cause: Cause.Cause<unknown>): string =>\n Option.match(Schema.decodeUnknownOption(ProviderError)(Cause.squash(cause)), {\n onNone: () => Cause.pretty(cause),\n onSome: ({ _tag, raw }) => `${_tag ?? \"Error\"}: ${Predicate.isString(raw) ? raw : toJsonText(raw)}`\n })\n\n// Approved, and not run yet: also one a crash cut off while it ran, which runs again with the same id.\nconst approvedCalls = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): ReadonlyArray<AssistantEvent.ToolCallPart> =>\n [...ToolApproval.fromEntries(entries).values()].flatMap(({ request, status, outcome }) =>\n status === \"approved\" && outcome === undefined\n ? [\n Response.makePart(\"tool-call\", {\n id: request.callId,\n name: request.tool,\n params: request.params,\n providerExecuted: false\n })\n ]\n : []\n )\n\nconst lastSequence = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) =>\n entries.at(-1)?.sequence ?? otherwise\n\n// The handler's stream can end between a step's output and its tools, or while they run. An interrupted turn (a\n// shutdown) leaves its calls open instead: the next turn runs them again.\nconst closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSequence: number) {\n const entries = (yield* chat.entries).filter(({ sequence }) => sequence > startSequence)\n\n const started = new Set(\n entries.flatMap(({ event }) => (Predicate.isTagged(event, \"ToolStarted\") ? [event.callId] : []))\n )\n\n const open = History.unansweredCalls(History.fromEntries(entries))\n\n if (open.length === 0) return\n\n yield* chat.append(\n open.map((call) =>\n AssistantEvent.ToolResult.make({\n callId: call.id,\n tool: call.name,\n outcome: \"failure\",\n ...(started.has(call.id)\n ? { result: History.mayHaveRunOutput, reason: \"MayHaveRun\" }\n : { result: History.notRunOutput, reason: \"NotRun\" })\n })\n )\n )\n})\n\n// The inputs before the turn's `TurnStarted` (at `startSequence`) woke it; past that, what its model outputs record\n// reading.\nconst handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, startSequence: number) =>\n entries.reduce(\n (through, { sequence, event }) =>\n sequence > startSequence &&\n AssistantEvent.isLibraryEvent(event) &&\n Predicate.isTagged(event, \"ModelOutput\") &&\n event.seenThroughSequence !== undefined\n ? Math.max(through, event.seenThroughSequence)\n : through,\n startSequence\n )\n\n// A turn that ran the loop fails visibly when the user's latest input got no reply; an input that arrived after what\n// its model read gets the next turn, which replies. A handler that ended without the loop decided that itself.\nconst finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, ranLoop: boolean) {\n const entries = yield* chat.entries\n\n const end =\n ranLoop && History.owesReply(entries, read) && !History.unreadAfter(entries, read)\n ? AssistantEvent.TurnFailed.make({ throughSequence: read, message: silentTurnMessage })\n : AssistantEvent.TurnCompleted.make({ throughSequence: read })\n\n yield* chat.append([end])\n})\n\n// Everything stored so far counts as read, so a failing input isn't retried forever.\nconst failTurn = Effect.fnUntraced(function* (chat: Writer, cause: Cause.Cause<unknown>) {\n yield* Effect.logError(\"assistant turn failed\", cause)\n\n const last = lastSequence(yield* chat.entries, 0)\n\n yield* chat.append([AssistantEvent.TurnFailed.make({ throughSequence: last, message: failureMessage(cause) })])\n})\n\nconst makeTurnApi = (\n registration: Registration,\n chat: Writer,\n startSequence: number,\n checkOpen: Effect.Effect<void>,\n ranLoop: Ref.Ref<boolean>\n): Assistant.Turn<any> => {\n const { definition, builtins } = registration\n const { app } = definition\n const shownTools = BlockKit.toolNames(definition.blocks)\n\n const running = <A, E, R>(stream: Stream.Stream<A, E, R>) =>\n Stream.unwrap(Ref.set(ranLoop, true).pipe(Effect.as(stream)))\n\n // With how far into the chat it goes: the step records it on the model's output.\n // Read before every model call: a chat the app deleted meanwhile stops here.\n const history = Effect.fnUntraced(function* (view: Assistant.HistoryView<any> = identity) {\n yield* checkOpen\n\n const all = yield* chat.entries\n\n const read: AssistantLoop.ChatHistory = {\n messages: History.fromEntries(view(AppEvents.decodeEntries(app, all))),\n throughSequence: lastSequence(all, startSequence)\n }\n\n return read\n })\n\n const instructions = (given: string | Instructions.Resolved) =>\n mapText(given, (text) => `${Instructions.protocol}\\n\\n${text}`)\n\n // The loop's own prompt and tools, so the prompt stays cached, with the nudge at the end.\n const replyPass = <R>(options: {\n readonly instructions: Instructions.Resolved\n readonly toolkit: Toolkits.Tools<R>\n readonly view: Assistant.HistoryView<any>\n readonly nudge: string | undefined\n }) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const all = yield* chat.entries\n const through = handledThrough(all, startSequence)\n\n // Something arrived after what the loop's model read: the next turn reads it with the full loop, and replies.\n if (History.unreadAfter(all, through)) return Stream.empty\n\n const owed = History.owesReply(all, through)\n const undelivered = !owed && History.owesDelivery(all, through)\n\n if (!owed && !undelivered) return Stream.empty\n\n const nudge = undelivered ? deliveryNudge : (options.nudge ?? defaultReplyNudge)\n\n return Loop.run({\n instructions: options.instructions,\n // A reply that's owed can only be sent; a result it may have given may also be for nobody.\n toolkit: Toolkits.runOnly(\n options.toolkit,\n owed ? [Tools.sendUserMessageName] : [Tools.sendUserMessageName, Tools.waitToolName],\n \"Not now: reply to the user with send_user_message.\"\n ),\n history: Effect.gen(function* () {\n yield* checkOpen\n\n const entries = yield* chat.entries\n\n return {\n messages: [\n ...History.fromEntries(options.view(AppEvents.decodeEntries(app, entries))),\n History.userText(nudge)\n ],\n throughSequence: lastSequence(entries, startSequence)\n }\n }),\n // A second step, for a reply after a call it was refused.\n maxSteps: 2,\n // The pass runs because the model wrote text of its own instead of replying, which reaches nobody: it has to\n // call a tool now. Changing the choice costs a read of the history at full price, on these rare turns only.\n toolChoice: \"required\"\n // The loop ran the approved calls; a pass that can only reply runs none.\n }).pipe(Stream.provideService(Loop.TurnApprovals, Option.none()))\n })\n )\n\n const replyIfOwed = (options: Assistant.ReplyOptions) =>\n running(\n replyPass({\n instructions: instructions(options.instructions),\n toolkit: builtins,\n view: identity,\n nudge: options.nudge\n })\n )\n\n const prepare = <T extends Record<string, Tool.Any>>(options: Assistant.RunOptions<T, any>) => {\n const own = Toolkits.showing(Toolkits.fromToolkitOrEmpty(options.toolkit), shownTools)\n\n return {\n system: instructions(options.instructions),\n toolkit: Toolkits.merge(builtins, own),\n view: options.history ?? identity\n }\n }\n\n return {\n chatId: chat.chatId,\n history,\n instructions,\n replyIfOwed,\n run: (options) => {\n const { system, toolkit, view } = prepare(options)\n const twice = Toolkits.duplicateNames(toolkit)\n\n const loop =\n twice.length > 0\n ? Stream.die(`Tool names used twice, like one named after a built-in: ${twice.join(\", \")}`)\n : Loop.run({\n instructions: system,\n toolkit,\n history: history(view),\n maxSteps: options.maxSteps ?? 16\n })\n\n return running(Stream.concat(loop, replyPass({ instructions: system, toolkit, view, nudge: options.replyNudge })))\n }\n }\n}\n\n// Every event the handler's loop emits is recorded before the loop is pulled again.\n/** @internal */\nexport const run = Effect.fnUntraced(\n function* (options: {\n readonly registration: Registration\n readonly handler: TurnHandler\n readonly chat: Writer\n readonly services: Context.Context<Assistant.TurnServices | LanguageModel.LanguageModel>\n // A chat the app deleted is deleted here, and the turn stops.\n readonly checkOpen: Effect.Effect<void>\n }) {\n const { chat } = options\n const before = yield* chat.entries\n\n yield* Telemetry.agentInput(History.unhandledInputs(before))\n\n const assistant = options.registration.definition.name\n const traceId = yield* Telemetry.currentTraceId\n\n const started = yield* chat.append([\n AssistantEvent.TurnStarted.make(\n Option.match(traceId, {\n onNone: () => ({ assistant }),\n onSome: (id) => ({ assistant, traceId: id })\n })\n )\n ])\n\n const startSequence = lastSequence(started, 0)\n const ranLoop = yield* Ref.make(false)\n const turn = makeTurnApi(options.registration, chat, startSequence, options.checkOpen, ranLoop)\n\n const record = (event: AssistantLoop.Event) => chat.append([event])\n\n yield* options.handler(turn).pipe(\n Stream.runForEach(record),\n Effect.onExit((exit) => (Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSequence))),\n // A loop the app writes gets the assistant's own tools too, with this turn's services.\n Effect.provideService(\n Loop.TurnTools,\n Option.some(Toolkits.provide(options.registration.builtins, options.services))\n ),\n // A turn's loop waits for the user's approval, and runs the calls they approved.\n Effect.provideService(Loop.TurnApprovals, Option.some({ approved: Effect.map(chat.entries, approvedCalls) })),\n Effect.provideContext(options.services),\n Effect.matchCauseEffect({\n onSuccess: Effect.fnUntraced(function* () {\n const all = yield* chat.entries\n\n yield* finishTurn(chat, handledThrough(all, startSequence), yield* Ref.get(ranLoop))\n }),\n onFailure: (cause) => failTurn(chat, cause)\n })\n )\n\n // The trace's output is what the user saw this turn, and a failure if any.\n const shown = (yield* chat.entries).flatMap(({ event, sequence }) =>\n sequence > startSequence &&\n (Predicate.isTagged(event, \"AssistantMessage\") || Predicate.isTagged(event, \"TurnFailed\"))\n ? [event]\n : []\n )\n\n yield* Telemetry.agentOutput(shown)\n },\n (effect, options) =>\n effect.pipe(\n Telemetry.agentSpan(options.registration.definition.name, {\n chatId: options.chat.chatId,\n root: true\n })\n )\n)\n"],"mappings":"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"}