@agent-native/core 0.157.6 → 0.157.7
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/templates/analytics/agent-native.config.ts +5 -0
- package/corpus/templates/analytics/netlify.toml +1 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +23 -5
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/assets/netlify.toml +3 -0
- package/corpus/templates/brain/netlify.toml +3 -0
- package/corpus/templates/calendar/agent-native.config.ts +5 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -1
- package/corpus/templates/calendar/netlify.toml +3 -0
- package/corpus/templates/chat/agent-native.config.ts +5 -0
- package/corpus/templates/chat/netlify.toml +3 -0
- package/corpus/templates/clips/actions/lib/meeting-content.ts +28 -0
- package/corpus/templates/clips/actions/list-meetings.ts +33 -11
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/lib/meeting-history-query.ts +11 -0
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +47 -21
- package/corpus/templates/clips/netlify.toml +4 -0
- package/corpus/templates/content/netlify.toml +3 -0
- package/corpus/templates/design/app/i18n-data.ts +2 -0
- package/corpus/templates/design/netlify.toml +3 -0
- package/corpus/templates/dispatch/netlify.toml +3 -0
- package/corpus/templates/factory/netlify.toml +3 -0
- package/corpus/templates/forms/netlify.toml +3 -0
- package/corpus/templates/macros/netlify.toml +3 -0
- package/corpus/templates/mail/agent-native.config.ts +5 -0
- package/corpus/templates/mail/netlify.toml +3 -0
- package/corpus/templates/plan/netlify.toml +3 -0
- package/corpus/templates/slides/netlify.toml +3 -0
- package/dist/agent/harness/acp-adapter.js +17 -2
- package/dist/agent/harness/ai-sdk-adapter.js +14 -3
- package/dist/agent/harness/background.js +10 -5
- package/dist/agent/harness/hosted.d.ts +20 -0
- package/dist/agent/harness/hosted.js +100 -0
- package/dist/agent/harness/runner.js +100 -77
- package/dist/agent/observational-memory/observer.js +12 -0
- package/dist/agent/production-agent.d.ts +3 -0
- package/dist/agent/production-agent.js +74 -4
- package/dist/agent/thread-data-builder.d.ts +12 -0
- package/dist/agent/thread-data-builder.js +23 -0
- package/dist/agent/types.d.ts +6 -0
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +113 -17
- package/dist/client/AgentTaskCard.d.ts +2 -2
- package/dist/client/AgentTaskCard.js +8 -22
- package/dist/client/AssistantChat.d.ts +8 -0
- package/dist/client/AssistantChat.js +106 -59
- package/dist/client/MultiTabAssistantChat.js +50 -6
- package/dist/client/NewWorkspaceAppFlow.js +7 -1
- package/dist/client/agent-chat-adapter.d.ts +6 -0
- package/dist/client/agent-chat-adapter.js +36 -8
- package/dist/client/app-chat-sidebar.d.ts +5 -0
- package/dist/client/app-chat-sidebar.js +53 -9
- package/dist/client/chat/composer-draft.d.ts +4 -0
- package/dist/client/chat/composer-draft.js +63 -0
- package/dist/client/chat-first/apps-rail.js +10 -1
- package/dist/client/chat-first/primary-nav.js +10 -5
- package/dist/client/frame-protocol.d.ts +1 -0
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/i18n.js +5 -2
- package/dist/client/progress/RunsTray.js +129 -16
- package/dist/client/settings/SettingsRow.js +1 -1
- package/dist/client/use-chat-threads.js +11 -6
- package/dist/config.d.ts +11 -0
- package/dist/config.js +52 -0
- package/dist/extensions/actions.js +22 -1
- package/dist/hosted-harness/actions/get-hosted-harness-config.d.ts +2 -0
- package/dist/hosted-harness/actions/get-hosted-harness-config.js +13 -0
- package/dist/hosted-harness/actions/set-hosted-harness-enabled.d.ts +4 -0
- package/dist/hosted-harness/actions/set-hosted-harness-enabled.js +32 -0
- package/dist/localization/default-messages.d.ts +4 -1
- package/dist/localization/default-messages.js +5 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-discovery.js +12 -0
- package/dist/server/agent-chat-plugin.js +24 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams.d.ts +20 -12
- package/dist/server/agent-teams.js +69 -35
- package/dist/server/hosted-harness-policy.d.ts +17 -0
- package/dist/server/hosted-harness-policy.js +38 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +4 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +39 -0
- package/dist/templates/chat/agent-native.config.ts +5 -0
- package/dist/templates/chat/netlify.toml +3 -0
- package/dist/vite/action-types-plugin.js +8 -0
- package/package.json +5 -3
- package/src/templates/chat/agent-native.config.ts +5 -0
- package/src/templates/chat/netlify.toml +3 -0
|
@@ -18,6 +18,7 @@ import { modelCatalogConfirmsMissing } from "./chat-model-groups.js";
|
|
|
18
18
|
import { AGENT_CHAT_VIEW_TRANSITION_PREPARE_EVENT } from "./chat-view-transition.js";
|
|
19
19
|
// ─── chat/ module imports ─────────────────────────────────────────────────────
|
|
20
20
|
import { DownscalingImageAttachmentAdapter, BinaryDocumentAttachmentAdapter, MAX_ESTIMATED_BODY_BYTES, AGGRESSIVE_MAX_IMAGE_DIMENSION, AGGRESSIVE_JPEG_QUALITY, transcodeImageToDataURL, createAgentImageAttachments, serializeQueuedAttachments, estimateAttachmentBodyBytes, } from "./chat/attachment-adapters.js";
|
|
21
|
+
import { readAssistantChatComposerDraft, writeAssistantChatComposerDraft, } from "./chat/composer-draft.js";
|
|
21
22
|
import { TextStreamingContext } from "./chat/markdown-renderer.js";
|
|
22
23
|
import { CheckpointContext, MessageActionsContext, assistantMessageRunId, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, displayableUserMessageText, isHiddenUserMessage, ServerRunActiveContext, UserStoppedRunContext, } from "./chat/message-components.js";
|
|
23
24
|
import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, dedupeRepoMessagesById, dropEmptyAssistantMessages, withLastAssistantRunDuration, } from "./chat/repo-helpers.js";
|
|
@@ -288,12 +289,6 @@ function clearPendingSelection() {
|
|
|
288
289
|
window.dispatchEvent(new CustomEvent("agent-panel:selection-cleared"));
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
|
-
// Thread ids the server has already told us don't exist (a prior mount's
|
|
292
|
-
// /threads/:id probe returned 404). Module-scoped so it survives remounts:
|
|
293
|
-
// re-probing a known-absent thread on every navigation just re-spams DevTools
|
|
294
|
-
// with 404s for a thread that has no server row yet (e.g. a freshly created,
|
|
295
|
-
// not-yet-sent chat). Reset on a full page reload.
|
|
296
|
-
const knownAbsentThreadIds = new Set();
|
|
297
292
|
export async function waitForThreadRunToClear(apiUrl, threadId) {
|
|
298
293
|
if (!threadId)
|
|
299
294
|
return true;
|
|
@@ -1375,7 +1370,7 @@ export function useAutoResumeStatus(tabId, forceStopped) {
|
|
|
1375
1370
|
function approvalResolutionIdentity(approvalKey, toolCallId) {
|
|
1376
1371
|
return `${toolCallId ?? ""}\u0000${approvalKey}`;
|
|
1377
1372
|
}
|
|
1378
|
-
const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, threadFooterSlot, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, contextNamespace, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, onComposerTextChange, composerAreaClassName, composerPlaceholder, missingApiKeySetupLayout = "default", composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, showModelSelector = true, composerDisabled = false, composerDisabledPlaceholder, isNewThread, isThreadStateLoading, onSlashCommand, execMode, onExecModeChange, approvalActions, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, modelListLoading, onModelChange, onEffortChange, availableAgents, selectedAgent, onAgentChange, imageModelMenu, onForkChat, onConnectProvider, onConnectLocalRuntime, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, agentChatSurface = "app", suppressInlineOpenApp = false, }, ref) {
|
|
1373
|
+
const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, threadFooterSlot, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, contextNamespace, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, onComposerTextChange, composerAreaClassName, composerPlaceholder, missingApiKeySetupLayout = "default", composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, showModelSelector = true, composerDisabled = false, composerDisabledPlaceholder, isNewThread, isThreadStateLoading, onSlashCommand, execMode, onExecModeChange, approvalActions, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEngine, selectedEffort, availableModels, modelListLoading, onModelChange, onEffortChange, availableAgents, selectedAgent, hostedHarness, onAgentChange, imageModelMenu, onForkChat, onConnectProvider, onConnectLocalRuntime, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, agentChatSurface = "app", suppressInlineOpenApp = false, }, ref) {
|
|
1379
1374
|
const t = useT();
|
|
1380
1375
|
const thread = useThread();
|
|
1381
1376
|
const threadRuntime = useThreadRuntime();
|
|
@@ -1419,10 +1414,13 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1419
1414
|
// Cleared on the next message send.
|
|
1420
1415
|
const [composerError, setComposerError] = useState(null);
|
|
1421
1416
|
const [composerText, setComposerText] = useState("");
|
|
1417
|
+
const composerDraftScope = threadId || tabId;
|
|
1418
|
+
const initialComposerText = useMemo(() => readAssistantChatComposerDraft(composerDraftScope), [composerDraftScope]);
|
|
1422
1419
|
const handleComposerTextChange = useCallback((text) => {
|
|
1423
1420
|
setComposerText(text);
|
|
1421
|
+
writeAssistantChatComposerDraft(composerDraftScope, text);
|
|
1424
1422
|
onComposerTextChange?.(text);
|
|
1425
|
-
}, [onComposerTextChange]);
|
|
1423
|
+
}, [composerDraftScope, onComposerTextChange]);
|
|
1426
1424
|
const dropDepthRef = useRef(0);
|
|
1427
1425
|
const handleChatDragEnter = useCallback((e) => {
|
|
1428
1426
|
if (!Array.from(e.dataTransfer?.types ?? []).includes("Files"))
|
|
@@ -1598,17 +1596,19 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1598
1596
|
return next;
|
|
1599
1597
|
});
|
|
1600
1598
|
}, []);
|
|
1601
|
-
|
|
1599
|
+
useBrowserLayoutEffect(() => {
|
|
1602
1600
|
if (!isActiveComposer)
|
|
1603
1601
|
return;
|
|
1604
1602
|
let cancelled = false;
|
|
1605
|
-
|
|
1603
|
+
const applyVisibleItems = (state) => {
|
|
1606
1604
|
if (cancelled || !isActiveComposerRef.current)
|
|
1607
1605
|
return;
|
|
1608
1606
|
const visibleItems = filterAgentChatContextItems(state.items, normalizedContextNamespace);
|
|
1609
1607
|
composerContextItemsRef.current = visibleItems;
|
|
1610
1608
|
setComposerContextItems(visibleItems);
|
|
1611
|
-
}
|
|
1609
|
+
};
|
|
1610
|
+
applyVisibleItems(getAgentChatContextState());
|
|
1611
|
+
void refreshAgentChatContext().then(applyVisibleItems);
|
|
1612
1612
|
const unsubscribe = subscribeAgentChatContext(() => {
|
|
1613
1613
|
if (cancelled || !isActiveComposerRef.current)
|
|
1614
1614
|
return;
|
|
@@ -1622,6 +1622,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1622
1622
|
unsubscribe();
|
|
1623
1623
|
};
|
|
1624
1624
|
}, [isActiveComposer, normalizedContextNamespace]);
|
|
1625
|
+
const visibleComposerContextItems = useMemo(() => filterAgentChatContextItems(composerContextItems, normalizedContextNamespace), [composerContextItems, normalizedContextNamespace]);
|
|
1625
1626
|
// Tracks the JSON of the last queue we successfully persisted so the
|
|
1626
1627
|
// debounced save effect can skip no-op writes (e.g. restore-from-server
|
|
1627
1628
|
// on mount, or queue state that hasn't actually changed).
|
|
@@ -1824,11 +1825,21 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1824
1825
|
}, [optimisticRunning]);
|
|
1825
1826
|
// ─── Chat persistence ──────────────────────────────────────────────
|
|
1826
1827
|
const hasRestoredRef = useRef(false);
|
|
1828
|
+
const [threadRestoreError, setThreadRestoreError] = useState(null);
|
|
1829
|
+
const [restoreAttempt, setRestoreAttempt] = useState(0);
|
|
1827
1830
|
const [initialCachedThreadSnapshot] = useState(() => readCachedThreadSnapshot(apiUrl, threadId));
|
|
1828
1831
|
const hasImportedInitialCachedSnapshotRef = useRef(false);
|
|
1829
1832
|
const [isRestoring, setIsRestoring] = useState(!!(threadId || loadHistoryRepository) &&
|
|
1830
1833
|
!isNewThread &&
|
|
1831
1834
|
!initialCachedThreadSnapshot);
|
|
1835
|
+
const retryThreadRestore = useCallback(() => {
|
|
1836
|
+
if (!threadId || isNewThread)
|
|
1837
|
+
return;
|
|
1838
|
+
hasRestoredRef.current = false;
|
|
1839
|
+
setThreadRestoreError(null);
|
|
1840
|
+
setIsRestoring(true);
|
|
1841
|
+
setRestoreAttempt((attempt) => attempt + 1);
|
|
1842
|
+
}, [isNewThread, threadId]);
|
|
1832
1843
|
const onSaveThreadRef = useRef(onSaveThread);
|
|
1833
1844
|
onSaveThreadRef.current = onSaveThread;
|
|
1834
1845
|
const onGenerateTitleRef = useRef(onGenerateTitle);
|
|
@@ -2610,87 +2621,111 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2610
2621
|
return;
|
|
2611
2622
|
hasRestoredRef.current = true;
|
|
2612
2623
|
if (loadHistoryRepository) {
|
|
2624
|
+
let cancelled = false;
|
|
2613
2625
|
(async () => {
|
|
2614
2626
|
try {
|
|
2615
2627
|
const repo = await loadHistoryRepository();
|
|
2628
|
+
if (cancelled)
|
|
2629
|
+
return;
|
|
2616
2630
|
if (repo) {
|
|
2617
2631
|
importThreadData(repo, { markTitleGenerated: true });
|
|
2618
2632
|
}
|
|
2619
2633
|
titleGeneratedRef.current = true;
|
|
2634
|
+
setThreadRestoreError(null);
|
|
2620
2635
|
}
|
|
2621
2636
|
catch {
|
|
2622
|
-
|
|
2637
|
+
if (!cancelled)
|
|
2638
|
+
setThreadRestoreError("unavailable");
|
|
2623
2639
|
}
|
|
2624
2640
|
finally {
|
|
2625
|
-
|
|
2641
|
+
if (!cancelled)
|
|
2642
|
+
setIsRestoring(false);
|
|
2626
2643
|
}
|
|
2627
2644
|
})();
|
|
2645
|
+
return () => {
|
|
2646
|
+
cancelled = true;
|
|
2647
|
+
};
|
|
2628
2648
|
}
|
|
2629
2649
|
else if (threadId && isNewThread) {
|
|
2630
2650
|
// Client-created empty tabs do not have a server row until the first
|
|
2631
2651
|
// message is sent. Avoid probing /threads/:id on mount; that request
|
|
2632
2652
|
// can only 404 and makes normal app startup look broken in DevTools.
|
|
2633
|
-
|
|
2634
|
-
}
|
|
2635
|
-
else if (threadId && knownAbsentThreadIds.has(threadId)) {
|
|
2636
|
-
// A prior mount already learned this thread has no server row (404).
|
|
2637
|
-
// Skip the re-probe so remounts don't re-spam 404s for the same id.
|
|
2653
|
+
setThreadRestoreError(null);
|
|
2638
2654
|
setIsRestoring(false);
|
|
2639
2655
|
}
|
|
2640
2656
|
else if (threadId) {
|
|
2657
|
+
let cancelled = false;
|
|
2641
2658
|
(async () => {
|
|
2659
|
+
let canReconnect = false;
|
|
2642
2660
|
try {
|
|
2643
2661
|
const res = await fetch(`${apiUrl}/threads/${encodeURIComponent(threadId)}`);
|
|
2644
|
-
if (res.ok) {
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2662
|
+
if (!res.ok) {
|
|
2663
|
+
if (!cancelled) {
|
|
2664
|
+
setThreadRestoreError(res.status === 404 ? "not-found" : "unavailable");
|
|
2665
|
+
}
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
const data = await res.json();
|
|
2669
|
+
if (cancelled || !data || typeof data !== "object") {
|
|
2670
|
+
if (!cancelled)
|
|
2671
|
+
setThreadRestoreError("unavailable");
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
if (!data.threadData) {
|
|
2675
|
+
if (!cancelled)
|
|
2676
|
+
setThreadRestoreError("unavailable");
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
if (data.threadData) {
|
|
2680
|
+
const repo = importThreadData(data.threadData, {
|
|
2681
|
+
markTitleGenerated: true,
|
|
2682
|
+
});
|
|
2683
|
+
if (repo) {
|
|
2684
|
+
let shouldCacheServerSnapshot = true;
|
|
2685
|
+
try {
|
|
2686
|
+
shouldCacheServerSnapshot = shouldImportServerThreadData(normalizeThreadRepository(threadRuntime.export()), repo);
|
|
2687
|
+
}
|
|
2688
|
+
catch {
|
|
2689
|
+
shouldCacheServerSnapshot = false;
|
|
2690
|
+
}
|
|
2691
|
+
if (shouldCacheServerSnapshot) {
|
|
2692
|
+
const { title, preview } = extractThreadMeta(repo);
|
|
2693
|
+
writeCachedThreadSnapshot(apiUrl, threadId, {
|
|
2694
|
+
threadData: typeof data.threadData === "string"
|
|
2695
|
+
? data.threadData
|
|
2696
|
+
: JSON.stringify(data.threadData),
|
|
2697
|
+
title: data.title || title,
|
|
2698
|
+
preview,
|
|
2699
|
+
messageCount: Array.isArray(repo.messages)
|
|
2700
|
+
? repo.messages.length
|
|
2701
|
+
: 0,
|
|
2702
|
+
});
|
|
2671
2703
|
}
|
|
2672
2704
|
}
|
|
2673
2705
|
// Also skip title generation if thread already has a title
|
|
2674
2706
|
if (data.title) {
|
|
2675
2707
|
titleGeneratedRef.current = true;
|
|
2676
2708
|
}
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
knownAbsentThreadIds.add(threadId);
|
|
2709
|
+
if (!cancelled) {
|
|
2710
|
+
setThreadRestoreError(null);
|
|
2711
|
+
canReconnect = true;
|
|
2712
|
+
}
|
|
2682
2713
|
}
|
|
2683
2714
|
}
|
|
2684
2715
|
catch {
|
|
2685
|
-
|
|
2716
|
+
if (!cancelled)
|
|
2717
|
+
setThreadRestoreError("unavailable");
|
|
2686
2718
|
}
|
|
2687
2719
|
finally {
|
|
2688
2720
|
// Clear the skeleton as soon as the persisted messages are imported.
|
|
2689
2721
|
// The active-run reconnect probe below must NOT gate first paint — it
|
|
2690
2722
|
// only matters when a run is mid-flight (e.g. after a hot reload), and
|
|
2691
2723
|
// it streams on top of the already-rendered messages.
|
|
2692
|
-
|
|
2724
|
+
if (!cancelled)
|
|
2725
|
+
setIsRestoring(false);
|
|
2693
2726
|
}
|
|
2727
|
+
if (cancelled || !canReconnect)
|
|
2728
|
+
return;
|
|
2694
2729
|
// Reconnect to an in-progress run after the skeleton has cleared, so a
|
|
2695
2730
|
// background `/runs/active` probe never delays showing the conversation.
|
|
2696
2731
|
try {
|
|
@@ -2700,6 +2735,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2700
2735
|
// No active run to reconnect to.
|
|
2701
2736
|
}
|
|
2702
2737
|
})();
|
|
2738
|
+
return () => {
|
|
2739
|
+
cancelled = true;
|
|
2740
|
+
};
|
|
2703
2741
|
}
|
|
2704
2742
|
else {
|
|
2705
2743
|
// Legacy: restore from sessionStorage
|
|
@@ -2726,6 +2764,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2726
2764
|
loadHistoryRepository,
|
|
2727
2765
|
isNewThread,
|
|
2728
2766
|
isThreadStateLoading,
|
|
2767
|
+
restoreAttempt,
|
|
2729
2768
|
]);
|
|
2730
2769
|
useEffect(() => {
|
|
2731
2770
|
if (!loadHistoryRepository ||
|
|
@@ -4063,6 +4102,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4063
4102
|
!authError &&
|
|
4064
4103
|
showEmptyState &&
|
|
4065
4104
|
!isRestoring;
|
|
4105
|
+
const threadRestoreErrorSurface = threadRestoreError ? (_jsxs("div", { role: "alert", className: "flex max-w-[320px] flex-col items-center gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3 text-center", children: [_jsx(IconRefresh, { className: "h-5 w-5 text-muted-foreground" }), _jsx("p", { className: "text-sm text-muted-foreground", children: t("agentChat.message.restoreRequestFailed") }), _jsx("button", { type: "button", onClick: retryThreadRestore, className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent", children: t("agentChat.common.retry") })] })) : null;
|
|
4066
4106
|
// Clarifying-question surface: the `ask-question` action writes a
|
|
4067
4107
|
// GuidedQuestionPayload to application_state under "guided-questions". The
|
|
4068
4108
|
// hook polls that key, and on submit/skip composes the answer as a normal
|
|
@@ -4079,7 +4119,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4079
4119
|
showComposerSlot ||
|
|
4080
4120
|
showCenteredEmptyThreadFooterSlot ||
|
|
4081
4121
|
(guidedQuestions && guidedQuestions.length > 0) ||
|
|
4082
|
-
|
|
4122
|
+
visibleComposerContextItems.length > 0 ||
|
|
4083
4123
|
showPlanModeCallout ||
|
|
4084
4124
|
showInlineMissingKeySetup);
|
|
4085
4125
|
const approvalResolutionScope = threadId ?? tabId ?? "default";
|
|
@@ -4173,7 +4213,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4173
4213
|
: "text-xs text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-accent", children: t("agentChat.auth.refreshChat") })] })] })) : isRestoring && centeredRestoringState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
|
|
4174
4214
|
? "sr-only"
|
|
4175
4215
|
: "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), "aria-busy": "true", children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ??
|
|
4176
|
-
t("agentChat.empty.loadingChat") })] })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) :
|
|
4216
|
+
t("agentChat.empty.loadingChat") })] })) : isRestoring ? (_jsxs("div", { className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "flex justify-end", children: _jsx("div", { className: "h-8 w-32 rounded-lg bg-muted animate-pulse" }) }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("div", { className: "h-4 w-48 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse" }), _jsx("div", { className: "h-4 w-40 rounded bg-muted animate-pulse" })] })] })) : threadRestoreError &&
|
|
4217
|
+
messages.length === 0 ? (_jsx("div", { className: "flex h-full flex-col items-center justify-center gap-3 px-4 py-16", children: threadRestoreErrorSurface })) : showEmptyState ? (_jsxs("div", { className: cn("agent-empty-state", emptyStateDisplay === "hidden"
|
|
4177
4218
|
? "sr-only"
|
|
4178
4219
|
: "flex h-full flex-col items-center justify-center gap-3 px-4 py-16"), children: [_jsx(IconMessage, { className: "h-5 w-5 text-muted-foreground/60" }), _jsx("p", { className: "sr-only", children: emptyStateText ??
|
|
4179
4220
|
t("agentChat.empty.prompt") }), emptyStateAddon, resolvedSuggestions &&
|
|
@@ -4183,7 +4224,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4183
4224
|
return;
|
|
4184
4225
|
}
|
|
4185
4226
|
void addToQueue(suggestion);
|
|
4186
|
-
}, className: "w-full px-2 py-1 text-center text-[13px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [_jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasRecentlyStoppedRun, children: _jsx(ThreadPrimitive.Messages
|
|
4227
|
+
}, className: "w-full px-2 py-1 text-center text-[13px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: suggestion }, suggestion))) })) : null, showInlineEmptyThreadFooterSlot ? (_jsx("div", { className: "agent-thread-footer-slot agent-thread-footer-slot--empty", children: resolvedThreadFooterSlot })) : null] })) : (_jsxs(MessageScrollerContent, { className: "agent-thread-content gap-4 px-4 py-4", children: [threadRestoreErrorSurface ? (_jsx(MessageScrollerItem, { children: threadRestoreErrorSurface })) : null, _jsx(AssistantMessageListErrorBoundary, { resetKey: messageListResetKey, children: _jsx(UserStoppedRunContext.Provider, { value: wasRecentlyStoppedRun, children: _jsx(ThreadPrimitive.Messages
|
|
4187
4228
|
// Deliberately NOT keyed on part structure. Doing that
|
|
4188
4229
|
// remounted the whole transcript every time a tool call
|
|
4189
4230
|
// started or a placeholder id was rewritten — a flash and a
|
|
@@ -4252,7 +4293,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4252
4293
|
"sidebar"
|
|
4253
4294
|
? t("agentChat.setup.connectToChat")
|
|
4254
4295
|
: t("agentChat.setup.connectToStart") }), missingApiKeySetupLayout !==
|
|
4255
|
-
"sidebar" ? (_jsx("span", { className: "agent-composer-missing-key-description", children: t("agentChat.setup.builderCredits") })) : null] }), _jsx("span", { className: "agent-composer-missing-key-cta", children: t("agentChat.setup.connectAi") })] }) }) })) : (_jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, onTextChange: isActiveComposer
|
|
4296
|
+
"sidebar" ? (_jsx("span", { className: "agent-composer-missing-key-description", children: t("agentChat.setup.builderCredits") })) : null] }), _jsx("span", { className: "agent-composer-missing-key-cta", children: t("agentChat.setup.connectAi") })] }) }) })) : (_jsxs(_Fragment, { children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, initialText: initialComposerText ?? undefined, initialTextKey: composerDraftScope, onTextChange: isActiveComposer
|
|
4256
4297
|
? handleComposerTextChange
|
|
4257
4298
|
: undefined, disabled: isComposerDisabled ||
|
|
4258
4299
|
showInlineMissingKeySetup, placeholder: showInlineMissingKeySetup
|
|
@@ -4269,16 +4310,16 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4269
4310
|
})
|
|
4270
4311
|
: t("agentChat.queue.followUp")
|
|
4271
4312
|
: resolveAssistantChatComposerPlaceholder(composerPlaceholder), onSubmit: isRunning ||
|
|
4272
|
-
|
|
4313
|
+
visibleComposerContextItems.length > 0
|
|
4273
4314
|
? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0
|
|
4274
4315
|
? references
|
|
4275
4316
|
: undefined, attachments, undefined, resolveAssistantChatSubmitIntent({
|
|
4276
4317
|
isRunning,
|
|
4277
4318
|
requestedIntent: options?.intent,
|
|
4278
4319
|
}), undefined, true)
|
|
4279
|
-
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, availableAgents: availableAgents, selectedAgent: selectedAgent, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
|
|
4320
|
+
: undefined, willQueue: engineSetupRequired || isRunning, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, availableAgents: availableAgents, selectedAgent: selectedAgent, hostedHarness: hostedHarness, modelListLoading: modelListLoading, onModelChange: shouldShowAssistantChatModelSelector(showModelSelector)
|
|
4280
4321
|
? onModelChange
|
|
4281
|
-
: undefined, onEffortChange: onEffortChange, onAgentChange: onAgentChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems:
|
|
4322
|
+
: undefined, onEffortChange: onEffortChange, onAgentChange: onAgentChange, imageModelMenu: imageModelMenu, onConnectProvider: onConnectProvider, onConnectLocalRuntime: onConnectLocalRuntime, toolbarSlot: composerToolbarSlot, contextItems: visibleComposerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, voiceEnabled: true, draftScope: composerDraftScope, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: composerExtraActionButton, stopButton: showRunningInUI ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: () => stopActiveRun({
|
|
4282
4323
|
preserveQueuedMessages: true,
|
|
4283
4324
|
}), "aria-label": t("agentChat.composer.stopResponse"), "data-agent-composer-slot": "stop-button", className: "shrink-0 flex h-7 w-7 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: _jsx(IconPlayerStopFilled, { className: "h-3 w-3" }) }) }), _jsx(TooltipContent, { children: t("agentChat.composer.stopResponse") })] })) : undefined })] })) }), showMissingKeySetup &&
|
|
4284
4325
|
!showInlineMissingKeySetup ? (_jsx(PopoverContent, { side: missingApiKeySetupLayout === "sidebar"
|
|
@@ -4292,6 +4333,10 @@ export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentN
|
|
|
4292
4333
|
engineRef.current = props.selectedEngine;
|
|
4293
4334
|
const effortRef = useRef(props.selectedEffort);
|
|
4294
4335
|
effortRef.current = props.selectedEffort;
|
|
4336
|
+
const harnessRef = useRef(props.hostedHarness ? props.selectedAgent : undefined);
|
|
4337
|
+
harnessRef.current = props.hostedHarness ? props.selectedAgent : undefined;
|
|
4338
|
+
const hostedHarnessRef = useRef(props.hostedHarness === true);
|
|
4339
|
+
hostedHarnessRef.current = props.hostedHarness === true;
|
|
4295
4340
|
const execModeRef = useRef(props.execMode);
|
|
4296
4341
|
execModeRef.current = props.execMode;
|
|
4297
4342
|
const scopeRef = useRef(contextScope);
|
|
@@ -4309,6 +4354,8 @@ export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentN
|
|
|
4309
4354
|
modelRef,
|
|
4310
4355
|
engineRef,
|
|
4311
4356
|
effortRef,
|
|
4357
|
+
harnessRef,
|
|
4358
|
+
hostedHarnessRef,
|
|
4312
4359
|
execModeRef,
|
|
4313
4360
|
browserTabId,
|
|
4314
4361
|
scopeRef,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { isClaudeCodeAgentId, isLunaModel, resolvePreferredAgentModel, } from "@agent-native/toolkit/composer/model-selection";
|
|
2
3
|
import { IconPlus, IconHistory, IconX } from "@tabler/icons-react";
|
|
3
|
-
import { useState, useRef, useEffect, useCallback } from "react";
|
|
4
|
+
import { useState, useRef, useEffect, useLayoutEffect, useCallback, } from "react";
|
|
4
5
|
import { DEFAULT_MODEL } from "../agent/default-model.js";
|
|
5
6
|
import { DEFAULT_REASONING_EFFORT, isReasoningEffort, resolveReasoningEffortSelection, } from "../shared/reasoning-effort.js";
|
|
6
7
|
import { AGENT_CHAT_CLEAR_CONTEXT_MESSAGE_TYPE, AGENT_CHAT_REMOVE_CONTEXT_MESSAGE_TYPE, AGENT_CHAT_SET_CONTEXT_MESSAGE_TYPE, appendAgentChatContextToMessage, claimAgentChatOpenRequest, claimAgentChatSubmit, drainBufferedAgentChatOpenRequests, drainBufferedAgentChatSubmits, filterAgentChatContextItems, getAgentChatContextState, isAgentChatSubmitCancelled, normalizeAgentChatContextItem, parseSubmitChatMessage, removeAgentChatContextItem, reportAgentChatSubmitResult, reportAgentChatSubmitTarget, setAgentChatContextItem, } from "./agent-chat.js";
|
|
@@ -20,6 +21,7 @@ import { CHAT_MODEL_SELECTION_CHANGED_EVENT } from "./use-chat-models.js";
|
|
|
20
21
|
import { useChatThreads, } from "./use-chat-threads.js";
|
|
21
22
|
import { usePollLoop } from "./use-poll-loop.js";
|
|
22
23
|
import { cn } from "./utils.js";
|
|
24
|
+
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
23
25
|
/** The single path that hands a queued send to a mounted chat ref. */
|
|
24
26
|
function deliverPendingSend(ref, send) {
|
|
25
27
|
if (isAgentChatSubmitCancelled(send.submitMessageId))
|
|
@@ -630,13 +632,23 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
630
632
|
const threadId = activeThreadIdRef.current;
|
|
631
633
|
if (!threadId)
|
|
632
634
|
return;
|
|
635
|
+
const preferredAgentModel = isClaudeCodeAgentId(props.selectedAgent) && isLunaModel(model)
|
|
636
|
+
? resolvePreferredAgentModel(props.selectedAgent, availableModels)
|
|
637
|
+
: undefined;
|
|
638
|
+
const nextModel = preferredAgentModel?.model ?? model;
|
|
639
|
+
const nextEngine = preferredAgentModel?.engine ?? engine;
|
|
633
640
|
const existing = threadModelRef.current.get(threadId);
|
|
634
|
-
const effort = resolveReasoningEffortSelection(
|
|
635
|
-
const selection = { model, engine, effort };
|
|
641
|
+
const effort = resolveReasoningEffortSelection(nextModel, existing?.effort);
|
|
642
|
+
const selection = { model: nextModel, engine: nextEngine, effort };
|
|
636
643
|
threadModelRef.current.set(threadId, selection);
|
|
637
644
|
persistModelSelection(selection);
|
|
638
645
|
bumpModelSelectionVersion();
|
|
639
|
-
}, [
|
|
646
|
+
}, [
|
|
647
|
+
availableModels,
|
|
648
|
+
bumpModelSelectionVersion,
|
|
649
|
+
persistModelSelection,
|
|
650
|
+
props.selectedAgent,
|
|
651
|
+
]);
|
|
640
652
|
const handleEffortChange = useCallback((effort) => {
|
|
641
653
|
const threadId = activeThreadIdRef.current;
|
|
642
654
|
if (!threadId)
|
|
@@ -659,6 +671,38 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
659
671
|
persistModelSelection,
|
|
660
672
|
resolveThreadModelSelection,
|
|
661
673
|
]);
|
|
674
|
+
useEffect(() => {
|
|
675
|
+
const preferredAgentModel = props.selectedAgent
|
|
676
|
+
? resolvePreferredAgentModel(props.selectedAgent, availableModels)
|
|
677
|
+
: undefined;
|
|
678
|
+
if (!preferredAgentModel)
|
|
679
|
+
return;
|
|
680
|
+
const threadId = activeThreadIdRef.current;
|
|
681
|
+
const current = threadId
|
|
682
|
+
? resolveThreadModelSelection(threadId)
|
|
683
|
+
: persistedModelSelection;
|
|
684
|
+
if (current?.model === preferredAgentModel.model &&
|
|
685
|
+
current.engine === preferredAgentModel.engine) {
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const selection = {
|
|
689
|
+
model: preferredAgentModel.model,
|
|
690
|
+
engine: preferredAgentModel.engine,
|
|
691
|
+
effort: resolveReasoningEffortSelection(preferredAgentModel.model, current?.effort),
|
|
692
|
+
};
|
|
693
|
+
if (threadId) {
|
|
694
|
+
threadModelRef.current.set(threadId, selection);
|
|
695
|
+
bumpModelSelectionVersion();
|
|
696
|
+
}
|
|
697
|
+
persistModelSelection(selection);
|
|
698
|
+
}, [
|
|
699
|
+
availableModels,
|
|
700
|
+
bumpModelSelectionVersion,
|
|
701
|
+
persistedModelSelection,
|
|
702
|
+
props.selectedAgent,
|
|
703
|
+
persistModelSelection,
|
|
704
|
+
resolveThreadModelSelection,
|
|
705
|
+
]);
|
|
662
706
|
const refreshEngines = useCallback(() => {
|
|
663
707
|
setModelListLoading(true);
|
|
664
708
|
Promise.all([
|
|
@@ -771,7 +815,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
771
815
|
openTabIdsRef.current = openTabIds;
|
|
772
816
|
const initializedRef = useRef(false);
|
|
773
817
|
const openTabsKeyRef = useRef(OPEN_TABS_KEY);
|
|
774
|
-
|
|
818
|
+
useBrowserLayoutEffect(() => {
|
|
775
819
|
const nextScope = scope;
|
|
776
820
|
if (!nextScope)
|
|
777
821
|
return;
|
|
@@ -782,7 +826,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
|
|
|
782
826
|
const marker = `Resource context: ${nextScope.type}:${nextScope.id}`;
|
|
783
827
|
const existing = getAgentChatContextState().items.find((item) => item.key === key);
|
|
784
828
|
let ownsContextItem = false;
|
|
785
|
-
if (!existing || existing.context.startsWith(
|
|
829
|
+
if (!existing || existing.context.startsWith("Resource context:")) {
|
|
786
830
|
setAgentChatContextItem({
|
|
787
831
|
key,
|
|
788
832
|
title,
|
|
@@ -226,7 +226,13 @@ export function NewWorkspaceAppFlow({ sourceApp = "chat", className = "", dispat
|
|
|
226
226
|
setStatusMessage("Sent to Builder chat.");
|
|
227
227
|
}
|
|
228
228
|
else if (isDevMode) {
|
|
229
|
-
sendToAgentChat({
|
|
229
|
+
sendToAgentChat({
|
|
230
|
+
message,
|
|
231
|
+
submit: true,
|
|
232
|
+
type: "code",
|
|
233
|
+
newTab: true,
|
|
234
|
+
reuseEmptyTab: true,
|
|
235
|
+
});
|
|
230
236
|
setStatusMessage("Sent to the local agent.");
|
|
231
237
|
}
|
|
232
238
|
else {
|
|
@@ -61,6 +61,12 @@ export interface CreateAgentChatAdapterOptions {
|
|
|
61
61
|
effortRef?: {
|
|
62
62
|
current: ReasoningEffort | undefined;
|
|
63
63
|
};
|
|
64
|
+
harnessRef?: {
|
|
65
|
+
current: string | undefined;
|
|
66
|
+
};
|
|
67
|
+
hostedHarnessRef?: {
|
|
68
|
+
current: boolean;
|
|
69
|
+
};
|
|
64
70
|
execModeRef?: {
|
|
65
71
|
current: "build" | "plan" | undefined;
|
|
66
72
|
};
|
|
@@ -65,6 +65,10 @@ const MAX_HISTORY_ATTACHMENT_CHARS = 60_000;
|
|
|
65
65
|
const MAX_OUTBOUND_ATTACHMENT_CHARS = 200_000;
|
|
66
66
|
const MAX_HISTORY_MESSAGES = 24;
|
|
67
67
|
const MAX_HISTORY_TOTAL_CHARS = 64_000;
|
|
68
|
+
// Budget for everything actually said in the thread — every user ask and every
|
|
69
|
+
// assistant conclusion. Separate from MAX_HISTORY_TOTAL_CHARS, which bounds the
|
|
70
|
+
// tool args/results those turns produced. See limitPriorMessagesForRequest.
|
|
71
|
+
const MAX_HISTORY_WORD_CHARS = 32_000;
|
|
68
72
|
const MAX_HISTORY_MESSAGE_CHARS = 12_000;
|
|
69
73
|
const MAX_HISTORY_TOOL_ARGS_CHARS = 8_000;
|
|
70
74
|
const MAX_HISTORY_TOOL_RESULT_CHARS = 12_000;
|
|
@@ -765,20 +769,36 @@ function estimateHistoryMessageCost(message) {
|
|
|
765
769
|
function limitPriorMessagesForRequest(messages) {
|
|
766
770
|
const recent = messages.slice(-MAX_HISTORY_MESSAGES);
|
|
767
771
|
const kept = [];
|
|
768
|
-
let
|
|
772
|
+
let words = 0;
|
|
773
|
+
let payload = 0;
|
|
769
774
|
for (let i = recent.length - 1; i >= 0; i--) {
|
|
770
775
|
const message = recent[i];
|
|
771
776
|
if (message.role !== "user" && message.role !== "assistant")
|
|
772
777
|
continue;
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
778
|
+
// What was said is cheap and cannot be re-derived; what a tool returned is
|
|
779
|
+
// ~97% of a tool-heavy turn and can always be re-read. Pricing both against
|
|
780
|
+
// one budget let a single read-heavy turn evict the whole conversation, so
|
|
781
|
+
// the agent re-derived the same answer and re-asked the same question.
|
|
782
|
+
const wordCost = messageTextForHistory(message).length;
|
|
783
|
+
if (kept.length > 0 && words + wordCost > MAX_HISTORY_WORD_CHARS)
|
|
784
|
+
continue;
|
|
785
|
+
const payloadCost = estimateHistoryMessageCost(message) - wordCost;
|
|
786
|
+
const affordsPayload = payload + payloadCost <= MAX_HISTORY_TOTAL_CHARS;
|
|
787
|
+
const content = affordsPayload
|
|
788
|
+
? message.content
|
|
789
|
+
: message.content.filter((part) => part.type === "text");
|
|
790
|
+
if (!content.length)
|
|
791
|
+
continue;
|
|
792
|
+
kept.push(affordsPayload ? message : { ...message, content });
|
|
793
|
+
words += wordCost;
|
|
794
|
+
if (affordsPayload)
|
|
795
|
+
payload += payloadCost;
|
|
779
796
|
}
|
|
780
797
|
kept.reverse();
|
|
781
|
-
|
|
798
|
+
// Stop before emptying: this rule exists to start history on a user turn for
|
|
799
|
+
// providers that require it, not to license sending no history at all. It was
|
|
800
|
+
// the last step that turned an over-budget turn into a blank conversation.
|
|
801
|
+
while (kept.length > 1 && kept[0].role !== "user") {
|
|
782
802
|
kept.shift();
|
|
783
803
|
}
|
|
784
804
|
return kept;
|
|
@@ -1471,6 +1491,8 @@ export function createAgentChatAdapter(options) {
|
|
|
1471
1491
|
const modelRef = options?.modelRef;
|
|
1472
1492
|
const engineRef = options?.engineRef;
|
|
1473
1493
|
const effortRef = options?.effortRef;
|
|
1494
|
+
const harnessRef = options?.harnessRef;
|
|
1495
|
+
const hostedHarnessRef = options?.hostedHarnessRef;
|
|
1474
1496
|
const execModeRef = options?.execModeRef;
|
|
1475
1497
|
const browserTabId = options?.browserTabId;
|
|
1476
1498
|
const scopeRef = options?.scopeRef;
|
|
@@ -1575,6 +1597,9 @@ export function createAgentChatAdapter(options) {
|
|
|
1575
1597
|
const engine = runConfigSelection("engine") ?? engineRef?.current;
|
|
1576
1598
|
const effort = runConfigSelection("effort") ??
|
|
1577
1599
|
effortRef?.current;
|
|
1600
|
+
const harness = hostedHarnessRef?.current
|
|
1601
|
+
? (runConfigSelection("harness") ?? harnessRef?.current)
|
|
1602
|
+
: undefined;
|
|
1578
1603
|
const requestedTurnId = (() => {
|
|
1579
1604
|
const raw = runConfig?.custom && typeof runConfig.custom === "object"
|
|
1580
1605
|
? runConfig.custom.turnId
|
|
@@ -1980,6 +2005,8 @@ export function createAgentChatAdapter(options) {
|
|
|
1980
2005
|
// "dev-frame" explicitly; the reusable in-product chat defaults to
|
|
1981
2006
|
// "app" even when it is running in Desktop or inside a preview iframe.
|
|
1982
2007
|
headers["x-agent-native-surface"] = surface;
|
|
2008
|
+
if (harness)
|
|
2009
|
+
headers["x-agent-native-hosted-harness"] = "1";
|
|
1983
2010
|
const reconnectCurrentRun = async function* () {
|
|
1984
2011
|
if (!runId)
|
|
1985
2012
|
return false;
|
|
@@ -3066,6 +3093,7 @@ export function createAgentChatAdapter(options) {
|
|
|
3066
3093
|
...(model ? { model } : {}),
|
|
3067
3094
|
...(engine ? { engine } : {}),
|
|
3068
3095
|
...(effort ? { effort } : {}),
|
|
3096
|
+
...(harness ? { harness: { runtime: harness } } : {}),
|
|
3069
3097
|
...(browserTabId ? { browserTabId } : {}),
|
|
3070
3098
|
...(scopeRef?.current ? { scope: scopeRef.current } : {}),
|
|
3071
3099
|
...(includeAttachments ? { attachments } : {}),
|
|
@@ -4,8 +4,11 @@ export declare const APP_CHAT_SIDEBAR_STATE_EVENT = "agent-native:per-app-chat-s
|
|
|
4
4
|
export declare const APP_CHAT_SIDEBAR_STATE_MESSAGE = "agentNative.perAppChatState";
|
|
5
5
|
/** Request sent by an iframe that mounted after the host announced its state. */
|
|
6
6
|
export declare const APP_CHAT_SIDEBAR_STATE_REQUEST_MESSAGE = "agentNative.perAppChatStateRequest";
|
|
7
|
+
export type AppChatSidebarCommand = "toggle" | "open" | "close";
|
|
7
8
|
export interface AppChatSidebarState {
|
|
8
9
|
open: boolean;
|
|
10
|
+
/** True when a parent shell owns chat for this app surface. */
|
|
11
|
+
hosted: boolean;
|
|
9
12
|
}
|
|
10
13
|
export declare function buildAppChatSidebarStateMessage(open: boolean): {
|
|
11
14
|
type: typeof APP_CHAT_SIDEBAR_STATE_MESSAGE;
|
|
@@ -20,4 +23,6 @@ export declare function buildAppChatSidebarStateRequest(): {
|
|
|
20
23
|
* agent panels from changing the app's navigation chrome.
|
|
21
24
|
*/
|
|
22
25
|
export declare function isPerAppChatStorageKey(storageKey: string | undefined): boolean;
|
|
26
|
+
export declare function requestPerAppChatCommand(command: AppChatSidebarCommand): boolean;
|
|
27
|
+
export declare function usePerAppChatState(enabled?: boolean): AppChatSidebarState;
|
|
23
28
|
export declare function usePerAppChatOpen(): boolean;
|