crewly 1.20.358 → 1.20.360

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 (137) 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 +189 -2
  4. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  5. package/dist/backend/backend/src/constants.js +200 -3
  6. package/dist/backend/backend/src/constants.js.map +1 -1
  7. package/dist/backend/backend/src/index.d.ts +58 -0
  8. package/dist/backend/backend/src/index.d.ts.map +1 -1
  9. package/dist/backend/backend/src/index.js +263 -19
  10. package/dist/backend/backend/src/index.js.map +1 -1
  11. package/dist/backend/backend/src/services/agent/boot-pressure.d.ts +147 -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 +212 -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 +28 -2
  22. package/dist/backend/backend/src/services/memory/agent-memory-refresh.service.js.map +1 -1
  23. package/dist/backend/backend/src/services/memory/startup-briefing.service.d.ts +10 -0
  24. package/dist/backend/backend/src/services/memory/startup-briefing.service.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/memory/startup-briefing.service.js +65 -22
  26. package/dist/backend/backend/src/services/memory/startup-briefing.service.js.map +1 -1
  27. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts +8 -0
  28. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts.map +1 -1
  29. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js +8 -0
  30. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js.map +1 -1
  31. package/dist/backend/backend/src/services/session/session-binding.d.ts +25 -2
  32. package/dist/backend/backend/src/services/session/session-binding.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/session/session-binding.js +32 -4
  34. package/dist/backend/backend/src/services/session/session-binding.js.map +1 -1
  35. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts +3 -1
  36. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js.map +1 -1
  38. package/dist/backend/backend/src/services/wiki/wiki-log-compile.service.d.ts +27 -2
  39. package/dist/backend/backend/src/services/wiki/wiki-log-compile.service.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/wiki/wiki-log-compile.service.js +105 -8
  41. package/dist/backend/backend/src/services/wiki/wiki-log-compile.service.js.map +1 -1
  42. package/dist/backend/backend/src/services/wiki/wiki-log-entries.d.ts +7 -0
  43. package/dist/backend/backend/src/services/wiki/wiki-log-entries.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/wiki/wiki-log-entries.js +10 -0
  45. package/dist/backend/backend/src/services/wiki/wiki-log-entries.js.map +1 -1
  46. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.d.ts +24 -0
  47. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.js +75 -9
  49. package/dist/backend/backend/src/services/wiki/wiki-log-rotation.service.js.map +1 -1
  50. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts +19 -0
  51. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js +23 -2
  53. package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js.map +1 -1
  54. package/dist/backend/backend/src/services/wiki/work-record-transcript.d.ts +64 -0
  55. package/dist/backend/backend/src/services/wiki/work-record-transcript.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/wiki/work-record-transcript.js +194 -0
  57. package/dist/backend/backend/src/services/wiki/work-record-transcript.js.map +1 -0
  58. package/dist/backend/backend/src/services/wiki/work-record.d.ts +239 -0
  59. package/dist/backend/backend/src/services/wiki/work-record.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/wiki/work-record.js +631 -0
  61. package/dist/backend/backend/src/services/wiki/work-record.js.map +1 -0
  62. package/dist/backend/backend/src/services/wiki/work-record.service.d.ts +231 -0
  63. package/dist/backend/backend/src/services/wiki/work-record.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/wiki/work-record.service.js +542 -0
  65. package/dist/backend/backend/src/services/wiki/work-record.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/worktree/low-disk-guard.d.ts +92 -8
  67. package/dist/backend/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/worktree/low-disk-guard.js +98 -14
  69. package/dist/backend/backend/src/services/worktree/low-disk-guard.js.map +1 -1
  70. package/dist/backend/backend/src/services/worktree/tmp-janitor.d.ts +138 -0
  71. package/dist/backend/backend/src/services/worktree/tmp-janitor.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/worktree/tmp-janitor.js +199 -0
  73. package/dist/backend/backend/src/services/worktree/tmp-janitor.js.map +1 -0
  74. package/dist/backend/backend/src/services/worktree/transcript-janitor.d.ts +154 -0
  75. package/dist/backend/backend/src/services/worktree/transcript-janitor.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/worktree/transcript-janitor.js +357 -0
  77. package/dist/backend/backend/src/services/worktree/transcript-janitor.js.map +1 -0
  78. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.d.ts.map +1 -1
  79. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.js +7 -2
  80. package/dist/backend/backend/src/services/worktree/workitem-worktree.service.js.map +1 -1
  81. package/dist/backend/backend/src/services/worktree/worktree-dirty.d.ts +16 -4
  82. package/dist/backend/backend/src/services/worktree/worktree-dirty.d.ts.map +1 -1
  83. package/dist/backend/backend/src/services/worktree/worktree-dirty.js +21 -5
  84. package/dist/backend/backend/src/services/worktree/worktree-dirty.js.map +1 -1
  85. package/dist/backend/backend/src/services/worktree/worktree-git.d.ts +18 -0
  86. package/dist/backend/backend/src/services/worktree/worktree-git.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/worktree/worktree-git.js +60 -0
  88. package/dist/backend/backend/src/services/worktree/worktree-git.js.map +1 -1
  89. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +164 -25
  90. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +494 -55
  92. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -1
  93. package/dist/backend/backend/src/services/worktree/worktree-tiers.d.ts +186 -0
  94. package/dist/backend/backend/src/services/worktree/worktree-tiers.d.ts.map +1 -0
  95. package/dist/backend/backend/src/services/worktree/worktree-tiers.js +279 -0
  96. package/dist/backend/backend/src/services/worktree/worktree-tiers.js.map +1 -0
  97. package/dist/backend/build-info.json +2 -2
  98. package/dist/cli/backend/src/constants.d.ts +189 -2
  99. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  100. package/dist/cli/backend/src/constants.js +200 -3
  101. package/dist/cli/backend/src/constants.js.map +1 -1
  102. package/dist/cli/backend/src/services/agent/boot-pressure.d.ts +147 -0
  103. package/dist/cli/backend/src/services/agent/boot-pressure.d.ts.map +1 -0
  104. package/dist/cli/backend/src/services/agent/boot-pressure.js +212 -0
  105. package/dist/cli/backend/src/services/agent/boot-pressure.js.map +1 -0
  106. package/dist/cli/backend/src/services/agent/restore-queue.d.ts +50 -1
  107. package/dist/cli/backend/src/services/agent/restore-queue.d.ts.map +1 -1
  108. package/dist/cli/backend/src/services/agent/restore-queue.js +100 -5
  109. package/dist/cli/backend/src/services/agent/restore-queue.js.map +1 -1
  110. package/dist/cli/backend/src/services/memory/startup-briefing.service.d.ts +10 -0
  111. package/dist/cli/backend/src/services/memory/startup-briefing.service.d.ts.map +1 -1
  112. package/dist/cli/backend/src/services/memory/startup-briefing.service.js +65 -22
  113. package/dist/cli/backend/src/services/memory/startup-briefing.service.js.map +1 -1
  114. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.d.ts +3 -1
  115. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -1
  116. package/dist/cli/backend/src/services/wiki/wiki-ingest.service.js.map +1 -1
  117. package/dist/cli/backend/src/services/wiki/wiki-log-entries.d.ts +7 -0
  118. package/dist/cli/backend/src/services/wiki/wiki-log-entries.d.ts.map +1 -1
  119. package/dist/cli/backend/src/services/wiki/wiki-log-entries.js +10 -0
  120. package/dist/cli/backend/src/services/wiki/wiki-log-entries.js.map +1 -1
  121. package/dist/cli/backend/src/services/wiki/work-record-transcript.d.ts +64 -0
  122. package/dist/cli/backend/src/services/wiki/work-record-transcript.d.ts.map +1 -0
  123. package/dist/cli/backend/src/services/wiki/work-record-transcript.js +194 -0
  124. package/dist/cli/backend/src/services/wiki/work-record-transcript.js.map +1 -0
  125. package/dist/cli/backend/src/services/wiki/work-record.d.ts +239 -0
  126. package/dist/cli/backend/src/services/wiki/work-record.d.ts.map +1 -0
  127. package/dist/cli/backend/src/services/wiki/work-record.js +631 -0
  128. package/dist/cli/backend/src/services/wiki/work-record.js.map +1 -0
  129. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts +92 -8
  130. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -1
  131. package/dist/cli/backend/src/services/worktree/low-disk-guard.js +98 -14
  132. package/dist/cli/backend/src/services/worktree/low-disk-guard.js.map +1 -1
  133. package/dist/cli/backend/src/services/worktree/worktree-git.d.ts +18 -0
  134. package/dist/cli/backend/src/services/worktree/worktree-git.d.ts.map +1 -1
  135. package/dist/cli/backend/src/services/worktree/worktree-git.js +60 -0
  136. package/dist/cli/backend/src/services/worktree/worktree-git.js.map +1 -1
  137. 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
  */
