crewly 1.20.184 → 1.20.185

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 (184) hide show
  1. package/config/constants.ts +83 -0
  2. package/config/index.ts +2 -0
  3. package/config/oss-boundary-allowlist.json +10 -0
  4. package/config/skills/agent/core/ask-owner/SKILL.md +5 -2
  5. package/config/skills/agent/core/complete-task/execute.sh +4 -12
  6. package/config/skills/agent/core/complete-task/execute.test.sh +11 -0
  7. package/config/skills/agent/core/report-status/SKILL.md +1 -1
  8. package/config/skills/agent/core/report-status/execute.sh +5 -10
  9. package/dist/backend/backend/src/constants.d.ts +40 -0
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +35 -1
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/decisions/decisions.controller.d.ts +11 -1
  14. package/dist/backend/backend/src/controllers/decisions/decisions.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/decisions/decisions.controller.js +38 -0
  16. package/dist/backend/backend/src/controllers/decisions/decisions.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/google/google.controller.js +3 -2
  19. package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/request/open-items.controller.d.ts +9 -0
  21. package/dist/backend/backend/src/controllers/request/open-items.controller.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/request/open-items.controller.js +29 -1
  23. package/dist/backend/backend/src/controllers/request/open-items.controller.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/request/request.routes.d.ts +1 -0
  25. package/dist/backend/backend/src/controllers/request/request.routes.d.ts.map +1 -1
  26. package/dist/backend/backend/src/controllers/request/request.routes.js +4 -1
  27. package/dist/backend/backend/src/controllers/request/request.routes.js.map +1 -1
  28. package/dist/backend/backend/src/controllers/standing/standing.controller.d.ts +5 -2
  29. package/dist/backend/backend/src/controllers/standing/standing.controller.d.ts.map +1 -1
  30. package/dist/backend/backend/src/controllers/standing/standing.controller.js +3 -1
  31. package/dist/backend/backend/src/controllers/standing/standing.controller.js.map +1 -1
  32. package/dist/backend/backend/src/index.d.ts.map +1 -1
  33. package/dist/backend/backend/src/index.js +15 -1
  34. package/dist/backend/backend/src/index.js.map +1 -1
  35. package/dist/backend/backend/src/scripts/check-oss-boundary.d.ts +217 -0
  36. package/dist/backend/backend/src/scripts/check-oss-boundary.d.ts.map +1 -0
  37. package/dist/backend/backend/src/scripts/check-oss-boundary.js +490 -0
  38. package/dist/backend/backend/src/scripts/check-oss-boundary.js.map +1 -0
  39. package/dist/backend/backend/src/services/ai/prompt-modules/standing-answers.module.d.ts +3 -1
  40. package/dist/backend/backend/src/services/ai/prompt-modules/standing-answers.module.d.ts.map +1 -1
  41. package/dist/backend/backend/src/services/ai/prompt-modules/standing-answers.module.js +15 -4
  42. package/dist/backend/backend/src/services/ai/prompt-modules/standing-answers.module.js.map +1 -1
  43. package/dist/backend/backend/src/services/decisions/decision-card.d.ts +31 -4
  44. package/dist/backend/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/decisions/decision-card.js +60 -2
  46. package/dist/backend/backend/src/services/decisions/decision-card.js.map +1 -1
  47. package/dist/backend/backend/src/services/decisions/decision-skip-command.d.ts +56 -0
  48. package/dist/backend/backend/src/services/decisions/decision-skip-command.d.ts.map +1 -0
  49. package/dist/backend/backend/src/services/decisions/decision-skip-command.js +108 -0
  50. package/dist/backend/backend/src/services/decisions/decision-skip-command.js.map +1 -0
  51. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +105 -2
  52. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  53. package/dist/backend/backend/src/services/decisions/decision.service.js +187 -11
  54. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  55. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts +8 -0
  56. package/dist/backend/backend/src/services/decisions/decision.wiring.d.ts.map +1 -1
  57. package/dist/backend/backend/src/services/decisions/decision.wiring.js +25 -0
  58. package/dist/backend/backend/src/services/decisions/decision.wiring.js.map +1 -1
  59. package/dist/backend/backend/src/services/memory/agent-memory.service.d.ts +19 -0
  60. package/dist/backend/backend/src/services/memory/agent-memory.service.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/memory/agent-memory.service.js +48 -0
  62. package/dist/backend/backend/src/services/memory/agent-memory.service.js.map +1 -1
  63. package/dist/backend/backend/src/services/memory/memory.service.d.ts.map +1 -1
  64. package/dist/backend/backend/src/services/memory/memory.service.js +20 -0
  65. package/dist/backend/backend/src/services/memory/memory.service.js.map +1 -1
  66. package/dist/backend/backend/src/services/memory/project-memory.service.d.ts +40 -0
  67. package/dist/backend/backend/src/services/memory/project-memory.service.d.ts.map +1 -1
  68. package/dist/backend/backend/src/services/memory/project-memory.service.js +101 -0
  69. package/dist/backend/backend/src/services/memory/project-memory.service.js.map +1 -1
  70. package/dist/backend/backend/src/services/memory/role-knowledge-eligibility.d.ts +1 -1
  71. package/dist/backend/backend/src/services/memory/role-knowledge-eligibility.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/memory/role-knowledge-eligibility.js.map +1 -1
  73. package/dist/backend/backend/src/services/memory/standing-answers.service.d.ts +61 -1
  74. package/dist/backend/backend/src/services/memory/standing-answers.service.d.ts.map +1 -1
  75. package/dist/backend/backend/src/services/memory/standing-answers.service.js +71 -3
  76. package/dist/backend/backend/src/services/memory/standing-answers.service.js.map +1 -1
  77. package/dist/backend/backend/src/services/memory/standing-refresh.service.d.ts +7 -4
  78. package/dist/backend/backend/src/services/memory/standing-refresh.service.d.ts.map +1 -1
  79. package/dist/backend/backend/src/services/memory/standing-refresh.service.js +18 -9
  80. package/dist/backend/backend/src/services/memory/standing-refresh.service.js.map +1 -1
  81. package/dist/backend/backend/src/services/memory/task-log-filter.d.ts +27 -0
  82. package/dist/backend/backend/src/services/memory/task-log-filter.d.ts.map +1 -0
  83. package/dist/backend/backend/src/services/memory/task-log-filter.js +56 -0
  84. package/dist/backend/backend/src/services/memory/task-log-filter.js.map +1 -0
  85. package/dist/backend/backend/src/services/open-items/open-items-backfill.d.ts +26 -1
  86. package/dist/backend/backend/src/services/open-items/open-items-backfill.d.ts.map +1 -1
  87. package/dist/backend/backend/src/services/open-items/open-items-backfill.js +59 -3
  88. package/dist/backend/backend/src/services/open-items/open-items-backfill.js.map +1 -1
  89. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +37 -2
  90. package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/open-items/open-items.service.js +101 -6
  92. package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
  93. package/dist/backend/backend/src/services/open-items/open-items.wiring.d.ts.map +1 -1
  94. package/dist/backend/backend/src/services/open-items/open-items.wiring.js +13 -0
  95. package/dist/backend/backend/src/services/open-items/open-items.wiring.js.map +1 -1
  96. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +13 -0
  97. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  98. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +34 -11
  99. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  100. package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts.map +1 -1
  101. package/dist/backend/backend/src/services/wiki/wiki-process.service.js +4 -2
  102. package/dist/backend/backend/src/services/wiki/wiki-process.service.js.map +1 -1
  103. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts +103 -1
  104. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts.map +1 -1
  105. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js +171 -20
  106. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js.map +1 -1
  107. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts +60 -0
  108. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.d.ts.map +1 -1
  109. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js +121 -7
  110. package/dist/backend/backend/src/services/wiki/wiki-workitem-bridge.service.js.map +1 -1
  111. package/dist/backend/backend/src/types/decision.types.d.ts +18 -3
  112. package/dist/backend/backend/src/types/decision.types.d.ts.map +1 -1
  113. package/dist/backend/backend/src/types/v2/open-item.types.d.ts +4 -1
  114. package/dist/backend/backend/src/types/v2/open-item.types.d.ts.map +1 -1
  115. package/dist/backend/backend/src/types/v2/open-item.types.js.map +1 -1
  116. package/dist/backend/build-info.json +2 -2
  117. package/dist/backend/config/constants.d.ts +73 -0
  118. package/dist/backend/config/constants.d.ts.map +1 -1
  119. package/dist/backend/config/constants.js +80 -0
  120. package/dist/backend/config/constants.js.map +1 -1
  121. package/dist/backend/config/index.d.ts +10 -2
  122. package/dist/backend/config/index.d.ts.map +1 -1
  123. package/dist/backend/config/index.js +2 -2
  124. package/dist/backend/config/index.js.map +1 -1
  125. package/dist/cli/backend/src/constants.d.ts +40 -0
  126. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  127. package/dist/cli/backend/src/constants.js +35 -1
  128. package/dist/cli/backend/src/constants.js.map +1 -1
  129. package/dist/cli/backend/src/services/ai/prompt-modules/standing-answers.module.d.ts +3 -1
  130. package/dist/cli/backend/src/services/ai/prompt-modules/standing-answers.module.d.ts.map +1 -1
  131. package/dist/cli/backend/src/services/ai/prompt-modules/standing-answers.module.js +15 -4
  132. package/dist/cli/backend/src/services/ai/prompt-modules/standing-answers.module.js.map +1 -1
  133. package/dist/cli/backend/src/services/decisions/decision-card.d.ts +31 -4
  134. package/dist/cli/backend/src/services/decisions/decision-card.d.ts.map +1 -1
  135. package/dist/cli/backend/src/services/decisions/decision-card.js +60 -2
  136. package/dist/cli/backend/src/services/decisions/decision-card.js.map +1 -1
  137. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +105 -2
  138. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  139. package/dist/cli/backend/src/services/decisions/decision.service.js +187 -11
  140. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  141. package/dist/cli/backend/src/services/memory/agent-memory.service.d.ts +19 -0
  142. package/dist/cli/backend/src/services/memory/agent-memory.service.d.ts.map +1 -1
  143. package/dist/cli/backend/src/services/memory/agent-memory.service.js +48 -0
  144. package/dist/cli/backend/src/services/memory/agent-memory.service.js.map +1 -1
  145. package/dist/cli/backend/src/services/memory/memory.service.d.ts.map +1 -1
  146. package/dist/cli/backend/src/services/memory/memory.service.js +20 -0
  147. package/dist/cli/backend/src/services/memory/memory.service.js.map +1 -1
  148. package/dist/cli/backend/src/services/memory/project-memory.service.d.ts +40 -0
  149. package/dist/cli/backend/src/services/memory/project-memory.service.d.ts.map +1 -1
  150. package/dist/cli/backend/src/services/memory/project-memory.service.js +101 -0
  151. package/dist/cli/backend/src/services/memory/project-memory.service.js.map +1 -1
  152. package/dist/cli/backend/src/services/memory/role-knowledge-eligibility.d.ts +1 -1
  153. package/dist/cli/backend/src/services/memory/role-knowledge-eligibility.d.ts.map +1 -1
  154. package/dist/cli/backend/src/services/memory/role-knowledge-eligibility.js.map +1 -1
  155. package/dist/cli/backend/src/services/memory/standing-answers.service.d.ts +61 -1
  156. package/dist/cli/backend/src/services/memory/standing-answers.service.d.ts.map +1 -1
  157. package/dist/cli/backend/src/services/memory/standing-answers.service.js +71 -3
  158. package/dist/cli/backend/src/services/memory/standing-answers.service.js.map +1 -1
  159. package/dist/cli/backend/src/services/memory/task-log-filter.d.ts +27 -0
  160. package/dist/cli/backend/src/services/memory/task-log-filter.d.ts.map +1 -0
  161. package/dist/cli/backend/src/services/memory/task-log-filter.js +56 -0
  162. package/dist/cli/backend/src/services/memory/task-log-filter.js.map +1 -0
  163. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +13 -0
  164. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  165. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +34 -11
  166. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  167. package/dist/cli/backend/src/types/decision.types.d.ts +18 -3
  168. package/dist/cli/backend/src/types/decision.types.d.ts.map +1 -1
  169. package/dist/cli/backend/src/types/v2/open-item.types.d.ts +4 -1
  170. package/dist/cli/backend/src/types/v2/open-item.types.d.ts.map +1 -1
  171. package/dist/cli/backend/src/types/v2/open-item.types.js.map +1 -1
  172. package/dist/cli/config/constants.d.ts +73 -0
  173. package/dist/cli/config/constants.d.ts.map +1 -1
  174. package/dist/cli/config/constants.js +80 -0
  175. package/dist/cli/config/constants.js.map +1 -1
  176. package/dist/cli/config/index.d.ts +10 -2
  177. package/dist/cli/config/index.d.ts.map +1 -1
  178. package/dist/cli/config/index.js +2 -2
  179. package/dist/cli/config/index.js.map +1 -1
  180. package/frontend/dist/assets/{index-a6cb8533.js → index-1ee8a9a2.js} +273 -273
  181. package/frontend/dist/assets/index-a79c7852.css +42 -0
  182. package/frontend/dist/index.html +2 -2
  183. package/package.json +2 -1
  184. package/frontend/dist/assets/index-04d192f8.css +0 -42
