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

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 (91) hide show
  1. package/lib/autonomous-run-record.js +32 -0
  2. package/lib/autonomous-run-scheduler.js +92 -0
  3. package/lib/durable-scheduler-record.js +61 -0
  4. package/lib/durable-scheduler-store.js +185 -0
  5. package/lib/durable-scheduler.js +495 -0
  6. package/lib/loop-cron.js +47 -0
  7. package/lib/loop-durable-schedule.js +159 -0
  8. package/lib/loop-guidance.js +35 -0
  9. package/lib/loop-registry-occurrence.js +76 -0
  10. package/lib/project-autonomous-run.js +498 -0
  11. package/lib/project-connection.js +4 -1
  12. package/lib/project-full-access.js +86 -0
  13. package/lib/project-http.js +3 -1
  14. package/lib/project-loop-interview.js +163 -0
  15. package/lib/project-loop.js +150 -64
  16. package/lib/project-pair-autonomous-stop.js +16 -0
  17. package/lib/project-pair-owned-stop.js +18 -0
  18. package/lib/project-pair-task-control.js +14 -1
  19. package/lib/project-scheduled-messages.js +316 -0
  20. package/lib/project-scheduled-task-execution.js +236 -0
  21. package/lib/project-scheduled-tasks.js +483 -0
  22. package/lib/project-session-pair.js +16 -19
  23. package/lib/project-sessions.js +60 -28
  24. package/lib/project-user-message.js +24 -18
  25. package/lib/project.js +260 -52
  26. package/lib/public/app.js +60 -23
  27. package/lib/public/css/home-hub.css +15 -2
  28. package/lib/public/css/home-sidebar.css +50 -2
  29. package/lib/public/css/input.css +71 -0
  30. package/lib/public/css/mates.css +0 -1
  31. package/lib/public/css/pane.css +58 -7
  32. package/lib/public/css/rewind.css +11 -2
  33. package/lib/public/css/scheduled-tasks.css +123 -0
  34. package/lib/public/css/sticky-notes.css +24 -4
  35. package/lib/public/index.html +19 -134
  36. package/lib/public/modules/app-loop-ui.js +4 -8
  37. package/lib/public/modules/app-messages.js +22 -6
  38. package/lib/public/modules/app-projects.js +2 -0
  39. package/lib/public/modules/app-rate-limit.js +8 -5
  40. package/lib/public/modules/autonomous-run.js +285 -0
  41. package/lib/public/modules/home-sidebar.js +16 -8
  42. package/lib/public/modules/home-surface.js +5 -0
  43. package/lib/public/modules/input.js +5 -4
  44. package/lib/public/modules/loop-interview.js +151 -0
  45. package/lib/public/modules/project-logs.js +2 -2
  46. package/lib/public/modules/scheduled-message-state.js +46 -0
  47. package/lib/public/modules/scheduled-task-edit-state.js +7 -0
  48. package/lib/public/modules/scheduled-task-engine.js +98 -0
  49. package/lib/public/modules/scheduled-task-form.js +122 -0
  50. package/lib/public/modules/scheduled-task-recurrence.js +46 -0
  51. package/lib/public/modules/scheduled-task-schedule-rule.js +115 -0
  52. package/lib/public/modules/scheduled-tasks.js +468 -0
  53. package/lib/public/modules/scheduler-config.js +6 -52
  54. package/lib/public/modules/scheduler.js +9 -21
  55. package/lib/public/modules/sidebar.js +0 -11
  56. package/lib/public/modules/split-view.js +2 -0
  57. package/lib/public/modules/sticky-notes-browser.js +1 -1
  58. package/lib/public/modules/sticky-notes.js +117 -4
  59. package/lib/public/modules/tool-palette-order.js +2 -3
  60. package/lib/public/modules/tools.js +5 -2
  61. package/lib/public/style.css +1 -0
  62. package/lib/record-revision.js +11 -0
  63. package/lib/schedule-rule.js +151 -0
  64. package/lib/schedule-validation.js +124 -0
  65. package/lib/scheduled-run-state.js +87 -0
  66. package/lib/scheduled-task-records.js +127 -0
  67. package/lib/scheduler.js +179 -196
  68. package/lib/sdk-bridge.js +123 -11
  69. package/lib/sdk-message-processor.js +3 -0
  70. package/lib/server-durable-scheduler.js +26 -0
  71. package/lib/server.js +22 -0
  72. package/lib/session-pair-factory.js +42 -4
  73. package/lib/session-pair-query-start.js +22 -0
  74. package/lib/session-query-tool-bridge.js +52 -0
  75. package/lib/session-split-groups.js +35 -1
  76. package/lib/sessions.js +51 -3
  77. package/lib/users-home-surface-preferences.js +2 -1
  78. package/lib/users-scheduled-task-preferences.js +44 -0
  79. package/lib/users.js +6 -0
  80. package/lib/ws-schema.js +25 -0
  81. package/lib/yoke/adapters/antigravity.js +1 -0
  82. package/lib/yoke/adapters/claude-worker.js +7 -4
  83. package/lib/yoke/adapters/claude.js +31 -1
  84. package/lib/yoke/adapters/codex.js +3 -0
  85. package/lib/yoke/claude-user-input.js +2 -0
  86. package/lib/yoke/index.js +2 -0
  87. package/lib/yoke/mcp-bridge-server.js +7 -1
  88. package/lib/yoke/session-tool-transport.js +53 -0
  89. package/lib/yoke/user-input.js +21 -7
  90. package/package.json +1 -1
  91. package/lib/public/modules/app-loop-wizard.js +0 -439
