@xenosystem/agent-interface-ui 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/agentHostPlatformBridge.d.ts.map +1 -1
- package/dist/adapters/agentHostPlatformBridge.js +131 -0
- package/dist/adapters/agentHostPlatformBridge.js.map +1 -1
- package/dist/adapters/hubAgentPlatformBridge.d.ts.map +1 -1
- package/dist/adapters/hubAgentPlatformBridge.js +9 -0
- package/dist/adapters/hubAgentPlatformBridge.js.map +1 -1
- package/dist/components/agent/AgentInterface.d.ts.map +1 -1
- package/dist/components/agent/AgentInterface.js +247 -32
- package/dist/components/agent/AgentInterface.js.map +1 -1
- package/dist/components/agent/AgentPtyTerminalPanel.d.ts.map +1 -1
- package/dist/components/agent/AgentPtyTerminalPanel.js +66 -21
- package/dist/components/agent/AgentPtyTerminalPanel.js.map +1 -1
- package/dist/components/agent/AgentView.d.ts +5 -0
- package/dist/components/agent/AgentView.d.ts.map +1 -1
- package/dist/components/agent/AgentView.js +243 -37
- package/dist/components/agent/AgentView.js.map +1 -1
- package/dist/components/agent/nativeTuiComposerInput.d.ts +42 -0
- package/dist/components/agent/nativeTuiComposerInput.d.ts.map +1 -0
- package/dist/components/agent/nativeTuiComposerInput.js +51 -0
- package/dist/components/agent/nativeTuiComposerInput.js.map +1 -0
- package/dist/components/agent/ptyOutputReconciliation.d.ts +41 -0
- package/dist/components/agent/ptyOutputReconciliation.d.ts.map +1 -0
- package/dist/components/agent/ptyOutputReconciliation.js +68 -0
- package/dist/components/agent/ptyOutputReconciliation.js.map +1 -0
- package/dist/components/agent/settings/sections/DefaultAgentConfigSection.d.ts.map +1 -1
- package/dist/components/agent/settings/sections/DefaultAgentConfigSection.js +3 -2
- package/dist/components/agent/settings/sections/DefaultAgentConfigSection.js.map +1 -1
- package/dist/components/agent/settings/sections/WorkspaceDirectoryAccessSection.js +1 -1
- package/dist/components/agent/settings/sections/WorkspaceDirectoryAccessSection.js.map +1 -1
- package/dist/components/agent/terminalSurfaceAvailability.d.ts +22 -0
- package/dist/components/agent/terminalSurfaceAvailability.d.ts.map +1 -0
- package/dist/components/agent/terminalSurfaceAvailability.js +43 -0
- package/dist/components/agent/terminalSurfaceAvailability.js.map +1 -0
- package/dist/components/agent/webJobProgressView.d.ts +90 -0
- package/dist/components/agent/webJobProgressView.d.ts.map +1 -0
- package/dist/components/agent/webJobProgressView.js +104 -0
- package/dist/components/agent/webJobProgressView.js.map +1 -0
- package/dist/platformApiTypes.d.ts +32 -0
- package/dist/platformApiTypes.d.ts.map +1 -1
- package/dist/platformBridge.d.ts +9 -0
- package/dist/platformBridge.d.ts.map +1 -1
- package/dist/platformBridge.js +11 -0
- package/dist/platformBridge.js.map +1 -1
- package/dist/stores/agentChatStore.d.ts +11 -1
- package/dist/stores/agentChatStore.d.ts.map +1 -1
- package/dist/stores/agentChatStore.js +103 -6
- package/dist/stores/agentChatStore.js.map +1 -1
- package/dist/stores/xenoChatStore.d.ts.map +1 -1
- package/dist/stores/xenoChatStore.js +2 -1
- package/dist/stores/xenoChatStore.js.map +1 -1
- package/dist/utils/acpExecutionAuthority.d.ts +8 -0
- package/dist/utils/acpExecutionAuthority.d.ts.map +1 -1
- package/dist/utils/acpExecutionAuthority.js +24 -0
- package/dist/utils/acpExecutionAuthority.js.map +1 -1
- package/dist/utils/modelAvailability.d.ts +11 -0
- package/dist/utils/modelAvailability.d.ts.map +1 -0
- package/dist/utils/modelAvailability.js +10 -0
- package/dist/utils/modelAvailability.js.map +1 -0
- package/dist/utils/providerAvailability.d.ts +8 -0
- package/dist/utils/providerAvailability.d.ts.map +1 -0
- package/dist/utils/providerAvailability.js +13 -0
- package/dist/utils/providerAvailability.js.map +1 -0
- package/package.json +5 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { getAgentPlatformBridge } from '../../platformBridge.js';
|
|
2
|
+
import { getAgentPlatformBridge, getOptionalAgentPlatformBridge } from '../../platformBridge.js';
|
|
3
3
|
/*
|
|
4
4
|
* AgentView.tsx - Active Agent Chat Interface
|
|
5
5
|
*
|
|
@@ -23,7 +23,10 @@ import { AIStatusIndicator } from './AIStatusIndicator.js';
|
|
|
23
23
|
import { CommandToolCard } from './CommandToolCard.js';
|
|
24
24
|
import { AssistantTurnErrorNotice, AssistantTurnStatusNotice } from './AssistantTurnStatusNotice.js';
|
|
25
25
|
import { commandPreviewText, compactCommandPreview, deriveToolInteractionState, hideDuplicateToolTitle, isCommandToolCall } from './toolCardDisplay.js';
|
|
26
|
+
import { deriveWebJobProgressView, webJobStopAvailability, WEB_JOB_STOP_LABEL, WEB_JOB_STOP_TITLE } from './webJobProgressView.js';
|
|
26
27
|
import { cloudModelName, cloudModelSortScore } from '../../utils/cloudModels.js';
|
|
28
|
+
import { isSelectableLiveModel } from '../../utils/modelAvailability.js';
|
|
29
|
+
import { availableAcpConfigIds } from '../../utils/providerAvailability.js';
|
|
27
30
|
import { extractWindowsFilePathsFromToolResult, prepareAgentMarkdown, resolveMarkdownLocalFilePath, } from '../../utils/markdownWindowsPaths.js';
|
|
28
31
|
import { agentReasoningEffortLabel, buildExplicitAgentReasoningEffortFamilies, indexAgentReasoningEffortFamilies, preferredAgentReasoningModelId, resolveAgentReasoningEffortFamily, } from '../../utils/agentReasoningEffort.js';
|
|
29
32
|
import { acpAgentDisplayName, resolveHubAgentRuntimeProvider } from '../../utils/agentRuntimeProvider.js';
|
|
@@ -31,6 +34,7 @@ import { createConversationRuntimeBinding, reconcileCloudConversationModel, runt
|
|
|
31
34
|
import { extractUserFileCreateRequest, extractUserFileEditRequest, isAbsoluteOrRootedPath } from '../../utils/fileCreateIntent.js';
|
|
32
35
|
import { buildRuntimeAttachedContextPrompt } from '../../utils/agentAttachedContextPrompt.js';
|
|
33
36
|
import { buildWorkspaceSelectionContinuationPrompt } from '../../utils/workspaceSelectionContinuation.js';
|
|
37
|
+
import { isAgentComposerInputLocked } from './nativeTuiComposerInput.js';
|
|
34
38
|
import { AgentProviderSelectorController } from '../../providerSelector.js';
|
|
35
39
|
import { loadHubAgentProviderCatalog, refreshHubAgentProviderCatalog, subscribeHubAgentProviderCatalog, } from '../../adapters/xenoAgentHostCatalog.js';
|
|
36
40
|
const AGENT_GPT55_DEFAULT_MODEL = 'gpt-5.5-high';
|
|
@@ -2454,6 +2458,40 @@ function WebContextEvidenceCard({ toolCall, expanded }) {
|
|
|
2454
2458
|
}
|
|
2455
2459
|
return (_jsxs("div", { "data-web-context-evidence": evidence.evidenceId, className: "ml-3 mt-0.5 max-w-[calc(100%-12px)] overflow-hidden rounded-[3px] border border-sky-300/[0.08] bg-sky-300/[0.025]", children: [_jsxs("div", { className: "flex min-h-[24px] items-center gap-2 px-2 text-[10px]", children: [_jsx("span", { className: "uppercase tracking-[0.14em] text-sky-200/38", children: "Evidence" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-white/42", title: evidence.finalUrl || evidence.sourceUrl, children: sourceLabel }), _jsxs("span", { className: "flex-shrink-0 text-white/24", children: [evidence.citations.length, " ", evidence.citations.length === 1 ? 'source' : 'sources'] }), projection.job && (_jsx("span", { className: "flex-shrink-0 uppercase tracking-[0.12em] text-white/20", children: projection.job.state }))] }), expanded && (_jsxs("div", { className: "border-t border-white/[0.04] px-2 py-1.5", children: [_jsx("div", { className: "truncate font-mono text-[9px] text-white/20", title: evidence.evidenceId, children: evidence.evidenceId }), visibleCitations.length > 0 && (_jsxs("div", { className: "mt-1 space-y-1", children: [visibleCitations.map((citation, index) => (_jsx("a", { href: citation.url, target: "_blank", rel: "noreferrer", className: "block min-w-0 truncate text-[10px] text-sky-200/45 hover:text-sky-100/70", title: citation.url, children: citation.title || citation.url }, `${citation.url}:${index}`))), evidence.citations.length > visibleCitations.length && (_jsxs("div", { className: "text-[9px] text-white/20", children: ["+", evidence.citations.length - visibleCitations.length, " more sources"] }))] }))] }))] }));
|
|
2456
2460
|
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Live progress for a durable XENO Web Context job, rendered inside the tool card
|
|
2463
|
+
* that owns it rather than in a surface of its own.
|
|
2464
|
+
*
|
|
2465
|
+
* It sits beside `WebContextEvidenceCard`, not inside it: progress is a live
|
|
2466
|
+
* projection that arrives many times while a tool runs, evidence is the terminal
|
|
2467
|
+
* artefact that arrives once. A tool call may carry either, both, or neither, and
|
|
2468
|
+
* a tool that reports no progress renders exactly what it rendered before this
|
|
2469
|
+
* existed.
|
|
2470
|
+
*/
|
|
2471
|
+
function WebContextJobProgressRow({ toolCall, onStop, }) {
|
|
2472
|
+
const progress = toolCall.webJobProgress;
|
|
2473
|
+
if (!progress)
|
|
2474
|
+
return null;
|
|
2475
|
+
const view = deriveWebJobProgressView(progress, toolCall.status);
|
|
2476
|
+
// A stale row is a record, not a live one: nothing is still moving, so nothing pulses.
|
|
2477
|
+
const indeterminate = view.percent === null && !view.stale;
|
|
2478
|
+
return (_jsx("div", { "data-web-job-progress": view.jobId, "data-web-job-state": progress.state, "data-web-job-stale": view.stale ? 'true' : 'false', className: "ml-3 mt-0.5 max-w-[calc(100%-12px)] overflow-hidden rounded-[3px] border border-white/[0.06] bg-white/[0.02]", children: _jsxs("div", { className: "flex min-h-[24px] items-center gap-2 px-2 text-[10px]", children: [_jsx("span", { className: "flex-shrink-0 uppercase tracking-[0.14em] text-white/32", children: view.operationLabel }), _jsx("span", { className: `flex-shrink-0 uppercase tracking-[0.12em] ${view.tone === 'failed'
|
|
2479
|
+
? 'text-[#ff8d86]/70'
|
|
2480
|
+
: view.tone === 'cancelled' || view.tone === 'partial'
|
|
2481
|
+
? 'text-white/30'
|
|
2482
|
+
: 'text-white/45'}`, children: view.stateLabel }), _jsx("div", { role: "progressbar", "aria-label": view.ariaLabel, "aria-valuetext": view.valueText, ...(view.percent === null
|
|
2483
|
+
? {}
|
|
2484
|
+
: { 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': view.percent }), className: "h-[3px] w-[56px] flex-shrink-0 overflow-hidden rounded-[1px] bg-white/[0.07]", children: _jsx("div", { className: indeterminate
|
|
2485
|
+
? // No percentage is known, so nothing may claim one. The pulse says
|
|
2486
|
+
// "moving"; under a reduced-motion preference it stops moving and
|
|
2487
|
+
// still reads as a partially filled track, and the counters text
|
|
2488
|
+
// carries the same fact for anyone who cannot see either.
|
|
2489
|
+
'h-full w-1/3 bg-white/25 animate-pulse motion-reduce:animate-none'
|
|
2490
|
+
: 'h-full bg-white/30', ...(indeterminate ? {} : { style: { width: `${view.percent}%` } }) }) }), _jsxs("span", { className: "min-w-0 flex-1 truncate tabular-nums text-white/38", title: view.valueText, children: [view.countersText, view.terminalReason ? ` · ${view.terminalReason}` : ''] }), onStop && (_jsx("button", { type: "button", onClick: (event) => {
|
|
2491
|
+
event.stopPropagation();
|
|
2492
|
+
onStop();
|
|
2493
|
+
}, title: WEB_JOB_STOP_TITLE, "aria-label": WEB_JOB_STOP_TITLE, className: "h-[18px] flex-shrink-0 rounded-[2px] border border-white/[0.08] bg-white/[0.04] px-1.5 text-[9px] uppercase tracking-[0.14em] text-white/45 transition-colors hover:border-white/[0.16] hover:bg-white/[0.07] hover:text-white/75", children: WEB_JOB_STOP_LABEL }))] }) }));
|
|
2494
|
+
}
|
|
2457
2495
|
function readToolStringParam(params, keys) {
|
|
2458
2496
|
if (!params)
|
|
2459
2497
|
return '';
|
|
@@ -2759,6 +2797,18 @@ export function ToolCallsBlock({ calls, requestId, onAddApprovalRule, onOpenEdit
|
|
|
2759
2797
|
const [expandedFilePreviewId, setExpandedFilePreviewId] = useState(null);
|
|
2760
2798
|
const resolvePermission = useAgentChatStore(s => s.resolvePermission);
|
|
2761
2799
|
const grantConversationDirectory = useAgentChatStore(s => s.grantConversationDirectory);
|
|
2800
|
+
const abortActiveTurn = useAgentChatStore(s => s.abort);
|
|
2801
|
+
const activeRequestId = useAgentChatStore(s => s.activeRequestId);
|
|
2802
|
+
/**
|
|
2803
|
+
* Whether a cancellation path actually exists in THIS composition. The bridge is
|
|
2804
|
+
* injected by the consumer shell, so an embed that wires no runtime has none —
|
|
2805
|
+
* and offering a Stop button that resolves to nothing is the fabricated-success
|
|
2806
|
+
* failure mode, not a harmless no-op.
|
|
2807
|
+
*/
|
|
2808
|
+
const optionalBridge = getOptionalAgentPlatformBridge();
|
|
2809
|
+
const hostExposesCancellation = !!(optionalBridge?.acp?.cancelAgentPrompt
|
|
2810
|
+
|| optionalBridge?.agent?.cancel
|
|
2811
|
+
|| optionalBridge?.llm?.abort);
|
|
2762
2812
|
if (calls.length === 0)
|
|
2763
2813
|
return null;
|
|
2764
2814
|
const handleGrantAccess = (pr) => {
|
|
@@ -3016,7 +3066,13 @@ export function ToolCallsBlock({ calls, requestId, onAddApprovalRule, onOpenEdit
|
|
|
3016
3066
|
setExpandedFilePreviewId(tc.id);
|
|
3017
3067
|
}
|
|
3018
3068
|
}
|
|
3019
|
-
}, className: "flex-shrink-0", "aria-label": isExpanded ? 'Collapse tool details' : 'Expand tool details', children: _jsx(ChevronRight, { className: `w-3 h-3 text-white/15 transition-transform ${isExpanded ? 'rotate-90' : ''}` }) }))] })] }) })), tc.
|
|
3069
|
+
}, className: "flex-shrink-0", "aria-label": isExpanded ? 'Collapse tool details' : 'Expand tool details', children: _jsx(ChevronRight, { className: `w-3 h-3 text-white/15 transition-transform ${isExpanded ? 'rotate-90' : ''}` }) }))] })] }) })), tc.webJobProgress && (_jsx(WebContextJobProgressRow, { toolCall: tc, onStop: webJobStopAvailability({
|
|
3070
|
+
progress: tc.webJobProgress,
|
|
3071
|
+
toolCallStatus: tc.status,
|
|
3072
|
+
requestId,
|
|
3073
|
+
activeRequestId,
|
|
3074
|
+
hostExposesCancellation,
|
|
3075
|
+
}).available ? abortActiveTurn : undefined })), tc.webContext && !isRunning && (_jsx(WebContextEvidenceCard, { toolCall: tc, expanded: isExpanded })), pr?.status === 'pending' && (_jsx(ToolApprovalCard, { toolCall: tc, permission: pr, canPersistAllowlist:
|
|
3020
3076
|
// An ACP `execute` names no directory, and gating this on
|
|
3021
3077
|
// `directory` alone hid the persist choice for every such
|
|
3022
3078
|
// permission — which is why a user could not create a policy
|
|
@@ -3311,7 +3367,10 @@ export function AgentView(props) {
|
|
|
3311
3367
|
acpAgents: acpAgentsForRuntime,
|
|
3312
3368
|
}), [acpAgentsForRuntime, effectiveRuntimeSelection, props.permissionProfile]);
|
|
3313
3369
|
const runtimeMode = acpModePending || runtimeProvider.kind === 'acp-provider' ? 'acp' : inferenceMode;
|
|
3314
|
-
const selectableAcpAgents = useMemo(() =>
|
|
3370
|
+
const selectableAcpAgents = useMemo(() => {
|
|
3371
|
+
const availableConfigIds = availableAcpConfigIds(sharedRuntimeProviders);
|
|
3372
|
+
return acpAgentsForRuntime.filter((agent) => agent.enabled && availableConfigIds.has(agent.id));
|
|
3373
|
+
}, [acpAgentsForRuntime, sharedRuntimeProviders]);
|
|
3315
3374
|
const rawAcpProviderModel = effectiveRuntimeSelection?.providerKind === 'acp-provider'
|
|
3316
3375
|
? effectiveRuntimeSelection.providerModel
|
|
3317
3376
|
: undefined;
|
|
@@ -3321,6 +3380,7 @@ export function AgentView(props) {
|
|
|
3321
3380
|
: EMPTY_ACP_MODEL_CHOICES;
|
|
3322
3381
|
const acpModelOptions = useMemo(() => acpProviderModelOptions
|
|
3323
3382
|
.filter((option) => !option.familyBaseModelId)
|
|
3383
|
+
.filter((option) => option.available !== false)
|
|
3324
3384
|
.map((option) => option.id), [acpProviderModelOptions]);
|
|
3325
3385
|
const acpProviderModelOptionById = useMemo(() => new Map(acpProviderModelOptions.map((option) => [option.id, option])), [acpProviderModelOptions]);
|
|
3326
3386
|
const acpModelMessage = selectedAcpAgentConfigId ? acpModelMessagesByAgentId[selectedAcpAgentConfigId] : undefined;
|
|
@@ -3399,6 +3459,38 @@ export function AgentView(props) {
|
|
|
3399
3459
|
cancelled = true;
|
|
3400
3460
|
};
|
|
3401
3461
|
}, [acpModelListOpen, selectedAcpAgentConfigId]);
|
|
3462
|
+
useEffect(() => {
|
|
3463
|
+
if (!modelListOpen || runtimeMode !== 'cloud')
|
|
3464
|
+
return;
|
|
3465
|
+
let cancelled = false;
|
|
3466
|
+
setSharedProviderCatalogRefreshing(true);
|
|
3467
|
+
refreshHubAgentProviderCatalog({ force: true })
|
|
3468
|
+
.then((catalog) => {
|
|
3469
|
+
if (cancelled)
|
|
3470
|
+
return;
|
|
3471
|
+
setSharedRuntimeProviders(catalog.providers);
|
|
3472
|
+
setSharedProviderCatalogLoaded(true);
|
|
3473
|
+
})
|
|
3474
|
+
.catch(() => {
|
|
3475
|
+
if (cancelled)
|
|
3476
|
+
return;
|
|
3477
|
+
setSharedRuntimeProviders((current) => current.map((provider) => (provider.id === 'xeno-cloud'
|
|
3478
|
+
? {
|
|
3479
|
+
...provider,
|
|
3480
|
+
availability: 'unavailable',
|
|
3481
|
+
safeUnavailableReason: 'The live XENO model catalog could not be refreshed.',
|
|
3482
|
+
models: [],
|
|
3483
|
+
}
|
|
3484
|
+
: provider)));
|
|
3485
|
+
})
|
|
3486
|
+
.finally(() => {
|
|
3487
|
+
if (!cancelled)
|
|
3488
|
+
setSharedProviderCatalogRefreshing(false);
|
|
3489
|
+
});
|
|
3490
|
+
return () => {
|
|
3491
|
+
cancelled = true;
|
|
3492
|
+
};
|
|
3493
|
+
}, [modelListOpen, runtimeMode]);
|
|
3402
3494
|
useEffect(() => {
|
|
3403
3495
|
if (effectiveRuntimeSelection?.providerKind !== 'acp-provider' && !modelListOpen && !acpCliListOpen && !acpModelListOpen)
|
|
3404
3496
|
return;
|
|
@@ -3460,9 +3552,10 @@ export function AgentView(props) {
|
|
|
3460
3552
|
const acpCliBtnRef = useRef(null);
|
|
3461
3553
|
const acpModelBtnRef = useRef(null);
|
|
3462
3554
|
const authDisplayName = useAuthStore(s => s.user?.display_name ?? null);
|
|
3555
|
+
const cloudRuntimeProvider = useMemo(() => sharedRuntimeProviders.find((entry) => entry.id === 'xeno-cloud'), [sharedRuntimeProviders]);
|
|
3463
3556
|
const cloudModels = useMemo(() => {
|
|
3464
|
-
|
|
3465
|
-
|
|
3557
|
+
return (cloudRuntimeProvider?.models || [])
|
|
3558
|
+
.filter(isSelectableLiveModel)
|
|
3466
3559
|
.map((modelEntry) => ({
|
|
3467
3560
|
id: modelEntry.id,
|
|
3468
3561
|
name: modelEntry.displayName || cloudModelName(modelEntry.id),
|
|
@@ -3479,7 +3572,9 @@ export function AgentView(props) {
|
|
|
3479
3572
|
const score = cloudModelSortScore(a.id) - cloudModelSortScore(b.id);
|
|
3480
3573
|
return score || a.name.localeCompare(b.name);
|
|
3481
3574
|
});
|
|
3482
|
-
}, [
|
|
3575
|
+
}, [cloudRuntimeProvider]);
|
|
3576
|
+
const cloudCatalogUnavailableReason = cloudRuntimeProvider?.safeUnavailableReason
|
|
3577
|
+
|| 'The live XENO model catalog is unavailable.';
|
|
3483
3578
|
const availableModels = useXenoChatStore(s => s.availableModels);
|
|
3484
3579
|
const fetchModels = useXenoChatStore(s => s.fetchModels);
|
|
3485
3580
|
const localModels = useMemo(() => availableModels.filter(m => m.provider === 'local'), [availableModels]);
|
|
@@ -3641,10 +3736,9 @@ export function AgentView(props) {
|
|
|
3641
3736
|
}).filter((entry) => !normalizedModelSearch || `${entry.name} ${entry.searchText}`.toLowerCase().includes(normalizedModelSearch));
|
|
3642
3737
|
}, [acpEffortFamilies, acpEffortFamilyByModelId, acpModelOptions, acpProviderModel, acpProviderModelOptionById, normalizedModelSearch, selectedAcpEffortFamily?.baseId]);
|
|
3643
3738
|
const preferredGpt55Model = useMemo(() => {
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
||
|
|
3647
|
-
|| selectableCloudModels[0]?.id
|
|
3739
|
+
return cloudModels.find(m => m.id === AGENT_GPT55_DEFAULT_MODEL)?.id
|
|
3740
|
+
|| cloudModels.find(m => m.id === 'gpt-5.5')?.id
|
|
3741
|
+
|| cloudModels[0]?.id
|
|
3648
3742
|
|| AGENT_GPT55_DEFAULT_MODEL;
|
|
3649
3743
|
}, [cloudModels]);
|
|
3650
3744
|
useEffect(() => {
|
|
@@ -3663,9 +3757,12 @@ export function AgentView(props) {
|
|
|
3663
3757
|
return cloudModelName(selectedCloudEffortFamily.baseId);
|
|
3664
3758
|
return isThinkingVariant ? `${cloud.name} (Thinking)` : cloud.name;
|
|
3665
3759
|
}
|
|
3760
|
+
if (inferenceMode === 'cloud' && sharedProviderCatalogLoaded) {
|
|
3761
|
+
return cloudCatalogUnavailableReason.startsWith('Sign in') ? 'Sign in for Cloud' : 'Cloud unavailable';
|
|
3762
|
+
}
|
|
3666
3763
|
const local = availableModels.find(m => m.id === actualModelId);
|
|
3667
3764
|
return local?.name || actualModelId;
|
|
3668
|
-
}, [cloudModels, availableModels, selectedModel, actualModelId, selectedCloudEffortFamily, isThinkingVariant, runtimeProvider.kind, runtimeProvider.displayName, acpProviderModelDisplayName]);
|
|
3765
|
+
}, [cloudModels, availableModels, selectedModel, actualModelId, selectedCloudEffortFamily, isThinkingVariant, runtimeProvider.kind, runtimeProvider.displayName, acpProviderModelDisplayName, inferenceMode, sharedProviderCatalogLoaded, cloudCatalogUnavailableReason]);
|
|
3669
3766
|
const currentCloudModel = useMemo(() => cloudModels.find(m => m.id === actualModelId), [cloudModels, actualModelId]);
|
|
3670
3767
|
const cloudModelSelectionMissing = runtimeProvider.kind !== 'acp-provider'
|
|
3671
3768
|
&& inferenceMode === 'cloud'
|
|
@@ -3678,11 +3775,13 @@ export function AgentView(props) {
|
|
|
3678
3775
|
}, [actualModelId, currentCloudModel, preferredGpt55Model]);
|
|
3679
3776
|
const cloudModelUnavailable = runtimeProvider.kind !== 'acp-provider'
|
|
3680
3777
|
&& inferenceMode === 'cloud'
|
|
3681
|
-
&& (
|
|
3778
|
+
&& ((sharedProviderCatalogLoaded && cloudModels.length === 0) || cloudModelSelectionMissing);
|
|
3682
3779
|
const cloudModelDegraded = runtimeProvider.kind !== 'acp-provider' && inferenceMode === 'cloud' && currentCloudModel?.availability === 'degraded';
|
|
3683
3780
|
const cloudModelStatusReason = cloudModelSelectionMissing
|
|
3684
3781
|
? 'The selected model is not present in the live model catalog. Choose an available model.'
|
|
3685
|
-
: (
|
|
3782
|
+
: (cloudModels.length === 0 && sharedProviderCatalogLoaded
|
|
3783
|
+
? cloudCatalogUnavailableReason
|
|
3784
|
+
: (currentCloudModel?.availabilityReason || null));
|
|
3686
3785
|
const renderModelSearch = () => (_jsx("div", { className: "shrink-0 border-b border-white/[0.06] bg-[#111111]", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-3 w-3 -translate-y-1/2 text-white/25" }), _jsx("input", { autoFocus: true, type: "text", value: modelSearchQuery, onChange: (event) => setModelSearchQuery(event.target.value), onKeyDown: (event) => {
|
|
3687
3786
|
if (event.key === 'Escape') {
|
|
3688
3787
|
setModelListOpen(false);
|
|
@@ -3698,6 +3797,64 @@ export function AgentView(props) {
|
|
|
3698
3797
|
if (availableModels.length === 0)
|
|
3699
3798
|
void fetchModels();
|
|
3700
3799
|
}, [availableModels.length, fetchModels]);
|
|
3800
|
+
const providerLaneSelectionRevisionRef = useRef(0);
|
|
3801
|
+
const bindRuntimeProviderLane = useCallback((selection, modelId) => {
|
|
3802
|
+
const providerLaneSelect = getAgentPlatformBridge().workspace.providerLaneSelect;
|
|
3803
|
+
if (!conversationId || !conversation || !providerLaneSelect)
|
|
3804
|
+
return;
|
|
3805
|
+
const binding = resolveConversationWorkspaceBinding({
|
|
3806
|
+
workspaceMode: conversation.workspaceMode,
|
|
3807
|
+
rootDirectory: conversation.rootDirectory,
|
|
3808
|
+
grantedDirectories: conversation.grantedDirectories || EMPTY_STRINGS,
|
|
3809
|
+
inheritedDirectories: allowedDirectories,
|
|
3810
|
+
hasMessages: conversation.messages.length > 0,
|
|
3811
|
+
});
|
|
3812
|
+
if (binding.mode === 'unselected') {
|
|
3813
|
+
setToolStatus('Choose Chat or Agent mode before selecting a provider.');
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
const bindRevision = ++providerLaneSelectionRevisionRef.current;
|
|
3817
|
+
const selectedAt = selection.selectedAt;
|
|
3818
|
+
const providerId = selection.providerId;
|
|
3819
|
+
const acpAgentConfigId = selection.acpAgentConfigId;
|
|
3820
|
+
void providerLaneSelect({
|
|
3821
|
+
conversationId,
|
|
3822
|
+
workspaceMode: binding.mode,
|
|
3823
|
+
...(binding.mode === 'workspace' && binding.rootDirectory
|
|
3824
|
+
? { workingDirectory: binding.rootDirectory }
|
|
3825
|
+
: {}),
|
|
3826
|
+
...(props.workspaceId ? { workspaceId: props.workspaceId } : {}),
|
|
3827
|
+
providerKind: selection.providerKind === 'acp-provider'
|
|
3828
|
+
? 'acp-provider'
|
|
3829
|
+
: 'sdk-native',
|
|
3830
|
+
...(providerId ? { providerId } : {}),
|
|
3831
|
+
...(acpAgentConfigId ? { acpAgentConfigId } : {}),
|
|
3832
|
+
model: modelId.replace(/:thinking$/, ''),
|
|
3833
|
+
}).then((result) => {
|
|
3834
|
+
if (bindRevision !== providerLaneSelectionRevisionRef.current)
|
|
3835
|
+
return;
|
|
3836
|
+
const latest = useAgentChatStore.getState().getConversation(conversationId);
|
|
3837
|
+
if (!latest || latest.runtime?.selection.selectedAt !== selectedAt)
|
|
3838
|
+
return;
|
|
3839
|
+
if (!result?.success || !result.activeLane || !result.hostBinding) {
|
|
3840
|
+
setToolStatus(result?.error || 'The selected provider could not be bound to this conversation.');
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
setToolStatus(null);
|
|
3844
|
+
updateConversation(conversationId, {
|
|
3845
|
+
sessionUi: {
|
|
3846
|
+
...(latest.sessionUi || {}),
|
|
3847
|
+
hostBinding: result.hostBinding,
|
|
3848
|
+
activeLane: result.activeLane,
|
|
3849
|
+
lanes: result.lanes || [result.activeLane],
|
|
3850
|
+
},
|
|
3851
|
+
});
|
|
3852
|
+
}).catch((error) => {
|
|
3853
|
+
if (bindRevision !== providerLaneSelectionRevisionRef.current)
|
|
3854
|
+
return;
|
|
3855
|
+
setToolStatus(error instanceof Error ? error.message : 'The selected provider could not be bound to this conversation.');
|
|
3856
|
+
});
|
|
3857
|
+
}, [allowedDirectories, conversation, conversationId, props.workspaceId, setToolStatus, updateConversation]);
|
|
3701
3858
|
const persistRuntimeBinding = useCallback((selection, uiModel, mode) => {
|
|
3702
3859
|
const previousSelection = conversation?.runtime?.selection;
|
|
3703
3860
|
const runtime = createConversationRuntimeBinding({
|
|
@@ -3712,7 +3869,12 @@ export function AgentView(props) {
|
|
|
3712
3869
|
schemaVersion: 2,
|
|
3713
3870
|
model: uiModel,
|
|
3714
3871
|
runtime,
|
|
3872
|
+
sessionUi: {
|
|
3873
|
+
...(conversation?.sessionUi || {}),
|
|
3874
|
+
activeLane: undefined,
|
|
3875
|
+
},
|
|
3715
3876
|
});
|
|
3877
|
+
bindRuntimeProviderLane(selection, uiModel);
|
|
3716
3878
|
}
|
|
3717
3879
|
if (conversationId
|
|
3718
3880
|
&& previousSelection?.providerKind === 'acp-provider'
|
|
@@ -3732,6 +3894,8 @@ export function AgentView(props) {
|
|
|
3732
3894
|
return runtime;
|
|
3733
3895
|
}, [
|
|
3734
3896
|
agentId,
|
|
3897
|
+
bindRuntimeProviderLane,
|
|
3898
|
+
conversation?.sessionUi,
|
|
3735
3899
|
conversation?.runtime?.selection,
|
|
3736
3900
|
conversationId,
|
|
3737
3901
|
props.teamId,
|
|
@@ -3775,11 +3939,14 @@ export function AgentView(props) {
|
|
|
3775
3939
|
setEffortListOpen(false);
|
|
3776
3940
|
setAcpCliListOpen(false);
|
|
3777
3941
|
setAcpModelListOpen(false);
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3942
|
+
if (cloudModels.length === 0) {
|
|
3943
|
+
setToolStatus('No cloud models are currently available from the live XENO catalog.');
|
|
3944
|
+
setModeExpanded(false);
|
|
3945
|
+
setModelListOpen(false);
|
|
3946
|
+
return;
|
|
3947
|
+
}
|
|
3948
|
+
const rememberedCloudModel = cloudModels.find((modelEntry) => (modelEntry.id === lastCloudModel.replace(/:thinking$/, '')));
|
|
3949
|
+
const fallbackCloudModel = rememberedCloudModel ? lastCloudModel : preferredGpt55Model;
|
|
3783
3950
|
setToolStatus(null);
|
|
3784
3951
|
setInferenceMode('cloud');
|
|
3785
3952
|
setModeExpanded(false);
|
|
@@ -3810,27 +3977,40 @@ export function AgentView(props) {
|
|
|
3810
3977
|
setShowLocalRuntimePrompt(false);
|
|
3811
3978
|
setToolStatus(null);
|
|
3812
3979
|
setAcpModePending(true);
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
}
|
|
3980
|
+
// Close the previous mode's menus as part of the synchronous transition.
|
|
3981
|
+
// Doing this after the awaited Host/registry loads lets a user open the
|
|
3982
|
+
// ACP provider picker and then has the stale transition completion close
|
|
3983
|
+
// it underneath them — exactly the "empty picker" race seen in the live
|
|
3984
|
+
// Electron scenario.
|
|
3819
3985
|
setModelListOpen(false);
|
|
3820
3986
|
setEffortListOpen(false);
|
|
3821
3987
|
setAcpCliListOpen(false);
|
|
3822
3988
|
setAcpModelListOpen(false);
|
|
3823
|
-
|
|
3989
|
+
let providers = sharedRuntimeProviders;
|
|
3990
|
+
if (!sharedProviderCatalogLoaded) {
|
|
3991
|
+
const catalog = await loadHubAgentProviderCatalog();
|
|
3992
|
+
providers = catalog.providers;
|
|
3993
|
+
setSharedRuntimeProviders(catalog.providers);
|
|
3994
|
+
setSharedProviderCatalogLoaded(true);
|
|
3995
|
+
}
|
|
3996
|
+
await loadAcpAgentsForRuntime();
|
|
3997
|
+
const availableConfigIds = availableAcpConfigIds(providers);
|
|
3998
|
+
const hasAvailableProvider = useAcpAgentStore.getState().agents.some((agent) => (agent.enabled && availableConfigIds.has(agent.id)));
|
|
3999
|
+
if (!hasAvailableProvider) {
|
|
4000
|
+
setToolStatus('No available ACP providers. Enable or repair one in Settings.');
|
|
4001
|
+
}
|
|
4002
|
+
}, [loadAcpAgentsForRuntime, setToolStatus, sharedProviderCatalogLoaded, sharedRuntimeProviders]);
|
|
3824
4003
|
const selectAcpRuntime = useCallback((acpAgentConfigId, providerModel) => {
|
|
3825
4004
|
const nextAcpAgentConfigId = acpAgentConfigId || selectedAcpAgentConfigId;
|
|
3826
4005
|
if (!nextAcpAgentConfigId)
|
|
3827
4006
|
return;
|
|
4007
|
+
setToolStatus(null);
|
|
3828
4008
|
setAcpRuntimeSelection(nextAcpAgentConfigId, providerModel);
|
|
3829
4009
|
setModelListOpen(false);
|
|
3830
4010
|
setEffortListOpen(false);
|
|
3831
4011
|
setAcpCliListOpen(false);
|
|
3832
4012
|
setAcpModelListOpen(false);
|
|
3833
|
-
}, [selectedAcpAgentConfigId, setAcpRuntimeSelection]);
|
|
4013
|
+
}, [selectedAcpAgentConfigId, setAcpRuntimeSelection, setToolStatus]);
|
|
3834
4014
|
// Delay model selector visibility - hide instantly on expand, show after collapse animation (200ms)
|
|
3835
4015
|
useEffect(() => {
|
|
3836
4016
|
if (modeExpanded) {
|
|
@@ -3969,12 +4149,15 @@ export function AgentView(props) {
|
|
|
3969
4149
|
&& !!selectedAcpAgentConfigId
|
|
3970
4150
|
&& !acpRuntimeAgentDetails[selectedAcpAgentConfigId]
|
|
3971
4151
|
&& !acpRuntimeAgentDetailErrors[selectedAcpAgentConfigId];
|
|
3972
|
-
const inputLocked =
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
4152
|
+
const inputLocked = isAgentComposerInputLocked({
|
|
4153
|
+
workspaceUnselected: workspaceBinding.mode === 'unselected',
|
|
4154
|
+
externalInputLocked: !!props.externalInputLockReason,
|
|
4155
|
+
nativeTuiWriterAttached: !!props.onSendToNativeTui,
|
|
4156
|
+
commandRunning: isRunningCommand,
|
|
4157
|
+
acpRuntimeReadinessPending,
|
|
4158
|
+
nonAcpModelNotReady: runtimeProvider.kind !== 'acp-provider' && !isModelReady,
|
|
4159
|
+
cloudModelUnavailable,
|
|
4160
|
+
});
|
|
3978
4161
|
const queuedTurnCount = conversation?.pendingTurns?.length ?? 0;
|
|
3979
4162
|
const checkpoints = conversation?.checkpoints || [];
|
|
3980
4163
|
const pendingTurns = conversation?.pendingTurns || [];
|
|
@@ -5968,6 +6151,30 @@ export function AgentView(props) {
|
|
|
5968
6151
|
const hasAttachments = attachmentsToSend.length > 0;
|
|
5969
6152
|
if ((!trimmed && !hasAttachments) || inputLocked)
|
|
5970
6153
|
return;
|
|
6154
|
+
if (props.onSendToNativeTui) {
|
|
6155
|
+
const targetConversationId = ensureConversationId(conversationId);
|
|
6156
|
+
if (hasAttachments) {
|
|
6157
|
+
appendMessage(targetConversationId, {
|
|
6158
|
+
role: 'assistant',
|
|
6159
|
+
content: 'Attach files from the native provider TUI or return to structured chat before sending attachments.',
|
|
6160
|
+
error: 'Native provider TUI input accepts text only.',
|
|
6161
|
+
});
|
|
6162
|
+
return;
|
|
6163
|
+
}
|
|
6164
|
+
setInput('');
|
|
6165
|
+
if (textareaRef.current)
|
|
6166
|
+
textareaRef.current.style.height = 'auto';
|
|
6167
|
+
const delivered = await props.onSendToNativeTui(trimmed);
|
|
6168
|
+
if (!delivered.success) {
|
|
6169
|
+
setInput(trimmed);
|
|
6170
|
+
appendMessage(targetConversationId, {
|
|
6171
|
+
role: 'assistant',
|
|
6172
|
+
content: delivered.error || 'The native provider TUI could not receive the message.',
|
|
6173
|
+
error: delivered.error || 'Native provider TUI input failed.',
|
|
6174
|
+
});
|
|
6175
|
+
}
|
|
6176
|
+
return;
|
|
6177
|
+
}
|
|
5971
6178
|
const useLegacyDeterministicFileActions = isLegacyDeterministicFileActionCompatibilityEnabled();
|
|
5972
6179
|
const replaceExistingFileCreate = useLegacyDeterministicFileActions && !hasAttachments && isReplaceExistingFileIntent(trimmed)
|
|
5973
6180
|
? findLastExistingFileWriteRequest(messages)
|
|
@@ -7271,18 +7478,17 @@ export function AgentView(props) {
|
|
|
7271
7478
|
const fixture = agent.certification?.status === 'fixture_only';
|
|
7272
7479
|
const ready = fixture || (agent.certification?.status === 'certified' && agent.providerReview?.runnable === true);
|
|
7273
7480
|
return (_jsxs("button", { onClick: () => { selectAcpRuntime(agent.id, selected ? acpProviderModel : undefined); }, role: "option", "aria-selected": selected, className: `w-full flex items-center justify-between gap-2 px-3 py-1.5 text-left transition-colors cursor-pointer ${selected ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}`, children: [_jsx("span", { className: `min-w-0 flex-1 text-[11px] truncate ${selected ? 'text-white/70' : 'text-white/40'}`, children: acpAgentDisplayName(agent) }), _jsx("span", { className: `text-[8px] uppercase tracking-wide ${ready ? 'text-[#96e59d]/70' : 'text-[#ffd38a]/70'}`, children: ready ? 'ready' : 'check' })] }, agent.id));
|
|
7274
|
-
})) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No
|
|
7481
|
+
})) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No available ACP CLI providers. Enable or repair one in Settings." }))] })), runtimeMode === 'acp' && acpModelListOpen && (_jsxs("div", { ref: acpModelListRef, className: "xeno-elevated-shell xeno-border-subtle absolute left-[170px] bottom-[44px] z-50 flex max-h-[280px] w-[220px] flex-col overflow-hidden rounded-md border", children: [renderModelSearch(), _jsxs("div", { role: "listbox", className: "min-h-0 overflow-y-auto", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-3 pt-2 pb-1 text-[9px] uppercase tracking-[0.14em] text-white/25", children: [_jsx("span", { children: "ACP model" }), sharedProviderCatalogRefreshing && !acpModelsLoading && _jsx("span", { children: "Refreshing..." })] }), _jsx("button", { onClick: () => { if (selectedAcpAgentConfigId)
|
|
7275
7482
|
selectAcpRuntime(selectedAcpAgentConfigId, undefined); }, disabled: !selectedAcpAgentConfigId, role: "option", "aria-selected": !acpProviderModel, className: `w-full flex items-center justify-between px-3 py-1.5 text-left transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed ${!acpProviderModel ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}`, children: _jsx("span", { className: `text-[11px] truncate ${!acpProviderModel ? 'text-white/70' : 'text-white/40'}`, children: "CLI default" }) }), acpModelsLoading ? (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "Loading models..." })) : acpModelOptions.length > 0 ? (filteredAcpModelOptions.length > 0 ? filteredAcpModelOptions.map((entry) => {
|
|
7276
7483
|
const selected = entry.selectionKey === (selectedAcpEffortFamily?.baseId || acpProviderModel);
|
|
7277
7484
|
return (_jsxs("button", { onClick: () => { if (selectedAcpAgentConfigId)
|
|
7278
7485
|
selectAcpRuntime(selectedAcpAgentConfigId, entry.id); }, disabled: !selectedAcpAgentConfigId || !entry.available, role: "option", "aria-selected": selected, className: `w-full flex items-center justify-between px-3 py-1.5 text-left transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed ${selected ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}`, title: entry.reason || undefined, children: [_jsx("span", { className: `min-w-0 flex-1 text-[11px] truncate ${selected ? 'text-white/70' : 'text-white/40'}`, children: entry.name }), !entry.available && _jsx("span", { className: "ml-2 flex-shrink-0 text-[8px] uppercase tracking-wide text-[#ff8f8f]", children: "Unavailable" })] }, entry.selectionKey));
|
|
7279
|
-
}) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No matching models" }))) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: selectedAcpAgentConfigId ? (acpModelMessage || 'No preset models for this ACP CLI') : 'Select a CLI first' }))] })] })), modelListOpen && runtimeMode !== 'acp' && (_jsxs("div", { ref: modelListRef, className: "xeno-elevated-shell xeno-border-subtle absolute left-3 bottom-[44px] z-50 flex max-h-[340px] w-[280px] flex-col overflow-hidden rounded-md border", children: [renderModelSearch(), _jsx("div", { role: "listbox", className: "min-h-0 overflow-y-auto", children: runtimeMode === 'cloud' ? (cloudModels.length > 0 ? (filteredCloudModelOptions.length > 0 ? filteredCloudModelOptions.map(entry => ((() => {
|
|
7486
|
+
}) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No matching models" }))) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: selectedAcpAgentConfigId ? (acpModelMessage || 'No preset models for this ACP CLI') : 'Select a CLI first' }))] })] })), modelListOpen && runtimeMode !== 'acp' && (_jsxs("div", { ref: modelListRef, className: "xeno-elevated-shell xeno-border-subtle absolute left-3 bottom-[44px] z-50 flex max-h-[340px] w-[280px] flex-col overflow-hidden rounded-md border", children: [renderModelSearch(), _jsx("div", { role: "listbox", className: "min-h-0 overflow-y-auto", children: runtimeMode === 'cloud' ? (sharedProviderCatalogRefreshing ? (_jsx("div", { className: "px-3 py-3 text-[10px] text-white/25 text-center", children: "Refreshing available models..." })) : cloudModels.length > 0 ? (filteredCloudModelOptions.length > 0 ? filteredCloudModelOptions.map(entry => ((() => {
|
|
7280
7487
|
const modelMeta = cloudModels.find(cm => cm.id === entry.id.replace(/:thinking$/, ''));
|
|
7281
|
-
const unavailable = modelMeta?.availability === 'unavailable';
|
|
7282
7488
|
const degraded = modelMeta?.availability === 'degraded';
|
|
7283
7489
|
const selected = entry.selectionKey === (selectedCloudEffortFamily?.baseId || selectedModel);
|
|
7284
|
-
return (_jsxs("button", { onClick: () => { setInferenceMode('cloud'); setSelectedModel(entry.id); setModelListOpen(false); setEffortListOpen(false); }, role: "option", "aria-selected": selected, className: `w-full flex items-center justify-between px-3 py-1.5 text-left transition-colors cursor-pointer ${selected ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}
|
|
7285
|
-
})())) : (_jsx("div", { className: "px-3 py-3 text-[10px] text-white/25 text-center", children: "No matching models" }))) : (_jsx("div", { className: "px-3 py-3 text-[10px] text-white/
|
|
7490
|
+
return (_jsxs("button", { onClick: () => { setInferenceMode('cloud'); setSelectedModel(entry.id); setModelListOpen(false); setEffortListOpen(false); }, role: "option", "aria-selected": selected, className: `w-full flex items-center justify-between px-3 py-1.5 text-left transition-colors cursor-pointer ${selected ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}`, title: modelMeta?.availabilityReason || undefined, children: [_jsxs("div", { className: "min-w-0 flex-1 flex items-center gap-2", children: [_jsx("span", { className: `text-[11px] truncate ${selected ? 'text-white/70' : 'text-white/40'}`, children: entry.name }), degraded && _jsx("span", { className: "text-[8px] uppercase tracking-wide text-[#ffd38a]", children: "Fallback" })] }), _jsx("span", { className: "text-[9px] text-white/20 flex-shrink-0 ml-2 tabular-nums", children: (() => { const m = cloudModels.find(cm => cm.id === entry.id.replace(/:thinking$/, '')); return m?.creditsPerKToken != null ? `${m.creditsPerKToken}/1k` : ''; })() })] }, entry.id));
|
|
7491
|
+
})())) : (_jsx("div", { className: "px-3 py-3 text-[10px] text-white/25 text-center", children: "No matching models" }))) : (_jsx("div", { className: "px-3 py-3 text-[10px] leading-relaxed text-white/35 text-center", children: cloudCatalogUnavailableReason }))) : (_jsxs(_Fragment, { children: [localModels.length > 0 ? (filteredLocalModels.length > 0 ? filteredLocalModels.map(m => {
|
|
7286
7492
|
const selected = m.id === (selectedLocalEffortFamily?.baseId || selectedModel);
|
|
7287
7493
|
return (_jsxs("button", { onClick: () => { setInferenceMode('local'); setSelectedModel(m.id); setModelListOpen(false); setEffortListOpen(false); }, role: "option", "aria-selected": selected, className: `w-full flex items-center gap-2 px-3 py-1.5 text-left transition-colors cursor-pointer ${selected ? 'bg-white/[0.06]' : 'hover:bg-white/[0.04]'}`, children: [_jsx("span", { className: `text-[11px] truncate ${selected ? 'text-white/70' : 'text-white/40'}`, children: m.name }), m.parameterSize && _jsx("span", { className: "text-[8px] text-white/20 flex-shrink-0 ml-auto", children: m.parameterSize })] }, m.id));
|
|
7288
7494
|
}) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No matching models" }))) : (_jsx("div", { className: "px-3 py-2.5 text-[10px] text-white/25 text-center", children: "No local models installed" })), _jsx("button", { onClick: () => { setModelListOpen(false); goToModelsMarketplace(); }, className: "w-full flex items-center justify-center gap-1.5 px-3 py-2 border-t border-white/[0.06] text-[10px] text-white/35 hover:text-white/60 hover:bg-white/[0.04] transition-colors cursor-pointer", children: "Browse models" })] })) })] })), _jsxs("div", { className: "space-y-0", children: [pendingTurns.length > 0 && (_jsxs("div", { className: "xeno-border-subtle rounded-t-md border border-b-0 bg-[#111111] overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2 border-b xeno-border-subtle", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("span", { className: "text-[10px] uppercase tracking-[0.14em] xeno-text-faint whitespace-nowrap", children: "Queued Messages" }), _jsx("span", { className: "text-[10px] xeno-text-muted tabular-nums whitespace-nowrap", children: pendingTurns.length })] }), _jsx("button", { onClick: () => patchSessionUi({ panelOpen: true }), className: "text-[10px] xeno-text-faint hover:text-[color:var(--xeno-ink-secondary)] transition-colors whitespace-nowrap cursor-pointer focus-visible:outline focus-visible:outline-1 focus-visible:outline-white/35", children: "Manage queue" })] }), _jsxs("div", { className: "divide-y xeno-border-subtle", children: [pendingTurns.slice(0, 3).map((turn) => (_jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [_jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-[4px] bg-white/[0.04] px-1.5 text-[10px] xeno-text-faint tabular-nums", children: new Date(turn.queuedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) }), _jsx("span", { className: "min-w-0 flex-1 text-[12px] leading-4 xeno-text-secondary truncate", children: turn.content }), _jsx("button", { onClick: () => handleRemoveQueuedConversationTurn(turn.id), className: "text-[10px] xeno-text-faint hover:text-[color:var(--xeno-ink-secondary)] transition-colors whitespace-nowrap cursor-pointer focus-visible:outline focus-visible:outline-1 focus-visible:outline-white/35", title: "Remove queued message", children: "Remove" })] }, turn.id))), pendingTurns.length > 3 && (_jsxs("div", { className: "px-3 py-2 text-[11px] xeno-text-faint", children: [pendingTurns.length - 3, " more queued"] }))] })] })), _jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", "data-agent-workspace-binding": workspaceBinding.mode, children: [_jsxs("div", { className: "inline-flex items-center rounded-md border border-white/[0.08] bg-white/[0.025] p-0.5 text-[11px]", children: [_jsx("button", { type: "button", onClick: () => { void handleSelectWorkspaceMode('chat-only'); }, disabled: isBusy || !!props.externalInputLockReason || isSelectingConversationWorkspace, className: `rounded px-2.5 py-1 font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-40 ${workspaceBinding.mode !== 'workspace'
|