@surething/cockpit 1.0.224 → 1.0.225

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 (86) hide show
  1. package/.next-prod/BUILD_ID +1 -1
  2. package/.next-prod/app-path-routes-manifest.json +2 -2
  3. package/.next-prod/build-manifest.json +2 -2
  4. package/.next-prod/prerender-manifest.json +3 -3
  5. package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/.next-prod/server/app/_global-error.html +1 -1
  7. package/.next-prod/server/app/_global-error.rsc +1 -1
  8. package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  10. package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  11. package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next-prod/server/app/_not-found.html +2 -2
  16. package/.next-prod/server/app/_not-found.rsc +2 -2
  17. package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +2 -2
  18. package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
  19. package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +2 -2
  20. package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  21. package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  22. package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  23. package/.next-prod/server/app/api/chat/route.js +1 -1
  24. package/.next-prod/server/app/api/chat/stop/route.js +1 -1
  25. package/.next-prod/server/app/api/claude-stats/route.js +1 -1
  26. package/.next-prod/server/app/api/comments/route.js +1 -1
  27. package/.next-prod/server/app/api/files/expanded/route.js +1 -1
  28. package/.next-prod/server/app/api/files/init/route.js +1 -1
  29. package/.next-prod/server/app/api/files/recent/route.js +1 -1
  30. package/.next-prod/server/app/api/note/route.js +1 -1
  31. package/.next-prod/server/app/api/pinned-sessions/route.js +1 -1
  32. package/.next-prod/server/app/api/project-settings/route.js +1 -1
  33. package/.next-prod/server/app/api/project-state/route.js +1 -1
  34. package/.next-prod/server/app/api/projects/route.js +1 -1
  35. package/.next-prod/server/app/api/review/[id]/route.js +1 -1
  36. package/.next-prod/server/app/api/review/identify/route.js +1 -1
  37. package/.next-prod/server/app/api/review/order/route.js +1 -1
  38. package/.next-prod/server/app/api/review/route.js +1 -1
  39. package/.next-prod/server/app/api/review/users/route.js +1 -1
  40. package/.next-prod/server/app/api/scheduled-tasks/route.js +1 -1
  41. package/.next-prod/server/app/api/services/config/route.js +1 -1
  42. package/.next-prod/server/app/api/session/[sessionId]/fork/route.js +1 -1
  43. package/.next-prod/server/app/api/session/[sessionId]/history/route.js +1 -1
  44. package/.next-prod/server/app/api/session-by-path/route.js +1 -1
  45. package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js +1 -1
  46. package/.next-prod/server/app/api/sessions/projects/route.js +1 -1
  47. package/.next-prod/server/app/api/sessions/route.js +1 -1
  48. package/.next-prod/server/app/api/settings/route.js +1 -1
  49. package/.next-prod/server/app/api/skills/[id]/route.js +1 -1
  50. package/.next-prod/server/app/api/terminal/aliases/route.js +1 -1
  51. package/.next-prod/server/app/api/terminal/bubble-order/route.js +1 -1
  52. package/.next-prod/server/app/api/terminal/env/route.js +1 -1
  53. package/.next-prod/server/app/api/terminal/history/route.js +1 -1
  54. package/.next-prod/server/app/m/page_client-reference-manifest.js +1 -1
  55. package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
  56. package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
  57. package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
  58. package/.next-prod/server/app-paths-manifest.json +2 -2
  59. package/.next-prod/server/chunks/2637.js +1 -1
  60. package/.next-prod/server/chunks/2939.js +1 -1
  61. package/.next-prod/server/chunks/6955.js +1 -1
  62. package/.next-prod/server/chunks/7003.js +1 -1
  63. package/.next-prod/server/chunks/8916.js +1 -1
  64. package/.next-prod/server/chunks/9658.js +8 -8
  65. package/.next-prod/server/middleware-build-manifest.js +1 -1
  66. package/.next-prod/server/pages/404.html +2 -2
  67. package/.next-prod/server/pages/500.html +1 -1
  68. package/.next-prod/server/server-reference-manifest.json +1 -1
  69. package/.next-prod/static/chunks/2576-7459f532e98e12d6.js +25 -0
  70. package/.next-prod/static/chunks/5156-04537ce3c95435b2.js +3 -0
  71. package/.next-prod/static/chunks/9469-424d7be830f93183.js +14 -0
  72. package/.next-prod/trace +13 -13
  73. package/.next-prod/trace-build +1 -1
  74. package/dist/{chunk-BM3ZNL5S.mjs → chunk-7T2FX6X4.mjs} +1 -1
  75. package/dist/{chunk-5BOGGUGT.mjs → chunk-EYFKVFTP.mjs} +9 -0
  76. package/dist/{chunk-SDWQ72YQ.mjs → chunk-WULEYRUC.mjs} +5 -1
  77. package/dist/httpApi.mjs +2 -2
  78. package/dist/scheduledTasks.mjs +85 -52
  79. package/dist/{server-DRMO2DUJ.mjs → server-TRLQURYS.mjs} +2 -2
  80. package/dist/wsServer.mjs +4 -4
  81. package/package.json +1 -1
  82. package/.next-prod/static/chunks/2576-fe33a1bae037bd9b.js +0 -25
  83. package/.next-prod/static/chunks/5156-2953531342c13696.js +0 -3
  84. package/.next-prod/static/chunks/9469-fb5c6f08ba6bab74.js +0 -14
  85. /package/.next-prod/static/{cTq1mDm5D_oZLiumFG6di → WSUa0F_X_5GfMavORSxeX}/_buildManifest.js +0 -0
  86. /package/.next-prod/static/{cTq1mDm5D_oZLiumFG6di → WSUa0F_X_5GfMavORSxeX}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- [{"name":"run-webpack","duration":21097348,"timestamp":205608532,"id":14,"parentId":1,"tags":{},"startTime":1782189063918,"traceId":"f46704213552fc13"},{"name":"run-typescript","duration":17048715,"timestamp":226715813,"id":1591,"parentId":1,"tags":{},"startTime":1782189085025,"traceId":"f46704213552fc13"},{"name":"static-check","duration":1123954,"timestamp":243805338,"id":1594,"parentId":1,"tags":{},"startTime":1782189102115,"traceId":"f46704213552fc13"},{"name":"static-generation","duration":5683030,"timestamp":245375182,"id":1864,"parentId":1,"tags":{},"startTime":1782189103684,"traceId":"f46704213552fc13"},{"name":"collect-build-traces","duration":19350921,"timestamp":244929922,"id":1861,"parentId":1,"tags":{},"startTime":1782189103239,"traceId":"f46704213552fc13"},{"name":"telemetry-flush","duration":55,"timestamp":264284860,"id":1873,"parentId":1,"tags":{},"startTime":1782189122594,"traceId":"f46704213552fc13"},{"name":"next-build","duration":58835290,"timestamp":205449636,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782189063759,"traceId":"f46704213552fc13"}]
