clay-server 3.3.2-beta.3 → 3.4.0-beta.10
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/lib/daemon-projects.js +3 -3
- package/lib/daemon.js +1 -1
- package/lib/project-connection.js +20 -5
- package/lib/project-debate.js +4 -0
- package/lib/project-file-watch.js +77 -16
- package/lib/project-mate-interaction.js +21 -1
- package/lib/project-memory.js +3 -0
- package/lib/project-shell-command.js +160 -0
- package/lib/project-user-message.js +10 -0
- package/lib/project.js +19 -4
- package/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/app.js +28 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/command-palette.css +22 -2
- package/lib/public/css/icon-strip.css +29 -13
- package/lib/public/css/input.css +79 -0
- package/lib/public/css/mates.css +7 -0
- package/lib/public/css/menus.css +48 -14
- package/lib/public/css/messages.css +9 -0
- package/lib/public/css/pane.css +4 -0
- package/lib/public/css/pwa-mobile.css +17 -0
- package/lib/public/css/title-bar.css +19 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +43 -4
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/app-header.js +4 -0
- package/lib/public/modules/app-messages.js +28 -5
- package/lib/public/modules/app-panels.js +37 -5
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +22 -1
- package/lib/public/modules/input.js +58 -28
- package/lib/public/modules/mate-sidebar.js +17 -3
- package/lib/public/modules/notifications.js +7 -1
- package/lib/public/modules/pane-bridge.js +11 -0
- package/lib/public/modules/pane-links.js +23 -0
- package/lib/public/modules/project-switcher.js +7 -6
- package/lib/public/modules/shell-command.js +148 -0
- package/lib/public/modules/sidebar-mates.js +7 -1
- package/lib/public/modules/sidebar-mobile.js +2 -1
- package/lib/public/modules/sidebar-projects.js +34 -43
- package/lib/public/modules/sidebar-sessions.js +6 -6
- package/lib/public/modules/split-pair-ui.js +3 -2
- package/lib/public/modules/tools.js +12 -1
- package/lib/public/modules/vendor-priority.js +14 -0
- package/lib/public/modules/vendor-selection.js +20 -0
- package/lib/public/modules/worktree-location.js +17 -0
- package/lib/public/opencode-avatar.svg +4 -0
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +3 -2
- package/lib/sdk-bridge.js +59 -19
- package/lib/sdk-message-processor.js +10 -1
- package/lib/session-notes-mcp-server.js +7 -1
- package/lib/worktree.js +9 -4
- package/lib/ws-schema.js +3 -0
- package/lib/yoke/acp-agent-profiles.js +238 -0
- package/lib/yoke/acp-driver-runtime.js +50 -0
- package/lib/yoke/acp-event-normalizer.js +179 -0
- package/lib/yoke/acp-process-manager.js +264 -0
- package/lib/yoke/acp-query-handle.js +487 -0
- package/lib/yoke/adapters/acp.js +317 -0
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/adapters/codex.js +98 -3
- package/lib/yoke/adapters/copilot.js +7 -0
- package/lib/yoke/adapters/grok.js +7 -0
- package/lib/yoke/adapters/junie.js +7 -0
- package/lib/yoke/adapters/kimi.js +7 -0
- package/lib/yoke/adapters/kiro.js +4 -4
- package/lib/yoke/adapters/opencode.js +7 -0
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-app-server.js +25 -8
- package/lib/yoke/index.js +81 -13
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/interface.js +2 -0
- package/lib/yoke/kiro-acp-server.js +30 -276
- package/lib/yoke/vendor-registry.js +77 -0
- package/package.json +1 -1
|
@@ -22,7 +22,7 @@ import { handleFindInSessionResults } from './session-search.js';
|
|
|
22
22
|
import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.js';
|
|
23
23
|
import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
|
|
24
24
|
import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
|
|
25
|
-
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled } from './input.js';
|
|
25
|
+
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent } from './input.js';
|
|
26
26
|
import { startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, closeToolGroup, removeToolFromGroup, resetToolState, getTools, getPlanContent, setPlanContent, renderPlanBanner, renderPlanCard, getTodoTools, handleTodoWrite, handleTaskCreate, handleTaskUpdate, applyDeadSessionTodoCompaction, isPlanFilePath, enableMainInput, addTurnMeta, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, initSubagentStop, updateSubagentProgress, updateSubagentTaskStatus, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionCancelled, markPermissionResolved, renderElicitationRequest, markElicitationResolved, renderUserDialogRequest, markUserDialogResolved, updateThinkingTokens } from './tools.js';
|
|
27
27
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
28
28
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
|
|
@@ -42,6 +42,7 @@ import { checkAdminAccess } from './admin.js';
|
|
|
42
42
|
import { mateAvatarUrl } from './avatar.js';
|
|
43
43
|
import { showImageModal, sendExtensionCommand, handleMcpToolCallMessage } from './app-misc.js';
|
|
44
44
|
import { handleMcpServersState } from './mcp-ui.js';
|
|
45
|
+
import { handleShellCommandResult } from './shell-command.js';
|
|
45
46
|
import { handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, isSchedulerOpen, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './scheduler.js';
|
|
46
47
|
|
|
47
48
|
// --- App module imports ---
|
|
@@ -51,6 +52,7 @@ import { setStatus } from './app-connection.js';
|
|
|
51
52
|
import { handleWhatsNewState, handleWhatsNewSeenResult, setKnownEntries as setWhatsNewKnownEntries } from './whats-new.js';
|
|
52
53
|
import { closeArticle as closeWhatsNewArticle } from './whats-new-article.js';
|
|
53
54
|
import { resolvePaneSession, resolveSwitchedVendor } from './pane-session.js';
|
|
55
|
+
import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
|
|
54
56
|
import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel } from './app-panels.js';
|
|
55
57
|
import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress } from './app-projects.js';
|
|
56
58
|
import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
|
|
@@ -413,6 +415,15 @@ export function processMessage(msg) {
|
|
|
413
415
|
break;
|
|
414
416
|
|
|
415
417
|
case "model_info": {
|
|
418
|
+
// Installation state applies across vendors. Keep it even when the
|
|
419
|
+
// model payload itself is stale for the active session.
|
|
420
|
+
var _vendorState = {};
|
|
421
|
+
if (msg.availableVendors) _vendorState.availableVendors = msg.availableVendors;
|
|
422
|
+
if (msg.installedVendors) _vendorState.installedVendors = msg.installedVendors;
|
|
423
|
+
if (Object.keys(_vendorState).length > 0) {
|
|
424
|
+
store.set(_vendorState);
|
|
425
|
+
selectDefaultVendorForBlankSession();
|
|
426
|
+
}
|
|
416
427
|
// Drop stale model_info from a vendor that doesn't match the active
|
|
417
428
|
// session's vendor. On high-latency connections, the server's default-
|
|
418
429
|
// adapter model_info can arrive after session_switched has already
|
|
@@ -446,8 +457,6 @@ export function processMessage(msg) {
|
|
|
446
457
|
_miUpdate.currentModel = store.get('currentModel');
|
|
447
458
|
}
|
|
448
459
|
if (msg.vendor && !store.get('vendorSelectionLocked')) _miUpdate.currentVendor = msg.vendor;
|
|
449
|
-
if (msg.availableVendors) _miUpdate.availableVendors = msg.availableVendors;
|
|
450
|
-
if (msg.installedVendors) _miUpdate.installedVendors = msg.installedVendors;
|
|
451
460
|
if (msg.capabilities) _miUpdate.vendorCapabilities = msg.capabilities;
|
|
452
461
|
store.set(_miUpdate);
|
|
453
462
|
updateSettingsModels(_modelVal, msg.models || []);
|
|
@@ -660,7 +669,7 @@ export function processMessage(msg) {
|
|
|
660
669
|
var _effectiveTerminalId = (typeof msg.runtimeTerminalId === "number")
|
|
661
670
|
? msg.runtimeTerminalId
|
|
662
671
|
: (typeof msg.terminalId === "number" ? msg.terminalId : null);
|
|
663
|
-
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || store.get('currentModel'), currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode') });
|
|
672
|
+
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || store.get('currentModel'), currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode') });
|
|
664
673
|
// TUI sessions swap the chat UI for an embedded xterm running
|
|
665
674
|
// `claude` inside a real PTY. Mount or tear down before the rest of
|
|
666
675
|
// the chat-side bookkeeping runs so we don't waste work on hidden DOM.
|
|
@@ -705,7 +714,7 @@ export function processMessage(msg) {
|
|
|
705
714
|
}
|
|
706
715
|
}
|
|
707
716
|
if (!msg.hasHistory && !msg.vendor) {
|
|
708
|
-
|
|
717
|
+
selectDefaultVendorForBlankSession();
|
|
709
718
|
}
|
|
710
719
|
// Vendor toggle visibility + active-vendor indicator next to the
|
|
711
720
|
// model chip.
|
|
@@ -1209,6 +1218,15 @@ export function processMessage(msg) {
|
|
|
1209
1218
|
addConflictMessage(msg);
|
|
1210
1219
|
break;
|
|
1211
1220
|
|
|
1221
|
+
case "session_writer_conflict":
|
|
1222
|
+
removeMatePreThinking();
|
|
1223
|
+
setActivity(null);
|
|
1224
|
+
stopThinking();
|
|
1225
|
+
markAllToolsDone();
|
|
1226
|
+
closeToolGroup();
|
|
1227
|
+
addSystemMessage(msg.text || "This session is already open in another process.", true);
|
|
1228
|
+
break;
|
|
1229
|
+
|
|
1212
1230
|
case "context_overflow":
|
|
1213
1231
|
removeMatePreThinking();
|
|
1214
1232
|
setActivity(null);
|
|
@@ -1371,6 +1389,11 @@ export function processMessage(msg) {
|
|
|
1371
1389
|
updateTerminalList(msg.terminals);
|
|
1372
1390
|
break;
|
|
1373
1391
|
|
|
1392
|
+
case "shell_command_result":
|
|
1393
|
+
handleShellCommandResult(msg);
|
|
1394
|
+
if (!msg.error) sendShellResultToAgent(msg);
|
|
1395
|
+
break;
|
|
1396
|
+
|
|
1374
1397
|
case "context_sources_state":
|
|
1375
1398
|
handleContextSourcesState(msg);
|
|
1376
1399
|
break;
|
|
@@ -5,7 +5,7 @@ import { refreshIcons } from "./icons.js";
|
|
|
5
5
|
import { escapeHtml, showToast } from "./utils.js";
|
|
6
6
|
import { store } from './store.js';
|
|
7
7
|
import { getWs } from './ws-ref.js';
|
|
8
|
-
import { VENDOR_NAMES } from './app-rendering.js';
|
|
8
|
+
import { VENDOR_NAMES, isExperimentalVendor } from './app-rendering.js';
|
|
9
9
|
import { reportPaneContext } from './pane-bridge.js';
|
|
10
10
|
|
|
11
11
|
// --- Module-owned state (not in store) ---
|
|
@@ -89,6 +89,8 @@ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
89
89
|
var EFFORT_LEVELS_BY_VENDOR = {
|
|
90
90
|
claude: ["low", "medium", "high", "xhigh", "max"],
|
|
91
91
|
codex: ["minimal", "low", "medium", "high", "xhigh"],
|
|
92
|
+
antigravity: [],
|
|
93
|
+
opencode: [],
|
|
92
94
|
kiro: ["low", "medium", "high", "xhigh", "max"],
|
|
93
95
|
};
|
|
94
96
|
var THINKING_OPTIONS = ["disabled", "adaptive", "budget"];
|
|
@@ -441,8 +443,26 @@ export function initPanels() {
|
|
|
441
443
|
var vendorToggleWrap = $("vendor-toggle-wrap");
|
|
442
444
|
var vendorBtnClaude = $("vendor-btn-claude");
|
|
443
445
|
var vendorBtnCodex = $("vendor-btn-codex");
|
|
446
|
+
var vendorBtnAntigravity = $("vendor-btn-antigravity");
|
|
447
|
+
var vendorBtnOpenCode = $("vendor-btn-opencode");
|
|
448
|
+
var vendorBtnKimi = $("vendor-btn-kimi");
|
|
449
|
+
var vendorBtnGrok = $("vendor-btn-grok");
|
|
450
|
+
var vendorBtnCopilot = $("vendor-btn-copilot");
|
|
451
|
+
var vendorBtnQwen = $("vendor-btn-qwen");
|
|
452
|
+
var vendorBtnJunie = $("vendor-btn-junie");
|
|
444
453
|
var vendorBtnKiro = $("vendor-btn-kiro");
|
|
445
|
-
var vendorBtns = {
|
|
454
|
+
var vendorBtns = {
|
|
455
|
+
claude: vendorBtnClaude,
|
|
456
|
+
codex: vendorBtnCodex,
|
|
457
|
+
antigravity: vendorBtnAntigravity,
|
|
458
|
+
opencode: vendorBtnOpenCode,
|
|
459
|
+
kimi: vendorBtnKimi,
|
|
460
|
+
grok: vendorBtnGrok,
|
|
461
|
+
copilot: vendorBtnCopilot,
|
|
462
|
+
qwen: vendorBtnQwen,
|
|
463
|
+
junie: vendorBtnJunie,
|
|
464
|
+
kiro: vendorBtnKiro,
|
|
465
|
+
};
|
|
446
466
|
|
|
447
467
|
function updateVendorToggle() {
|
|
448
468
|
var installed = store.get('installedVendors') || [];
|
|
@@ -456,7 +476,10 @@ export function initPanels() {
|
|
|
456
476
|
var isInstalled = installed.indexOf(v) !== -1;
|
|
457
477
|
btn.classList.toggle("active", v === current);
|
|
458
478
|
btn.classList.toggle("disabled", !isInstalled);
|
|
459
|
-
btn.
|
|
479
|
+
btn.classList.toggle("experimental", isExperimentalVendor(v));
|
|
480
|
+
var title = isInstalled ? (VENDOR_NAMES[v] || v) : (VENDOR_NAMES[v] || v) + " is not installed";
|
|
481
|
+
if (isExperimentalVendor(v)) title += " · Experimental integration; not yet validated through direct use testing";
|
|
482
|
+
btn.title = title;
|
|
460
483
|
}
|
|
461
484
|
}
|
|
462
485
|
|
|
@@ -464,13 +487,20 @@ export function initPanels() {
|
|
|
464
487
|
if (vendor === (store.get('currentVendor') || "claude")) return;
|
|
465
488
|
var installed = store.get('installedVendors') || [];
|
|
466
489
|
if (installed.indexOf(vendor) === -1) return;
|
|
467
|
-
store.set({ currentVendor: vendor, currentModel: "", currentModels: [], vendorSelectionLocked: true });
|
|
490
|
+
store.set({ currentVendor: vendor, currentModel: "", currentModels: [], vendorSelectionLocked: true, sessionVendorBound: true });
|
|
468
491
|
var ws = getWs();
|
|
469
492
|
if (ws) ws.send(JSON.stringify({ type: "set_vendor", vendor: vendor }));
|
|
470
493
|
}
|
|
471
494
|
|
|
472
495
|
if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
|
|
473
496
|
if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
|
|
497
|
+
if (vendorBtnAntigravity) vendorBtnAntigravity.addEventListener("click", function() { onVendorClick("antigravity"); });
|
|
498
|
+
if (vendorBtnOpenCode) vendorBtnOpenCode.addEventListener("click", function() { onVendorClick("opencode"); });
|
|
499
|
+
if (vendorBtnKimi) vendorBtnKimi.addEventListener("click", function() { onVendorClick("kimi"); });
|
|
500
|
+
if (vendorBtnGrok) vendorBtnGrok.addEventListener("click", function() { onVendorClick("grok"); });
|
|
501
|
+
if (vendorBtnCopilot) vendorBtnCopilot.addEventListener("click", function() { onVendorClick("copilot"); });
|
|
502
|
+
if (vendorBtnQwen) vendorBtnQwen.addEventListener("click", function() { onVendorClick("qwen"); });
|
|
503
|
+
if (vendorBtnJunie) vendorBtnJunie.addEventListener("click", function() { onVendorClick("junie"); });
|
|
474
504
|
if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
|
|
475
505
|
|
|
476
506
|
// --- Reactive UI sync ---
|
|
@@ -849,7 +879,9 @@ export function updateContextPanel() {
|
|
|
849
879
|
hCtxEl = document.createElement("div");
|
|
850
880
|
hCtxEl.className = "header-context";
|
|
851
881
|
hCtxEl.innerHTML = '<div class="header-context-bar"><div class="header-context-fill"></div></div><span class="header-context-label"></span>';
|
|
852
|
-
|
|
882
|
+
var workerBtn = document.getElementById("header-add-worker-btn");
|
|
883
|
+
var contextAnchor = workerBtn && workerBtn.parentNode === statusArea ? workerBtn.nextSibling : statusArea.firstChild;
|
|
884
|
+
statusArea.insertBefore(hCtxEl, contextAnchor);
|
|
853
885
|
hCtxEl.addEventListener("mouseenter", function() {
|
|
854
886
|
if (store.get('richContextUsage')) {
|
|
855
887
|
showCtxPopover();
|
|
@@ -24,6 +24,7 @@ import { isSchedulerOpen, closeScheduler, resetScheduler } from './scheduler.js'
|
|
|
24
24
|
import { connect, cancelReconnect, setStatus } from './app-connection.js';
|
|
25
25
|
import { setTurnCounter, setPrependAnchor, setActivityEl, setIsUserScrolledUp, hideSuggestionChips } from './app-rendering.js';
|
|
26
26
|
import { resetToolState, enableMainInput, resetTurnMetaCost } from './tools.js';
|
|
27
|
+
import { resetShellCommand } from './shell-command.js';
|
|
27
28
|
import { clearPendingImages } from './input.js';
|
|
28
29
|
import { clearAllMentionActive } from './sidebar-mates.js';
|
|
29
30
|
import { setRewindMode } from './rewind.js';
|
|
@@ -321,7 +322,7 @@ export function renderProjectList() {
|
|
|
321
322
|
isWorktree: p.isWorktree || false,
|
|
322
323
|
parentSlug: p.parentSlug || null,
|
|
323
324
|
branch: p.branch || null,
|
|
324
|
-
|
|
325
|
+
worktreeExternal: p.worktreeExternal === true,
|
|
325
326
|
};
|
|
326
327
|
});
|
|
327
328
|
var st = store.snap();
|
|
@@ -368,6 +369,7 @@ export function resetClientState() {
|
|
|
368
369
|
store.set({ currentFullText: "" });
|
|
369
370
|
resetToolState();
|
|
370
371
|
clearPendingImages();
|
|
372
|
+
resetShellCommand();
|
|
371
373
|
clearAllMentionActive();
|
|
372
374
|
setActivityEl(null);
|
|
373
375
|
store.set({ processing: false });
|
|
@@ -13,27 +13,48 @@ import { showImageModal, showPasteModal } from './app-misc.js';
|
|
|
13
13
|
import { sendMessage, hasSendableContent } from './input.js';
|
|
14
14
|
import { getChatLayout } from './theme.js';
|
|
15
15
|
import { getScheduledMsgEl } from './app-rate-limit.js';
|
|
16
|
+
export { VENDOR_ORDER, EXPERIMENTAL_VENDORS, isExperimentalVendor } from './vendor-priority.js';
|
|
16
17
|
|
|
17
18
|
// Keep these client constants aligned with lib/yoke/vendor-registry.js. The
|
|
18
19
|
// browser cannot import the server's CommonJS registry directly.
|
|
19
20
|
export var VENDOR_AVATARS = {
|
|
20
21
|
claude: "/claude-code-avatar.png",
|
|
21
22
|
codex: "/codex-avatar.png",
|
|
23
|
+
antigravity: "/antigravity-avatar.png",
|
|
24
|
+
opencode: "/opencode-avatar.svg",
|
|
25
|
+
kimi: "/kimi-avatar.svg",
|
|
26
|
+
grok: "/grok-avatar.svg",
|
|
27
|
+
copilot: "/copilot-avatar.svg",
|
|
28
|
+
qwen: "/qwen-avatar.svg",
|
|
29
|
+
junie: "/junie-avatar.svg",
|
|
22
30
|
kiro: "/kiro-avatar.svg",
|
|
23
31
|
};
|
|
24
32
|
export var VENDOR_NAMES = {
|
|
25
33
|
claude: "Claude Code",
|
|
26
34
|
codex: "Codex",
|
|
35
|
+
antigravity: "Antigravity CLI",
|
|
36
|
+
opencode: "OpenCode",
|
|
37
|
+
kimi: "Kimi Code",
|
|
38
|
+
grok: "Grok Build",
|
|
39
|
+
copilot: "GitHub Copilot CLI",
|
|
40
|
+
qwen: "Qwen Code",
|
|
41
|
+
junie: "Junie CLI",
|
|
27
42
|
kiro: "Kiro CLI",
|
|
28
43
|
};
|
|
29
44
|
// Display order for every vendor Clay knows about, installed or not. Pickers
|
|
30
45
|
// render the full list so a missing CLI reads as "not installed yet" rather
|
|
31
46
|
// than "Clay doesn't support it".
|
|
32
|
-
export var VENDOR_ORDER = ["claude", "codex", "kiro"];
|
|
33
47
|
// Where to send the user when they pick a vendor whose CLI isn't installed.
|
|
34
48
|
export var VENDOR_HOMEPAGES = {
|
|
35
49
|
claude: "https://claude.com/product/claude-code",
|
|
36
50
|
codex: "https://openai.com/codex/",
|
|
51
|
+
antigravity: "https://antigravity.google/product/antigravity-cli",
|
|
52
|
+
opencode: "https://opencode.ai/",
|
|
53
|
+
kimi: "https://www.kimi.com/code/",
|
|
54
|
+
grok: "https://docs.x.ai/build/overview",
|
|
55
|
+
copilot: "https://github.com/features/copilot/cli",
|
|
56
|
+
qwen: "https://qwenlm.github.io/qwen-code-docs/",
|
|
57
|
+
junie: "https://junie.jetbrains.com/",
|
|
37
58
|
kiro: "https://kiro.dev/",
|
|
38
59
|
};
|
|
39
60
|
var NEW_MSG_BTN_DEFAULT = "\u2193 Latest";
|
|
@@ -7,6 +7,7 @@ import { mateAvatarUrl } from './avatar.js';
|
|
|
7
7
|
import { tuiIsActive, tuiSubmitText } from './session-tui-view.js';
|
|
8
8
|
import { VENDOR_AVATARS, VENDOR_NAMES } from './app-rendering.js';
|
|
9
9
|
import { showToast } from './utils.js';
|
|
10
|
+
import { isShellCommandMode, submitShellCommand } from './shell-command.js';
|
|
10
11
|
|
|
11
12
|
var ctx;
|
|
12
13
|
|
|
@@ -75,6 +76,37 @@ export var builtinCommands = [
|
|
|
75
76
|
{ name: "status", desc: "Process status and resource usage" },
|
|
76
77
|
];
|
|
77
78
|
|
|
79
|
+
function commitVendorForTurn() {
|
|
80
|
+
var committedVendor = store.get('currentVendor');
|
|
81
|
+
var vendorToggle = document.getElementById("vendor-toggle-wrap");
|
|
82
|
+
var activeIndicator = document.getElementById("active-vendor-indicator");
|
|
83
|
+
var activeIcon = document.getElementById("active-vendor-icon");
|
|
84
|
+
if (committedVendor) {
|
|
85
|
+
if (vendorToggle) {
|
|
86
|
+
vendorToggle.classList.add("hidden");
|
|
87
|
+
vendorToggle.classList.remove("locked");
|
|
88
|
+
}
|
|
89
|
+
if (activeIndicator && activeIcon) {
|
|
90
|
+
activeIcon.src = VENDOR_AVATARS[committedVendor] || VENDOR_AVATARS.claude;
|
|
91
|
+
activeIcon.alt = VENDOR_NAMES[committedVendor] || VENDOR_NAMES.claude;
|
|
92
|
+
activeIndicator.title = (VENDOR_NAMES[committedVendor] || VENDOR_NAMES.claude) + " session";
|
|
93
|
+
activeIndicator.classList.remove("hidden");
|
|
94
|
+
}
|
|
95
|
+
} else if (vendorToggle) {
|
|
96
|
+
vendorToggle.classList.remove("hidden");
|
|
97
|
+
vendorToggle.classList.add("locked");
|
|
98
|
+
}
|
|
99
|
+
store.set({ vendorSelectionLocked: false, sessionHasHistory: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function showPreThinkingForTurn() {
|
|
103
|
+
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
104
|
+
ctx.showMatePreThinking();
|
|
105
|
+
} else if (ctx.showClaudePreThinking) {
|
|
106
|
+
ctx.showClaudePreThinking();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
78
110
|
// --- Send ---
|
|
79
111
|
export function sendMessage() {
|
|
80
112
|
// Debate ended mode intercept: route to resume handler
|
|
@@ -92,6 +124,14 @@ export function sendMessage() {
|
|
|
92
124
|
ctx.handleDebateFloorSend();
|
|
93
125
|
return;
|
|
94
126
|
}
|
|
127
|
+
if (isShellCommandMode()) {
|
|
128
|
+
var shellText = ctx.inputEl.value.trim();
|
|
129
|
+
if (!shellText || !submitShellCommand(shellText)) return;
|
|
130
|
+
ctx.inputEl.value = "";
|
|
131
|
+
sendInputSync();
|
|
132
|
+
autoResize();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
95
135
|
// DM mode intercept: if in DM mode, route to DM handler instead
|
|
96
136
|
if (ctx.isDmMode && ctx.isDmMode() && ctx.handleDmSend) {
|
|
97
137
|
ctx.handleDmSend();
|
|
@@ -304,36 +344,10 @@ export function sendMessage() {
|
|
|
304
344
|
// Bumping sessionHasHistory drives in-session lock states (e.g. the
|
|
305
345
|
// Codex model picker that becomes informational once the thread is
|
|
306
346
|
// bound to a model).
|
|
307
|
-
|
|
308
|
-
var _vtw2 = document.getElementById("vendor-toggle-wrap");
|
|
309
|
-
var _avi = document.getElementById("active-vendor-indicator");
|
|
310
|
-
var _avIcon = document.getElementById("active-vendor-icon");
|
|
311
|
-
if (_committedVendor) {
|
|
312
|
-
if (_vtw2) {
|
|
313
|
-
_vtw2.classList.add("hidden");
|
|
314
|
-
_vtw2.classList.remove("locked");
|
|
315
|
-
}
|
|
316
|
-
if (_avi && _avIcon) {
|
|
317
|
-
_avIcon.src = VENDOR_AVATARS[_committedVendor] || VENDOR_AVATARS.claude;
|
|
318
|
-
_avIcon.alt = VENDOR_NAMES[_committedVendor] || VENDOR_NAMES.claude;
|
|
319
|
-
_avi.title = (VENDOR_NAMES[_committedVendor] || VENDOR_NAMES.claude) + " session";
|
|
320
|
-
_avi.classList.remove("hidden");
|
|
321
|
-
}
|
|
322
|
-
} else if (_vtw2) {
|
|
323
|
-
// No committed vendor (defensive — shouldn't happen because the
|
|
324
|
-
// input is otherwise gated on a vendor pick). Fall back to the
|
|
325
|
-
// locked toggle so the running vendor is still visible.
|
|
326
|
-
_vtw2.classList.remove("hidden");
|
|
327
|
-
_vtw2.classList.add("locked");
|
|
328
|
-
}
|
|
329
|
-
store.set({ vendorSelectionLocked: false, sessionHasHistory: true });
|
|
347
|
+
commitVendorForTurn();
|
|
330
348
|
|
|
331
349
|
// Show pre-thinking dots before server responds
|
|
332
|
-
|
|
333
|
-
ctx.showMatePreThinking();
|
|
334
|
-
} else if (ctx.showClaudePreThinking) {
|
|
335
|
-
ctx.showClaudePreThinking();
|
|
336
|
-
}
|
|
350
|
+
showPreThinkingForTurn();
|
|
337
351
|
|
|
338
352
|
ctx.inputEl.value = "";
|
|
339
353
|
sendInputSync();
|
|
@@ -354,6 +368,22 @@ export function sendTextMessage(text) {
|
|
|
354
368
|
return true;
|
|
355
369
|
}
|
|
356
370
|
|
|
371
|
+
export function sendShellResultToAgent(msg) {
|
|
372
|
+
if (!ctx || !ctx.connected || !msg || msg.error || !msg.command) return false;
|
|
373
|
+
if (String(msg.sessionId) !== String(store.get("activeSessionId"))) return false;
|
|
374
|
+
var payload = {
|
|
375
|
+
type: "message",
|
|
376
|
+
text: "$ " + msg.command,
|
|
377
|
+
shellCommandResponse: true,
|
|
378
|
+
};
|
|
379
|
+
var selectedVendor = store.get("currentVendor") || null;
|
|
380
|
+
if (selectedVendor) payload.vendor = selectedVendor;
|
|
381
|
+
ctx.ws.send(JSON.stringify(payload));
|
|
382
|
+
commitVendorForTurn();
|
|
383
|
+
showPreThinkingForTurn();
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
|
|
357
387
|
export function autoResize() {
|
|
358
388
|
ctx.inputEl.style.height = "auto";
|
|
359
389
|
ctx.inputEl.style.height = Math.min(ctx.inputEl.scrollHeight, 120) + "px";
|
|
@@ -6,6 +6,7 @@ import { hideKnowledge } from './mate-knowledge.js';
|
|
|
6
6
|
import { isSchedulerOpen, closeScheduler } from './scheduler.js';
|
|
7
7
|
import { hideNotes } from './sticky-notes.js';
|
|
8
8
|
import { openSearch as openSessionSearch } from './session-search.js';
|
|
9
|
+
import { isExperimentalVendor } from './vendor-priority.js';
|
|
9
10
|
|
|
10
11
|
var getWs = null;
|
|
11
12
|
var currentMateId = null;
|
|
@@ -149,16 +150,29 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
149
150
|
if (mateVendorWrap) {
|
|
150
151
|
var available = store.get('availableVendors') || [];
|
|
151
152
|
var mateVendor = mateData.vendor || "claude";
|
|
152
|
-
var vendorIcons = {
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
var vendorIcons = {
|
|
154
|
+
claude: "/claude-code-avatar.png",
|
|
155
|
+
codex: "/codex-avatar.png",
|
|
156
|
+
antigravity: "/antigravity-avatar.png",
|
|
157
|
+
opencode: "/opencode-avatar.svg",
|
|
158
|
+
kimi: "/kimi-avatar.svg",
|
|
159
|
+
grok: "/grok-avatar.svg",
|
|
160
|
+
copilot: "/copilot-avatar.svg",
|
|
161
|
+
qwen: "/qwen-avatar.svg",
|
|
162
|
+
junie: "/junie-avatar.svg",
|
|
163
|
+
kiro: "/kiro-avatar.svg",
|
|
164
|
+
};
|
|
165
|
+
var vendorNames = { claude: "Claude Code", codex: "Codex", grok: "Grok Build", kimi: "Kimi Code", copilot: "GitHub Copilot CLI", qwen: "Qwen Code", junie: "Junie CLI", antigravity: "Antigravity CLI", opencode: "OpenCode", kiro: "Kiro CLI" };
|
|
166
|
+
var vendorKeys = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
|
|
155
167
|
mateVendorWrap.innerHTML = "";
|
|
156
168
|
for (var vi = 0; vi < vendorKeys.length; vi++) {
|
|
157
169
|
var vk = vendorKeys[vi];
|
|
158
170
|
var vBtn = document.createElement("button");
|
|
159
171
|
vBtn.className = "mate-vendor-btn" + (vk === mateVendor ? " active" : "");
|
|
172
|
+
if (isExperimentalVendor(vk)) vBtn.classList.add("experimental");
|
|
160
173
|
if (available.indexOf(vk) === -1) vBtn.classList.add("disabled");
|
|
161
174
|
vBtn.dataset.vendor = vk;
|
|
175
|
+
vBtn.title = vendorNames[vk] + (isExperimentalVendor(vk) ? " · Experimental integration; not yet validated through direct use testing" : "");
|
|
162
176
|
vBtn.innerHTML = '<img src="' + vendorIcons[vk] + '" class="mate-vendor-icon" alt="' + vendorNames[vk] + '"><span class="mate-vendor-label">' + vendorNames[vk] + '</span>';
|
|
163
177
|
vBtn.addEventListener("click", function() {
|
|
164
178
|
var v = this.dataset.vendor;
|
|
@@ -72,6 +72,7 @@ export function initNotifications(_ctx) {
|
|
|
72
72
|
if (window.visualViewport) {
|
|
73
73
|
var layout = $("layout");
|
|
74
74
|
var mobileTabBar = document.getElementById("mobile-tab-bar");
|
|
75
|
+
var keyboardWasOpen = false;
|
|
75
76
|
// Capture initial viewport height before any keyboard interaction.
|
|
76
77
|
// In iOS PWA standalone mode, window.innerHeight shrinks when the
|
|
77
78
|
// keyboard opens, so we need a stable baseline for comparison.
|
|
@@ -86,7 +87,12 @@ export function initNotifications(_ctx) {
|
|
|
86
87
|
// Toggle class so CSS can remove the tab-bar bottom padding while keyboard is up
|
|
87
88
|
var keyboardOpen = vv.height < stableViewportHeight - 100;
|
|
88
89
|
document.body.classList.toggle("keyboard-open", keyboardOpen);
|
|
89
|
-
if (
|
|
90
|
+
if (keyboardOpen && !keyboardWasOpen && ctx.forceScrollToBottom) {
|
|
91
|
+
requestAnimationFrame(function() { ctx.forceScrollToBottom(); });
|
|
92
|
+
} else if (!keyboardOpen) {
|
|
93
|
+
ctx.scrollToBottom();
|
|
94
|
+
}
|
|
95
|
+
keyboardWasOpen = keyboardOpen;
|
|
90
96
|
// Hide tab bar when software keyboard is open
|
|
91
97
|
if (mobileTabBar) {
|
|
92
98
|
if (keyboardOpen) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { store } from './store.js';
|
|
7
7
|
import { applyContextView, getContextView } from './app-panels.js';
|
|
8
|
+
import { forceExternalLinkToNewTab } from './pane-links.js';
|
|
8
9
|
|
|
9
10
|
var panelOpen = false;
|
|
10
11
|
|
|
@@ -29,9 +30,19 @@ function togglePaneContextPanel() {
|
|
|
29
30
|
applyContextView(panelOpen ? "panel" : "off");
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function preparePaneLink(event) {
|
|
34
|
+
var target = event.target;
|
|
35
|
+
if (!target || typeof target.closest !== "function") return;
|
|
36
|
+
var anchor = target.closest("a[href]");
|
|
37
|
+
if (!anchor || anchor.hasAttribute("download")) return;
|
|
38
|
+
forceExternalLinkToNewTab(anchor, window.location.href);
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
export function initPaneBridge() {
|
|
33
42
|
if (!store.get('paneMode')) return;
|
|
34
43
|
panelOpen = getContextView() === "panel";
|
|
44
|
+
document.addEventListener("click", preparePaneLink, true);
|
|
45
|
+
document.addEventListener("auxclick", preparePaneLink, true);
|
|
35
46
|
window.addEventListener("message", function (event) {
|
|
36
47
|
if (event.origin !== window.location.origin) return;
|
|
37
48
|
var msg = event.data;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function isExternalWebLink(href, currentHref) {
|
|
2
|
+
if (!href) return false;
|
|
3
|
+
try {
|
|
4
|
+
var url = new URL(href, currentHref);
|
|
5
|
+
var currentUrl = new URL(currentHref);
|
|
6
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
7
|
+
return url.origin !== currentUrl.origin;
|
|
8
|
+
} catch (err) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function forceExternalLinkToNewTab(anchor, currentHref) {
|
|
14
|
+
if (!anchor || !isExternalWebLink(anchor.href, currentHref)) return false;
|
|
15
|
+
|
|
16
|
+
var rel = (anchor.getAttribute("rel") || "").split(/\s+/).filter(Boolean);
|
|
17
|
+
if (rel.indexOf("noopener") === -1) rel.push("noopener");
|
|
18
|
+
if (rel.indexOf("noreferrer") === -1) rel.push("noreferrer");
|
|
19
|
+
|
|
20
|
+
anchor.setAttribute("target", "_blank");
|
|
21
|
+
anchor.setAttribute("rel", rel.join(" "));
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
@@ -19,6 +19,7 @@ import { getCachedProjects, switchProject } from './app-projects.js';
|
|
|
19
19
|
import { openDm } from './app-dm.js';
|
|
20
20
|
import { mateAvatarUrl } from './avatar.js';
|
|
21
21
|
import { parseEmojis } from './markdown.js';
|
|
22
|
+
import { isExternalWorktree, externalWorktreeTooltip, appendExternalWorktreeBadge } from './worktree-location.js';
|
|
22
23
|
|
|
23
24
|
var _projectMru = []; // project slugs, most recent first
|
|
24
25
|
var _mateMru = []; // mate ids (mate_XXX), most recent first
|
|
@@ -257,18 +258,16 @@ function buildProjectEntries() {
|
|
|
257
258
|
// Keep natural (server-provided) order so the list looks the same
|
|
258
259
|
// every time the switcher opens. MRU only drives initial highlight.
|
|
259
260
|
return projects.map(function (p) {
|
|
260
|
-
|
|
261
|
-
// unmounted or moved); those are effectively unreachable from
|
|
262
|
-
// this session and shouldn't be switchable.
|
|
263
|
-
var unreachable = p.isWorktree && p.worktreeAccessible === false;
|
|
261
|
+
var external = isExternalWorktree(p);
|
|
264
262
|
return {
|
|
265
263
|
key: p.slug,
|
|
266
264
|
title: p.title || p.project || p.slug,
|
|
267
265
|
icon: p.icon || null,
|
|
268
266
|
fallbackIcon: 'folder',
|
|
269
267
|
isCurrent: p.slug === store.get('currentSlug'),
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
external: external,
|
|
269
|
+
disabled: false,
|
|
270
|
+
disabledReason: null,
|
|
272
271
|
};
|
|
273
272
|
});
|
|
274
273
|
}
|
|
@@ -336,6 +335,7 @@ function buildEntryNode(entry, index) {
|
|
|
336
335
|
item.className = 'cmd-palette-item project-switcher-item';
|
|
337
336
|
if (entry.disabled) item.classList.add('disabled');
|
|
338
337
|
if (entry.disabled && entry.disabledReason) item.title = entry.disabledReason;
|
|
338
|
+
if (entry.external) item.title = externalWorktreeTooltip(entry.title);
|
|
339
339
|
item.dataset.index = String(index);
|
|
340
340
|
|
|
341
341
|
var iconWrap = document.createElement('div');
|
|
@@ -353,6 +353,7 @@ function buildEntryNode(entry, index) {
|
|
|
353
353
|
fallback.setAttribute('data-lucide', entry.fallbackIcon || 'folder');
|
|
354
354
|
iconWrap.appendChild(fallback);
|
|
355
355
|
}
|
|
356
|
+
if (entry.external) appendExternalWorktreeBadge(iconWrap, 'project-switcher-external-badge');
|
|
356
357
|
item.appendChild(iconWrap);
|
|
357
358
|
|
|
358
359
|
var body = document.createElement('div');
|