effect-assistant 0.5.0 → 0.7.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 (77) 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 +2 -2
  29. package/dist/Skill.js +2 -2
  30. package/dist/Skill.js.map +1 -1
  31. package/dist/Specialist.d.ts +17 -16
  32. package/dist/Specialist.d.ts.map +1 -1
  33. package/dist/Specialist.js +5 -5
  34. package/dist/Specialist.js.map +1 -1
  35. package/dist/Timeline.d.ts +9 -9
  36. package/dist/Timeline.js +16 -16
  37. package/dist/Timeline.js.map +1 -1
  38. package/dist/ToolApproval.d.ts +1 -1
  39. package/dist/ToolApproval.js +1 -1
  40. package/dist/ToolApproval.js.map +1 -1
  41. package/dist/ToolDiscovery.d.ts +1 -1
  42. package/dist/ToolDiscovery.js +1 -1
  43. package/dist/ToolDiscovery.js.map +1 -1
  44. package/dist/internal/chat.js +4 -4
  45. package/dist/internal/chat.js.map +1 -1
  46. package/dist/internal/events.js +1 -1
  47. package/dist/internal/events.js.map +1 -1
  48. package/dist/internal/history.js +23 -23
  49. package/dist/internal/history.js.map +1 -1
  50. package/dist/internal/loop.d.ts +1 -1
  51. package/dist/internal/loop.js +4 -4
  52. package/dist/internal/loop.js.map +1 -1
  53. package/dist/internal/specialists.js +6 -6
  54. package/dist/internal/specialists.js.map +1 -1
  55. package/dist/internal/turns.d.ts.map +1 -1
  56. package/dist/internal/turns.js +18 -18
  57. package/dist/internal/turns.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/Assistant.ts +11 -11
  60. package/src/AssistantActivity.ts +9 -7
  61. package/src/AssistantEngine.ts +3 -3
  62. package/src/AssistantEvent.ts +8 -8
  63. package/src/AssistantLoop.ts +3 -3
  64. package/src/ChatLog.ts +31 -26
  65. package/src/Compaction.ts +21 -21
  66. package/src/Memory.ts +1 -1
  67. package/src/Skill.ts +2 -2
  68. package/src/Specialist.ts +19 -18
  69. package/src/Timeline.ts +22 -22
  70. package/src/ToolApproval.ts +1 -1
  71. package/src/ToolDiscovery.ts +1 -1
  72. package/src/internal/chat.ts +4 -4
  73. package/src/internal/events.ts +1 -1
  74. package/src/internal/history.ts +32 -28
  75. package/src/internal/loop.ts +5 -5
  76. package/src/internal/specialists.ts +7 -7
  77. package/src/internal/turns.ts +28 -26
@@ -87,7 +87,7 @@ const deletedChat = (entries: ReadonlyArray<AssistantEvent.Entry>): Chat => ({
87
87
  submit: () => Effect.void,
88
88
  submitInBackground: () => Effect.void,
89
89
  resume: Effect.void,
90
- stream: (after) => Stream.fromIterable(entries.filter((entry) => entry.seq > after)),
90
+ stream: (after) => Stream.fromIterable(entries.filter((entry) => entry.sequence > after)),
91
91
  delete: Effect.void
92
92
  })
93
93
 
@@ -314,15 +314,15 @@ export const make = Effect.fnUntraced(function* (options: {
314
314
  // Subscribes before it reads, so nothing appended in between is missed.
315
315
  const subscription = yield* PubSub.subscribe(channel)
316
316
  const all = yield* chat.entries
317
- const stored = all.filter((entry) => entry.seq > after)
318
- const last = stored.at(-1)?.seq ?? after
317
+ const stored = all.filter((entry) => entry.sequence > after)
318
+ const last = stored.at(-1)?.sequence ?? after
319
319
 
320
320
  // Deleted: what the reader hasn't seen, then the end. Otherwise the stream ends with the `ChatDeleted`.
321
321
  if (all.some(isChatDeleted)) return Stream.fromIterable(stored)
322
322
 
323
323
  return Stream.concat(
324
324
  Stream.fromIterable(stored),
325
- Stream.fromSubscription(subscription).pipe(Stream.filter((entry) => entry.seq > last))
325
+ Stream.fromSubscription(subscription).pipe(Stream.filter((entry) => entry.sequence > last))
326
326
  ).pipe(Stream.takeUntil(isChatDeleted))
327
327
  })
328
328
  ),
