crewly 1.20.306 → 1.20.308

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 (126) hide show
  1. package/config/hooks/agent-status/report.sh +39 -1
  2. package/dist/backend/backend/src/constants.d.ts +120 -0
  3. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  4. package/dist/backend/backend/src/constants.js +120 -0
  5. package/dist/backend/backend/src/constants.js.map +1 -1
  6. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts +2 -1
  7. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
  8. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +18 -1
  9. package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
  10. package/dist/backend/backend/src/controllers/board/board.controller.d.ts +23 -0
  11. package/dist/backend/backend/src/controllers/board/board.controller.d.ts.map +1 -0
  12. package/dist/backend/backend/src/controllers/board/board.controller.js +48 -0
  13. package/dist/backend/backend/src/controllers/board/board.controller.js.map +1 -0
  14. package/dist/backend/backend/src/controllers/board/board.routes.d.ts +15 -0
  15. package/dist/backend/backend/src/controllers/board/board.routes.d.ts.map +1 -0
  16. package/dist/backend/backend/src/controllers/board/board.routes.js +21 -0
  17. package/dist/backend/backend/src/controllers/board/board.routes.js.map +1 -0
  18. package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
  19. package/dist/backend/backend/src/controllers/browser/browser.controller.js +2 -0
  20. package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
  21. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/chat/chat.controller.js +5 -0
  23. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  25. package/dist/backend/backend/src/controllers/slack/slack.controller.js +3 -0
  26. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  27. package/dist/backend/backend/src/index.d.ts +18 -0
  28. package/dist/backend/backend/src/index.d.ts.map +1 -1
  29. package/dist/backend/backend/src/index.js +172 -2
  30. package/dist/backend/backend/src/index.js.map +1 -1
  31. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  32. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  33. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  34. package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts +102 -0
  35. package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts.map +1 -0
  36. package/dist/backend/backend/src/services/board/board-activity.normalizer.js +244 -0
  37. package/dist/backend/backend/src/services/board/board-activity.normalizer.js.map +1 -0
  38. package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts +159 -0
  39. package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts.map +1 -0
  40. package/dist/backend/backend/src/services/board/board-activity.tracker.js +346 -0
  41. package/dist/backend/backend/src/services/board/board-activity.tracker.js.map +1 -0
  42. package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts +43 -0
  43. package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts.map +1 -0
  44. package/dist/backend/backend/src/services/board/board-outcome-phrases.js +76 -0
  45. package/dist/backend/backend/src/services/board/board-outcome-phrases.js.map +1 -0
  46. package/dist/backend/backend/src/services/board/board-push.service.d.ts +132 -0
  47. package/dist/backend/backend/src/services/board/board-push.service.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/board/board-push.service.js +374 -0
  49. package/dist/backend/backend/src/services/board/board-push.service.js.map +1 -0
  50. package/dist/backend/backend/src/services/board/board-redact.d.ts +90 -0
  51. package/dist/backend/backend/src/services/board/board-redact.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/board/board-redact.js +359 -0
  53. package/dist/backend/backend/src/services/board/board-redact.js.map +1 -0
  54. package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts +183 -0
  55. package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/board/board-snapshot.builder.js +344 -0
  57. package/dist/backend/backend/src/services/board/board-snapshot.builder.js.map +1 -0
  58. package/dist/backend/backend/src/services/board/board-usage.d.ts +20 -0
  59. package/dist/backend/backend/src/services/board/board-usage.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/board/board-usage.js +82 -0
  61. package/dist/backend/backend/src/services/board/board-usage.js.map +1 -0
  62. package/dist/backend/backend/src/services/board/board.service.d.ts +60 -0
  63. package/dist/backend/backend/src/services/board/board.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/board/board.service.js +82 -0
  65. package/dist/backend/backend/src/services/board/board.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/board/board.sources.d.ts +25 -0
  67. package/dist/backend/backend/src/services/board/board.sources.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/board/board.sources.js +300 -0
  69. package/dist/backend/backend/src/services/board/board.sources.js.map +1 -0
  70. package/dist/backend/backend/src/services/board/board.types.d.ts +122 -0
  71. package/dist/backend/backend/src/services/board/board.types.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/board/board.types.js +8 -0
  73. package/dist/backend/backend/src/services/board/board.types.js.map +1 -0
  74. package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.d.ts +122 -0
  75. package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.js +339 -0
  77. package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.js.map +1 -0
  78. package/dist/backend/backend/src/services/board/canvas/canvas-redact.d.ts +44 -0
  79. package/dist/backend/backend/src/services/board/canvas/canvas-redact.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/board/canvas/canvas-redact.js +161 -0
  81. package/dist/backend/backend/src/services/board/canvas/canvas-redact.js.map +1 -0
  82. package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.d.ts +57 -0
  83. package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.d.ts.map +1 -0
  84. package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.js +164 -0
  85. package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.js.map +1 -0
  86. package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.d.ts +140 -0
  87. package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.d.ts.map +1 -0
  88. package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.js +300 -0
  89. package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.js.map +1 -0
  90. package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.d.ts +99 -0
  91. package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.d.ts.map +1 -0
  92. package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.js +223 -0
  93. package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.js.map +1 -0
  94. package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.d.ts +126 -0
  95. package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.d.ts.map +1 -0
  96. package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.js +385 -0
  97. package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.js.map +1 -0
  98. package/dist/backend/backend/src/services/board/canvas/canvas-wiring.d.ts +103 -0
  99. package/dist/backend/backend/src/services/board/canvas/canvas-wiring.d.ts.map +1 -0
  100. package/dist/backend/backend/src/services/board/canvas/canvas-wiring.js +124 -0
  101. package/dist/backend/backend/src/services/board/canvas/canvas-wiring.js.map +1 -0
  102. package/dist/backend/backend/src/services/board/canvas/canvas.types.d.ts +88 -0
  103. package/dist/backend/backend/src/services/board/canvas/canvas.types.d.ts.map +1 -0
  104. package/dist/backend/backend/src/services/board/canvas/canvas.types.js +8 -0
  105. package/dist/backend/backend/src/services/board/canvas/canvas.types.js.map +1 -0
  106. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  107. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  108. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  109. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  110. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts +13 -0
  111. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts.map +1 -1
  112. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js +15 -0
  113. package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js.map +1 -1
  114. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  115. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  116. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  117. package/dist/backend/build-info.json +2 -2
  118. package/dist/cli/backend/src/constants.d.ts +120 -0
  119. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  120. package/dist/cli/backend/src/constants.js +120 -0
  121. package/dist/cli/backend/src/constants.js.map +1 -1
  122. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  123. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  124. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  125. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  126. package/package.json +1 -1
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Board snapshot builder (crewly#1247): turns what the machine knows (teams,
3
+ * runtime turn state, WorkItems, tickets, decisions, the usage ledger and the
4
+ * live activity tracker) into the {@link BoardSnapshot} Cloud and the local
5
+ * dashboard show.
6
+ *
7
+ * Everything is read through {@link BoardSources}, so the builder is a pure
8
+ * function of its inputs and is tested with fixtures. A full build refreshes
9
+ * a cache (roster, usage, tasks, owner waits); the per-agent delta rows are
10
+ * built from that cache plus the live activity, so a delta never rescans the
11
+ * disk.
12
+ *
13
+ * Every text in the result went through {@link scrubText}.
14
+ *
15
+ * @module services/board/board-snapshot.builder
16
+ */
17
+ import { BOARD_CONSTANTS } from '../../constants.js';
18
+ import { scrubText } from './board-redact.js';
19
+ import { resultOf, summarizeResults } from './board-outcome-phrases.js';
20
+ const C = BOARD_CONSTANTS;
21
+ /** Local midnight of `now`. */
22
+ export function startOfLocalDay(now) {
23
+ const d = new Date(now);
24
+ d.setHours(0, 0, 0, 0);
25
+ return d.getTime();
26
+ }
27
+ const iso = (ms) => new Date(ms).toISOString();
28
+ const msOf = (s) => (s ? Date.parse(s) : NaN);
29
+ /** Hours/minutes in words for stuck reasons. */
30
+ function age(ms) {
31
+ const m = Math.max(1, Math.round(ms / 60_000));
32
+ if (m < 60)
33
+ return `${m} min`;
34
+ const h = Math.round(m / 60);
35
+ return h < 48 ? `${h} h` : `${Math.round(h / 24)} days`;
36
+ }
37
+ /**
38
+ * Builds board snapshots and per-agent rows.
39
+ */
40
+ export class BoardSnapshotBuilder {
41
+ sources;
42
+ tracker;
43
+ clock;
44
+ cache = { roster: new Map(), usage: null, tasks: new Map(), waits: new Map() };
45
+ /**
46
+ * @param sources - Everything the board reads
47
+ * @param tracker - Live activity
48
+ * @param clock - Clock (tests)
49
+ */
50
+ constructor(sources, tracker, clock = Date.now) {
51
+ this.sources = sources;
52
+ this.tracker = tracker;
53
+ this.clock = clock;
54
+ }
55
+ /** Whether a full build has filled the cache. */
56
+ get primed() {
57
+ return this.cache.roster.size > 0;
58
+ }
59
+ /** This machine's identity. */
60
+ identity() {
61
+ return this.sources.identity();
62
+ }
63
+ /** Sessions known from the last full build. */
64
+ knownSessions() {
65
+ return [...this.cache.roster.keys()];
66
+ }
67
+ async safe(read, fallback) {
68
+ try {
69
+ return await read();
70
+ }
71
+ catch {
72
+ return fallback;
73
+ }
74
+ }
75
+ /**
76
+ * Build the full snapshot and refresh the cache.
77
+ *
78
+ * @returns The snapshot
79
+ */
80
+ async build() {
81
+ const now = this.clock();
82
+ const dayStart = startOfLocalDay(now);
83
+ const [identity, version, teams, workItems, tickets, decisions, holds, usage, active] = await Promise.all([
84
+ this.sources.identity(),
85
+ this.safe(() => this.sources.version(), 'unknown'),
86
+ this.safe(() => this.sources.teams(), []),
87
+ this.safe(() => this.sources.workItems(), []),
88
+ this.safe(() => this.sources.tickets(), []),
89
+ this.safe(() => this.sources.decisions(), []),
90
+ this.safe(() => this.sources.holds(), []),
91
+ this.safe(() => this.sources.usage(now), null),
92
+ this.safe(() => this.sources.activeWork(), new Map()),
93
+ ]);
94
+ const roster = new Map();
95
+ for (const team of teams)
96
+ for (const member of team.members)
97
+ roster.set(member.session, { team, member });
98
+ const waits = new Map();
99
+ for (const d of decisions)
100
+ if (d.asker && !waits.has(d.asker))
101
+ waits.set(d.asker, d);
102
+ const tasks = new Map(usage?.taskOf ?? []);
103
+ for (const [session, task] of active)
104
+ tasks.set(session, task);
105
+ this.cache = { roster, usage, tasks, waits };
106
+ const teamName = (id, agent) => {
107
+ const byId = id ? teams.find((t) => t.teamId === id)?.name : undefined;
108
+ return byId ?? (agent ? roster.get(agent)?.team.name : undefined);
109
+ };
110
+ const agentName = (session) => (session ? (roster.get(session)?.member.name ?? undefined) : undefined);
111
+ return {
112
+ v: 1,
113
+ instanceId: identity.instanceId,
114
+ machineName: scrubText(identity.machineName, 60) || 'This machine',
115
+ crewlyVersion: scrubText(version, 30) || 'unknown',
116
+ generatedAt: iso(now),
117
+ outcomes: {
118
+ ...this.doneToday(tickets, workItems, dayStart, teamName, agentName),
119
+ waitingOnOwner: this.waiting(decisions, holds, tickets, teamName, agentName),
120
+ stuck: this.stuck(workItems, tickets, roster, now, teamName, agentName),
121
+ spend: usage?.spend ?? { todayUsd: 0, weekUsd: 0, todayTokens: 0, weekTokens: 0 },
122
+ },
123
+ teams: teams.map((t) => ({
124
+ teamId: t.teamId,
125
+ name: scrubText(t.name, 60) || t.teamId,
126
+ ...(t.paused ? { paused: true } : {}),
127
+ agents: t.members.map((m) => this.row(m, now)),
128
+ })),
129
+ };
130
+ }
131
+ /**
132
+ * Rows for some agents, from the cache and the live activity. No disk or
133
+ * network reads.
134
+ *
135
+ * @param sessions - Sessions to build
136
+ * @returns Rows of the known sessions
137
+ */
138
+ rows(sessions) {
139
+ const now = this.clock();
140
+ const out = [];
141
+ for (const session of new Set(sessions)) {
142
+ const entry = this.cache.roster.get(session);
143
+ if (!entry)
144
+ continue;
145
+ out.push({ ...this.row(entry.member, now), teamId: entry.team.teamId });
146
+ }
147
+ return out;
148
+ }
149
+ // -------------------------------------------------------------------------
150
+ // Agent rows
151
+ // -------------------------------------------------------------------------
152
+ row(m, now) {
153
+ const turn = this.safeSync(() => this.sources.turnState(m.session, now), 'unknown');
154
+ const working = turn === 'working' || (turn === 'unknown' && m.workingStatus === 'in_progress' && m.agentStatus !== 'inactive');
155
+ const decision = this.cache.waits.get(m.session);
156
+ const hookWaiting = this.safeSync(() => this.sources.waitingOnHuman(m.session), false);
157
+ const error = this.safeSync(() => this.sources.errorOf?.(m.session) ?? null, null);
158
+ const queued = this.safeSync(() => this.sources.queuedFor(m.session), 0);
159
+ const stopped = m.agentStatus === 'inactive' || m.agentStatus === 'suspended';
160
+ const live = this.tracker.live(m.session, working && !stopped, now);
161
+ const trackerWaiting = live.activity?.type === 'waiting_owner';
162
+ let state;
163
+ if (stopped)
164
+ state = 'stopped';
165
+ else if (error)
166
+ state = 'error';
167
+ else if (hookWaiting || trackerWaiting || (decision && !working))
168
+ state = 'waiting';
169
+ else if (working)
170
+ state = 'working';
171
+ else if (queued > 0)
172
+ state = 'queued';
173
+ else
174
+ state = 'idle';
175
+ let activity = live.activity ?? undefined;
176
+ if (state === 'waiting' && activity?.type !== 'waiting_owner') {
177
+ const what = decision ? scrubText(decision.title, 100) : 'a reply to its prompt';
178
+ activity = { type: 'waiting_owner', detail: `Waiting for the owner: ${what}`.slice(0, C.MAX_TEXT), since: decision?.createdAt ?? iso(now) };
179
+ }
180
+ if (stopped || state === 'idle' || state === 'queued')
181
+ activity = undefined;
182
+ if (state === 'error' && error)
183
+ activity = { type: 'other', detail: scrubText(error), since: iso(now) };
184
+ const task = this.cache.tasks.get(m.session);
185
+ const usage = this.cache.usage?.perAgent.get(m.session);
186
+ const capabilities = this.safeSync(() => this.sources.capabilities?.(m.session, { runtime: m.runtime, stopped, now }) ?? null, null);
187
+ const row = {
188
+ session: m.session,
189
+ name: scrubText(m.name, 60) || m.session,
190
+ role: scrubText(m.role, 40),
191
+ runtime: scrubText(m.runtime, 30),
192
+ ...(m.model ? { model: scrubText(m.model, 60) } : {}),
193
+ state,
194
+ ...(activity ? { activity } : {}),
195
+ ...(task && !stopped ? { task: { ...(task.id ? { id: task.id } : {}), title: scrubText(task.title), since: task.since } } : {}),
196
+ ...(live.lastAction ? { lastAction: live.lastAction } : {}),
197
+ ...(usage ? { todayUsd: round4(usage.todayUsd), todayTokens: Math.round(usage.todayTokens) } : {}),
198
+ ...(capabilities ? { capabilities } : {}),
199
+ };
200
+ return row;
201
+ }
202
+ safeSync(read, fallback) {
203
+ try {
204
+ return read();
205
+ }
206
+ catch {
207
+ return fallback;
208
+ }
209
+ }
210
+ // -------------------------------------------------------------------------
211
+ // Outcomes
212
+ // -------------------------------------------------------------------------
213
+ doneToday(tickets, workItems, dayStart, teamName, agentName) {
214
+ const items = [];
215
+ for (const t of tickets) {
216
+ if (t.column !== 'done')
217
+ continue;
218
+ const at = msOf(t.completedAt ?? t.updatedAt);
219
+ if (!Number.isFinite(at) || at < dayStart)
220
+ continue;
221
+ const title = scrubText(t.title);
222
+ items.push({
223
+ ticketId: t.label ?? t.id,
224
+ title,
225
+ result: resultOf(title, t.kind),
226
+ team: teamName(t.teamId, t.assignee) ?? 'Crewly',
227
+ ...(agentName(t.assignee) ? { agent: agentName(t.assignee) } : {}),
228
+ at: iso(at),
229
+ });
230
+ }
231
+ // WorkItems that belong to no ticket (a ticket carries its own result, and the
232
+ // harness's own verify / follow-through / retry items are bookkeeping, not results)
233
+ const OWNER_FACING_TYPES = new Set(['delegate', 'project_task', 'review']);
234
+ const BOOKKEEPING_TITLE = /^(?:verify|follow[- ]through|retry\b|refresh standing|tl fallback|re-?verify|\[?auto|[A-Z][A-Z0-9]{1,6}-\d+\b)/i;
235
+ for (const w of workItems) {
236
+ if (w.status !== 'verified' && w.status !== 'done')
237
+ continue;
238
+ if (!OWNER_FACING_TYPES.has(w.type))
239
+ continue;
240
+ if (w.requestId || w.projectTaskId)
241
+ continue;
242
+ if (BOOKKEEPING_TITLE.test(w.title.trim()))
243
+ continue;
244
+ const at = msOf(w.completedAt ?? w.statusChangedAt);
245
+ if (!Number.isFinite(at) || at < dayStart)
246
+ continue;
247
+ const title = scrubText(w.title);
248
+ items.push({
249
+ title,
250
+ result: resultOf(title),
251
+ team: teamName(undefined, w.target) ?? 'Crewly',
252
+ ...(agentName(w.target) ? { agent: agentName(w.target) } : {}),
253
+ at: iso(at),
254
+ });
255
+ }
256
+ items.sort((a, b) => Date.parse(b.at) - Date.parse(a.at));
257
+ return { doneTodaySummary: summarizeResults(items.map((i) => i.result)), doneToday: items.slice(0, C.MAX_DONE) };
258
+ }
259
+ waiting(decisions, holds, tickets, teamName, agentName) {
260
+ const out = [];
261
+ for (const d of decisions) {
262
+ out.push({
263
+ id: `decision:${d.id}`,
264
+ kind: d.kind,
265
+ title: scrubText(d.title) || 'A decision is waiting',
266
+ ...(teamName(d.teamId, d.asker) ? { team: teamName(d.teamId, d.asker) } : {}),
267
+ ...(agentName(d.asker) ? { agent: agentName(d.asker) } : {}),
268
+ since: d.createdAt,
269
+ action: { label: d.kind === 'approval' ? 'Approve' : 'Answer' },
270
+ });
271
+ }
272
+ for (const t of tickets) {
273
+ if (t.column !== 'to_review')
274
+ continue;
275
+ out.push({
276
+ id: `review:${t.id}`,
277
+ kind: 'review',
278
+ title: scrubText(t.label ? `${t.label} ${t.title}` : t.title),
279
+ ...(teamName(t.teamId, t.assignee) ? { team: teamName(t.teamId, t.assignee) } : {}),
280
+ ...(agentName(t.assignee) ? { agent: agentName(t.assignee) } : {}),
281
+ since: t.submittedAt ?? t.updatedAt,
282
+ action: { label: 'Review' },
283
+ });
284
+ }
285
+ for (const h of holds)
286
+ out.push({ ...h, title: scrubText(h.title) });
287
+ return out.sort((a, b) => Date.parse(a.since) - Date.parse(b.since)).slice(0, C.MAX_WAITING);
288
+ }
289
+ stuck(workItems, tickets, roster, now, teamName, agentName) {
290
+ const out = [];
291
+ const ownerWords = /owner|approv|sign[- ]?in|log ?in|credential|password|permission|reconnect|authori[sz]/i;
292
+ const add = (id, title, target, since, on, reason, teamId) => {
293
+ out.push({
294
+ id,
295
+ title: scrubText(title),
296
+ ...(teamName(teamId, target) ? { team: teamName(teamId, target) } : {}),
297
+ ...(agentName(target) ? { agent: agentName(target) } : {}),
298
+ since: iso(Number.isFinite(since) ? since : now),
299
+ on,
300
+ reason: scrubText(reason),
301
+ });
302
+ };
303
+ for (const w of workItems) {
304
+ const changed = msOf(w.statusChangedAt) || msOf(w.createdAt);
305
+ if (w.status === 'blocked') {
306
+ const reason = w.blockedReason ?? 'Blocked';
307
+ const on = w.blockSource === 'explicit' ? (ownerWords.test(reason) ? 'owner' : 'agent') : 'system';
308
+ add(`wi:${w.id}`, w.title, w.target, changed, on, reason);
309
+ }
310
+ else if (w.status === 'escalated') {
311
+ add(`wi:${w.id}`, w.title, w.target, changed, 'agent', 'Escalated and not resolved yet');
312
+ }
313
+ else if (w.status === 'failed') {
314
+ const at = msOf(w.completedAt) || changed;
315
+ if (now - at <= C.STUCK_FAILED_MAX_AGE_MS)
316
+ add(`wi:${w.id}`, w.title, w.target, at, 'agent', `Failed${w.error ? `: ${w.error}` : ''}`);
317
+ }
318
+ else if (w.status === 'queued') {
319
+ const waited = now - msOf(w.createdAt);
320
+ const scheduledLater = msOf(w.scheduledAt) > now;
321
+ if (waited >= C.STUCK_QUEUED_MS && !scheduledLater) {
322
+ add(`wi:${w.id}`, w.title, w.target, msOf(w.createdAt), w.target ? 'agent' : 'system', `Waiting in the queue for ${age(waited)}`);
323
+ }
324
+ }
325
+ else if (w.status === 'running') {
326
+ const ran = now - (msOf(w.startedAt) || changed);
327
+ if (ran >= C.STUCK_RUNNING_MS)
328
+ add(`wi:${w.id}`, w.title, w.target, msOf(w.startedAt) || changed, 'agent', `Running for ${age(ran)}`);
329
+ }
330
+ }
331
+ for (const t of tickets) {
332
+ if (t.column === 'blocked')
333
+ add(`ticket:${t.id}`, t.label ? `${t.label} ${t.title}` : t.title, t.assignee, msOf(t.updatedAt), 'agent', 'Ticket is blocked', t.teamId);
334
+ }
335
+ for (const session of roster.keys()) {
336
+ const stalled = this.safeSync(() => this.sources.stalledBackground(session, now), null);
337
+ if (stalled)
338
+ add(`bg:${session}`, stalled.description ?? 'Background task', session, stalled.since, 'agent', `Background task stopped making progress ${age(now - stalled.since)} ago`);
339
+ }
340
+ return out.sort((a, b) => Date.parse(a.since) - Date.parse(b.since)).slice(0, C.MAX_STUCK);
341
+ }
342
+ }
343
+ const round4 = (n) => Math.round(n * 10_000) / 10_000;
344
+ //# sourceMappingURL=board-snapshot.builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-snapshot.builder.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-snapshot.builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Spend and "what is each agent on" from the usage ledger (crewly#1247).
3
+ * A pure fold over the ledger's in-memory rollup: per-agent and total cost
4
+ * for today (local midnight) and the last 7 days, the owner's minutes, and the
5
+ * open ticket each agent used tokens on most recently (the ledger's
6
+ * attribution, #1250).
7
+ *
8
+ * @module services/board/board-usage
9
+ */
10
+ import type { UnitRollup } from '../usage-ledger/ledger-rollup.js';
11
+ import type { BoardUsage } from './board-snapshot.builder.js';
12
+ /**
13
+ * Fold the ledger rollup into board usage.
14
+ *
15
+ * @param units - The rollup's units
16
+ * @param now - Epoch ms
17
+ * @returns Usage for the board
18
+ */
19
+ export declare function computeBoardUsage(units: Iterable<UnitRollup>, now: number): BoardUsage;
20
+ //# sourceMappingURL=board-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-usage.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAuDtF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Spend and "what is each agent on" from the usage ledger (crewly#1247).
3
+ * A pure fold over the ledger's in-memory rollup: per-agent and total cost
4
+ * for today (local midnight) and the last 7 days, the owner's minutes, and the
5
+ * open ticket each agent used tokens on most recently (the ledger's
6
+ * attribution, #1250).
7
+ *
8
+ * @module services/board/board-usage
9
+ */
10
+ import { BOARD_CONSTANTS } from '../../constants.js';
11
+ import { buildOwnerSpans, ownerMinutesByUnit } from '../usage-ledger/owner-spans.js';
12
+ import { scrubText } from './board-redact.js';
13
+ import { startOfLocalDay } from './board-snapshot.builder.js';
14
+ const HOUR_MS = 3_600_000;
15
+ const WEEK_MS = 7 * 24 * HOUR_MS;
16
+ /**
17
+ * Fold the ledger rollup into board usage.
18
+ *
19
+ * @param units - The rollup's units
20
+ * @param now - Epoch ms
21
+ * @returns Usage for the board
22
+ */
23
+ export function computeBoardUsage(units, now) {
24
+ const dayStart = startOfLocalDay(now);
25
+ const weekStart = now - WEEK_MS;
26
+ const perAgent = new Map();
27
+ const spend = { todayUsd: 0, weekUsd: 0, todayTokens: 0, weekTokens: 0 };
28
+ const recent = new Map();
29
+ const spans = [];
30
+ for (const unit of units) {
31
+ if (unit.ownerMessages.length > 0)
32
+ spans.push(...buildOwnerSpans(unit.unitId, unit.ownerMessages));
33
+ for (const e of unit.usage) {
34
+ const end = e.hourMs + HOUR_MS;
35
+ if (end <= weekStart || e.hourMs > now)
36
+ continue;
37
+ const tokens = e.budget * e.share;
38
+ const usd = e.costUsd * e.share;
39
+ spend.weekUsd += usd;
40
+ spend.weekTokens += tokens;
41
+ if (e.hourMs + HOUR_MS > dayStart) {
42
+ spend.todayUsd += usd;
43
+ spend.todayTokens += tokens;
44
+ const a = perAgent.get(e.agent) ?? { todayUsd: 0, todayTokens: 0 };
45
+ a.todayUsd += usd;
46
+ a.todayTokens += tokens;
47
+ perAgent.set(e.agent, a);
48
+ }
49
+ if (!unit.synthetic && !unit.closedAt) {
50
+ const at = Math.min(now, e.hourMs + HOUR_MS);
51
+ const prev = recent.get(e.agent);
52
+ if (!prev || at > prev.at)
53
+ recent.set(e.agent, { unit, at });
54
+ }
55
+ }
56
+ }
57
+ const taskOf = new Map();
58
+ for (const [agent, { unit, at }] of recent) {
59
+ if (now - at > BOARD_CONSTANTS.LEDGER_TASK_MAX_AGE_MS)
60
+ continue;
61
+ const title = scrubText(unit.title || unit.ticketId || '');
62
+ if (!title)
63
+ continue;
64
+ taskOf.set(agent, { ...(unit.ticketId ? { id: unit.ticketId } : {}), title, since: new Date(unit.firstMs || at).toISOString() });
65
+ }
66
+ const sum = (m) => [...m.values()].reduce((a, b) => a + b, 0);
67
+ const minutes = (from) => Math.round(sum(ownerMinutesByUnit(spans, from, now)));
68
+ return {
69
+ perAgent,
70
+ taskOf,
71
+ spend: {
72
+ todayUsd: round2(spend.todayUsd),
73
+ weekUsd: round2(spend.weekUsd),
74
+ todayTokens: Math.round(spend.todayTokens),
75
+ weekTokens: Math.round(spend.weekTokens),
76
+ ownerMinutesToday: minutes(dayStart),
77
+ ownerMinutesWeek: minutes(weekStart),
78
+ },
79
+ };
80
+ }
81
+ const round2 = (n) => Math.round(n * 100) / 100;
82
+ //# sourceMappingURL=board-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-usage.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The Owner Board on this machine (crewly#1247): the snapshot builder, the
3
+ * live activity tracker and the Cloud push, behind one handle. The local
4
+ * `GET /api/board` and the push share one builder, so the dashboard and the
5
+ * phone show the same board.
6
+ *
7
+ * @module services/board/board.service
8
+ */
9
+ import { BoardActivityTracker } from './board-activity.tracker.js';
10
+ import { BoardPushService, type BoardCloudSession } from './board-push.service.js';
11
+ import { BoardSnapshotBuilder, type BoardSources } from './board-snapshot.builder.js';
12
+ import type { BoardSnapshot, BoardTimelineEvent } from './board.types.js';
13
+ /** Constructor options. */
14
+ export interface BoardServiceOptions {
15
+ sources: BoardSources;
16
+ cloud: BoardCloudSession;
17
+ tracker?: BoardActivityTracker;
18
+ env?: NodeJS.ProcessEnv;
19
+ }
20
+ /** Owner board for this machine. */
21
+ export declare class BoardService {
22
+ readonly builder: BoardSnapshotBuilder;
23
+ readonly push: BoardPushService;
24
+ readonly tracker: BoardActivityTracker;
25
+ private last;
26
+ private inflight;
27
+ constructor(options: BoardServiceOptions);
28
+ /** Start the Cloud push (a no-op when switched off). */
29
+ start(): void;
30
+ /** Stop the Cloud push. */
31
+ stop(): void;
32
+ /**
33
+ * The current snapshot; one built within `maxAgeMs` is reused, and
34
+ * concurrent callers share one build.
35
+ *
36
+ * @param maxAgeMs - Oldest acceptable snapshot (ms)
37
+ * @returns The snapshot
38
+ */
39
+ getSnapshot(maxAgeMs?: number): Promise<BoardSnapshot>;
40
+ /**
41
+ * An agent's recent timeline.
42
+ *
43
+ * @param session - Agent session
44
+ * @returns Events, oldest first
45
+ */
46
+ timeline(session: string): BoardTimelineEvent[];
47
+ }
48
+ /**
49
+ * Install the process-wide board (composition root).
50
+ *
51
+ * @param service - The board, or null
52
+ */
53
+ export declare function setBoardService(service: BoardService | null): void;
54
+ /**
55
+ * The process-wide board, or null before boot wired it.
56
+ *
57
+ * @returns The board or null
58
+ */
59
+ export declare function getBoardService(): BoardService | null;
60
+ //# sourceMappingURL=board.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,2BAA2B;AAC3B,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,oCAAoC;AACpC,qBAAa,YAAY;IACxB,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,OAAO,CAAC,IAAI,CAAwD;IACpE,OAAO,CAAC,QAAQ,CAAuC;gBAE3C,OAAO,EAAE,mBAAmB;IAMxC,wDAAwD;IACxD,KAAK,IAAI,IAAI;IAIb,2BAA2B;IAC3B,IAAI,IAAI,IAAI;IAIZ;;;;;;OAMG;IACG,WAAW,CAAC,QAAQ,SAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAgB3D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE;CAG/C;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAErD"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The Owner Board on this machine (crewly#1247): the snapshot builder, the
3
+ * live activity tracker and the Cloud push, behind one handle. The local
4
+ * `GET /api/board` and the push share one builder, so the dashboard and the
5
+ * phone show the same board.
6
+ *
7
+ * @module services/board/board.service
8
+ */
9
+ import { BoardActivityTracker } from './board-activity.tracker.js';
10
+ import { BoardPushService } from './board-push.service.js';
11
+ import { BoardSnapshotBuilder } from './board-snapshot.builder.js';
12
+ /** Owner board for this machine. */
13
+ export class BoardService {
14
+ builder;
15
+ push;
16
+ tracker;
17
+ last = null;
18
+ inflight = null;
19
+ constructor(options) {
20
+ this.tracker = options.tracker ?? BoardActivityTracker.getInstance();
21
+ this.builder = new BoardSnapshotBuilder(options.sources, this.tracker);
22
+ this.push = new BoardPushService({ builder: this.builder, tracker: this.tracker, cloud: options.cloud, ...(options.env ? { env: options.env } : {}) });
23
+ }
24
+ /** Start the Cloud push (a no-op when switched off). */
25
+ start() {
26
+ this.push.start();
27
+ }
28
+ /** Stop the Cloud push. */
29
+ stop() {
30
+ this.push.stop();
31
+ }
32
+ /**
33
+ * The current snapshot; one built within `maxAgeMs` is reused, and
34
+ * concurrent callers share one build.
35
+ *
36
+ * @param maxAgeMs - Oldest acceptable snapshot (ms)
37
+ * @returns The snapshot
38
+ */
39
+ async getSnapshot(maxAgeMs = 5_000) {
40
+ if (this.last && Date.now() - this.last.at < maxAgeMs)
41
+ return this.last.snapshot;
42
+ if (!this.inflight) {
43
+ this.inflight = this.builder
44
+ .build()
45
+ .then((snapshot) => {
46
+ this.last = { at: Date.now(), snapshot };
47
+ return snapshot;
48
+ })
49
+ .finally(() => {
50
+ this.inflight = null;
51
+ });
52
+ }
53
+ return this.inflight;
54
+ }
55
+ /**
56
+ * An agent's recent timeline.
57
+ *
58
+ * @param session - Agent session
59
+ * @returns Events, oldest first
60
+ */
61
+ timeline(session) {
62
+ return this.tracker.timeline(session);
63
+ }
64
+ }
65
+ let instance = null;
66
+ /**
67
+ * Install the process-wide board (composition root).
68
+ *
69
+ * @param service - The board, or null
70
+ */
71
+ export function setBoardService(service) {
72
+ instance = service;
73
+ }
74
+ /**
75
+ * The process-wide board, or null before boot wired it.
76
+ *
77
+ * @returns The board or null
78
+ */
79
+ export function getBoardService() {
80
+ return instance;
81
+ }
82
+ //# sourceMappingURL=board.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAqB,MAAM,6BAA6B,CAAC;AAWtF,oCAAoC;AACpC,MAAM,OAAO,YAAY;IACf,OAAO,CAAuB;IAC9B,IAAI,CAAmB;IACvB,OAAO,CAAuB;IAC/B,IAAI,GAAmD,IAAI,CAAC;IAC5D,QAAQ,GAAkC,IAAI,CAAC;IAEvD,YAAY,OAA4B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,oBAAoB,CAAC,WAAW,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACxJ,CAAC;IAED,wDAAwD;IACxD,KAAK;QACJ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,2BAA2B;IAC3B,IAAI;QACH,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAAC,QAAQ,GAAG,KAAK;QACjC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;QACjF,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO;iBAC1B,KAAK,EAAE;iBACP,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;gBAClB,IAAI,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC;gBACzC,OAAO,QAAQ,CAAC;YACjB,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACb,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,OAAe;QACvB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;CACD;AAED,IAAI,QAAQ,GAAwB,IAAI,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC3D,QAAQ,GAAG,OAAO,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,QAAQ,CAAC;AACjB,CAAC"}