clay-server 4.0.0-beta.8 → 4.0.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.
Files changed (185) hide show
  1. package/lib/capsule-display-floor.js +51 -0
  2. package/lib/capsule-frame-server.js +255 -0
  3. package/lib/capsule-mate-turn.js +105 -0
  4. package/lib/capsule-pig-logic.js +300 -0
  5. package/lib/capsule-server-runtimes.js +39 -0
  6. package/lib/capsules/pig/display.js +198 -0
  7. package/lib/capsules/pig/manifest.json +9 -0
  8. package/lib/capsules/pig/ui.json +84 -0
  9. package/lib/config.js +25 -1
  10. package/lib/daemon-projects.js +13 -8
  11. package/lib/daemon.js +127 -14
  12. package/lib/git-cli.js +95 -1
  13. package/lib/knowledge-import.js +436 -0
  14. package/lib/knowledge-record-store.js +188 -0
  15. package/lib/knowledge-search.js +240 -0
  16. package/lib/mate-knowledge-mcp-server.js +151 -0
  17. package/lib/mate-knowledge-migration.js +283 -0
  18. package/lib/mate-knowledge-service.js +488 -0
  19. package/lib/mate-knowledge-sync.js +482 -0
  20. package/lib/migrate-single-user.js +2 -0
  21. package/lib/notes-lifecycle.js +149 -0
  22. package/lib/notes.js +60 -3
  23. package/lib/os-users.js +3 -1
  24. package/lib/project-capsule-catalog.js +9 -1
  25. package/lib/project-capsule-turn.js +96 -0
  26. package/lib/project-connection.js +15 -1
  27. package/lib/project-file-history.js +223 -0
  28. package/lib/project-file-watch.js +77 -49
  29. package/lib/project-filesystem.js +31 -220
  30. package/lib/project-http.js +15 -1
  31. package/lib/project-knowledge.js +22 -0
  32. package/lib/project-log-context.js +172 -0
  33. package/lib/project-log-feedback-delivery.js +118 -0
  34. package/lib/project-logs-comments.js +137 -0
  35. package/lib/project-logs-context-state.js +42 -0
  36. package/lib/project-logs-mcp-server.js +328 -0
  37. package/lib/project-logs-query.js +185 -0
  38. package/lib/project-logs-root.js +75 -0
  39. package/lib/project-logs-schema.js +259 -0
  40. package/lib/project-logs-service.js +463 -0
  41. package/lib/project-logs-snapshot.js +205 -0
  42. package/lib/project-logs-store.js +495 -0
  43. package/lib/project-logs-versioning.js +132 -0
  44. package/lib/project-logs.js +400 -0
  45. package/lib/project-mate-interaction.js +14 -0
  46. package/lib/project-mate-knowledge.js +110 -0
  47. package/lib/project-memory.js +22 -0
  48. package/lib/project-message-delivery.js +70 -0
  49. package/lib/project-pair-lifecycle.js +460 -0
  50. package/lib/project-request-access.js +66 -0
  51. package/lib/project-session-handoff.js +1 -1
  52. package/lib/project-session-notes.js +64 -13
  53. package/lib/project-session-pair.js +159 -146
  54. package/lib/project-session-spawn.js +1 -1
  55. package/lib/project-sessions.js +58 -13
  56. package/lib/project-user-message.js +53 -4
  57. package/lib/project-worker-permission.js +430 -0
  58. package/lib/project-worker-proposal.js +208 -62
  59. package/lib/project.js +185 -6
  60. package/lib/public/app.js +63 -35
  61. package/lib/public/css/admin.css +2 -2
  62. package/lib/public/css/capsule-ui.css +38 -0
  63. package/lib/public/css/command-palette.css +19 -9
  64. package/lib/public/css/git-placard.css +154 -0
  65. package/lib/public/css/home-hub.css +4 -0
  66. package/lib/public/css/home-session-actions.css +66 -0
  67. package/lib/public/css/home-sidebar.css +66 -0
  68. package/lib/public/css/input.css +84 -3
  69. package/lib/public/css/messages.css +40 -1
  70. package/lib/public/css/mobile-nav.css +80 -0
  71. package/lib/public/css/notifications-center.css +67 -0
  72. package/lib/public/css/pane.css +97 -10
  73. package/lib/public/css/project-logs.css +198 -0
  74. package/lib/public/css/scheduler.css +1 -1
  75. package/lib/public/css/session-actions.css +16 -101
  76. package/lib/public/css/sidebar.css +103 -0
  77. package/lib/public/css/sticky-notes.css +237 -229
  78. package/lib/public/css/worker-proposal.css +36 -3
  79. package/lib/public/index.html +43 -11
  80. package/lib/public/modules/admin.js +8 -8
  81. package/lib/public/modules/app-connection.js +124 -6
  82. package/lib/public/modules/app-home-hub.js +12 -8
  83. package/lib/public/modules/app-message-cards.js +2 -1
  84. package/lib/public/modules/app-messages.js +92 -11
  85. package/lib/public/modules/app-misc.js +2 -14
  86. package/lib/public/modules/app-notifications.js +11 -7
  87. package/lib/public/modules/app-panels.js +3 -2
  88. package/lib/public/modules/app-projects.js +56 -18
  89. package/lib/public/modules/app-rendering.js +1 -0
  90. package/lib/public/modules/capsule-preference.js +44 -0
  91. package/lib/public/modules/chat-bubble-renderer.js +1 -1
  92. package/lib/public/modules/clay-log-links.js +70 -0
  93. package/lib/public/modules/command-palette.js +52 -15
  94. package/lib/public/modules/context-sources.js +0 -3
  95. package/lib/public/modules/git-agent-sessions.js +85 -0
  96. package/lib/public/modules/git-panel.js +111 -84
  97. package/lib/public/modules/git-placard.js +161 -0
  98. package/lib/public/modules/home-chat-empty-state.js +39 -0
  99. package/lib/public/modules/home-chat-identity.js +9 -0
  100. package/lib/public/modules/home-conversations-sheet.js +102 -48
  101. package/lib/public/modules/home-mate-chat.js +15 -35
  102. package/lib/public/modules/home-session-actions.js +6 -0
  103. package/lib/public/modules/home-sidebar-chat-list.js +101 -41
  104. package/lib/public/modules/home-sidebar.js +7 -0
  105. package/lib/public/modules/home-surface-boot.js +7 -3
  106. package/lib/public/modules/home-tool-frame.js +182 -0
  107. package/lib/public/modules/home-tools.js +138 -1
  108. package/lib/public/modules/input.js +38 -7
  109. package/lib/public/modules/markdown.js +2 -0
  110. package/lib/public/modules/mate-sidebar.js +0 -8
  111. package/lib/public/modules/message-delivery.js +97 -0
  112. package/lib/public/modules/paste-modal.js +84 -0
  113. package/lib/public/modules/project-activation.js +45 -0
  114. package/lib/public/modules/project-logs-render.js +405 -0
  115. package/lib/public/modules/project-logs.js +461 -0
  116. package/lib/public/modules/project-removal-target.js +26 -0
  117. package/lib/public/modules/scheduler.js +17 -3
  118. package/lib/public/modules/session-actions.js +29 -105
  119. package/lib/public/modules/session-hierarchy.js +54 -0
  120. package/lib/public/modules/sidebar-mobile.js +40 -15
  121. package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
  122. package/lib/public/modules/sidebar-sessions.js +77 -11
  123. package/lib/public/modules/sidebar.js +17 -5
  124. package/lib/public/modules/split-group-helpers.js +8 -0
  125. package/lib/public/modules/split-pair-ui.js +15 -4
  126. package/lib/public/modules/split-view.js +5 -2
  127. package/lib/public/modules/split-worker-runtime.js +24 -0
  128. package/lib/public/modules/sticky-note-markdown.js +5 -0
  129. package/lib/public/modules/sticky-notes-browser.js +326 -0
  130. package/lib/public/modules/sticky-notes-card.js +360 -0
  131. package/lib/public/modules/sticky-notes-editor.js +291 -0
  132. package/lib/public/modules/sticky-notes-shared.js +90 -0
  133. package/lib/public/modules/sticky-notes.js +170 -946
  134. package/lib/public/modules/thinking-lifecycle.js +175 -0
  135. package/lib/public/modules/thinking-summary.js +28 -0
  136. package/lib/public/modules/thinking-view.js +75 -0
  137. package/lib/public/modules/tool-palette-order.js +143 -0
  138. package/lib/public/modules/tool-palette-overlays.js +159 -0
  139. package/lib/public/modules/tool-palette.js +21 -188
  140. package/lib/public/modules/tools.js +31 -123
  141. package/lib/public/modules/update-snooze.js +169 -0
  142. package/lib/public/modules/user-settings.js +4 -0
  143. package/lib/public/modules/worker-pane-lock.js +176 -0
  144. package/lib/public/modules/worker-proposal-state.js +16 -0
  145. package/lib/public/modules/worker-proposal.js +49 -20
  146. package/lib/public/style.css +3 -1
  147. package/lib/sdk-bridge.js +135 -46
  148. package/lib/sdk-message-processor.js +36 -19
  149. package/lib/server-experimental-settings.js +49 -0
  150. package/lib/server-global-ws.js +11 -0
  151. package/lib/server-home-chat-events.js +31 -1
  152. package/lib/server-home-chat.js +27 -4
  153. package/lib/server-home-clay-entry.js +1 -1
  154. package/lib/server-home-clay-session-links.js +34 -5
  155. package/lib/server-settings.js +5 -0
  156. package/lib/server-tools.js +127 -9
  157. package/lib/server.js +137 -22
  158. package/lib/session-driver-eligibility.js +38 -0
  159. package/lib/session-driver-orchestration.js +31 -0
  160. package/lib/session-hygiene.js +3 -0
  161. package/lib/session-notes-mcp-server.js +25 -5
  162. package/lib/session-pair-factory.js +247 -0
  163. package/lib/session-pair-mcp-server.js +46 -8
  164. package/lib/session-pair-prompts.js +79 -0
  165. package/lib/session-pair-turn-control.js +153 -0
  166. package/lib/session-provenance.js +119 -0
  167. package/lib/session-spawn-mcp-server.js +1 -1
  168. package/lib/session-split-groups.js +4 -1
  169. package/lib/session-title-policy.js +60 -0
  170. package/lib/session-visibility.js +41 -0
  171. package/lib/sessions.js +57 -10
  172. package/lib/tools-registry.js +89 -10
  173. package/lib/update-snooze.js +437 -0
  174. package/lib/users-experimental-preferences.js +30 -0
  175. package/lib/users-permissions.js +6 -6
  176. package/lib/users.js +22 -16
  177. package/lib/workspace-query-access.js +102 -0
  178. package/lib/workspace-query-service.js +24 -17
  179. package/lib/worktree.js +25 -38
  180. package/lib/ws-schema.js +38 -7
  181. package/lib/yoke/adapters/claude-worker.js +16 -6
  182. package/lib/yoke/adapters/claude.js +12 -0
  183. package/lib/yoke/adapters/codex.js +24 -14
  184. package/lib/yoke/vendor-registry.js +1 -1
  185. package/package.json +2 -2
