effect-assistant 0.7.0 → 0.7.1
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.
package/dist/internal/history.js
CHANGED
|
@@ -164,11 +164,11 @@ const owesDelivery = (entries, throughSequence) => {
|
|
|
164
164
|
const latest = entries.findLast(({ event, sequence }) => sequence <= throughSequence && isInput$1(event) && isUserInput(event));
|
|
165
165
|
if (latest === void 0) return false;
|
|
166
166
|
const after = entries.flatMap(({ sequence, event }) => sequence > latest.sequence && isLibraryEvent(event) ? [event] : []);
|
|
167
|
-
const
|
|
167
|
+
const last = after.findLast((event) => Predicate.isTagged(event, "AssistantMessage"));
|
|
168
168
|
const progress = progressMessages(entries);
|
|
169
169
|
const started = after.flatMap((event) => Predicate.isTagged(event, "SpecialistStarted") ? [event.runId] : []);
|
|
170
170
|
const finished = new Set(entries.flatMap(({ event }) => isLibraryEvent(event) && Predicate.isTagged(event, "SpecialistFinished") ? [event.runId] : []));
|
|
171
|
-
return
|
|
171
|
+
return last !== void 0 && last.block === void 0 && progress.has(last.id) && !after.some((event) => Predicate.isTagged(event, "TurnFailed") || Predicate.isTagged(event, "ApprovalRequested")) && started.every((runId) => finished.has(runId));
|
|
172
172
|
};
|
|
173
173
|
/** @internal */
|
|
174
174
|
const unreadAfter = (entries, throughSequence) => entries.some(({ event, sequence }) => sequence > throughSequence && isInput(event));
|
|
@@ -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, \"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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"}
|
|
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: its last message since the user's latest input was\n// progress. An earlier message may have answered something else, such as an input that came before. A specialist still\n// 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 last = after.findLast((event) => Predicate.isTagged(event, \"AssistantMessage\"))\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 last !== undefined &&\n last.block === undefined &&\n progress.has(last.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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"}
|
package/package.json
CHANGED
package/src/internal/history.ts
CHANGED
|
@@ -321,8 +321,9 @@ const progressMessages = (entries: ReadonlyArray<AssistantEvent.AnyEntry>): Read
|
|
|
321
321
|
)
|
|
322
322
|
)
|
|
323
323
|
|
|
324
|
-
// The model said it was on it and stopped without the result:
|
|
325
|
-
//
|
|
324
|
+
// The model said it was on it and stopped without the result: its last message since the user's latest input was
|
|
325
|
+
// progress. An earlier message may have answered something else, such as an input that came before. A specialist still
|
|
326
|
+
// working doesn't count, since its report is what comes next.
|
|
326
327
|
/** @internal */
|
|
327
328
|
export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, throughSequence: number): boolean => {
|
|
328
329
|
const latest = entries.findLast(
|
|
@@ -336,7 +337,7 @@ export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, th
|
|
|
336
337
|
sequence > latest.sequence && AssistantEvent.isLibraryEvent(event) ? [event] : []
|
|
337
338
|
)
|
|
338
339
|
|
|
339
|
-
const
|
|
340
|
+
const last = after.findLast((event) => Predicate.isTagged(event, "AssistantMessage"))
|
|
340
341
|
const progress = progressMessages(entries)
|
|
341
342
|
const started = after.flatMap((event) => (Predicate.isTagged(event, "SpecialistStarted") ? [event.runId] : []))
|
|
342
343
|
|
|
@@ -348,8 +349,9 @@ export const owesDelivery = (entries: ReadonlyArray<AssistantEvent.AnyEntry>, th
|
|
|
348
349
|
|
|
349
350
|
// A call waiting for the user's approval is what comes next, like a specialist's report.
|
|
350
351
|
return (
|
|
351
|
-
|
|
352
|
-
|
|
352
|
+
last !== undefined &&
|
|
353
|
+
last.block === undefined &&
|
|
354
|
+
progress.has(last.id) &&
|
|
353
355
|
!after.some((event) => Predicate.isTagged(event, "TurnFailed") || Predicate.isTagged(event, "ApprovalRequested")) &&
|
|
354
356
|
started.every((runId) => finished.has(runId))
|
|
355
357
|
)
|