@ai-matrx/applets
v0.19.0
Published
AI Matrx Applets: the contract types and AppletHost doorway, an in-memory host for tests and development, the React frame hooks Applet code imports (useRows, useJob, useValue, useAction, Pages, Applet, Kind), and mountApplet, which compiles a stored multi
Maintainers
Readme
@ai-matrx/applets
Applets are custom interfaces an agent builds on a person's own data and the platform's features. This package is the doorway between an Applet and whatever hosts it.
| Entry | What it is |
|---|---|
| @ai-matrx/applets | The contract types and the AppletHost interface. No React. |
| @ai-matrx/applets/host | createMemoryHost(spec) — an in-memory host for tests and development. |
| @ai-matrx/applets/react | What Applet code imports: useRows, useJob, useConversation, WritingBox, useValue, useAction, Pages, … |
| @ai-matrx/applets/platform | createPlatformHost(opts) — the real host over the live platform; every client injected. |
| @ai-matrx/applets/frame | mountApplet(record, host, scope) — compile a stored Applet and render it. |
import { createMemoryHost } from "@ai-matrx/applets/host";
import { mountApplet } from "@ai-matrx/applets/frame";
const host = createMemoryHost({ record, tables: { posts: { columns, rows } } });
const mounted = mountApplet(record, host, { entries: [], shadowDangerousGlobals: true });
if (mounted.ok) root.render(<mounted.Component />);Inside an Applet:
import { useRows, useJob, JobOutput, Pages } from "@ai-matrx/applets/react";
export default function Posts() {
const { rows, update, error } = useRows("posts"); // optimistic, versioned writes
const draft = useJob("draft"); // a declared job, by alias
return (
<>
<button onClick={() => draft.run({ topic: "launch" })}>Draft</button>
<JobOutput job={draft} label="Drafting" /> {/* the run streams here */}
<ul>{rows.map((r) => <li key={r._id}>{String(r.title)}</li>)}</ul>
</>
);
}A job's run always renders through <JobOutput job={…} /> — never job.text in a paragraph,
never a "busy" label while it works, never <Kind> on job.result at the end. The host renders the
run through its one live-run pipeline (streamed text, partial kinds, tool steps, loading and errors,
the final kind) via renderRun. Where there is no room to stream inline, use
<JobOutput job={…} mode="window" /> (each run opens once in the host's floating run window) or call
job.open() from a button. Hosts supply renderRun / openRun to mountApplet / AppletFrame.
A heading around a run never claims success before the run has one. A title like "Content ready for
publication" above <JobOutput> shows only when job.status === "done"; while it works or after it fails
(job.status === "error"), say what is happening or nothing. job.error.message is always a plain sentence
for the visitor (forVisitor); the server's own words go to the host's error inspector, never to the page.
A box a person writes in is <WritingBox value={text} onValueChange={setText} /> — never Textarea.
It renders the host's writing box (renderWritingBox), which carries the platform's microphone (dictation) and
read-aloud; <ConversationComposer> writes through it too.
A conversation is useConversation(alias) — a multi-turn chat with one declared job. The first
send(text, variables?) starts it, every later send continues the same conversation, stop() stops
the turn in flight. Render it with <ConversationOutput conversation={…} />: each answer streams through
the host's same live-run pipeline, never hand-rendered.
const chat = useConversation("assistant");
<ConversationOutput conversation={chat} label="Answering" />
<button onClick={() => chat.send(draftText)}>Send</button>Hooks never throw: each answers a status and an error carrying the system's own sentence. On useRows / useRow,
error means the data could not be read; a save the store refused is writeError (its sentence) — show it beside the
control that saved, never in place of the list.
React and React DOM are peer dependencies. The contract this implements is
common-docs/projects/applets/CONTRACTS.md (v2).
