clay-server 4.1.0-beta.8 → 4.1.0
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/context-usage.js +47 -0
- package/lib/issues-mcp-server.js +1 -1
- package/lib/issues-service.js +3 -1
- package/lib/loop-registry-occurrence.js +1 -0
- package/lib/notes.js +20 -3
- package/lib/os-users.js +6 -0
- package/lib/project-connection.js +14 -3
- package/lib/project-file-path.js +13 -0
- package/lib/project-file-watch.js +19 -8
- package/lib/project-filesystem.js +10 -16
- package/lib/project-http.js +53 -48
- package/lib/project-logs-mcp-server.js +8 -8
- package/lib/project-logs-schema.js +1 -1
- package/lib/project-logs-service.js +46 -13
- package/lib/project-logs-store.js +17 -15
- package/lib/project-logs.js +70 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-notifications.js +1 -1
- package/lib/project-pending-message-queue.js +71 -7
- package/lib/project-scheduled-messages.js +235 -52
- package/lib/project-scheduled-result-store.js +131 -0
- package/lib/project-scheduled-task-execution.js +35 -2
- package/lib/project-session-notes.js +11 -4
- package/lib/project-sessions.js +7 -0
- package/lib/project-user-message.js +72 -5
- package/lib/project.js +60 -5
- package/lib/public/app.js +27 -12
- package/lib/public/css/avatar-imprints.css +1 -1
- package/lib/public/css/command-palette.css +20 -0
- package/lib/public/css/git-panel.css +13 -12
- package/lib/public/css/git-placard.css +4 -4
- package/lib/public/css/input.css +0 -109
- package/lib/public/css/issues.css +78 -31
- package/lib/public/css/mobile-nav.css +7 -0
- package/lib/public/css/notifications-center.css +3 -3
- package/lib/public/css/pending-message-queue.css +3 -0
- package/lib/public/css/project-logs.css +15 -0
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +29 -0
- package/lib/public/modules/app-connection.js +9 -1
- package/lib/public/modules/app-favicon.js +10 -0
- package/lib/public/modules/app-messages.js +49 -37
- package/lib/public/modules/app-notifications.js +2 -0
- package/lib/public/modules/app-panels.js +77 -23
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/app-rate-limit.js +148 -243
- package/lib/public/modules/avatar-imprint.js +25 -70
- package/lib/public/modules/avatar.js +12 -4
- package/lib/public/modules/chat-render-runtime.js +20 -2
- package/lib/public/modules/clay-issue-links.js +23 -2
- package/lib/public/modules/clay-log-links.js +11 -1
- package/lib/public/modules/context-view-preference.js +143 -0
- package/lib/public/modules/default-vendor.js +116 -0
- package/lib/public/modules/filebrowser-tabs.js +1 -0
- package/lib/public/modules/filebrowser.js +45 -18
- package/lib/public/modules/input.js +81 -20
- package/lib/public/modules/issues-render.js +36 -10
- package/lib/public/modules/issues.js +95 -13
- package/lib/public/modules/markdown.js +3 -1
- package/lib/public/modules/pane-bridge.js +14 -8
- package/lib/public/modules/pane-issue-bridge.js +27 -0
- package/lib/public/modules/pending-message-queue.js +43 -1
- package/lib/public/modules/permission-hints.js +13 -0
- package/lib/public/modules/profile.js +9 -9
- package/lib/public/modules/project-activation.js +49 -0
- package/lib/public/modules/project-logs-render.js +13 -3
- package/lib/public/modules/project-logs.js +45 -15
- package/lib/public/modules/right-workbench.js +44 -0
- package/lib/public/modules/scheduled-message-state.js +12 -36
- package/lib/public/modules/scheduled-result-client.js +33 -0
- package/lib/public/modules/scheduled-result-display.js +50 -0
- package/lib/public/modules/scheduled-result-notification.js +31 -0
- package/lib/public/modules/scheduled-task-presentation.js +15 -0
- package/lib/public/modules/scheduled-tasks.js +9 -21
- package/lib/public/modules/sidebar-mobile.js +51 -3
- package/lib/public/modules/sidebar-sessions.js +65 -5
- package/lib/public/modules/split-session-boundary.js +28 -0
- package/lib/public/modules/split-view.js +42 -7
- package/lib/public/modules/sticky-notes-browser.js +130 -18
- package/lib/public/modules/sticky-notes.js +2 -3
- package/lib/public/modules/terminal.js +4 -4
- package/lib/public/modules/thinking-lifecycle.js +4 -1
- package/lib/public/modules/tools.js +5 -1
- package/lib/scheduled-run-state.js +10 -2
- package/lib/scheduler.js +1 -0
- package/lib/sdk-bridge.js +97 -61
- package/lib/sdk-message-processor.js +44 -13
- package/lib/sdk-skill-discovery.js +6 -28
- package/lib/sdk-turn-queue.js +14 -0
- package/lib/server-context-view.js +53 -0
- package/lib/server-default-vendor.js +60 -0
- package/lib/server.js +23 -6
- package/lib/session-notes-mcp-server.js +3 -5
- package/lib/session-pair-factory.js +2 -2
- package/lib/session-pair-mcp-server.js +5 -4
- package/lib/session-title-generator.js +89 -0
- package/lib/session-title-policy.js +8 -2
- package/lib/sessions.js +26 -1
- package/lib/user-presence.js +6 -0
- package/lib/users-context-view-preferences.js +65 -0
- package/lib/users-default-vendor-preferences.js +80 -0
- package/lib/users.js +12 -0
- package/lib/ws-schema.js +11 -0
- package/lib/yoke/acp-agent-profiles.js +15 -2
- package/lib/yoke/adapters/acp.js +3 -2
- package/lib/yoke/adapters/antigravity.js +2 -2
- package/lib/yoke/adapters/claude-worker.js +11 -12
- package/lib/yoke/adapters/claude.js +75 -22
- package/lib/yoke/adapters/codex.js +216 -29
- package/lib/yoke/adapters/kiro.js +6 -5
- package/lib/yoke/claude-context-usage.js +57 -0
- package/lib/yoke/claude-message-correlation.js +31 -0
- package/lib/yoke/claude-model-switch.js +15 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/mcp-bridge-server.js +18 -5
- package/lib/yoke/session-skill-catalog.js +45 -0
- package/lib/yoke/skill-discovery.js +60 -20
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +5 -7
|
@@ -19,6 +19,8 @@ import { refreshMobileChatSheet } from './sidebar-mobile.js';
|
|
|
19
19
|
import { renderMateSessionList, handleMateSearchResults, updateMateSidebarProfile } from './mate-sidebar.js';
|
|
20
20
|
import { openHomeChat, handleHomeMateHistory, handleHomeMateDelta, handleHomeMateSegment, handleHomeMateDone, handleHomeMateError, handleHomeMateSessionsState } from './home-mate-chat.js';
|
|
21
21
|
import { handleHomeSurfaceState } from './home-surface.js';
|
|
22
|
+
import { handleDefaultVendorMessage } from './default-vendor.js';
|
|
23
|
+
import { handleContextViewMessage } from './context-view-preference.js';
|
|
22
24
|
import { handleHomeMateMemoryState, handleHomeMateKnowledgeState } from './home-mate-settings.js';
|
|
23
25
|
import { renderKnowledgeList, handleKnowledgeContent } from './mate-knowledge.js';
|
|
24
26
|
import { renderMemoryList } from './mate-memory.js';
|
|
@@ -28,7 +30,7 @@ import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.
|
|
|
28
30
|
import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
|
|
29
31
|
import { handleSessionActionMessage } from './session-actions.js';
|
|
30
32
|
import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
|
|
31
|
-
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent, handleAcceptedOrdinaryMessage } from './input.js';
|
|
33
|
+
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent, handleAcceptedOrdinaryMessage, handleScheduleMessageResult } from './input.js';
|
|
32
34
|
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';
|
|
33
35
|
import { finishThinkingTurn, resumeThinkingAfterReplay } from './thinking-lifecycle.js';
|
|
34
36
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
@@ -44,7 +46,8 @@ import { tuiModalHandleTermOutput, tuiModalHandleTermResized, tuiModalHandleTerm
|
|
|
44
46
|
import { updateTerminalList, handleContextSourcesState, updateEmailAccountList, updateEmailUnreadCounts, handleEmailTestResult, handleEmailAddResult, handleEmailRemoveResult } from './context-sources.js';
|
|
45
47
|
import { refreshEmailSettings } from './user-settings.js';
|
|
46
48
|
import { handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, handleNoteWritten } from './sticky-notes.js';
|
|
47
|
-
import {
|
|
49
|
+
import { handleNoteDeleteResult } from './sticky-notes-browser.js';
|
|
50
|
+
import { handleProjectLogsState, handleProjectLogEntry, handleProjectLogCommented, handleProjectLogCommentReviewed, handleProjectLogDeleted, handleProjectLogUpdated, handleProjectLogsError, handleScheduledTaskResultsState, handleScheduledTaskResultCreated, handleScheduledTaskResultSession } from './project-logs.js';
|
|
48
51
|
import { handleSkillInstalled, handleSkillUninstalled } from './skills.js';
|
|
49
52
|
import { showRewindModal, onRewindComplete, setRewindMode, onRewindError, clearPendingRewindUuid, addRewindButton } from './rewind.js';
|
|
50
53
|
import { checkAdminAccess } from './admin.js';
|
|
@@ -63,9 +66,10 @@ import { setStatus } from './app-connection.js';
|
|
|
63
66
|
import { handleWhatsNewState, handleWhatsNewSeenResult, setKnownEntries as setWhatsNewKnownEntries } from './whats-new.js';
|
|
64
67
|
import { closeArticle as closeWhatsNewArticle } from './whats-new-article.js';
|
|
65
68
|
import { resolvePaneSession, resolveSwitchedVendor } from './pane-session.js';
|
|
69
|
+
import { markProjectSessionListHydrated, applyProjectSplitGroups, clearProjectSplitState } from './split-session-boundary.js';
|
|
66
70
|
import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
|
|
67
71
|
import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
|
|
68
|
-
import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel } from './app-panels.js';
|
|
72
|
+
import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel, markContextUnavailable } from './app-panels.js';
|
|
69
73
|
import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress, finishProjectSessionActivation } from './app-projects.js';
|
|
70
74
|
import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
|
|
71
75
|
import { hideHomeHub, showHomeHub } from './app-home-hub.js';
|
|
@@ -73,8 +77,7 @@ import { handleToolsState, handleToolInstalled, handleToolRemoved, handleToolSto
|
|
|
73
77
|
import { handleHomeDockState } from './home-dock.js';
|
|
74
78
|
import { handleToolFrameUrlState } from './home-tool-frame.js';
|
|
75
79
|
import { enterDmMode, exitDmMode, handleMateCreatedInApp, updateMateIconStatus, appendDmMessage, showDmTypingIndicator, buildMateInterviewPrompt } from './app-dm.js';
|
|
76
|
-
import { handleRateLimitEvent, updateRateLimitUsage,
|
|
77
|
-
import { applyScheduledMessageQueued, applyScheduledMessageState, applyScheduledMessageTerminal } from './scheduled-message-state.js';
|
|
80
|
+
import { handleRateLimitEvent, updateRateLimitUsage, handleFastModeState } from './app-rate-limit.js';
|
|
78
81
|
import { handleRemoteCursorMove, handleRemoteCursorLeave, handleRemoteSelection, clearRemoteCursors } from './app-cursors.js';
|
|
79
82
|
import { showLoopBanner, updateLoopBanner, updateLoopInputVisibility, showRalphApprovalBar, updateRalphApprovalStatus, openRalphPreviewModal, showExecModal, updateExecModalStatus } from './app-loop-ui.js';
|
|
80
83
|
import { handleAutonomousRunMessage, syncAutonomousRunForSession } from './autonomous-run.js';
|
|
@@ -98,6 +101,12 @@ export function processMessage(msg) {
|
|
|
98
101
|
if (handleAutonomousRunMessage(msg)) return;
|
|
99
102
|
if (handleLoopInterviewMessage(msg)) return;
|
|
100
103
|
if (handleScheduledTaskMessage(msg)) return;
|
|
104
|
+
if (handleDefaultVendorMessage(msg)) return;
|
|
105
|
+
if (handleContextViewMessage(msg)) return;
|
|
106
|
+
if (msg && msg.type === "schedule_message_result") {
|
|
107
|
+
handleScheduleMessageResult(msg);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
101
110
|
if (msg.type === "access_revoked") {
|
|
102
111
|
window.location.assign("/");
|
|
103
112
|
return;
|
|
@@ -223,9 +232,8 @@ export function processMessage(msg) {
|
|
|
223
232
|
applyDeadSessionTodoCompaction();
|
|
224
233
|
}
|
|
225
234
|
// Restore cached rich context usage BEFORE updateContextPanel runs
|
|
226
|
-
if (msg.contextUsage) {
|
|
227
|
-
|
|
228
|
-
}
|
|
235
|
+
if (msg.contextUsage && !msg.contextUsage.unavailable) store.set({ richContextUsage: msg.contextUsage });
|
|
236
|
+
else markContextUnavailable();
|
|
229
237
|
// Restore accurate context data from the last result in full history
|
|
230
238
|
if (msg.lastUsage || msg.lastModelUsage) {
|
|
231
239
|
accumulateContext(msg.lastCost, msg.lastUsage, msg.lastModelUsage, msg.lastStreamInputTokens);
|
|
@@ -327,10 +335,13 @@ export function processMessage(msg) {
|
|
|
327
335
|
// start receiving the new project's session_switched. The TUI
|
|
328
336
|
// host lives on document.body (it's position: fixed), so it
|
|
329
337
|
// survives project navigation unless we detach explicitly here.
|
|
338
|
+
if (msg.slug && store.get('activeProjectSlug') && store.get('activeProjectSlug') !== msg.slug) {
|
|
339
|
+
clearProjectSplitState();
|
|
340
|
+
}
|
|
330
341
|
detachTuiView();
|
|
331
342
|
store.set({ projectName: msg.project || msg.cwd, vendorInfo: msg.vendors || {} });
|
|
332
343
|
if (msg.cwd) store.set({ cwd: msg.cwd });
|
|
333
|
-
if (msg.slug) store.set({ currentSlug: msg.slug, activeProjectSlug: msg.slug });
|
|
344
|
+
if (msg.slug) store.set({ currentSlug: msg.slug, activeProjectSlug: msg.slug, sessionListProjectSlug: null, splitGroupsProjectSlug: null });
|
|
334
345
|
try { var _is = store.snap(); localStorage.setItem("clay-project-name-" + (_is.currentSlug || "default"), _is.projectName); } catch (e) {}
|
|
335
346
|
// In mate DM, keep title as mate name and re-apply mate color
|
|
336
347
|
if (store.get('dmMode') && store.get('dmTargetUser') && store.get('dmTargetUser').isMate) {
|
|
@@ -687,6 +698,7 @@ export function processMessage(msg) {
|
|
|
687
698
|
break;
|
|
688
699
|
|
|
689
700
|
case "session_list":
|
|
701
|
+
markProjectSessionListHydrated();
|
|
690
702
|
renderMateSessionList(msg.sessions || []);
|
|
691
703
|
renderSessionList(msg.sessions || []);
|
|
692
704
|
syncPaneTitles();
|
|
@@ -698,13 +710,14 @@ export function processMessage(msg) {
|
|
|
698
710
|
}
|
|
699
711
|
}
|
|
700
712
|
handlePaletteSessionSwitch();
|
|
713
|
+
maybeRestoreSplitGroup();
|
|
701
714
|
break;
|
|
702
715
|
|
|
703
716
|
case "split_groups":
|
|
704
|
-
|
|
717
|
+
applyProjectSplitGroups(msg.groups || []);
|
|
705
718
|
renderSessionList(null);
|
|
706
|
-
maybeRestoreSplitGroup();
|
|
707
719
|
syncPaneTitles();
|
|
720
|
+
maybeRestoreSplitGroup();
|
|
708
721
|
break;
|
|
709
722
|
|
|
710
723
|
case "split_group_result":
|
|
@@ -885,8 +898,8 @@ export function processMessage(msg) {
|
|
|
885
898
|
}
|
|
886
899
|
// Reload survival: a restored active session that is a split-group
|
|
887
900
|
// member reopens its group (no-op when a split is already open).
|
|
888
|
-
maybeRestoreSplitGroup();
|
|
889
901
|
if (finishProjectSessionActivation()) hideHomeHub();
|
|
902
|
+
maybeRestoreSplitGroup();
|
|
890
903
|
break;
|
|
891
904
|
|
|
892
905
|
case "session_full_access_changed":
|
|
@@ -1041,6 +1054,9 @@ export function processMessage(msg) {
|
|
|
1041
1054
|
case "thinking_start":
|
|
1042
1055
|
removeMatePreThinking();
|
|
1043
1056
|
startThinking();
|
|
1057
|
+
// The live Thinking entry is the progress affordance while reasoning
|
|
1058
|
+
// is streaming; avoid a second bouncing indicator at the same tail.
|
|
1059
|
+
setActivity(null);
|
|
1044
1060
|
break;
|
|
1045
1061
|
|
|
1046
1062
|
case "thinking_delta":
|
|
@@ -1168,7 +1184,7 @@ export function processMessage(msg) {
|
|
|
1168
1184
|
|
|
1169
1185
|
case "permission_request":
|
|
1170
1186
|
setActivity(null);
|
|
1171
|
-
renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor);
|
|
1187
|
+
renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor, msg);
|
|
1172
1188
|
startUrgentBlink();
|
|
1173
1189
|
break;
|
|
1174
1190
|
|
|
@@ -1184,7 +1200,7 @@ export function processMessage(msg) {
|
|
|
1184
1200
|
|
|
1185
1201
|
case "permission_request_pending":
|
|
1186
1202
|
setActivity(null);
|
|
1187
|
-
renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor);
|
|
1203
|
+
renderPermissionRequest(msg.requestId, msg.toolName, msg.toolInput, msg.decisionReason, msg.mateId, msg.vendor, msg);
|
|
1188
1204
|
startUrgentBlink();
|
|
1189
1205
|
break;
|
|
1190
1206
|
|
|
@@ -1268,8 +1284,10 @@ export function processMessage(msg) {
|
|
|
1268
1284
|
break;
|
|
1269
1285
|
|
|
1270
1286
|
case "context_usage":
|
|
1287
|
+
if (msg.sessionId != null && msg.sessionId !== store.get("activeSessionId")) break;
|
|
1271
1288
|
if (msg.data && !store.get('replayingHistory')) {
|
|
1272
|
-
|
|
1289
|
+
if (msg.data.unavailable) markContextUnavailable();
|
|
1290
|
+
else store.set({ richContextUsage: msg.data });
|
|
1273
1291
|
// UI sync handled by store subscriber in app-panels.js
|
|
1274
1292
|
}
|
|
1275
1293
|
break;
|
|
@@ -1411,28 +1429,6 @@ export function processMessage(msg) {
|
|
|
1411
1429
|
updateRateLimitUsage(msg);
|
|
1412
1430
|
break;
|
|
1413
1431
|
|
|
1414
|
-
case "scheduled_message_queued":
|
|
1415
|
-
applyScheduledMessageQueued(msg, { add: addScheduledMessageBubble, setDisabled: setScheduleBtnDisabled });
|
|
1416
|
-
break;
|
|
1417
|
-
|
|
1418
|
-
case "scheduled_message_sent":
|
|
1419
|
-
applyScheduledMessageTerminal(msg, { remove: removeScheduledMessageBubble, setDisabled: setScheduleBtnDisabled });
|
|
1420
|
-
setStatus("processing");
|
|
1421
|
-
break;
|
|
1422
|
-
|
|
1423
|
-
case "scheduled_message_cancelled":
|
|
1424
|
-
applyScheduledMessageTerminal(msg, { remove: removeScheduledMessageBubble, setDisabled: setScheduleBtnDisabled });
|
|
1425
|
-
break;
|
|
1426
|
-
|
|
1427
|
-
case "scheduled_message_state":
|
|
1428
|
-
applyScheduledMessageState(msg, {
|
|
1429
|
-
remove: removeScheduledMessageBubble,
|
|
1430
|
-
setDisabled: setScheduleBtnDisabled,
|
|
1431
|
-
add: addScheduledMessageBubble,
|
|
1432
|
-
interrupted: function () { addSystemMessage("A scheduled message was interrupted during server recovery and was not sent again automatically."); },
|
|
1433
|
-
});
|
|
1434
|
-
break;
|
|
1435
|
-
|
|
1436
1432
|
case "auto_continue_scheduled":
|
|
1437
1433
|
// Scheduler auto-continue, just show info
|
|
1438
1434
|
break;
|
|
@@ -1654,6 +1650,10 @@ export function processMessage(msg) {
|
|
|
1654
1650
|
handleNoteDeleted(msg);
|
|
1655
1651
|
break;
|
|
1656
1652
|
|
|
1653
|
+
case "note_delete_result":
|
|
1654
|
+
handleNoteDeleteResult(msg);
|
|
1655
|
+
break;
|
|
1656
|
+
|
|
1657
1657
|
case "project_logs_state":
|
|
1658
1658
|
handleProjectLogsState(msg);
|
|
1659
1659
|
break;
|
|
@@ -1682,6 +1682,18 @@ export function processMessage(msg) {
|
|
|
1682
1682
|
handleProjectLogsError(msg);
|
|
1683
1683
|
break;
|
|
1684
1684
|
|
|
1685
|
+
case "scheduled_task_results_state":
|
|
1686
|
+
handleScheduledTaskResultsState(msg);
|
|
1687
|
+
break;
|
|
1688
|
+
|
|
1689
|
+
case "scheduled_task_result_created":
|
|
1690
|
+
handleScheduledTaskResultCreated(msg);
|
|
1691
|
+
break;
|
|
1692
|
+
|
|
1693
|
+
case "scheduled_task_result_session":
|
|
1694
|
+
handleScheduledTaskResultSession(msg);
|
|
1695
|
+
break;
|
|
1696
|
+
|
|
1685
1697
|
case "process_stats":
|
|
1686
1698
|
updateStatusPanel(msg);
|
|
1687
1699
|
updateSettingsStats(msg);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyPermissionHints } from './permission-hints.js';
|
|
1
2
|
// app-notifications.js - Notification banners (Apple banner style)
|
|
2
3
|
// New notifications appear as individual banners top-right, auto-dismiss after 3s.
|
|
3
4
|
// Bell button click shows all stored notifications as banners.
|
|
@@ -190,6 +191,7 @@ function showBanner(notif, autoDismissMs) {
|
|
|
190
191
|
(!isEmpty ? '<button class="notif-banner-close">' + iconHtml("x") + '</button>' : '');
|
|
191
192
|
|
|
192
193
|
bannerContainer.appendChild(banner);
|
|
194
|
+
if (isPermission) applyPermissionHints(banner, notif.meta);
|
|
193
195
|
refreshIcons();
|
|
194
196
|
ensureClearAllButton();
|
|
195
197
|
|
|
@@ -5,15 +5,17 @@ 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 { getContextView as getStoredContextView, getEffectiveContextView, setContextView as setStoredContextView } from './context-view-preference.js';
|
|
8
9
|
import { reportPaneContext } from './pane-bridge.js';
|
|
9
10
|
import { setupModelPicker, renderModelPicker, requestVendorModels, prepareModelPickerOpen, modelDisplayName } from './model-picker.js';
|
|
10
11
|
|
|
11
12
|
// --- Module-owned state (not in store) ---
|
|
12
13
|
var sessionUsage = { cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
13
|
-
var contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0, thinking: null, costBasis: null };
|
|
14
|
+
var contextData = { contextWindow: 0, currentInput: null, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0, thinking: null, costBasis: null };
|
|
14
15
|
var ctxPopoverEl = null;
|
|
15
16
|
var ctxHoverTimer = null;
|
|
16
17
|
var statusRefreshTimer = null;
|
|
18
|
+
var lastAppliedContextView = null;
|
|
17
19
|
|
|
18
20
|
// --- DOM refs ---
|
|
19
21
|
var configChipWrap = null;
|
|
@@ -374,6 +376,15 @@ export function initPanels() {
|
|
|
374
376
|
|
|
375
377
|
// --- Reactive UI sync ---
|
|
376
378
|
store.subscribe(function (state, prev) {
|
|
379
|
+
var effectiveContextView = getEffectiveContextView();
|
|
380
|
+
if (effectiveContextView !== lastAppliedContextView) {
|
|
381
|
+
lastAppliedContextView = effectiveContextView;
|
|
382
|
+
applyContextView(effectiveContextView);
|
|
383
|
+
}
|
|
384
|
+
if (state.contextViewPreferenceState && state.contextViewPreferenceState.error &&
|
|
385
|
+
state.contextViewPreferenceState.error !== (prev.contextViewPreferenceState && prev.contextViewPreferenceState.error)) {
|
|
386
|
+
showToast(state.contextViewPreferenceState.error, "error");
|
|
387
|
+
}
|
|
377
388
|
// richContextUsage changed -> update popover + panel
|
|
378
389
|
if (state.richContextUsage !== prev.richContextUsage) {
|
|
379
390
|
if (state.richContextUsage) {
|
|
@@ -540,7 +551,6 @@ export function initPanels() {
|
|
|
540
551
|
if (contextPanelClose) {
|
|
541
552
|
contextPanelClose.addEventListener("click", function () {
|
|
542
553
|
setContextView("off");
|
|
543
|
-
applyContextView("off");
|
|
544
554
|
});
|
|
545
555
|
}
|
|
546
556
|
|
|
@@ -553,7 +563,8 @@ export function initPanels() {
|
|
|
553
563
|
}
|
|
554
564
|
|
|
555
565
|
// Restore context view on load
|
|
556
|
-
|
|
566
|
+
lastAppliedContextView = getEffectiveContextView();
|
|
567
|
+
applyContextView(lastAppliedContextView);
|
|
557
568
|
}
|
|
558
569
|
|
|
559
570
|
// --- Config chip ---
|
|
@@ -718,9 +729,18 @@ export function contextPctClass(pct) {
|
|
|
718
729
|
|
|
719
730
|
export function updateContextPanel() {
|
|
720
731
|
if (!contextUsedEl) return;
|
|
721
|
-
// Context
|
|
722
|
-
|
|
723
|
-
|
|
732
|
+
// Context occupancy is separate from generic per-turn accounting. Codex
|
|
733
|
+
// only supplies currentInput from a verified last-turn snapshot; rich
|
|
734
|
+
// context usage is more authoritative when available.
|
|
735
|
+
var rich = store.get('richContextUsage');
|
|
736
|
+
var hasRich = rich && (
|
|
737
|
+
(typeof rich.totalTokens === "number" && typeof rich.maxTokens === "number" && rich.maxTokens > 0) ||
|
|
738
|
+
(typeof rich.input_tokens === "number" && typeof rich.contextWindow === "number" && rich.contextWindow > 0)
|
|
739
|
+
);
|
|
740
|
+
var hasCurrent = typeof contextData.currentInput === "number";
|
|
741
|
+
var hasContext = !!(hasRich || hasCurrent);
|
|
742
|
+
var used = hasRich ? (typeof rich.totalTokens === "number" ? rich.totalTokens : rich.input_tokens) : (hasCurrent ? contextData.currentInput : 0);
|
|
743
|
+
var win = hasRich ? (typeof rich.maxTokens === "number" ? rich.maxTokens : rich.contextWindow) : (hasCurrent ? contextData.contextWindow : 0);
|
|
724
744
|
var pct = win > 0 ? Math.min(100, (used / win) * 100) : 0;
|
|
725
745
|
var cls = contextPctClass(pct);
|
|
726
746
|
reportPaneContext({ pct: pct, used: used, win: win, cls: cls, model: contextData.model, cost: contextData.cost });
|
|
@@ -734,7 +754,7 @@ export function updateContextPanel() {
|
|
|
734
754
|
contextMiniFill.className = "context-mini-fill" + cls;
|
|
735
755
|
}
|
|
736
756
|
if (contextMiniLabel) {
|
|
737
|
-
contextMiniLabel.textContent = (win > 0 ? formatTokens(used) + "/" + formatTokens(win) : "
|
|
757
|
+
contextMiniLabel.textContent = (win > 0 ? formatTokens(used) + "/" + formatTokens(win) : "-");
|
|
738
758
|
}
|
|
739
759
|
// Header bar. A split group has no meaningful group-level context usage:
|
|
740
760
|
// the two members have separate windows, and the parent shell's own numbers
|
|
@@ -742,9 +762,9 @@ export function updateContextPanel() {
|
|
|
742
762
|
// chip instead, so hide the title-bar gauge while a split is open.
|
|
743
763
|
var splitOpen = !!store.get('splitPanes');
|
|
744
764
|
var headerCtxEl = store.get('headerContextEl');
|
|
745
|
-
if (headerCtxEl) headerCtxEl.classList.toggle("hidden", splitOpen);
|
|
746
|
-
if (contextMini) contextMini.classList.toggle("split-hidden", splitOpen);
|
|
747
|
-
if (
|
|
765
|
+
if (headerCtxEl) headerCtxEl.classList.toggle("hidden", splitOpen || !hasContext);
|
|
766
|
+
if (contextMini) contextMini.classList.toggle("split-hidden", splitOpen || !hasContext);
|
|
767
|
+
if (hasContext && !splitOpen) {
|
|
748
768
|
var statusArea = document.querySelector(".title-bar-content .status");
|
|
749
769
|
var hCtxEl = store.get('headerContextEl');
|
|
750
770
|
if (statusArea && !hCtxEl) {
|
|
@@ -808,7 +828,7 @@ export function accumulateContext(cost, usage, modelUsage, lastStreamInputTokens
|
|
|
808
828
|
// when available -- result.usage.input_tokens sums all API calls in a turn,
|
|
809
829
|
// inflating context usage when tools are involved.
|
|
810
830
|
// Falls back to the summed value for setups that don't emit message_start.
|
|
811
|
-
if (lastStreamInputTokens) {
|
|
831
|
+
if (typeof lastStreamInputTokens === "number") {
|
|
812
832
|
contextData.input = lastStreamInputTokens;
|
|
813
833
|
} else {
|
|
814
834
|
contextData.input = (usage.input_tokens || usage.inputTokens || 0)
|
|
@@ -817,6 +837,11 @@ export function accumulateContext(cost, usage, modelUsage, lastStreamInputTokens
|
|
|
817
837
|
contextData.output = usage.output_tokens || usage.outputTokens || 0;
|
|
818
838
|
contextData.cacheRead = usage.cache_read_input_tokens || usage.cacheReadInputTokens || 0;
|
|
819
839
|
contextData.cacheWrite = usage.cache_creation_input_tokens || usage.cacheCreationInputTokens || 0;
|
|
840
|
+
if (store.get('currentVendor') === "codex") {
|
|
841
|
+
contextData.currentInput = typeof lastStreamInputTokens === "number" ? lastStreamInputTokens : null;
|
|
842
|
+
} else {
|
|
843
|
+
contextData.currentInput = typeof lastStreamInputTokens === "number" ? lastStreamInputTokens : contextData.input;
|
|
844
|
+
}
|
|
820
845
|
}
|
|
821
846
|
contextData.turns++;
|
|
822
847
|
if (modelUsage) {
|
|
@@ -830,6 +855,21 @@ export function accumulateContext(cost, usage, modelUsage, lastStreamInputTokens
|
|
|
830
855
|
contextData.model = displayModel;
|
|
831
856
|
contextData.contextWindow = resolveContextWindow(displayModel, mu.contextWindow);
|
|
832
857
|
if (mu.maxOutputTokens) contextData.maxOutputTokens = mu.maxOutputTokens;
|
|
858
|
+
if (store.get('currentVendor') === "codex") {
|
|
859
|
+
var snapshot = mu.contextSnapshot;
|
|
860
|
+
if (snapshot) store.set({ richContextUsage: null });
|
|
861
|
+
if (snapshot && snapshot.valid === true
|
|
862
|
+
&& typeof snapshot.inputTokens === "number"
|
|
863
|
+
&& typeof snapshot.contextWindow === "number"
|
|
864
|
+
&& snapshot.contextWindow > 0) {
|
|
865
|
+
contextData.currentInput = snapshot.inputTokens;
|
|
866
|
+
contextData.contextWindow = snapshot.contextWindow;
|
|
867
|
+
} else {
|
|
868
|
+
// An explicit invalid marker is authoritative: do not retain an
|
|
869
|
+
// occupancy value from the previous live turn or history replay.
|
|
870
|
+
contextData.currentInput = null;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
833
873
|
|
|
834
874
|
// thinkingTokens and costBasis are per-model and optional: absent on
|
|
835
875
|
// older CLI builds and, for a resumed session, on the turns that ran
|
|
@@ -860,50 +900,56 @@ export function accumulateContext(cost, usage, modelUsage, lastStreamInputTokens
|
|
|
860
900
|
|
|
861
901
|
// contextView: "off" | "mini" | "panel"
|
|
862
902
|
export function getContextView() {
|
|
863
|
-
|
|
903
|
+
return getStoredContextView();
|
|
864
904
|
}
|
|
865
905
|
|
|
866
906
|
export function setContextView(v) {
|
|
867
|
-
|
|
907
|
+
if (store.get("paneMode") === true) {
|
|
908
|
+
store.set({ contextViewOverride: v });
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
return setStoredContextView(v);
|
|
868
912
|
}
|
|
869
913
|
|
|
870
914
|
export function applyContextView(view) {
|
|
871
915
|
if (contextPanel) contextPanel.classList.toggle("hidden", view !== "panel");
|
|
872
916
|
if (contextMini) contextMini.classList.toggle("hidden", view !== "mini");
|
|
873
|
-
if (view === "panel") refreshIcons();
|
|
917
|
+
if (view === "panel") { refreshIcons(); requestContextDetails(); }
|
|
874
918
|
}
|
|
875
919
|
|
|
876
920
|
export function resetContextData() {
|
|
877
|
-
contextData = { contextWindow: 0, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0, thinking: null, costBasis: null };
|
|
921
|
+
contextData = { contextWindow: 0, currentInput: null, maxOutputTokens: 0, model: "-", cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0, thinking: null, costBasis: null };
|
|
878
922
|
store.set({ richContextUsage: null });
|
|
879
923
|
// hideCtxPopover + updateContextPanel handled by store subscriber
|
|
880
924
|
}
|
|
881
925
|
|
|
926
|
+
export function markContextUnavailable() {
|
|
927
|
+
contextData.currentInput = null;
|
|
928
|
+
store.set({ richContextUsage: null });
|
|
929
|
+
updateContextPanel();
|
|
930
|
+
}
|
|
931
|
+
|
|
882
932
|
export function resetContext() {
|
|
883
933
|
resetContextData();
|
|
884
934
|
// Keep view state, just reset data
|
|
885
|
-
applyContextView(
|
|
935
|
+
applyContextView(getEffectiveContextView());
|
|
886
936
|
}
|
|
887
937
|
|
|
888
938
|
export function minimizeContext() {
|
|
889
939
|
setContextView("mini");
|
|
890
|
-
applyContextView("mini");
|
|
891
940
|
}
|
|
892
941
|
|
|
893
942
|
export function expandContext() {
|
|
894
943
|
setContextView("panel");
|
|
895
|
-
applyContextView("panel");
|
|
896
944
|
}
|
|
897
945
|
|
|
898
946
|
export function toggleContextPanel() {
|
|
899
947
|
if (!contextPanel) return;
|
|
900
|
-
var view =
|
|
948
|
+
var view = getEffectiveContextView();
|
|
901
949
|
if (view === "panel") {
|
|
902
950
|
setContextView("mini");
|
|
903
|
-
applyContextView("mini");
|
|
904
951
|
} else {
|
|
905
952
|
setContextView("panel");
|
|
906
|
-
applyContextView("panel");
|
|
907
953
|
}
|
|
908
954
|
}
|
|
909
955
|
|
|
@@ -922,9 +968,17 @@ export function ensureCtxPopover() {
|
|
|
922
968
|
});
|
|
923
969
|
}
|
|
924
970
|
|
|
971
|
+
function requestContextDetails() {
|
|
972
|
+
var ws = getWs();
|
|
973
|
+
if (!store.get("replayingHistory") && ws && ws.readyState === 1) {
|
|
974
|
+
ws.send(JSON.stringify({ type: "context_usage_request", sessionId: store.get("activeSessionId") }));
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
925
978
|
export function showCtxPopover() {
|
|
926
979
|
var s = store.snap();
|
|
927
980
|
if (!s.headerContextEl || !s.richContextUsage) return;
|
|
981
|
+
requestContextDetails();
|
|
928
982
|
if (ctxHoverTimer) { clearTimeout(ctxHoverTimer); ctxHoverTimer = null; }
|
|
929
983
|
ensureCtxPopover();
|
|
930
984
|
s.headerContextEl.appendChild(ctxPopoverEl);
|
|
@@ -944,8 +998,8 @@ export function renderCtxPopover() {
|
|
|
944
998
|
if (!ctxPopoverEl || !richContextUsage) return;
|
|
945
999
|
var d = richContextUsage;
|
|
946
1000
|
var cats = d.categories || [];
|
|
947
|
-
var total = d.totalTokens || 0;
|
|
948
|
-
var max = d.maxTokens || 0;
|
|
1001
|
+
var total = d.totalTokens != null ? d.totalTokens : (d.input_tokens || 0);
|
|
1002
|
+
var max = d.maxTokens != null ? d.maxTokens : (d.contextWindow || 0);
|
|
949
1003
|
var pct = d.percentage != null ? d.percentage : (max > 0 ? (total / max) * 100 : 0);
|
|
950
1004
|
|
|
951
1005
|
var html = "";
|
|
@@ -39,6 +39,7 @@ import { closeProjectSettings } from './project-settings.js';
|
|
|
39
39
|
import { chooseProjectAfterRemoval } from './project-removal-target.js';
|
|
40
40
|
import { isProjectActivated, isProjectActivationPending, isProjectContextConnected, projectWsPath } from './project-activation.js';
|
|
41
41
|
import { rememberHomePrimarySurface } from './home-surface.js';
|
|
42
|
+
import { clearProjectSplitState } from './split-session-boundary.js';
|
|
42
43
|
|
|
43
44
|
// --- Module-owned state ---
|
|
44
45
|
var cachedProjects = [];
|
|
@@ -377,6 +378,7 @@ export function resetClientState() {
|
|
|
377
378
|
store.set({ currentFullText: "" });
|
|
378
379
|
resetToolState();
|
|
379
380
|
clearPendingImages();
|
|
381
|
+
store.set({ pendingScheduleAdmissions: {}, pendingScheduleAdmission: null });
|
|
380
382
|
resetShellCommand();
|
|
381
383
|
clearAllMentionActive();
|
|
382
384
|
setActivityEl(null);
|
|
@@ -446,6 +448,7 @@ export function switchProject(slug) {
|
|
|
446
448
|
}
|
|
447
449
|
if (homeVisible) store.set({ pendingHomeProjectSlug: slug });
|
|
448
450
|
if (isProjectActivationPending(store.snap(), slug, ws)) return;
|
|
451
|
+
if (store.get('currentSlug') !== slug) clearProjectSplitState();
|
|
449
452
|
resetFileBrowser();
|
|
450
453
|
if (typeof closeScheduledTasks === "function") closeScheduledTasks();
|
|
451
454
|
closeNotesBrowser();
|