1
+ [{"name":"run-webpack","duration":21296988,"timestamp":324162232,"id":14,"parentId":1,"tags":{},"startTime":1782222901329,"traceId":"ca2feaa78d558124"},{"name":"run-typescript","duration":17419600,"timestamp":345469969,"id":1591,"parentId":1,"tags":{},"startTime":1782222922637,"traceId":"ca2feaa78d558124"},{"name":"static-check","duration":1170810,"timestamp":362928791,"id":1594,"parentId":1,"tags":{},"startTime":1782222940095,"traceId":"ca2feaa78d558124"},{"name":"static-generation","duration":6156547,"timestamp":364227904,"id":1864,"parentId":1,"tags":{},"startTime":1782222941395,"traceId":"ca2feaa78d558124"},{"name":"collect-build-traces","duration":20165323,"timestamp":364100254,"id":1861,"parentId":1,"tags":{},"startTime":1782222941267,"traceId":"ca2feaa78d558124"},{"name":"telemetry-flush","duration":56,"timestamp":384269532,"id":1873,"parentId":1,"tags":{},"startTime":1782222961436,"traceId":"ca2feaa78d558124"},{"name":"next-build","duration":60284028,"timestamp":323985571,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1782222901152,"traceId":"ca2feaa78d558124"}]
@@ -13,7 +13,7 @@ import {
13
13
  getTerminalOutputPath,
14
14
  readJsonFile,
15
15
  writeJsonFile
16
- } from "./chunk-5BOGGUGT.mjs";
16
+ } from "./chunk-EYFKVFTP.mjs";
17
17
 
18
18
  // packages/feature/console/src/server/plugins/browser/BrowserBridge.ts
19
19
  import { WebSocket } from "ws";
@@ -28801,6 +28801,14 @@ function withFileLock(filePath, fn2) {
28801
28801
  });
28802
28802
  return run6;
28803
28803
  }