@@ -3,6 +3,7 @@
3
3
  // All dependencies are direct imports; no context injection needed.
4
4
 
5
5
  import { store } from './store.js';
6
+ import { acknowledgeMessage, replayPendingMessages } from './message-delivery.js';
6
7
  import { getWs } from './ws-ref.js';
7
8
 
8
9
  // --- Leaf module imports ---
@@ -15,7 +16,7 @@ import { updateDmBadge, setMentionActive, renderUserStrip } from './sidebar-mate
15
16
  import { renderSidebarPresence } from './sidebar-presence.js';
16
17
  import { refreshMobileChatSheet } from './sidebar-mobile.js';
17
18
  import { renderMateSessionList, handleMateSearchResults, updateMateSidebarProfile } from './mate-sidebar.js';
18
- import { openHomeChat, handleHomeMateHistory, handleHomeMateDelta, handleHomeMateDone, handleHomeMateError, handleHomeMateSessionsState } from './home-mate-chat.js';
19
+ import { openHomeChat, handleHomeMateHistory, handleHomeMateDelta, handleHomeMateSegment, handleHomeMateDone, handleHomeMateError, handleHomeMateSessionsState } from './home-mate-chat.js';
19
20
  import { handleHomeSurfaceState } from './home-surface.js';
20
21
  import { handleHomeMateMemoryState, handleHomeMateKnowledgeState } from './home-mate-settings.js';
