@agent-native/core 0.148.0 → 0.148.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/sanitize-html.js +66 -2
- package/dist/client/blocks/library/wireframe-kit.js +69 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +4 -4
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/styles/blocks.css +132 -0
- package/docs/content/actions.mdx +4 -1
- package/docs/content/agent-surfaces.mdx +8 -6
- package/docs/content/automations.mdx +1 -1
- package/docs/content/database.mdx +6 -1
- package/docs/content/docs-components.mdx +22 -14
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/getting-started-actions.mdx +5 -4
- package/docs/content/getting-started-pages.mdx +7 -6
- package/docs/content/getting-started.mdx +4 -3
- package/docs/content/notifications.mdx +6 -6
- package/docs/content/progress.mdx +5 -5
- package/docs/content/routing.mdx +3 -1
- package/docs/content/server-database.mdx +13 -4
- package/docs/content/server-overview.mdx +9 -9
- package/docs/content/server-plugins.mdx +10 -4
- package/docs/content/server-routes.mdx +7 -2
- package/docs/content/tracking.mdx +4 -4
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ import { RoughOverlay, toggleWireframeStyle, useIsDark, useWireframeStyle, } fro
|
|
|
41
41
|
/* HTML/SVG diagram path */
|
|
42
42
|
/* -------------------------------------------------------------------------- */
|
|
43
43
|
/** The rough-overlay selector for diagram bordered boxes (mirrors the plan). */
|
|
44
|
-
const DIAGRAM_ROUGH_SELECTOR = "[data-rough],.diagram-panel,.diagram-node,.diagram-box,.diagram-pill,.diagram-card,[class*='card'],[class*='box'],[class*='panel'],[class*='pill'],[class*='chip'],[class*='badge'],hr";
|
|
44
|
+
const DIAGRAM_ROUGH_SELECTOR = "[data-rough],.diagram-panel,.diagram-node,.diagram-box,.diagram-pill,.diagram-card,.diagram-arrow[data-arrow],[class*='card'],[class*='box'],[class*='panel'],[class*='pill'],[class*='chip'],[class*='badge'],hr";
|
|
45
45
|
function HtmlDiagram({ data, ctx, compact, }) {
|
|
46
46
|
const ref = useRef(null);
|
|
47
47
|
const isDark = useIsDark();
|
|
@@ -30,6 +30,28 @@ const URL_ATTRS = new Set([
|
|
|
30
30
|
"ping",
|
|
31
31
|
]);
|
|
32
32
|
const SAFE_SCHEMES = new Set(["http:", "https:", "mailto:", "tel:", "ftp:"]);
|
|
33
|
+
const DIAGRAM_ARROW_DIRECTIONS = new Set([
|
|
34
|
+
"right",
|
|
35
|
+
"left",
|
|
36
|
+
"up",
|
|
37
|
+
"down",
|
|
38
|
+
"both",
|
|
39
|
+
"refresh",
|
|
40
|
+
]);
|
|
41
|
+
const DIAGRAM_ARROW_ENTITY_TEXT = {
|
|
42
|
+
"→": "→",
|
|
43
|
+
"←": "←",
|
|
44
|
+
"↑": "↑",
|
|
45
|
+
"↓": "↓",
|
|
46
|
+
"↔": "↔",
|
|
47
|
+
"←": "←",
|
|
48
|
+
"↑": "↑",
|
|
49
|
+
"→": "→",
|
|
50
|
+
"↓": "↓",
|
|
51
|
+
"↔": "↔",
|
|
52
|
+
"➡": "➡",
|
|
53
|
+
"↻": "↻",
|
|
54
|
+
};
|
|
33
55
|
const WHITESPACE = /\s+/g;
|
|
34
56
|
const HAS_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
35
57
|
const SAFE_DATA_IMAGE = /^data:image\/(png|jpe?g|gif|webp);/i;
|
|
@@ -109,6 +131,47 @@ function stripWireframeThemeClasses(value) {
|
|
|
109
131
|
.filter((className) => !isWireframeThemeClass(className))
|
|
110
132
|
.join(" ");
|
|
111
133
|
}
|
|
134
|
+
function diagramArrowDirection(value) {
|
|
135
|
+
const normalized = value
|
|
136
|
+
.replace(/&(?:r|l|u|d|h)arr;|&#(?:8592|8593|8594|8595|8596|8635|10145);/gi, (entity) => DIAGRAM_ARROW_ENTITY_TEXT[entity.toLowerCase()] ?? entity)
|
|
137
|
+
.replace(/\uFE0F/g, "")
|
|
138
|
+
.trim();
|
|
139
|
+
if (DIAGRAM_ARROW_DIRECTIONS.has(normalized))
|
|
140
|
+
return normalized;
|
|
141
|
+
if (/[↔⇄⇆⇔]/u.test(normalized))
|
|
142
|
+
return "both";
|
|
143
|
+
if (/[←⬅⇐]/u.test(normalized))
|
|
144
|
+
return "left";
|
|
145
|
+
if (/[↑⬆⇑]/u.test(normalized))
|
|
146
|
+
return "up";
|
|
147
|
+
if (/[↓⬇⇓]/u.test(normalized))
|
|
148
|
+
return "down";
|
|
149
|
+
if (/[↻↺⟳⟲]/u.test(normalized))
|
|
150
|
+
return "refresh";
|
|
151
|
+
if (/[→➡➜➔➝➞➟➠⇢⇒]/u.test(normalized))
|
|
152
|
+
return "right";
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
function normalizeDiagramArrows(root) {
|
|
156
|
+
root.querySelectorAll(".diagram-arrow").forEach((node) => {
|
|
157
|
+
const explicit = node.getAttribute("data-arrow");
|
|
158
|
+
const direction = DIAGRAM_ARROW_DIRECTIONS.has(explicit ?? "")
|
|
159
|
+
? explicit
|
|
160
|
+
: diagramArrowDirection(node.textContent ?? "");
|
|
161
|
+
if (!direction)
|
|
162
|
+
return;
|
|
163
|
+
node.setAttribute("data-arrow", direction);
|
|
164
|
+
node.replaceChildren();
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function normalizeFallbackDiagramArrows(html) {
|
|
168
|
+
return html.replace(/(<(?:div|span)\b[^>]*\bclass\s*=\s*(?:"[^"]*\bdiagram-arrow\b[^"]*"|'[^']*\bdiagram-arrow\b[^']*')[^>]*>)([\s\S]*?)(<\/(?:div|span)>)/gi, (_match, open, body, close) => {
|
|
169
|
+
const direction = diagramArrowDirection(body);
|
|
170
|
+
if (!direction)
|
|
171
|
+
return `${open}${body}${close}`;
|
|
172
|
+
return `${open.replace(/>$/, ` data-arrow="${direction}">`)}${close}`;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
112
175
|
/** Conservative no-DOM fallback for any non-browser code path (SSR). */
|
|
113
176
|
function fallbackStrip(html, options) {
|
|
114
177
|
// Drop the whole url attribute if its value (whitespace/control-stripped)
|
|
@@ -147,13 +210,14 @@ export function sanitizeDiagramHtml(html) {
|
|
|
147
210
|
if (!html)
|
|
148
211
|
return "";
|
|
149
212
|
if (typeof DOMParser === "undefined" || typeof document === "undefined") {
|
|
150
|
-
return fallbackStrip(html)
|
|
213
|
+
return normalizeFallbackDiagramArrows(fallbackStrip(html)
|
|
151
214
|
.replace(/<\/?\s*(?:math|foreignObject|foreignobject)\b[^>]*>/gi, "")
|
|
152
|
-
.replace(/\s(?:@[\w:.-]+|x-on:[\w:.-]+|:on[\w:.-]+|x-bind:on[\w:.-]+|:style|x-bind:style)\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, "");
|
|
215
|
+
.replace(/\s(?:@[\w:.-]+|x-on:[\w:.-]+|:on[\w:.-]+|x-bind:on[\w:.-]+|:style|x-bind:style)\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, ""));
|
|
153
216
|
}
|
|
154
217
|
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
155
218
|
doc.querySelectorAll(DIAGRAM_BLOCKED_TAGS).forEach((el) => el.remove());
|
|
156
219
|
sanitizeElementAttributes(doc.body, { stripRuntimeDirectives: true });
|
|
220
|
+
normalizeDiagramArrows(doc.body);
|
|
157
221
|
return doc.body.innerHTML;
|
|
158
222
|
}
|
|
159
223
|
export function sanitizeWireframeCss(css) {
|
|
@@ -745,6 +745,18 @@ function elementStroke(node, fallback) {
|
|
|
745
745
|
if (explicit)
|
|
746
746
|
return explicit;
|
|
747
747
|
const cs = getComputedStyle(node);
|
|
748
|
+
if (node.classList.contains("diagram-arrow")) {
|
|
749
|
+
const color = cs.color;
|
|
750
|
+
const normalizedColor = color.replace(/\s+/g, "").toLowerCase();
|
|
751
|
+
const isTransparent = normalizedColor === "transparent" ||
|
|
752
|
+
normalizedColor.endsWith("/0)") ||
|
|
753
|
+
(normalizedColor.startsWith("rgb") &&
|
|
754
|
+
normalizedColor.split(",").length === 4 &&
|
|
755
|
+
normalizedColor.endsWith(",0)"));
|
|
756
|
+
if (color && !isTransparent) {
|
|
757
|
+
return color;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
748
760
|
for (const side of [
|
|
749
761
|
"borderTopColor",
|
|
750
762
|
"borderLeftColor",
|
|
@@ -831,6 +843,56 @@ function build(scope, opts) {
|
|
|
831
843
|
strokeWidth: sw,
|
|
832
844
|
preserveVertices: true,
|
|
833
845
|
});
|
|
846
|
+
const drawRoughArrow = (direction, x, y, w, h, stroke, sw, seedBase) => {
|
|
847
|
+
let lineIndex = 0;
|
|
848
|
+
const drawLine = (x1, y1, x2, y2) => {
|
|
849
|
+
push(gen.line(x1, y1, x2, y2, makeOpts(stroke, sw, seedFrom(seedBase, "line", lineIndex++))), stroke, sw);
|
|
850
|
+
};
|
|
851
|
+
const drawDirectional = (fromX, fromY, toX, toY) => {
|
|
852
|
+
const angle = Math.atan2(toY - fromY, toX - fromX);
|
|
853
|
+
const length = Math.hypot(toX - fromX, toY - fromY);
|
|
854
|
+
const head = Math.min(7, Math.max(4, length * 0.28));
|
|
855
|
+
const shaftEndX = toX - Math.cos(angle) * head;
|
|
856
|
+
const shaftEndY = toY - Math.sin(angle) * head;
|
|
857
|
+
drawLine(fromX, fromY, shaftEndX, shaftEndY);
|
|
858
|
+
const spread = Math.PI / 6;
|
|
859
|
+
drawLine(toX, toY, toX - Math.cos(angle - spread) * head, toY - Math.sin(angle - spread) * head);
|
|
860
|
+
drawLine(toX, toY, toX - Math.cos(angle + spread) * head, toY - Math.sin(angle + spread) * head);
|
|
861
|
+
};
|
|
862
|
+
const centerX = x + w / 2;
|
|
863
|
+
const centerY = y + h / 2;
|
|
864
|
+
const pad = Math.min(3, w / 5, h / 5);
|
|
865
|
+
const horizontalFrom = x + pad;
|
|
866
|
+
const horizontalTo = x + w - pad;
|
|
867
|
+
const verticalFrom = y + pad;
|
|
868
|
+
const verticalTo = y + h - pad;
|
|
869
|
+
if (direction === "refresh") {
|
|
870
|
+
const diameter = Math.max(8, Math.min(w, h) - pad * 2);
|
|
871
|
+
const start = -Math.PI * 0.8;
|
|
872
|
+
const stop = Math.PI * 1.15;
|
|
873
|
+
push(gen.arc(centerX, centerY, diameter, diameter, start, stop, false, makeOpts(stroke, sw, seedFrom(seedBase, "arc"))), stroke, sw);
|
|
874
|
+
drawDirectional(x + w - pad - 5, y + pad + 1, x + w - pad, y + pad + 1);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (direction === "down") {
|
|
878
|
+
drawDirectional(centerX, verticalFrom, centerX, verticalTo);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (direction === "up") {
|
|
882
|
+
drawDirectional(centerX, verticalTo, centerX, verticalFrom);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (direction === "left") {
|
|
886
|
+
drawDirectional(horizontalTo, centerY, horizontalFrom, centerY);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (direction === "both") {
|
|
890
|
+
drawDirectional(horizontalFrom, centerY, horizontalTo, centerY);
|
|
891
|
+
drawDirectional(horizontalTo, centerY, horizontalFrom, centerY);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
drawDirectional(horizontalFrom, centerY, horizontalTo, centerY);
|
|
895
|
+
};
|
|
834
896
|
if (opts.drawFrame && hasDrawableRoughBounds(layoutW, layoutH, 2)) {
|
|
835
897
|
const sw = 2;
|
|
836
898
|
push(gen.path(roundedRectPath(2, 2, layoutW - 4, layoutH - 4, opts.frameRadius), {
|
|
@@ -858,6 +920,13 @@ function build(scope, opts) {
|
|
|
858
920
|
: rawStroke;
|
|
859
921
|
const sw = Number(readVar(node, "--rough-w")) || 1.4;
|
|
860
922
|
const seed = seedFrom(kind, Math.round(x), Math.round(y), Math.round(w), Math.round(h), index++);
|
|
923
|
+
const arrow = node.classList.contains("diagram-arrow")
|
|
924
|
+
? node.getAttribute("data-arrow")
|
|
925
|
+
: null;
|
|
926
|
+
if (arrow) {
|
|
927
|
+
drawRoughArrow(arrow, x, y, w, h, stroke, sw, seedFrom("arrow", seed));
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
861
930
|
const o = makeOpts(stroke, sw, seed);
|
|
862
931
|
let drawable;
|
|
863
932
|
if (kind === "ellipse") {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatFirstAppRailProps } from "./types.js";
|
|
2
|
-
export declare function ChatFirstAppsRail({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps): import("react").JSX.Element;
|
|
2
|
+
export declare function ChatFirstAppsRail({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
3
|
-
import { IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
|
|
3
|
+
import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
|
|
4
4
|
import { useMemo, useState } from "react";
|
|
5
5
|
import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
|
|
6
6
|
import { cn } from "../utils.js";
|
|
@@ -36,7 +36,7 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
|
|
|
36
36
|
}), "aria-pressed": pinned, title: pinned ? copy("removePinned") : copy("pinTop"), onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] })] })] }, app.id));
|
|
37
37
|
}) }));
|
|
38
38
|
}
|
|
39
|
-
export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
39
|
+
export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
40
40
|
const [localLayout, setLocalLayout] = useState(() => readChatFirstAppLayout());
|
|
41
41
|
const layout = controlledLayout ?? localLayout;
|
|
42
42
|
const [draggedAppId, setDraggedAppId] = useState(null);
|
|
@@ -97,7 +97,7 @@ export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, c
|
|
|
97
97
|
? [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "size-9 rounded-md" }, index)))
|
|
98
98
|
: visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md transition-colors", activeAppId === app.id
|
|
99
99
|
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
100
|
-
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
|
|
100
|
+
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
|
|
101
101
|
}
|
|
102
|
-
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-7 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-6 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
102
|
+
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-7 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-6 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-7 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
103
103
|
}
|
|
@@ -44,6 +44,7 @@ export interface ChatFirstAppRailProps {
|
|
|
44
44
|
onLayoutError?: (reason: "unavailable" | "write-failed") => void;
|
|
45
45
|
onRetry?: () => void;
|
|
46
46
|
onOpenApp: (app: ChatFirstAppItem) => void;
|
|
47
|
+
onOpenAllApps?: () => void;
|
|
47
48
|
onCreateApp?: () => void;
|
|
48
49
|
createAppTrigger?: ReactNode;
|
|
49
50
|
renderIcon: (app: ChatFirstAppItem) => ReactNode;
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
* DELETE /_agent-native/notifications/:id — delete
|
|
12
12
|
*/
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
|
+
error?: undefined;
|
|
14
15
|
count: number;
|
|
15
16
|
updated?: undefined;
|
|
16
|
-
error?: undefined;
|
|
17
17
|
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
|
+
error?: undefined;
|
|
19
20
|
count?: undefined;
|
|
20
21
|
updated: number;
|
|
21
|
-
error?: undefined;
|
|
22
22
|
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
@@ -26,8 +26,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
error: string;
|
|
27
27
|
ok?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
count?: undefined;
|
|
30
31
|
updated?: undefined;
|
|
31
|
-
error?: undefined;
|
|
32
32
|
ok: boolean;
|
|
33
33
|
}>>;
|
|
@@ -41,23 +41,23 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error?: undefined;
|
|
45
46
|
summary: import("./types.js").TraceSummary;
|
|
46
47
|
spans: import("./types.js").TraceSpan[];
|
|
47
48
|
id?: undefined;
|
|
48
|
-
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
ok?: undefined;
|
|
50
51
|
error?: undefined;
|
|
51
52
|
summary?: undefined;
|
|
52
53
|
spans?: undefined;
|
|
53
54
|
id: string;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
error: string;
|
|
52
|
+
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
package/dist/styles/blocks.css
CHANGED
|
@@ -1333,6 +1333,138 @@
|
|
|
1333
1333
|
direction: inherit;
|
|
1334
1334
|
}
|
|
1335
1335
|
|
|
1336
|
+
/* Diagram arrows are geometry, not text glyphs. The clean path draws these
|
|
1337
|
+
shapes with CSS; sketchy mode replaces them with the rough.js paths below. */
|
|
1338
|
+
.plan-diagram-frame :is(.diagram-arrow[data-arrow]) {
|
|
1339
|
+
position: relative;
|
|
1340
|
+
display: inline-block;
|
|
1341
|
+
flex: 0 0 28px;
|
|
1342
|
+
width: 28px;
|
|
1343
|
+
height: 24px;
|
|
1344
|
+
min-width: 28px;
|
|
1345
|
+
font-size: 0 !important;
|
|
1346
|
+
line-height: 1;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
.plan-diagram-frame .diagram-arrow[data-arrow]::before,
|
|
1350
|
+
.plan-diagram-frame .diagram-arrow[data-arrow]::after {
|
|
1351
|
+
position: absolute;
|
|
1352
|
+
display: block;
|
|
1353
|
+
content: "";
|
|
1354
|
+
pointer-events: none;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.plan-diagram-frame
|
|
1358
|
+
:is(
|
|
1359
|
+
.diagram-arrow[data-arrow="right"],
|
|
1360
|
+
.diagram-arrow[data-arrow="left"]
|
|
1361
|
+
)::before {
|
|
1362
|
+
top: calc(50% - 0.75px);
|
|
1363
|
+
left: 3px;
|
|
1364
|
+
width: 22px;
|
|
1365
|
+
height: 1.5px;
|
|
1366
|
+
background: currentColor;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
.plan-diagram-frame
|
|
1370
|
+
:is(
|
|
1371
|
+
.diagram-arrow[data-arrow="up"],
|
|
1372
|
+
.diagram-arrow[data-arrow="down"]
|
|
1373
|
+
)::before {
|
|
1374
|
+
top: 2px;
|
|
1375
|
+
left: calc(50% - 0.75px);
|
|
1376
|
+
width: 1.5px;
|
|
1377
|
+
height: 20px;
|
|
1378
|
+
background: currentColor;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="right"]::after,
|
|
1382
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="left"]::after,
|
|
1383
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="up"]::after,
|
|
1384
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="down"]::after {
|
|
1385
|
+
width: 7px;
|
|
1386
|
+
height: 7px;
|
|
1387
|
+
border-top: 1.5px solid currentColor;
|
|
1388
|
+
border-right: 1.5px solid currentColor;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="right"]::after {
|
|
1392
|
+
top: calc(50% - 4px);
|
|
1393
|
+
right: 2px;
|
|
1394
|
+
transform: rotate(45deg);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="left"]::after {
|
|
1398
|
+
top: calc(50% - 4px);
|
|
1399
|
+
left: 2px;
|
|
1400
|
+
transform: rotate(225deg);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="up"]::after {
|
|
1404
|
+
top: 0;
|
|
1405
|
+
left: calc(50% - 4px);
|
|
1406
|
+
transform: rotate(-45deg);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="down"]::after {
|
|
1410
|
+
bottom: 0;
|
|
1411
|
+
left: calc(50% - 4px);
|
|
1412
|
+
transform: rotate(135deg);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="both"]::before {
|
|
1416
|
+
top: calc(50% - 0.75px);
|
|
1417
|
+
left: 3px;
|
|
1418
|
+
width: 22px;
|
|
1419
|
+
height: 1.5px;
|
|
1420
|
+
background: currentColor;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="both"]::after {
|
|
1424
|
+
inset: 3px;
|
|
1425
|
+
background: currentColor;
|
|
1426
|
+
clip-path: polygon(
|
|
1427
|
+
0 50%,
|
|
1428
|
+
22% 0,
|
|
1429
|
+
22% 38%,
|
|
1430
|
+
78% 38%,
|
|
1431
|
+
78% 0,
|
|
1432
|
+
100% 50%,
|
|
1433
|
+
78% 100%,
|
|
1434
|
+
78% 62%,
|
|
1435
|
+
22% 62%,
|
|
1436
|
+
22% 100%
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="refresh"]::before {
|
|
1441
|
+
top: 3px;
|
|
1442
|
+
left: 5px;
|
|
1443
|
+
width: 16px;
|
|
1444
|
+
height: 16px;
|
|
1445
|
+
border: 1.5px solid currentColor;
|
|
1446
|
+
border-left-color: transparent;
|
|
1447
|
+
border-radius: 50%;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
.plan-diagram-frame .diagram-arrow[data-arrow="refresh"]::after {
|
|
1451
|
+
top: 1px;
|
|
1452
|
+
right: 2px;
|
|
1453
|
+
width: 0;
|
|
1454
|
+
height: 0;
|
|
1455
|
+
border-right: 4px solid transparent;
|
|
1456
|
+
border-bottom: 7px solid currentColor;
|
|
1457
|
+
border-left: 4px solid transparent;
|
|
1458
|
+
transform: rotate(35deg);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
.plan-diagram-frame[data-style="sketchy"][data-rough-ready]
|
|
1462
|
+
.diagram-arrow[data-arrow]::before,
|
|
1463
|
+
.plan-diagram-frame[data-style="sketchy"][data-rough-ready]
|
|
1464
|
+
.diagram-arrow[data-arrow]::after {
|
|
1465
|
+
opacity: 0;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1336
1468
|
.plan-diagram-frame[data-frame="show"] .plan-diagram-frame-content {
|
|
1337
1469
|
padding: 0;
|
|
1338
1470
|
}
|
package/docs/content/actions.mdx
CHANGED
|
@@ -391,12 +391,15 @@ export default defineAction({
|
|
|
391
391
|
description: "Create a lead in the CRM.",
|
|
392
392
|
schema: z.object({ name: z.string(), company: z.string() }),
|
|
393
393
|
run: async ({ name, company }, ctx) => {
|
|
394
|
+
if (!ctx?.userEmail) {
|
|
395
|
+
throw new Error("An authenticated user is required.");
|
|
396
|
+
}
|
|
394
397
|
const db = getDb();
|
|
395
398
|
await db.insert(schema.leads).values({
|
|
396
399
|
id: crypto.randomUUID(),
|
|
397
400
|
name,
|
|
398
401
|
company,
|
|
399
|
-
ownerEmail: ctx
|
|
402
|
+
ownerEmail: ctx.userEmail,
|
|
400
403
|
});
|
|
401
404
|
return { ok: true };
|
|
402
405
|
},
|
|
@@ -10,7 +10,7 @@ A **surface** is the way users (or other systems) interact with your app: a chat
|
|
|
10
10
|
|
|
11
11
|
## How surfaces relate to each other
|
|
12
12
|
|
|
13
|
-
The four main product shapes sit on a spectrum from most interactive to fully headless. What makes them composable is that
|
|
13
|
+
The four main product shapes sit on a spectrum from most interactive to fully headless. What makes them composable is that they share the same actions and SQL-backed app state. Use the framework agent loop when a surface needs an agent. Adding a new surface doesn't mean rewriting what's underneath — you're adding a new way to reach the same operations.
|
|
14
14
|
|
|
15
15
|
<Diagram id="doc-block-hvdxgh" title="The surface spectrum" summary={"One action surface, four product shapes, each adding UI without changing the operation underneath."}>
|
|
16
16
|
|
|
@@ -37,7 +37,9 @@ The four main product shapes sit on a spectrum from most interactive to fully he
|
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
<div class="diagram-base" data-rough>
|
|
40
|
-
<span class="diagram-muted"
|
|
40
|
+
<span class="diagram-muted"
|
|
41
|
+
>same actions · SQL-backed state · agent loop where used</span
|
|
42
|
+
>
|
|
41
43
|
</div>
|
|
42
44
|
```
|
|
43
45
|
|
|
@@ -192,7 +194,7 @@ Full apps add product UI around the same action and agent contract:
|
|
|
192
194
|
|
|
193
195
|
### SQL state
|
|
194
196
|
|
|
195
|
-
App data, navigation, settings, and chat history are
|
|
197
|
+
App data, navigation, settings, and chat history are durable in SQL. The agent reads and writes the same rows the UI does.
|
|
196
198
|
|
|
197
199
|
### Context awareness
|
|
198
200
|
|
|
@@ -200,7 +202,7 @@ The agent knows the current route, selection, and focused object, so "edit this"
|
|
|
200
202
|
|
|
201
203
|
### Live sync
|
|
202
204
|
|
|
203
|
-
Agent changes update the UI
|
|
205
|
+
Agent changes update the UI through live sync, and UI changes update the agent's context. The framework uses SSE with polling fallback, so users do not need to refresh manually.
|
|
204
206
|
|
|
205
207
|
### Deep links
|
|
206
208
|
|
|
@@ -224,9 +226,9 @@ want a complete product shape.
|
|
|
224
226
|
|
|
225
227
|
Every Agent-Native app eventually needs a place where users can configure their agent: set standing instructions, review what it's done, connect MCP servers, manage automations, and control access. Building that UI from scratch is a lot of work. Agent-Native ships a pre-built full-page component, `AgentTabsPage`, that covers all of it across twelve tabs.
|
|
226
228
|
|
|
227
|
-
Mount it at `/agent` in your app. Current templates
|
|
229
|
+
Mount it at `/settings/agent` in your app. Current templates use this as the canonical route and pass `agentPageHref="/settings/agent"` to `AgentSidebar`. `/agent` is kept as a legacy redirect.
|
|
228
230
|
|
|
229
|
-
```tsx filename="app/routes/agent.tsx"
|
|
231
|
+
```tsx filename="app/routes/settings.agent.tsx"
|
|
230
232
|
import { AgentTabsPage } from "@agent-native/core/client/agent-chat";
|
|
231
233
|
export default function AgentRoute() {
|
|
232
234
|
return <AgentTabsPage />;
|
|
@@ -267,7 +267,12 @@ All database schema updates must be **strictly additive**.
|
|
|
267
267
|
|
|
268
268
|
### Safe Migration Path
|
|
269
269
|
|
|
270
|
-
Instead of pushing directly, schema changes should be applied via
|
|
270
|
+
Instead of pushing directly, schema changes should be applied via additive SQL
|
|
271
|
+
migrations. For local development, implement them in a server plugin (e.g.
|
|
272
|
+
`server/plugins/db.ts`) by invoking the framework's `runMigrations()` helper.
|
|
273
|
+
For production or serverless deployments, run migrations in a release or deploy
|
|
274
|
+
step instead of on the first request. The Chat template provides
|
|
275
|
+
`pnpm migrate:production` for this.
|
|
271
276
|
|
|
272
277
|
<AnnotatedCode
|
|
273
278
|
id="doc-block-zaaj7e"
|
|
@@ -105,7 +105,7 @@ pnpm install
|
|
|
105
105
|
pnpm dev
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
Open
|
|
108
|
+
Open the local URL shown by the dev server. The app is ready when **VITE ready** appears in the terminal.
|
|
109
109
|
|
|
110
110
|
### Sign in
|
|
111
111
|
|
|
@@ -130,7 +130,7 @@ pnpm install
|
|
|
130
130
|
pnpm dev
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
Open
|
|
133
|
+
Open the local URL shown by the dev server. The app is ready when **VITE ready** appears in the terminal.
|
|
134
134
|
|
|
135
135
|
### Sign in
|
|
136
136
|
|
|
@@ -215,13 +215,13 @@ Use `Notice` when a `Callout` would be too easy to skim past — something the r
|
|
|
215
215
|
|
|
216
216
|
<Banner
|
|
217
217
|
tone="warning"
|
|
218
|
-
body="This page describes
|
|
218
|
+
body="This page describes a beta API. Check the release notes before using it."
|
|
219
219
|
/>
|
|
220
220
|
|
|
221
221
|
```mdx
|
|
222
222
|
<Banner
|
|
223
223
|
tone="warning"
|
|
224
|
-
body="This page describes
|
|
224
|
+
body="This page describes a beta API. Check the release notes before using it."
|
|
225
225
|
/>
|
|
226
226
|
```
|
|
227
227
|
|
|
@@ -377,13 +377,17 @@ Use a fenced code block with the `mermaid` language tag. Mermaid supports flowch
|
|
|
377
377
|
note: "New action — callable from agent, UI, and CLI",
|
|
378
378
|
},
|
|
379
379
|
{ path: "actions/list-text-analyses.ts", change: "added" },
|
|
380
|
-
{ path: "app/routes/analyses.tsx", change: "
|
|
380
|
+
{ path: "app/routes/analyses.tsx", change: "added" },
|
|
381
381
|
{
|
|
382
|
-
path: "
|
|
383
|
-
change: "
|
|
384
|
-
note: "
|
|
382
|
+
path: "server/db/schema.ts",
|
|
383
|
+
change: "added",
|
|
384
|
+
note: "Define the app-owned table",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
path: "server/plugins/db.ts",
|
|
388
|
+
change: "added",
|
|
389
|
+
note: "Run additive migrations",
|
|
385
390
|
},
|
|
386
|
-
{ path: "db/migrations/001_analyses.sql" },
|
|
387
391
|
]}
|
|
388
392
|
/>
|
|
389
393
|
|
|
@@ -397,13 +401,17 @@ Use a fenced code block with the `mermaid` language tag. Mermaid supports flowch
|
|
|
397
401
|
note: "New action — callable from agent, UI, and CLI",
|
|
398
402
|
},
|
|
399
403
|
{ path: "actions/list-text-analyses.ts", change: "added" },
|
|
400
|
-
{ path: "app/routes/analyses.tsx", change: "
|
|
404
|
+
{ path: "app/routes/analyses.tsx", change: "added" },
|
|
401
405
|
{
|
|
402
|
-
path: "
|
|
403
|
-
change: "
|
|
404
|
-
note: "
|
|
406
|
+
path: "server/db/schema.ts",
|
|
407
|
+
change: "added",
|
|
408
|
+
note: "Define the app-owned table",
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
path: "server/plugins/db.ts",
|
|
412
|
+
change: "added",
|
|
413
|
+
note: "Run additive migrations",
|
|
405
414
|
},
|
|
406
|
-
{ path: "db/migrations/001_analyses.sql" },
|
|
407
415
|
]}
|
|
408
416
|
/>
|
|
409
417
|
```
|
|
@@ -170,7 +170,7 @@ export default function AgentRoute() {
|
|
|
170
170
|
}
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
`<AgentPanel>` gives you the raw tabs (Chat / CLI / Resources) without the sidebar wrapper
|
|
173
|
+
`<AgentPanel>` gives you the raw tabs (Chat / CLI / Resources) without the sidebar wrapper or the collapse button. Put it wherever you want; you control the surrounding layout.
|
|
174
174
|
|
|
175
175
|
### Selected props
|
|
176
176
|
|
|
@@ -72,7 +72,7 @@ export default defineAction({
|
|
|
72
72
|
});
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Run
|
|
75
|
+
Run this from the app directory:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
pnpm action analyze-text --text "Hello world. How are you today?"
|
|
@@ -82,9 +82,10 @@ Or ask the agent in chat:
|
|
|
82
82
|
|
|
83
83
|
> Run the analyze-text action on "Hello world. How are you today?"
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
the
|
|
87
|
-
custom native components, see
|
|
85
|
+
When the agent runs this action in chat, the built-in `data-chart` renderer
|
|
86
|
+
turns the structured result into a chart in the transcript. There is no renderer
|
|
87
|
+
file or `app/root.tsx` import to add. For custom native components, see
|
|
88
|
+
[Native Chat UI](/docs/native-chat-ui).
|
|
88
89
|
|
|
89
90
|
## Continue
|
|
90
91
|
|
|
@@ -6,14 +6,15 @@ description: "Build a React route that calls your action and displays its result
|
|
|
6
6
|
# Add a Page
|
|
7
7
|
|
|
8
8
|
This is part three of the Getting Started series. In [Add an Action](/docs/getting-started-actions)
|
|
9
|
-
you created `analyze-text`. Here you'll call that same action from a route.
|
|
10
|
-
|
|
11
|
-
to persist domain data.
|
|
9
|
+
you created `analyze-text`. Here you'll call that same action from a route. This
|
|
10
|
+
example does not need a new database table. Chat already stores chat history in
|
|
11
|
+
SQL; add your own tables when you need to persist domain data.
|
|
12
12
|
|
|
13
13
|
## Create the route {#add-a-page}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
`app/routes/text-analyses.tsx
|
|
15
|
+
The dev server reads route files when it starts. Create
|
|
16
|
+
`app/routes/text-analyses.tsx`, then restart `pnpm dev` if it is already
|
|
17
|
+
running. The route will be available at
|
|
17
18
|
`http://localhost:8080/text-analyses`.
|
|
18
19
|
|
|
19
20
|
```tsx filename="app/routes/text-analyses.tsx"
|
|
@@ -58,7 +59,7 @@ export default function TextAnalysesRoute() {
|
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
`useActionQuery` calls the same read-only action the agent uses. The page does
|
|
61
|
-
not need a custom API route, a second data layer, or SQL
|
|
62
|
+
not need a custom API route, a second data layer, or a new SQL table.
|
|
62
63
|
|
|
63
64
|
Open `http://localhost:8080/text-analyses`, or ask the agent:
|
|
64
65
|
|
|
@@ -6,9 +6,9 @@ description: "Create a Chat app and configure an agent, then extend it with an a
|
|
|
6
6
|
# Getting Started
|
|
7
7
|
|
|
8
8
|
Agent-Native apps give the agent and the UI the same [actions](/docs/actions),
|
|
9
|
-
SQL data, and application state. This
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
SQL data, and application state. This path starts with Chat, which already
|
|
10
|
+
creates the framework tables it needs and stores chat history in local SQLite by
|
|
11
|
+
default. Add your own tables only when your app needs to persist domain data.
|
|
12
12
|
|
|
13
13
|
<Steps>
|
|
14
14
|
|
|
@@ -65,4 +65,5 @@ Once the agent responds, continue with [Add an Action](/docs/getting-started-act
|
|
|
65
65
|
|
|
66
66
|
- [What Is Agent-Native?](/docs/what-is-agent-native): the architecture behind this path.
|
|
67
67
|
- [Chat Template](/docs/template-chat): what the starter app includes.
|
|
68
|
+
- [Database](/docs/database): add app-owned tables when your app needs domain data.
|
|
68
69
|
- [FAQ](/docs/faq): answers about models, hosting, and templates.
|
|
@@ -14,7 +14,7 @@ import { notify } from "@agent-native/core/notifications";
|
|
|
14
14
|
|
|
15
15
|
await notify(
|
|
16
16
|
{ severity: "info", title: "Booking confirmed", body: "Jane at 3pm" },
|
|
17
|
-
{ owner: "
|
|
17
|
+
{ owner: "user@example.com" },
|
|
18
18
|
);
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -238,14 +238,14 @@ import {
|
|
|
238
238
|
deleteNotification,
|
|
239
239
|
} from "@agent-native/core/notifications";
|
|
240
240
|
|
|
241
|
-
const rows = await listNotifications("
|
|
241
|
+
const rows = await listNotifications("user@example.com", {
|
|
242
242
|
unreadOnly: true,
|
|
243
243
|
limit: 50,
|
|
244
244
|
});
|
|
245
|
-
const unread = await countUnread("
|
|
246
|
-
await markNotificationRead(rows[0].id, "
|
|
247
|
-
await markAllNotificationsRead("
|
|
248
|
-
await deleteNotification(rows[0].id, "
|
|
245
|
+
const unread = await countUnread("user@example.com");
|
|
246
|
+
await markNotificationRead(rows[0].id, "user@example.com");
|
|
247
|
+
await markAllNotificationsRead("user@example.com");
|
|
248
|
+
await deleteNotification(rows[0].id, "user@example.com");
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
Each function is owner-scoped — no cross-user reads, no cross-user writes.
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "@agent-native/core/progress";
|
|
16
16
|
|
|
17
17
|
const run = await startRun({
|
|
18
|
-
owner: "
|
|
18
|
+
owner: "user@example.com",
|
|
19
19
|
title: "Triage 128 unread emails",
|
|
20
20
|
step: "Fetching inbox",
|
|
21
21
|
});
|
|
@@ -100,7 +100,7 @@ Create a run. Returns the full `AgentRun` with a generated id.
|
|
|
100
100
|
|
|
101
101
|
```ts
|
|
102
102
|
const run = await startRun({
|
|
103
|
-
owner: "
|
|
103
|
+
owner: "user@example.com",
|
|
104
104
|
title: "Ingest 1M rows",
|
|
105
105
|
step: "Opening CSV",
|
|
106
106
|
metadata: { jobId: "abc123", artifactPath: "s3://..." },
|
|
@@ -140,9 +140,9 @@ Also emits `run.progress.updated` with the terminal status.
|
|
|
140
140
|
```ts
|
|
141
141
|
import { listRuns, getRun, deleteRun } from "@agent-native/core/progress";
|
|
142
142
|
|
|
143
|
-
const active = await listRuns("
|
|
144
|
-
const run = await getRun("run-id", "
|
|
145
|
-
await deleteRun("run-id", "
|
|
143
|
+
const active = await listRuns("user@example.com", { activeOnly: true });
|
|
144
|
+
const run = await getRun("run-id", "user@example.com");
|
|
145
|
+
await deleteRun("run-id", "user@example.com");
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
## HTTP API {#http}
|
package/docs/content/routing.mdx
CHANGED
|
@@ -82,7 +82,9 @@ export default function SettingsPage() {
|
|
|
82
82
|
}
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
That's it — React Router picks it up automatically
|
|
85
|
+
That's it — React Router picks it up automatically after the route table is
|
|
86
|
+
rebuilt. If `pnpm dev` is already running, restart it after adding the file. No
|
|
87
|
+
registration is needed.
|
|
86
88
|
|
|
87
89
|
## Dynamic params {#dynamic-params}
|
|
88
90
|
|
|
@@ -5,7 +5,7 @@ description: "Connect a portable SQL database to your agent-native app: schema h
|
|
|
5
5
|
|
|
6
6
|
# Database
|
|
7
7
|
|
|
8
|
-
Every agent-native app stores its state in SQL. The UI and the agent both read and write the same tables through the same actions, using the same [Drizzle ORM](https://orm.drizzle.team) client.
|
|
8
|
+
Every agent-native app stores its state in SQL. The UI and the agent both read and write the same tables through the same actions, using the same [Drizzle ORM](https://orm.drizzle.team) client. Live sync uses SSE with polling fallback, so changes appear in the other surface without a manual refresh.
|
|
9
9
|
|
|
10
10
|
<Diagram id="doc-block-9mtx2f" title="How data flows to the database" summary="The UI and the agent both reach the database through the same Actions layer. Both callers use the same getDb() client and the same SQL tables. There is no separate backend for each.">
|
|
11
11
|
|
|
@@ -110,7 +110,7 @@ An app that uses the database needs three files:
|
|
|
110
110
|
|
|
111
111
|
- `server/db/schema.ts`: table definitions
|
|
112
112
|
- `server/db/index.ts`: the typed DB client singleton
|
|
113
|
-
- `server/plugins/db.ts`: migrations
|
|
113
|
+
- `server/plugins/db.ts`: app-owned migrations for local startup
|
|
114
114
|
|
|
115
115
|
### 1. Define your schema {#schema}
|
|
116
116
|
|
|
@@ -208,7 +208,8 @@ Import `getDb` from this template-local path in actions and routes. Do not impor
|
|
|
208
208
|
|
|
209
209
|
### 3. Write migrations {#migrations}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
For local development, schema changes can run through a Nitro plugin in
|
|
212
|
+
`server/plugins/db.ts`. Use `runMigrations` from `@agent-native/core/db`:
|
|
212
213
|
|
|
213
214
|
<AnnotatedCode
|
|
214
215
|
id="doc-block-7qwlpz"
|
|
@@ -242,7 +243,15 @@ Schema changes are applied at startup via a Nitro plugin in `server/plugins/db.t
|
|
|
242
243
|
]}
|
|
243
244
|
/>
|
|
244
245
|
|
|
245
|
-
`runMigrations` runs each entry in order once,
|
|
246
|
+
`runMigrations` runs each pending entry in order once, records it as applied, and
|
|
247
|
+
skips it on later runs. Give new entries a stable `name`; named migrations are
|
|
248
|
+
tracked independently of their version number.
|
|
249
|
+
|
|
250
|
+
<Callout tone="warning">
|
|
251
|
+
For production or serverless deployments, run migrations in a release or
|
|
252
|
+
deploy step instead of on the first request. The Chat template provides `pnpm
|
|
253
|
+
migrate:production` for this.
|
|
254
|
+
</Callout>
|
|
246
255
|
|
|
247
256
|
<Callout tone="warning">
|
|
248
257
|
Give every new migration a stable `name` (e.g. `{ version: 4, name:
|
|
@@ -85,7 +85,7 @@ The `server/` directory is the infrastructure layer of an agent-native app. It h
|
|
|
85
85
|
|
|
86
86
|
## Where the Server Fits {#role}
|
|
87
87
|
|
|
88
|
-
In a traditional web app, the server is where most logic lives. Business rules, data access, and presentation all happen inside route handlers. Agent-native apps work differently. Logic lives in actions, which the agent and UI share as equal partners. The server's job is coordination:
|
|
88
|
+
In a traditional web app, the server is where most logic lives. Business rules, data access, and presentation all happen inside route handlers. Agent-native apps work differently. Logic lives in actions, which the agent and UI share as equal partners. The server's job is coordination: apply app-owned database migrations, configure authentication, mount the agent loop, and handle the narrow set of HTTP concerns that actions can't express on their own.
|
|
89
89
|
|
|
90
90
|
<Diagram id="doc-block-server-overview-2" title="Traditional server vs. Agent Native">
|
|
91
91
|
|
|
@@ -194,19 +194,19 @@ Browser requests, the agent loop, and external API clients all route through the
|
|
|
194
194
|
|
|
195
195
|
These are the directories and files inside `server/` that every agent-native app ships. Each corresponds to a specific piece of the server layer: what it does, when to reach for it, and how it connects to the rest of the framework.
|
|
196
196
|
|
|
197
|
-
| Page | What it covers
|
|
198
|
-
| ----------------------------------------- |
|
|
199
|
-
| **[Database](/docs/server-database)** | Schema definition with `table()` helpers, `getDb`, and ownable table patterns
|
|
200
|
-
| **[Middleware](/docs/server-middleware)** | Auth guards in `server/middleware/` and why they run there instead of in a plugin
|
|
201
|
-
| **[Plugins](/docs/server-plugins)** |
|
|
202
|
-
| **[Routes](/docs/server-routes)** | File-based routes, the SSR catch-all, and scoping custom routes to the request user
|
|
197
|
+
| Page | What it covers |
|
|
198
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
199
|
+
| **[Database](/docs/server-database)** | Schema definition with `table()` helpers, `getDb`, and ownable table patterns |
|
|
200
|
+
| **[Middleware](/docs/server-middleware)** | Auth guards in `server/middleware/` and why they run there instead of in a plugin |
|
|
201
|
+
| **[Plugins](/docs/server-plugins)** | Startup plugins for auth and agent-chat, plus optional app-owned database migrations |
|
|
202
|
+
| **[Routes](/docs/server-routes)** | File-based routes, the SSR catch-all, and scoping custom routes to the request user |
|
|
203
203
|
|
|
204
204
|
## Nitro {#nitro}
|
|
205
205
|
|
|
206
206
|
[Nitro](https://nitro.build) is the server toolkit that powers the agent-native HTTP layer. Most of what you'll configure in `server/` is Nitro: file-based routes, startup plugins, and middleware.
|
|
207
207
|
|
|
208
208
|
- **File-based routing**: the filename maps directly to an HTTP method and URL path, with no registration needed. `server/routes/api/health.get.ts` becomes `GET /api/health` automatically. Visit [Routes](/docs/server-routes) for more details.
|
|
209
|
-
- **Plugins**: code
|
|
209
|
+
- **Plugins**: code registered at server startup and loaded once. Framework helpers initialize auth and the agent-chat mount; optional app-owned database migrations may still be running when the first request arrives. Visit [Plugins](/docs/server-plugins) for more details.
|
|
210
210
|
- **Middleware**: code that runs on every incoming request before it reaches a route. The auth guard lives in `server/middleware/`. Visit [Middleware](/docs/server-middleware) for more details.
|
|
211
211
|
|
|
212
212
|
Nitro is also deployment-agnostic. The same app deploys to Node.js, Cloudflare Workers, Netlify, Vercel, and other runtimes with no code changes. Just set a preset. See [Deployment](/docs/deployment).
|
|
@@ -230,5 +230,5 @@ Use custom `/api/*` routes only when you need a route-shaped protocol or binary/
|
|
|
230
230
|
- [**Actions**](/docs/actions): the default operation surface. Reach for a custom route only when it doesn't fit.
|
|
231
231
|
- [**Database**](/docs/server-database): the `getDb()`/schema pattern custom routes and actions both import
|
|
232
232
|
- [**Middleware**](/docs/server-middleware): auth guards and the `server/middleware/` directory
|
|
233
|
-
- [**Plugins**](/docs/server-plugins): startup plugins for
|
|
233
|
+
- [**Plugins**](/docs/server-plugins): startup plugins for auth, the agent, and optional app-owned migrations
|
|
234
234
|
- [**Routes**](/docs/server-routes): file-based routes, request context, and the SSR catch-all
|
|
@@ -5,7 +5,7 @@ description: "Startup plugins in server/plugins/ — what they are, the two plug
|
|
|
5
5
|
|
|
6
6
|
# Plugins
|
|
7
7
|
|
|
8
|
-
Plugins are startup hooks.
|
|
8
|
+
Plugins are startup hooks. Nitro loads each file in `server/plugins/` once when the server initializes. They are the right place for one-time setup: configuring auth, mounting the agent loop, running database migrations, or registering other initialization code. Framework helpers coordinate their async setup; custom plugin work may still be in flight when the first request arrives.
|
|
9
9
|
|
|
10
10
|
Nitro discovers plugins automatically from the filesystem. No registration or imports are required. Files run in alphabetical order, so execution order is controlled by filename prefix.
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ Apps that need custom database tables add a third:
|
|
|
21
21
|
|
|
22
22
|
```text
|
|
23
23
|
server/plugins/
|
|
24
|
-
db.ts ->
|
|
24
|
+
db.ts -> register app-owned migrations for local startup
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Auth Plugin {#auth-plugin}
|
|
@@ -80,7 +80,7 @@ screen when context matters.`,
|
|
|
80
80
|
|
|
81
81
|
## Database Plugin {#database-plugin}
|
|
82
82
|
|
|
83
|
-
If your app defines custom database tables, add `server/plugins/db.ts` to
|
|
83
|
+
If your app defines custom database tables, add `server/plugins/db.ts` to register schema migrations for local startup. It uses `runMigrations` from `@agent-native/core/db`:
|
|
84
84
|
|
|
85
85
|
```ts filename="server/plugins/db.ts"
|
|
86
86
|
import { runMigrations } from "@agent-native/core/db";
|
|
@@ -110,6 +110,12 @@ Each entry needs a `version` number and a `sql` string. Give it a stable `name`
|
|
|
110
110
|
TABLE ... DROP COLUMN`, `DELETE FROM`) in startup plugins.
|
|
111
111
|
</Callout>
|
|
112
112
|
|
|
113
|
+
<Callout tone="warning">
|
|
114
|
+
For production or serverless deployments, run migrations in a release or
|
|
115
|
+
deploy step instead of on the first request. The Chat template provides `pnpm
|
|
116
|
+
migrate:production` for this.
|
|
117
|
+
</Callout>
|
|
118
|
+
|
|
113
119
|
## Writing Custom Plugins {#custom-plugins}
|
|
114
120
|
|
|
115
121
|
The framework helpers (`createAuthPlugin`, `createAgentChatPlugin`, `runMigrations`) each return a Nitro plugin function. For anything outside those helpers, write a plugin using `defineNitroPlugin`:
|
|
@@ -142,6 +148,6 @@ If you add custom plugins, prefix their filenames to control invocation order, b
|
|
|
142
148
|
|
|
143
149
|
- [**Database**](/docs/server-database): schema helpers and the `getDb` pattern used inside plugins
|
|
144
150
|
- [**Middleware**](/docs/server-middleware): the auth guard that enforces what the auth plugin configures
|
|
145
|
-
- [**Routes**](/docs/server-routes): custom file routes
|
|
151
|
+
- [**Routes**](/docs/server-routes): custom file routes and when to use them
|
|
146
152
|
- [**Writing Agent Instructions**](/docs/writing-agent-instructions): how to craft the `systemPrompt` in the agent-chat plugin
|
|
147
153
|
- [**Nitro plugin docs**](https://nitro.build/guide/plugins): the full plugin API and lifecycle hooks
|
|
@@ -77,7 +77,12 @@ export default defineEventHandler(async (event) => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
|
|
80
|
-
|
|
80
|
+
const signatureBytes = Buffer.from(signature, "utf8");
|
|
81
|
+
const expectedBytes = Buffer.from(expected, "utf8");
|
|
82
|
+
if (
|
|
83
|
+
signatureBytes.length !== expectedBytes.length ||
|
|
84
|
+
!timingSafeEqual(signatureBytes, expectedBytes)
|
|
85
|
+
) {
|
|
81
86
|
throw createError({ statusCode: 401, statusMessage: "Invalid signature" });
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -189,6 +194,6 @@ Every SSR response is an impersonal, public shell cached at the CDN. Never read
|
|
|
189
194
|
## What's next {#whats-next}
|
|
190
195
|
|
|
191
196
|
- [**Actions**](/docs/actions): the preferred surface for app operations; reach for a route only when it doesn't fit
|
|
192
|
-
- [**Plugins**](/docs/server-plugins): startup plugins
|
|
197
|
+
- [**Plugins**](/docs/server-plugins): startup plugins and their initialization behavior
|
|
193
198
|
- [**Middleware**](/docs/server-middleware): auth guards that protect all routes including `/api/*`
|
|
194
199
|
- [**Security**](/docs/security#access-guards): `accessFilter`/`assertAccess` and the full data-scoping model
|
|
@@ -15,7 +15,7 @@ import { track } from "@agent-native/core/tracking";
|
|
|
15
15
|
track(
|
|
16
16
|
"order.completed",
|
|
17
17
|
{ total: 49.99, items: 3 },
|
|
18
|
-
{ userId: "
|
|
18
|
+
{ userId: "user@example.com" },
|
|
19
19
|
);
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -126,7 +126,7 @@ import { track } from "@agent-native/core/tracking";
|
|
|
126
126
|
track(
|
|
127
127
|
"meal.logged",
|
|
128
128
|
{ mealName: "Salad", calories: 350 },
|
|
129
|
-
{ userId: "
|
|
129
|
+
{ userId: "user@example.com" },
|
|
130
130
|
);
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -150,7 +150,7 @@ Identify a user with traits. Forwarded to providers that support it (PostHog, Mi
|
|
|
150
150
|
```ts
|
|
151
151
|
import { identify } from "@agent-native/core/tracking";
|
|
152
152
|
|
|
153
|
-
identify("
|
|
153
|
+
identify("user@example.com", { plan: "pro", company: "Builder.io" });
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
Need a custom backend, the provider-registry API, or the batching/singleton internals? See [Advanced: custom providers & internals](#advanced) at the end.
|
|
@@ -391,5 +391,5 @@ The framework also tracks `builder connect clicked` from Connect Builder CTAs, a
|
|
|
391
391
|
## What's next
|
|
392
392
|
|
|
393
393
|
- [**Actions**](/docs/actions) — where most tracking calls originate
|
|
394
|
-
- [**Server Plugins**](/docs/server
|
|
394
|
+
- [**Server Plugins**](/docs/server-plugins) — `registerBuiltinProviders()` runs in the core-routes plugin at startup
|
|
395
395
|
- [**Security — Secrets Management**](/docs/security#secrets) — manage API keys for tracking providers
|
|
@@ -39,7 +39,7 @@ The UI updates in real time as the agent opens views, edits drafts, and runs rep
|
|
|
39
39
|
|
|
40
40
|
### Steer it like a teammate
|
|
41
41
|
|
|
42
|
-
Give feedback, queue tasks, audit what it did, edit its instructions.
|
|
42
|
+
Give feedback, queue tasks, audit what it did, and edit its instructions.
|
|
43
43
|
|
|
44
44
|
</Cards>
|
|
45
45
|
|
|
@@ -93,7 +93,7 @@ graph TD
|
|
|
93
93
|
DB --> AG_OUT
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
The diagram above shows what this looks like at runtime: both the user and the agent submit actions to the same layer, which writes to the same database and
|
|
96
|
+
The diagram above shows what this looks like at runtime: both the user and the agent submit actions to the same layer, which writes to the same database and sends updates back to both surfaces. When the agent creates a draft email, it appears in your inbox immediately. When you click Send, the agent knows it was sent. See [Key Concepts](/docs/key-concepts) for the full architecture.
|
|
97
97
|
|
|
98
98
|
## Why agents need a UI — and apps need an agent {#why-both}
|
|
99
99
|
|
|
@@ -211,7 +211,7 @@ Browse [agent-native.com/templates](/templates) and pick the one closest to what
|
|
|
211
211
|
|
|
212
212
|
### Use it immediately
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
Many first-party templates are available as hosted apps (e.g. `mail.agent-native.com`). Try one before you touch any code.
|
|
215
215
|
|
|
216
216
|
### Clone and customize
|
|
217
217
|
|
|
@@ -235,7 +235,7 @@ Agent-native apps can talk to each other. From inside the mail app, you can tag
|
|
|
235
235
|
|
|
236
236
|
### [Getting Started](/docs/getting-started)
|
|
237
237
|
|
|
238
|
-
Build a working chat app in under five minutes, then add
|
|
238
|
+
Build a working chat app in under five minutes, then add an action and a page. Add your own database tables when you need to persist domain data.
|
|
239
239
|
|
|
240
240
|
### [Key Concepts](/docs/key-concepts)
|
|
241
241
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.148.
|
|
3
|
+
"version": "0.148.1",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|