clay-server 4.1.0-beta.6 → 4.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +661 -21
  2. package/LICENSES/MIT-legacy.txt +21 -0
  3. package/LICENSES/README.md +7 -0
  4. package/README.md +2 -2
  5. package/bin/cli.js +0 -2
  6. package/lib/daemon.js +0 -2
  7. package/lib/default-ai-runtime.js +117 -0
  8. package/lib/git-cli.js +1 -0
  9. package/lib/issue-work-session.js +10 -0
  10. package/lib/issues-comments.js +40 -0
  11. package/lib/issues-commit-verification.js +30 -0
  12. package/lib/issues-mcp-server.js +27 -0
  13. package/lib/issues-schema.js +119 -0
  14. package/lib/issues-service.js +339 -0
  15. package/lib/issues-store.js +210 -0
  16. package/lib/model-entry.js +15 -0
  17. package/lib/project-capsule-turn.js +1 -0
  18. package/lib/project-connection.js +2 -0
  19. package/lib/project-delegated-session.js +1 -0
  20. package/lib/project-filesystem.js +10 -10
  21. package/lib/project-issue-launch.js +154 -0
  22. package/lib/project-issues.js +58 -0
  23. package/lib/project-logs-mcp-server.js +8 -12
  24. package/lib/project-message-delivery.js +4 -2
  25. package/lib/project-models.js +71 -3
  26. package/lib/project-pending-message-queue.js +264 -0
  27. package/lib/project-scheduled-tasks.js +2 -4
  28. package/lib/project-sessions.js +82 -31
  29. package/lib/project-tool-llm-runtime.js +48 -0
  30. package/lib/project-user-message.js +178 -42
  31. package/lib/project-workspace-query.js +25 -0
  32. package/lib/project.js +102 -18
  33. package/lib/public/app.js +38 -4
  34. package/lib/public/css/default-ai.css +33 -0
  35. package/lib/public/css/input.css +89 -5
  36. package/lib/public/css/issues.css +101 -0
  37. package/lib/public/css/messages.css +19 -0
  38. package/lib/public/css/pending-message-queue.css +187 -0
  39. package/lib/public/css/scheduled-tasks.css +3 -4
  40. package/lib/public/index.html +25 -17
  41. package/lib/public/modules/app-connection.js +3 -0
  42. package/lib/public/modules/app-cursors.js +29 -12
  43. package/lib/public/modules/app-message-router.js +8 -0
  44. package/lib/public/modules/app-messages.js +12 -3
  45. package/lib/public/modules/clay-file-links.js +111 -0
  46. package/lib/public/modules/clay-issue-links.js +131 -0
  47. package/lib/public/modules/clay-log-links.js +2 -0
  48. package/lib/public/modules/cursor-sharing-preference.js +38 -0
  49. package/lib/public/modules/default-ai.js +377 -0
  50. package/lib/public/modules/filebrowser.js +43 -2
  51. package/lib/public/modules/input.js +7 -17
  52. package/lib/public/modules/issues-context.js +117 -0
  53. package/lib/public/modules/issues-render.js +29 -0
  54. package/lib/public/modules/issues.js +270 -0
  55. package/lib/public/modules/markdown.js +4 -0
  56. package/lib/public/modules/message-delivery.js +5 -3
  57. package/lib/public/modules/pending-message-image-editor.js +143 -0
  58. package/lib/public/modules/pending-message-queue-model.js +22 -0
  59. package/lib/public/modules/pending-message-queue.js +449 -0
  60. package/lib/public/modules/scheduled-task-engine.js +0 -50
  61. package/lib/public/modules/scheduled-tasks.js +7 -16
  62. package/lib/public/modules/sidebar-mobile.js +3 -0
  63. package/lib/public/modules/sticky-note-markdown.js +1 -0
  64. package/lib/public/modules/terminal.js +2 -0
  65. package/lib/public/modules/tool-palette-order.js +1 -0
  66. package/lib/public/style.css +2 -0
  67. package/lib/sdk-bridge.js +34 -11
  68. package/lib/server-cursor-sharing.js +65 -0
  69. package/lib/server-default-ai.js +265 -0
  70. package/lib/server-home-chat.js +29 -9
  71. package/lib/server-home-clay-entry.js +4 -1
  72. package/lib/server-home-debate-planning.js +32 -14
  73. package/lib/server-home-mate-creation.js +30 -11
  74. package/lib/server-home-models.js +14 -6
  75. package/lib/server-issue-links.js +18 -0
  76. package/lib/server.js +41 -0
  77. package/lib/session-notes-mcp-server.js +1 -1
  78. package/lib/session-search.js +4 -1
  79. package/lib/tool-llm.js +1 -0
  80. package/lib/users-default-ai-preferences.js +80 -0
  81. package/lib/users-preferences.js +35 -0
  82. package/lib/users.js +10 -0
  83. package/lib/workspace-query-projection.js +29 -0
  84. package/lib/workspace-query-service.js +19 -29
  85. package/lib/ws-schema.js +36 -1
  86. package/lib/yoke/adapters/claude-worker.js +4 -2
  87. package/lib/yoke/adapters/claude.js +5 -1
  88. package/lib/yoke/package.json +1 -1
  89. package/package.json +5 -6
  90. package/bin/claude-relay.js +0 -6
