crewly 1.20.359 → 1.20.361

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 (99) hide show
  1. package/config/roles/developer/prompt.md +3 -1
  2. package/config/skills/registry.json +125 -1
  3. package/dist/backend/backend/src/constants.d.ts +132 -2
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +143 -3
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/index.d.ts +37 -0
  8. package/dist/backend/backend/src/index.d.ts.map +1 -1
  9. package/dist/backend/backend/src/index.js +106 -10
  10. package/dist/backend/backend/src/index.js.map +1 -1
  11. package/dist/backend/backend/src/services/agent/boot-pressure.d.ts +163 -0
  12. package/dist/backend/backend/src/services/agent/boot-pressure.d.ts.map +1 -0
  13. package/dist/backend/backend/src/services/agent/boot-pressure.js +235 -0
  14. package/dist/backend/backend/src/services/agent/boot-pressure.js.map +1 -0
  15. package/dist/backend/backend/src/services/agent/restore-queue.d.ts +50 -1
  16. package/dist/backend/backend/src/services/agent/restore-queue.d.ts.map +1 -1
  17. package/dist/backend/backend/src/services/agent/restore-queue.js +100 -5
  18. package/dist/backend/backend/src/services/agent/restore-queue.js.map +1 -1
  19. package/dist/backend/backend/src/services/memory/agent-memory-refresh.service.d.ts +11 -0
  20. package/dist/backend/backend/src/services/memory/agent-memory-refresh.service.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/memory/agent-memory-refresh.service.js +23 -2
  22. package/dist/backend/backend/src/services/memory/agent-memory-refresh.service.js.map +1 -1
  23. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts +8 -0
  24. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js +8 -0
  26. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js.map +1 -1
  27. package/dist/backend/backend/src/services/wiki/wiki-log-entries.d.ts +7 -0
  28. package/dist/backend/backend/src/services/wiki/wiki-log-entries.d.ts.map +1 -1
  29. package/dist/backend/backend/src/services/wiki/wiki-log-entries.js +10 -0
  30. package/dist/backend/backend/src/services/wiki/wiki-log-entries.js.map +1 -1
  31. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.d.ts +24 -0
  32. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.js +75 -9
  34. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.js.map +1 -1
  35. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts +19 -0
  36. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js +23 -2
  38. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/wiki/work-record.service.d.ts +23 -1
  40. package/dist/backend/backend/src/services/wiki/work-record.service.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/wiki/work-record.service.js +33 -5
  42. package/dist/backend/backend/src/services/wiki/work-record.service.js.map +1 -1
  43. package/dist/backend/backend/src/services/worktree/low-disk-guard.d.ts +92 -8
  44. package/dist/backend/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/worktree/low-disk-guard.js +98 -14
  46. package/dist/backend/backend/src/services/worktree/low-disk-guard.js.map +1 -1
  47. package/dist/backend/backend/src/services/worktree/tmp-janitor.d.ts +138 -0
  48. package/dist/backend/backend/src/services/worktree/tmp-janitor.d.ts.map +1 -0
  49. package/dist/backend/backend/src/services/worktree/tmp-janitor.js +199 -0
  50. package/dist/backend/backend/src/services/worktree/tmp-janitor.js.map +1 -0
  51. package/dist/backend/backend/src/services/worktree/transcript-janitor.d.ts +154 -0
  52. package/dist/backend/backend/src/services/worktree/transcript-janitor.d.ts.map +1 -0
  53. package/dist/backend/backend/src/services/worktree/transcript-janitor.js +357 -0
  54. package/dist/backend/backend/src/services/worktree/transcript-janitor.js.map +1 -0
  55. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.js +7 -2
  57. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/worktree/worktree-dirty.d.ts +16 -4
  59. package/dist/backend/backend/src/services/worktree/worktree-dirty.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/worktree/worktree-dirty.js +21 -5
  61. package/dist/backend/backend/src/services/worktree/worktree-dirty.js.map +1 -1
  62. package/dist/backend/backend/src/services/worktree/worktree-git.d.ts +18 -0
  63. package/dist/backend/backend/src/services/worktree/worktree-git.d.ts.map +1 -1
  64. package/dist/backend/backend/src/services/worktree/worktree-git.js +60 -0
  65. package/dist/backend/backend/src/services/worktree/worktree-git.js.map +1 -1
  66. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +164 -25
  67. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +494 -55
  69. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -1
  70. package/dist/backend/backend/src/services/worktree/worktree-tiers.d.ts +186 -0
  71. package/dist/backend/backend/src/services/worktree/worktree-tiers.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/worktree/worktree-tiers.js +279 -0
  73. package/dist/backend/backend/src/services/worktree/worktree-tiers.js.map +1 -0
  74. package/dist/backend/build-info.json +2 -2
  75. package/dist/cli/backend/src/constants.d.ts +132 -2
  76. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  77. package/dist/cli/backend/src/constants.js +143 -3
  78. package/dist/cli/backend/src/constants.js.map +1 -1
  79. package/dist/cli/backend/src/services/agent/boot-pressure.d.ts +163 -0
  80. package/dist/cli/backend/src/services/agent/boot-pressure.d.ts.map +1 -0
  81. package/dist/cli/backend/src/services/agent/boot-pressure.js +235 -0
  82. package/dist/cli/backend/src/services/agent/boot-pressure.js.map +1 -0
  83. package/dist/cli/backend/src/services/agent/restore-queue.d.ts +50 -1
  84. package/dist/cli/backend/src/services/agent/restore-queue.d.ts.map +1 -1
  85. package/dist/cli/backend/src/services/agent/restore-queue.js +100 -5
  86. package/dist/cli/backend/src/services/agent/restore-queue.js.map +1 -1
  87. package/dist/cli/backend/src/services/wiki/wiki-log-entries.d.ts +7 -0
  88. package/dist/cli/backend/src/services/wiki/wiki-log-entries.d.ts.map +1 -1
  89. package/dist/cli/backend/src/services/wiki/wiki-log-entries.js +10 -0
  90. package/dist/cli/backend/src/services/wiki/wiki-log-entries.js.map +1 -1
  91. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts +92 -8
  92. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -1
  93. package/dist/cli/backend/src/services/worktree/low-disk-guard.js +98 -14
  94. package/dist/cli/backend/src/services/worktree/low-disk-guard.js.map +1 -1
  95. package/dist/cli/backend/src/services/worktree/worktree-git.d.ts +18 -0
  96. package/dist/cli/backend/src/services/worktree/worktree-git.d.ts.map +1 -1
  97. package/dist/cli/backend/src/services/worktree/worktree-git.js +60 -0
  98. package/dist/cli/backend/src/services/worktree/worktree-git.js.map +1 -1
  99. package/package.json +1 -1