@@ -15,17 +18,35 @@ export interface StatFsLike {
15
18
  bavail: number | bigint;
16
19
  /** Block size in bytes */
17
20
  bsize: number | bigint;
21
+ /** Total blocks on the volume (optional: an older probe may not report it) */
22
+ blocks?: number | bigint;
23
+ }
24
+ /** Free and total bytes of a volume. */
25
+ export interface DiskSpace {
26
+ /** Free bytes for unprivileged users */
27
+ freeBytes: number;
28
+ /** Total bytes, or null when the probe did not report the size */
29
+ totalBytes: number | null;
18
30
  }
19
31
  /** `fs.promises.statfs`-compatible probe. */
20
32
  export type StatFsFn = (p: string) => Promise<StatFsLike>;
21
33
  /** Disk level relative to the thresholds. */
22
34
  export type DiskLevel = 'ok' | 'low' | 'critical';
23
- /** When the owner was last told about low disk (persisted). */
35
+ /** Janitor state persisted under CREWLY_HOME (notice cadence, sweep cadence, arming). */
24
36
  export interface LowDiskNoticeState {
25
37
  /** Last notice of any kind (ms epoch) */
26
38
  lastNoticeAt?: number;
27
39
  /** Last urgent notice (ms epoch) */
28
40
  lastUrgentAt?: number;
41
+ /**
42
+ * First real pass that ran with the crewly#1354 retention code (ms epoch).
43
+ * Its deletes stay dry-run until RETENTION_ARM_DELAY_MS after this.
44
+ */
45
+ retentionArmedAt?: number;
46
+ /** Last transcript sweep (ms epoch) */
47
+ lastTranscriptSweepAt?: number;
48
+ /** Last tmp / logs sweep (ms epoch) */
49
+ lastTmpSweepAt?: number;
29
50
  }