@@ -95,6 +95,6 @@ export type DecodedEntries = ReturnType<typeof decodeEntries>
95
95
 
96
96
  const toEntry = (entry: AssistantEvent.AnyEntry) => ({
97
97
  chatId: entry.chatId,
98
- seq: entry.seq,
98
+ sequence: entry.sequence,
99
99
  at: entry.at
100
100
  })
@@ -140,7 +140,7 @@ const awaitingMessage = (request: AssistantEvent.ApprovalRequested): Prompt.Tool
140
140
  })
141
141
 
142
142
  const isEntry = (item: AssistantEvent.AnyEntry | Prompt.Message): item is AssistantEvent.AnyEntry =>
143
- Predicate.hasProperty(item, "seq")
143
+ Predicate.hasProperty(item, "sequence")
144
144
 
145
145
  // In the order the model saw the entries, not the order they were stored. A message a history view put in stands
146
146
  // 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
@@ -166,15 +166,15 @@ export const fromEntries = (
166
166
  const next = entries.slice(from + 1).find(({ event }) => Predicate.isTagged(event, "ModelOutput"))?.event
167
167
 
168
168
  return next !== undefined && AssistantEvent.isLibraryEvent(next) && Predicate.isTagged(next, "ModelOutput")
169
- ? next.seenThroughSeq
169
+ ? next.seenThroughSequence
170
170
  : undefined
171
171
  }
172
172
 
