Playground

A chidori agent running entirely in this tab — ask it about chidori, or hand it a tool. Reload mid-conversation and it resumes; every effect is journaled.

Loading the wasm engine…

Under the hood
  • This chat is a chidori agent — the source below — executed by the pure-Rust engine compiled to WebAssembly, entirely in this tab.
  • Every chidori.prompt / tool / input effect is journaled: the conversation auto-saves each turn, survives a reload, and Replay offline repaints it — cards and all — with zero live calls.
  • Docs answers are grounded: these docs are indexed at build time, retrieved into the model's context, and exposed as the search_docs tool.
type Decision = { tool?: string; args?: unknown; reply?: string };
type Message = { role: string; content: string };

const transcript: Message[] = [];

function emit(event: unknown): void {
  console.log(JSON.stringify(event));
}

async function turn(userText: string): Promise<void> {
  transcript.push({ role: 'user', content: userText });
  emit({ kind: 'user', text: userText });

  for (let hop = 0; hop < 6; hop++) {
    // The host answers with one JSON decision: {tool, args} or {reply}.
    const raw = await chidori.prompt(JSON.stringify(transcript), {
      protocol: 'chat-v1',
    });
    let decision: Decision;
    try {
      decision = JSON.parse(String(raw)) as Decision;
    } catch (err) {
      decision = { reply: String(raw) };
    }

    if (decision.tool) {
      let result: unknown;
      try {
        result = await chidori.tool(decision.tool, decision.args);
      } catch (err) {
        result = { error: String(err) };
      }
      emit({ kind: 'tool', name: decision.tool, args: decision.args, result });
      transcript.push({
        role: 'tool',
        content: JSON.stringify({ name: decision.tool, result }),
      });
      continue;
    }

    const reply = decision.reply ? String(decision.reply) : '\u2026';
    emit({ kind: 'assistant', text: reply });
    transcript.push({ role: 'assistant', content: reply });
    return;
  }

  const bail = 'I hit the tool-call limit for this turn \u2014 ask me to continue.';
  emit({ kind: 'assistant', text: bail });
  transcript.push({ role: 'assistant', content: bail });
}

async function main(): Promise<void> {
  for (;;) {
    const text = await chidori.input('message');
    await turn(String(text));
  }
}
main();