30
51
  /** Something the janitor looked at and left on disk. */
31
52
  export interface KeptItem {
@@ -45,12 +66,51 @@ export interface KeptItem {
45
66
  */
46
67
  export declare function readFreeBytes(p: string, statfs?: StatFsFn): Promise<number | null>;
47
68
  /**
48
- * Classify free space.
69
+ * Free and total bytes of the volume holding `p`.
70
+ *
71
+ * @param p - Any path on the volume
72
+ * @param statfs - Probe (default `fs.promises.statfs`)
73
+ * @returns Free and total bytes, or null when the probe failed
74
+ */
75
+ export declare function readDiskSpace(p: string, statfs?: StatFsFn): Promise<DiskSpace | null>;
76
+ /**
77
+ * The low-disk mark for a volume: the absolute floor or a fraction of the
78
+ * disk, whichever is larger. A 926 GB disk gets ~74 GB, a 100 GB disk 15 GB.
79
+ *
80
+ * @param totalBytes - Disk size, or null when unknown (the floor is used)
81
+ * @returns Bytes below which free space counts as low
82
+ */
83
+ export declare function lowDiskThresholdBytes(totalBytes: number | null): number;
84
+ /**
85
+ * The critical mark for a volume: max(5 GB, 3 % of the disk).
86
+ *
87
+ * @param totalBytes - Disk size, or null when unknown (the floor is used)
88
+ * @returns Bytes below which free space counts as critical
89
+ */
90
+ export declare function criticalDiskThresholdBytes(totalBytes: number | null): number;
91
+ /**
92
+ * Classify free space against the volume's marks.
49
93
  *
50
94
  * @param freeBytes - Free bytes
51
- * @returns `critical` below CRITICAL_DISK_BYTES, `low` below LOW_DISK_BYTES, else `ok`
95
+ * @param totalBytes - Disk size (null / omitted: the absolute floors apply)
96
+ * @returns `critical` below the critical mark, `low` below the low mark, else `ok`
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * diskLevel(50 * GB, 926 * GB); // 'low' (mark is 8 % = 74 GB)
101
+ * diskLevel(50 * GB); // 'ok' (no size: 15 GB floor)
102
+ * ```
103
+ */
104
+ export declare function diskLevel(freeBytes: number, totalBytes?: number | null): DiskLevel;
105
+ /**
106
+ * Whether the crewly#1354 retention deletes are armed: the first real pass
107
+ * records the time and only logs; deletes start RETENTION_ARM_DELAY_MS later.
108
+ *
109
+ * @param state - Janitor state
110
+ * @param now - Current time (ms)
111
+ * @returns True when deletes may run
52
112
  */
53
- export declare function diskLevel(freeBytes: number): DiskLevel;
113
+ export declare function retentionArmed(state: LowDiskNoticeState, now: number): boolean;
54
114
  /**
55
115
  * Idle threshold to use, shortened in low-disk mode.
56
116
  *
@@ -83,6 +143,26 @@ export declare function noticeToSend(level: DiskLevel, state: LowDiskNoticeState
83
143
  * @returns New state
84
144
  */
85
145
  export declare function recordNotice(state: LowDiskNoticeState, kind: 'urgent' | 'normal', now: number): LowDiskNoticeState;
146
+ /** What one pass freed, by kind (bytes), for the owner line. */
147
+ export interface FreedBreakdown {
148
+ /** Finished worktrees and scratch dirs removed */
149
+ worktrees: number;
150
+ /** node_modules / build output stripped from kept worktrees */
151
+ stripped: number;
152
+ /** Worktrees archived as a patch and removed */
153
+ archived: number;
154
+ /** Agent transcripts pruned */
155
+ transcripts: number;
156
+ /** CREWLY_HOME/tmp and logs */
157
+ tmp: number;
158
+ }
159
+ /**
160
+ * One short line of what a pass freed, by kind; empty when nothing was.
161
+ *
162
+ * @param freed - Bytes freed per kind
163
+ * @returns `"freed 3.2 GB (worktrees 2.1 GB, node_modules 1.1 GB)"` or ''
164
+ */
165
+ export declare function describeFreed(freed: FreedBreakdown): string;
86
166
  /**
87
167
  * Plain-English reason for a kept item.
88
168
  *
@@ -102,6 +182,10 @@ export declare function buildLowDiskNotice(input: {
102
182
  urgent: boolean;
103
183
  freedBytes: number;
104
184
  items: KeptItem[];
185
+ /** Optional breakdown of what was freed (replaces the plain "cleaned up X" line) */
186
+ freed?: FreedBreakdown;
187
+ /** Optional: total disk size, so the owner sees the mark that was crossed */
188
+ totalBytes?: number | null;
105
189
  }): {
106
190
  title: string;
107
191
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"low-disk-guard.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/low-disk-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,kEAAkE;AAClE,MAAM,WAAW,UAAU;IAC1B,kDAAkD;IAClD,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE1D,6CAA6C;AAC7C,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,GAAG,UAAU,CAAC;AAElD,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAClC,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACxB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,QAAwB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvG;AAMD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAItD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAIxE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAQjH;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAElH;AAqBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;CAClB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAkBrC"}
1
+ {"version":3,"file":"low-disk-guard.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/low-disk-guard.ts"],"names":[],"mappings":"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"}
@@ -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":"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"}
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;CACpC;AAED,2CAA2C;AAC3C,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,cAAc,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,kBAAkB,GAAG,SAAS,GAAG,eAAe,CAAC;AAEzJ,iCAAiC;AACjC,MAAM,WAAW,eAAe;IAC/B,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC5B,wBAAwB;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC/B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,kCAAkC;IAClC,OAAO,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC7B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAC1B,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,8CAA8C;IAC9C,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAED,qEAAqE;AACrE,wBAAsB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBxE;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAmE7E;AAED,0CAA0C;AAC1C,MAAM,WAAW,WAAW;IAC3B,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,iDAAiD;IACjD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC7B,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,yCAAyC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CA4ClF"}