@@ -2,10 +2,13 @@
2
2
  * Low-disk guard — pure helpers for the disk janitor's free-space check.
3
3
  *
4
4
  * The janitor checks free space on the volume holding CREWLY_HOME every
5
- * 10 minutes. Below 15 GB it cleans up at once with shorter idle thresholds;
6
- * if space is still short it tells the owner (at most once per 24 h, or once
7
- * per 6 h and marked urgent below 5 GB) what it could not remove and why.
8
- * This file holds the decisions and the wording; the service does the I/O.
5
+ * 10 minutes. Below the low mark — max(15 GB, 8 % of the disk), because a
6
+ * ~1 TB Mac already misbehaves at 5–8 % free — it cleans up at once with
7
+ * shorter idle thresholds; if space is still short it tells the owner (at most
8
+ * once per 24 h, or once per 6 h and marked urgent below max(5 GB, 3 %)) what
9
+ * it freed, what it could not remove and why, and that it never touches the
10
+ * owner's own files. This file holds the decisions and the wording; the
11
+ * service does the I/O.
9
12
  *
10
13
  * @module services/worktree/low-disk-guard
11
14
  */
@@ -20,30 +23,81 @@ import { formatBytes } from './worktree-janitor.git.js';
20
23
  * @returns Free bytes for unprivileged users, or null when the probe failed
21
24
  */
22
25
  export async function readFreeBytes(p, statfs = defaultStatFs) {
26
+ return (await readDiskSpace(p, statfs))?.freeBytes ?? null;
27
+ }
28
+ /**
29
+ * Free and total bytes of the volume holding `p`.
30
+ *
31
+ * @param p - Any path on the volume
32
+ * @param statfs - Probe (default `fs.promises.statfs`)
33
+ * @returns Free and total bytes, or null when the probe failed
34
+ */
35
+ export async function readDiskSpace(p, statfs = defaultStatFs) {
23
36
  try {
24
37
  const s = await statfs(p);
25
- return Number(s.bavail) * Number(s.bsize);
38
+ const bsize = Number(s.bsize);
39
+ const total = s.blocks === undefined ? null : Number(s.blocks) * bsize;
40
+ return { freeBytes: Number(s.bavail) * bsize, totalBytes: total !== null && Number.isFinite(total) && total > 0 ? total : null };
26
41
  }
27
42
  catch {
28
43
  return null;
29
44
  }
30
45
  }