package/lib/project.js CHANGED
@@ -35,6 +35,7 @@ var { attachFilesystem } = require("./project-filesystem");
35
35
  var { attachSessions } = require("./project-sessions");
36
36
  var { attachModels } = require("./project-models");
37
37
  var { attachUserMessage } = require("./project-user-message");
38
+ var { createPendingMessageQueue } = require("./project-pending-message-queue");
38
39
  var { attachLoopInterview } = require("./project-loop-interview");
39
40
  var { attachScheduledTasks, authorizeStoredOwner } = require("./project-scheduled-tasks");
40
41
  var { attachScheduledTaskExecution } = require("./project-scheduled-task-execution");
@@ -65,6 +66,7 @@ var { attachSplitGroups } = require("./session-split-groups");
65
66
  var toolControlMcp = require("./tool-control-mcp-server");
66
67
  var capsuleFloor = require("./capsule-display-floor");
67
68
  var toolLlm = require("./tool-llm");
69
+ var { attachProjectToolLlmRuntime } = require("./project-tool-llm-runtime");
68
70
  // project-notifications is attached globally in server.js, passed via opts.notificationsModule
69
71
 
70
72
  function createMateToolControlMcp(adapter, config) {
@@ -602,6 +604,38 @@ function createProjectContext(opts) {
602
604
  sm.availableVendors = Object.keys(adapters);
603
605
  sm.defaultVendor = defaultVendor;
604
606
 
607
+ var _pendingMessageQueue = createPendingMessageQueue({
608
+ cwd: cwd,
609
+ slug: slug,
610
+ ensureSessionIdentity: function (session) { sm.saveSessionFile(session); },
611
+ broadcast: function (session, message) {
612
+ var data = JSON.stringify(message);
613
+ clients.forEach(function (ws) {
614
+ if (ws.readyState !== 1 || ws._clayActiveSession !== session.localId) return;
615
+ if (usersModule.isMultiUser()) {
616
+ var viewer = ws._clayUser && usersModule.findUserById(ws._clayUser.id);
617
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
618
+ if (!viewer || (opts.canAccessProjectSlug && !opts.canAccessProjectSlug(viewer.id, slug)) || !usersModule.canAccessSession(viewer.id, session, access)) return;
619
+ }
620
+ ws.send(data);
621
+ });
622
+ },
623
+ authorize: function (session, actor) {
624
+ if (!session) return false;
625
+ if (!usersModule.isMultiUser()) return true;
626
+ var freshActor = actor && actor.id && usersModule.findUserById(actor.id);
627
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
628
+ return !!(freshActor && (!opts.canAccessProjectSlug || opts.canAccessProjectSlug(freshActor.id, slug)) && usersModule.canAccessSession(freshActor.id, session, access));
629
+ },
630
+ canMutate: function (session, actor, item) {
631
+ if (!actor || !actor.id) return !usersModule.isMultiUser();
632
+ if (item && item.actorId !== actor.id) return false;
633
+ var freshActor = usersModule.findUserById(actor.id);
634
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
635
+ return !usersModule.isMultiUser() || !!(freshActor && (!opts.canAccessProjectSlug || opts.canAccessProjectSlug(freshActor.id, slug)) && usersModule.canAccessSession(freshActor.id, session, access));
636
+ },
637
+ });
638
+
605
639
  var sdk = null;
606
640
  var _autonomousRun = null;
607
641
  var _fullAccess = createFullAccessService({ sm: sm });
@@ -735,6 +769,17 @@ function createProjectContext(opts) {
735
769
  getClients: function () { return clients; },
736
770
  onFeedback: _logFeedbackDelivery.deliver,
737
771
  });
772
+ var _issueWork = require("./project-issue-launch").attachIssueLaunch({
773
+ sm: sm, resolveDefaultAi: opts.resolveDefaultAi, getSdk: function () { return sdk; },
774
+ getLinuxUserForSession: getLinuxUserForSession, osUsers: osUsers,
775
+ ensureProjectAccessForSession: ensureProjectAccessForSession,
776
+ canStartWork: function (ws, actor) { return clients.has(ws) && ws.readyState === 1 && ws._clayUser === actor; },
777
+ onProcessingChanged: onProcessingChanged, sendTo: sendTo,
778
+ });
779
+ var _issues = require("./project-issues").attachProjectIssues({
780
+ service: opts.issuesService, projectSlug: slug, isMate: isMate, sm: sm,
781
+ sendTo: sendTo, getClients: function () { return clients; }, startWork: _issueWork,
782
+ });
738
783
  var _mateKnowledge = attachProjectMateKnowledge({
739
784
  service: opts.mateKnowledgeService || null,
740
785
  sm: sm,
@@ -866,6 +911,8 @@ function createProjectContext(opts) {
866
911
  // get the read-only cross-project set only when the Mate registry confirms
867
912
  // authoritative builtin Clay.
868
913
  try {
914
+ var issuesMcpConfig = _issues.createMcpServer(adapter, null);
915
+ if (issuesMcpConfig) servers["clay-issues"] = issuesMcpConfig;
869
916
  var projectLogsMcpConfig = _projectLogs.createMcpServer(adapter, null);
870
917
  if (projectLogsMcpConfig) servers[projectLogsMcpConfig.name || "clay-logs"] = projectLogsMcpConfig;
871
918
  } catch (e) {
@@ -1047,6 +1094,11 @@ function createProjectContext(opts) {
1047
1094
  }
1048
1095
  continue;
1049
1096
  }
1097
+ if (name === "clay-issues" && forSession) {
1098
+ var boundIssues = _issues.createMcpServer(adapter, forSession);
1099
+ if (boundIssues) { filtered[name] = boundIssues; hasAny = true; }
1100
+ continue;
1101
+ }
1050
1102
  if (name === "clay-logs" && forSession) {
1051
1103
  try {
1052
1104
  var boundLogs = _projectLogs.createMcpServer(adapter, forSession);
@@ -1131,6 +1183,15 @@ function createProjectContext(opts) {
1131
1183
  filtered[name] = mcpServers[name];
1132
1184
  hasAny = true;
1133
1185
  }
1186
+
1187
+ if (forSession) {
1188
+ var lateServers = _workspaceQuery.getLateSessionMcpServers(adapter, forSession, mcpServers, filtered);
1189
+ var lateNames = Object.keys(lateServers);
1190
+ for (var li = 0; li < lateNames.length; li++) {
1191
+ filtered[lateNames[li]] = lateServers[lateNames[li]];
1192
+ hasAny = true;
1193
+ }
1194
+ }
1134
1195
  return hasAny ? filtered : undefined;
1135
1196
  }
1136
1197
 
@@ -1161,13 +1222,17 @@ function createProjectContext(opts) {
1161
1222
  return !isMate && _sessionPair.workerPermission.isDriverOperated(session);
1162
1223
  },
1163
1224
  onProcessingChanged: onProcessingChanged,
1164
- onSessionAvailable: function () { if (_scheduledMessages) _scheduledMessages.notify(); },
1225
+ onSessionAvailable: function (session) {
1226
+ if (_scheduledMessages) _scheduledMessages.notify();
1227
+ if (_userMessage && session) setImmediate(function () { _userMessage.consumePendingMessage(session); });
1228
+ },
1165
1229
  onTurnDone: function (session, preview) {
1166
1230
  if (isMate) digestDmTurn(session, preview);
1167
1231
  else _sessionPair.handleTurnDone(session);
1168
1232
  if (_scheduledTaskExecution) _scheduledTaskExecution.onTurnDone(session);
1169
1233
  if (_autonomousRun) _autonomousRun.onTurnDone(session);
1170
1234
  if (session && typeof session.onTurnDone === "function") session.onTurnDone(session, preview);
1235
+ if (_userMessage) setImmediate(function () { _userMessage.consumePendingMessage(session); });
1171
1236
  },
1172
1237
  onRateLimitRejected: function (session, resetsAt) {
1173
1238
  if (_autonomousRun) _autonomousRun.onRateLimit(session, resetsAt);
@@ -1198,6 +1263,7 @@ function createProjectContext(opts) {
1198
1263
  _sessionDocument.getSystemPrompt(session),
1199
1264
  _capsuleCatalog.getSystemPrompt(session),
1200
1265
  _projectLogs.getSystemPrompt(session),
1266
+ _issues.getSystemPrompt(session),
1201
1267
  _scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
1202
1268
  _mateKnowledge.getSystemPrompt(session),
1203
1269
  _autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
@@ -1217,6 +1283,7 @@ function createProjectContext(opts) {
1217
1283
  .concat(_workspaceQuery.getDynamicToolDefs(session))
1218
1284
  .concat(_workspaceQuery.getHistoryDynamicToolDefs(session))
1219
1285
  .concat(_projectLogs.getDynamicToolDefs(session))
1286
+ .concat(_issues.getDynamicToolDefs(session))
1220
1287
  .concat(_mateKnowledge.getDynamicToolDefs(session))
1221
1288
  .concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
1222
1289
  .concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
@@ -1512,11 +1579,11 @@ function createProjectContext(opts) {
1512
1579
  }
1513
1580
 
1514
1581
  // --- DM messages (delegated to server-level handler) ---
1515
- if (msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response") {
1582
+ if (msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response" || msg.type === "default_ai_get" || msg.type === "default_ai_catalog_get" || msg.type === "default_ai_set" || msg.type === "cursor_sharing_get" || msg.type === "cursor_sharing_set") {
1516
1583
  if (typeof opts.onDmMessage === "function") opts.onDmMessage(ws, msg);
1517
1584
  return;
1518
1585
  }
1519
- if (msg.type === "home_clay_ask" || msg.type === "home_clay_session_resolve" || msg.type === "home_clay_log_resolve") {
1586
+ if (msg.type === "issue_reference_resolve" || msg.type === "home_clay_ask" || msg.type === "home_clay_session_resolve" || msg.type === "home_clay_log_resolve") {
1520
1587
  if (typeof opts.onDmMessage === "function") opts.onDmMessage(ws, msg, slug);
1521
1588
  return;
1522
1589
  }
@@ -1600,6 +1667,7 @@ function createProjectContext(opts) {
1600
1667
 
1601
1668
  // --- Project Logs (delegated to project-logs.js) ---
1602
1669
  if (_projectLogs.handleLogsMessage(ws, msg)) return;
1670
+ if (_issues.handleMessage(ws, msg)) return;
1603
1671
 
1604
1672
  // --- Notifications (delegated to project-notifications.js) ---
1605
1673
  if (_notifications.handleNotificationMessage(ws, msg)) return;
@@ -1818,6 +1886,7 @@ function createProjectContext(opts) {
1818
1886
  matesModule: matesModule,
1819
1887
  pushModule: pushModule,
1820
1888
  getSessionForWs: getSessionForWs,
1889
+ resolveDefaultAi: opts.resolveDefaultAi,
1821
1890
  canCreateScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canCreateInterview(ws, session)); },
1822
1891
  canStartScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canStartInterview(ws, session)); },
1823
1892
  onHumanPairStop: function (session) {
@@ -1825,6 +1894,7 @@ function createProjectContext(opts) {
1825
1894
  if (_scheduledTaskExecution) _scheduledTaskExecution.stopForSession(session, "The human stopped this scheduled run.");
1826
1895
  return stopped;
1827
1896
  },
1897
+ onPendingQueuePause: function (session, actor) { return _pendingMessageQueue.pause(session, actor); },
1828
1898
  getLinuxUserForSession: getLinuxUserForSession,
1829
1899
  ensureProjectAccessForSession: ensureProjectAccessForSession,
1830
1900
  getOsUserInfoForWs: getOsUserInfoForWs,
@@ -1871,6 +1941,16 @@ function createProjectContext(opts) {
1871
1941
  serverPort: serverPort,
1872
1942
  serverTls: serverTls,
1873
1943
  serverAuthToken: serverAuthToken,
1944
+ resolveDefaultAi: opts.resolveDefaultAi,
1945
+ });
1946
+ var _toolLlmRuntime = attachProjectToolLlmRuntime({
1947
+ users: usersModule,
1948
+ osUsers: osUsers,
1949
+ getProjectAccess: opts.getProjectAccess || function () { return { visibility: "public", ownerId: projectOwnerId || null }; },
1950
+ resolveModel: _models.resolveConfiguredModel,
1951
+ complete: toolLlm.complete,
1952
+ adapters: adapters,
1953
+ cwd: cwd,
1874
1954
  });
1875
1955
  var _delegatedSession = attachDelegatedSession({
1876
1956
  sm: sm,
@@ -1914,6 +1994,17 @@ function createProjectContext(opts) {
1914
1994
  return _sessionPair.workerPermission.isDriverOperated(session);
1915
1995
  },
1916
1996
  beginHumanPairTurn: function (session) { return _sessionPair.beginHumanTurn(session); },
1997
+ authorizePendingDispatch: function (session, actor, item) {
1998
+ if (!session || sm.sessions.get(session.localId) !== session || !item || item.projectSlug !== slug) return false;
1999
+ if (!isMate && _sessionPair.workerPermission && _sessionPair.workerPermission.isDriverOperated(session)) return false;
2000
+ if (!usersModule.isMultiUser()) return item.actorId === "default" || !!(actor && String(actor.id) === item.actorId);
2001
+ var freshActor = actor && actor.id && usersModule.findUserById(actor.id);
2002
+ if (!freshActor || String(freshActor.id) !== item.actorId) return false;
2003
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
2004
+ if (opts.canAccessProjectSlug && !opts.canAccessProjectSlug(freshActor.id, slug)) return false;
2005
+ if (!usersModule.canAccessSession(freshActor.id, session, access)) return false;
2006
+ return !osUsers || !!getLinuxUserForSession(session);
2007
+ },
1917
2008
  autonomousRun: _autonomousRun,
1918
2009
  cwd: cwd,
1919
2010
  slug: slug,
@@ -1955,6 +2046,7 @@ function createProjectContext(opts) {
1955
2046
  getHubSchedules: getHubSchedules,
1956
2047
  getProjectOwnerId: function () { return projectOwnerId; },
1957
2048
  _email: _email,
2049
+ pendingMessageQueue: _pendingMessageQueue,
1958
2050
  });
1959
2051
 
1960
2052
  var _shellCommand = attachShellCommand({
@@ -2107,6 +2199,7 @@ function createProjectContext(opts) {
2107
2199
  for (var wqti = 0; wqti < workspaceTools.length; wqti++) tools.push(workspaceTools[wqti]);
2108
2200
  var historyTools = _workspaceQuery.getHistoryBridgeTools(boundSession, normalizeToolSchema);
2109
2201
  for (var cht = 0; cht < historyTools.length; cht++) tools.push(historyTools[cht]);
2202
+ tools = tools.concat(_issues.getBridgeTools(boundSession, normalizeToolSchema));
2110
2203
  var logsTools = _projectLogs.getBridgeTools(boundSession, normalizeToolSchema);
2111
2204
  for (var plti = 0; plti < logsTools.length; plti++) tools.push(logsTools[plti]);
2112
2205
  var knowledgeTools = _mateKnowledge.getBridgeTools(boundSession, normalizeToolSchema);
@@ -2134,7 +2227,7 @@ function createProjectContext(opts) {
2134
2227
  // Debate tools were added above only when this request had an
2135
2228
  // exact session. Never duplicate them or advertise the static,
2136
2229
  // intentionally unbound proposal server.
2137
- if (inAppNames[i] === "clay-debate" || inAppNames[i] === "clay-mate-creation" || inAppNames[i] === "clay-workspace" || inAppNames[i] === "clay-history" || inAppNames[i] === "clay-logs" || inAppNames[i] === "clay-knowledge") continue;
2230
+ if (inAppNames[i] === "clay-debate" || inAppNames[i] === "clay-mate-creation" || inAppNames[i] === "clay-workspace" || inAppNames[i] === "clay-history" || inAppNames[i] === "clay-issues" || inAppNames[i] === "clay-logs" || inAppNames[i] === "clay-knowledge") continue;
2138
2231
  extractServerTools(inAppNames[i], localMcp[inAppNames[i]]);
2139
2232
  }
2140
2233
  }
@@ -2209,13 +2302,14 @@ function createProjectContext(opts) {
2209
2302
  if (boundSession && serverName === "clay-history") {
2210
2303
  return _workspaceQuery.callHistoryBridgeTool(boundSession, toolName, args);
2211
2304
  }
2305
+ if (boundSession && serverName === "clay-issues") return _issues.callBridgeTool(boundSession, toolName, args);
2212
2306
  if (boundSession && serverName === "clay-logs") {
2213
2307
  return _projectLogs.callBridgeTool(boundSession, toolName, args);
2214
2308
  }
2215
2309
  if (boundSession && serverName === "clay-knowledge") {
2216
2310
  return _mateKnowledge.callBridgeTool(boundSession, toolName, args);
2217
2311
  }
2218
- if (serverName === "clay-sessions" || serverName === "clay-notes" || serverName === "clay-handoff" || serverName === "clay-documents" || serverName === "clay-debate" || serverName === "clay-mate-creation" || serverName === "clay-workspace" || serverName === "clay-history" || serverName === "clay-logs" || serverName === "clay-knowledge") {
2312
+ if (serverName === "clay-sessions" || serverName === "clay-notes" || serverName === "clay-handoff" || serverName === "clay-documents" || serverName === "clay-debate" || serverName === "clay-mate-creation" || serverName === "clay-workspace" || serverName === "clay-history" || serverName === "clay-issues" || serverName === "clay-logs" || serverName === "clay-knowledge") {
2219
2313
  return Promise.reject(new Error("Session-bound tool requires a valid Clay session: " + serverName + "/" + toolName));
2220
2314
  }
2221
2315
  if (sessionOnly) return Promise.reject(new Error("Session tool not found: " + serverName + "/" + toolName));
@@ -2302,6 +2396,7 @@ function createProjectContext(opts) {
2302
2396
  _email: _email,
2303
2397
  restoreDebateState: restoreDebateState,
2304
2398
  scheduledMessages: _scheduledMessages,
2399
+ sendCursorSharingState: opts.sendCursorSharingState,
2305
2400
  stopFileWatch: stopFileWatch,
2306
2401
  stopAllDirWatches: stopAllDirWatches,
2307
2402
  getProjectOwnerId: function () { return projectOwnerId; },
@@ -2561,20 +2656,9 @@ function createProjectContext(opts) {
2561
2656
  setProjectOwner: function (ownerId) { projectOwnerId = ownerId; },
2562
2657
  getProjectOwner: function () { return projectOwnerId; },
2563
2658
  getToolLlmConfig: function (ws, alias) {
2564
- return _models.resolveConfiguredModel(ws, alias);
2565
- },
2566
- completeToolLlm: async function (ws, args) {
2567
- var owner = projectOwnerId ? usersModule.findUserById(projectOwnerId) : null;
2568
- var selection = await _models.resolveConfiguredModel(ws, args.model);
2569
- return toolLlm.complete({
2570
- adapters: adapters,
2571
- cwd: cwd,
2572
- linuxUser: owner && owner.linuxUser ? owner.linuxUser : null,
2573
- args: args,
2574
- selection: selection,
2575
- timeoutMs: 60000,
2576
- });
2659
+ return _toolLlmRuntime.getConfiguration(ws, alias);
2577
2660
  },
2661
+ completeToolLlm: _toolLlmRuntime.complete,
2578
2662
  refreshUserProfile: function (userId) {
2579
2663
  var user = usersModule.findUserById(userId);
2580
2664
  if (!user) return;
package/lib/public/app.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { initIssueLinks } from './modules/clay-issue-links.js';
2
+ import { initIssues, closeIssues } from './modules/issues.js';
3
+ import { initIssuesContext } from './modules/issues-context.js';
1
4
  import { avatarUrl, userAvatarUrl, mateAvatarUrl } from './modules/avatar.js';
2
5
  import { showToast, escapeHtml } from './modules/utils.js';
3
6
  import { refreshIcons, iconHtml } from './modules/icons.js';
@@ -67,6 +70,7 @@ import { initHomeSurfaceBoot } from './modules/home-surface-boot.js';
67
70
  import { rememberHomePrimarySurface } from './modules/home-surface.js';
68
71
  import { initRateLimit, handleRateLimitEvent as _rlHandleRateLimitEvent, updateRateLimitUsage as _rlUpdateRateLimitUsage, addScheduledMessageBubble as _rlAddScheduledMessageBubble, removeScheduledMessageBubble as _rlRemoveScheduledMessageBubble, handleFastModeState as _rlHandleFastModeState, getScheduledMsgEl, resetRateLimitState } from './modules/app-rate-limit.js';
69
72
  import { initCursors, handleRemoteCursorMove as _curHandleRemoteCursorMove, handleRemoteCursorLeave as _curHandleRemoteCursorLeave, handleRemoteSelection as _curHandleRemoteSelection, clearRemoteCursors as _curClearRemoteCursors, initCursorToggle } from './modules/app-cursors.js';
73
+ import { initDefaultAi } from './modules/default-ai.js';
70
74
  import { initFavicon, updateFavicon as _favUpdateFavicon, setSendBtnMode as _favSetSendBtnMode, blinkIO as _favBlinkIO, blinkSessionDot as _favBlinkSessionDot, updateCrossProjectBlink as _favUpdateCrossProjectBlink, startUrgentBlink as _favStartUrgentBlink, stopUrgentBlink as _favStopUrgentBlink, setActivity as _favSetActivity } from './modules/app-favicon.js';
71
75
  import { initHeader, closeSessionInfoPopover as _hdrCloseSessionInfoPopover, updateHistorySentinel as _hdrUpdateHistorySentinel, requestMoreHistory as _hdrRequestMoreHistory, prependOlderHistory as _hdrPrependOlderHistory } from './modules/app-header.js';
72
76
  import { initSessionActions } from './modules/session-actions.js';
@@ -76,6 +80,7 @@ import { initSkillInstall, requireSkills as _siRequireSkills, handleSkillInstall
76
80
  import { initDebateUi, showDebateConcludeConfirm as _debShowDebateConcludeConfirm, exitDebateConcludeMode as _debExitDebateConcludeMode, handleDebateConcludeSend as _debHandleDebateConcludeSend, showDebateEndedMode as _debShowDebateEndedMode, exitDebateEndedMode as _debExitDebateEndedMode, showDebateUserFloor as _debShowDebateUserFloor, exitDebateFloorMode as _debExitDebateFloorMode, handleDebateFloorSend as _debHandleDebateFloorSend, renderDebateUserFloorDone as _debRenderDebateUserFloorDone, showDebateSticky as _debShowDebateSticky, showDebateBottomBar as _debShowDebateBottomBar, removeDebateBottomBar as _debRemoveDebateBottomBar, sendDebateStickyComment as _debSendDebateStickyComment, updateDebateRound as _debUpdateDebateRound } from './modules/app-debate-ui.js';
77
81
  import { initLoopUi, updateLoopInputVisibility as _loopUpdateLoopInputVisibility, updateLoopButton as _loopUpdateLoopButton, showLoopBanner as _loopShowLoopBanner, updateLoopBanner as _loopUpdateLoopBanner, updateRalphBars as _loopUpdateRalphBars, showRalphCraftingBar as _loopShowRalphCraftingBar, showRalphApprovalBar as _loopShowRalphApprovalBar, updateRalphApprovalStatus as _loopUpdateRalphApprovalStatus, openRalphPreviewModal as _loopOpenRalphPreviewModal, showExecModal as _loopShowExecModal, closeExecModal as _loopCloseExecModal, updateExecModalStatus as _loopUpdateExecModalStatus } from './modules/app-loop-ui.js';
78
82
  import { initBackgroundTasksUi } from './modules/background-tasks-ui.js';
83
+ import { initPendingMessageQueue } from './modules/pending-message-queue.js';
79
84
  import { initAppNotifications, handleNotificationsState as _notifHandleState, handleNotificationCreated as _notifHandleCreated, handleNotificationDismissed as _notifHandleDismissed, handleNotificationDismissedAll as _notifHandleDismissedAll } from './modules/app-notifications.js';
80
85
  import { initWhatsNew, handleWhatsNewState as _wnHandleState, handleWhatsNewSeenResult as _wnHandleSeenResult } from './modules/whats-new.js';
81
86
  import { initWhatsNewArticle, openArticle as openWhatsNewArticle } from './modules/whats-new-article.js';
@@ -316,7 +321,16 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
316
321
  isHeadlessMode: false,
317
322
  savedMainSlug: null,
318
323
  connected: false,
324
+ fileReadRequest: null,
325
+ pendingFileNavigation: null,
326
+ cursorSharingEnabled: false,
327
+ cursorSharingHydrated: false,
319
328
  pendingOutboundMessages: [],
329
+ pendingMessageQueue: { projectSlug: null, sessionId: null, revision: 0, paused: false, items: [], loading: false, error: "" },
330
+ pendingMessageQueueRequests: {},
331
+ pendingMessageQueueEdit: null,
332
+ pendingMessageQueueDrag: null,
333
+ pendingMessageQueueFocusReturn: null,
320
334
  searchClayChatState: null,
321
335
  homeChatMateId: null,
322
336
  homeChatSessionId: null,
@@ -367,6 +381,18 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
367
381
  projectLogsCanDelete: false,
368
382
  projectLogsUnread: 0,
369
383
  projectLogsSeenRevisions: {},
384
+ issuesOpen: false,
385
+ issuesView: "list",
386
+ issuesSelected: null,
387
+ issuesRows: [],
388
+ issuesRequests: {},
389
+ issuesViewRequest: null,
390
+ issuesViewGeneration: 0,
391
+ issuesCache: {},
392
+ issuesEpoch: 0,
393
+ issuesSearchTimer: null,
394
+ issueLinkRequests: {},
395
+ issueLinkOpenRef: null,
370
396
  scheduledTasksOpen: false,
371
397
  scheduledTasksWide: false,
372
398
  scheduledTasksFullscreen: false,
@@ -389,6 +415,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
389
415
  capsulesPreferenceLoaded: false,
390
416
  homeToolRegistryLoaded: false,
391
417
  toolScanErrors: [],
418
+ defaultAiState: { loading: false, saving: false, refreshRequestId: null, saveRequestId: null, catalogRequestIds: {}, catalogs: {}, preference: null, selection: null, installedVendors: [], accountAvailable: true, accountId: null, serverEpoch: null, canonicalRevision: 0, error: "" },
419
+ defaultAiDraft: { vendor: "", model: "", effort: "" },
420
+ defaultAiDraftDirty: false,
392
421
 
393
422
  // dm
394
423
  dmTargetUser: null,
@@ -540,6 +569,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
540
569
  // mcp-ui.js / etc.) attach click handlers by ID.
541
570
  initToolPalettes();
542
571
  initProjectLogs();
572
+ initIssues();
573
+ initIssuesContext();
574
+ initIssueLinks();
543
575
  initScheduledTasks();
544
576
  initSidebar(sidebarCtx);
545
577
  var wsGetter = function () { return _getWsRef(); };
@@ -728,6 +760,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
728
760
 
729
761
  initLoopUi();
730
762
  initBackgroundTasksUi();
763
+ initPendingMessageQueue();
731
764
  initAutonomousRun();
732
765
  initLoopInterview();
733
766
 
@@ -990,6 +1023,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
990
1023
  }
991
1024
  }
992
1025
  initCursorToggle();
1026
+ initDefaultAi();
993
1027
  // Apply RBAC UI gating
994
1028
  var _perms = store.get('permissions');
995
1029
  if (_perms) {
@@ -1158,7 +1192,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1158
1192
  setBrowserRefresh(function () { if (isNotesBrowserOpen()) renderNotesBrowser(); });
1159
1193
  // Opening the browser claims the single right workbench slot. Registered here
1160
1194
  // so the browser module never imports the other tools.
1161
- registerExclusiveClosers([closeProjectLogs, closeScheduledTasks, closeFileViewer, closeTerminal]);
1195
+ registerExclusiveClosers([closeIssues, closeProjectLogs, closeScheduledTasks, closeFileViewer, closeTerminal]);
1162
1196
 
1163
1197
  // --- Sticky Notes sidebar button (create new note) ---
1164
1198
  var stickyNotesSidebarBtn = $("sticky-notes-sidebar-btn");
@@ -1200,9 +1234,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
1200
1234
  var fileBrowserBtn = $("file-browser-btn");
1201
1235
  var gitPlacardMoreBtn = $("git-placard-more");
1202
1236
  var terminalSidebarBtn = $("terminal-sidebar-btn");
1203
- if (fileBrowserBtn) fileBrowserBtn.addEventListener("click", function () { closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1204
- if (gitPlacardMoreBtn) gitPlacardMoreBtn.addEventListener("click", function () { closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1205
- if (terminalSidebarBtn) terminalSidebarBtn.addEventListener("click", function () { closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1237
+ if (fileBrowserBtn) fileBrowserBtn.addEventListener("click", function () { closeIssues(); closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1238
+ if (gitPlacardMoreBtn) gitPlacardMoreBtn.addEventListener("click", function () { closeIssues(); closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1239
+ if (terminalSidebarBtn) terminalSidebarBtn.addEventListener("click", function () { closeIssues(); closeProjectLogs(); closeScheduledTasks(); if (isNotesBrowserOpen()) closeNotesBrowser(); if (isSchedulerOpen()) closeScheduler(); });
1206
1240
 
1207
1241
  // --- Legacy scheduled Loop UI ---
1208
1242
  function updateLoopInputVisibility(loop) { _loopUpdateLoopInputVisibility(loop); }
@@ -0,0 +1,33 @@
1
+ .default-ai-popover {
2
+ position: fixed;
3
+ z-index: 10050;
4
+ box-sizing: border-box;
5
+ padding: 18px;
6
+ border: 1px solid var(--border);
7
+ border-radius: 14px;
8
+ background: var(--bg);
9
+ box-shadow: 0 18px 46px rgba(0, 0, 0, .38);
10
+ color: var(--text);
11
+ font-size: 13px;
12
+ max-height: calc(100vh - 24px);
13
+ overflow-y: auto;
14
+ }
15
+
16
+ .default-ai-popover.hidden { display: none; }
17
+
18
+ .default-ai-title { font-size: 16px; font-weight: 700; letter-spacing: -.01em; }
19
+ .default-ai-copy { margin: 5px 0 14px; color: var(--text-secondary, #9699a5); line-height: 1.45; }
20
+ .default-ai-label { display: block; margin-top: 10px; color: var(--text-secondary); font-size: 11px; font-weight: 600; }
21
+ .default-ai-select { display: block; width: 100%; min-height: 38px; margin-top: 5px; padding: 8px 30px 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--input-bg); color: var(--text); font: inherit; }
22
+ .default-ai-select:focus-visible, .default-ai-save:focus-visible, .default-ai-button:focus-visible { outline: 2px solid var(--accent, #8f89ff); outline-offset: 2px; }
23
+ .default-ai-save { width: 100%; min-height: 38px; margin-top: 10px; padding: 9px; border: 0; border-radius: 8px; background: var(--accent, #635bff); color: #fff; font-weight: 700; cursor: pointer; }
24
+ .default-ai-save:disabled, .default-ai-select:disabled { opacity: .48; cursor: not-allowed; }
25
+ .default-ai-status { padding: 10px 0 0; color: var(--text-secondary, #9699a5); line-height: 1.4; }
26
+ .default-ai-error { color: var(--error); }
27
+ .default-ai-button { position: relative; }
28
+ .default-ai-avatar img { display: block; width: 18px; height: 18px; border-radius: 5px; }
29
+ .default-ai-button .default-ai-avatar:empty::after { content: '✦'; color: var(--accent, #8f89ff); font-size: 16px; }
30
+
31
+ @media (max-width: 420px) {
32
+ .default-ai-popover { padding: 15px; border-radius: 12px; }
33
+ }
@@ -973,12 +973,14 @@
973
973
  align-items: center;
974
974
  justify-content: space-between;
975
975
  gap: 4px;
976
+ flex-wrap: wrap;
976
977
  }
977
978
 
978
979
  #input-bottom-right {
979
980
  display: flex;
980
981
  align-items: center;
981
982
  gap: 4px;
983
+ margin-left: auto;
982
984
  flex-shrink: 0;
983
985
  }
984
986
 
@@ -1012,13 +1014,94 @@
1012
1014
  display: flex;
1013
1015
  align-items: center;
1014
1016
  gap: 2px;
1015
- flex-wrap: nowrap;
1016
- overflow: hidden;
1017
+ flex-wrap: wrap;
1018
+ flex: 0 0 auto;
1019
+ max-width: 100%;
1020
+ overflow: visible;
1017
1021
  }
1018
1022
 
1019
- /* Allow popovers (context picker) to escape when open */
1020
- #attach-wrap:has(#context-sources-picker:not(.hidden)) {
1021
- overflow: visible;
1023
+ .composer-tool-group {
1024
+ position: relative;
1025
+ display: flex;
1026
+ align-items: center;
1027
+ gap: 2px;
1028
+ flex: none;
1029
+ }
1030
+
1031
+ .composer-tool-group > .hidden {
1032
+ display: none !important;
1033
+ }
1034
+
1035
+ .composer-tool-group:not(:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))) {
1036
+ display: none;
1037
+ }
1038
+
1039
+ /* A group owns its divider only when it still has a visible control. This
1040
+ keeps hidden mode-specific groups from leaving a stray bar. */
1041
+ .composer-tool-group + .composer-tool-group:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))::before {
1042
+ content: "";
1043
+ display: block;
1044
+ flex: 0 0 1px;
1045
+ width: 1px;
1046
+ height: 18px;
1047
+ margin: 0 4px;
1048
+ background: var(--border-subtle);
1049
+ opacity: 0.8;
1050
+ }
1051
+
1052
+ .composer-tool-group-inputs:not(:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))) + .composer-tool-group-collaboration::before {
1053
+ display: none;
1054
+ }
1055
+
1056
+ /* If Collaboration is empty, Execution still follows the visible Inputs
1057
+ group; if both preceding groups are empty, suppress the divider entirely. */
1058
+ .composer-tool-group-inputs:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"])) ~ .composer-tool-group-execution:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))::before {
1059
+ content: "";
1060
+ display: block;
1061
+ flex: 0 0 1px;
1062
+ width: 1px;
1063
+ height: 18px;
1064
+ margin: 0 4px;
1065
+ background: var(--border-subtle);
1066
+ opacity: 0.8;
1067
+ }
1068
+
1069
+ .composer-tool-group-inputs:not(:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))) + .composer-tool-group-collaboration:not(:has(> :not(.hidden):not([style*="display: none"]):not([style*="display:none"]))) ~ .composer-tool-group-execution::before {
1070
+ display: none;
1071
+ }
1072
+
1073
+ @media (max-width: 768px) {
1074
+ .composer-tool-group:not(:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))) {
1075
+ display: none;
1076
+ }
1077
+
1078
+ .composer-tool-group + .composer-tool-group:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))::before {
1079
+ margin-left: 3px;
1080
+ margin-right: 3px;
1081
+ }
1082
+
1083
+ .composer-tool-group + .composer-tool-group:not(:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"])))::before {
1084
+ display: none;
1085
+ }
1086
+
1087
+ .composer-tool-group-inputs:not(:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))) + .composer-tool-group-collaboration::before {
1088
+ display: none;
1089
+ }
1090
+
1091
+ .composer-tool-group-inputs:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"])) ~ .composer-tool-group-execution:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))::before {
1092
+ margin-left: 3px;
1093
+ margin-right: 3px;
1094
+ }
1095
+
1096
+ .composer-tool-group-inputs:not(:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))) + .composer-tool-group-collaboration:not(:has(> :not(.hidden):not(.desktop-only):not([style*="display: none"]):not([style*="display:none"]))) ~ .composer-tool-group-execution::before {
1097
+ display: none;
1098
+ }
1099
+
1100
+ body.tui-composer-active .composer-tool-group-execution,
1101
+ body.debate-floor-mode .composer-tool-group-collaboration,
1102
+ body.debate-floor-mode .composer-tool-group-execution {
1103
+ display: none !important;
1104
+ }
1022
1105
  }
1023
1106
 
1024
1107
  #attach-file-btn,
@@ -1422,6 +1505,7 @@
1422
1505
  body.tui-composer-active #schedule-btn,
1423
1506
  body.tui-composer-active #ask-mate-btn,
1424
1507
  body.tui-composer-active .composer-session-action,
1508
+ body.tui-composer-active .composer-tool-group-collaboration,
1425
1509
  body.tui-composer-active #active-vendor-indicator,
1426
1510
  body.tui-composer-active #config-chip-wrap {
1427
1511
  display: none !important;