@@ -30,8 +30,22 @@ import * as fs from 'fs/promises';
30
30
  import { existsSync } from 'fs';
31
31
  import { randomUUID } from 'crypto';
32
32
  import { LoggerService } from '../core/logger.service.js';
33
+ import { WIKI_QUEUE_CONSTANTS } from '../../constants.js';
34
+ /**
35
+ * Canonical form of a vault path, so `/p/.crewly/wiki/` and
36
+ * `/p/.crewly/wiki` (or a path with `..`) name the same vault. Queue
37
+ * filters used to compare the raw strings; an item stored with a trailing
38
+ * slash never matched the discovered vault and was never drained (#914).
39
+ *
40
+ * @param vaultPath - Absolute vault path as given by a caller
41
+ * @returns The resolved path without a trailing separator
42
+ */
43
+ export function normalizeVaultPath(vaultPath) {
44
+ return path.resolve(vaultPath);
45
+ }
33
46
  const MAX_CONTENT_BYTES = 64 * 1024;
34
47
  const MAX_REASON_BYTES = 2 * 1024;
48
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
35
49
  /**
36
50
  * File-backed queue. Stateless other than the on-disk JSONs and an
37
51
  * in-memory cache of the root directory. Singleton because all writes
@@ -68,7 +82,7 @@ export class WikiQueueService {
68
82
  await this.ensureRoot();
69
83
  const item = {
70
84
  id: randomUUID(),
71
- vaultPath: input.vaultPath,
85
+ vaultPath: normalizeVaultPath(input.vaultPath),
72
86
  queuedAt: new Date().toISOString(),
73
87
  queuedBy: input.queuedBy,
74
88
  sourceType: input.sourceType,
@@ -87,27 +101,26 @@ export class WikiQueueService {
87
101
  });
88
102
  return item;
89
103
  }
90
- /** List items, newest-first, optionally filtered. */
104
+ /**
105
+ * List items, optionally filtered. Newest-first unless `order: 'oldest'`.
106
+ * Vault paths are compared in normalised form (see {@link normalizeVaultPath}).
107
+ *
108
+ * @param filter - Vault / status / author filters, limit and order
109
+ * @returns Matching items, sorted and capped
110
+ */
91
111
  async list(filter = {}) {
92
- await this.ensureRoot();
93
112
  const limit = filter.limit ?? 50;
94
- const entries = await fs.readdir(this.rootDir, { withFileTypes: true });
95
- const items = [];
96
- for (const entry of entries) {
97
- if (!entry.isFile() || !entry.name.endsWith('.json'))
98
- continue;
99
- const item = await this.readItemFile(path.join(this.rootDir, entry.name));
100
- if (!item)
101
- continue;
102
- if (filter.vaultPath && item.vaultPath !== filter.vaultPath)
103
- continue;
104
- if (filter.status && item.status !== filter.status)
105
- continue;
106
- if (filter.queuedBy && item.queuedBy !== filter.queuedBy)
107
- continue;
108
- items.push(item);
109
- }
110
- items.sort((a, b) => (a.queuedAt < b.queuedAt ? 1 : -1));
113
+ const wantVault = filter.vaultPath ? normalizeVaultPath(filter.vaultPath) : null;
114
+ const items = (await this.readAll()).filter((item) => (!wantVault || normalizeVaultPath(item.vaultPath) === wantVault) &&
115
+ (!filter.status || item.status === filter.status) &&
116
+ (!filter.queuedBy || item.queuedBy === filter.queuedBy));
117
+ const oldestFirst = filter.order === 'oldest';
118
+ items.sort((a, b) => {
119
+ if (a.queuedAt === b.queuedAt)
120
+ return 0;
121
+ const aFirst = oldestFirst ? a.queuedAt < b.queuedAt : a.queuedAt > b.queuedAt;
122
+ return aFirst ? -1 : 1;
123
+ });
111
124
  return items.slice(0, limit);
112
125
  }