28804
+ function mutateJsonFile(filePath, defaultValue, mutate5) {
28805
+ return withFileLock(filePath, async () => {
28806
+ const current2 = await readJsonFile(filePath, defaultValue);
28807
+ const next4 = await mutate5(current2);
28808
+ await writeJsonFile(filePath, next4);
28809
+ return next4;
28810
+ });
28811
+ }
28804
28812
 
28805
28813
  // packages/shared/effect-core/src/logger.ts
28806
28814
  var parseLevel = (raw) => {
@@ -29921,5 +29929,6 @@ export {
29921
29929
  readJsonFile,
29922
29930
  writeJsonFile,
29923
29931
  withFileLock,
29932
+ mutateJsonFile,
29924
29933
  AppRuntime
29925
29934
  };
@@ -10,7 +10,7 @@ import {
10
10
  readJsonFile,
11
11
  withFileLock,
12
12
  writeJsonFile
13
- } from "./chunk-5BOGGUGT.mjs";
13
+ } from "./chunk-EYFKVFTP.mjs";
14
14
 
15
15
  // packages/feature/agent/src/server/state/globalState.ts
16
16
  import { createReadStream, existsSync } from "fs";
@@ -359,6 +359,9 @@ function getRunSnapshot(key) {
359
359
  function isRunActive(key) {
360
360
  return registry.get(key)?.status === "running";
361
361
  }
362
+ function getRunSessionId(key) {
363
+ return registry.get(key)?.sessionId ?? null;
364
+ }
362
365
  function requestStop(key) {
363
366
  const r = registry.get(key);
364
367
  if (!r) return false;
@@ -388,6 +391,7 @@ export {
388
391
  getSessionPreview,
389
392
  getRunSnapshot,
390
393
  isRunActive,
394
+ getRunSessionId,
391
395
  requestStop,
392
396
  addRunListener
393
397
  };
package/dist/httpApi.mjs CHANGED
@@ -21,10 +21,10 @@ import {
21
21
  unregisterBrowser,
22
22
  unregisterTerminal,
23
23
  writeStdinToCommand
24
- } from "./chunk-BM3ZNL5S.mjs";
24
+ } from "./chunk-7T2FX6X4.mjs";
25
25
  import {
26
26
  getTerminalHistoryPath
27
- } from "./chunk-5BOGGUGT.mjs";
27
+ } from "./chunk-EYFKVFTP.mjs";
28
28
  import "./chunk-7P6ASYW6.mjs";
29
29
 
30
30
  // src/lib/httpApi.ts
@@ -1,9 +1,10 @@
1
1
  import {
2
+ getRunSessionId,
2
3
  getRunSnapshot,
3
4
  isRunActive,
4
5
  requestStop,
5
6
  updateGlobalState
6
- } from "./chunk-SDWQ72YQ.mjs";
7
+ } from "./chunk-WULEYRUC.mjs";
7
8
  import {
8
9
  AgentError,
9
10
  AppRuntime,
@@ -16,13 +17,16 @@ import {
16
17
  getClaudeSessionPath,
17
18
  getDeepseekSessionPath,
18
19
  getOllamaSessionPath,
20
+ mutateJsonFile,
19
21
  readJsonFile,
22
+ withFileLock,
20
23
  writeJsonFile
21
- } from "./chunk-5BOGGUGT.mjs";
24
+ } from "./chunk-EYFKVFTP.mjs";
22
25
  import "./chunk-7P6ASYW6.mjs";
23
26
 
24
27
  // packages/feature/agent/src/server/scheduledTasks.ts
25
28
  import { existsSync } from "fs";
