@agent-native/core 0.78.9 → 0.79.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/docs/content/actions.md +4 -2
- package/corpus/core/docs/content/agent-surfaces.md +9 -0
- package/corpus/core/docs/content/extensions.md +10 -0
- package/corpus/core/docs/content/generative-ui.md +217 -0
- package/corpus/core/docs/content/key-concepts.md +2 -2
- package/corpus/core/docs/content/mcp-apps.md +3 -5
- package/corpus/core/docs/content/native-chat-ui.md +9 -5
- package/corpus/core/docs/content/tracking.md +9 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action-ui.ts +1 -0
- package/corpus/core/src/agent/production-agent.ts +8 -0
- package/corpus/core/src/client/analytics.ts +195 -12
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +5 -2
- package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +139 -0
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +27 -1
- package/corpus/core/src/client/conversation/AgentConversation.tsx +38 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +9 -0
- package/corpus/core/src/client/conversation/types.ts +4 -0
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +27 -5
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +26 -5
- package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +654 -0
- package/corpus/core/src/client/extensions/iframe-bridge.ts +30 -0
- package/corpus/core/src/client/extensions/index.ts +5 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/session-replay.ts +89 -14
- package/corpus/core/src/code-agents/transcript-normalizer.ts +14 -0
- package/corpus/core/src/extensions/actions.ts +218 -1
- package/corpus/core/src/extensions/html-shell.ts +70 -3
- package/corpus/core/src/extensions/routes.ts +12 -4
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +21 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +198 -59
- package/corpus/templates/analytics/AGENTS.md +12 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1105 -0
- package/corpus/templates/analytics/actions/dashboard-panel-order.ts +169 -0
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +89 -6
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +333 -0
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +214 -0
- package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +48 -21
- package/corpus/templates/analytics/actions/update-dashboard.ts +193 -100
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +11 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +81 -10
- package/corpus/templates/analytics/app/i18n-data.ts +249 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +41 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +4 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +48 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +204 -70
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +1101 -227
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +160 -133
- package/corpus/templates/analytics/changelog/2026-06-26-agents-can-now-edit-dashboards-through-a-typed-mutation-scri.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-bar-chart-hovers-stay-subtle-in-dark-mode.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-chart-moves-are-now-handled-by-panel-id-making-sim.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-drag-smoothly-with-stable-cards-inactive-ch.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-show-a-refresh-spinner-while-keeping-existi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-edits-stay-stable-while-chart-order-and-deletions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-email-reports-queue-immediate-sends.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-explorer-dashboards-now-drag-charts-with-a-stable-preview-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replay-playback-can-now-be-toggled-by-clicking-the-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replays-now-open-from-the-full-row-and-play-with-tim.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +41 -17
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/dist/action-ui.d.ts +1 -0
- package/dist/action-ui.d.ts.map +1 -1
- package/dist/action-ui.js +1 -0
- package/dist/action-ui.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +6 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +173 -12
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts +17 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js +85 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +15 -2
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +33 -0
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +8 -0
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/conversation/types.d.ts +4 -0
- package/dist/client/conversation/types.d.ts.map +1 -1
- package/dist/client/conversation/types.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +29 -1
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +28 -1
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.d.ts +25 -0
- package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -0
- package/dist/client/extensions/InlineExtensionFrame.js +488 -0
- package/dist/client/extensions/InlineExtensionFrame.js.map +1 -0
- package/dist/client/extensions/iframe-bridge.d.ts +2 -0
- package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
- package/dist/client/extensions/iframe-bridge.js +23 -0
- package/dist/client/extensions/iframe-bridge.js.map +1 -1
- package/dist/client/extensions/index.d.ts +1 -0
- package/dist/client/extensions/index.d.ts.map +1 -1
- package/dist/client/extensions/index.js +1 -0
- package/dist/client/extensions/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +11 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +74 -14
- package/dist/client/session-replay.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.d.ts +2 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +8 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +200 -1
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +70 -3
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +11 -4
- package/dist/extensions/routes.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +21 -9
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/docs/content/actions.md +4 -2
- package/docs/content/agent-surfaces.md +9 -0
- package/docs/content/extensions.md +10 -0
- package/docs/content/generative-ui.md +217 -0
- package/docs/content/key-concepts.md +2 -2
- package/docs/content/mcp-apps.md +3 -5
- package/docs/content/native-chat-ui.md +9 -5
- package/docs/content/tracking.md +9 -3
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
package/corpus/core/src/index.ts
CHANGED
|
@@ -3036,21 +3036,29 @@ function buildFrameworkPrompts(
|
|
|
3036
3036
|
? "source-code handoffs and app-created artifacts such as extensions, widgets, dashboards, calculators, mini-apps, documents, designs, slides, or videos"
|
|
3037
3037
|
: "source-code handoffs and app-created artifacts such as documents, designs, slides, or videos";
|
|
3038
3038
|
const planModeBlockedTools = extensionToolsEnabled
|
|
3039
|
-
? "`create-extension`, `update-extension`, `connect-builder`, or any action that creates, updates, deletes, sends, publishes, or persists data"
|
|
3039
|
+
? "`render-inline-extension`, `create-extension`, `update-extension`, `connect-builder`, or any action that creates, updates, deletes, sends, publishes, or persists data"
|
|
3040
3040
|
: "`connect-builder`, or any action that creates, updates, deletes, sends, publishes, or persists data";
|
|
3041
3041
|
const extensionConnectBuilderGuard = extensionToolsEnabled
|
|
3042
|
-
? "If the request matches the Extensions section above, use `create-extension` or `update-extension` instead — do NOT route it to `connect-builder`."
|
|
3042
|
+
? "If the request matches the Extensions section above, use `render-inline-extension`, `create-extension`, `show-extension-inline`, or `update-extension` instead — do NOT route it to `connect-builder`."
|
|
3043
3043
|
: "Because extension tools are disabled, do NOT invent an extension workflow. Only use `connect-builder` when the request genuinely requires changing the host app's source code.";
|
|
3044
3044
|
const extensionInstructionsFull = extensionToolsEnabled
|
|
3045
|
-
? `### Extensions (Mini-Apps)
|
|
3045
|
+
? `### Generative UI and Extensions (Mini-Apps)
|
|
3046
3046
|
|
|
3047
|
-
In Act mode, if the user asks
|
|
3047
|
+
In Act mode, if the user asks for generated interactive UI in chat, choose the smallest extension action that matches the lifetime:
|
|
3048
|
+
|
|
3049
|
+
- For a **one-time inline UI** that answers the current chat turn (knobs, controls, pickers, calculators, temporary dashboards, visualizers), call \`render-inline-extension\` immediately with a self-contained Alpine.js HTML body. It renders inside the transcript and is not saved.
|
|
3050
|
+
- For a **reusable or saved UI** (an extension/widget/dashboard/calculator/mini-app the user can reopen from Extensions), call \`create-extension\` with a self-contained Alpine.js HTML body. It saves to the Extensions view and also renders inline in chat.
|
|
3051
|
+
- To **reuse an existing saved extension inline**, call \`show-extension-inline\` with its id, or a search string when the id is unknown.
|
|
3052
|
+
|
|
3053
|
+
These are **NOT** source-code changes and do **NOT** go through \`connect-builder\`. Extensions are sandboxed mini-apps — no source files are touched, no PR is opened, no build is required. Saved extensions can be edited later via \`update-extension\`.
|
|
3048
3054
|
|
|
3049
3055
|
Keep \`create-extension\` payloads compact enough to finish quickly. For complex extensions, create a useful working v1 first, then call \`update-extension\` with focused edits for refinements instead of trying to assemble one enormous initial tool input.
|
|
3050
3056
|
|
|
3057
|
+
Generated UI content can use appAction(), appFetch(), dbQuery(), dbExec(), extensionFetch(), extensionData, agentNative.ui.output(value, opts?), and agentNative.chat.send(...)/sendToAgentChat(...). It can receive chat inputs through slotContext/window.onSlotContext. Use agentNative.ui.output for passive current values from knobs, sliders, selections, and controls; it writes application state at \`inline-ui:<extensionId>:output\` scoped to the inline extension id returned by \`render-inline-extension\` or \`show-extension-inline\`. When the user later says "use that value", "apply the current setting", or similar, read it with \`readAppState("inline-ui:<id>:output")\` instead of asking them to send it again. Use agentNative.chat.send for visible submit/apply actions that should put a message into chat. Transient extensionData is browser-local and not agent-readable, synced, promoted, or garbage-collected; use application_state/appFetch, appAction, ui.output, or chat.send for anything the agent or app must observe. Use semantic Tailwind classes like bg-background, text-foreground, bg-primary, border-border, and text-muted-foreground so the UI inherits the parent app theme.
|
|
3058
|
+
|
|
3051
3059
|
If the user asks to change, edit, fix, style, rename, or add behavior to an existing extension/widget/dashboard/calculator/mini-app, use the current extension id from \`<current-screen>\` or \`<current-url>\` when present. Call \`get-extension\` only if you need to inspect its content, then \`update-extension\` with that id. Use \`list-extensions\` only when no current id/name is available. Existing extension edits are SQL data updates, not source-code changes, even when the request says "change the UI" or "fix this". Do **NOT** call \`connect-builder\` for existing extension edits.
|
|
3052
3060
|
|
|
3053
|
-
In Act mode, when in doubt — if the request
|
|
3061
|
+
In Act mode, when in doubt — if the request asks for a new small interactive utility and does not need reuse, choose \`render-inline-extension\`; if it mentions saving/reuse or asks for an extension/widget/dashboard/calculator/mini-app, choose \`create-extension\`. If it references an existing one or the current extension page, choose \`update-extension\`. Do **not** preface the call with planning text like "let me build the dashboard…" — just call the right extension action directly.
|
|
3054
3062
|
|
|
3055
3063
|
Note: "extension" is the user-facing primitive (the sandboxed Alpine.js mini-app). Don't confuse it with the LLM concept of "tools" (function calls) — those are how you invoke ANY action, including \`create-extension\` itself.
|
|
3056
3064
|
|
|
@@ -3063,10 +3071,12 @@ Route by what the request changes, not how it is phrased. Extensions render in t
|
|
|
3063
3071
|
<routing>
|
|
3064
3072
|
| The request is for… | Path |
|
|
3065
3073
|
| ---------------------------------------------------------------- | ----------------------------- |
|
|
3074
|
+
| A one-off interactive answer inside chat (controls, picker, calculator, temporary visualizer) | \`render-inline-extension\` — inline only |
|
|
3066
3075
|
| A new self-contained surface (widget, dashboard, calculator, viewer, list, tracker) | \`create-extension\` — ships instantly, no PR |
|
|
3076
|
+
| Loading a saved extension inside chat | \`show-extension-inline\` |
|
|
3067
3077
|
| Editing an existing extension (fix, restyle, rename, add behavior) | \`update-extension\` |
|
|
3068
3078
|
| The host app's own chrome (nav bar, sidebar, layout, routes, shipped components, existing styles, business logic) | \`connect-builder\` — a real source-code change |
|
|
3069
|
-
| Ambiguous, satisfiable either way (e.g. "give me an unread view") | \`
|
|
3079
|
+
| Ambiguous, satisfiable either way (e.g. "give me an unread view") | \`render-inline-extension\` for chat-only, \`create-extension\` for reusable |
|
|
3070
3080
|
</routing>
|
|
3071
3081
|
|
|
3072
3082
|
Worked examples: "a widget showing unread emails grouped by sender", "a dashboard summarizing my pipeline", "a tracker for my newsletter subscriptions" → \`create-extension\`. "Add an Unread tab to the left navigation", "make the subject lines wrap", "change the inbox grouping logic", "add a field to the compose form" → \`connect-builder\`.`
|
|
@@ -3074,19 +3084,21 @@ Worked examples: "a widget showing unread emails grouped by sender", "a dashboar
|
|
|
3074
3084
|
|
|
3075
3085
|
Extension creation and management tools are disabled for this app. Do not claim you can create, edit, hide, or delete Agent-Native extensions unless the template exposes its own typed action for that workflow. For requests that would otherwise be handled as an extension/widget/dashboard/calculator mini-app, explain that this app has disabled extension tools and use the app's available actions instead.`;
|
|
3076
3086
|
const extensionInstructionsCompact = extensionToolsEnabled
|
|
3077
|
-
? `### Extensions (Mini-Apps)
|
|
3087
|
+
? `### Generative UI and Extensions (Mini-Apps)
|
|
3078
3088
|
|
|
3079
|
-
In Act mode, if the user asks for an **extension**, **widget**, **dashboard**, **calculator**, or **mini-app
|
|
3089
|
+
In Act mode, if the user asks for generated interactive UI in chat, call \`render-inline-extension\` for one-time inline controls/knobs/calculators/visualizers that do not need saving. If the user asks for an **extension**, **widget**, **dashboard**, **calculator**, or **mini-app** that should be reusable or saved, call \`create-extension\` with a self-contained Alpine.js HTML body. To load a saved extension inline, call \`show-extension-inline\`. These are NOT code changes — extensions are sandboxed mini-apps. Do not preface with "let me build…" — just call the right extension action.
|
|
3080
3090
|
|
|
3081
3091
|
Keep the first \`create-extension\` call compact and working. If the request is complex, create the v1 first and then refine with focused \`update-extension\` edits.
|
|
3082
3092
|
|
|
3093
|
+
Generated UI can read chat inputs from slotContext/window.onSlotContext, see/update app state through appFetch/appAction, use extensionData, record passive current values through agentNative.ui.output(value, opts?), and send visible results through agentNative.chat.send(...) or sendToAgentChat(...). ui.output writes \`inline-ui:<extensionId>:output\` in application state; when the user asks to use the current slider/selection/value, read \`readAppState("inline-ui:<id>:output")\`. Transient extensionData is browser-local only, so do not rely on it for values the agent or app must observe. Use semantic Tailwind theme classes.
|
|
3094
|
+
|
|
3083
3095
|
If the user asks to change, edit, fix, style, rename, or add behavior to an existing extension/widget/dashboard/calculator/mini-app, use the current extension id from \`<current-screen>\` or \`<current-url>\` when present. Call \`get-extension\` only if you need to inspect its content, then \`update-extension\` with that id. Use \`list-extensions\` only when no current id/name is available. Existing extension edits are SQL data updates, not source-code changes. Do NOT call \`connect-builder\` for them.
|
|
3084
3096
|
|
|
3085
3097
|
For existing extensions, use \`get-extension\` or \`update-extension\` directly when \`<current-screen>\` or \`<current-url>\` provides an \`extensionId\`. Use \`list-extensions\` only to browse or resolve an unknown name. Use \`hide-extension\` when the user wants a shared extension removed only from their own view. Do not query the legacy \`tools\` table directly.
|
|
3086
3098
|
|
|
3087
3099
|
### Extensions vs. Code Changes — Pick the Right Path
|
|
3088
3100
|
|
|
3089
|
-
If the user wants a **new self-contained surface** (custom widget, dashboard, list, viewer, calculator), use \`create-extension\` — extensions ship instantly without a PR. Use \`connect-builder\` only when the request **modifies the host app's existing chrome** (nav bar, sidebar, current components, layout, styles, routes). Extensions cannot change the host nav or restyle existing components.`
|
|
3101
|
+
If the user wants a **one-off interactive answer in chat**, use \`render-inline-extension\`. If they want a **new reusable self-contained surface** (custom widget, dashboard, list, viewer, calculator), use \`create-extension\` — extensions ship instantly without a PR. Use \`connect-builder\` only when the request **modifies the host app's existing chrome** (nav bar, sidebar, current components, layout, styles, routes). Extensions cannot change the host nav or restyle existing components.`
|
|
3090
3102
|
: `### Extensions Disabled
|
|
3091
3103
|
|
|
3092
3104
|
Extension creation and management tools are disabled for this app. Do not claim you can create, edit, hide, or delete Agent-Native extensions unless the template exposes its own typed action for that workflow.`;
|
|
@@ -73,6 +73,19 @@ Extensions have full access to app data via helpers injected into the iframe
|
|
|
73
73
|
per-extension (supports `{ scope: 'user' | 'org' | 'all' }` option). Legacy
|
|
74
74
|
alias: `toolData` — kept for back-compat; both names refer to the same
|
|
75
75
|
store.
|
|
76
|
+
- `agentNative.ui.output(value, opts?)` — when an extension is rendered inline
|
|
77
|
+
in chat, record passive control/selection output at
|
|
78
|
+
`inline-ui:<extensionId>:output` in application state so the agent can read it
|
|
79
|
+
later with `readAppState`.
|
|
80
|
+
- `agentNative.chat.send(message, opts?)` — send a visible prompt or selected
|
|
81
|
+
value back into the current agent chat.
|
|
82
|
+
|
|
83
|
+
For transient inline generative UI, `extensionData` is host-browser
|
|
84
|
+
`localStorage`: the agent cannot read it, it does not sync across devices, it
|
|
85
|
+
does not migrate when the UI is saved later, and the server does not garbage
|
|
86
|
+
collect it. Use it only for throwaway local UI state. Use application state,
|
|
87
|
+
`agentNative.ui.output`, `appAction`, or `agentNative.chat.send` for anything
|
|
88
|
+
the agent or app must observe.
|
|
76
89
|
|
|
77
90
|
## Data Persistence is Built In
|
|
78
91
|
|
package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md
CHANGED
|
@@ -105,6 +105,8 @@ await dbExec("UPDATE notes SET title = 'Updated Title' WHERE id = 'abc'");
|
|
|
105
105
|
| `extensionData.list(collection, opts?)` | List items in a collection | `extensionData.list('todos', { scope: 'all' })` |
|
|
106
106
|
| `extensionData.get(collection, id, opts?)` | Get a single item by id | `extensionData.get('todos', 'todo-1')` |
|
|
107
107
|
| `extensionData.remove(collection, id, opts?)` | Delete an item | `extensionData.remove('todos', 'todo-1')` |
|
|
108
|
+
| `agentNative.ui.output(value, opts?)` | Record passive inline UI output in application state | `agentNative.ui.output({ threshold })` |
|
|
109
|
+
| `agentNative.chat.send(message, opts?)` | Send a visible prompt or selected value back to chat | `agentNative.chat.send('Use Q2', { context: { q: 2 } })` |
|
|
108
110
|
|
|
109
111
|
## Persisting Custom Data
|
|
110
112
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generative-ui
|
|
3
|
+
description: >-
|
|
4
|
+
Generate transient or saved inline chat UI with Alpine/Tailwind controls,
|
|
5
|
+
outputs, app state, and extensions. Use for knobs, pickers, calculators,
|
|
6
|
+
dashboards, widgets, or reusable mini-apps.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Generative UI
|
|
10
|
+
|
|
11
|
+
Generative UI is sandboxed Alpine.js HTML rendered inline in chat. It is not a
|
|
12
|
+
source-code change and does not go through Builder.
|
|
13
|
+
|
|
14
|
+
## Pick the Lifetime
|
|
15
|
+
|
|
16
|
+
- Use `render-inline-extension` for one-off UI that belongs only in the current
|
|
17
|
+
chat: knobs, controls, pickers, calculators, temporary charts, and previews.
|
|
18
|
+
- Use `create-extension` when the UI should be saved, reusable, or visible in
|
|
19
|
+
the Extensions view. It also renders inline after creation.
|
|
20
|
+
- Use `show-extension-inline` to reopen a saved extension inside chat.
|
|
21
|
+
- Use `update-extension` to edit an existing saved extension.
|
|
22
|
+
|
|
23
|
+
## Inputs From Chat
|
|
24
|
+
|
|
25
|
+
Pass initial values through the action `context` argument. The iframe reads them
|
|
26
|
+
from `window.slotContext` and can subscribe to changes with
|
|
27
|
+
`window.onSlotContext(fn)`.
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<script>
|
|
31
|
+
Alpine.data("controls", () => ({
|
|
32
|
+
threshold: 50,
|
|
33
|
+
init() {
|
|
34
|
+
this.threshold = Number(window.slotContext?.threshold ?? 50);
|
|
35
|
+
window.onSlotContext?.((ctx) => {
|
|
36
|
+
if (ctx.threshold !== undefined) {
|
|
37
|
+
this.threshold = Number(ctx.threshold);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Outputs To The Agent
|
|
46
|
+
|
|
47
|
+
Use passive output when a control's current value should be visible to the agent
|
|
48
|
+
on the next turn without requiring a submit click:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<input
|
|
52
|
+
type="range"
|
|
53
|
+
min="0"
|
|
54
|
+
max="100"
|
|
55
|
+
x-model.number="threshold"
|
|
56
|
+
@input="agentNative.ui.output({ threshold }, { label: 'Threshold' })"
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`agentNative.ui.output(value, opts?)` writes application state at:
|
|
61
|
+
|
|
62
|
+
```txt
|
|
63
|
+
inline-ui:<extensionId>:output
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The id is scoped automatically; the HTML author must not pass it. When the user
|
|
67
|
+
says "use that value", "apply the current setting", or "run it with the current
|
|
68
|
+
selection", read the value with:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const output = await readAppState("inline-ui:<id>:output");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The stored payload includes `value`, `updatedAt`, `extensionId`, and
|
|
75
|
+
`source: "inline-ui"`, plus optional `label`, `context`, or `meta`.
|
|
76
|
+
|
|
77
|
+
Use `agentNative.chat.send(message, opts?)` or `sendToAgentChat(message, opts?)`
|
|
78
|
+
for a visible Apply/Submit button that should send a prompt or selected value
|
|
79
|
+
into the chat.
|
|
80
|
+
|
|
81
|
+
## App Data And State
|
|
82
|
+
|
|
83
|
+
- Prefer `appAction(name, params)` for template data and operations.
|
|
84
|
+
- Use `appFetch(path, options)` only for allowed framework endpoints under
|
|
85
|
+
`/_agent-native/*`, including application-state reads and writes.
|
|
86
|
+
- Do not call template `/api/*` routes from generated UI.
|
|
87
|
+
- Use `dbQuery`/`dbExec` only for known existing SQL tables when no action fits.
|
|
88
|
+
- Use `extensionFetch` for external APIs through the sandbox proxy and secrets.
|
|
89
|
+
|
|
90
|
+
## Transient extensionData Boundary
|
|
91
|
+
|
|
92
|
+
For saved extensions, `extensionData` uses the authenticated database-backed
|
|
93
|
+
extension data API.
|
|
94
|
+
|
|
95
|
+
For transient inline UIs, `extensionData` is host `localStorage`. The agent
|
|
96
|
+
cannot read it, it does not sync across devices, it does not migrate when a UI
|
|
97
|
+
is promoted to a saved Extension, and the server does not garbage-collect it.
|
|
98
|
+
Use it only for throwaway local UI state.
|
|
99
|
+
|
|
100
|
+
For anything the agent or app must observe, use `agentNative.ui.output`,
|
|
101
|
+
application state through `appFetch`, an `appAction`, or
|
|
102
|
+
`agentNative.chat.send`.
|
|
103
|
+
|
|
104
|
+
## Styling
|
|
105
|
+
|
|
106
|
+
Use semantic Tailwind classes so the iframe inherits the parent app theme:
|
|
107
|
+
|
|
108
|
+
- `bg-background`, `text-foreground`, `border-border`
|
|
109
|
+
- `bg-card`, `text-card-foreground`
|
|
110
|
+
- `bg-primary`, `text-primary-foreground`
|
|
111
|
+
- `text-muted-foreground`, `bg-accent`
|
|
112
|
+
|
|
113
|
+
Do not hardcode provider tokens, private data, or internal secrets in generated
|
|
114
|
+
HTML. Do not call an LLM directly from the iframe; route AI work through the
|
|
115
|
+
agent chat.
|
|
@@ -23,13 +23,17 @@ Current storage:
|
|
|
23
23
|
|
|
24
24
|
Legacy settings keys such as `u:<email>:dashboard-*`, `u:<email>:sql-dashboard-*`, `o:<orgId>:sql-dashboard-*`, and `adhoc-analysis-*` are still read as a fallback and copied into SQL on access. Do not create new dashboard settings rows.
|
|
25
25
|
|
|
26
|
-
Use `
|
|
26
|
+
Use `mutate-dashboard` for existing dashboard edits. It resolves the current
|
|
27
|
+
user/org context, validates the resulting config, writes the SQL-backed record,
|
|
28
|
+
syncs collab, and returns compact proof. Use `update-dashboard` for new
|
|
29
|
+
full-config saves, UI full-config saves, or explicitly requested low-level
|
|
30
|
+
JSON-pointer edits.
|
|
27
31
|
|
|
28
32
|
Never use `db-patch`, raw SQL, or settings-key edits to create or modify a
|
|
29
33
|
dashboard config. Those bypass the dashboard action's access checks, SQL
|
|
30
|
-
validation, collab sync, and proof-of-done return. If
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
validation, collab sync, and proof-of-done return. If a dashboard action fails
|
|
35
|
+
because the argument shape was wrong, fix that action's arguments and retry
|
|
36
|
+
once — do not switch to db-patch or raw SQL.
|
|
33
37
|
|
|
34
38
|
## Valid Panel Sources
|
|
35
39
|
|
|
@@ -68,7 +72,7 @@ The save path dry-runs BigQuery panels before persisting. If validation returns
|
|
|
68
72
|
## When To Use An Extension Instead
|
|
69
73
|
|
|
70
74
|
Native Analytics dashboards are JSON configs rendered by the built-in dashboard
|
|
71
|
-
components. Use
|
|
75
|
+
components. Use native dashboard actions only when the request fits that model:
|
|
72
76
|
standard panels, supported chart types, filters, variables, sections, and grid
|
|
73
77
|
layout.
|
|
74
78
|
|
|
@@ -167,71 +171,196 @@ Filters auto-apply on change — there is no Apply button. Each filter change wr
|
|
|
167
171
|
|
|
168
172
|
## Modifying A Dashboard
|
|
169
173
|
|
|
170
|
-
|
|
174
|
+
For existing dashboard edits, default to `mutate-dashboard`. It gives the
|
|
175
|
+
agent a small typed script API without exposing arbitrary JavaScript execution.
|
|
176
|
+
The main action payload is a string, so it avoids native-array serialization
|
|
177
|
+
traps in tool calls while still giving the agent a code-like editing surface.
|
|
178
|
+
The server parses only documented `dashboard.*` method calls, applies the
|
|
179
|
+
resulting operations in memory, validates the final dashboard config, writes
|
|
180
|
+
SQL once, syncs collab, and returns compact proof.
|
|
181
|
+
|
|
182
|
+
Arguments must be JSON-compatible literals, so quote object keys. Variables,
|
|
183
|
+
imports, loops, functions, templates, network, filesystem, DB access, and
|
|
184
|
+
calling other actions from the script are not available.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
type DashboardMutationApi = {
|
|
188
|
+
dashboard: {
|
|
189
|
+
set(patch: DashboardPatch): void;
|
|
190
|
+
panel(id: string): PanelSelection;
|
|
191
|
+
section(id: string): SectionSelection;
|
|
192
|
+
panels(ids: string[]): PanelSelection;
|
|
193
|
+
panelsMatching(filter: PanelFilter): PanelSelection;
|
|
194
|
+
insertPanel(panel: PanelInput): InsertedPanel;
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
type DashboardPatch = {
|
|
199
|
+
name?: string;
|
|
200
|
+
description?: string;
|
|
201
|
+
columns?: number;
|
|
202
|
+
filters?: unknown[];
|
|
203
|
+
variables?: Record<string, string>;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
type PanelPatch = {
|
|
207
|
+
title?: string;
|
|
208
|
+
sql?: string;
|
|
209
|
+
source?: "bigquery" | "ga4" | "amplitude" | "first-party" | "demo" | "prometheus";
|
|
210
|
+
chartType?: "line" | "area" | "bar" | "metric" | "table" | "pie" | "section" | "heatmap" | "callout";
|
|
211
|
+
width?: number;
|
|
212
|
+
columns?: number;
|
|
213
|
+
tab?: string;
|
|
214
|
+
config?: Record<string, unknown>;
|
|
215
|
+
description?: string;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
type PanelInput = PanelPatch & {
|
|
219
|
+
id: string;
|
|
220
|
+
title: string;
|
|
221
|
+
chartType: NonNullable<PanelPatch["chartType"]>;
|
|
222
|
+
source?: PanelPatch["source"]; // required for non-section panels
|
|
223
|
+
sql?: string; // required for non-section panels
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
type PanelFilter = {
|
|
227
|
+
id?: string;
|
|
228
|
+
ids?: string[];
|
|
229
|
+
idIncludes?: string;
|
|
230
|
+
title?: string;
|
|
231
|
+
titleIncludes?: string;
|
|
232
|
+
chartType?: PanelPatch["chartType"];
|
|
233
|
+
source?: PanelPatch["source"];
|
|
234
|
+
tab?: string;
|
|
235
|
+
isSection?: boolean;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
type PanelSelection = {
|
|
239
|
+
moveToTop(): void;
|
|
240
|
+
moveToBottom(): void;
|
|
241
|
+
moveBefore(panelId: string): void;
|
|
242
|
+
moveAfter(panelId: string): void;
|
|
243
|
+
moveToIndex(index: number): void;
|
|
244
|
+
remove(): void;
|
|
245
|
+
set(patch: PanelPatch): void;
|
|
246
|
+
setTitle(title: string): void;
|
|
247
|
+
setSql(sql: string): void;
|
|
248
|
+
setWidth(width: number): void;
|
|
249
|
+
setConfig(patch: Record<string, unknown>): void;
|
|
250
|
+
setConfigPath(path: string, value: unknown): void;
|
|
251
|
+
duplicate(newPanelId: string, patch?: PanelPatch): void;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
type SectionSelection = PanelSelection & {
|
|
255
|
+
append(panelIds: string[]): void;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
type InsertedPanel = {
|
|
259
|
+
atTop(): void;
|
|
260
|
+
atBottom(): void;
|
|
261
|
+
before(panelId: string): void;
|
|
262
|
+
after(panelId: string): void;
|
|
263
|
+
atIndex(index: number): void;
|
|
264
|
+
};
|
|
265
|
+
```
|
|
171
266
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
267
|
+
Examples:
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
dashboard.panels(["dau-over-time", "wau-over-time"]).moveToTop();
|
|
271
|
+
dashboard.panel("top-referrers").setTitle("Top Referrers by Domain");
|
|
272
|
+
dashboard.panel("retention").set({
|
|
273
|
+
"width": 2,
|
|
274
|
+
"config": { "description": "Updated definition." }
|
|
275
|
+
});
|
|
276
|
+
dashboard.panelsMatching({ "source": "first-party" }).setWidth(2);
|
|
277
|
+
dashboard.panelsMatching({ "titleIncludes": "Revenue" }).setConfigPath(
|
|
278
|
+
"yAxis.format",
|
|
279
|
+
"currency"
|
|
280
|
+
);
|
|
281
|
+
dashboard.panelsMatching({ "titleIncludes": "Signed-In" }).moveToTop();
|
|
282
|
+
dashboard.section("retention-activity-section").append([
|
|
283
|
+
"repeat-users",
|
|
284
|
+
"retention-over-time"
|
|
285
|
+
]);
|
|
286
|
+
dashboard.insertPanel({
|
|
287
|
+
"id": "new-kpi",
|
|
288
|
+
"title": "New KPI",
|
|
289
|
+
"source": "first-party",
|
|
290
|
+
"chartType": "metric",
|
|
291
|
+
"width": 1,
|
|
292
|
+
"sql": "SELECT COUNT(*) AS value FROM analytics_events"
|
|
293
|
+
}).atTop();
|
|
294
|
+
```
|
|
176
295
|
|
|
177
|
-
|
|
178
|
-
|
|
296
|
+
Native tool call:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"dashboardId": "weekly-metrics",
|
|
301
|
+
"code": "dashboard.panels([\"dau-over-time\",\"wau-over-time\"]).moveToTop();"
|
|
302
|
+
}
|
|
179
303
|
```
|
|
180
304
|
|
|
181
|
-
|
|
305
|
+
Use `update-dashboard` only for new full-config saves, UI full-config saves, or
|
|
306
|
+
when the user specifically requests low-level JSON-pointer edits.
|
|
182
307
|
|
|
183
|
-
|
|
308
|
+
`get-sql-dashboard` is compact by default. It returns panel summaries, ids,
|
|
309
|
+
titles, chart types, sources, layout groups, `layout.panelOrder`, and
|
|
310
|
+
`layout.firstPanelIds` without embedding every panel's full SQL. Use that
|
|
311
|
+
compact result to find panel ids and verify order. Pass `includeConfig: true`
|
|
312
|
+
only when you need full panel SQL/config for a detailed edit.
|
|
184
313
|
|
|
185
|
-
|
|
314
|
+
After a mutation, navigate to the dashboard if the user is elsewhere. The app syncs through the framework's polling/query invalidation path.
|
|
186
315
|
|
|
187
|
-
|
|
188
|
-
2. Find panel indexes by `panel.id` from the current config you just read. Do
|
|
189
|
-
not rely on seed-file order, stale memory, or screenshots.
|
|
190
|
-
3. Build one `ops` array that includes every change.
|
|
191
|
-
4. Call `update-dashboard` once.
|
|
192
|
-
5. Verify the returned `panelCount`, `appliedOps`, and `summary`. If possible,
|
|
193
|
-
read the affected panels back and confirm the exact fields changed.
|
|
316
|
+
### Reordering Panels
|
|
194
317
|
|
|
195
|
-
For
|
|
318
|
+
For simple "move this chart/section" requests, prefer `mutate-dashboard` with a
|
|
319
|
+
string script:
|
|
196
320
|
|
|
197
321
|
```json
|
|
198
322
|
{
|
|
199
323
|
"dashboardId": "weekly-metrics",
|
|
200
|
-
"
|
|
201
|
-
{
|
|
202
|
-
"op": "replace",
|
|
203
|
-
"path": "/panels/3/sql",
|
|
204
|
-
"value": "SELECT COUNT(*) AS value FROM analytics_events"
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
"op": "replace",
|
|
208
|
-
"path": "/panels/3/config/description",
|
|
209
|
-
"value": "Updated definition."
|
|
210
|
-
}
|
|
211
|
-
]
|
|
324
|
+
"code": "dashboard.panels([\"dau-over-time\",\"wau-over-time\"]).moveToTop();"
|
|
212
325
|
}
|
|
213
326
|
```
|
|
214
327
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
328
|
+
Do not do index arithmetic with `/panels/<index>` unless the user specifically
|
|
329
|
+
asks for a low-level JSON-pointer edit. Use `moveBefore`, `moveAfter`,
|
|
330
|
+
`moveToTop`, `moveToBottom`, or `moveToIndex` against panel ids instead.
|
|
331
|
+
|
|
332
|
+
`get-sql-dashboard` returns `layout.panelOrder`, `layout.firstPanelIds`, and
|
|
333
|
+
row/group summaries. Use those fields for orientation and verification instead
|
|
334
|
+
of re-reading stale screenshots or counting positions from memory.
|
|
335
|
+
|
|
336
|
+
### Existing Dashboard Edits
|
|
337
|
+
|
|
338
|
+
When the user asks to change existing panels:
|
|
219
339
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
340
|
+
1. Read the current dashboard with `get-sql-dashboard` compact mode unless full
|
|
341
|
+
SQL/config is required.
|
|
342
|
+
2. Call `mutate-dashboard` once with every change in one script. Use
|
|
343
|
+
`panel(...)`, `panels([...])`, or `panelsMatching({...})` selectors by id or
|
|
344
|
+
metadata; do not compute shifted array indexes.
|
|
345
|
+
3. Verify the returned `panelCount`, `appliedOps`, `firstPanelIds`, and
|
|
346
|
+
`summary`. If possible, read the affected panels back and confirm the exact
|
|
347
|
+
fields changed.
|
|
348
|
+
|
|
349
|
+
For SQL-only panel edits, use `dashboard.panel("id").setSql("...")`. If the
|
|
350
|
+
metric semantics changed, also update the visible definition with
|
|
351
|
+
`setConfigPath("description", "...")` or `set({ "description": "..." })`. If
|
|
352
|
+
the title, source, chart type, width, or config shape changes together, put them
|
|
353
|
+
in the same `set({...})` call.
|
|
225
354
|
|
|
226
355
|
### First-Party User Metrics
|
|
227
356
|
|
|
228
357
|
For first-party `/track` events, be precise about identity:
|
|
229
358
|
|
|
230
|
-
| Metric intent | Identity expression
|
|
231
|
-
| -------------------------------------------- |
|
|
232
|
-
| Account users, DAU, WAU, retention, cohorts | `NULLIF(user_id, '')` plus `NULLIF(user_id, '') IS NOT NULL`, but only on events that actually represent the activity being measured
|
|
359
|
+
| Metric intent | Identity expression |
|
|
360
|
+
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
361
|
+
| Account users, DAU, WAU, retention, cohorts | `NULLIF(user_id, '')` plus `NULLIF(user_id, '') IS NOT NULL`, but only on events that actually represent the activity being measured |
|
|
233
362
|
| Signed-in visitor activity | `event_name = 'session status' AND signed_in = 'true'` keyed by `COALESCE(NULLIF(user_id, ''), NULLIF(anonymous_id, ''))`, labeled as signed-in visitors rather than account users |
|
|
234
|
-
| Public traffic, visitors, clip/share viewers | `COALESCE(NULLIF(user_id, ''), NULLIF(anonymous_id, ''))`
|
|
363
|
+
| Public traffic, visitors, clip/share viewers | `COALESCE(NULLIF(user_id, ''), NULLIF(anonymous_id, ''))` |
|
|
235
364
|
|
|
236
365
|
Do not call anonymous visitors "users" in dashboard labels or descriptions.
|
|
237
366
|
When a user asks for DAU, WAU, retention, repeat users, or account cohorts,
|
|
@@ -272,18 +401,26 @@ This is the dashboard-specific application of the framework-wide `reliable-mutat
|
|
|
272
401
|
Hosted agent runs have a **~40s budget**. Many sequential `update-dashboard` calls (one per panel, plus schema-discovery calls) will blow that budget and leave the dashboard in a partial state — earlier inserts looked like they succeeded (✓), but nothing actually persisted. Avoid this:
|
|
273
402
|
|
|
274
403
|
- **For a large first-party dashboard, use `compose-dashboard`** (see the section above): name the metrics, the server generates the panels in one call. Do not hand-author the big config.
|
|
275
|
-
- **Batch ALL
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
-
|
|
404
|
+
- **Batch ALL edits into ONE `mutate-dashboard` call.** One script can move,
|
|
405
|
+
insert, remove, duplicate, and update many panels. Never loop dashboard edit
|
|
406
|
+
actions panel-by-panel.
|
|
407
|
+
- To bulk edit existing panels, use selectors:
|
|
408
|
+
`dashboard.panelsMatching({"source":"first-party"}).setWidth(2);`
|
|
409
|
+
- To make nested config edits, use
|
|
410
|
+
`setConfigPath("yAxis.format", "percent")` instead of resending/clobbering
|
|
411
|
+
the whole nested object.
|
|
279
412
|
- **To add a shipped template's panels, prefer `install-dashboard-template` with `mergePanels: true`** and the existing `dashboardId`. It appends only the template panels whose id is not already present (preserving existing panels and order) in one atomic save — you don't author each panel yourself.
|
|
280
|
-
- **Always verify the returned proof-of-done and report it.**
|
|
413
|
+
- **Always verify the returned proof-of-done and report it.**
|
|
414
|
+
`mutate-dashboard` returns `panelCount`, `appliedOps`, `panelOrder`,
|
|
415
|
+
`firstPanelIds`, `changedPanelIds`, `commandLog`, and a `summary` string.
|
|
416
|
+
`install-dashboard-template --mergePanels` returns `addedPanelIds`,
|
|
417
|
+
`skippedExistingIds`, and `panelCount`. Tell the user the resulting panel
|
|
418
|
+
count instead of assuming success.
|
|
281
419
|
|
|
282
420
|
```bash
|
|
283
|
-
# Add several panels in ONE atomic call (never one call per panel)
|
|
284
|
-
pnpm action
|
|
285
|
-
--
|
|
286
|
-
{"op":"insert","path":"/panels/-","value":{"id":"p2","title":"B","source":"first-party","chartType":"metric","width":1,"sql":"SELECT COUNT(DISTINCT user_id) AS value FROM analytics_events"}}]'
|
|
421
|
+
# Add or edit several panels in ONE atomic call (never one call per panel)
|
|
422
|
+
pnpm action mutate-dashboard --dashboardId weekly-metrics \
|
|
423
|
+
--code 'dashboard.panelsMatching({"source":"first-party"}).setWidth(2);'
|
|
287
424
|
|
|
288
425
|
# Append a template's panels to an existing dashboard in one call
|
|
289
426
|
pnpm action install-dashboard-template --templateId skills-cli-funnel --dashboardId weekly-metrics --mergePanels true
|
|
@@ -318,7 +455,9 @@ Writes require editor access; deletes require admin access. Owners always satisf
|
|
|
318
455
|
- Never fabricate data or create a dashboard from guessed schema. A panel's SQL must hit a real source; do not present figures you did not actually query.
|
|
319
456
|
- Never write dashboard configs into the settings table.
|
|
320
457
|
- Never use `db-patch` as a fallback for dashboard config edits. Use
|
|
321
|
-
`
|
|
458
|
+
`mutate-dashboard` for existing edits, or `update-dashboard` for new/full
|
|
459
|
+
config saves, and fix the action arguments.
|
|
322
460
|
- Never set `panel.source` to a table name or unsupported backend.
|
|
323
461
|
- Use `first-party` for `/track` data and `query-agent-native-analytics` for ad-hoc first-party event questions.
|
|
324
|
-
- Use `update-dashboard` for
|
|
462
|
+
- Use `update-dashboard` for new dashboard config saves and full config
|
|
463
|
+
replacements. Use `mutate-dashboard` for existing dashboard edits.
|
|
@@ -65,6 +65,18 @@ details live in `.agents/skills/`.
|
|
|
65
65
|
- For shipped dashboard templates, call `list-dashboard-templates` first, then
|
|
66
66
|
`install-dashboard-template` with the selected `templateId`. Do not recreate a
|
|
67
67
|
catalog template by hand unless the user asks for a custom variant.
|
|
68
|
+
- For dashboard edits, default to `mutate-dashboard` with its typed
|
|
69
|
+
`dashboard.*` script API. It supports id-based panel moves, title/SQL/config
|
|
70
|
+
edits, inserts, duplication, removal, and dashboard field patches in one
|
|
71
|
+
atomic save. The main payload is a string, so it avoids native-array
|
|
72
|
+
serialization traps. The script is constrained: only documented dashboard
|
|
73
|
+
method calls with JSON-compatible arguments are parsed; variables, imports,
|
|
74
|
+
loops, functions, network, filesystem, and DB access are not available.
|
|
75
|
+
- Do not count shifting `/panels/<index>` values for ordinary dashboard edit
|
|
76
|
+
requests. Use low-level JSON-pointer edits only when explicitly requested.
|
|
77
|
+
- `get-sql-dashboard` is compact by default for agents. Use its `panels`
|
|
78
|
+
summaries and `layout.panelOrder` / `layout.firstPanelIds` for orientation and
|
|
79
|
+
proof. Pass `includeConfig: true` only when full panel SQL/config is needed.
|
|
68
80
|
- Native dashboards and saved analyses are constrained artifacts. If a requested
|
|
69
81
|
dashboard, analysis surface, visualization, interaction model, custom layout,
|
|
70
82
|
or bespoke workflow cannot be done faithfully with the built-in dashboard JSON
|