113
126
  /** Fetch one by id. Returns null when missing. */
@@ -186,12 +199,100 @@ export class WikiQueueService {
186
199
  processed: 0,
187
200
  skipped: 0,
188
201
  total: items.length,
202
+ oldestPendingQueuedAt: null,
189
203
  };
190
204
  for (const it of items) {
191
205
  counts[it.status]++;
206
+ if (it.status === 'pending' &&
207
+ (counts.oldestPendingQueuedAt === null || it.queuedAt < counts.oldestPendingQueuedAt)) {
208
+ counts.oldestPendingQueuedAt = it.queuedAt;
209
+ }
192
210
  }
193
211
  return counts;
194
212
  }
213
+ /**
214
+ * Housekeeping pass over the whole queue (#914). One read of the
215
+ * directory does three things:
216
+ *
217
+ * 1. A `claimed` item whose claim is older than `claimTimeoutMs` goes
218
+ * back to `pending` — the agent that claimed it died or forgot to
219
+ * call process/skip, and nothing else would ever release it.
220
+ * 2. A `pending`/`claimed` item queued longer ago than `maxItemAgeMs`
221
+ * moves to `<root>/dead-letter/<id>.json` with `expiredAt` +
222
+ * `expireReason`. Never deleted; one warn line per item.
223
+ * 3. Reports the remaining pending backlog per vault (count + oldest
224
+ * `queuedAt`) so the caller can alert on stale vaults and on items
225
+ * whose vault is not discovered.
226
+ *
227
+ * Processed/skipped items are left alone (they are the audit trail).
228
+ *
229
+ * @param opts - Clock and age thresholds (defaults from WIKI_QUEUE_CONSTANTS)
230
+ * @returns Expired items, released claim ids and per-vault backlog
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * const { expired, backlog } = await WikiQueueService.getInstance().sweep();
235
+ * ```
236
+ */
237
+ async sweep(opts = {}) {
238
+ const now = opts.now ?? Date.now();
239
+ const maxAge = opts.maxItemAgeMs ?? WIKI_QUEUE_CONSTANTS.MAX_ITEM_AGE_MS;
240
+ const claimTimeout = opts.claimTimeoutMs ?? WIKI_QUEUE_CONSTANTS.CLAIM_TIMEOUT_MS;
241
+ const result = { expired: [], releasedClaims: [], backlog: [] };
242
+ const backlog = new Map();
243
+ for (const item of await this.readAll()) {
244
+ if (item.status !== 'pending' && item.status !== 'claimed')
245
+ continue;
246
+ const queuedMs = Date.parse(item.queuedAt);
247
+ if (Number.isFinite(queuedMs) && now - queuedMs > maxAge) {
248
+ await this.deadLetter(item, now, queuedMs);
249
+ result.expired.push(item);
250
+ continue;
251
+ }
252
+ let current = item;
253
+ if (item.status === 'claimed') {
254
+ const claimedMs = Date.parse(item.claimedAt ?? '');
255
+ if (Number.isFinite(claimedMs) && now - claimedMs <= claimTimeout)
256
+ continue;
257
+ current = { ...item, status: 'pending', claimedBy: undefined, claimedAt: undefined };
258
+ await this.writeItem(current);
259
+ result.releasedClaims.push(item.id);
260
+ this.logger.warn('WikiQueue claim released — claimant never processed or skipped it', {
261
+ id: item.id,
262
+ vaultPath: item.vaultPath,
263
+ claimedBy: item.claimedBy,
264
+ claimedAt: item.claimedAt,
265
+ });
266
+ }
267
+ const vaultPath = normalizeVaultPath(current.vaultPath);
268
+ const entry = backlog.get(vaultPath);
269
+ if (!entry) {
270
+ backlog.set(vaultPath, { vaultPath, pending: 1, oldestQueuedAt: current.queuedAt });
271
+ }
272
+ else {
273
+ entry.pending++;
274
+ if (current.queuedAt < entry.oldestQueuedAt)
275
+ entry.oldestQueuedAt = current.queuedAt;
276
+ }
277
+ }
278
+ result.backlog = [...backlog.values()].sort((a, b) => a.vaultPath.localeCompare(b.vaultPath));
279
+ if (result.expired.length > 0 || result.releasedClaims.length > 0) {
280
+ this.logger.warn('WikiQueue sweep', {
281
+ expired: result.expired.length,
282
+ releasedClaims: result.releasedClaims.length,
283
+ deadLetterDir: this.getDeadLetterDir(),
284
+ });
285
+ }
286
+ return result;
287
+ }
288
+ /**
289
+ * Folder that holds expired items.
290
+ *
291
+ * @returns Absolute path of the dead-letter folder
292
+ */
293
+ getDeadLetterDir() {
294
+ return path.join(this.rootDir, WIKI_QUEUE_CONSTANTS.DEAD_LETTER_DIR);
295
+ }
195
296
  // ---------------------------------------------------------------------------
