@onkeiki/channels
v0.1.6
Published
Framework-free helpers for Keiki external harness channels
Readme
@onkeiki/channels
Framework-free helpers for applications that receive Keiki external-harness turns. The package verifies signed turn and cancellation payloads and provides a client for reporting messages, streamed text, typing, and turn completion back to Keiki.
import { createTurnClient, verifyKeikiRequest } from '@onkeiki/channels'
const request = verifyKeikiRequest({
rawBody,
headers: requestHeaders,
}, {
signingSecret: process.env.KEIKI_SIGNING_SECRET!,
})
if (request.kind === 'turn') {
const turn = createTurnClient({
turnUrl: request.turn.turnUrl,
turnToken: request.turnToken,
})
await turn.message('Hello.', { idempotencyKey: request.turn.turnId })
await turn.complete({ status: 'completed' })
}turn.message(text, { idempotencyKey, replyToMessageId?, effect? }) sends a threaded reply or an iMessage effect, and turn.action({ action: 'react' | 'edit' | 'delete' | 'send_file', ... }) runs a native channel action; a channel that cannot do it answers 400 unsupported before anything is sent. turn.sendFile({ data, filename, contentType?, caption? }) uploads a file from your server and sends it. Inbound files are on message.attachments: each has a short-lived download url, and turn.downloadAttachment(id) fetches it for as long as the turn is open. verifyKeikiRequest also returns { kind: 'reaction', reaction } for a contact's keiki.reaction event, which never needs a turn token. Give your model keikiMessagePrompt(message) (or keikiMessagesPrompt(turn.messages)) rather than raw text: it uses Keiki's annotated content, which carries the message id the reply guidance names, and prefixes PROMPT_GUARD_NOTE to screened messages.
The dispatch signature covers ${timestamp}.${rawBody} using HMAC-SHA256. Keep the signing secret and per-turn ht_ token private. For runtimes that cannot carry the dispatch token between processes, use signTurnAuth({ signingSecret, agentId, turnId }) to create a short-lived kts1.… bearer credential; verifyKeikiRequest(..., { requireTurnToken: false }) verifies the dispatch without requiring its Keiki-Turn-Token header. Turn clients accept a static bearer or a provider resolved afresh on every request and retry. Message delivery uses the API's idempotencyKey JSON field; a 502 from message() is not retried because delivery may have succeeded despite the response. Only typing and completion calls retry transient failures.
