82 lines
3.4 KiB
TypeScript
82 lines
3.4 KiB
TypeScript
/**
|
|
* Messages: messages between conversations and the user, delivered as inputs or notes. Other extensions send one-way
|
|
* messages and answer the user's open questions here.
|
|
*/
|
|
import type { Context } from '@earendil-works/chord'
|
|
import { defineDoc } from '@earendil-works/pi-durable'
|
|
import type { EntryId } from '@earendil-works/pi-durable'
|
|
import { defineApi } from '../messages/compat.ts'
|
|
import type { Device } from '../messages/compat.ts'
|
|
import { MessagesDoc } from './refs.ts'
|
|
import type { Question, Via } from './refs.ts'
|
|
|
|
/** One message as the log stores it. */
|
|
export type Message = {
|
|
id: number
|
|
from: number | 'user'
|
|
to: number | 'user'
|
|
text: string
|
|
at: number
|
|
reply: boolean
|
|
re?: number
|
|
state: 'sent' | 'delivered' | 'answered' | 'dismissed' | 'failed' | 'stopped'
|
|
entry?: EntryId
|
|
answer?: EntryId
|
|
error?: string
|
|
/** The tool call that sent it: `message`'s or `branch`'s own id, `<script>/<n>` for a nested codemode call. */
|
|
call?: string
|
|
/** Images forwarded with it: the sender's entry that holds them, attached to the recipient's input on delivery. */
|
|
images?: { entry: EntryId; count: number }
|
|
/** A branch's final answer routed to its starter after it stopped with nothing under way: it awaits the starter. */
|
|
report?: true
|
|
/** A branch's reply to its starter: it awaits the starter's answer, routed down to it. */
|
|
awaits?: true
|
|
/** The starter's final answer to a branch's report or reply, routed to that branch: the answer's entry. */
|
|
routed?: EntryId
|
|
/** A branch's report answering routed answers only: the starter's answer to it stays local. */
|
|
echo?: true
|
|
/** The questions a reply-expected message to the user asks, answered on its card. */
|
|
questions?: Question[]
|
|
/** A user answer or dismissal that the standing agent settled for the user, not their own click. */
|
|
via?: Via
|
|
}
|
|
/** Every message between conversations and the user, on the root conversation. */
|
|
export const Messages = defineDoc<{ messages: Message[]; next: number }>({
|
|
kind: MessagesDoc.kind,
|
|
checkpointWhen: () => false,
|
|
version: 1,
|
|
scope: 'conversation',
|
|
history: 'latest',
|
|
fork: 'initial',
|
|
initial: () => ({ messages: [], next: 1 }),
|
|
})
|
|
|
|
export type MessagesService = {
|
|
/**
|
|
* Reply `text` from the user to open message `messageId`, from browser `device`; returns the reply's message
|
|
* id. Records a `user.input` event.
|
|
*/
|
|
answer(messageId: number, text: string, ctx: Context, device?: Device): Promise<number>
|
|
/**
|
|
* The messages that wait on the user's answer, oldest first: each one's id, the conversation that asked, when, and its
|
|
* question in one line (the line its card's header shows).
|
|
*/
|
|
openQuestions(
|
|
ctx: Context,
|
|
): Promise<{ message: number; conversation: number; at: number; text: string }[]>
|
|
/** Close open message `messageId` unanswered; its sender gets a one-way note. Returns the note's message id. */
|
|
dismiss(messageId: number, ctx: Context, device?: Device): Promise<number>
|
|
/** Send a one-way message from conversation `from` to `to` (`#id`, `@name` or `user`), once per `requestId`. */
|
|
send(
|
|
from: number,
|
|
input: { to: string; text: string; reply: false },
|
|
requestId: string,
|
|
ctx: Context,
|
|
): Promise<number>
|
|
}
|
|
export { MessageSent } from './refs.ts'
|
|
export type { Question } from './refs.ts'
|
|
/** Messages between conversations; core, so `need` it. */
|
|
export const MessagesApi = defineApi<MessagesService>()('messages')
|
|
|
|
export const tier = 'core' |