Files
sausage/extensions/subagents/api.ts
T

100 lines
3.5 KiB
TypeScript

/**
* Subagents: conversations delegated to agent definitions, and the main agents. Other extensions create
* configured child conversations and open main agents here; definitions themselves are read with `lib/agents.ts`.
*/
import type { Context } from '@earendil-works/chord'
import { defineDoc } from '@earendil-works/pi-durable'
import type { ConversationId, EntryId, TaskId, Tx } from '@earendil-works/pi-durable'
const defineApi = <S>() => (name: string): {name: string; service?: S} => ({name})
import { RunsDoc, MainAgentDoc } from './refs.ts'
import type { Run } from './refs.ts'
import type { createChild } from './children.ts'
export { Started, MainAgentDoc } from './refs.ts'
/** Each conversation's subagents and pending subagent actions, on the conversation that started them. */
export const Subagents = defineDoc<{
agents: Record<
string,
{
conversationId: ConversationId
agent: string
background: boolean
reported: EntryId[]
}
>
actions: Record<
string,
{
conversationId: ConversationId
reporter: TaskId
answer?: EntryId
/**
* Unset while delivering; then `done`, `unanswered` (stopped by the parent), `interrupted` (stopped in the
* child), `failed` (a provider error the watchdog holds, or a delivery error) or `given-up` (the watchdog
* reported that provider error instead of retrying it: final).
*/
status?: string
}
>
}>({
kind: 'main-agent.subagents',
checkpointWhen: () => false,
version: 1,
scope: 'conversation',
history: 'latest',
fork: 'initial',
initial: () => ({ agents: {}, actions: {} }),
})
/** `{ [name]: conversationId }` of the main agents, on the root conversation. */
export const MainAgent = defineDoc<Record<string, ConversationId>>({
kind: MainAgentDoc.kind,
checkpointWhen: () => false,
version: 1,
scope: 'conversation',
history: 'latest',
fork: 'initial',
initial: () => ({}),
})
/** `{ [conversationId]: Run }` of the children `subagent start` made, on the root, for the sidebar and call cards. */
export const Runs = defineDoc<Record<string, Run>>({
kind: RunsDoc.kind,
checkpointWhen: () => false,
version: 1,
scope: 'conversation',
history: 'latest',
fork: 'initial',
initial: () => ({}),
})
/**
* The definition a conversation was configured from: children (including automation runs) and main agents. An agent
* made on the fly has none: `adhoc`, so nothing looks a definition up by its label.
*/
export const Profile = defineDoc<{ definition?: string; adhoc?: true }>({
kind: 'main-agent.agent-profile',
checkpointWhen: () => false,
version: 1,
scope: 'conversation',
history: 'latest',
fork: 'initial',
initial: () => ({}),
})
export type SubagentsService = {
/** The main agents that run in this home, by name and description; use it to list valid `@name` addresses. */
residents(ctx: Context): Promise<{ name: string; description: string }[]>
/** The conversation of main agent `name`, created or refreshed from its definition; throws for unknown names. */
mainAgent(name: string, ctx: Context): Promise<ConversationId>
/** Create, inside `tx`, a child conversation configured from a definition or as a copy of the parent's agent. */
createChild: (
tx: Tx,
options: Parameters<typeof createChild>[2],
) => ReturnType<typeof createChild>
}
/** Child conversations and main agents; `need` it where delegation is required, else `want` it. */
export const SubagentsApi = defineApi<SubagentsService>()('subagents')
export const tier = 'core'