dashboard
Data types for dashboard widgets. The Widget type, the Binding and WidgetSource schemas, and WidgetSize, also used by services.widgets.
This module defines the data structure of dashboard widgets. There are two kinds of widget. The
agent builds the first kind, which shows a UI description (the spec) filled by data bindings. A
plugin provides the second kind and draws it with native components. A plugin uses these types when
it reads or writes widgets through services.widgets.
Source: packages/sdk/src/dashboard.ts. For the concepts, see Dashboard.
Types and schemas
import { z } from 'zod';
export const bindingSchema = z.object({
tool: z.string().describe('read-only tool id, e.g. "calendar.today"'),
args: z.record(z.string(), z.unknown()).optional(),
/** Dot path into the tool result; empty = the whole result. */
path: z.string().optional(),
/** Cache lifetime in minutes (default 5). */
ttlMinutes: z.number().min(0).max(24 * 60).optional(),
});
export type Binding = z.infer<typeof bindingSchema>;
export const widgetSizeSchema = z
.enum(['sm', 'md', 'lg'])
.describe('sm = half width tile, md = full width, lg = full width hero');
export type WidgetSize = z.infer<typeof widgetSizeSchema>;
/** A widget a plugin ships (`contributes.widgets`), placed on the dashboard by key. */
export const widgetSourceSchema = z.object({
plugin: z.string(),
widget: z.string().describe("key in the plugin's contributes.widgets"),
config: z.record(z.string(), z.unknown()).optional(),
});
export type WidgetSource = z.infer<typeof widgetSourceSchema>;
/**
* Two kinds, rendered side by side: AI-built (`spec` + `bindings`) or plugin-provided
* (`source`, the plugin draws it natively; `spec`/`bindings` stay empty).
*/
export type Widget = {
id: string;
title: string;
size: WidgetSize;
/** json-render spec ({ root, elements, state? }). */
spec: Record<string, unknown>;
/** State path (JSON pointer, e.g. "/card") → data source. */
bindings: Record<string, Binding>;
source?: WidgetSource;
position: number;
createdAt: number;
updatedAt: number;
};An sm widget is a half-width tile, md fills the row, and lg is a large card that fills the row.
How bindings fill in data
Each binding calls a read-only tool and writes the result into the spec's state with
setPointer. For example, a binding at /card with path: "balance"
makes { "$state": "/card" } in the spec resolve to the balance.
Placing a plugin widget from a tool
To place a plugin's widget from a tool, build a Widget with a source and pass it to put:
const w: Widget = {
id: ctx.services.widgets.newId(),
title: '校园卡余额',
size: 'md',
spec: {},
bindings: {},
source: { plugin: 'wallet', widget: 'card', config: { warnBelow: 30 } },
position: ctx.services.widgets.list().length,
createdAt: Date.now(),
updatedAt: Date.now(),
};
await ctx.services.widgets.put(w);Most plugins never need to do this, because the agent places widgets itself with
dashboard.addPluginWidget.
genui
specSchema and the UISpec type, which describe generative UI specs, plus the SpecCheck result that services.genui.checkSpec returns.
scheduler
Types and helpers for the scheduler. Trigger, Action, Condition and Job with their zod schemas, the pure recurrence engine, the model-friendly ScheduleInput format, condition evaluation and the limits for unattended runs.