46
+ /**
47
+ * The low-disk mark for a volume: the absolute floor or a fraction of the
48
+ * disk, whichever is larger. A 926 GB disk gets ~74 GB, a 100 GB disk 15 GB.
49
+ *
50
+ * @param totalBytes - Disk size, or null when unknown (the floor is used)
51
+ * @returns Bytes below which free space counts as low
52
+ */
53
+ export function lowDiskThresholdBytes(totalBytes) {
54
+ const floor = WORKTREE_JANITOR_CONSTANTS.LOW_DISK_BYTES;
55
+ return totalBytes ? Math.max(floor, Math.round(totalBytes * WORKTREE_JANITOR_CONSTANTS.LOW_DISK_FRACTION)) : floor;
56
+ }
57
+ /**
58
+ * The critical mark for a volume: max(5 GB, 3 % of the disk).
59
+ *
60
+ * @param totalBytes - Disk size, or null when unknown (the floor is used)
61
+ * @returns Bytes below which free space counts as critical
62
+ */
63
+ export function criticalDiskThresholdBytes(totalBytes) {
64
+ const floor = WORKTREE_JANITOR_CONSTANTS.CRITICAL_DISK_BYTES;
65
+ return totalBytes ? Math.max(floor, Math.round(totalBytes * WORKTREE_JANITOR_CONSTANTS.CRITICAL_DISK_FRACTION)) : floor;
66
+ }
31
67
  async function defaultStatFs(p) {
32
68
  return fs.promises.statfs(p);
33
69
  }
34
70
  /**
35
- * Classify free space.
71
+ * Classify free space against the volume's marks.
36
72
  *
37
73
  * @param freeBytes - Free bytes
38
- * @returns `critical` below CRITICAL_DISK_BYTES, `low` below LOW_DISK_BYTES, else `ok`
74
+ * @param totalBytes - Disk size (null / omitted: the absolute floors apply)
75
+ * @returns `critical` below the critical mark, `low` below the low mark, else `ok`
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * diskLevel(50 * GB, 926 * GB); // 'low' (mark is 8 % = 74 GB)
80
+ * diskLevel(50 * GB); // 'ok' (no size: 15 GB floor)
81
+ * ```
39
82
  */
