@agent-native/core 0.99.1 → 0.99.3
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 +19 -0
- package/corpus/core/docs/content/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ar-SA/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/de-DE/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/es-ES/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/fr-FR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/hi-IN/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ja-JP/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/ko-KR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/pt-BR/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/zh-CN/a2a-protocol.mdx +42 -0
- package/corpus/core/docs/content/locales/zh-TW/a2a-protocol.mdx +42 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +12 -38
- package/corpus/core/src/audit/config.ts +0 -1
- package/corpus/core/src/chat-threads/store.ts +57 -41
- package/corpus/core/src/client/AssistantChat.tsx +1 -2
- package/corpus/core/src/client/chat/message-components.tsx +15 -3
- package/corpus/core/src/client/chat/tool-call-display.tsx +18 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +5 -9
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +0 -133
- package/corpus/core/src/client/index.ts +7 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +9 -65
- package/corpus/core/src/client/settings/SettingsPanel.tsx +1 -1
- package/corpus/core/src/client/use-db-sync.ts +3 -15
- package/corpus/core/src/client/use-demo-mode-status.ts +16 -30
- package/corpus/core/src/demo/browser-state.ts +47 -0
- package/corpus/core/src/demo/fetch-interceptor.ts +12 -59
- package/corpus/core/src/deploy/workspace-deploy.ts +2 -2
- package/corpus/core/src/server/action-discovery.ts +0 -1
- package/corpus/core/src/server/core-routes-plugin.ts +0 -30
- package/corpus/core/src/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +48 -7
- package/corpus/templates/analytics/AGENTS.md +4 -4
- package/corpus/templates/analytics/actions/compose-dashboard.ts +12 -1
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +15 -4
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +2 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +14 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -27
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +21 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +14 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +31 -12
- package/corpus/templates/analytics/changelog/2026-07-13-dashboard-charts-now-honor-the-selected-time-range-by-defaul.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-keep-real-authorized-identities-in-analytics-incident-lookup.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-the-analytics-agents-tab-is-available-again-from-the-main-na.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +4 -0
- package/corpus/templates/analytics/server/lib/dashboard-time-scope.ts +170 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +6 -62
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +20 -5
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +1 -1
- package/corpus/templates/analytics/server/lib/session-replay.ts +5 -111
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/assets/app/routes/library.tsx +3 -4
- package/corpus/templates/assets/changelog/2026-07-13-internal-library-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +43 -40
- package/corpus/templates/calendar/app/pages/ManageBookingPage.tsx +6 -8
- package/corpus/templates/calendar/changelog/2026-07-13-booking-links-and-previews-open-in-new-tabs.md +6 -0
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +4 -7
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +19 -9
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +5 -3
- package/corpus/templates/clips/changelog/2026-07-13-recording-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +6 -8
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -3
- package/corpus/templates/content/app/components/editor/database/FormView.tsx +6 -9
- package/corpus/templates/content/changelog/2026-07-13-document-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +33 -16
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +18 -1
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +4 -4
- package/corpus/templates/design/app/pages/DesignSystems.tsx +8 -10
- package/corpus/templates/design/app/pages/Index.tsx +6 -1
- package/corpus/templates/design/app/pages/Present.tsx +12 -6
- package/corpus/templates/design/changelog/2026-07-13-design-navigation-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-13-design-session-replays-include-preview-iframes.md +6 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -11
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +6 -8
- package/corpus/templates/forms/changelog/2026-07-13-form-navigation-links-open-in-new-tabs.md +6 -0
- package/corpus/templates/mail/app/pages/DraftQueuePage.tsx +3 -8
- package/corpus/templates/mail/changelog/2026-07-13-settings-links-open-in-new-tabs.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -37
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/audit/config.d.ts.map +1 -1
- package/dist/audit/config.js +0 -1
- package/dist/audit/config.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +47 -33
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +9 -3
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +10 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +2 -6
- 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 +2 -59
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +1 -13
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +5 -52
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +3 -7
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-demo-mode-status.d.ts +3 -4
- package/dist/client/use-demo-mode-status.d.ts.map +1 -1
- package/dist/client/use-demo-mode-status.js +8 -23
- package/dist/client/use-demo-mode-status.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 +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/demo/browser-state.d.ts +10 -0
- package/dist/demo/browser-state.d.ts.map +1 -0
- package/dist/demo/browser-state.js +49 -0
- package/dist/demo/browser-state.js.map +1 -0
- package/dist/demo/fetch-interceptor.d.ts +2 -7
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +12 -56
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +2 -2
- package/dist/deploy/workspace-deploy.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 +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +0 -1
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +0 -27
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/docs/content/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ar-SA/a2a-protocol.mdx +42 -0
- package/docs/content/locales/de-DE/a2a-protocol.mdx +42 -0
- package/docs/content/locales/es-ES/a2a-protocol.mdx +42 -0
- package/docs/content/locales/fr-FR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/hi-IN/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ja-JP/a2a-protocol.mdx +42 -0
- package/docs/content/locales/ko-KR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/pt-BR/a2a-protocol.mdx +42 -0
- package/docs/content/locales/zh-CN/a2a-protocol.mdx +42 -0
- package/docs/content/locales/zh-TW/a2a-protocol.mdx +42 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/native-navigation/SKILL.md +67 -0
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +0 -23
- package/corpus/core/src/demo/config.ts +0 -57
- package/dist/demo/actions/toggle-demo-mode.d.ts +0 -8
- package/dist/demo/actions/toggle-demo-mode.d.ts.map +0 -1
- package/dist/demo/actions/toggle-demo-mode.js +0 -21
- package/dist/demo/actions/toggle-demo-mode.js.map +0 -1
- package/dist/demo/config.d.ts +0 -9
- package/dist/demo/config.d.ts.map +0 -1
- package/dist/demo/config.js +0 -58
- package/dist/demo/config.js.map +0 -1
|
@@ -11,8 +11,6 @@ import type { EventHandler as H3EventHandler } from "h3";
|
|
|
11
11
|
import { isAgentActionStopError } from "../action.js";
|
|
12
12
|
import { readAppState } from "../application-state/script-helpers.js";
|
|
13
13
|
import { isReadOnlyShellCommand } from "../coding-tools/index.js";
|
|
14
|
-
import { isDemoModeEnabled } from "../demo/config.js";
|
|
15
|
-
import { redactDemoData, redactDemoString } from "../demo/redact.js";
|
|
16
14
|
import { extensionIdFromPathname } from "../extensions/path.js";
|
|
17
15
|
import { preUploadAttachments } from "../file-upload/pre-upload-attachments.js";
|
|
18
16
|
import { isMcpActionResult } from "../mcp-client/app-result.js";
|
|
@@ -4456,48 +4454,24 @@ export async function runAgentLoop(opts: {
|
|
|
4456
4454
|
isError = true;
|
|
4457
4455
|
}
|
|
4458
4456
|
mcpApp = mcpResult?.mcpApp;
|
|
4459
|
-
// Demo mode
|
|
4460
|
-
//
|
|
4461
|
-
|
|
4462
|
-
// intact and follow-up tool calls still work. Gated — the expensive
|
|
4463
|
-
// walk only runs when demo mode is on.
|
|
4464
|
-
let redacted: unknown = rawForAgent;
|
|
4465
|
-
const demoMode = await isDemoModeEnabled();
|
|
4466
|
-
if (demoMode) {
|
|
4467
|
-
mcpApp = undefined;
|
|
4468
|
-
if (typeof rawForAgent === "string") {
|
|
4469
|
-
try {
|
|
4470
|
-
redacted = JSON.stringify(
|
|
4471
|
-
redactDemoData(JSON.parse(rawForAgent)),
|
|
4472
|
-
null,
|
|
4473
|
-
2,
|
|
4474
|
-
);
|
|
4475
|
-
} catch {
|
|
4476
|
-
redacted = redactDemoString(rawForAgent);
|
|
4477
|
-
}
|
|
4478
|
-
} else {
|
|
4479
|
-
redacted = redactDemoData(rawForAgent);
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4457
|
+
// Demo mode is browser-local presentation state. The agent and MCP
|
|
4458
|
+
// layers always receive the real, access-scoped tool result.
|
|
4459
|
+
let resultForAgent: unknown = rawForAgent;
|
|
4482
4460
|
// Vision images for the model: MCP tools return standard `image`
|
|
4483
4461
|
// content parts; first-party actions opt in via the well-known
|
|
4484
4462
|
// `_agentImages` result field (stripped from the JSON the model
|
|
4485
|
-
// reads
|
|
4486
|
-
// text redaction can't scrub pixels, and a screenshot may expose
|
|
4487
|
-
// original visual data. The images array
|
|
4463
|
+
// reads). The images array
|
|
4488
4464
|
// never touches the ledger — only the compact text notes appended
|
|
4489
4465
|
// below are persisted.
|
|
4490
4466
|
let imageNotes: string[] = [];
|
|
4491
4467
|
if (mcpResult) {
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
if (mcpImages.length > 0) toolResultImages = mcpImages;
|
|
4495
|
-
}
|
|
4468
|
+
const mcpImages = extractMcpToolResultImages(mcpResult.raw);
|
|
4469
|
+
if (mcpImages.length > 0) toolResultImages = mcpImages;
|
|
4496
4470
|
} else {
|
|
4497
|
-
const extracted = extractAgentImagesFromActionResult(
|
|
4498
|
-
|
|
4471
|
+
const extracted = extractAgentImagesFromActionResult(resultForAgent);
|
|
4472
|
+
resultForAgent = extracted.value;
|
|
4499
4473
|
imageNotes = extracted.notes;
|
|
4500
|
-
if (extracted.images.length > 0
|
|
4474
|
+
if (extracted.images.length > 0) {
|
|
4501
4475
|
toolResultImages = extracted.images;
|
|
4502
4476
|
}
|
|
4503
4477
|
}
|
|
@@ -4508,9 +4482,9 @@ export async function runAgentLoop(opts: {
|
|
|
4508
4482
|
];
|
|
4509
4483
|
}
|
|
4510
4484
|
let resultStr =
|
|
4511
|
-
typeof
|
|
4512
|
-
?
|
|
4513
|
-
: JSON.stringify(
|
|
4485
|
+
typeof resultForAgent === "string"
|
|
4486
|
+
? resultForAgent
|
|
4487
|
+
: JSON.stringify(resultForAgent, null, 2);
|
|
4514
4488
|
if (resultStr.length > toolMaxResultChars) {
|
|
4515
4489
|
const truncated = resultStr.slice(0, toolMaxResultChars);
|
|
4516
4490
|
resultStr = `${truncated}\n\n...[truncated — full result was ${resultStr.length.toLocaleString()} chars; only first ${toolMaxResultChars.toLocaleString()} shown]`;
|
|
@@ -886,57 +886,71 @@ export async function updateThreadData(
|
|
|
886
886
|
messageCount: number,
|
|
887
887
|
options: UpdateThreadDataOptions = {},
|
|
888
888
|
): Promise<void> {
|
|
889
|
-
|
|
889
|
+
// getThread() ensures the table exists. Keep that bootstrap inside the
|
|
890
|
+
// retry boundary below so a cold serverless process can recover from a
|
|
891
|
+
// transient initialization/read failure too.
|
|
890
892
|
const client = getDbExec();
|
|
891
|
-
const maxAttempts =
|
|
892
|
-
|
|
893
|
+
const maxAttempts = Math.max(
|
|
894
|
+
1,
|
|
895
|
+
options.maxAttempts ?? DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS,
|
|
896
|
+
);
|
|
893
897
|
let lastConflict = false;
|
|
898
|
+
let lastError: unknown = null;
|
|
894
899
|
|
|
895
900
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
896
|
-
const current = await getThread(id);
|
|
897
|
-
if (!current) return;
|
|
898
|
-
|
|
899
|
-
let nextThreadData = threadData;
|
|
900
|
-
let nextMessageCount = messageCount;
|
|
901
901
|
try {
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
902
|
+
const current = await getThread(id);
|
|
903
|
+
if (!current) return;
|
|
904
|
+
|
|
905
|
+
let nextThreadData = threadData;
|
|
906
|
+
let nextMessageCount = messageCount;
|
|
907
|
+
try {
|
|
908
|
+
const merged = mergeThreadDataForClientSave(
|
|
909
|
+
parseThreadData(current.threadData),
|
|
910
|
+
parseThreadData(threadData),
|
|
911
|
+
{
|
|
912
|
+
preserveExistingQueuedMessages:
|
|
913
|
+
options.preserveExistingQueuedMessages ?? true,
|
|
914
|
+
preserveExistingTopLevelKeys:
|
|
915
|
+
options.preserveExistingTopLevelKeys ?? true,
|
|
916
|
+
},
|
|
917
|
+
);
|
|
918
|
+
nextThreadData = JSON.stringify(merged);
|
|
919
|
+
if (Array.isArray(merged.messages)) {
|
|
920
|
+
nextMessageCount = merged.messages.length;
|
|
921
|
+
}
|
|
922
|
+
} catch {
|
|
923
|
+
// Keep the caller's serialized value if either JSON blob is malformed.
|
|
915
924
|
}
|
|
916
|
-
} catch {
|
|
917
|
-
// Keep the caller's serialized value if either JSON blob is malformed.
|
|
918
|
-
}
|
|
919
925
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
926
|
+
const nextUpdatedAt = Math.max(Date.now(), current.updatedAt + 1);
|
|
927
|
+
const result = await client.execute({
|
|
928
|
+
sql: `UPDATE chat_threads SET thread_data = ?, title = ?, preview = ?, message_count = ?, updated_at = ? WHERE id = ? AND updated_at = ?`,
|
|
929
|
+
args: [
|
|
930
|
+
nextThreadData,
|
|
931
|
+
title,
|
|
932
|
+
preview,
|
|
933
|
+
nextMessageCount,
|
|
934
|
+
nextUpdatedAt,
|
|
935
|
+
id,
|
|
936
|
+
current.updatedAt,
|
|
937
|
+
],
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
if (result.rowsAffected > 0) {
|
|
941
|
+
emitChatThreadChange(id);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
933
944
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
945
|
+
lastConflict = true;
|
|
946
|
+
} catch (error) {
|
|
947
|
+
// Completion saves happen after a long model/tool turn, when a
|
|
948
|
+
// transient connection or serverless DB failure is especially costly.
|
|
949
|
+
// Retry the whole read/merge/write attempt like a CAS conflict, while
|
|
950
|
+
// preserving the final error if the database remains unavailable.
|
|
951
|
+
lastError = error;
|
|
937
952
|
}
|
|
938
953
|
|
|
939
|
-
lastConflict = true;
|
|
940
954
|
if (attempt < maxAttempts - 1) {
|
|
941
955
|
await new Promise((resolve) =>
|
|
942
956
|
setTimeout(
|
|
@@ -947,6 +961,8 @@ export async function updateThreadData(
|
|
|
947
961
|
}
|
|
948
962
|
}
|
|
949
963
|
|
|
964
|
+
if (lastError) throw lastError;
|
|
965
|
+
|
|
950
966
|
if (lastConflict) {
|
|
951
967
|
if (options.ignoreConflicts) return;
|
|
952
968
|
throw new Error(
|
|
@@ -2307,8 +2307,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2307
2307
|
const missingApiKey = agentEngineConfigured.missing;
|
|
2308
2308
|
const isProviderStatusChecking =
|
|
2309
2309
|
providerStatusChecksEnabled && agentEngineConfigured.state === "unknown";
|
|
2310
|
-
const isComposerDisabled =
|
|
2311
|
-
missingApiKey || isProviderStatusChecking || composerDisabled;
|
|
2310
|
+
const isComposerDisabled = missingApiKey || composerDisabled;
|
|
2312
2311
|
const [missingKeySetupOpen, setMissingKeySetupOpen] = useState(false);
|
|
2313
2312
|
const requestMissingKeySetup = useCallback(() => {
|
|
2314
2313
|
setMissingKeySetupOpen(true);
|
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
BranchPickerPrimitive,
|
|
12
12
|
ComposerPrimitive,
|
|
13
13
|
useMessagePartReasoning,
|
|
14
|
+
useMessagePartRuntime,
|
|
14
15
|
} from "@assistant-ui/react";
|
|
15
16
|
import type { Attachment } from "@assistant-ui/react";
|
|
17
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
16
18
|
import {
|
|
17
19
|
IconX,
|
|
18
20
|
IconCheck,
|
|
@@ -842,8 +844,18 @@ export function shouldShowAssistantMessageFooter({
|
|
|
842
844
|
|
|
843
845
|
function ReasoningMessagePart() {
|
|
844
846
|
const part = useMessagePartReasoning();
|
|
845
|
-
const
|
|
847
|
+
const partRuntime = useMessagePartRuntime();
|
|
848
|
+
const messageParts = useAuiState((state) => state.message.parts);
|
|
846
849
|
const isStreaming = part.status?.type === "running";
|
|
850
|
+
const partIndex =
|
|
851
|
+
partRuntime.path.messagePartSelector.type === "index"
|
|
852
|
+
? partRuntime.path.messagePartSelector.index
|
|
853
|
+
: -1;
|
|
854
|
+
const latestReasoningPartIndex = messageParts.reduce(
|
|
855
|
+
(latestIndex, messagePart, index) =>
|
|
856
|
+
messagePart.type === "reasoning" ? index : latestIndex,
|
|
857
|
+
-1,
|
|
858
|
+
);
|
|
847
859
|
// Time thinking client-side: record the moment streaming first starts and
|
|
848
860
|
// the moment it stops so the cell can show "Thought for Xs". Historical
|
|
849
861
|
// messages that were never observed streaming in this session never get a
|
|
@@ -865,8 +877,8 @@ function ReasoningMessagePart() {
|
|
|
865
877
|
text={part.text}
|
|
866
878
|
isStreaming={isStreaming}
|
|
867
879
|
durationMs={durationMs}
|
|
868
|
-
defaultOpen={
|
|
869
|
-
|
|
880
|
+
defaultOpen={partIndex === latestReasoningPartIndex}
|
|
881
|
+
collapseWhenReplaced={partIndex < latestReasoningPartIndex}
|
|
870
882
|
/>
|
|
871
883
|
);
|
|
872
884
|
}
|
|
@@ -897,6 +897,11 @@ export function ReconnectStreamMessage({
|
|
|
897
897
|
content: ContentPart[];
|
|
898
898
|
}) {
|
|
899
899
|
const chatRunning = React.useContext(ChatRunningContext);
|
|
900
|
+
const latestReasoningPartIndex = content.reduce(
|
|
901
|
+
(latestIndex, part, index) =>
|
|
902
|
+
part.type === "reasoning" ? index : latestIndex,
|
|
903
|
+
-1,
|
|
904
|
+
);
|
|
900
905
|
const streamingTextPartIndex =
|
|
901
906
|
content.at(-1)?.type === "text" ? content.length - 1 : -1;
|
|
902
907
|
const streamingReasoningPartIndex =
|
|
@@ -924,7 +929,8 @@ export function ReconnectStreamMessage({
|
|
|
924
929
|
key={`reconnect-reasoning-${i}`}
|
|
925
930
|
text={part.text}
|
|
926
931
|
isStreaming={chatRunning && i === streamingReasoningPartIndex}
|
|
927
|
-
defaultOpen={
|
|
932
|
+
defaultOpen={i === latestReasoningPartIndex}
|
|
933
|
+
collapseWhenReplaced={i < latestReasoningPartIndex}
|
|
928
934
|
/>
|
|
929
935
|
);
|
|
930
936
|
}
|
|
@@ -969,6 +975,7 @@ export function ReasoningCell({
|
|
|
969
975
|
isStreaming = false,
|
|
970
976
|
defaultOpen,
|
|
971
977
|
autoCollapse = false,
|
|
978
|
+
collapseWhenReplaced = false,
|
|
972
979
|
durationMs,
|
|
973
980
|
}: {
|
|
974
981
|
text: string;
|
|
@@ -976,6 +983,8 @@ export function ReasoningCell({
|
|
|
976
983
|
defaultOpen?: boolean;
|
|
977
984
|
/** Animate closed when a live reasoning segment finishes during a run. */
|
|
978
985
|
autoCollapse?: boolean;
|
|
986
|
+
/** Animate closed when a newer reasoning segment replaces this one. */
|
|
987
|
+
collapseWhenReplaced?: boolean;
|
|
979
988
|
/**
|
|
980
989
|
* Elapsed thinking time in ms, once known. Only meaningful once streaming
|
|
981
990
|
* has finished — callers that track live timing (see ReasoningMessagePart)
|
|
@@ -987,6 +996,7 @@ export function ReasoningCell({
|
|
|
987
996
|
const embeddedInWorkSummary = React.useContext(WorkSummaryContentContext);
|
|
988
997
|
const [open, setOpen] = useState(defaultOpen ?? true);
|
|
989
998
|
const wasStreamingRef = useRef(isStreaming);
|
|
999
|
+
const wasReplacedRef = useRef(collapseWhenReplaced);
|
|
990
1000
|
const trimmed = text.trim();
|
|
991
1001
|
|
|
992
1002
|
useEffect(() => {
|
|
@@ -996,6 +1006,13 @@ export function ReasoningCell({
|
|
|
996
1006
|
wasStreamingRef.current = isStreaming;
|
|
997
1007
|
}, [autoCollapse, isStreaming]);
|
|
998
1008
|
|
|
1009
|
+
useEffect(() => {
|
|
1010
|
+
if (collapseWhenReplaced && !wasReplacedRef.current) {
|
|
1011
|
+
setOpen(false);
|
|
1012
|
+
}
|
|
1013
|
+
wasReplacedRef.current = collapseWhenReplaced;
|
|
1014
|
+
}, [collapseWhenReplaced]);
|
|
1015
|
+
|
|
999
1016
|
if (!trimmed && !isStreaming) return null;
|
|
1000
1017
|
|
|
1001
1018
|
if (embeddedInWorkSummary) {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "@tabler/icons-react";
|
|
7
7
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
8
8
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
-
import {
|
|
9
|
+
import { Link } from "react-router";
|
|
10
10
|
|
|
11
11
|
import { extensionPath } from "../../extensions/path.js";
|
|
12
12
|
import { THEME_VAR_NAMES } from "../../extensions/theme.js";
|
|
@@ -482,7 +482,6 @@ function EmbeddedToolMenu({
|
|
|
482
482
|
const [open, setOpen] = useState(false);
|
|
483
483
|
const [confirmingDelete, setConfirmingDelete] = useState(false);
|
|
484
484
|
const queryClient = useQueryClient();
|
|
485
|
-
const navigate = useNavigate();
|
|
486
485
|
|
|
487
486
|
const closeMenu = () => {
|
|
488
487
|
setOpen(false);
|
|
@@ -545,17 +544,14 @@ function EmbeddedToolMenu({
|
|
|
545
544
|
<PopoverContent align="end" sideOffset={4} className="w-56 p-1">
|
|
546
545
|
{!confirmingDelete ? (
|
|
547
546
|
<div className="flex flex-col">
|
|
548
|
-
<
|
|
549
|
-
|
|
550
|
-
onClick={
|
|
551
|
-
closeMenu();
|
|
552
|
-
navigate(extensionPath(extensionId, toolName));
|
|
553
|
-
}}
|
|
547
|
+
<Link
|
|
548
|
+
to={extensionPath(extensionId, toolName)}
|
|
549
|
+
onClick={closeMenu}
|
|
554
550
|
className="flex items-center gap-2 rounded-sm px-2 py-1.5 text-[12px] hover:bg-accent cursor-pointer text-left"
|
|
555
551
|
>
|
|
556
552
|
<IconExternalLink className="h-3.5 w-3.5" />
|
|
557
553
|
<span>{t("extensions.openFullView")}</span>
|
|
558
|
-
</
|
|
554
|
+
</Link>
|
|
559
555
|
<button
|
|
560
556
|
type="button"
|
|
561
557
|
onClick={removeFromSlot}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IconArrowBackUp,
|
|
3
3
|
IconChevronRight,
|
|
4
|
-
IconCode,
|
|
5
4
|
IconDotsVertical,
|
|
6
5
|
IconHistory,
|
|
7
6
|
IconLoader2,
|
|
@@ -21,7 +20,6 @@ import { SESSION_REPLAY_IFRAME_ATTRIBUTE } from "../../session-replay-iframe-pro
|
|
|
21
20
|
import { sendToAgentChat } from "../agent-chat.js";
|
|
22
21
|
import { AgentToggleButton } from "../AgentPanel.js";
|
|
23
22
|
import { agentNativePath, appPath } from "../api-path.js";
|
|
24
|
-
import { Dialog, DialogContent, DialogTitle } from "../components/ui/dialog.js";
|
|
25
23
|
import {
|
|
26
24
|
Popover,
|
|
27
25
|
PopoverContent,
|
|
@@ -366,131 +364,6 @@ function applyCanonicalLink(path: string): () => void {
|
|
|
366
364
|
};
|
|
367
365
|
}
|
|
368
366
|
|
|
369
|
-
function SourceCodeDialog({
|
|
370
|
-
extension,
|
|
371
|
-
onSaved,
|
|
372
|
-
}: {
|
|
373
|
-
extension: Extension;
|
|
374
|
-
onSaved?: () => void;
|
|
375
|
-
}) {
|
|
376
|
-
const queryClient = useQueryClient();
|
|
377
|
-
const [open, setOpen] = useState(false);
|
|
378
|
-
const [code, setCode] = useState(extension.content ?? "");
|
|
379
|
-
const [saving, setSaving] = useState(false);
|
|
380
|
-
const [error, setError] = useState<string | null>(null);
|
|
381
|
-
|
|
382
|
-
// Sync in when the dialog opens, or when the viewed extension changes
|
|
383
|
-
// while the dialog stays mounted (e.g. re-parented to a different id).
|
|
384
|
-
useEffect(() => {
|
|
385
|
-
if (open) setCode(extension.content ?? "");
|
|
386
|
-
}, [open, extension.id]);
|
|
387
|
-
|
|
388
|
-
const isDirty = code !== (extension.content ?? "");
|
|
389
|
-
|
|
390
|
-
// Block Escape / outside-click from closing while there are unsaved edits.
|
|
391
|
-
const handleOpenChange = (next: boolean) => {
|
|
392
|
-
if (!next && isDirty) return;
|
|
393
|
-
setOpen(next);
|
|
394
|
-
if (!next) setError(null);
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
const handleCancel = () => {
|
|
398
|
-
setCode(extension.content ?? "");
|
|
399
|
-
setOpen(false);
|
|
400
|
-
setError(null);
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const handleSave = async () => {
|
|
404
|
-
setSaving(true);
|
|
405
|
-
setError(null);
|
|
406
|
-
try {
|
|
407
|
-
const res = await fetch(
|
|
408
|
-
agentNativePath(`/_agent-native/extensions/${extension.id}`),
|
|
409
|
-
{
|
|
410
|
-
method: "PUT",
|
|
411
|
-
headers: { "Content-Type": "application/json" },
|
|
412
|
-
body: JSON.stringify({ content: code }),
|
|
413
|
-
},
|
|
414
|
-
);
|
|
415
|
-
if (!res.ok) {
|
|
416
|
-
const body = await res.json().catch(() => ({}));
|
|
417
|
-
throw new Error(body?.error ?? `Save failed (${res.status})`);
|
|
418
|
-
}
|
|
419
|
-
setOpen(false);
|
|
420
|
-
queryClient.setQueryData<Extension>(["extension", extension.id], (old) =>
|
|
421
|
-
old ? { ...old, content: code } : old,
|
|
422
|
-
);
|
|
423
|
-
queryClient.invalidateQueries({
|
|
424
|
-
queryKey: ["extension", extension.id],
|
|
425
|
-
});
|
|
426
|
-
queryClient.invalidateQueries({ queryKey: ["extensions"] });
|
|
427
|
-
onSaved?.();
|
|
428
|
-
} catch (err: any) {
|
|
429
|
-
setError(err?.message ?? "Save failed");
|
|
430
|
-
} finally {
|
|
431
|
-
setSaving(false);
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
return (
|
|
436
|
-
<>
|
|
437
|
-
<Tooltip>
|
|
438
|
-
<TooltipTrigger asChild>
|
|
439
|
-
<button
|
|
440
|
-
type="button"
|
|
441
|
-
onClick={() => setOpen(true)}
|
|
442
|
-
className="inline-flex items-center justify-center rounded-md h-8 w-8 text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer"
|
|
443
|
-
>
|
|
444
|
-
<IconCode className="h-4 w-4" />
|
|
445
|
-
</button>
|
|
446
|
-
</TooltipTrigger>
|
|
447
|
-
<TooltipContent>View / edit source</TooltipContent>
|
|
448
|
-
</Tooltip>
|
|
449
|
-
<Dialog open={open} onOpenChange={handleOpenChange}>
|
|
450
|
-
<DialogContent className="flex h-[85vh] w-[90vw] max-w-[900px] flex-col gap-0 overflow-hidden p-0">
|
|
451
|
-
<div className="flex shrink-0 items-center border-b border-border px-5 py-3 pr-12">
|
|
452
|
-
<DialogTitle className="truncate text-sm font-medium">
|
|
453
|
-
{extension.name} — source
|
|
454
|
-
</DialogTitle>
|
|
455
|
-
</div>
|
|
456
|
-
<textarea
|
|
457
|
-
className="flex-1 resize-none bg-muted/40 px-5 py-4 font-mono text-xs leading-relaxed text-foreground focus:outline-none"
|
|
458
|
-
value={code}
|
|
459
|
-
onChange={(e) => setCode(e.target.value)}
|
|
460
|
-
spellCheck={false}
|
|
461
|
-
/>
|
|
462
|
-
<div className="flex shrink-0 items-center justify-between border-t border-border px-5 py-3">
|
|
463
|
-
{error ? (
|
|
464
|
-
<p className="text-xs text-destructive">{error}</p>
|
|
465
|
-
) : (
|
|
466
|
-
<span className="text-xs text-muted-foreground">
|
|
467
|
-
Alpine.js / HTML · {code.length.toLocaleString()} chars
|
|
468
|
-
</span>
|
|
469
|
-
)}
|
|
470
|
-
<div className="flex items-center gap-2">
|
|
471
|
-
<button
|
|
472
|
-
type="button"
|
|
473
|
-
onClick={handleCancel}
|
|
474
|
-
className="inline-flex h-8 cursor-pointer items-center rounded-md border border-input px-3 text-xs hover:bg-accent"
|
|
475
|
-
>
|
|
476
|
-
Cancel
|
|
477
|
-
</button>
|
|
478
|
-
<button
|
|
479
|
-
type="button"
|
|
480
|
-
onClick={handleSave}
|
|
481
|
-
disabled={saving}
|
|
482
|
-
className="inline-flex h-8 cursor-pointer items-center rounded-md bg-primary px-4 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50"
|
|
483
|
-
>
|
|
484
|
-
{saving ? "Saving…" : "Save"}
|
|
485
|
-
</button>
|
|
486
|
-
</div>
|
|
487
|
-
</div>
|
|
488
|
-
</DialogContent>
|
|
489
|
-
</Dialog>
|
|
490
|
-
</>
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
367
|
function EditToolPopover({
|
|
495
368
|
extension,
|
|
496
369
|
onOpenChange,
|
|
@@ -1342,12 +1215,6 @@ export function ExtensionViewer({ extensionId }: ExtensionViewerProps) {
|
|
|
1342
1215
|
onRestored={() => setRefreshKey((k) => k + 1)}
|
|
1343
1216
|
onOpenChange={onPopoverOpenChange}
|
|
1344
1217
|
/>
|
|
1345
|
-
{extension.canEdit && (
|
|
1346
|
-
<SourceCodeDialog
|
|
1347
|
-
extension={extension}
|
|
1348
|
-
onSaved={() => setRefreshKey((k) => k + 1)}
|
|
1349
|
-
/>
|
|
1350
|
-
)}
|
|
1351
1218
|
<EditToolPopover
|
|
1352
1219
|
extension={extension}
|
|
1353
1220
|
onOpenChange={onPopoverOpenChange}
|
|
@@ -388,6 +388,13 @@ export {
|
|
|
388
388
|
type BuildAgentNativeExtensionHtmlOptions,
|
|
389
389
|
type CreateHttpAgentNativeExtensionStorageOptions,
|
|
390
390
|
} from "./extensions/portable-extension.js";
|
|
391
|
+
export { injectSessionReplayIframeBootstrap } from "../extensions/session-replay-iframe.js";
|
|
392
|
+
export {
|
|
393
|
+
SESSION_REPLAY_IFRAME_ATTRIBUTE,
|
|
394
|
+
SESSION_REPLAY_IFRAME_PROBE,
|
|
395
|
+
SESSION_REPLAY_IFRAME_START,
|
|
396
|
+
SESSION_REPLAY_IFRAME_STOP,
|
|
397
|
+
} from "../session-replay-iframe-protocol.js";
|
|
391
398
|
export {
|
|
392
399
|
AGENT_NATIVE_HOST_BRIDGE_VERSION,
|
|
393
400
|
AGENT_NATIVE_HOST_MESSAGE_TYPES,
|
|
@@ -1,61 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <DemoModeSection /> — toggle that replaces emails and numbers with realistic
|
|
3
|
-
* fake data everywhere (UI + what the agent sees) while preserving names,
|
|
4
|
-
* free text, labels, IDs, and structure so the app keeps working.
|
|
5
|
-
*
|
|
6
|
-
* The toggle is stored in application_state under `demo-mode`
|
|
7
|
-
* (`{ enabled: boolean }`) and written via `PUT
|
|
8
|
-
* /_agent-native/application-state/demo-mode` (optimistic — flip first,
|
|
9
|
-
* write in the background). It READS from `/_agent-native/demo/status`
|
|
10
|
-
* instead, which ORs the per-user toggle with the `DEMO_MODE` env: a hosted
|
|
11
|
-
* demo deployment is forced on and the switch is locked so it can't be
|
|
12
|
-
* accidentally turned off on stage.
|
|
13
|
-
*/
|
|
1
|
+
/** Browser-local presentation toggle. Backend and agent results stay real. */
|
|
14
2
|
|
|
15
3
|
import { IconEyeOff } from "@tabler/icons-react";
|
|
16
|
-
import { useEffect, useState } from "react";
|
|
17
4
|
|
|
18
|
-
import {
|
|
5
|
+
import { setBrowserDemoModeEnabled } from "../../demo/browser-state.js";
|
|
19
6
|
import { useDemoModeStatus } from "../use-demo-mode-status.js";
|
|
20
7
|
|
|
21
|
-
const DEMO_MODE_WRITE_URL = agentNativePath(
|
|
22
|
-
"/_agent-native/application-state/demo-mode",
|
|
23
|
-
);
|
|
24
|
-
|
|
25
8
|
export function DemoModeSection() {
|
|
26
|
-
const
|
|
27
|
-
const { enabled: serverEnabled, forced, isLoading } = useDemoModeStatus();
|
|
28
|
-
|
|
29
|
-
// Surface the server value once it arrives (and on subsequent polls), but
|
|
30
|
-
// never clobber an in-flight optimistic toggle with a stale read. When
|
|
31
|
-
// forced by env, always reflect the locked-on state.
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (forced) {
|
|
34
|
-
setEnabled(true);
|
|
35
|
-
} else if (!isLoading) {
|
|
36
|
-
setEnabled((prev) => (prev === null ? serverEnabled : prev));
|
|
37
|
-
}
|
|
38
|
-
}, [serverEnabled, forced, isLoading]);
|
|
39
|
-
|
|
40
|
-
const toggle = async (next: boolean) => {
|
|
41
|
-
if (forced) return;
|
|
42
|
-
const previous = enabled;
|
|
43
|
-
// Optimistic: flip immediately, write in the background.
|
|
44
|
-
setEnabled(next);
|
|
45
|
-
try {
|
|
46
|
-
const res = await fetch(DEMO_MODE_WRITE_URL, {
|
|
47
|
-
method: "PUT",
|
|
48
|
-
headers: { "Content-Type": "application/json" },
|
|
49
|
-
credentials: "include",
|
|
50
|
-
body: JSON.stringify({ enabled: next }),
|
|
51
|
-
});
|
|
52
|
-
if (!res.ok) {
|
|
53
|
-
throw new Error(`HTTP ${res.status}`);
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
setEnabled(previous);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
9
|
+
const { enabled } = useDemoModeStatus();
|
|
59
10
|
|
|
60
11
|
return (
|
|
61
12
|
<div className="flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2">
|
|
@@ -64,30 +15,23 @@ export function DemoModeSection() {
|
|
|
64
15
|
Enable demo mode
|
|
65
16
|
</div>
|
|
66
17
|
<p className="text-[10px] text-muted-foreground mt-0.5">
|
|
67
|
-
Anonymize emails in
|
|
68
|
-
presentations.
|
|
69
|
-
|
|
18
|
+
Anonymize displayed emails in this browser and reshape supported
|
|
19
|
+
dashboard charts for presentations. Backend, MCP, and agent results
|
|
20
|
+
stay real and access-scoped.
|
|
70
21
|
</p>
|
|
71
|
-
{forced && (
|
|
72
|
-
<p className="text-[10px] text-muted-foreground mt-0.5">
|
|
73
|
-
Enforced by the <code>DEMO_MODE</code> environment variable — can't
|
|
74
|
-
be turned off here.
|
|
75
|
-
</p>
|
|
76
|
-
)}
|
|
77
22
|
</div>
|
|
78
23
|
<button
|
|
79
24
|
type="button"
|
|
80
25
|
role="switch"
|
|
81
|
-
aria-checked={
|
|
26
|
+
aria-checked={enabled}
|
|
82
27
|
aria-label="Enable demo mode"
|
|
83
|
-
|
|
84
|
-
onClick={() => toggle(!enabled)}
|
|
28
|
+
onClick={() => setBrowserDemoModeEnabled(!enabled)}
|
|
85
29
|
// Theme tokens; streaming agent owns layout.
|
|
86
30
|
className={`relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${
|
|
87
31
|
enabled
|
|
88
32
|
? "bg-primary"
|
|
89
33
|
: "bg-muted-foreground/30 hover:bg-muted-foreground/50"
|
|
90
|
-
}
|
|
34
|
+
}`}
|
|
91
35
|
>
|
|
92
36
|
<span
|
|
93
37
|
className={`inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${
|
|
@@ -2790,7 +2790,7 @@ function SettingsPanelContent({
|
|
|
2790
2790
|
id={settingsSectionDomId("demo-mode")}
|
|
2791
2791
|
icon={<IconEyeOff size={14} />}
|
|
2792
2792
|
title="Demo mode"
|
|
2793
|
-
subtitle="Replace emails
|
|
2793
|
+
subtitle="Replace displayed emails with realistic fake data in this browser and reshape supported charts for presentations. Backend, MCP, and agent results stay real and access-scoped."
|
|
2794
2794
|
open={openSection === "demo-mode"}
|
|
2795
2795
|
onToggle={() => toggle("demo-mode")}
|
|
2796
2796
|
>
|