21
22
  import { renderKnowledgeList, handleKnowledgeContent } from './mate-knowledge.js';
@@ -28,6 +29,7 @@ import { handleSessionActionMessage } from './session-actions.js';
28
29
  import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
29
30
  import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent } from './input.js';
30
31
  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';
32
+ import { finishThinkingTurn, resumeThinkingAfterReplay } from './thinking-lifecycle.js';
31
33
  import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
32
34
  import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
33
35
  import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
@@ -41,6 +43,7 @@ import { tuiModalHandleTermOutput, tuiModalHandleTermResized, tuiModalHandleTerm
41
43
  import { updateTerminalList, handleContextSourcesState, updateEmailAccountList, updateEmailUnreadCounts, handleEmailTestResult, handleEmailAddResult, handleEmailRemoveResult } from './context-sources.js';
42
44
  import { refreshEmailSettings } from './user-settings.js';
43
45
  import { handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, handleNoteWritten } from './sticky-notes.js';
46
+ import { handleProjectLogsState, handleProjectLogEntry, handleProjectLogCommented, handleProjectLogCommentReviewed, handleProjectLogDeleted, handleProjectLogUpdated, handleProjectLogsError } from './project-logs.js';
44
47
  import { handleSkillInstalled, handleSkillUninstalled } from './skills.js';
45
48
  import { showRewindModal, onRewindComplete, setRewindMode, onRewindError, clearPendingRewindUuid, addRewindButton } from './rewind.js';
46
49
  import { checkAdminAccess } from './admin.js';
@@ -61,11 +64,12 @@ import { resolvePaneSession, resolveSwitchedVendor } from './pane-session.js';
61
64
  import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
62
65
  import { getModelInfoUpdate, modelEntryValue, modelEntryMatches, handleModelSelectionResult, requestVendorModels } from './model-picker.js';
63
66
  import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel } from './app-panels.js';
64
- import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress } from './app-projects.js';
67
+ import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress, finishProjectSessionActivation } from './app-projects.js';
65
68
  import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
66
69
  import { hideHomeHub, showHomeHub } from './app-home-hub.js';
67
- import { handleToolsState, handleToolInstalled, handleToolRemoved, handleToolStorageResult, handleToolLlmResult, handleToolsError, handleToolControlRequest } from './home-tools.js';
70
+ import { handleToolsState, handleToolInstalled, handleToolRemoved, handleToolStorageResult, handleToolLlmResult, handleToolsError, handleToolControlRequest, handleToolServerState, handleToolServerEvent, handleCapsuleGameSession } from './home-tools.js';
68
71
  import { handleHomeDockState } from './home-dock.js';
72
+ import { handleToolFrameUrlState } from './home-tool-frame.js';
69
73
  import { enterDmMode, exitDmMode, handleMateCreatedInApp, updateMateIconStatus, appendDmMessage, showDmTypingIndicator, buildMateInterviewPrompt } from './app-dm.js';
70
74
  import { handleRateLimitEvent, updateRateLimitUsage, addScheduledMessageBubble, removeScheduledMessageBubble, handleFastModeState } from './app-rate-limit.js';
71
75
  import { handleRemoteCursorMove, handleRemoteCursorLeave, handleRemoteSelection, clearRemoteCursors } from './app-cursors.js';