40
- export function diskLevel(freeBytes) {
41
- if (freeBytes < WORKTREE_JANITOR_CONSTANTS.CRITICAL_DISK_BYTES)
83
+ export function diskLevel(freeBytes, totalBytes = null) {
84
+ if (freeBytes < criticalDiskThresholdBytes(totalBytes))
42
85
  return 'critical';
43
- if (freeBytes < WORKTREE_JANITOR_CONSTANTS.LOW_DISK_BYTES)
86
+ if (freeBytes < lowDiskThresholdBytes(totalBytes))
44
87
  return 'low';
45
88
  return 'ok';
46
89
  }
90
+ /**
91
+ * Whether the crewly#1354 retention deletes are armed: the first real pass
92
+ * records the time and only logs; deletes start RETENTION_ARM_DELAY_MS later.
93
+ *
94
+ * @param state - Janitor state
95
+ * @param now - Current time (ms)
96
+ * @returns True when deletes may run
97
+ */
98
+ export function retentionArmed(state, now) {
99
+ return state.retentionArmedAt !== undefined && now - state.retentionArmedAt >= WORKTREE_JANITOR_CONSTANTS.RETENTION_ARM_DELAY_MS;
100
+ }
47
101
  /**
48
102
  * Idle threshold to use, shortened in low-disk mode.
49
103
  *
@@ -92,6 +146,27 @@ export function noticeToSend(level, state, now) {
92
146
  export function recordNotice(state, kind, now) {
93
147
  return { ...state, lastNoticeAt: now, ...(kind === 'urgent' ? { lastUrgentAt: now } : {}) };
94
148
  }
149
+ /**
150
+ * One short line of what a pass freed, by kind; empty when nothing was.
151
+ *
152
+ * @param freed - Bytes freed per kind
153
+ * @returns `"freed 3.2 GB (worktrees 2.1 GB, node_modules 1.1 GB)"` or ''
154
+ */
155
+ export function describeFreed(freed) {
156
+ const parts = [];
157
+ if (freed.worktrees > 0)
158
+ parts.push(`finished worktrees ${formatBytes(freed.worktrees)}`);
159
+ if (freed.stripped > 0)
160
+ parts.push(`node_modules of idle worktrees ${formatBytes(freed.stripped)}`);
161
+ if (freed.archived > 0)
162
+ parts.push(`archived worktrees ${formatBytes(freed.archived)}`);
163
+ if (freed.transcripts > 0)
164
+ parts.push(`old agent transcripts ${formatBytes(freed.transcripts)}`);
165
+ if (freed.tmp > 0)
166
+ parts.push(`temp files and logs ${formatBytes(freed.tmp)}`);
167
+ const total = freed.worktrees + freed.stripped + freed.archived + freed.transcripts + freed.tmp;
168
+ return total > 0 ? `freed ${formatBytes(total)} (${parts.join(', ')})` : '';
169
+ }
95
170
  const REASON_TEXT = {
96
171
  dirty: 'has changes that were never committed',
97
172
  'not-merged': 'has work that is not merged yet',
@@ -109,6 +184,10 @@ const REASON_TEXT = {
109
184
  'status-failed': 'git could not read it',
110
185
  'git-failed': 'git could not read it',
111
186
  'remove-failed': 'deleting it failed',
187
+ 'archive-failed': 'saving its changes as a patch failed, so it stays',
188
+ 'contains-git': 'holds a git checkout, which only the worktree rules may remove',
189
+ 'live-session': 'is the conversation of a running agent',
190
+ foreign: 'is not an agent conversation',
112
191
  };
113
192
  /**
114
193
  * Plain-English reason for a kept item.
@@ -129,10 +208,14 @@ export function describeReason(reason) {
129
208
  export function buildLowDiskNotice(input) {
130
209
  const title = input.urgent ? 'URGENT: disk almost full' : 'Disk space is running low';
131
210
  const lines = [];
132
- lines.push(`Only ${formatBytes(input.freeBytes)} free on this machine.${input.urgent ? ' Crewly and the agents may stop working soon.' : ''}`);
133
- lines.push(input.freedBytes > 0
134
- ? `I cleaned up ${formatBytes(input.freedBytes)} of finished work, but it is not enough.`
135
- : 'I found nothing more that is safe to delete on my own.');
211
+ const ofDisk = input.totalBytes ? ` of ${formatBytes(input.totalBytes)}` : '';
212
+ lines.push(`Only ${formatBytes(input.freeBytes)}${ofDisk} free on this machine.${input.urgent ? ' Crewly and the agents may stop working soon.' : ''}`);
213
+ const breakdown = input.freed ? describeFreed(input.freed) : '';
214
+ lines.push(breakdown
215
+ ? `Today I ${breakdown}, but it is not enough.`
216
+ : input.freedBytes > 0
217
+ ? `I cleaned up ${formatBytes(input.freedBytes)} of finished work, but it is not enough.`
218
+ : 'I found nothing more that is safe to delete on my own.');
136
219
  const top = [...input.items].sort((a, b) => b.bytes - a.bytes).slice(0, WORKTREE_JANITOR_CONSTANTS.LOW_DISK_REPORT_ITEMS);
137
220
  if (top.length > 0) {
138
221
  lines.push('', 'Biggest things I left alone:');
@@ -143,6 +226,7 @@ export function buildLowDiskNotice(input) {
143
226
  else {
144
227
  lines.push('Please free up some space.');
145
228
  }
229
+ lines.push('I only clean what agents left behind (worktrees, scratch dirs, agent transcripts, Crewly temp files and logs); your own files, downloads and app caches I never touch.');
146
230
  return { title, message: lines.join('\n') };
147
231
  }
148
232
  //# sourceMappingURL=low-disk-guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"low-disk-guard.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/low-disk-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAkCxD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,CAAS,EAAE,SAAmB,aAAa;IAC9E,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,CAAS;IACrC,OAAO,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,SAAiB;IAC1C,IAAI,SAAS,GAAG,0BAA0B,CAAC,mBAAmB;QAAE,OAAO,UAAU,CAAC;IAClF,IAAI,SAAS,GAAG,0BAA0B,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IACxE,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,OAAgB;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,QAAQ,GAAG,0BAA0B,CAAC,qBAAqB,CAAC;IAC3E,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,0BAA0B,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC,CAAC;AACpG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAgB,EAAE,KAAyB,EAAE,GAAW;IACpF,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC;QAChC,OAAO,IAAI,KAAK,SAAS,IAAI,GAAG,GAAG,IAAI,IAAI,0BAA0B,CAAC,gCAAgC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1H,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC;IAChC,OAAO,IAAI,KAAK,SAAS,IAAI,GAAG,GAAG,IAAI,IAAI,0BAA0B,CAAC,2BAA2B,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACrH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB,EAAE,IAAyB,EAAE,GAAW;IAC7F,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7F,CAAC;AAED,MAAM,WAAW,GAA2B;IAC3C,KAAK,EAAE,uCAAuC;IAC9C,YAAY,EAAE,iCAAiC;IAC/C,QAAQ,EAAE,oCAAoC;IAC9C,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EAAE,qEAAqE;IAClF,wBAAwB,EAAE,kDAAkD;IAC5E,mBAAmB,EAAE,uDAAuD;IAC5E,MAAM,EAAE,mBAAmB;IAC3B,MAAM,EAAE,6BAA6B;IACrC,gBAAgB,EAAE,+BAA+B;IACjD,sBAAsB,EAAE,2BAA2B;IACnD,kBAAkB,EAAE,+CAA+C;IACnE,+BAA+B,EAAE,yBAAyB;IAC1D,eAAe,EAAE,uBAAuB;IACxC,YAAY,EAAE,uBAAuB;IACrC,eAAe,EAAE,oBAAoB;CACrC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC5C,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,SAAS,MAAM,GAAG,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAKlC;IACA,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,2BAA2B,CAAC;IACtF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,QAAQ,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,yBAAyB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/I,KAAK,CAAC,IAAI,CACT,KAAK,CAAC,UAAU,GAAG,CAAC;QACnB,CAAC,CAAC,gBAAgB,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,0CAA0C;QACzF,CAAC,CAAC,wDAAwD,CAC3D,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC,qBAAqB,CAAC,CAAC;IAC1H,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,8BAA8B,CAAC,CAAC;QAC/C,KAAK,MAAM,EAAE,IAAI,GAAG;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3G,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,0DAA0D,CAAC,CAAC;IAC5E,CAAC;SAAM,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"low-disk-guard.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/low-disk-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Tmp and log janitor — retention for `<CREWLY_HOME>/tmp` and size caps for
3
+ * `<CREWLY_HOME>/logs/archive` and `logs/sessions` (crewly#1354).
4
+ *
5
+ * `~/.crewly/tmp` is where agents, skills and Crewly itself put scratch
6
+ * output (Slack downloads, release build dirs, previews, skill output) and
7
+ * nothing ever removed it (600 MB on one Mac); the log archive had a 7-day
8
+ * rule but no cap (600 MB) and session logs none at all (200 MB).
9
+ *
10
+ * Tmp rules: a top-level entry untouched (shallow tree mtime) for 7 days is
11
+ * removed. Some sub-directories are swept per child with their own age
12
+ * instead (`slack-files` / `slack-images` / `slack-pdfs` 30 days,
13
+ * `crewly-web-release` 2 days, `skill-output` 7 days) and the directory itself
14
+ * stays. Never removed: symlinks, anything a process or agent session has as
15
+ * cwd, and any entry holding a git checkout — a linked worktree in tmp
16
+ * belongs to the worktree rules (the service adds tmp to their discovery
17
+ * roots), and a clone may be the only copy of its history.
18
+ *
19
+ * Log caps: the oldest files go first until the directory fits; a file
20
+ * written in the last hour, or the log of a live session, is never deleted.
21
+ *
22
+ * @module services/worktree/tmp-janitor
23
+ */
24
+ /** A place a live process or agent is working in (same shape as the janitor's BusyCwd). */
25
+ export interface TmpBusyCwd {
26
+ cwd: string;
27
+ source: 'agent-session' | 'process';
28
+ }
29
+ /** Why a tmp entry was removed or kept. */
30
+ export type TmpReason = 'old' | 'recent' | 'contains-git' | 'process-inside' | 'agent-session-inside' | 'cwd-probe-failed' | 'symlink' | 'remove-failed';
31
+ /** Verdict for one tmp entry. */
32
+ export interface TmpEntryVerdict {
33
+ /** Absolute path */
34
+ path: string;
35
+ /** Bytes (0 when unknown) */
36
+ bytes: number;
37
+ /** What the sweep decided */
38
+ decision: 'remove' | 'keep';
39
+ /** The deciding rule */
40
+ reason: TmpReason;
41
+ /** Ms since last touched, when known */
42
+ idleMs?: number;
43
+ /** True when deleted */
44
+ removed: boolean;
45
+ /** Error text when deletion failed */
46
+ error?: string;
47
+ }
48
+ /** Result of one tmp sweep. */
49
+ export interface TmpSweepSummary {
50
+ /** The tmp root */
51
+ root: string;
52
+ /** Entries deleted */
53
+ removed: number;
54
+ /** Dry run: entries that would be deleted */
55
+ wouldRemove: number;
56
+ /** Bytes freed */
57
+ freedBytes: number;
58
+ /** Dry run: bytes that would be freed */
59
+ wouldFreeBytes: number;
60
+ /** Entries kept (`recent` ones are counted, not listed) */
61
+ kept: number;
62
+ /** Kept entries by reason */
63
+ keptReasons: Record<string, number>;
64
+ /** Verdicts (all but `recent`) */
65
+ entries: TmpEntryVerdict[];
66
+ }
67
+ /** Input of {@link sweepTmp}. */
68
+ export interface TmpSweepInput {
69
+ /** `<CREWLY_HOME>/tmp` */
70
+ root: string;
71
+ /** Busy cwds, or null when the probe failed (everything is then kept) */
72
+ busy: TmpBusyCwd[] | null;
73
+ /** Current time (ms) */
74
+ now: number;
75
+ /** When true, nothing is deleted */
76
+ dryRun: boolean;
77
+ /** Age for top-level entries (default TMP_MAX_AGE_MS) */
78
+ maxAgeMs?: number;
79
+ /** Per-child policies by sub-directory name (default TMP_CHILD_POLICIES) */
80
+ childPolicies?: Readonly<Record<string, number>>;
81
+ /** Size probe (default: shallow stat walk) */
82
+ sizeOf?: (p: string) => Promise<number | null>;
83
+ }
84
+ /** Bytes of a path by a bounded stat walk (no symlinks followed). */
85
+ export declare function shallowBytes(p: string, depth?: number): Promise<number>;
86
+ /**
87
+ * One sweep of the tmp root. Never throws; an unreadable root yields an
88
+ * empty summary.
89
+ *
90
+ * @param input - Root, busy cwds, clock, dry-run flag
91
+ * @returns What was removed and kept
92
+ */
93
+ export declare function sweepTmp(input: TmpSweepInput): Promise<TmpSweepSummary>;
94
+ /** Input of {@link capDirectoryBytes}. */
95
+ export interface DirCapInput {
96
+ /** Directory of files to cap (not recursive) */
97
+ dir: string;
98
+ /** Allowed total bytes */
99
+ capBytes: number;
100
+ /** Current time (ms) */
101
+ now: number;
102
+ /** When true, nothing is deleted */
103
+ dryRun: boolean;
104
+ /** Files to consider (default: every regular file) */
105
+ filter?: (name: string) => boolean;
106
+ /** Files never deleted (a live session's log) */
107
+ protect?: (name: string) => boolean;
108
+ /** A file written within this long is never deleted (default LOG_CAP_MIN_AGE_MS) */
109
+ minAgeMs?: number;
110
+ }
111
+ /** Result of {@link capDirectoryBytes}. */
112
+ export interface DirCapSummary {
113
+ /** The directory */
114
+ dir: string;
115
+ /** Total bytes before */
116
+ totalBytes: number;
117
+ /** The cap */
118
+ capBytes: number;
119
+ /** Files deleted (oldest first) */
120
+ deleted: string[];
121
+ /** Bytes freed */
122
+ freedBytes: number;
123
+ /** Dry run: files that would be deleted */
124
+ wouldDelete: string[];
125
+ /** Dry run: bytes that would be freed */
126
+ wouldFreeBytes: number;
127
+ /** Bytes that stay over the cap because every remaining file is protected or too new */
128
+ overCapBytes: number;
129
+ }
130
+ /**
131
+ * Delete the oldest files of a directory until it fits the cap. Never
132
+ * throws; an unreadable directory yields an empty summary.
133
+ *
134
+ * @param input - Directory, cap, filters, clock, dry-run flag
135
+ * @returns What was deleted and what still exceeds the cap
136
+ */
137
+ export declare function capDirectoryBytes(input: DirCapInput): Promise<DirCapSummary>;
138
+ //# sourceMappingURL=tmp-janitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmp-janitor.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/tmp-janitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Tmp and log janitor — retention for `<CREWLY_HOME>/tmp` and size caps for
3
+ * `<CREWLY_HOME>/logs/archive` and `logs/sessions` (crewly#1354).
4
+ *
5
+ * `~/.crewly/tmp` is where agents, skills and Crewly itself put scratch
6
+ * output (Slack downloads, release build dirs, previews, skill output) and
7
+ * nothing ever removed it (600 MB on one Mac); the log archive had a 7-day
8
+ * rule but no cap (600 MB) and session logs none at all (200 MB).
9
+ *
10
+ * Tmp rules: a top-level entry untouched (shallow tree mtime) for 7 days is
11
+ * removed. Some sub-directories are swept per child with their own age
12
+ * instead (`slack-files` / `slack-images` / `slack-pdfs` 30 days,
13
+ * `crewly-web-release` 2 days, `skill-output` 7 days) and the directory itself
14
+ * stays. Never removed: symlinks, anything a process or agent session has as
15
+ * cwd, and any entry holding a git checkout — a linked worktree in tmp
16
+ * belongs to the worktree rules (the service adds tmp to their discovery
17
+ * roots), and a clone may be the only copy of its history.
18
+ *
19
+ * Log caps: the oldest files go first until the directory fits; a file
20
+ * written in the last hour, or the log of a live session, is never deleted.
21
+ *
22
+ * @module services/worktree/tmp-janitor
23
+ */
24
+ import * as fs from 'fs';
25
+ import * as path from 'path';
26
+ import { WORKTREE_JANITOR_CONSTANTS } from '../../constants.js';
27
+ import { EventLoopYielder, findGitCheckouts, isPathInside, latestTreeMtimeMs } from './worktree-janitor.git.js';
28
+ /** Bytes of a path by a bounded stat walk (no symlinks followed). */
29
+ export async function shallowBytes(p, depth = 6) {
30
+ let total = 0;
31
+ const yielder = new EventLoopYielder();
32
+ const walk = async (q, level) => {
33
+ await yielder.tick();
34
+ let st;
35
+ try {
36
+ st = await fs.promises.lstat(q);
37
+ }
38
+ catch {
39
+ return;
40
+ }
41
+ total += st.size;
42
+ if (!st.isDirectory() || level >= depth)
43
+ return;
44
+ let names;
45
+ try {
46
+ names = await fs.promises.readdir(q);
47
+ }
48
+ catch {
49
+ return;
50
+ }
51
+ for (const n of names)
52
+ await walk(path.join(q, n), level + 1);
53
+ };
54
+ await walk(p, 0);
55
+ return total;
56
+ }
57
+ /**
58
+ * One sweep of the tmp root. Never throws; an unreadable root yields an
59
+ * empty summary.
60
+ *
61
+ * @param input - Root, busy cwds, clock, dry-run flag
62
+ * @returns What was removed and kept
63
+ */
64
+ export async function sweepTmp(input) {
65
+ const summary = { root: input.root, removed: 0, wouldRemove: 0, freedBytes: 0, wouldFreeBytes: 0, kept: 0, keptReasons: {}, entries: [] };
66
+ const policies = input.childPolicies ?? WORKTREE_JANITOR_CONSTANTS.TMP_CHILD_POLICIES;
67
+ const defaultAge = input.maxAgeMs ?? WORKTREE_JANITOR_CONSTANTS.TMP_MAX_AGE_MS;
68
+ let top;
69
+ try {
70
+ top = await fs.promises.readdir(input.root, { withFileTypes: true });
71
+ }
72
+ catch {
73
+ return summary;
74
+ }
75
+ const keep = (v) => {
76
+ summary.kept += 1;
77
+ summary.keptReasons[v.reason] = (summary.keptReasons[v.reason] ?? 0) + 1;
78
+ if (v.reason !== 'recent')
79
+ summary.entries.push(v);
80
+ };
81
+ const sizeOf = async (p) => (input.sizeOf ? await input.sizeOf(p) : null) ?? (await shallowBytes(p));
82
+ const consider = async (entry, full, maxAge) => {
83
+ const base = { path: full, bytes: 0, removed: false };
84
+ if (entry.isSymbolicLink())
85
+ return keep({ ...base, decision: 'keep', reason: 'symlink' });
86
+ if (input.busy === null)
87
+ return keep({ ...base, decision: 'keep', reason: 'cwd-probe-failed' });
88
+ const hit = input.busy.find((b) => isPathInside(b.cwd, full));
89
+ if (hit)
90
+ return keep({ ...base, decision: 'keep', reason: hit.source === 'agent-session' ? 'agent-session-inside' : 'process-inside' });
91
+ const mtime = await latestTreeMtimeMs(full, WORKTREE_JANITOR_CONSTANTS.TMP_MTIME_DEPTH);
92
+ const idleMs = mtime === null ? undefined : Math.max(0, input.now - mtime);
93
+ if (idleMs === undefined || idleMs < maxAge)
94
+ return keep({ ...base, decision: 'keep', reason: 'recent', idleMs });
95
+ if (entry.isDirectory()) {
96
+ const repos = await findGitCheckouts(full, WORKTREE_JANITOR_CONSTANTS.TMP_MTIME_DEPTH, 5000);
97
+ if (repos.length > 0)
98
+ return keep({ ...base, decision: 'keep', reason: 'contains-git', idleMs });
99
+ }
100
+ const bytes = await sizeOf(full);
101
+ const verdict = { ...base, bytes, decision: 'remove', reason: 'old', idleMs };
102
+ if (input.dryRun) {
103
+ summary.wouldRemove += 1;
104
+ summary.wouldFreeBytes += bytes;
105
+ summary.entries.push(verdict);
106
+ return;
107
+ }
108
+ try {
109
+ await fs.promises.rm(full, { recursive: true, force: true });
110
+ verdict.removed = true;
111
+ summary.removed += 1;
112
+ summary.freedBytes += bytes;
113
+ summary.entries.push(verdict);
114
+ }
115
+ catch (err) {
116
+ keep({ ...verdict, decision: 'keep', reason: 'remove-failed', error: err instanceof Error ? err.message : String(err) });
117
+ }
118
+ };
119
+ const yielder = new EventLoopYielder();
120
+ for (const entry of top) {
121
+ await yielder.tick();
122
+ const full = path.join(input.root, entry.name);
123
+ const childAge = policies[entry.name];
124
+ if (childAge !== undefined && entry.isDirectory()) {
125
+ let children;
126
+ try {
127
+ children = await fs.promises.readdir(full, { withFileTypes: true });
128
+ }
129
+ catch {
130
+ continue;
131
+ }
132
+ for (const child of children)
133
+ await consider(child, path.join(full, child.name), childAge);
134
+ continue;
135
+ }
136
+ await consider(entry, full, defaultAge);
137
+ }
138
+ return summary;
139
+ }
140
+ /**
141
+ * Delete the oldest files of a directory until it fits the cap. Never
142
+ * throws; an unreadable directory yields an empty summary.
143
+ *
144
+ * @param input - Directory, cap, filters, clock, dry-run flag
145
+ * @returns What was deleted and what still exceeds the cap
146
+ */
147
+ export async function capDirectoryBytes(input) {
148
+ const summary = { dir: input.dir, totalBytes: 0, capBytes: input.capBytes, deleted: [], freedBytes: 0, wouldDelete: [], wouldFreeBytes: 0, overCapBytes: 0 };
149
+ let names;
150
+ try {
151
+ names = await fs.promises.readdir(input.dir);
152
+ }
153
+ catch {
154
+ return summary;
155
+ }
156
+ const minAge = input.minAgeMs ?? WORKTREE_JANITOR_CONSTANTS.LOG_CAP_MIN_AGE_MS;
157
+ const files = [];
158
+ for (const name of names) {
159
+ if (input.filter && !input.filter(name))
160
+ continue;
161
+ try {
162
+ const st = await fs.promises.lstat(path.join(input.dir, name));
163
+ if (!st.isFile())
164
+ continue;
165
+ const locked = (input.protect?.(name) ?? false) || input.now - st.mtimeMs < minAge;
166
+ files.push({ name, bytes: st.size, mtimeMs: st.mtimeMs, locked });
167
+ summary.totalBytes += st.size;
168
+ }
169
+ catch {
170
+ // vanished
171
+ }
172
+ }
173
+ let total = summary.totalBytes;
174
+ files.sort((a, b) => a.mtimeMs - b.mtimeMs);
175
+ for (const f of files) {
176
+ if (total <= input.capBytes)
177
+ break;
178
+ if (f.locked)
179
+ continue;
180
+ if (input.dryRun) {
181
+ summary.wouldDelete.push(f.name);
182
+ summary.wouldFreeBytes += f.bytes;
183
+ total -= f.bytes;
184
+ continue;
185
+ }
186
+ try {
187
+ await fs.promises.unlink(path.join(input.dir, f.name));
188
+ summary.deleted.push(f.name);
189
+ summary.freedBytes += f.bytes;
190
+ total -= f.bytes;
191
+ }
192
+ catch {
193
+ // busy — try the next one
194
+ }
195
+ }
196
+ summary.overCapBytes = Math.max(0, total - input.capBytes);
197
+ return summary;
198
+ }
199
+ //# sourceMappingURL=tmp-janitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmp-janitor.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/tmp-janitor.ts"],"names":[],"mappings":"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"}