@agent-native/core 0.107.0 → 0.107.2
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 +1 -1
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/docs/content/integrations.mdx +27 -27
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +32 -10
- package/corpus/core/src/agent/production-agent.ts +19 -0
- package/corpus/core/src/agent/run-store.ts +98 -2
- package/corpus/core/src/client/AssistantChat.tsx +13 -0
- package/corpus/core/src/client/CommandMenu.tsx +5 -0
- package/corpus/core/src/client/active-run-state.ts +34 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +4 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +41 -11
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -9
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +45 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +30 -9
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +2 -2
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +107 -3
- package/corpus/core/src/integrations/pending-tasks-store.ts +98 -1
- package/corpus/core/src/integrations/plugin.ts +129 -4
- package/corpus/core/src/integrations/webhook-handler.ts +339 -36
- package/corpus/core/src/localization/default-messages.ts +4 -1
- package/corpus/core/src/secrets/register-framework-secrets.ts +28 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +122 -9
- package/corpus/core/src/vite/client.ts +24 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -21
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +6 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +35 -7
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/extension-slot.ts +11 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +27 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extension-boxes.md +6 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/assets/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/brain/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/content/actions/_content-space-access.ts +45 -2
- package/corpus/templates/content/actions/create-content-database.ts +41 -7
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/root.tsx +1 -10
- package/corpus/templates/content/changelog/2026-07-17-creating-a-database-now-completes-reliably-in-hosted-workspa.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/dispatch/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +2 -4
- package/corpus/templates/forms/app/components/ui/popover.tsx +25 -1
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +13 -10
- package/corpus/templates/forms/app/root.tsx +6 -1
- package/corpus/templates/forms/changelog/2026-07-17-form-builder-popovers-stay-within-the-viewport.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-form-editor-loading-matches-current-sidebar-free-layout.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +14 -3
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +8 -1
- package/corpus/templates/plan/app/root.tsx +0 -2
- package/corpus/templates/plan/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +23 -23
- package/corpus/templates/slides/changelog/2026-07-17-duplicated-slides-now-stay-in-the-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/tasks/app/components/layout/Layout.tsx +2 -13
- package/corpus/templates/tasks/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it.md +6 -0
- package/dist/a2a/artifact-response.d.ts +5 -1
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +18 -12
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +84 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +10 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +4 -0
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/active-run-state.d.ts +7 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +28 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +27 -8
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +4 -2
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +33 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -12
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +1 -1
- package/dist/client/use-agent-chat-home-handoff.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/integrations/a2a-continuation-processor.js +62 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +3 -0
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +75 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +81 -5
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +23 -2
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +257 -35
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts +6 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +94 -7
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/dist/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +20 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/integrations.mdx +27 -27
- package/package.json +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "h3";
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
+
appendA2AArtifactLinks,
|
|
17
18
|
buildA2ARecoverableArtifactMessage,
|
|
18
19
|
type A2AToolResultSummary,
|
|
19
20
|
} from "../a2a/artifact-response.js";
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
createA2AApproval,
|
|
28
29
|
updateTaskStatusMessage,
|
|
29
30
|
} from "../a2a/task-store.js";
|
|
31
|
+
import type { Message as A2AMessage } from "../a2a/types.js";
|
|
30
32
|
import type { ActionHttpConfig } from "../action.js";
|
|
31
33
|
import {
|
|
32
34
|
canUpdateAgentAppModelDefaultSettings,
|
|
@@ -69,7 +71,7 @@ import {
|
|
|
69
71
|
callerHasRunAccess,
|
|
70
72
|
callerHasThreadAccess,
|
|
71
73
|
} from "../agent/run-ownership.js";
|
|
72
|
-
import { readBackgroundRunClaim } from "../agent/run-store.js";
|
|
74
|
+
import { markTurnAborted, readBackgroundRunClaim } from "../agent/run-store.js";
|
|
73
75
|
import {
|
|
74
76
|
buildCurrentTimeUserContext,
|
|
75
77
|
buildRuntimeContextPrompt,
|
|
@@ -275,6 +277,74 @@ export { loadRunCodeToolEntries };
|
|
|
275
277
|
export { shouldDisableRecurringJobsRuntime };
|
|
276
278
|
export { finalizeClaimedAgentChatProcessRunFailure };
|
|
277
279
|
|
|
280
|
+
export function createSerializedA2ATaskStatusWriter(
|
|
281
|
+
taskId: string,
|
|
282
|
+
writeStatus: (
|
|
283
|
+
taskId: string,
|
|
284
|
+
message: A2AMessage,
|
|
285
|
+
) => Promise<void> = updateTaskStatusMessage,
|
|
286
|
+
onError: (error: unknown) => void = (error) => {
|
|
287
|
+
console.error(
|
|
288
|
+
`[A2A] Failed to persist recoverable artifact message for task ${taskId}:`,
|
|
289
|
+
error,
|
|
290
|
+
);
|
|
291
|
+
},
|
|
292
|
+
): {
|
|
293
|
+
enqueue: (message: A2AMessage) => void;
|
|
294
|
+
flush: () => Promise<void>;
|
|
295
|
+
} {
|
|
296
|
+
const maxAttempts = 3;
|
|
297
|
+
let latestWrite: Promise<void> = Promise.resolve();
|
|
298
|
+
|
|
299
|
+
const persistWithRetry = async (message: A2AMessage): Promise<void> => {
|
|
300
|
+
let lastError: unknown;
|
|
301
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
302
|
+
try {
|
|
303
|
+
await writeStatus(taskId, message);
|
|
304
|
+
return;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
lastError = error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
onError(lastError);
|
|
311
|
+
} catch {
|
|
312
|
+
// The durable-write error below remains the authoritative failure.
|
|
313
|
+
}
|
|
314
|
+
throw lastError;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
enqueue(message) {
|
|
319
|
+
// A newer checkpoint supersedes an earlier failed checkpoint, so keep
|
|
320
|
+
// the queue moving. flush() still rejects when the latest write itself
|
|
321
|
+
// cannot be made durable after bounded retries.
|
|
322
|
+
latestWrite = latestWrite
|
|
323
|
+
.catch(() => undefined)
|
|
324
|
+
.then(() => {
|
|
325
|
+
return persistWithRetry(message);
|
|
326
|
+
});
|
|
327
|
+
},
|
|
328
|
+
flush() {
|
|
329
|
+
return latestWrite;
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export async function resolveA2ARecoverableArtifactSecret(
|
|
335
|
+
orgId: string | null | undefined = getRequestOrgId(),
|
|
336
|
+
): Promise<string | undefined> {
|
|
337
|
+
const globalSecret = process.env.A2A_SECRET?.trim();
|
|
338
|
+
if (globalSecret) return globalSecret;
|
|
339
|
+
if (!orgId) return undefined;
|
|
340
|
+
try {
|
|
341
|
+
const { getOrgA2ASecret } = await import("../org/context.js");
|
|
342
|
+
return (await getOrgA2ASecret(orgId))?.trim() || undefined;
|
|
343
|
+
} catch {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
278
348
|
export function buildLeanRunPolicyPrompt(
|
|
279
349
|
codeEditingSurfaceRestriction: string,
|
|
280
350
|
prodCodeExecPromptNote: string,
|
|
@@ -1468,6 +1538,10 @@ export function createAgentChatPlugin(
|
|
|
1468
1538
|
const a2aEvents: AgentChatEvent[] = [];
|
|
1469
1539
|
const a2aToolResults: A2AToolResultSummary[] = [];
|
|
1470
1540
|
let lastRecoverableArtifactText = "";
|
|
1541
|
+
const recoverableArtifactSecret =
|
|
1542
|
+
await resolveA2ARecoverableArtifactSecret();
|
|
1543
|
+
const recoverableArtifactStatusWriter =
|
|
1544
|
+
createSerializedA2ATaskStatusWriter(context.taskId);
|
|
1471
1545
|
const controller = new AbortController();
|
|
1472
1546
|
|
|
1473
1547
|
console.log(
|
|
@@ -1504,16 +1578,28 @@ export function createAgentChatPlugin(
|
|
|
1504
1578
|
isError: event.isError,
|
|
1505
1579
|
completedSideEffect: event.completedSideEffect,
|
|
1506
1580
|
});
|
|
1507
|
-
const
|
|
1581
|
+
const artifactBaseUrl = resolveArtifactBaseUrl(context.event);
|
|
1582
|
+
const recoverableArtifactMessage =
|
|
1508
1583
|
buildA2ARecoverableArtifactMessage(a2aToolResults, {
|
|
1509
|
-
baseUrl:
|
|
1584
|
+
baseUrl: artifactBaseUrl,
|
|
1510
1585
|
});
|
|
1586
|
+
const recoverableArtifactText = recoverableArtifactMessage
|
|
1587
|
+
? appendA2AArtifactLinks(
|
|
1588
|
+
recoverableArtifactMessage,
|
|
1589
|
+
a2aToolResults,
|
|
1590
|
+
{
|
|
1591
|
+
baseUrl: artifactBaseUrl,
|
|
1592
|
+
includePersistedArtifactMarker: true,
|
|
1593
|
+
persistedArtifactSecret: recoverableArtifactSecret,
|
|
1594
|
+
},
|
|
1595
|
+
)
|
|
1596
|
+
: null;
|
|
1511
1597
|
if (
|
|
1512
1598
|
recoverableArtifactText &&
|
|
1513
1599
|
recoverableArtifactText !== lastRecoverableArtifactText
|
|
1514
1600
|
) {
|
|
1515
1601
|
lastRecoverableArtifactText = recoverableArtifactText;
|
|
1516
|
-
|
|
1602
|
+
recoverableArtifactStatusWriter.enqueue({
|
|
1517
1603
|
role: "agent",
|
|
1518
1604
|
metadata: { agentNativeRecoverableArtifacts: true },
|
|
1519
1605
|
parts: [
|
|
@@ -1522,11 +1608,6 @@ export function createAgentChatPlugin(
|
|
|
1522
1608
|
text: recoverableArtifactText,
|
|
1523
1609
|
},
|
|
1524
1610
|
],
|
|
1525
|
-
}).catch((err) => {
|
|
1526
|
-
console.error(
|
|
1527
|
-
`[A2A] Failed to persist recoverable artifact message for task ${context.taskId}:`,
|
|
1528
|
-
err,
|
|
1529
|
-
);
|
|
1530
1611
|
});
|
|
1531
1612
|
}
|
|
1532
1613
|
} else if (event.type === "error") {
|
|
@@ -1548,6 +1629,10 @@ export function createAgentChatPlugin(
|
|
|
1548
1629
|
},
|
|
1549
1630
|
);
|
|
1550
1631
|
|
|
1632
|
+
// The continuation can observe terminal output immediately, so make
|
|
1633
|
+
// its latest mutation checkpoint durable first.
|
|
1634
|
+
await recoverableArtifactStatusWriter.flush();
|
|
1635
|
+
|
|
1551
1636
|
const approval = [...a2aEvents]
|
|
1552
1637
|
.reverse()
|
|
1553
1638
|
.find(
|
|
@@ -4137,6 +4222,31 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4137
4222
|
callerHasRunAccess(owner, runId, "viewer", { orgId });
|
|
4138
4223
|
const canEditRun = (runId: string) =>
|
|
4139
4224
|
callerHasRunAccess(owner, runId, "editor", { orgId });
|
|
4225
|
+
const canEditThread = (threadId: string) =>
|
|
4226
|
+
callerHasThreadAccess(owner, threadId, "editor", { orgId });
|
|
4227
|
+
|
|
4228
|
+
// Route: POST /runs/turn/:turnId/abort
|
|
4229
|
+
// Covers the short window where the client has sent a turn but the
|
|
4230
|
+
// server has not yet created its background run id.
|
|
4231
|
+
const turnAbortMatch =
|
|
4232
|
+
url.match(/\/runs\/turn\/([^/?]+)\/abort/) ||
|
|
4233
|
+
url.match(/^\/turn\/([^/?]+)\/abort/);
|
|
4234
|
+
if (turnAbortMatch && method === "POST") {
|
|
4235
|
+
const turnId = decodeURIComponent(turnAbortMatch[1]);
|
|
4236
|
+
const body = await readBody(event).catch(() => null);
|
|
4237
|
+
const threadId =
|
|
4238
|
+
typeof body?.threadId === "string" ? body.threadId : "";
|
|
4239
|
+
if (
|
|
4240
|
+
!/^[a-zA-Z0-9_-]{1,160}$/.test(turnId) ||
|
|
4241
|
+
!threadId ||
|
|
4242
|
+
!(await canEditThread(threadId))
|
|
4243
|
+
) {
|
|
4244
|
+
setResponseStatus(event, 404);
|
|
4245
|
+
return { error: "Run not found" };
|
|
4246
|
+
}
|
|
4247
|
+
await markTurnAborted(threadId, turnId);
|
|
4248
|
+
return { ok: true };
|
|
4249
|
+
}
|
|
4140
4250
|
|
|
4141
4251
|
// Route: GET /runs/list?goalId=agent-team|agent-harness
|
|
4142
4252
|
// Returns background agents in the Code hub-compatible run shape.
|
|
@@ -5180,6 +5290,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5180
5290
|
}
|
|
5181
5291
|
workerBody = {
|
|
5182
5292
|
...parsedPayload,
|
|
5293
|
+
...(prepared.body.internalContinuation === true
|
|
5294
|
+
? { internalContinuation: true }
|
|
5295
|
+
: {}),
|
|
5183
5296
|
[AGENT_CHAT_BACKGROUND_RUN_FIELD]: preparedMarker,
|
|
5184
5297
|
};
|
|
5185
5298
|
}
|
|
@@ -2579,6 +2579,27 @@ function createAgentNativePlugins(
|
|
|
2579
2579
|
].filter(Boolean);
|
|
2580
2580
|
}
|
|
2581
2581
|
|
|
2582
|
+
function resolveAgentNativeTemplate(cwd: string): string {
|
|
2583
|
+
const configured = [
|
|
2584
|
+
process.env.AGENT_NATIVE_TEMPLATE,
|
|
2585
|
+
process.env.VITE_AGENT_NATIVE_TEMPLATE,
|
|
2586
|
+
process.env.VITE_APP_TEMPLATE,
|
|
2587
|
+
].find((value) => value?.trim());
|
|
2588
|
+
if (configured) return configured.trim().toLowerCase();
|
|
2589
|
+
|
|
2590
|
+
const normalizedCwd = cwd.replaceAll("\\", "/");
|
|
2591
|
+
const marker = "/templates/";
|
|
2592
|
+
const markerIndex = normalizedCwd.lastIndexOf(marker);
|
|
2593
|
+
if (markerIndex === -1) return "";
|
|
2594
|
+
return (
|
|
2595
|
+
normalizedCwd
|
|
2596
|
+
.slice(markerIndex + marker.length)
|
|
2597
|
+
.split("/")[0]
|
|
2598
|
+
?.trim()
|
|
2599
|
+
.toLowerCase() ?? ""
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2582
2603
|
function createAgentNativeConfig(
|
|
2583
2604
|
options: ClientConfigOptions | AgentNativeVitePluginOptions = {},
|
|
2584
2605
|
command?: AgentNativeViteCommand,
|
|
@@ -2683,6 +2704,9 @@ function createAgentNativeConfig(
|
|
|
2683
2704
|
__AGENT_NATIVE_MCP_INTEGRATIONS_CONFIG__: JSON.stringify(
|
|
2684
2705
|
normalizeMcpIntegrationsConfig(options.mcpIntegrations),
|
|
2685
2706
|
),
|
|
2707
|
+
__AGENT_NATIVE_TEMPLATE__: JSON.stringify(
|
|
2708
|
+
resolveAgentNativeTemplate(cwd),
|
|
2709
|
+
),
|
|
2686
2710
|
},
|
|
2687
2711
|
server: {
|
|
2688
2712
|
...(userConfig.server ?? {}),
|
|
@@ -83,7 +83,7 @@ When the user asks for a dashboard:
|
|
|
83
83
|
2. If a metric definition, date range, or grain is ambiguous and the choice would change the panel's numbers, use the `ask-question` clarifying tool once before building. Skip it when the dictionary or the user already settled it.
|
|
84
84
|
3. If a metric is not documented, do not guess column names. Ask for the table/columns or introspect the provider schema, then propose a dictionary entry with `save-data-dictionary-entry`.
|
|
85
85
|
4. Build a complete `SqlDashboardConfig` with `name` and `panels`. Optionally set top-level `columns` (1–6, default 2) to control how many grid columns the panels before any section use.
|
|
86
|
-
5. Every panel needs `id`, `title`, `source`, `chartType`, `width`, and `sql`. `width` is the number of grid columns the panel spans (1..6, clamped to the active section's column count). Section panels skip `source` and `sql` and may set their own `columns` (1–6) to override the dashboard default for the panels following the section. Extension panels (`chartType: "extension"`) also skip `source` and `sql`;
|
|
86
|
+
5. Every panel needs `id`, `title`, `source`, `chartType`, `width`, and `sql`. `width` is the number of grid columns the panel spans (1..6, clamped to the active section's column count). Section panels skip `source` and `sql` and may set their own `columns` (1–6) to override the dashboard default for the panels following the section. Extension panels (`chartType: "extension"`) also skip `source` and `sql`; use `config.extensionSlotId` for new panels, with legacy `config.extensionId` supported for existing direct embeds (see "Embedding An Extension As A Panel").
|
|
87
87
|
6. Persist with `update-dashboard`, not raw SQL or settings writes.
|
|
88
88
|
7. Navigate to it with `pnpm action navigate --view=adhoc --dashboardId=<id>`.
|
|
89
89
|
|
|
@@ -114,14 +114,18 @@ rather than forcing it into a native dashboard config.
|
|
|
114
114
|
|
|
115
115
|
## Embedding An Extension As A Panel
|
|
116
116
|
|
|
117
|
-
Use `chartType: "extension"` to
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
Use `chartType: "extension"` to add an extension box alongside normal SQL
|
|
118
|
+
charts. The panel skips `source` and `sql`. New boxes use a stable slot id in
|
|
119
|
+
`config.extensionSlotId`:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
analytics.dashboard.<dashboard-id>.panel.<panel-id>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Create or choose the extension, call `add-extension-slot-target` with the
|
|
126
|
+
extension id and slot id, then call `install-extension` with the same values.
|
|
127
|
+
The dashboard panel is shared, while the installed extension is per-user.
|
|
128
|
+
Empty slots show the normal install affordance instead of a broken iframe.
|
|
125
129
|
|
|
126
130
|
```jsonc
|
|
127
131
|
{
|
|
@@ -129,21 +133,22 @@ extension panel without a non-empty `config.extensionId`.
|
|
|
129
133
|
"title": "Pipeline Widget",
|
|
130
134
|
"chartType": "extension",
|
|
131
135
|
"width": 3,
|
|
132
|
-
"config": {
|
|
136
|
+
"config": {
|
|
137
|
+
"extensionSlotId": "analytics.dashboard.weekly-metrics.panel.pipeline-widget",
|
|
138
|
+
},
|
|
133
139
|
}
|
|
134
140
|
```
|
|
135
141
|
|
|
136
142
|
Notes:
|
|
137
143
|
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
the same audience as the dashboard so all viewers can see it.
|
|
144
|
+
- Slot-backed extensions receive the dashboard id, name, description, current
|
|
145
|
+
filters, and panel id/title/slot id as live context.
|
|
146
|
+
- Installs and extension access are per viewer. Sharing the dashboard does not
|
|
147
|
+
automatically install or grant access to its extension for other viewers.
|
|
148
|
+
- `config.extensionId` remains supported for legacy direct embeds. Use it only
|
|
149
|
+
when preserving an existing dashboard; new boxes should use a slot.
|
|
145
150
|
|
|
146
|
-
## Cloning
|
|
151
|
+
## Cloning A Legacy Direct-Extension Dashboard (e.g. per-customer copies)
|
|
147
152
|
|
|
148
153
|
When the user asks for a copy of an existing extension-backed dashboard for a
|
|
149
154
|
different customer/org (for example "make an Intuit version of the Roku usage
|
|
@@ -154,8 +159,11 @@ from that written file — never by pulling the body into chat context first or
|
|
|
154
159
|
re-typing it as a `content` argument.
|
|
155
160
|
|
|
156
161
|
1. `get-sql-dashboard` with `includeConfig: true` on the source dashboard and
|
|
157
|
-
confirm the target panel is `chartType: "extension"
|
|
158
|
-
`config.extensionId
|
|
162
|
+
confirm the target panel is a legacy `chartType: "extension"` panel with
|
|
163
|
+
`config.extensionId`; grab that extension id. For a slot-backed panel, clone
|
|
164
|
+
the dashboard panel with a new stable `extensionSlotId`, then target and
|
|
165
|
+
install the desired extension into that slot instead of using this body-copy
|
|
166
|
+
playbook.
|
|
159
167
|
2. `get-extension` for that id with `forceContent: true` **exactly once**. Reuse
|
|
160
168
|
that body for the rest of the turn — a second same-run read intentionally
|
|
161
169
|
omits `content` and returns `contentOmitted` instead. That is not the content
|
|
@@ -397,7 +405,7 @@ type PanelInput = PanelPatch & {
|
|
|
397
405
|
chartType: NonNullable<PanelPatch["chartType"]>;
|
|
398
406
|
source?: PanelPatch["source"]; // required for non-section / non-extension panels
|
|
399
407
|
sql?: string; // required for non-section / non-extension panels
|
|
400
|
-
// For chartType "extension": config.extensionId is
|
|
408
|
+
// For chartType "extension": use config.extensionSlotId; extensionId is legacy direct embed compatibility.
|
|
401
409
|
};
|
|
402
410
|
|
|
403
411
|
type PanelFilter = {
|
|
@@ -163,6 +163,14 @@ membership id when its native update status reports `update-available`.
|
|
|
163
163
|
serialization traps. The script is constrained: only documented dashboard
|
|
164
164
|
method calls with JSON-compatible arguments are parsed; variables, imports,
|
|
165
165
|
loops, functions, network, filesystem, and DB access are not available.
|
|
166
|
+
- Dashboard extension boxes use `chartType: "extension"` with
|
|
167
|
+
`config.extensionSlotId`. The stable per-box slot is
|
|
168
|
+
`analytics.dashboard.<dashboard-id>.panel.<panel-id>`. To add an extension,
|
|
169
|
+
create/choose it, call `add-extension-slot-target` with that slot id, then
|
|
170
|
+
`install-extension` with the same slot id. Installs are per-user; the
|
|
171
|
+
dashboard panel remains shared. Slot-backed extensions receive dashboard id,
|
|
172
|
+
name, description, current filters, and the panel id/title/slot id as context.
|
|
173
|
+
`config.extensionId` is legacy direct embedding for existing dashboards.
|
|
166
174
|
- Dashboard saves keep bounded history in SQL. Use
|
|
167
175
|
`list-dashboard-revisions` to inspect undo points and
|
|
168
176
|
`restore-dashboard-revision` to restore one instead of hand-editing history
|
|
@@ -132,7 +132,7 @@ export const DASHBOARD_MUTATION_EXAMPLES = [
|
|
|
132
132
|
`dashboard.insertPanel({"id":"new-kpi","title":"New KPI","source":"first-party","chartType":"metric","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}'"}).atTop();`,
|
|
133
133
|
`dashboard.insertPanel({"id":"new-chart","title":"New Chart","source":"first-party","chartType":"line","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT event_date AS date, COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}' GROUP BY event_date ORDER BY event_date"}).nextTo("retention-over-time");`,
|
|
134
134
|
`dashboard.insertPanel({"id":"row-chart","title":"Row Chart","source":"first-party","chartType":"bar","width":1,"config":{"timeScope":"dashboard"},"sql":"SELECT event_name AS name, COUNT(*) AS value FROM analytics_events WHERE event_date >= '{{dateStart}}' AND event_date < '{{dateEnd}}' GROUP BY event_name"}).atRow(2);`,
|
|
135
|
-
'dashboard.insertPanel({"id":"pipeline-widget","title":"Pipeline Widget","chartType":"extension","width":3,"config":{"
|
|
135
|
+
'dashboard.insertPanel({"id":"pipeline-widget","title":"Pipeline Widget","chartType":"extension","width":3,"config":{"extensionSlotId":"analytics.dashboard.<dashboard-id>.panel.pipeline-widget"}}).atBottom();',
|
|
136
136
|
] as const;
|
|
137
137
|
|
|
138
138
|
export type DashboardMutationOperation =
|
|
@@ -369,8 +369,12 @@ export function validateDashboardConfig(
|
|
|
369
369
|
cfg && typeof cfg.extensionId === "string"
|
|
370
370
|
? cfg.extensionId.trim()
|
|
371
371
|
: "";
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
const extensionSlotId =
|
|
373
|
+
cfg && typeof cfg.extensionSlotId === "string"
|
|
374
|
+
? cfg.extensionSlotId.trim()
|
|
375
|
+
: "";
|
|
376
|
+
if (!extensionId && !extensionSlotId) {
|
|
377
|
+
return `panel[${i}].config.extensionId or config.extensionSlotId is required for extension panels`;
|
|
374
378
|
}
|
|
375
379
|
}
|
|
376
380
|
if (
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useDemoModeStatus, useT } from "@agent-native/core/client";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
EmbeddedExtension,
|
|
4
|
+
ExtensionSlot,
|
|
5
|
+
} from "@agent-native/core/client/extensions";
|
|
3
6
|
import {
|
|
4
7
|
IconArrowsSort,
|
|
5
8
|
IconSortAscending,
|
|
@@ -1185,6 +1188,8 @@ interface SqlChartProps {
|
|
|
1185
1188
|
className?: string;
|
|
1186
1189
|
loadData?: boolean;
|
|
1187
1190
|
onExportCsvChange?: (handler: (() => void) | null) => void;
|
|
1191
|
+
/** Dashboard/panel state sent to slot-backed extension boxes. */
|
|
1192
|
+
extensionContext?: Record<string, unknown> | null;
|
|
1188
1193
|
}
|
|
1189
1194
|
|
|
1190
1195
|
export function SqlChart({
|
|
@@ -1192,6 +1197,7 @@ export function SqlChart({
|
|
|
1192
1197
|
resolvedSql,
|
|
1193
1198
|
loadData = true,
|
|
1194
1199
|
onExportCsvChange,
|
|
1200
|
+
extensionContext,
|
|
1195
1201
|
}: SqlChartProps) {
|
|
1196
1202
|
const t = useT();
|
|
1197
1203
|
const { enabled: demoModeEnabled } = useDemoModeStatus();
|
|
@@ -1266,11 +1272,12 @@ export function SqlChart({
|
|
|
1266
1272
|
);
|
|
1267
1273
|
}
|
|
1268
1274
|
|
|
1269
|
-
// Extension panels render a
|
|
1270
|
-
//
|
|
1275
|
+
// Extension panels render either a named extension-point slot or a legacy
|
|
1276
|
+
// direct extension iframe instead of querying a data source.
|
|
1271
1277
|
if (isExtension) {
|
|
1272
1278
|
const extensionId = panel.config?.extensionId;
|
|
1273
|
-
|
|
1279
|
+
const slotId = panel.config?.extensionSlotId;
|
|
1280
|
+
if (!extensionId && !slotId) {
|
|
1274
1281
|
return (
|
|
1275
1282
|
<div className="flex flex-1 items-center justify-center px-4 py-8 min-h-[120px]">
|
|
1276
1283
|
<p className="text-sm text-muted-foreground text-center">
|
|
@@ -1280,7 +1287,12 @@ export function SqlChart({
|
|
|
1280
1287
|
);
|
|
1281
1288
|
}
|
|
1282
1289
|
return (
|
|
1283
|
-
<DashboardExtensionPanel
|
|
1290
|
+
<DashboardExtensionPanel
|
|
1291
|
+
extensionId={extensionId}
|
|
1292
|
+
panelId={panel.id}
|
|
1293
|
+
slotId={slotId}
|
|
1294
|
+
context={extensionContext}
|
|
1295
|
+
/>
|
|
1284
1296
|
);
|
|
1285
1297
|
}
|
|
1286
1298
|
const colors = panel.config?.colors || DEFAULT_COLORS;
|
|
@@ -1409,9 +1421,13 @@ export function SqlChart({
|
|
|
1409
1421
|
function DashboardExtensionPanel({
|
|
1410
1422
|
extensionId,
|
|
1411
1423
|
panelId,
|
|
1424
|
+
slotId,
|
|
1425
|
+
context,
|
|
1412
1426
|
}: {
|
|
1413
|
-
extensionId
|
|
1427
|
+
extensionId?: string;
|
|
1414
1428
|
panelId: string;
|
|
1429
|
+
slotId?: string;
|
|
1430
|
+
context?: Record<string, unknown> | null;
|
|
1415
1431
|
}) {
|
|
1416
1432
|
const t = useT();
|
|
1417
1433
|
// Hold the report-readiness marker until the extension iframe paints so
|
|
@@ -1419,6 +1435,18 @@ function DashboardExtensionPanel({
|
|
|
1419
1435
|
const [ready, setReady] = useState(false);
|
|
1420
1436
|
const [unavailable, setUnavailable] = useState(false);
|
|
1421
1437
|
|
|
1438
|
+
if (slotId) {
|
|
1439
|
+
return (
|
|
1440
|
+
<ExtensionSlot
|
|
1441
|
+
id={slotId}
|
|
1442
|
+
context={context}
|
|
1443
|
+
showEmptyAffordance
|
|
1444
|
+
className="min-h-[120px] w-full"
|
|
1445
|
+
toolClassName="w-full"
|
|
1446
|
+
/>
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1422
1450
|
// Embedding never grants access to the extension itself (same model as
|
|
1423
1451
|
// ExtensionSlots). A viewer with dashboard-only access who can't see the
|
|
1424
1452
|
// referenced extension gets a clear message instead of a blank panel.
|
|
@@ -1438,7 +1466,7 @@ function DashboardExtensionPanel({
|
|
|
1438
1466
|
data-dashboard-report-loading={ready ? undefined : "true"}
|
|
1439
1467
|
>
|
|
1440
1468
|
<EmbeddedExtension
|
|
1441
|
-
extensionId={extensionId}
|
|
1469
|
+
extensionId={extensionId!}
|
|
1442
1470
|
slotId={`dashboard-panel-${panelId}`}
|
|
1443
1471
|
className="w-full"
|
|
1444
1472
|
// Intentional for v1: extension panels are standalone widgets and do not
|
|
@@ -163,8 +163,6 @@ export function Layout({ children }: LayoutProps) {
|
|
|
163
163
|
activePath: location.pathname,
|
|
164
164
|
enabled: !isAskRoute && !reportScreenshot,
|
|
165
165
|
});
|
|
166
|
-
const sidebarScope = chatHomeHandoffActive ? null : analyticsScope;
|
|
167
|
-
|
|
168
166
|
useEffect(() => {
|
|
169
167
|
function handleChatRunning(event: Event) {
|
|
170
168
|
const detail = (event as CustomEvent).detail;
|
|
@@ -294,7 +292,7 @@ export function Layout({ children }: LayoutProps) {
|
|
|
294
292
|
t("chat.suggestionAnomalies"),
|
|
295
293
|
t("chat.suggestionMrr"),
|
|
296
294
|
]}
|
|
297
|
-
scope={
|
|
295
|
+
scope={analyticsScope}
|
|
298
296
|
composerSlot={<CreativeContextComposerChip />}
|
|
299
297
|
>
|
|
300
298
|
{contentFrame}
|
|
@@ -67,6 +67,7 @@ interface SqlChartCardProps {
|
|
|
67
67
|
isDragSource?: boolean;
|
|
68
68
|
selectedForChat?: boolean;
|
|
69
69
|
onSelectForChat?: (options?: SelectDashboardPanelOptions) => void;
|
|
70
|
+
extensionContext?: Record<string, unknown> | null;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
const PanelDragHandle = memo(function PanelDragHandle({
|
|
@@ -122,6 +123,7 @@ export function SqlChartCard({
|
|
|
122
123
|
isDragSource = false,
|
|
123
124
|
selectedForChat = false,
|
|
124
125
|
onSelectForChat,
|
|
126
|
+
extensionContext,
|
|
125
127
|
}: SqlChartCardProps) {
|
|
126
128
|
const t = useT();
|
|
127
129
|
const queryClient = useQueryClient();
|
|
@@ -346,6 +348,7 @@ export function SqlChartCard({
|
|
|
346
348
|
panel={panel}
|
|
347
349
|
resolvedSql={resolvedSql}
|
|
348
350
|
loadData
|
|
351
|
+
extensionContext={extensionContext}
|
|
349
352
|
/>
|
|
350
353
|
)}
|
|
351
354
|
<div className="absolute right-1 top-1 flex items-center gap-1 opacity-0 group-hover:opacity-100">
|
|
@@ -435,6 +438,7 @@ export function SqlChartCard({
|
|
|
435
438
|
panel={panel}
|
|
436
439
|
resolvedSql={resolvedSql}
|
|
437
440
|
loadData
|
|
441
|
+
extensionContext={extensionContext}
|
|
438
442
|
/>
|
|
439
443
|
</ChartFillHeight>
|
|
440
444
|
</div>
|
|
@@ -616,6 +620,7 @@ export function SqlChartCard({
|
|
|
616
620
|
resolvedSql={resolvedSql}
|
|
617
621
|
loadData={shouldLoadData}
|
|
618
622
|
onExportCsvChange={handleExportCsvChange}
|
|
623
|
+
extensionContext={extensionContext}
|
|
619
624
|
/>
|
|
620
625
|
</CardContent>
|
|
621
626
|
</Card>
|
|
@@ -627,7 +632,12 @@ export function SqlChartCard({
|
|
|
627
632
|
</DialogHeader>
|
|
628
633
|
<div className="flex min-h-0 flex-1 flex-col overflow-auto">
|
|
629
634
|
<ChartFillHeight>
|
|
630
|
-
<SqlChart
|
|
635
|
+
<SqlChart
|
|
636
|
+
panel={panel}
|
|
637
|
+
resolvedSql={resolvedSql}
|
|
638
|
+
loadData
|
|
639
|
+
extensionContext={extensionContext}
|
|
640
|
+
/>
|
|
631
641
|
</ChartFillHeight>
|
|
632
642
|
</div>
|
|
633
643
|
</DialogContent>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable extension-point name for a dashboard box. The dashboard and panel ids
|
|
3
|
+
* are encoded independently so arbitrary saved ids cannot collapse into one
|
|
4
|
+
* slot or add path separators to the slots API.
|
|
5
|
+
*/
|
|
6
|
+
export function dashboardExtensionSlotId(
|
|
7
|
+
dashboardId: string,
|
|
8
|
+
panelId: string,
|
|
9
|
+
): string {
|
|
10
|
+
return `analytics.dashboard.${encodeURIComponent(dashboardId)}.panel.${encodeURIComponent(panelId)}`;
|
|
11
|
+
}
|
|
@@ -142,6 +142,7 @@ import {
|
|
|
142
142
|
resolveFilterVars,
|
|
143
143
|
} from "./DashboardFilterBar";
|
|
144
144
|
import { EmailReportDialog } from "./EmailReportDialog";
|
|
145
|
+
import { dashboardExtensionSlotId } from "./extension-slot";
|
|
145
146
|
import { interpolate } from "./interpolate";
|
|
146
147
|
import { serializePanelSql } from "./panel-sql";
|
|
147
148
|
import { AddPanelPopover, PanelEditorDialog } from "./PanelEditorDialog";
|
|
@@ -271,6 +272,7 @@ const PanelCell = memo(function PanelCell({
|
|
|
271
272
|
onRemovePanel,
|
|
272
273
|
onEditPanel,
|
|
273
274
|
onSavePanel,
|
|
275
|
+
dashboardExtensionContext,
|
|
274
276
|
}: {
|
|
275
277
|
panel: SqlPanel;
|
|
276
278
|
vars: Record<string, string>;
|
|
@@ -286,6 +288,7 @@ const PanelCell = memo(function PanelCell({
|
|
|
286
288
|
onRemovePanel: (panelId: string) => void;
|
|
287
289
|
onEditPanel: (panel: SqlPanel) => void;
|
|
288
290
|
onSavePanel: (panel: SqlPanel) => Promise<void>;
|
|
291
|
+
dashboardExtensionContext: Record<string, unknown>;
|
|
289
292
|
}) {
|
|
290
293
|
const resolved = useMemo(
|
|
291
294
|
() =>
|
|
@@ -366,6 +369,23 @@ const PanelCell = memo(function PanelCell({
|
|
|
366
369
|
isDragSource={isDragSource}
|
|
367
370
|
selectedForChat={selectedForChat}
|
|
368
371
|
onSelectForChat={handleSelectForChat}
|
|
372
|
+
extensionContext={
|
|
373
|
+
panel.chartType === "extension"
|
|
374
|
+
? {
|
|
375
|
+
...dashboardExtensionContext,
|
|
376
|
+
panel: {
|
|
377
|
+
id: panel.id,
|
|
378
|
+
title: panel.title,
|
|
379
|
+
slotId:
|
|
380
|
+
panel.config?.extensionSlotId ??
|
|
381
|
+
dashboardExtensionSlotId(
|
|
382
|
+
String(dashboardExtensionContext.dashboardId),
|
|
383
|
+
panel.id,
|
|
384
|
+
),
|
|
385
|
+
},
|
|
386
|
+
}
|
|
387
|
+
: undefined
|
|
388
|
+
}
|
|
369
389
|
/>
|
|
370
390
|
</div>
|
|
371
391
|
);
|
|
@@ -1966,6 +1986,13 @@ export default function SqlDashboardPage() {
|
|
|
1966
1986
|
onRemovePanel={removePanel}
|
|
1967
1987
|
onEditPanel={openEditPanel}
|
|
1968
1988
|
onSavePanel={handleSavePanel}
|
|
1989
|
+
dashboardExtensionContext={{
|
|
1990
|
+
dashboardId,
|
|
1991
|
+
dashboardName: dashboard.name,
|
|
1992
|
+
dashboardDescription:
|
|
1993
|
+
dashboard.description ?? null,
|
|
1994
|
+
filters: vars,
|
|
1995
|
+
}}
|
|
1969
1996
|
/>
|
|
1970
1997
|
<DashboardDropLine
|
|
1971
1998
|
slot={{
|
|
@@ -99,8 +99,14 @@ export interface SqlPanelConfig {
|
|
|
99
99
|
/**
|
|
100
100
|
* Extension panels only (`chartType: "extension"`): id of the extension to
|
|
101
101
|
* render inline as a sandboxed iframe instead of running the SQL pipeline.
|
|
102
|
+
* Kept for dashboards saved before slot-backed dashboard boxes.
|
|
102
103
|
*/
|
|
103
104
|
extensionId?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Extension panels only: a named extension-point slot. The dashboard renders
|
|
107
|
+
* its installed extensions as this panel's box and supplies dashboard context.
|
|
108
|
+
*/
|
|
109
|
+
extensionSlotId?: string;
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
export interface SqlPanel {
|
|
@@ -64,6 +64,14 @@ function panelSummaries(config: Record<string, unknown>) {
|
|
|
64
64
|
typeof panelConfig.description === "string"
|
|
65
65
|
? panelConfig.description
|
|
66
66
|
: undefined,
|
|
67
|
+
extensionId:
|
|
68
|
+
typeof panelConfig.extensionId === "string"
|
|
69
|
+
? panelConfig.extensionId
|
|
70
|
+
: undefined,
|
|
71
|
+
extensionSlotId:
|
|
72
|
+
typeof panelConfig.extensionSlotId === "string"
|
|
73
|
+
? panelConfig.extensionSlotId
|
|
74
|
+
: undefined,
|
|
67
75
|
};
|
|
68
76
|
});
|
|
69
77
|
}
|