AkurAI Build
Menu

Public repository / 91bd2a37654909c5a3aef6ddd614991bd378ccb8

AkurAI-Framework / site/frontend/ai/runtime/index.js

Latest change 22a320a8e40a1822b386e2d5fd9bcf3537282876 - feat(ai): generative-UI / tool-call runtime + primitives by Ólafur Búi Ólafsson

// index.js — public entry point for the generative-UI / tool-call runtime.
//
// Re-exports the whole runtime API and wires the bridge to the chat core: it
// listens for `chat:toolcall` DOM events and renders the matched component into
// the message element. Importing this module is enough to activate the bridge.
//
// ── chat:toolcall CONTRACT ───────────────────────────────────────────────────
//
//   document.dispatchEvent(new CustomEvent("chat:toolcall", { detail: {
//     payload,        // the tool-call payload object to render
//     target,         // (optional) HTMLElement to append the rendered node into
//     ctx,            // (optional) partial ctx; merged over the default ctx
//     toolCallId,     // (optional) threaded into receipts
//   }}))
//
// On each event the runtime renders via renderToolPayload(payload, ctx) and:
//   - appends the node to detail.target if present, and
//   - dispatches `toolui:rendered` (detail = { element, payload, toolCallId }).
//
// The default ctx routes sendReceipt through receipts.js, so any interactive
// component reaches the assistant's tool-result channel out of the box.

import registry, {
  registerToolComponent,
  registerAll,
  renderToolPayload,
  getRegistered,
  clearRegistry,
  normalizeCtx,
} from "./registry.js";
import { sendReceipt, makeReceiptCtx, RECEIPT_ENDPOINT } from "./receipts.js";
import { renderApproval, genericApprovalComponent } from "./approvals.js";
import { createThreadList } from "./threadlist.js";
import { createBranchPicker } from "./branch.js";
import { createReasoning } from "./reasoning.js";
import { createSuggestions } from "./suggestions.js";

// The default ctx — receipts wired to the assistant tool-result channel.
function defaultCtx(extra) {
  return normalizeCtx({
    sendReceipt,
    onAction(name, data) {
      console.log("[runtime] action:", name, data);
    },
    theme: "dark",
    ...(extra || {}),
  });
}

// Render a tool payload with the runtime default ctx (sendReceipt wired).
function render(payload, extraCtx) {
  return renderToolPayload(payload, defaultCtx(extraCtx));
}

// The bridge handler. Exported so it can be re-bound or tested in isolation.
function handleToolCall(event) {
  const detail = (event && event.detail) || {};
  const ctx = defaultCtx({
    ...(detail.ctx || {}),
    sendReceipt: (obj) =>
      sendReceipt({ toolCallId: detail.toolCallId, ...obj }),
  });
  const el = renderToolPayload(detail.payload, ctx);
  if (detail.target && typeof detail.target.appendChild === "function") {
    detail.target.appendChild(el);
  }
  document.dispatchEvent(
    new CustomEvent("toolui:rendered", {
      detail: { element: el, payload: detail.payload, toolCallId: detail.toolCallId },
    })
  );
  return el;
}

// Activate the chat-core bridge exactly once.
let bridged = false;
function activate() {
  if (bridged || typeof document === "undefined") return;
  bridged = true;
  document.addEventListener("chat:toolcall", handleToolCall);
  console.log("[runtime] chat:toolcall bridge active");
}
activate();

const ToolUiRuntime = {
  // registry / dispatch
  registerToolComponent,
  registerAll,
  renderToolPayload,
  render,
  getRegistered,
  clearRegistry,
  normalizeCtx,
  // receipts
  sendReceipt,
  makeReceiptCtx,
  RECEIPT_ENDPOINT,
  // approvals
  renderApproval,
  genericApprovalComponent,
  // primitives
  createThreadList,
  createBranchPicker,
  createReasoning,
  createSuggestions,
  // bridge
  handleToolCall,
  activate,
  registry,
  version: "1.0.0",
};
if (typeof window !== "undefined") window.ToolUiRuntime = ToolUiRuntime;

export {
  registerToolComponent,
  registerAll,
  renderToolPayload,
  render,
  getRegistered,
  clearRegistry,
  normalizeCtx,
  sendReceipt,
  makeReceiptCtx,
  RECEIPT_ENDPOINT,
  renderApproval,
  genericApprovalComponent,
  createThreadList,
  createBranchPicker,
  createReasoning,
  createSuggestions,
  handleToolCall,
  activate,
};
export default ToolUiRuntime;