package/lib/project.js CHANGED
@@ -35,9 +35,15 @@ 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 { attachLoopInterview } = require("./project-loop-interview");
39
+ var { attachScheduledTasks, authorizeStoredOwner } = require("./project-scheduled-tasks");
40
+ var { attachScheduledTaskExecution } = require("./project-scheduled-task-execution");
41
+ var { createFullAccessService } = require("./project-full-access");
42
+ var { attachAutonomousRun } = require("./project-autonomous-run");
38
43
  var { attachShellCommand } = require("./project-shell-command");
39
44
  var { attachVendorLogin } = require("./project-vendor-login");
40
45
  var { attachConnection } = require("./project-connection");
46
+ var { attachProjectScheduledMessages } = require("./project-scheduled-messages");
41
47
  var { attachMcp } = require("./project-mcp");
42
48
  var { createLocalMcp } = require("./mcp-local");
43
49
  var { attachEmail: attachEmailModule } = require("./project-email");
@@ -149,6 +155,7 @@ function validateEnvString(str) {
149
155
 
150
156
  // YOKE adapter (replaces direct SDK access)
151
157
  var yoke = require("./yoke");
158
+ var createSessionQueryToolBridge = require("./session-query-tool-bridge").createSessionQueryToolBridge;
152
159
 
153
160
  // --- Shared constants ---
154
161
  var IGNORED_DIRS = new Set(["node_modules", ".git", ".next", "__pycache__", ".cache", "dist", "build", ".clay", ".claude-relay"]);
@@ -210,7 +217,14 @@ function createProjectContext(opts) {
210
217
  var moveScheduleToProject = opts.moveScheduleToProject || function () { return { ok: false, error: "Not supported" }; };
211
218
  var moveAllSchedulesToProject = opts.moveAllSchedulesToProject || function () { return { ok: false, error: "Not supported" }; };
212
219
  var getScheduleCount = opts.getScheduleCount || function () { return 0; };
213
- var onProcessingChanged = opts.onProcessingChanged || function () {};
220
+ var notifyProcessingChanged = opts.onProcessingChanged || function () {};
221
+ var _scheduledMessages = null;
222
+ var _scheduledTasks = null;
223
+ var _scheduledTaskExecution = null;
224
+ function onProcessingChanged() {
225
+ notifyProcessingChanged();
226
+ if (_scheduledMessages) _scheduledMessages.notify();
227
+ }
214
228
  var onSessionDone = opts.onSessionDone || function () {};
215
229
  var onPresenceChange = opts.onPresenceChange || function () {};
216
230
  var updateChannel = opts.updateChannel || "stable";
@@ -589,6 +603,8 @@ function createProjectContext(opts) {
589
603
  sm.defaultVendor = defaultVendor;
590
604
 
591
605
  var sdk = null;
606
+ var _autonomousRun = null;
607
+ var _fullAccess = createFullAccessService({ sm: sm });
592
608
  var _splitGroups = attachSplitGroups({
593
609
  sm: sm,
594
610
  clients: clients,
@@ -749,6 +765,10 @@ function createProjectContext(opts) {
749
765
  getLinuxUserForSession: getLinuxUserForSession,
750
766
  onProcessingChanged: onProcessingChanged,
751
767
  adapters: adapters,
768
+ onPartnerResult: function (driver, worker, token) {
769
+ if (_scheduledTaskExecution) _scheduledTaskExecution.onPartnerResult(driver, worker, token);
770
+ if (_autonomousRun) _autonomousRun.onPartnerResult(driver, worker, token);
771
+ },
752
772
  });
753
773
  var _sessionSpawn = attachSessionSpawn({
754
774
  cwd: cwd,
@@ -1137,17 +1157,28 @@ function createProjectContext(opts) {
1137
1157
  getWorkerPermissionRouter: function () {
1138
1158
  return isMate ? null : _sessionPair.workerPermission;
1139
1159
  },
1160
+ isDriverOperatedSession: function (session) {
1161
+ return !isMate && _sessionPair.workerPermission.isDriverOperated(session);
1162
+ },
1140
1163
  onProcessingChanged: onProcessingChanged,
1164
+ onSessionAvailable: function () { if (_scheduledMessages) _scheduledMessages.notify(); },
1141
1165
  onTurnDone: function (session, preview) {
1142
1166
  if (isMate) digestDmTurn(session, preview);
1143
1167
  else _sessionPair.handleTurnDone(session);
1168
+ if (_scheduledTaskExecution) _scheduledTaskExecution.onTurnDone(session);
1169
+ if (_autonomousRun) _autonomousRun.onTurnDone(session);
1144
1170
  if (session && typeof session.onTurnDone === "function") session.onTurnDone(session, preview);
1145
1171
  },
1172
+ onRateLimitRejected: function (session, resetsAt) {
1173
+ if (_autonomousRun) _autonomousRun.onRateLimit(session, resetsAt);
1174
+ },
1146
1175
  scheduleMessage: function (session, text, resetsAt) {
1147
- scheduleMessage(session, text, resetsAt);
1176
+ var owner = session && session.ownerId ? usersModule.findUserById(session.ownerId) : null;
1177
+ scheduleMessage(session, text, resetsAt, owner, null);
1148
1178
  },
1149
1179
  saveImageFile: saveImageFile,
1150
1180
  getAutoContinueSetting: function (session) {
1181
+ if (session && session.autonomousRun && ["armed", "running", "reviewing", "waiting-worker", "waiting-user", "paused"].indexOf(session.autonomousRun.state) !== -1) return false;
1151
1182
  // Per-user setting in multi-user mode
1152
1183
  if (usersModule.isMultiUser() && session && session.ownerId) {
1153
1184
  return usersModule.getAutoContinue(session.ownerId);
@@ -1167,7 +1198,9 @@ function createProjectContext(opts) {
1167
1198
  _sessionDocument.getSystemPrompt(session),
1168
1199
  _capsuleCatalog.getSystemPrompt(session),
1169
1200
  _projectLogs.getSystemPrompt(session),
1201
+ _scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
1170
1202
  _mateKnowledge.getSystemPrompt(session),
1203
+ _autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
1171
1204
  ]);
1172
1205
  },
1173
1206
  getRuntimeEnv: getRuntimeEnvForSession,
@@ -1184,7 +1217,81 @@ function createProjectContext(opts) {
1184
1217
  .concat(_workspaceQuery.getDynamicToolDefs(session))
1185
1218
  .concat(_workspaceQuery.getHistoryDynamicToolDefs(session))
1186
1219
  .concat(_projectLogs.getDynamicToolDefs(session))
1187
- .concat(_mateKnowledge.getDynamicToolDefs(session));
1220
+ .concat(_mateKnowledge.getDynamicToolDefs(session))
1221
+ .concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
1222
+ .concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
1223
+ .concat(_scheduledTaskExecution ? _scheduledTaskExecution.getToolDefs(session) : [])
1224
+ .concat(_autonomousRun ? _autonomousRun.getToolDefs(session) : []);
1225
+ },
1226
+ canUseSessionTools: function (session) {
1227
+ if (!session || sm.sessions.get(session.localId) !== session) return false;
1228
+ if (!usersModule.isMultiUser()) return true;
1229
+ if (!session.ownerId) return false;
1230
+ var owner = usersModule.findUserById(session.ownerId);
1231
+ if (!owner) return false;
1232
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1233
+ if (!usersModule.canAccessSession(session.ownerId, session, access)) return false;
1234
+ return !osUsers || !!getLinuxUserForSession(session);
1235
+ },
1236
+ });
1237
+
1238
+ _autonomousRun = attachAutonomousRun({
1239
+ sm: sm,
1240
+ isMate: isMate,
1241
+ sendTo: sendTo,
1242
+ fullAccess: _fullAccess,
1243
+ getSdk: function () { return sdk; },
1244
+ getSessionForWs: getSessionForWs,
1245
+ ensureProjectAccessForSession: ensureProjectAccessForSession,
1246
+ onProcessingChanged: onProcessingChanged,
1247
+ isDriverOperatedSession: function (session) { return _sessionPair.workerPermission.isDriverOperated(session); },
1248
+ canAccess: function (ws, session) {
1249
+ return !usersModule.isMultiUser() || !ws._clayUser || usersModule.canAccessSession(ws._clayUser.id, session, { visibility: "public" });
1250
+ },
1251
+ stopPair: function (session) { return _sessionPair.stopAutonomousWork(session); },
1252
+ stopBarrier: function (session) { return _sessionPair.stopBarrier(session); },
1253
+ durableScheduler: opts.durableScheduler,
1254
+ projectSlug: slug,
1255
+ isMultiUser: function () { return usersModule.isMultiUser(); },
1256
+ authorizeSession: function (session) {
1257
+ if (!session || sm.sessions.get(session.localId) !== session) return false;
1258
+ if (!usersModule.isMultiUser()) return !session.ownerId;
1259
+ var owner = session.ownerId && usersModule.findUserById(session.ownerId);
1260
+ if (!owner) return false;
1261
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1262
+ if (!usersModule.canAccessSession(owner.id, session, access)) return false;
1263
+ return !osUsers || !!getLinuxUserForSession(session);
1264
+ },
1265
+ });
1266
+ var _loopInterview = attachLoopInterview({
1267
+ sm: sm, sendTo: sendTo, sendToSession: sendToSession, sdk: sdk, autonomousRun: _autonomousRun, isMate: isMate,
1268
+ getSessionForWs: getSessionForWs,
1269
+ onProcessingChanged: onProcessingChanged, ensureProjectAccessForSession: ensureProjectAccessForSession,
1270
+ canAccess: function (ws, session) {
1271
+ return !usersModule.isMultiUser() || !ws._clayUser || usersModule.canAccessSession(ws._clayUser.id, session, { visibility: "public" });
1272
+ },
1273
+ isDriverOperatedSession: function (session) { return !isMate && _sessionPair.workerPermission.isDriverOperated(session); },
1274
+ });
1275
+
1276
+ _scheduledMessages = attachProjectScheduledMessages({
1277
+ scheduler: opts.durableScheduler,
1278
+ slug: slug,
1279
+ isMate: isMate,
1280
+ osUsers: osUsers,
1281
+ sm: sm,
1282
+ sdk: sdk,
1283
+ usersModule: usersModule,
1284
+ sendTo: sendTo,
1285
+ sendToSession: sendToSession,
1286
+ onProcessingChanged: onProcessingChanged,
1287
+ ensureProjectAccessForSession: ensureProjectAccessForSession,
1288
+ getProjectAccess: function () {
1289
+ if (isMate) return { visibility: "private", ownerId: projectOwnerId || null, allowedUsers: [] };
1290
+ return opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1291
+ },
1292
+ canAccessProjectSlug: opts.canAccessProjectSlug || function () { return !usersModule.isMultiUser(); },
1293
+ isDriverOperatedSession: function (session) {
1294
+ return !isMate && _sessionPair.workerPermission.isDriverOperated(session);
1188
1295
  },
1189
1296
  });
1190
1297
 
@@ -1202,8 +1309,39 @@ function createProjectContext(opts) {
1202
1309
  sendToSession: sendToSession,
1203
1310
  pushModule: pushModule,
1204
1311
  notificationsModule: _notifications,
1312
+ durableScheduler: opts.durableScheduler,
1313
+ isMultiUser: function () { return usersModule.isMultiUser(); },
1314
+ requiresLinuxUser: function () { return osUsers; },
1205
1315
  getHubSchedules: getHubSchedules,
1206
1316
  getLinuxUserForSession: getLinuxUserForSession,
1317
+ getScheduleActor: function (ws) {
1318
+ var scheduleSession = getSessionForWs(ws);
1319
+ var scheduleUser = ws && ws._clayUser ? usersModule.findUserById(ws._clayUser.id) : null;
1320
+ if (usersModule.isMultiUser() && (!scheduleSession || !scheduleUser || scheduleSession.ownerId !== scheduleUser.id)) return null;
1321
+ return {
1322
+ ownerId: scheduleUser && scheduleUser.id || null,
1323
+ ownerName: scheduleUser && (scheduleUser.displayName || scheduleUser.username) || null,
1324
+ sessionId: scheduleSession && scheduleSession.localId || null,
1325
+ };
1326
+ },
1327
+ authorizeScheduledRun: function (record) {
1328
+ return authorizeStoredOwner(record, {
1329
+ usersModule: usersModule,
1330
+ osUsers: osUsers,
1331
+ projectAccess: opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null },
1332
+ resolveLinuxUser: getOsUserInfoForLinuxUser,
1333
+ grantProjectAccess: function (linuxUser) { grantProjectAccess(cwd, linuxUser); },
1334
+ });
1335
+ },
1336
+ prepareScheduledTask: function (record, source) {
1337
+ return _scheduledTaskExecution ? _scheduledTaskExecution.prepare(record, source) : { ok: false, error: "Scheduled execution is not ready." };
1338
+ },
1339
+ executeScheduledTask: function (record, source, prepared) {
1340
+ return _scheduledTaskExecution ? _scheduledTaskExecution.trigger(record, source, prepared) : { ok: false, error: "Scheduled execution is not ready." };
1341
+ },
1342
+ canExecuteScheduledTask: function (record) {
1343
+ return !!(_scheduledTaskExecution && _scheduledTaskExecution.runnable(record).ok);
1344
+ },
1207
1345
  onProcessingChanged: onProcessingChanged,
1208
1346
  hydrateImageRefs: hydrateImageRefs,
1209
1347
  });
@@ -1214,6 +1352,75 @@ function createProjectContext(opts) {
1214
1352
  var stopLoop = _loop.stopLoop;
1215
1353
  var resumeLoop = _loop.resumeLoop;
1216
1354
 
1355
+ _scheduledTaskExecution = attachScheduledTaskExecution({
1356
+ cwd: cwd, sm: sm, registry: loopRegistry, sessionPair: _sessionPair,
1357
+ getSdk: function () { return sdk; }, getLinuxUserForSession: getLinuxUserForSession,
1358
+ requiresLinuxUser: function () { return osUsers; },
1359
+ onProcessingChanged: onProcessingChanged,
1360
+ authorize: function (record) {
1361
+ return authorizeStoredOwner(record, {
1362
+ usersModule: usersModule, osUsers: osUsers,
1363
+ projectAccess: opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null },
1364
+ resolveLinuxUser: getOsUserInfoForLinuxUser,
1365
+ grantProjectAccess: function (linuxUser) { grantProjectAccess(cwd, linuxUser); },
1366
+ });
1367
+ },
1368
+ });
1369
+
1370
+ _scheduledTasks = attachScheduledTasks({
1371
+ cwd: cwd,
1372
+ projectSlug: slug,
1373
+ projectTitle: title || project,
1374
+ isMate: isMate,
1375
+ sm: sm,
1376
+ sdk: sdk,
1377
+ registry: loopRegistry,
1378
+ usersModule: usersModule,
1379
+ sendTo: sendTo,
1380
+ sendToSession: sendToSession,
1381
+ getSessionForWs: getSessionForWs,
1382
+ ensureProjectAccessForSession: ensureProjectAccessForSession,
1383
+ onProcessingChanged: onProcessingChanged,
1384
+ isDriverOperatedSession: function (session) { return !isMate && _sessionPair.workerPermission.isDriverOperated(session); },
1385
+ canUseSession: function (session) {
1386
+ if (!session || sm.sessions.get(session.localId) !== session) return false;
1387
+ if (!usersModule.isMultiUser()) return !session.ownerId;
1388
+ if (!session.ownerId) return false;
1389
+ var sessionOwner = usersModule.findUserById(session.ownerId);
1390
+ if (!sessionOwner || !usersModule.getEffectivePermissions(sessionOwner, osUsers).scheduledTasks) return false;
1391
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1392
+ if (!usersModule.canAccessSession(session.ownerId, session, access)) return false;
1393
+ return !osUsers || !!getLinuxUserForSession(session);
1394
+ },
1395
+ canAccessRecord: function (session, record) {
1396
+ if (!usersModule.isMultiUser()) return !record.ownerId;
1397
+ if (record.ownerId) return record.ownerId === session.ownerId;
1398
+ var user = session && session.ownerId ? usersModule.findUserById(session.ownerId) : null;
1399
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1400
+ return !!(user && (user.role === "admin" || access.ownerId === session.ownerId));
1401
+ },
1402
+ canClaimOwner: function (session) {
1403
+ if (!usersModule.isMultiUser() || !session || !session.ownerId) return false;
1404
+ var user = usersModule.findUserById(session.ownerId);
1405
+ var access = opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public", ownerId: projectOwnerId || null };
1406
+ return !!(user && (user.role === "admin" || access.ownerId === session.ownerId));
1407
+ },
1408
+ canAccess: function (ws, session) {
1409
+ return !usersModule.isMultiUser() || !ws._clayUser || usersModule.canAccessSession(ws._clayUser.id, session, { visibility: "public" });
1410
+ },
1411
+ hasPermission: function (ws) {
1412
+ if (!ws._clayUser) return true;
1413
+ return !!usersModule.getEffectivePermissions(ws._clayUser, osUsers).scheduledTasks;
1414
+ },
1415
+ getEngineCatalog: function (ws, vendor) { return _models.getVendorCatalog(ws, vendor); },
1416
+ scheduledExecution: _scheduledTaskExecution,
1417
+ preflightRuntime: _sessionPair.preflightRuntime,
1418
+ resolveOwnerName: function (ownerId) {
1419
+ var user = ownerId ? usersModule.findUserById(ownerId) : null;
1420
+ return user && (user.displayName || user.username) || null;
1421
+ },
1422
+ });
1423
+
1217
1424
  // Mate CLAUDE.md crisis safety watcher
1218
1425
  var crisisWatcher = null;
1219
1426
  var crisisDebounce = null;
@@ -1270,51 +1477,16 @@ function createProjectContext(opts) {
1270
1477
  }
1271
1478
 
1272
1479
  // --- Schedule / cancel a message (used by WS handler and auto-continue) ---
1273
- function scheduleMessage(session, text, resetsAt) {
1274
- if (!session || !text || !resetsAt) return;
1275
- // Cancel any existing scheduled message
1276
- if (session.scheduledMessage && session.scheduledMessage.timer) {
1277
- clearTimeout(session.scheduledMessage.timer);
1278
- }
1279
- var isPastReset = resetsAt <= Date.now();
1280
- var schedDelay = isPastReset ? 5000 : Math.max(0, resetsAt - Date.now()) + 60000; // +1min buffer after reset, or 5s for immediate
1281
- var sendsAt = Date.now() + schedDelay;
1282
- var schedEntry = {
1283
- type: "scheduled_message_queued",
1284
- text: text,
1285
- resetsAt: sendsAt,
1286
- scheduledAt: Date.now(),
1287
- };
1288
- sm.sendAndRecord(session, schedEntry);
1289
- session.scheduledMessage = {
1290
- text: text,
1291
- resetsAt: resetsAt,
1292
- timer: setTimeout(function () {
1293
- session.scheduledMessage = null;
1294
- if (session.destroying) return;
1295
- console.log("[project] Scheduled message firing for session " + session.localId);
1296
- sm.sendAndRecord(session, { type: "scheduled_message_sent" });
1297
- var schedUserMsg = { type: "user_message", text: text, _ts: Date.now() };
1298
- session.history.push(schedUserMsg);
1299
- sm.appendToSessionFile(session, schedUserMsg);
1300
- sendToSession(session.localId, schedUserMsg);
1301
- session.isProcessing = true;
1302
- onProcessingChanged();
1303
- sendToSession(session.localId, { type: "status", status: "processing" });
1304
- sdk.startQuery(session, text, null, ensureProjectAccessForSession(session));
1305
- sm.broadcastSessionList();
1306
- }, schedDelay),
1307
- };
1480
+ function scheduleMessage(session, text, resetsAt, actorUser, targetWs) {
1481
+ return _scheduledMessages ? _scheduledMessages.schedule(session, text, resetsAt, actorUser, targetWs) : null;
1308
1482
  }
1309
1483
 
1310
- function cancelScheduledMessage(session) {
1311
- if (!session) return;
1312
- if (session.scheduledMessage && session.scheduledMessage.timer) {
1313
- clearTimeout(session.scheduledMessage.timer);
1314
- session.scheduledMessage = null;
1315
- session.rateLimitAutoContinuePending = false;
1316
- sm.sendAndRecord(session, { type: "scheduled_message_cancelled" });
1317
- }
1484
+ function cancelScheduledMessage(session, actorUser, targetWs, correlation) {
1485
+ return _scheduledMessages ? _scheduledMessages.cancel(session, actorUser, targetWs, null, correlation) : false;
1486
+ }
1487
+
1488
+ function sendScheduledMessageNow(session, actorUser, targetWs, correlation) {
1489
+ return _scheduledMessages ? _scheduledMessages.sendNow(session, actorUser, targetWs, correlation) : false;
1318
1490
  }
1319
1491
 
1320
1492
  function handleMessage(ws, msg) {
@@ -1442,6 +1614,9 @@ function createProjectContext(opts) {
1442
1614
  if (_sessionHandoff.handleMessage(ws, msg)) return;
1443
1615
  if (_splitGroups.handleMessage(ws, msg)) return;
1444
1616
  if (_models.handleMessage(ws, msg)) return;
1617
+ if (_scheduledTasks.handleMessage(ws, msg)) return;
1618
+ if (_loopInterview.handleMessage(ws, msg)) return;
1619
+ if (_autonomousRun.handleMessage(ws, msg)) return;
1445
1620
  if (_sessions.handleSessionsMessage(ws, msg)) return;
1446
1621
 
1447
1622
  // --- Filesystem, settings, env (delegated to project-filesystem.js) ---
@@ -1643,7 +1818,13 @@ function createProjectContext(opts) {
1643
1818
  matesModule: matesModule,
1644
1819
  pushModule: pushModule,
1645
1820
  getSessionForWs: getSessionForWs,
1646
- onHumanPairStop: function (session) { return _sessionPair.handleHumanStop(session); },
1821
+ canCreateScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canCreateInterview(ws, session)); },
1822
+ canStartScheduledTaskInterview: function (ws, session) { return !!(_scheduledTasks && _scheduledTasks.canStartInterview(ws, session)); },
1823
+ onHumanPairStop: function (session) {
1824
+ var stopped = _sessionPair.handleHumanStop(session);
1825
+ if (_scheduledTaskExecution) _scheduledTaskExecution.stopForSession(session, "The human stopped this scheduled run.");
1826
+ return stopped;
1827
+ },
1647
1828
  getLinuxUserForSession: getLinuxUserForSession,
1648
1829
  ensureProjectAccessForSession: ensureProjectAccessForSession,
1649
1830
  getOsUserInfoForWs: getOsUserInfoForWs,
@@ -1651,6 +1832,8 @@ function createProjectContext(opts) {
1651
1832
  onProcessingChanged: onProcessingChanged,
1652
1833
  broadcastPresence: broadcastPresence,
1653
1834
  adapter: adapter,
1835
+ fullAccessService: _fullAccess,
1836
+ autonomousRun: _autonomousRun,
1654
1837
  getProjectList: getProjectList,
1655
1838
  getProjectCount: getProjectCount,
1656
1839
  getScheduleCount: getScheduleCount,
@@ -1661,6 +1844,7 @@ function createProjectContext(opts) {
1661
1844
  isNewer: isNewer,
1662
1845
  scheduleMessage: scheduleMessage,
1663
1846
  cancelScheduledMessage: cancelScheduledMessage,
1847
+ sendScheduledMessageNow: sendScheduledMessageNow,
1664
1848
  getProjectOwnerId: function () { return projectOwnerId; },
1665
1849
  setProjectOwnerId: function (id) { projectOwnerId = id; },
1666
1850
  getUpdateChannel: function () { return updateChannel; },
@@ -1730,6 +1914,7 @@ function createProjectContext(opts) {
1730
1914
  return _sessionPair.workerPermission.isDriverOperated(session);
1731
1915
  },
1732
1916
  beginHumanPairTurn: function (session) { return _sessionPair.beginHumanTurn(session); },
1917
+ autonomousRun: _autonomousRun,
1733
1918
  cwd: cwd,
1734
1919
  slug: slug,
1735
1920
  isMate: isMate,
@@ -1827,8 +2012,9 @@ function createProjectContext(opts) {
1827
2012
  // Provides list_tools and call_tool operations over HTTP for mcp-bridge-server.js.
1828
2013
  // The normal Codex bridge excludes local MCP servers it manages natively;
1829
2014
  // Kiro's session-only bridge exposes only tools bound to its Clay session.
1830
- function getMcpBridgeHandler(sessionId, sessionOnly) {
2015
+ function getMcpBridgeHandler(sessionId, sessionOnly, queryGeneration) {
1831
2016
  var boundSession = Number.isInteger(sessionId) ? sm.sessions.get(sessionId) : null;
2017
+ var queryToolBridge = createSessionQueryToolBridge({ session: boundSession, sdk: sdk });
1832
2018
  // Build set of local MCP server names to exclude (Codex handles these natively)
1833
2019
  var localMcpNames = {};
1834
2020
  try {
@@ -1875,6 +2061,8 @@ function createProjectContext(opts) {
1875
2061
  }
1876
2062
 
1877
2063
  if (boundSession) {
2064
+ var queryTools = queryToolBridge.listTools(queryGeneration);
2065
+ for (var qti = 0; qti < queryTools.length; qti++) tools.push(queryTools[qti]);
1878
2066
  var pairTools = _sessionPair.getToolDefs(boundSession);
1879
2067
  for (var pti = 0; pti < pairTools.length; pti++) {
1880
2068
  tools.push({
@@ -1923,6 +2111,14 @@ function createProjectContext(opts) {
1923
2111
  for (var plti = 0; plti < logsTools.length; plti++) tools.push(logsTools[plti]);
1924
2112
  var knowledgeTools = _mateKnowledge.getBridgeTools(boundSession, normalizeToolSchema);
1925
2113
  for (var mkti = 0; mkti < knowledgeTools.length; mkti++) tools.push(knowledgeTools[mkti]);
2114
+ var interviewTools = _loopInterview ? _loopInterview.getToolDefs(boundSession) : [];
2115
+ for (var liti = 0; liti < interviewTools.length; liti++) {
2116
+ tools.push({ server: "clay-until-complete", name: interviewTools[liti].name, description: interviewTools[liti].description, inputSchema: normalizeToolSchema(interviewTools[liti].inputSchema) });
2117
+ }
2118
+ var autonomousTools = _autonomousRun ? _autonomousRun.getToolDefs(boundSession) : [];
2119
+ for (var ati = 0; ati < autonomousTools.length; ati++) {
2120
+ tools.push({ server: "clay-until-complete", name: autonomousTools[ati].name, description: autonomousTools[ati].description, inputSchema: normalizeToolSchema(autonomousTools[ati].inputSchema) });
2121
+ }
1926
2122
  }
1927
2123
 
1928
2124
  if (sessionOnly) return Promise.resolve(tools);
@@ -1957,6 +2153,8 @@ function createProjectContext(opts) {
1957
2153
  return Promise.resolve(tools);
1958
2154
  },
1959
2155
  callTool: function (serverName, toolName, args) {
2156
+ var queryCall = queryToolBridge.callTool(queryGeneration, serverName, toolName, args);
2157
+ if (queryCall) return queryCall;
1960
2158
  if (boundSession && serverName === "clay-sessions") {
1961
2159
  var pairTools = _sessionPair.getToolDefs(boundSession);
1962
2160
  for (var pti = 0; pti < pairTools.length; pti++) {
@@ -1989,6 +2187,16 @@ function createProjectContext(opts) {
1989
2187
  }
1990
2188
  }
1991
2189
  }
2190
+ if (boundSession && serverName === "clay-until-complete") {
2191
+ var interviewTools = _loopInterview ? _loopInterview.getToolDefs(boundSession) : [];
2192
+ for (var iti = 0; iti < interviewTools.length; iti++) {
2193
+ if (interviewTools[iti].name === toolName && typeof interviewTools[iti].handler === "function") return Promise.resolve(interviewTools[iti].handler(args || {}));
2194
+ }
2195
+ var autonomousTools = _autonomousRun ? _autonomousRun.getToolDefs(boundSession) : [];
2196
+ for (var ati = 0; ati < autonomousTools.length; ati++) {
2197
+ if (autonomousTools[ati].name === toolName && typeof autonomousTools[ati].handler === "function") return Promise.resolve(autonomousTools[ati].handler(args || {}));
2198
+ }
2199
+ }
1992
2200
  if (boundSession && serverName === "clay-debate") {
1993
2201
  return _debateProposal.callBridgeTool(boundSession, toolName, args);
1994
2202
  }
@@ -2082,6 +2290,7 @@ function createProjectContext(opts) {
2082
2290
  _notifications: _notifications,
2083
2291
  _splitGroups: _splitGroups,
2084
2292
  _vendorLogin: _vendorLogin,
2293
+ autonomousRun: _autonomousRun,
2085
2294
  resolveSessionForView: _sessions.resolveSessionForView,
2086
2295
  hydrateImageRefs: hydrateImageRefs,
2087
2296
  broadcastClientCount: broadcastClientCount,
@@ -2092,6 +2301,7 @@ function createProjectContext(opts) {
2092
2301
  saveContextSources: saveContextSources,
2093
2302
  _email: _email,
2094
2303
  restoreDebateState: restoreDebateState,
2304
+ scheduledMessages: _scheduledMessages,
2095
2305
  stopFileWatch: stopFileWatch,
2096
2306
  stopAllDirWatches: stopAllDirWatches,
2097
2307
  getProjectOwnerId: function () { return projectOwnerId; },
@@ -2119,6 +2329,8 @@ function createProjectContext(opts) {
2119
2329
 
2120
2330
  // --- Destroy ---
2121
2331
  function destroy() {
2332
+ if (_scheduledMessages) _scheduledMessages.shutdown();
2333
+ if (_autonomousRun) _autonomousRun.shutdown();
2122
2334
  _loop.stopTimer();
2123
2335
  _email.destroy();
2124
2336
  stopFileWatch();
@@ -2130,10 +2342,6 @@ function createProjectContext(opts) {
2130
2342
  clearTimeout(session.autoContinueTimer);
2131
2343
  session.autoContinueTimer = null;
2132
2344
  }
2133
- if (session.scheduledMessage && session.scheduledMessage.timer) {
2134
- clearTimeout(session.scheduledMessage.timer);
2135
- session.scheduledMessage = null;
2136
- }
2137
2345
  if (session.abortController) {
2138
2346
  try { session.abortController.abort(); } catch (e) {}
2139
2347
  }