196
297
  // Internals
197
298
  // ---------------------------------------------------------------------------
@@ -220,6 +321,56 @@ export class WikiQueueService {
220
321
  throw new Error(`WikiQueue.add: reason exceeds ${MAX_REASON_BYTES} bytes`);
221
322
  }
222
323
  }
324
+ /**
325
+ * Read every well-formed item in the queue root (not the dead-letter
326
+ * folder — `readdir` is not recursive).
327
+ *
328
+ * @returns All items, unsorted
329
+ */
330
+ async readAll() {
331
+ await this.ensureRoot();
332
+ const entries = await fs.readdir(this.rootDir, { withFileTypes: true });
333
+ const items = [];
334
+ for (const entry of entries) {
335
+ if (!entry.isFile() || !entry.name.endsWith('.json'))
336
+ continue;
337
+ const item = await this.readItemFile(path.join(this.rootDir, entry.name));
338
+ if (item)
339
+ items.push(item);
340
+ }
341
+ return items;
342
+ }
343
+ /**
344
+ * Move one item to the dead-letter folder, stamped with why. Written
345
+ * there first, then removed from the queue, so a crash in between
346
+ * leaves a duplicate rather than a loss.
347
+ *
348
+ * @param item - Item to expire
349
+ * @param now - Current epoch ms
350
+ * @param queuedMs - Item's `queuedAt` in epoch ms
351
+ */
352
+ async deadLetter(item, now, queuedMs) {
353
+ const ageDays = Math.floor((now - queuedMs) / MS_PER_DAY);
354
+ const expired = {
355
+ ...item,
356
+ expiredAt: new Date(now).toISOString(),
357
+ expireReason: `still ${item.status} after ${ageDays} days`,
358
+ };
359
+ const dir = this.getDeadLetterDir();
360
+ await fs.mkdir(dir, { recursive: true });
361
+ const target = path.join(dir, `${item.id}.json`);
362
+ await fs.writeFile(`${target}.tmp`, JSON.stringify(expired, null, 2), 'utf8');
363
+ await fs.rename(`${target}.tmp`, target);
364
+ await fs.rm(path.join(this.rootDir, `${item.id}.json`), { force: true });
365
+ this.logger.warn('WikiQueue item expired to dead-letter', {
366
+ id: item.id,
367
+ vaultPath: item.vaultPath,
368
+ status: item.status,
369
+ queuedAt: item.queuedAt,
370
+ ageDays,
371
+ file: target,
372
+ });
373
+ }
223
374
  async ensureRoot() {
224
375
  if (this.initPromise) {
225
376
  await this.initPromise;
@@ -1 +1 @@
1
- {"version":3,"file":"wiki-queue.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-queue.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wiki-queue.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-queue.service.ts"],"names":[],"mappings":"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"}
@@ -89,6 +89,24 @@ export interface WikiWorkItemBridgeOptions {
89
89
  cleanupService?: WikiCleanupService;
90
90
  /** Task pool override (test seam). */
91
91
  taskPool?: TaskPoolService;
92
+ /**
93
+ * Absolute path of `config/skills/orchestrator`. Substituted for
94
+ * `{{ORCHESTRATOR_SKILLS_PATH}}` in WI briefs: a brief is read raw by
95
+ * whoever claims it (poll-tasks), and since drains route to team leaders
96
+ * — whose prompts never define that placeholder — the literal string
97
+ * was all they got to run (#914). Null/omitted leaves it as-is.
98
+ */
99
+ orchestratorSkillsPath?: string | null;
100
+ /**
101
+ * Owner notification for a stale queue (oldest pending item older than
102
+ * {@link WIKI_QUEUE_CONSTANTS.STALE_ALERT_AGE_MS}). Boot wires the Slack
103
+ * owner notification, like auto-update. Omitted → warn log only.
104
+ */
105
+ notifyOwner?: (title: string, message: string) => Promise<unknown>;
106
+ /** Queue max item age override (default {@link WIKI_QUEUE_CONSTANTS.MAX_ITEM_AGE_MS}). */
107
+ maxItemAgeMs?: number;
108
+ /** Stale-alert age override (default {@link WIKI_QUEUE_CONSTANTS.STALE_ALERT_AGE_MS}). */
109
+ staleAlertAgeMs?: number;
92
110
  }
93
111
  export interface WikiBridgeTickResult {
94
112
  scannedVaults: string[];
@@ -103,6 +121,14 @@ export interface WikiBridgeTickResult {
103
121
  skippedByCooldown: string[];
104
122
  /** Vault paths for which a cleanup WI was created this tick. */
105
123
  createdCleanupForVault: string[];
124
+ /** Queue items moved to dead-letter by this tick's sweep. */
125
+ expiredQueueItems: number;
126
+ /** Abandoned claims released back to pending by this tick's sweep. */
127
+ releasedQueueClaims: number;
128
+ /** Vault paths with pending items that discovery does not return (never drained). */
129
+ orphanQueueVaults: string[];
130
+ /** Vault paths whose oldest pending item is older than the stale-alert age. */
131
+ staleQueueVaults: string[];
106
132
  }
107
133
  /**
108
134
  * Periodic bridge: pending wiki queue items + migration candidates
@@ -144,6 +170,12 @@ export declare class WikiWorkItemBridgeService {
144
170
  private readonly statePath;
145
171
  /** True after `loadStateFromDisk` has either populated or no-op'd. */
146
172
  private stateLoaded;
173
+ private readonly orchestratorSkillsPath;
174
+ private readonly notifyOwner;
175
+ private readonly maxItemAgeMs;
176
+ private readonly staleAlertAgeMs;
177
+ /** vaultPath → epoch ms of the last stale-queue owner alert. */
178
+ private readonly lastStaleAlertAt;
147
179
  constructor(opts?: WikiWorkItemBridgeOptions);
148
180
  /**
149
181
  * Hydrate {@link lastCreatedAt} from disk. Idempotent — runs once per
@@ -194,6 +226,34 @@ export declare class WikiWorkItemBridgeService {
194
226
  * terminal — i.e. it's still queued/claimed/running/etc.
195
227
  */
196
228
  private snapshotInflight;
229
+ /**
230
+ * Queue housekeeping at the start of each tick (#914): release abandoned
231
+ * claims, dead-letter items past the max age, warn about pending items
232
+ * whose vault discovery does not return (nothing would ever drain them),
233
+ * and alert the owner when a vault's oldest pending item is older than
234
+ * the stale-alert age. Failures are logged and never abort the tick.
235
+ *
236
+ * @param vaults - Vault paths discovery returned this tick
237
+ * @param result - Tick result to fill in
238
+ */
239
+ private sweepQueue;
240
+ /**
241
+ * Report one stale vault: a warn log line always, plus the owner
242
+ * notification when one is wired.
243
+ *
244
+ * @param backlog - The vault's pending backlog
245
+ * @param ageMs - Age of its oldest pending item
246
+ * @param orphan - True when discovery does not return the vault
247
+ */
248
+ private alertStaleQueue;
249
+ /**
250
+ * Fill in placeholders a WI brief carries, since the claimant reads the
251
+ * brief raw. See {@link WikiWorkItemBridgeOptions.orchestratorSkillsPath}.
252
+ *
253
+ * @param brief - Brief markdown with `{{ORCHESTRATOR_SKILLS_PATH}}`
254
+ * @returns The brief with the path filled in, or unchanged when unknown
255
+ */
256
+ private resolveBrief;
197
257
  /**
198
258
  * The session that should do a vault's wiki work: the resolver's answer
199
259
  * (a team leader) or the fallback target.
@@ -1 +1 @@
1
- {"version":3,"file":"wiki-workitem-bridge.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-workitem-bridge.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wiki-workitem-bridge.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-workitem-bridge.service.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ import path from 'path';
30
30
  import { existsSync, promises as fs } from 'fs';
31
31
  import { LoggerService } from '../core/logger.service.js';
32
32
  import { TaskPoolService } from '../task-pool/task-pool.service.js';
33
- import { WikiQueueService } from './wiki-queue.service.js';
33
+ import { WikiQueueService, normalizeVaultPath } from './wiki-queue.service.js';
34
34
  import { WikiMigrateService } from './wiki-migrate.service.js';
35
35
  import { WikiCleanupService } from './wiki-cleanup.service.js';
36
36
  import { discoverWikiVaults } from './wiki-bookkeep-trigger.service.js';
@@ -38,7 +38,7 @@ import { isInsidePackageTree } from '../v3/mission-paths.js';
38
38
  import { createWorkItem } from '../../types/v2/work-item.types.js';
39
39
  import { atomicWriteJson, safeReadJson, ensureDir } from '../../utils/file-io.utils.js';
40
40
  import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
41
- import { WIKI_KB_CONSTANTS } from '../../constants.js';
41
+ import { WIKI_KB_CONSTANTS, WIKI_QUEUE_CONSTANTS } from '../../constants.js';
42
42
  // ---------------------------------------------------------------------------
43
43
  // Constants
44
44
  // ---------------------------------------------------------------------------
@@ -157,6 +157,12 @@ export class WikiWorkItemBridgeService {
157
157
  statePath;
158
158
  /** True after `loadStateFromDisk` has either populated or no-op'd. */
159
159
  stateLoaded = false;
160
+ orchestratorSkillsPath;
161
+ notifyOwner;
162
+ maxItemAgeMs;
163
+ staleAlertAgeMs;
164
+ /** vaultPath → epoch ms of the last stale-queue owner alert. */
165
+ lastStaleAlertAt = new Map();
160
166
  constructor(opts = {}) {
161
167
  this.logger = LoggerService.getInstance().createComponentLogger('WikiWorkItemBridge');
162
168
  this.intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
@@ -171,6 +177,10 @@ export class WikiWorkItemBridgeService {
171
177
  this.migrate = opts.migrateService ?? WikiMigrateService.getInstance();
172
178
  this.cleanup = opts.cleanupService ?? WikiCleanupService.getInstance();
173
179
  this.pool = opts.taskPool ?? TaskPoolService.getInstance();
180
+ this.orchestratorSkillsPath = opts.orchestratorSkillsPath ?? null;
181
+ this.notifyOwner = opts.notifyOwner ?? null;
182
+ this.maxItemAgeMs = opts.maxItemAgeMs ?? WIKI_QUEUE_CONSTANTS.MAX_ITEM_AGE_MS;
183
+ this.staleAlertAgeMs = opts.staleAlertAgeMs ?? WIKI_QUEUE_CONSTANTS.STALE_ALERT_AGE_MS;
174
184
  if (opts.statePath === null) {
175
185
  this.statePath = null;
176
186
  }
@@ -367,6 +377,10 @@ export class WikiWorkItemBridgeService {
367
377
  deferredByThrottle: [],
368
378
  skippedByCooldown: [],
369
379
  createdCleanupForVault: [],
380
+ expiredQueueItems: 0,
381
+ releasedQueueClaims: 0,
382
+ orphanQueueVaults: [],
383
+ staleQueueVaults: [],
370
384
  };
371
385
  if (this.running) {
372
386
  // Overlapping ticks would create duplicates; the dedupe pass below
@@ -381,6 +395,7 @@ export class WikiWorkItemBridgeService {
381
395
  const { inflightVaults, inflightProjects, inflightCleanupVaults } = await this.snapshotInflight();
382
396
  // Drains come first — they're cheap, fast, and the most common case.
383
397
  const vaults = await this.discoverRoots();
398
+ await this.sweepQueue(vaults, result);
384
399
  for (const vaultPath of vaults) {
385
400
  result.scannedVaults.push(vaultPath);
386
401
  if (inflightVaults.has(vaultPath)) {
@@ -393,10 +408,14 @@ export class WikiWorkItemBridgeService {
393
408
  continue;
394
409
  }
395
410
  try {
411
+ // Uncapped (#914): with `limit: 200` a vault holding ≥ 200 pending
412
+ // items reported 200 every time, so every drain WI looked like
413
+ // "no progress" and the cooldown climbed to 24h even while the
414
+ // agent was draining.
396
415
  const pending = await this.queue.list({
397
416
  vaultPath,
398
417
  status: 'pending',
399
- limit: 200,
418
+ limit: Number.MAX_SAFE_INTEGER,
400
419
  });
401
420
  if (pending.length === 0)
402
421
  continue;
@@ -410,7 +429,7 @@ export class WikiWorkItemBridgeService {
410
429
  createdThisTick++;
411
430
  }
412
431
  catch (err) {
413
- this.logger.debug('queue.list / drain WI create failed (non-fatal)', {
432
+ this.logger.warn('queue.list / drain WI create failed (non-fatal)', {
414
433
  vaultPath,
415
434
  error: err.message,
416
435
  });
@@ -569,6 +588,101 @@ export class WikiWorkItemBridgeService {
569
588
  }
570
589
  return { inflightVaults, inflightProjects, inflightCleanupVaults };
571
590
  }
591
+ /**
592
+ * Queue housekeeping at the start of each tick (#914): release abandoned
593
+ * claims, dead-letter items past the max age, warn about pending items
594
+ * whose vault discovery does not return (nothing would ever drain them),
595
+ * and alert the owner when a vault's oldest pending item is older than
596
+ * the stale-alert age. Failures are logged and never abort the tick.
597
+ *
598
+ * @param vaults - Vault paths discovery returned this tick
599
+ * @param result - Tick result to fill in
600
+ */
601
+ async sweepQueue(vaults, result) {
602
+ const now = this.nowFn();
603
+ let sweep;
604
+ try {
605
+ sweep = await this.queue.sweep({ now, maxItemAgeMs: this.maxItemAgeMs });
606
+ }
607
+ catch (err) {
608
+ this.logger.warn('Wiki queue sweep failed (non-fatal)', { error: err.message });
609
+ return;
610
+ }
611
+ result.expiredQueueItems = sweep.expired.length;
612
+ result.releasedQueueClaims = sweep.releasedClaims.length;
613
+ const discovered = new Set(vaults.map(normalizeVaultPath));
614
+ for (const backlog of sweep.backlog) {
615
+ if (!discovered.has(backlog.vaultPath)) {
616
+ result.orphanQueueVaults.push(backlog.vaultPath);
617
+ if (!this.exhaustedWarned.has(`orphan:${backlog.vaultPath}`)) {
618
+ this.exhaustedWarned.add(`orphan:${backlog.vaultPath}`);
619
+ this.logger.warn('Wiki queue has pending items for a vault the bridge does not discover — no drain WI will be created for them', {
620
+ vaultPath: backlog.vaultPath,
621
+ pending: backlog.pending,
622
+ oldestQueuedAt: backlog.oldestQueuedAt,
623
+ hint: 'the vault needs a SCHEMA.md and its project must be in ~/.crewly/projects.json; otherwise the items expire to the dead-letter folder',
624
+ });
625
+ }
626
+ }
627
+ const ageMs = now - Date.parse(backlog.oldestQueuedAt);
628
+ if (!(ageMs > this.staleAlertAgeMs))
629
+ continue;
630
+ result.staleQueueVaults.push(backlog.vaultPath);
631
+ const last = this.lastStaleAlertAt.get(backlog.vaultPath);
632
+ if (last !== undefined && now - last < WIKI_QUEUE_CONSTANTS.STALE_ALERT_COOLDOWN_MS)
633
+ continue;
634
+ this.lastStaleAlertAt.set(backlog.vaultPath, now);
635
+ await this.alertStaleQueue(backlog, ageMs, !discovered.has(backlog.vaultPath));
636
+ }
637
+ }
638
+ /**
639
+ * Report one stale vault: a warn log line always, plus the owner
640
+ * notification when one is wired.
641
+ *
642
+ * @param backlog - The vault's pending backlog
643
+ * @param ageMs - Age of its oldest pending item
644
+ * @param orphan - True when discovery does not return the vault
645
+ */
646
+ async alertStaleQueue(backlog, ageMs, orphan) {
647
+ const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
648
+ const expireDays = Math.round(this.maxItemAgeMs / (24 * 60 * 60 * 1000));
649
+ const scope = describeVaultScope(backlog.vaultPath);
650
+ const message = `${backlog.pending} wiki queue item(s) for ${scope} are waiting; the oldest was queued ${ageDays} days ago (${backlog.oldestQueuedAt}). ` +
651
+ (orphan
652
+ ? 'This vault is not discovered (no SCHEMA.md or project not registered), so nothing will process them. '
653
+ : 'The drain WorkItems are not keeping up — check that the vault owner is claiming and finishing them. ') +
654
+ `Items older than ${expireDays} days move to ~/.crewly/wiki-queue/${WIKI_QUEUE_CONSTANTS.DEAD_LETTER_DIR}/.`;
655
+ this.logger.warn('Wiki queue is stale', {
656
+ vaultPath: backlog.vaultPath,
657
+ pending: backlog.pending,
658
+ oldestQueuedAt: backlog.oldestQueuedAt,
659
+ ageDays,
660
+ orphan,
661
+ });
662
+ if (!this.notifyOwner)
663
+ return;
664
+ try {
665
+ await this.notifyOwner('Wiki queue backlog', message);
666
+ }
667
+ catch (err) {
668
+ this.logger.warn('Stale wiki queue notification failed (non-fatal)', {
669
+ vaultPath: backlog.vaultPath,
670
+ error: err.message,
671
+ });
672
+ }
673
+ }
674
+ /**
675
+ * Fill in placeholders a WI brief carries, since the claimant reads the
676
+ * brief raw. See {@link WikiWorkItemBridgeOptions.orchestratorSkillsPath}.
677
+ *
678
+ * @param brief - Brief markdown with `{{ORCHESTRATOR_SKILLS_PATH}}`
679
+ * @returns The brief with the path filled in, or unchanged when unknown
680
+ */
681
+ resolveBrief(brief) {
682
+ if (!this.orchestratorSkillsPath)
683
+ return brief;
684
+ return brief.replace(/\{\{ORCHESTRATOR_SKILLS_PATH\}\}/g, this.orchestratorSkillsPath);
685
+ }
572
686
  /**
573
687
  * The session that should do a vault's wiki work: the resolver's answer
574
688
  * (a team leader) or the fallback target.
@@ -595,7 +709,7 @@ export class WikiWorkItemBridgeService {
595
709
  target,
596
710
  title: `Drain ${pendingCount} wiki queue item(s) — ${scope}`,
597
711
  description: `Process ${pendingCount} pending wiki queue items in ${vaultPath}.`,
598
- briefMarkdown: drainBrief(vaultPath, pendingCount),
712
+ briefMarkdown: this.resolveBrief(drainBrief(vaultPath, pendingCount)),
599
713
  maxRetries: 1,
600
714
  metadata: {
601
715
  kind: META_KIND_WIKI_DRAIN,
@@ -621,7 +735,7 @@ export class WikiWorkItemBridgeService {
621
735
  target,
622
736
  title: `Cleanup ${chunk.length} of ${totalCandidates} low-quality wiki page(s) — ${scope}`,
623
737
  description: `Review ${chunk.length} cleanup candidates in ${vaultPath}; decide keep/delete; call wiki-cleanup --apply with the final list.`,
624
- briefMarkdown: cleanupBrief(vaultPath, chunk, totalCandidates),
738
+ briefMarkdown: this.resolveBrief(cleanupBrief(vaultPath, chunk, totalCandidates)),
625
739
  maxRetries: 1,
626
740
  metadata: {
627
741
  kind: META_KIND_WIKI_CLEANUP,
@@ -654,7 +768,7 @@ export class WikiWorkItemBridgeService {
654
768
  target,
655
769
  title: `Migrate ${proposedCount} legacy wiki item(s) — ${path.basename(projectRoot)}`,
656
770
  description: `Apply wiki-migrate for ${projectRoot} (${proposedCount} new pages).`,
657
- briefMarkdown: migrateBrief(projectRoot, proposedCount),
771
+ briefMarkdown: this.resolveBrief(migrateBrief(projectRoot, proposedCount)),
658
772
  maxRetries: 1,
659
773
  metadata: {
660
774
  kind: META_KIND_WIKI_MIGRATE,