173
- // `index` is the last entry at or before the item, and `seq` where the item stands.
174
- const input = (index: number, seq: number, item: Prompt.Message) => {
173
+ // `index` is the last entry at or before the item, and `sequence` where the item stands.
174
+ const input = (index: number, sequence: number, item: Prompt.Message) => {
175
175
  const seen = nextSeen(index)
176
176
 
177
- if (open.size > 0 || (seen !== undefined && seen < seq)) held.push(item)
177
+ if (open.size > 0 || (seen !== undefined && seen < sequence)) held.push(item)
178
178
  else history.push(item)
179
179
  }
180
180
 
@@ -182,14 +182,14 @@ export const fromEntries = (
182
182
 
183
183
  for (const item of items) {
184
184
  if (!isEntry(item)) {
185
- input(index, (entries[index]?.seq ?? 0) + 0.5, item)
185
+ input(index, (entries[index]?.sequence ?? 0) + 0.5, item)
186
186
 
187
187
  continue
188
188
  }
189
189
 
190
190
  index += 1
191
191
 
192
- const { event, seq } = item
192
+ const { event, sequence } = item
193
193
  const at = index
194
194
 
195
195
  if (!AssistantEvent.isLibraryEvent(event)) continue
@@ -197,8 +197,8 @@ export const fromEntries = (
197
197
  Match.value(event).pipe(
198
198
  Match.tagsExhaustive({
199
199
  UserMessage: (message) =>
200
- input(at, seq, userText(AssistantEvent.textWithAttachments(message.text, message.attachments))),
201
- BlockAnswer: (answer) => input(at, seq, userText(answerText(answer))),
200
+ input(at, sequence, userText(AssistantEvent.textWithAttachments(message.text, message.attachments))),
201
+ BlockAnswer: (answer) => input(at, sequence, userText(answerText(answer))),
202
202
  ModelOutput: (output) => {
203
203
  history.push(...Prompt.fromResponseParts(output.parts).content)
204
204
 
@@ -212,11 +212,11 @@ export const fromEntries = (
212
212
  flushIfClosed()
213
213
  },
214
214
  ToolResult: (result) => {
215
- if (requests.has(result.callId)) return input(at, seq, userText(approvedText(result)))
215
+ if (requests.has(result.callId)) return input(at, sequence, userText(approvedText(result)))
216
216
 
217
217
  // A view that starts after the call (since a summary, say) keeps its result as text: a tool result needs its
218
218
  // call right before it.
219
- if (!called.has(result.callId)) return input(at, seq, userText(resultText(result)))
219
+ if (!called.has(result.callId)) return input(at, sequence, userText(resultText(result)))
220
220
 
221
221
  history.push(toolMessage(result))
222
222
  open.delete(result.callId)
@@ -227,7 +227,7 @@ export const fromEntries = (
227
227
  TurnStarted: () => undefined,
228
228
  TurnCompleted: () => undefined,
229
229
  TurnFailed: () => undefined,
230
- SpecialistFinished: (finished) => input(at, seq, userText(finishedText(entries, at, finished))),
230
+ SpecialistFinished: (finished) => input(at, sequence, userText(finishedText(entries, at, finished))),
231
231
  // Every run starts in a call of the model's own (a spawn or a follow-up), whose result says so.
232
232
  SpecialistStarted: () => undefined,
233
233
  BlocksClosed: () => undefined,
@@ -235,7 +235,7 @@ export const fromEntries = (
235
235
  ApprovalRequested: (request) => {
236
236
  requests.set(request.callId, request)
237
237
 
238
- if (!called.has(request.callId)) return input(at, seq, userText(awaitingText(request)))
238
+ if (!called.has(request.callId)) return input(at, sequence, userText(awaitingText(request)))
239
239
 
240
240
  history.push(awaitingMessage(request))
241
241
  open.delete(request.callId)
@@ -244,10 +244,10 @@ export const fromEntries = (
244
244
  // An approved call's result says so.
245
245
  ApprovalDecision: (decision) =>
246
246
  decision.decision === "rejected"
247
- ? input(at, seq, userText(rejectedText(decision, toolOf(decision.callId))))
247
+ ? input(at, sequence, userText(rejectedText(decision, toolOf(decision.callId))))
248
248
  : undefined,
249
249
  ApprovalsExpired: (expired) =>
250
- expired.callIds.forEach((callId) => input(at, seq, userText(expiredText(callId, toolOf(callId))))),
250
+ expired.callIds.forEach((callId) => input(at, sequence, userText(expiredText(callId, toolOf(callId))))),
251
251
  ChatDeleted: () => undefined
252
252
  })
253
253
  )
@@ -264,7 +264,7 @@ export const isInput = (event: { readonly _tag: string }): boolean =>
264
264
  export const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): number => {
265
265
  const last = entries.findLast(({ event }) => AssistantEvent.isTurnEnd(event))?.event
266
266
 
267
- return last !== undefined && AssistantEvent.isTurnEnd(last) ? last.throughSeq : 0
267
+ return last !== undefined && AssistantEvent.isTurnEnd(last) ? last.throughSequence : 0
268
268
  }
269
269
 
270
270
  // A run the assistant stopped isn't an input here: it wakes nothing.
@@ -274,8 +274,10 @@ export const unhandledInputs = (
274
274
  ): ReadonlyArray<{ readonly _tag: string }> => {
275
275
  const seen = handledThrough(entries)
276
276
 
277
- return entries.flatMap(({ event, seq }) =>
278
- seq > seen && isInput(event) && !(AssistantEvent.isInput(event) && !AssistantEvent.isWaking(event)) ? [event] : []
277
+ return entries.flatMap(({ event, sequence }) =>
278
+ sequence > seen && isInput(event) && !(AssistantEvent.isInput(event) && !AssistantEvent.isWaking(event))
279
+ ? [event]
280
+ : []
279
281
  )
280
282
  }
281
283
 
@@ -285,16 +287,17 @@ export const needsTurn = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): bool
285
287
 
286
288
  // A failure the user was shown counts as a reply, and so does a call they're asked to approve.
287
289
  /** @internal */
288
- export const owesReply = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {
290
+ export const owesReply = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean => {
289
291
  const latest = entries.findLast(
290
- ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)
292
+ ({ event, sequence }) =>
293
+ sequence <= throughSequence && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)
291
294
  )
292
295
 
293
296
  return (
294
297
  latest !== undefined &&
295
298
  !entries.some(
296
- ({ event, seq }) =>
297
- seq > latest.seq &&
299
+ ({ event, sequence }) =>
300
+ sequence > latest.sequence &&
298
301
  (Predicate.isTagged(event, "AssistantMessage") ||
299
302
  Predicate.isTagged(event, "TurnFailed") ||
300
303
  Predicate.isTagged(event, "ApprovalRequested"))
@@ -321,15 +324,16 @@ const progressMessages = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Read
321
324
  // The model said it was on it and stopped without the result: only progress messages since the user's latest input.
322
325
  // A specialist still working doesn't count, since its report is what comes next.
323
326
  /** @internal */
324
- export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean => {
327
+ export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean => {
325
328
  const latest = entries.findLast(
326
- ({ event, seq }) => seq <= throughSeq && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)
329
+ ({ event, sequence }) =>
330
+ sequence <= throughSequence && AssistantEvent.isInput(event) && AssistantEvent.isUserInput(event)
327
331
  )
328
332
 
329
333
  if (latest === undefined) return false
330
334
 
331
- const after = entries.flatMap(({ seq, event }) =>
332
- seq > latest.seq && AssistantEvent.isLibraryEvent(event) ? [event] : []
335
+ const after = entries.flatMap(({ sequence, event }) =>
336
+ sequence > latest.sequence && AssistantEvent.isLibraryEvent(event) ? [event] : []
333
337
  )
334
338
 
335
339
  const messages = after.flatMap((event) => (Predicate.isTagged(event, "AssistantMessage") ? [event] : []))
@@ -352,5 +356,5 @@ export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, th
352
356
  }
353
357
 
354
358
  /** @internal */
355
- export const unreadAfter = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSeq: number): boolean =>
356
- entries.some(({ event, seq }) => seq > throughSeq && isInput(event))
359
+ export const unreadAfter = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean =>
360
+ entries.some(({ event, sequence }) => sequence > throughSequence && isInput(event))
@@ -44,13 +44,13 @@ export const TurnTools = Context.Reference<Option.Option<Toolkits.Tools>>("effec
44
44
  /** @internal */
45
45
  export interface ChatHistory {
46
46
  readonly messages: ReadonlyArray<Prompt.Message>
47
- readonly throughSeq: number
47
+ readonly throughSequence: number
48
48
  }
49
49
 
50
50
  const readHistory = (read: ReadonlyArray<Prompt.Message> | ChatHistory) =>
51
51
  "messages" in read
52
- ? { messages: read.messages, throughSeq: read.throughSeq }
53
- : { messages: read, throughSeq: undefined }
52
+ ? { messages: read.messages, throughSequence: read.throughSequence }
53
+ : { messages: read, throughSequence: undefined }
54
54
 
55
55
  // `AssistantLoop` takes native toolkits; turns and specialist runs come here with their tools already merged.
56
56
  /** @internal */
@@ -281,7 +281,7 @@ export const step = <R, S, E2 = never, R2 = never>(
281
281
 
282
282
  return Stream.unwrap(
283
283
  Effect.gen(function* () {
284
- const { messages: history, throughSeq } = readHistory(yield* options.history)
284
+ const { messages: history, throughSequence } = readHistory(yield* options.history)
285
285
  // Calls a crash cut off, with no result: they run again, with the same ids, before the model goes on.
286
286
  const cutOff = History.unansweredCalls(history)
287
287
  const approvals = yield* TurnApprovals
@@ -318,7 +318,7 @@ export const step = <R, S, E2 = never, R2 = never>(
318
318
 
319
319
  const output = AssistantEvent.ModelOutput.make({
320
320
  parts: modelParts(response.content),
321
- seenThroughSeq: throughSeq,
321
+ seenThroughSequence: throughSequence,
322
322
  model: metadata?.modelId ?? Option.getOrUndefined(name),
323
323
  responseId: metadata?.id,
324
324
  usage: response.usage,
@@ -30,7 +30,7 @@ import * as Toolkits from "./toolkit.ts"
30
30
  import type { Writer } from "./writer.ts"
31
31
 
32
32
  /** @internal */
33
- export type RunHandler = (run: Specialist.Run) => Stream.Stream<AssistantLoop.Event, unknown, Specialist.RunServices>
33
+ export type RunHandler = (turn: Specialist.Turn) => Stream.Stream<AssistantLoop.Event, unknown, Specialist.TurnServices>
34
34
 
35
35
  /** @internal */
36
36
  export const briefText = (brief: Specialist.Brief): string =>
@@ -75,18 +75,18 @@ export const runBrief = Effect.fnUntraced(
75
75
  return yield* Ref.get(history)
76
76
  })
77
77
 
78
- const run: Specialist.Run = {
78
+ const turn: Specialist.Turn = {
79
79
  runId: options.runId,
80
80
  chatId: options.chatId,
81
81
  brief: options.brief,
82
82
  history: current,
83
83
  // A run can't wait for the user's approval: a call that needs it fails, and the run reads why.
84
- loop: (loop) =>
84
+ run: (run) =>
85
85
  Loop.run({
86
- instructions: loop.instructions,
87
- toolkit: Toolkits.fromToolkitOrEmpty(loop.toolkit),
86
+ instructions: run.instructions,
87
+ toolkit: Toolkits.fromToolkitOrEmpty(run.toolkit),
88
88
  history: current,
89
- maxSteps: loop.maxSteps ?? 8
89
+ maxSteps: run.maxSteps ?? 8
90
90
  }).pipe(Stream.provideService(Loop.TurnApprovals, Option.none()))
91
91
  }
92
92
 
@@ -102,7 +102,7 @@ export const runBrief = Effect.fnUntraced(
102
102
  if (text.length > 0) yield* Ref.set(answer, text)
103
103
  })
104
104
 
105
- yield* Stream.runForEach(handler(run), record)
105
+ yield* Stream.runForEach(handler(turn), record)
106
106
 
107
107
  const text = yield* Ref.get(answer)
108
108
 
@@ -140,12 +140,13 @@ const approvedCalls = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Readonl
140
140
  : []
141
141
  )
142
142
 
143
- const lastSeq = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) => entries.at(-1)?.seq ?? otherwise
143
+ const lastSequence = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, otherwise: number) =>
144
+ entries.at(-1)?.sequence ?? otherwise
144
145
 
145
146
  // The handler's stream can end between a step's output and its tools, or while they run. An interrupted turn (a
146
147
  // shutdown) leaves its calls open instead: the next turn runs them again.
147
- const closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSeq: number) {
148
- const entries = (yield* chat.entries).filter(({ seq }) => seq > startSeq)
148
+ const closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSequence: number) {
149
+ const entries = (yield* chat.entries).filter(({ sequence }) => sequence > startSequence)
149
150
 
150
151
  const started = new Set(
151
152
  entries.flatMap(({ event }) => (Predicate.isTagged(event, "ToolStarted") ? [event.callId] : []))
@@ -169,18 +170,18 @@ const closeOpenCalls = Effect.fnUntraced(function* (chat: Writer, startSeq: numb
169
170
  )
170
171
  })
171
172
 
172
- // The inputs before the turn's `TurnStarted` (at `startSeq`) woke it; past that, what its model outputs record
173
+ // The inputs before the turn's `TurnStarted` (at `startSequence`) woke it; past that, what its model outputs record
173
174
  // reading.
174
- const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, startSeq: number) =>
175
+ const handledThrough = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, startSequence: number) =>
175
176
  entries.reduce(
176
- (through, { seq, event }) =>
177
- seq > startSeq &&
177
+ (through, { sequence, event }) =>
178
+ sequence > startSequence &&
178
179
  AssistantEvent.isLibraryEvent(event) &&
179
180
  Predicate.isTagged(event, "ModelOutput") &&
180
- event.seenThroughSeq !== undefined
181
- ? Math.max(through, event.seenThroughSeq)
181
+ event.seenThroughSequence !== undefined
182
+ ? Math.max(through, event.seenThroughSequence)
182
183
  : through,
183
- startSeq
184
+ startSequence
184
185
  )
185
186
 
186
187
  // A turn that ran the loop fails visibly when the user's latest input got no reply; an input that arrived after what
@@ -190,8 +191,8 @@ const finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, ranL
190
191
 
191
192
  const end =
192
193
  ranLoop && History.owesReply(entries, read) && !History.unreadAfter(entries, read)
193
- ? AssistantEvent.TurnFailed.make({ throughSeq: read, message: silentTurnMessage })
194
- : AssistantEvent.TurnCompleted.make({ throughSeq: read })
194
+ ? AssistantEvent.TurnFailed.make({ throughSequence: read, message: silentTurnMessage })
195
+ : AssistantEvent.TurnCompleted.make({ throughSequence: read })
195
196
 
196
197
  yield* chat.append([end])
197
198
  })
@@ -200,15 +201,15 @@ const finishTurn = Effect.fnUntraced(function* (chat: Writer, read: number, ranL
200
201
  const failTurn = Effect.fnUntraced(function* (chat: Writer, cause: Cause.Cause<unknown>) {
201
202
  yield* Effect.logError("assistant turn failed", cause)
202
203
 
203
- const last = lastSeq(yield* chat.entries, 0)
204
+ const last = lastSequence(yield* chat.entries, 0)
204
205
 
205
- yield* chat.append([AssistantEvent.TurnFailed.make({ throughSeq: last, message: failureMessage(cause) })])
206
+ yield* chat.append([AssistantEvent.TurnFailed.make({ throughSequence: last, message: failureMessage(cause) })])
206
207
  })
207
208
 
208
209
  const makeTurnApi = (
209
210
  registration: Registration,
210
211
  chat: Writer,
211
- startSeq: number,
212
+ startSequence: number,
212
213
  checkOpen: Effect.Effect<void>,
213
214
  ranLoop: Ref.Ref<boolean>
214
215
  ): Assistant.Turn<any> => {
@@ -228,7 +229,7 @@ const makeTurnApi = (
228
229
 
229
230
  const read: AssistantLoop.ChatHistory = {
230
231
  messages: History.fromEntries(view(AppEvents.decodeEntries(app, all))),
231
- throughSeq: lastSeq(all, startSeq)
232
+ throughSequence: lastSequence(all, startSequence)
232
233
  }
233
234
 
234
235
  return read
@@ -247,7 +248,7 @@ const makeTurnApi = (
247
248
  Stream.unwrap(
248
249
  Effect.gen(function* () {
249
250
  const all = yield* chat.entries
250
- const through = handledThrough(all, startSeq)
251
+ const through = handledThrough(all, startSequence)
251
252
 
252
253
  // Something arrived after what the loop's model read: the next turn reads it with the full loop, and replies.
253
254
  if (History.unreadAfter(all, through)) return Stream.empty
@@ -277,7 +278,7 @@ const makeTurnApi = (
277
278
  ...History.fromEntries(options.view(AppEvents.decodeEntries(app, entries))),
278
279
  History.userText(nudge)
279
280
  ],
280
- throughSeq: lastSeq(entries, startSeq)
281
+ throughSequence: lastSequence(entries, startSequence)
281
282
  }
282
283
  }),
283
284
  // A second step, for a reply after a call it was refused.
@@ -300,7 +301,7 @@ const makeTurnApi = (
300
301
  })
301
302
  )
302
303
 
303
- const prepare = <T extends Record<string, Tool.Any>>(options: Assistant.LoopOptions<T, any>) => {
304
+ const prepare = <T extends Record<string, Tool.Any>>(options: Assistant.RunOptions<T, any>) => {
304
305
  const own = Toolkits.showing(Toolkits.fromToolkitOrEmpty(options.toolkit), shownTools)
305
306
 
306
307
  return {
@@ -315,7 +316,7 @@ const makeTurnApi = (
315
316
  history,
316
317
  instructions,
317
318
  replyIfOwed,
318
- loop: (options) => {
319
+ run: (options) => {
319
320
  const { system, toolkit, view } = prepare(options)
320
321
  const twice = Toolkits.duplicateNames(toolkit)
321
322
 
@@ -362,15 +363,15 @@ export const run = Effect.fnUntraced(
362
363
  )
363
364
  ])
364
365
 
365
- const startSeq = lastSeq(started, 0)
366
+ const startSequence = lastSequence(started, 0)
366
367
  const ranLoop = yield* Ref.make(false)
367
- const turn = makeTurnApi(options.registration, chat, startSeq, options.checkOpen, ranLoop)
368
+ const turn = makeTurnApi(options.registration, chat, startSequence, options.checkOpen, ranLoop)
368
369
 
369
370
  const record = (event: AssistantLoop.Event) => chat.append([event])
370
371
 
371
372
  yield* options.handler(turn).pipe(
372
373
  Stream.runForEach(record),
373
- Effect.onExit((exit) => (Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSeq))),
374
+ Effect.onExit((exit) => (Exit.hasInterrupts(exit) ? Effect.void : closeOpenCalls(chat, startSequence))),
374
375
  // A loop the app writes gets the assistant's own tools too, with this turn's services.
375
376
  Effect.provideService(
376
377
  Loop.TurnTools,
@@ -383,15 +384,16 @@ export const run = Effect.fnUntraced(
383
384
  onSuccess: Effect.fnUntraced(function* () {
384
385
  const all = yield* chat.entries
385
386
 
386
- yield* finishTurn(chat, handledThrough(all, startSeq), yield* Ref.get(ranLoop))
387
+ yield* finishTurn(chat, handledThrough(all, startSequence), yield* Ref.get(ranLoop))
387
388
  }),
388
389
  onFailure: (cause) => failTurn(chat, cause)
389
390
  })
390
391
  )
391
392
 
392
393
  // The trace's output is what the user saw this turn, and a failure if any.
393
- const shown = (yield* chat.entries).flatMap(({ event, seq }) =>
394
- seq > startSeq && (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed"))
394
+ const shown = (yield* chat.entries).flatMap(({ event, sequence }) =>
395
+ sequence > startSequence &&
396
+ (Predicate.isTagged(event, "AssistantMessage") || Predicate.isTagged(event, "TurnFailed"))
395
397
  ? [event]
396
398
  : []
397
399
  )