@@ -73,6 +77,7 @@ import { showLoopBanner, updateLoopBanner, updateLoopInputVisibility, showRalphA
73
77
  import { showDebateSticky, showDebateConcludeConfirm, showDebateUserFloor, exitDebateFloorMode, exitDebateConcludeMode, exitDebateEndedMode, updateDebateRound, renderDebateUserFloorDone } from './app-debate-ui.js';
74
78
  import { handleSkillInstallWs } from './app-skills-install.js';
75
79
  import { handleNotificationsState, handleNotificationCreated, handleNotificationDismissed, handleNotificationDismissedAll, showUpdateBanner } from './app-notifications.js';
80
+ import { handleUpdateSnoozed } from './update-snooze.js';
76
81
  import { autoStartLoginIfNeeded, handleVendorLoginReady, handleVendorLoginState, handleVendorLoginError, handleAuthRefreshed } from './vendor-login.js';
77
82
  import { handleDebatePreparing, handleDebateBriefReady, renderDebateBriefReady, handleDebateStarted, renderDebateStarted, handleDebateTurn, handleDebateActivity, handleDebateStream, handleDebateTurnDone, handleDebateCommentQueued, handleDebateCommentInjected, renderDebateCommentInjected, handleDebateResumed, handleDebateEnded, renderDebateEnded, handleDebateError, isDebateActive, renderMcpDebateProposal, renderDebateUserResume } from './debate.js';
78
83
  import { handleMentionStart, handleMentionActivity, handleMentionStream, handleMentionDone, handleMentionError, renderMentionUser, renderMentionResponse, renderUserMention } from './mention.js';
@@ -160,6 +165,20 @@ export function processMessage(msg) {
160
165
  }
161
166
 
162
167
  switch (msg.type) {
168
+ case "message_ack":
169
+ var acknowledgedEl = msg.clientMessageId
170
+ ? messagesEl.querySelector('[data-client-message-id="' + msg.clientMessageId + '"]')
171
+ : null;
172
+ acknowledgeMessage(msg.clientMessageId);
173
+ if (!acknowledgedEl && msg.message) {
174
+ addUserMessage(msg.message.text, msg.message.images || null, msg.message.pastes || null, msg.message.from, msg.message.fromName);
175
+ var acknowledgedUsers = messagesEl.querySelectorAll(".msg-user");
176
+ if (acknowledgedUsers.length > 0) {
177
+ acknowledgedUsers[acknowledgedUsers.length - 1].dataset.clientMessageId = msg.clientMessageId;
178
+ }
179
+ }
180
+ break;
181
+
163
182
  case "history_meta":
164
183
  store.set({ historyFrom: msg.from, historyTotal: msg.total, replayingHistory: true });
165
184
  updateHistorySentinel();
@@ -171,6 +190,7 @@ export function processMessage(msg) {
171
190
 
172
191
  case "history_done":
173
192
  store.set({ replayingHistory: false });
193
+ resumeThinkingAfterReplay(store.get('sessionIsProcessing'));
174
194
  // Batched syntax highlight + mermaid pass for the entire replayed
175
195
  // transcript. Per-message highlights are skipped during replay
176
196
  // (see markdown.js) to avoid cascading reflows that the sticky-
@@ -293,7 +313,7 @@ export function processMessage(msg) {
293
313
  detachTuiView();
294
314
  store.set({ projectName: msg.project || msg.cwd, vendorInfo: msg.vendors || {} });
295
315
  if (msg.cwd) store.set({ cwd: msg.cwd });
296
- if (msg.slug) store.set({ currentSlug: msg.slug });
316
+ if (msg.slug) store.set({ currentSlug: msg.slug, activeProjectSlug: msg.slug });
297
317
  try { var _is = store.snap(); localStorage.setItem("clay-project-name-" + (_is.currentSlug || "default"), _is.projectName); } catch (e) {}
298
318
  // In mate DM, keep title as mate name and re-apply mate color
299
319
  if (store.get('dmMode') && store.get('dmTargetUser') && store.get('dmTargetUser').isMate) {
@@ -330,7 +350,9 @@ export function processMessage(msg) {
330
350
  break;
331
351
 
332
352
  case "update_available":
333
- // In multi-user mode, only show update UI to admins
353
+ // The server already refuses to send this frame to anyone who is not
354
+ // admin-grade, so this check is defence in depth for the UI, not the
355
+ // access control.
334
356
  if (store.get('isMultiUserMode')) {
335
357
  checkAdminAccess().then(function (isAdmin) {
336
358
  if (!isAdmin) return;
@@ -343,6 +365,10 @@ export function processMessage(msg) {
343
365
  }
344
366
  break;
345
367
 
368
+ case "update_snoozed":
369
+ handleUpdateSnoozed(msg);
370
+ break;
371
+
346
372
  case "up_to_date":
347
373
  var utdBtn = document.getElementById("settings-update-check");
348
374
  if (utdBtn) {
@@ -619,6 +645,26 @@ export function processMessage(msg) {
619
645
  handleToolControlRequest(msg);
620
646
  break;
621
647
 
648
+ case "tool_server_state":
649
+ handleToolServerState(msg);
650
+ break;
651
+
652
+ case "tool_server_event":
653
+ handleToolServerEvent(msg);
654
+ break;
655
+
656
+ case "tool_frame_url_state":
657
+ handleToolFrameUrlState(msg);
658
+ break;
659
+
660
+ case "capsule_game_session":
661
+ handleCapsuleGameSession(msg);
662
+ break;
663
+
664
+ case "home_mate_segment":
665
+ handleHomeMateSegment(msg);
666
+ break;
667
+
622
668
  case "input_sync":
623
669
  if (!store.get('dmMode')) handleInputSync(msg.text);
624
670
  break;
@@ -718,7 +764,6 @@ export function processMessage(msg) {
718
764
  break;
719
765
 
720
766
  case "session_switched":
721
- hideHomeHub();
722
767
  closeWhatsNewArticle();
723
768
  // Save draft from outgoing session
724
769
  var _prevSid = store.get('activeSessionId');
@@ -737,6 +782,7 @@ export function processMessage(msg) {
737
782
  ? msg.runtimeTerminalId
738
783
  : (typeof msg.terminalId === "number" ? msg.terminalId : null);
739
784
  store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || "", 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') });
785
+ replayPendingMessages(msg.id);
740
786
  // TUI sessions swap the chat UI for an embedded xterm running
741
787
  // `claude` inside a real PTY. Mount or tear down before the rest of
742
788
  // the chat-side bookkeeping runs so we don't waste work on hidden DOM.
@@ -815,6 +861,7 @@ export function processMessage(msg) {
815
861
  // Reload survival: a restored active session that is a split-group
816
862
  // member reopens its group (no-op when a split is already open).
817
863
  maybeRestoreSplitGroup();
864
+ if (finishProjectSessionActivation()) hideHomeHub();
818
865
  break;
819
866
 
820
867
  case "session_full_access_changed":
@@ -858,6 +905,10 @@ export function processMessage(msg) {
858
905
  } else {
859
906
  addUserMessage(msg.text, msg.images || null, msg.pastes || null, msg.from, msg.fromName, msg.delegated ? msg : null);
860
907
  }
908
+ if (msg.clientMessageId) {
909
+ var replayedUsers = messagesEl.querySelectorAll(".msg-user");
910
+ if (replayedUsers.length > 0) replayedUsers[replayedUsers.length - 1].dataset.clientMessageId = msg.clientMessageId;
911
+ }
861
912
  // Split Worker pane: a delegated instruction starts (or replays into) a
862
913
  // driven turn; surface the composer notice. Cleared on "done".
863
914
  if (msg.delegated) showWorkerDelegationNotice(msg.delegatedByTitle || null);
@@ -979,7 +1030,6 @@ export function processMessage(msg) {
979
1030
  if (typeof msg.text !== "string") break;
980
1031
  removeMatePreThinking();
981
1032
  stopThinking();
982
- resetThinkingGroup();
983
1033
  setActivity(null);
984
1034
  appendDelta(msg.text);
985
1035
  break;
@@ -1179,7 +1229,7 @@ export function processMessage(msg) {
1179
1229
  // were missed by the client).
1180
1230
  removeMatePreThinking();
1181
1231
  setActivity(null);
1182
- stopThinking();
1232
+ finishThinkingTurn();
1183
1233
  markAllToolsDone();
1184
1234
  closeToolGroup();
1185
1235
  clearAllImageGenerationProgress();
@@ -1199,7 +1249,7 @@ export function processMessage(msg) {
1199
1249
  case "done":
1200
1250
  removeMatePreThinking();
1201
1251
  setActivity(null);
1202
- stopThinking();
1252
+ finishThinkingTurn();
1203
1253
  hideWorkerDelegationNotice();
1204
1254
  markAllToolsDone();
1205
1255
  closeToolGroup();
@@ -1222,6 +1272,7 @@ export function processMessage(msg) {
1222
1272
  case "error":
1223
1273
  removeMatePreThinking();
1224
1274
  setActivity(null);
1275
+ finishThinkingTurn();
1225
1276
  addSystemMessage(msg.text, true);
1226
1277
  break;
1227
1278
 
@@ -1273,13 +1324,14 @@ export function processMessage(msg) {
1273
1324
  case "process_conflict":
1274
1325
  removeMatePreThinking();
1275
1326
  setActivity(null);
1327
+ finishThinkingTurn();
1276
1328
  addConflictMessage(msg);
1277
1329
  break;
1278
1330
 
1279
1331
  case "session_writer_conflict":
1280
1332
  removeMatePreThinking();
1281
1333
  setActivity(null);
1282
- stopThinking();
1334
+ finishThinkingTurn();
1283
1335
  markAllToolsDone();
1284
1336
  closeToolGroup();
1285
1337
  addSystemMessage(msg.text || "This session is already open in another process.", true);
@@ -1288,13 +1340,14 @@ export function processMessage(msg) {
1288
1340
  case "context_overflow":
1289
1341
  removeMatePreThinking();
1290
1342
  setActivity(null);
1343
+ finishThinkingTurn();
1291
1344
  addContextOverflowMessage(msg);
1292
1345
  break;
1293
1346
 
1294
1347
  case "auth_required":
1295
1348
  removeMatePreThinking();
1296
1349
  setActivity(null);
1297
- stopThinking();
1350
+ finishThinkingTurn();
1298
1351
  markAllToolsDone();
1299
1352
  closeToolGroup();
1300
1353
  appendDelta((msg.text || "Authentication required.") + "\n");
@@ -1567,6 +1620,34 @@ export function processMessage(msg) {
1567
1620
  handleNoteDeleted(msg);
1568
1621
  break;
1569
1622
 
1623
+ case "project_logs_state":
1624
+ handleProjectLogsState(msg);
1625
+ break;
1626
+
1627
+ case "project_log_entry":
1628
+ handleProjectLogEntry(msg);
1629
+ break;
1630
+
1631
+ case "project_log_commented":
1632
+ handleProjectLogCommented(msg);
1633
+ break;
1634
+
1635
+ case "project_log_comment_reviewed":
1636
+ handleProjectLogCommentReviewed(msg);
1637
+ break;
1638
+
1639
+ case "project_log_deleted":
1640
+ handleProjectLogDeleted(msg);
1641
+ break;
1642
+
1643
+ case "project_log_updated":
1644
+ handleProjectLogUpdated(msg);
1645
+ break;
1646
+
1647
+ case "project_logs_error":
1648
+ handleProjectLogsError(msg);
1649
+ break;
1650
+
1570
1651
  case "process_stats":
1571
1652
  updateStatusPanel(msg);
1572
1653
  updateSettingsStats(msg);
@@ -2,10 +2,10 @@
2
2
  // Extracted from app.js (PR-34)
3
3
 
4
4
  import { refreshIcons, iconHtml } from './icons.js';
5
- import { escapeHtml, copyToClipboard } from './utils.js';
6
5
  import { getWs } from './ws-ref.js';
7
6
  import { updateBrowserTabList } from './context-sources.js';
8
7
  import { setExtensionConnected } from './mcp-ui.js';
8
+ import { initPasteModal } from './paste-modal.js';
9
9
 
10
10
  // --- Module-owned state ---
11
11
  var confirmCallback = null;
@@ -44,6 +44,7 @@ export function flushPendingExtMessages() {
44
44
  }
45
45
 
46
46
  export function initMisc() {
47
+ initPasteModal();
47
48
  // --- Confirm modal listeners ---
48
49
  var confirmModal = document.getElementById("confirm-modal");
49
50
  var confirmOk = document.getElementById("confirm-ok");
@@ -258,19 +259,6 @@ export function closeImageModal() {
258
259
  if (modal) modal.classList.add("hidden");
259
260
  }
260
261
 
261
- export function showPasteModal(text) {
262
- var modal = document.getElementById("paste-modal");
263
- var body = document.getElementById("paste-modal-body");
264
- if (!modal || !body) return;
265
- body.textContent = text;
266
- modal.classList.remove("hidden");
267
- }
268
-
269
- export function closePasteModal() {
270
- var modal = document.getElementById("paste-modal");
271
- if (modal) modal.classList.add("hidden");
272
- }
273
-
274
262
  export function showConfirm(text, onConfirm, okLabel, destructive) {
275
263
  var confirmText = document.getElementById("confirm-text");
276
264
  var confirmOk = document.getElementById("confirm-ok");
@@ -14,6 +14,7 @@ import { openTuiModal } from './tui-attention.js';
14
14
  import { requestVendorLogin } from './vendor-login.js';
15
15
  import { startUrgentBlink, stopUrgentBlink } from './app-favicon.js';
16
16
  import { playDoneSound, isNotifSoundEnabled } from './notifications.js';
17
+ import { attachSnoozeControl, setSnoozeBannerRemover } from './update-snooze.js';
17
18
  var notifications = [];
18
19
  var unreadCount = 0;
19
20
  var bannerContainer = null;
@@ -56,6 +57,10 @@ export function initAppNotifications() {
56
57
  bellBtn = document.getElementById("notif-center-btn");
57
58
  badgeEl = document.getElementById("notif-center-badge");
58
59
 
60
+ // Banner teardown lives here (exit animation, clear-all sync), so the
61
+ // snooze module borrows it rather than importing this module back.
62
+ setSnoozeBannerRemover(removeBanner);
63
+
59
64
  // Split panes are secondary clients inside the parent shell. They keep
60
65
  // notification state in sync, but the parent owns the single visible
61
66
  // banner surface for the whole split view.
@@ -708,13 +713,11 @@ export function showUpdateBanner(msg) {
708
713
  banner.setAttribute("data-notif-id", "_update");
709
714
  banner.setAttribute("data-update-version", msg.version);
710
715
 
711
- var actionsHtml = '';
712
- if (!isHeadless) {
713
- actionsHtml =
714
- '<div class="notif-banner-actions">' +
715
- '<button class="notif-banner-update-now">Update now</button>' +
716
- '</div>';
717
- }
716
+ // Headless installs update from the shell, so they get no install button —
717
+ // but they still get the action row, because Snooze applies either way.
718
+ var actionsHtml = '<div class="notif-banner-actions">' +
719
+ (isHeadless ? '' : '<button class="notif-banner-update-now">Update now</button>') +
720
+ '</div>';
718
721
 
719
722
  banner.innerHTML =
720
723
  '<div class="notif-banner-icon"><img src="/clay-studio-symbol.png" width="32" height="32" alt="Clay Studio"></div>' +
@@ -729,6 +732,7 @@ export function showUpdateBanner(msg) {
729
732
  '<button class="notif-banner-close">' + iconHtml("x") + '</button>';
730
733
 
731
734
  bannerContainer.appendChild(banner);
735
+ attachSnoozeControl(banner.querySelector(".notif-banner-actions"));
732
736
  refreshIcons();
733
737
 
734
738
  requestAnimationFrame(function () {
@@ -91,7 +91,7 @@ var MODE_FULL_AUTO = { value: "bypassPermissions", label: "Full auto" };
91
91
  var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
92
92
  var EFFORT_LEVELS_BY_VENDOR = {
93
93
  claude: ["low", "medium", "high", "xhigh", "max"],
94
- codex: ["minimal", "low", "medium", "high", "xhigh"],
94
+ codex: ["low", "medium", "high", "xhigh", "max"],
95
95
  antigravity: [],
96
96
  opencode: [],
97
97
  kiro: ["low", "medium", "high", "xhigh", "max"],
@@ -115,7 +115,8 @@ var CODEX_WEBSEARCH_OPTIONS = [
115
115
  var KNOWN_CONTEXT_WINDOWS = {
116
116
  "opus-4-6": 1000000,
117
117
  "claude-sonnet-4": 1000000,
118
- "gpt-5.6": 272000,
118
+ "gpt-6-astra": 1050000,
119
+ "gpt-5.6": 1050000,
119
120
  "gpt-5.5": 1048576,
120
121
  "gpt-5.4": 1048576,
121
122
  "gpt-5.3": 1048576,
@@ -19,7 +19,7 @@ import { exitDmMode } from './app-dm.js';
19
19
  import { isHomeHubVisible, hideHomeHub, showHomeHub } from './app-home-hub.js';
20
20
  import { closeArticle as closeWhatsNewArticle } from './whats-new-article.js';
21
21
  import { resetFileBrowser } from './filebrowser.js';
22
- import { closeArchive } from './sticky-notes.js';
22
+ import { closeNotesBrowser } from './sticky-notes-browser.js';
23
23
  import { hideMemory } from './mate-memory.js';
24
24
  import { isSchedulerOpen, closeScheduler, resetScheduler } from './scheduler.js';
25
25
  import { connect, cancelReconnect, setStatus } from './app-connection.js';
@@ -34,6 +34,10 @@ import { resetRateLimitState } from './app-rate-limit.js';
34
34
  import { closeSessionInfoPopover } from './app-header.js';
35
35
  import { resetDebateState } from './debate.js';
36
36
  import { removeDebateBottomBar } from './app-debate-ui.js';
37
+ import { closeProjectSettings } from './project-settings.js';
38
+ import { chooseProjectAfterRemoval } from './project-removal-target.js';
39
+ import { isProjectActivated, isProjectActivationPending, isProjectContextConnected, projectWsPath } from './project-activation.js';
40
+ import { rememberHomePrimarySurface } from './home-surface.js';
37
41
 
38
42
  // --- Module-owned state ---
39
43
  var cachedProjects = [];
@@ -415,43 +419,76 @@ export function switchProject(slug) {
415
419
  if (!slug) return;
416
420
  document.cookie = "clay_last_project=" + encodeURIComponent(slug) + "; Path=/; SameSite=Strict; Max-Age=31536000";
417
421
  var st = store.snap();
422
+ var ws = getWs();
423
+ var targetWsPath = projectWsPath(slug);
424
+ var homeVisible = isHomeHubVisible();
425
+ var alreadyInProject = isProjectActivated(st, slug, ws);
418
426
  var wasDm = st.dmMode;
419
427
  var wasMate = st.dmMode && st.dmTargetUser && st.dmTargetUser.isMate;
420
428
  if (st.dmMode) exitDmMode(wasMate);
421
429
  closeWhatsNewArticle();
422
- if (isHomeHubVisible()) {
430
+ if (homeVisible && alreadyInProject) {
431
+ rememberHomePrimarySurface("project", slug);
423
432
  hideHomeHub();
424
- if (slug === store.get('currentSlug')) {
425
- if (document.documentElement.classList.contains("pwa-standalone")) {
426
- history.replaceState(null, "", "/p/" + slug + "/");
427
- } else {
428
- history.pushState(null, "", "/p/" + slug + "/");
429
- }
430
- return;
433
+ if (document.documentElement.classList.contains("pwa-standalone")) {
434
+ history.replaceState(null, "", "/p/" + slug + "/");
435
+ } else {
436
+ history.pushState(null, "", "/p/" + slug + "/");
431
437
  }
438
+ return;
432
439
  }
433
- if (slug === store.get('currentSlug')) {
434
- var ws = getWs();
440
+ if (alreadyInProject) {
435
441
  if (wasDm && ws && ws.readyState === 1) {
436
442
  ws.send(JSON.stringify({ type: "switch_session", id: store.get('activeSessionId') }));
437
443
  }
438
444
  return;
439
445
  }
446
+ if (homeVisible) store.set({ pendingHomeProjectSlug: slug });
447
+ if (isProjectActivationPending(store.snap(), slug, ws)) return;
440
448
  resetFileBrowser();
441
- closeArchive();
449
+ closeNotesBrowser();
442
450
  hideMemory();
443
451
  if (isSchedulerOpen()) closeScheduler();
444
452
  resetScheduler(slug);
445
453
  store.set({ currentSlug: slug });
446
454
  store.set({ basePath: "/p/" + slug + "/" });
447
- store.set({ wsPath: "/p/" + slug + "/ws" });
455
+ store.set({ wsPath: targetWsPath });
456
+ if (!homeVisible) {
457
+ if (document.documentElement.classList.contains("pwa-standalone")) {
458
+ history.replaceState(null, "", "/p/" + slug + "/");
459
+ } else {
460
+ history.pushState(null, "", "/p/" + slug + "/");
461
+ }
462
+ }
463
+ resetClientState();
464
+ connect();
465
+ }
466
+
467
+ export function completePendingProjectActivation() {
468
+ var slug = store.get('pendingHomeProjectSlug');
469
+ if (!slug || !isProjectActivated(store.snap(), slug, getWs())) return false;
470
+ store.set({ pendingHomeProjectSlug: null });
471
+ rememberHomePrimarySurface("project", slug);
448
472
  if (document.documentElement.classList.contains("pwa-standalone")) {
449
473
  history.replaceState(null, "", "/p/" + slug + "/");
450
474
  } else {
451
475
  history.pushState(null, "", "/p/" + slug + "/");
452
476
  }
453
- resetClientState();
454
- connect();
477
+ return true;
478
+ }
479
+
480
+ export function confirmCurrentProjectSessionActivation() {
481
+ var slug = store.get('activeProjectSlug');
482
+ if (!slug || !isProjectContextConnected(store.snap(), slug, getWs())) return false;
483
+ store.set({ sessionActivatedProjectSlug: slug });
484
+ return true;
485
+ }
486
+
487
+ export function finishProjectSessionActivation() {
488
+ var confirmed = confirmCurrentProjectSessionActivation();
489
+ if (!confirmed) return false;
490
+ if (!store.get('pendingHomeProjectSlug')) return true;
491
+ return completePendingProjectActivation();
455
492
  }
456
493
 
457
494
  export function showUpdateAvailable(msg) {
@@ -572,9 +609,10 @@ export function handleRemoveProjectResult(msg) {
572
609
  var currentSlug = store.get('currentSlug');
573
610
  if (msg.slug === currentSlug) {
574
611
  var isWorktree = msg.slug.indexOf("--") !== -1;
575
- var parentSlug = isWorktree ? msg.slug.split("--")[0] : null;
612
+ var targetSlug = chooseProjectAfterRemoval(cachedProjects, msg.slug);
576
613
 
577
614
  showToast(isWorktree ? "Worktree removed" : "Project removed", "success");
615
+ closeProjectSettings();
578
616
 
579
617
  // Suppress disconnect overlay and reconnect by detaching the WS
580
618
  var ws = getWs();
@@ -602,8 +640,8 @@ export function handleRemoveProjectResult(msg) {
602
640
  renderProjectList();
603
641
  resetClientState();
604
642
 
605
- if (parentSlug) {
606
- switchProject(parentSlug);
643
+ if (targetSlug) {
644
+ switchProject(targetSlug);
607
645
  } else {
608
646
  showHomeHub();
609
647
  }
@@ -111,6 +111,7 @@ export function finalizeAssistantBlock() {
111
111
 
112
112
  export function showClaudePreThinking() {
113
113
  if (getChatLayout() !== "channel") return;
114
+ removeMatePreThinking();
114
115
  var vendor = store.get('currentVendor') || "claude";
115
116
  var vendorAvatar = VENDOR_AVATARS[vendor] || VENDOR_AVATARS.claude;
116
117
  var vendorName = VENDOR_NAMES[vendor] || VENDOR_NAMES.claude;
@@ -0,0 +1,44 @@
1
+ import { store } from './store.js';
2
+ import { requestTools } from './home-tools.js';
3
+ import { closeHomeDock } from './home-dock.js';
4
+ import { showToast } from './utils.js';
5
+
6
+ function applyCapsulesEnabled(enabled, refreshTools) {
7
+ var want = enabled === true;
8
+ store.set({ capsulesEnabled: want, capsulesPreferenceLoaded: true });
9
+ if (document.body) document.body.classList.toggle('capsules-disabled', !want);
10
+ if (!want && store.get('dockOpen') === true) closeHomeDock();
11
+ if (refreshTools !== false) requestTools();
12
+ }
13
+
14
+ export function initCapsulePreference() {
15
+ var toggle = document.getElementById('us-capsules-enabled');
16
+ if (!toggle) return;
17
+ toggle.addEventListener('change', function () {
18
+ var previous = store.get('capsulesEnabled') === true;
19
+ var want = this.checked === true;
20
+ applyCapsulesEnabled(want, false);
21
+ fetch('/api/user/capsules-enabled', {
22
+ method: 'PUT',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({ enabled: want }),
25
+ }).then(function (response) {
26
+ if (!response.ok) throw new Error('Could not save the Capsules preference.');
27
+ return response.json();
28
+ }).then(function (data) {
29
+ applyCapsulesEnabled(data.capsulesEnabled === true, true);
30
+ showToast(data.capsulesEnabled ? 'Experimental Capsules enabled' : 'Experimental Capsules disabled');
31
+ }).catch(function () {
32
+ toggle.checked = previous;
33
+ applyCapsulesEnabled(previous, true);
34
+ showToast('Could not save the Capsules preference.', 'error');
35
+ });
36
+ });
37
+ }
38
+
39
+ export function syncCapsulePreference(enabled) {
40
+ var want = enabled === true;
41
+ var toggle = document.getElementById('us-capsules-enabled');
42
+ if (toggle) toggle.checked = want;
43
+ applyCapsulesEnabled(want, true);
44
+ }
@@ -105,7 +105,7 @@ export function addAssistantCopyHandler(row, rawText) {
105
105
  }
106
106
 
107
107
  row.addEventListener("click", function (event) {
108
- if (event.target.closest("a, pre, code")) return;
108
+ if (event.target.closest("a, button, pre, code")) return;
109
109
  var selection = window.getSelection();
110
110
  if (selection && selection.toString().length > 0) return;
111
111
  if (!primed) {
@@ -0,0 +1,70 @@
1
+ export function parseClayLogReferences(text) {
2
+ var pattern = /\[clayos\/(log:[A-Za-z0-9_-]{24})(?:\s+[\u2014-]\s+([^\]\n]{1,80}))?\]|(^|[^A-Za-z0-9_/:])(log:[A-Za-z0-9_-]{24})(?![A-Za-z0-9_-])/g;
3
+ var results = [];
4
+ var match;
5
+ while ((match = pattern.exec(text || ""))) {
6
+ results.push({
7
+ start: match.index,
8
+ end: pattern.lastIndex,
9
+ prefix: match[3] || "",
10
+ ref: match[1] || match[4],
11
+ label: match[2] ? match[2].trim() : "",
12
+ });
13
+ }
14
+ return results;
15
+ }
16
+
17
+ export function isExactClayLogReference(text) {
18
+ return /^log:[A-Za-z0-9_-]{24}$/.test((text || "").trim());
19
+ }
20
+
21
+ function createLogLink(ref, labelText) {
22
+ var button = document.createElement("button");
23
+ button.type = "button";
24
+ button.className = "clayos-log-link";
25
+ button.contentEditable = "false";
26
+ button.dataset.logRef = ref;
27
+ button.setAttribute("aria-label", "Open Project Log" + (labelText ? " " + labelText : ""));
28
+ var label = document.createElement("span");
29
+ label.textContent = "Log";
30
+ button.appendChild(label);
31
+ if (labelText) {
32
+ var meta = document.createElement("small");
33
+ meta.textContent = labelText;
34
+ button.appendChild(meta);
35
+ }
36
+ return button;
37
+ }
38
+
39
+ export function enhanceClayLogLinks(root) {
40
+ var codeNodes = root.querySelectorAll("code");
41
+ for (var codeIndex = 0; codeIndex < codeNodes.length; codeIndex++) {
42
+ var code = codeNodes[codeIndex];
43
+ var codeText = (code.textContent || "").trim();
44
+ if (code.closest("pre") || !isExactClayLogReference(codeText)) continue;
45
+ code.parentNode.replaceChild(createLogLink(codeText, ""), code);
46
+ }
47
+
48
+ var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
49
+ var nodes = [];
50
+ var node;
51
+ while ((node = walker.nextNode())) {
52
+ if (!node.parentElement || node.parentElement.closest("code, pre, a, button")) continue;
53
+ if (parseClayLogReferences(node.nodeValue).length) nodes.push(node);
54
+ }
55
+ for (var i = 0; i < nodes.length; i++) {
56
+ var text = nodes[i].nodeValue || "";
57
+ var matches = parseClayLogReferences(text);
58
+ var fragment = document.createDocumentFragment();
59
+ var offset = 0;
60
+ for (var j = 0; j < matches.length; j++) {
61
+ var match = matches[j];
62
+ fragment.appendChild(document.createTextNode(text.slice(offset, match.start)));
63
+ if (match.prefix) fragment.appendChild(document.createTextNode(match.prefix));
64
+ fragment.appendChild(createLogLink(match.ref, match.label));
65
+ offset = match.end;
66
+ }
67
+ fragment.appendChild(document.createTextNode(text.slice(offset)));
68
+ nodes[i].parentNode.replaceChild(fragment, nodes[i]);
69
+ }
70
+ }