input

The message box the user types into.

interface InputApi {
    get(): string
    set(text: string): void

    // Append without clearing — the voice-transcript behaviour.
    append(text: string): void

    clear(): void

    // Send to the focused agent as Enter would, and clear. No-op when empty.
    submit(): Promise<void>
}

Drafting for the user

commands.register("review", {
    async run() {
        const branch = await palette.pick(await gitBranches())
        if (!branch) return

        input.set(`review the changes on ${branch}`)
        // Left in the box — the user hits Enter when ready.
    },
    description: "Draft a review request",
})

That is the difference from agents.send: the text is visible and editable before it goes.

Sending it yourself

input.set("status report")
await input.submit()   // already cleared afterwards

submit() takes Enter's own path, so queueing, history and the switching-agent rule all behave identically — a message submitted while an agent is still booting is queued, not lost.

Building onto a draft

commands.register("cite", {
    async run() {
        const file = await palette.pick(await listFiles())
        if (file) input.append(` @${file}`)
    },
    description: "Reference a file",
})

append adds no separator — space it yourself.

Mode keys need an empty box

keys.register("ctrl+b", async () => {
    if (input.get().length > 0) return   // mid-sentence — leave it alone
    await palette.open("branches")
})

The same rule the built-in mode keys follow: a ~ typed mid-sentence stays a literal character.