29
+ var MAX_CONSECUTIVE_FAILURES = 3;
26
30
  function parseCronField(field, min, max) {
27
31
  const values = [];
28
32
  for (const part of field.split(",")) {
@@ -67,7 +71,7 @@ function getNextCronTime(cronExpr, after = /* @__PURE__ */ new Date()) {
67
71
  }
68
72
  return after.getTime() + 864e5;
69
73
  }
70
- var sendHttpEngineMessageEff = (task, engine) => Effect_exports.tryPromise({
74
+ var sendHttpEngineMessageEff = (task, engine, startFresh) => Effect_exports.tryPromise({
71
75
  try: async () => {
72
76
  const isClaude = engine === "claude" || engine === "claude2";
73
77
  const url = isClaude ? `http://127.0.0.1:${task.port}/api/chat` : `http://127.0.0.1:${task.port}/api/chat/${engine}`;
@@ -76,7 +80,9 @@ var sendHttpEngineMessageEff = (task, engine) => Effect_exports.tryPromise({
76
80
  headers: { "Content-Type": "application/json" },
77
81
  body: JSON.stringify({
78
82
  prompt: task.message,
79
- sessionId: task.sessionId,
83
+ // Omit sessionId to start a brand-new session when the resume target is gone;
84
+ // the engine generates a fresh id (captured below via getRunSessionId).
85
+ ...startFresh ? {} : { sessionId: task.sessionId },
80
86
  cwd: task.cwd,
81
87
  engine,
82
88
  // route uses it for claude2's CLAUDE_CONFIG_DIR; no-op for the others
@@ -103,6 +109,13 @@ var sendHttpEngineMessageEff = (task, engine) => Effect_exports.tryPromise({
103
109
  if (!snap || snap.status === "error") {
104
110
  throw new Error(`${engine} run failed (session ${task.sessionId})`);
105
111
  }
112
+ if (startFresh) {
113
+ const newSessionId = getRunSessionId(key);
114
+ if (newSessionId && newSessionId !== task.sessionId) {
115
+ console.warn(`[ScheduledTask] task ${task.id}: rebound session ${task.sessionId} \u2192 ${newSessionId}`);
116
+ task.sessionId = newSessionId;
117
+ }
118
+ }
106
119
  return true;
107
120
  },
108
121
  // claude2 shares the 'claude' provider for error classification (same SDK).
@@ -132,16 +145,13 @@ var sendChatMessageEff = (task) => Effect_exports.gen(function* () {
132
145
  );
133
146
  }
134
147
  const sessionPath = sessionPathFor(engine, task);
135
- if (!sessionPath || !existsSync(sessionPath)) {
136
- return yield* Effect_exports.fail(
137
- new AgentError({
138
- provider: engine === "claude2" ? "claude" : engine,
139
- kind: "session-not-found",
140
- cause: new Error(`session file missing: ${sessionPath ?? `(no session for ${task.sessionId})`} (task ${task.id}, engine ${engine})`)
141
- })
148
+ const startFresh = !sessionPath || !existsSync(sessionPath);
149
+ if (startFresh) {
150
+ console.warn(
151
+ `[ScheduledTask] resume session missing (${sessionPath ?? `no session for ${task.sessionId}`}) for task ${task.id}, engine ${engine}; starting a fresh session`
142
152
  );
143
153
  }
144
- return yield* sendHttpEngineMessageEff(task, engine);
154
+ return yield* sendHttpEngineMessageEff(task, engine, startFresh);
145
155
  }).pipe(
146
156
  Effect_exports.catchAll(
147
157
  (err) => Effect_exports.gen(function* () {
@@ -245,9 +255,7 @@ var ScheduledTaskManager = class {
245
255
  async addTask(task) {
246
256
  await this.ensureInit();
247
257
  const fullTask = { ...task, port: this.getPort() };
248
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
249
- allTasks.push(fullTask);
250
- await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
258
+ await mutateJsonFile(SCHEDULED_TASKS_FILE, [], (allTasks) => [...allTasks, fullTask]);
251
259
  this.tasks.push(fullTask);
252
260
  if (!fullTask.paused && !fullTask.completed) {
253
261
  this.scheduleTask(fullTask);
@@ -259,12 +267,16 @@ var ScheduledTaskManager = class {
259
267
  */
260
268
  async updateTask(id, fields) {
261
269
  await this.ensureInit();
262
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
263
- const idx = allTasks.findIndex((t) => t.id === id);
264
- if (idx === -1) return null;
265
- const task = { ...allTasks[idx], ...fields };
266
- allTasks[idx] = task;
267
- await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
270
+ const task = await withFileLock(SCHEDULED_TASKS_FILE, async () => {
271
+ const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
272
+ const idx = allTasks.findIndex((t) => t.id === id);
273
+ if (idx === -1) return null;
274
+ const updated = { ...allTasks[idx], ...fields };
275
+ allTasks[idx] = updated;
276
+ await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
277
+ return updated;
278
+ });
279
+ if (!task) return null;
268
280
  const memIdx = this.tasks.findIndex((t) => t.id === id);
269
281
  if (memIdx !== -1) {
270
282
  this.tasks[memIdx] = task;
@@ -280,11 +292,15 @@ var ScheduledTaskManager = class {
280
292
  */
281
293
  async deleteTask(id) {
282
294
  await this.ensureInit();
283
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
284
- const idx = allTasks.findIndex((t) => t.id === id);
285
- if (idx === -1) return false;
286
- allTasks.splice(idx, 1);
287
- await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
295
+ const removed = await withFileLock(SCHEDULED_TASKS_FILE, async () => {
296
+ const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
297
+ const idx = allTasks.findIndex((t) => t.id === id);
298
+ if (idx === -1) return false;
299
+ allTasks.splice(idx, 1);
300
+ await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
301
+ return true;
302
+ });
303
+ if (!removed) return false;
288
304
  const memIdx = this.tasks.findIndex((t) => t.id === id);
289
305
  if (memIdx !== -1) {
290
306
  this.clearTimer(id);
@@ -316,7 +332,7 @@ var ScheduledTaskManager = class {
316
332
  nextFireTime = now + 6e4;
317
333
  }
318
334
  }
319
- return this.updateTask(id, { paused: false, nextFireTime });
335
+ return this.updateTask(id, { paused: false, nextFireTime, consecutiveFailures: 0 });
320
336
  }
321
337
  /**
322
338
  * Manually trigger a task (runs in the background; returns immediately; does not affect the existing schedule).
@@ -351,6 +367,7 @@ var ScheduledTaskManager = class {
351
367
  const success = await sendChatMessage(task);
352
368
  task.lastFiredAt = Date.now();
353
369
  task.lastResult = success ? "success" : "error";
370
+ task.consecutiveFailures = success ? 0 : (task.consecutiveFailures ?? 0) + 1;
354
371
  task.unread = true;
355
372
  await this.saveToDisk();
356
373
  if (this.onTaskFired) {
@@ -372,15 +389,17 @@ var ScheduledTaskManager = class {
372
389
  async markReadBySessionId(sessionId) {
373
390
  await this.ensureInit();
374
391
  const port = this.getPort();
375
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
376
- let changed = false;
377
- for (const task of allTasks) {
378
- if (task.port === port && task.sessionId === sessionId && task.unread) {
379
- task.unread = false;
380
- changed = true;
392
+ await withFileLock(SCHEDULED_TASKS_FILE, async () => {
393
+ const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
394
+ let changed = false;
395
+ for (const task of allTasks) {
396
+ if (task.port === port && task.sessionId === sessionId && task.unread) {
397
+ task.unread = false;
398
+ changed = true;
399
+ }
381
400
  }
382
- }
383
- if (changed) await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
401
+ if (changed) await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
402
+ });
384
403
  }
385
404
  /**
386
405
  * Mark all tasks as read (operates directly on disk; avoids dual-instance issues).
@@ -388,27 +407,31 @@ var ScheduledTaskManager = class {
388
407
  async markAllRead() {
389
408
  await this.ensureInit();
390
409
  const port = this.getPort();
391
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
392
- let changed = false;
393
- for (const task of allTasks) {
394
- if (task.port === port && task.unread) {
395
- task.unread = false;
396
- changed = true;
410
+ await withFileLock(SCHEDULED_TASKS_FILE, async () => {
411
+ const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
412
+ let changed = false;
413
+ for (const task of allTasks) {
414
+ if (task.port === port && task.unread) {
415
+ task.unread = false;
416
+ changed = true;
417
+ }
397
418
  }
398
- }
399
- if (changed) await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
419
+ if (changed) await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
420
+ });
400
421
  }
401
422
  /**
402
423
  * Reorder tasks by writing sortIndex values based on the given id array order.
403
424
  */
404
425
  async reorderTasks(orderedIds) {
405
426
  await this.ensureInit();
406
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
407
- for (let i = 0; i < orderedIds.length; i++) {
408
- const task = allTasks.find((t) => t.id === orderedIds[i]);
409
- if (task) task.sortIndex = i;
410
- }
411
- await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
427
+ await withFileLock(SCHEDULED_TASKS_FILE, async () => {
428
+ const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
429
+ for (let i = 0; i < orderedIds.length; i++) {
430
+ const task = allTasks.find((t) => t.id === orderedIds[i]);
431
+ if (task) task.sortIndex = i;
432
+ }
433
+ await writeJsonFile(SCHEDULED_TASKS_FILE, allTasks);
434
+ });
412
435
  }
413
436
  // ---- Internal ----
414
437
  /**
@@ -481,9 +504,17 @@ var ScheduledTaskManager = class {
481
504
  const success = await sendChatMessage(task);
482
505
  task.lastFiredAt = Date.now();
483
506
  task.lastResult = success ? "success" : "error";
507
+ task.consecutiveFailures = success ? 0 : (task.consecutiveFailures ?? 0) + 1;
484
508
  task.unread = true;
509
+ const tripped = !success && (task.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES;
485
510
  if (task.type === "once") {
486
511
  task.completed = true;
512
+ } else if (tripped) {
513
+ task.paused = true;
514
+ this.clearTimer(id);
515
+ console.warn(
516
+ `[ScheduledTask] auto-paused task ${id} after ${task.consecutiveFailures} consecutive failures`
517
+ );
487
518
  } else if (task.type === "interval" && task.intervalMinutes) {
488
519
  task.nextFireTime = Date.now() + task.intervalMinutes * 6e4;
489
520
  this.scheduleTask(task);
@@ -501,9 +532,11 @@ var ScheduledTaskManager = class {
501
532
  }
502
533
  async saveToDisk() {
503
534
  try {
504
- const allTasks = await readJsonFile(SCHEDULED_TASKS_FILE, []);
505
- const otherTasks = allTasks.filter((t) => t.port !== this.port);
506
- await writeJsonFile(SCHEDULED_TASKS_FILE, [...otherTasks, ...this.tasks]);
535
+ await mutateJsonFile(
536
+ SCHEDULED_TASKS_FILE,
537
+ [],
538
+ (allTasks) => [...allTasks.filter((t) => t.port !== this.port), ...this.tasks]
539
+ );
507
540
  } catch (error) {
508
541
  console.error("[ScheduledTaskManager] Failed to save:", error);
509
542
  }
@@ -41,12 +41,12 @@ import {
41
41
  unregisterTerminal,
42
42
  updateBrowserWs,
43
43
  writeStdinToCommand
44
- } from "./chunk-BM3ZNL5S.mjs";
44
+ } from "./chunk-7T2FX6X4.mjs";
45
45
  import {
46
46
  createNeo4jDriver,
47
47
  runCypherWithDriver,
48
48
  serializeValue
49
- } from "./chunk-5BOGGUGT.mjs";
49
+ } from "./chunk-EYFKVFTP.mjs";
50
50
  import "./chunk-7P6ASYW6.mjs";
51
51
  export {
52
52
  PtyRingBuffer,
package/dist/wsServer.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  addRunListener,
3
3
  getRunSnapshot,
4
4
  getSessionPreview
5
- } from "./chunk-SDWQ72YQ.mjs";
5
+ } from "./chunk-WULEYRUC.mjs";
6
6
  import {
7
7
  addExitListener,
8
8
  addOutputListener,
@@ -18,7 +18,7 @@ import {
18
18
  resolvePendingRequest,
19
19
  unregisterBrowser,
20
20
  writeStdinToCommand
21
- } from "./chunk-BM3ZNL5S.mjs";
21
+ } from "./chunk-7T2FX6X4.mjs";
22
22
  import {
23
23
  AppError,
24
24
  Effect_exports,
@@ -34,7 +34,7 @@ import {
34
34
  ValidationError,
35
35
  WSError,
36
36
  readJsonFile
37
- } from "./chunk-5BOGGUGT.mjs";
37
+ } from "./chunk-EYFKVFTP.mjs";
38
38
  import "./chunk-7P6ASYW6.mjs";
39
39
 
40
40
  // src/lib/wsServer.ts
@@ -690,7 +690,7 @@ var handleJupyter = (conn, bubbleId, cwd) => Effect_exports.gen(function* () {
690
690
  Effect_exports.annotateLogs("cwd", cwd)
691
691
  );
692
692
  const { kernelManager } = yield* Effect_exports.tryPromise({
693
- try: () => import("./server-DRMO2DUJ.mjs"),
693
+ try: () => import("./server-TRLQURYS.mjs"),
694
694
  catch: (cause) => new AppError({
695
695
  message: "failed to import kernel manager",
696
696
  cause
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surething/cockpit",
3
- "version": "1.0.224",
3
+ "version": "1.0.225",
4
4
  "description": "Claude Code GUI for parallel AI coding — drive Claude (default), Codex, DeepSeek, Kimi, or local Ollama in tabs. Multi-project sessions, terminal, browser & DB bubbles, code review, slash modes. Local-first, MIT.",
5
5
  "author": "Robert",
6
6
  "license": "MIT",