@mastra/factory 0.10.2-alpha.3 → 0.11.0
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.
- package/CHANGELOG.md +52 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +18 -2
- package/dist/factory.js.map +1 -1
- package/dist/routes/attention-providers.d.ts +101 -0
- package/dist/routes/attention-providers.d.ts.map +1 -0
- package/dist/routes/attention-providers.js +384 -0
- package/dist/routes/attention-providers.js.map +1 -0
- package/dist/routes/attention.d.ts +11 -9
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +143 -193
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/surface.d.ts +2 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +3 -1
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts +3 -0
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +3 -1
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +3 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +9 -1
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +2 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +10 -2
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/storage/domains/audit/domain.d.ts.map +1 -1
- package/dist/storage/domains/audit/domain.js +2 -5
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/dist/storage/domains/channel-identity/base.d.ts +4 -0
- package/dist/storage/domains/channel-identity/base.d.ts.map +1 -1
- package/dist/storage/domains/channel-identity/base.js +15 -0
- package/dist/storage/domains/channel-identity/base.js.map +1 -1
- package/dist/storage/domains/comments/actor.d.ts +51 -0
- package/dist/storage/domains/comments/actor.d.ts.map +1 -0
- package/dist/storage/domains/comments/actor.js +38 -0
- package/dist/storage/domains/comments/actor.js.map +1 -0
- package/dist/storage/domains/comments/base.d.ts +230 -0
- package/dist/storage/domains/comments/base.d.ts.map +1 -0
- package/dist/storage/domains/comments/base.js +490 -0
- package/dist/storage/domains/comments/base.js.map +1 -0
- package/dist/storage/domains/comments/domain.d.ts +125 -0
- package/dist/storage/domains/comments/domain.d.ts.map +1 -0
- package/dist/storage/domains/comments/domain.js +270 -0
- package/dist/storage/domains/comments/domain.js.map +1 -0
- package/dist/storage/domains/comments/feed-context.d.ts +20 -0
- package/dist/storage/domains/comments/feed-context.d.ts.map +1 -0
- package/dist/storage/domains/comments/feed-context.js +74 -0
- package/dist/storage/domains/comments/feed-context.js.map +1 -0
- package/dist/storage/domains/comments/feed-sync.d.ts +38 -0
- package/dist/storage/domains/comments/feed-sync.d.ts.map +1 -0
- package/dist/storage/domains/comments/feed-sync.js +1 -0
- package/dist/storage/domains/comments/parse.d.ts +22 -0
- package/dist/storage/domains/comments/parse.d.ts.map +1 -0
- package/dist/storage/domains/comments/parse.js +90 -0
- package/dist/storage/domains/comments/parse.js.map +1 -0
- package/dist/storage/domains/comments/routes.d.ts +22 -0
- package/dist/storage/domains/comments/routes.d.ts.map +1 -0
- package/dist/storage/domains/comments/routes.js +248 -0
- package/dist/storage/domains/comments/routes.js.map +1 -0
- package/dist/storage/domains/comments/schema.d.ts +4 -0
- package/dist/storage/domains/comments/schema.d.ts.map +1 -0
- package/dist/storage/domains/comments/schema.js +135 -0
- package/dist/storage/domains/comments/schema.js.map +1 -0
- package/dist/storage/domains/comments/wire.d.ts +40 -0
- package/dist/storage/domains/comments/wire.d.ts.map +1 -0
- package/dist/storage/domains/comments/wire.js +37 -0
- package/dist/storage/domains/comments/wire.js.map +1 -0
- package/dist/storage/domains/work-items/base.d.ts +14 -10
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +109 -22
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/factory-skills/factory-rereview/SKILL.md +20 -12
- package/factory-skills/factory-review/SKILL.md +17 -12
- package/package.json +8 -8
package/dist/routes/attention.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import { factoryAttentionKey
|
|
2
|
-
import {
|
|
1
|
+
import { factoryAttentionKey } from "../storage/domains/work-items/base.js";
|
|
2
|
+
import { AutomationFailedAttentionProvider, MentionAttentionProvider, factoryDecisionType } from "./attention-providers.js";
|
|
3
3
|
import { registerApiRoute } from "@mastra/core/server";
|
|
4
4
|
//#region src/routes/attention.ts
|
|
5
5
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
6
|
const DEFAULT_PAGE_SIZE = 25;
|
|
7
7
|
const MAX_PAGE_SIZE = 50;
|
|
8
|
-
const MAX_RECEIPT_SCAN_PAGES = 4;
|
|
9
|
-
function factoryDecisionType(decision) {
|
|
10
|
-
return typeof decision.decision.type === "string" ? decision.decision.type.slice(0, 64) : "unknown";
|
|
11
|
-
}
|
|
12
8
|
function parseAttentionView(raw) {
|
|
13
9
|
if (!raw || raw === "open") return "open";
|
|
14
10
|
if (raw === "unread" || raw === "archived") return raw;
|
|
@@ -18,90 +14,130 @@ function parseAttentionLimit(raw) {
|
|
|
18
14
|
if (!Number.isFinite(parsed)) return DEFAULT_PAGE_SIZE;
|
|
19
15
|
return Math.max(1, Math.min(MAX_PAGE_SIZE, parsed));
|
|
20
16
|
}
|
|
21
|
-
function
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
function attentionKey(factoryProjectId, decision) {
|
|
25
|
-
return factoryAttentionKey(factoryProjectId, attentionIdentity(decision));
|
|
17
|
+
function isAttentionKind(value) {
|
|
18
|
+
return value === "automation-failed" || value === "mention";
|
|
26
19
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
20
|
+
function encodeAttentionCursor(cursors) {
|
|
21
|
+
const wire = {};
|
|
22
|
+
for (const [kind, position] of cursors) wire[kind] = position ? [position.occurredAt.toISOString(), position.id] : null;
|
|
23
|
+
return Buffer.from(JSON.stringify(wire), "utf8").toString("base64url");
|
|
29
24
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
25
|
+
function parseStreamPosition(value) {
|
|
26
|
+
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "string" || typeof value[1] !== "string") return;
|
|
27
|
+
const occurredAt = new Date(value[0]);
|
|
28
|
+
if (Number.isNaN(occurredAt.getTime()) || !UUID_RE.test(value[1])) return void 0;
|
|
29
|
+
return {
|
|
30
|
+
occurredAt,
|
|
31
|
+
id: value[1]
|
|
32
|
+
};
|
|
32
33
|
}
|
|
33
34
|
function parseAttentionCursor(raw) {
|
|
34
35
|
if (!raw) return void 0;
|
|
35
36
|
try {
|
|
36
37
|
const decoded = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
if (Array.isArray(decoded)) {
|
|
39
|
+
const legacy = parseStreamPosition(decoded);
|
|
40
|
+
return legacy ? /* @__PURE__ */ new Map([["automation-failed", legacy]]) : void 0;
|
|
41
|
+
}
|
|
42
|
+
if (!decoded || typeof decoded !== "object") return void 0;
|
|
43
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
44
|
+
for (const [kind, value] of Object.entries(decoded)) {
|
|
45
|
+
if (!isAttentionKind(kind)) return void 0;
|
|
46
|
+
if (value === null) {
|
|
47
|
+
cursors.set(kind, void 0);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const position = parseStreamPosition(value);
|
|
51
|
+
if (!position) return void 0;
|
|
52
|
+
cursors.set(kind, position);
|
|
53
|
+
}
|
|
54
|
+
return cursors.size > 0 ? cursors : void 0;
|
|
44
55
|
} catch {
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
|
-
function
|
|
59
|
+
function parseOccurrence(raw) {
|
|
49
60
|
if (!raw || !/^(0|[1-9]\d*)$/.test(raw)) return void 0;
|
|
50
61
|
const occurrence = Number(raw);
|
|
51
62
|
return Number.isSafeInteger(occurrence) ? occurrence : void 0;
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Take the newest `limit` entries across provider pages. Each kind's next
|
|
66
|
+
* cursor is the resume position of its last consumed entry; a kind consumed to
|
|
67
|
+
* the end inherits the provider's own continuation.
|
|
68
|
+
*/
|
|
69
|
+
function mergeAttentionPages(pages, limit) {
|
|
70
|
+
const consumed = new Map(pages.map((page) => [page.kind, 0]));
|
|
71
|
+
const items = [];
|
|
72
|
+
while (items.length < limit) {
|
|
73
|
+
let best;
|
|
74
|
+
for (const page of pages) {
|
|
75
|
+
const next = page.result.entries[consumed.get(page.kind) ?? 0];
|
|
76
|
+
if (!next) continue;
|
|
77
|
+
const at = next.occurredAt.getTime();
|
|
78
|
+
if (!best || at > best.at) best = {
|
|
79
|
+
kind: page.kind,
|
|
80
|
+
at
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (!best) break;
|
|
84
|
+
const index = consumed.get(best.kind) ?? 0;
|
|
85
|
+
const entry = pages.find((page) => page.kind === best.kind)?.result.entries[index];
|
|
86
|
+
if (!entry) break;
|
|
87
|
+
items.push(entry.item);
|
|
88
|
+
consumed.set(best.kind, index + 1);
|
|
89
|
+
}
|
|
90
|
+
const nextCursors = /* @__PURE__ */ new Map();
|
|
91
|
+
let hasMore = false;
|
|
92
|
+
for (const page of pages) {
|
|
93
|
+
const used = consumed.get(page.kind) ?? 0;
|
|
94
|
+
const entries = page.result.entries;
|
|
95
|
+
if (used < entries.length) {
|
|
96
|
+
hasMore = true;
|
|
97
|
+
const lastConsumed = used > 0 ? entries[used - 1] : void 0;
|
|
98
|
+
nextCursors.set(page.kind, lastConsumed ? lastConsumed.resumeCursor : page.incoming);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (page.result.hasMore) {
|
|
102
|
+
hasMore = true;
|
|
103
|
+
nextCursors.set(page.kind, page.result.continuation ?? entries.at(-1)?.resumeCursor ?? page.incoming);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
63
106
|
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
107
|
+
items,
|
|
108
|
+
hasMore,
|
|
109
|
+
...hasMore && nextCursors.size > 0 ? { nextCursor: encodeAttentionCursor(nextCursors) } : {}
|
|
67
110
|
};
|
|
68
111
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
workItemId: decision.workItemId,
|
|
77
|
-
title: item?.title ?? failure.label,
|
|
78
|
-
detail: decision.lastError?.slice(0, 512) ?? failure.label,
|
|
79
|
-
decisionType: factoryDecisionType(decision),
|
|
80
|
-
failureCode: decision.failureCode,
|
|
81
|
-
canRetry: failure.canRetry,
|
|
82
|
-
occurredAt: failureOccurredAt(decision).toISOString(),
|
|
83
|
-
read: receipt !== void 0,
|
|
84
|
-
archived: receipt?.state === "archived",
|
|
85
|
-
target: attentionTarget(decision, item)
|
|
86
|
-
};
|
|
112
|
+
function newestLatest(latests) {
|
|
113
|
+
let newest = null;
|
|
114
|
+
for (const latest of latests) {
|
|
115
|
+
if (!latest) continue;
|
|
116
|
+
if (!newest || latest.at.getTime() > newest.at.getTime()) newest = latest;
|
|
117
|
+
}
|
|
118
|
+
return newest;
|
|
87
119
|
}
|
|
88
120
|
function receiptRoute(dependencies, verb, action) {
|
|
89
|
-
return registerApiRoute(`/web/factory/projects/:id/attention
|
|
121
|
+
return registerApiRoute(`/web/factory/projects/:id/attention/:kind/:sourceId/:occurrence/${verb}`, {
|
|
90
122
|
method: "POST",
|
|
91
123
|
requiresAuth: false,
|
|
92
124
|
handler: async (context) => {
|
|
93
125
|
const resolved = await dependencies.resolveProject(context);
|
|
94
126
|
if ("response" in resolved) return resolved.response;
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
127
|
+
const kind = context.req.param("kind");
|
|
128
|
+
const sourceId = context.req.param("sourceId");
|
|
129
|
+
const occurrence = parseOccurrence(context.req.param("occurrence"));
|
|
130
|
+
if (!kind || !isAttentionKind(kind) || !sourceId || !UUID_RE.test(sourceId) || occurrence === void 0) return context.json({ error: "invalid_attention_item" }, 422);
|
|
98
131
|
await dependencies.workItems.ensureReady();
|
|
99
132
|
const receipt = await dependencies.workItems.setAttentionReceipt({
|
|
100
133
|
orgId: resolved.orgId,
|
|
101
134
|
factoryProjectId: resolved.factoryProjectId,
|
|
102
135
|
userId: resolved.userId,
|
|
103
|
-
|
|
104
|
-
|
|
136
|
+
identity: {
|
|
137
|
+
kind,
|
|
138
|
+
sourceId,
|
|
139
|
+
occurrence
|
|
140
|
+
},
|
|
105
141
|
action,
|
|
106
142
|
now: /* @__PURE__ */ new Date()
|
|
107
143
|
});
|
|
@@ -116,7 +152,11 @@ function receiptRoute(dependencies, verb, action) {
|
|
|
116
152
|
});
|
|
117
153
|
}
|
|
118
154
|
function buildAttentionRoutes(dependencies) {
|
|
119
|
-
const { workItems } = dependencies;
|
|
155
|
+
const { workItems, comments } = dependencies;
|
|
156
|
+
const providers = [new AutomationFailedAttentionProvider({ workItems }), new MentionAttentionProvider({
|
|
157
|
+
workItems,
|
|
158
|
+
comments
|
|
159
|
+
})];
|
|
120
160
|
return [
|
|
121
161
|
registerApiRoute("/web/factory/projects/:id/attention", {
|
|
122
162
|
method: "GET",
|
|
@@ -130,115 +170,45 @@ function buildAttentionRoutes(dependencies) {
|
|
|
130
170
|
const before = parseAttentionCursor(cursorRaw);
|
|
131
171
|
if (cursorRaw && !before) return context.json({ error: "invalid_cursor" }, 400);
|
|
132
172
|
await workItems.ensureReady();
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}),
|
|
173
|
+
await comments.ensureReady();
|
|
174
|
+
const search = context.req.query("search")?.trim().toLowerCase().slice(0, 200);
|
|
175
|
+
const limit = parseAttentionLimit(context.req.query("limit"));
|
|
176
|
+
const active = providers.filter((provider) => !before || before.has(provider.kind));
|
|
177
|
+
const [approvalCount, counts, latests, pages] = await Promise.all([
|
|
139
178
|
workItems.countDeferredDecisionsByStatuses({
|
|
140
179
|
orgId: resolved.orgId,
|
|
141
180
|
factoryProjectId: resolved.factoryProjectId,
|
|
142
181
|
statuses: ["proposed"]
|
|
143
182
|
}),
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
orgId: resolved.orgId,
|
|
157
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
158
|
-
limit: 1
|
|
159
|
-
})
|
|
183
|
+
Promise.all(providers.map((provider) => provider.counts(resolved))),
|
|
184
|
+
Promise.all(providers.map((provider) => provider.latest(resolved))),
|
|
185
|
+
Promise.all(active.map(async (provider) => ({
|
|
186
|
+
kind: provider.kind,
|
|
187
|
+
incoming: before?.get(provider.kind),
|
|
188
|
+
result: await provider.page(resolved, {
|
|
189
|
+
view,
|
|
190
|
+
...search ? { search } : {},
|
|
191
|
+
before: before?.get(provider.kind),
|
|
192
|
+
limit
|
|
193
|
+
})
|
|
194
|
+
})))
|
|
160
195
|
]);
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const newestReceipt = newestFailure ? (await workItems.listAttentionReceipts({
|
|
167
|
-
orgId: resolved.orgId,
|
|
168
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
169
|
-
userId: resolved.userId,
|
|
170
|
-
identities: [attentionIdentity(newestFailure)]
|
|
171
|
-
}))[0] : void 0;
|
|
172
|
-
const search = context.req.query("search")?.trim().toLowerCase().slice(0, 200);
|
|
173
|
-
const requestedLimit = parseAttentionLimit(context.req.query("limit"));
|
|
174
|
-
const visible = [];
|
|
175
|
-
let scanBefore = before;
|
|
176
|
-
let cursorDecision;
|
|
177
|
-
let continuationDecision;
|
|
178
|
-
let scannedPages = 0;
|
|
179
|
-
let hasMore = false;
|
|
180
|
-
scan: while (view === "open" && failureOpenCount > 0 || view === "unread" && unreadCount > 0 || view === "archived" && archivedCount > 0) {
|
|
181
|
-
const page = await workItems.listFailedDecisionPage({
|
|
182
|
-
orgId: resolved.orgId,
|
|
183
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
184
|
-
before: scanBefore,
|
|
185
|
-
limit: MAX_PAGE_SIZE
|
|
186
|
-
});
|
|
187
|
-
scannedPages += 1;
|
|
188
|
-
if (page.decisions.length === 0) break;
|
|
189
|
-
const receipts = await workItems.listAttentionReceipts({
|
|
190
|
-
orgId: resolved.orgId,
|
|
191
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
192
|
-
userId: resolved.userId,
|
|
193
|
-
identities: page.decisions.map(attentionIdentity)
|
|
194
|
-
});
|
|
195
|
-
const receiptByKey = new Map(receipts.map((receipt) => [factoryAttentionKey(resolved.factoryProjectId, receipt), receipt]));
|
|
196
|
-
const linkedItems = await workItems.listByIds({
|
|
197
|
-
orgId: resolved.orgId,
|
|
198
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
199
|
-
ids: page.decisions.flatMap((decision) => decision.workItemId ? [decision.workItemId] : [])
|
|
200
|
-
});
|
|
201
|
-
const itemById = new Map(linkedItems.map((item) => [item.id, item]));
|
|
202
|
-
for (const decision of page.decisions) {
|
|
203
|
-
const receipt = receiptByKey.get(attentionKey(resolved.factoryProjectId, decision));
|
|
204
|
-
if (view === "archived" ? receipt?.state !== "archived" : view === "unread" ? receipt : receipt?.state === "archived") continue;
|
|
205
|
-
const item = decision.workItemId ? itemById.get(decision.workItemId) : void 0;
|
|
206
|
-
if (search && item?.title.toLowerCase().includes(search) !== true && decision.lastError?.toLowerCase().includes(search) !== true && !factoryDecisionType(decision).toLowerCase().includes(search)) continue;
|
|
207
|
-
if (visible.length === requestedLimit) {
|
|
208
|
-
hasMore = true;
|
|
209
|
-
continuationDecision = cursorDecision;
|
|
210
|
-
break scan;
|
|
211
|
-
}
|
|
212
|
-
visible.push({
|
|
213
|
-
decision,
|
|
214
|
-
item,
|
|
215
|
-
receipt
|
|
216
|
-
});
|
|
217
|
-
if (visible.length === requestedLimit) cursorDecision = decision;
|
|
218
|
-
}
|
|
219
|
-
const lastScanned = page.decisions.at(-1);
|
|
220
|
-
if (!page.hasMore || !lastScanned) break;
|
|
221
|
-
if (scannedPages === MAX_RECEIPT_SCAN_PAGES) {
|
|
222
|
-
hasMore = true;
|
|
223
|
-
continuationDecision = lastScanned;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
scanBefore = {
|
|
227
|
-
occurredAt: failureOccurredAt(lastScanned),
|
|
228
|
-
id: lastScanned.id
|
|
229
|
-
};
|
|
230
|
-
}
|
|
196
|
+
const openCount = counts.reduce((sum, count) => sum + count.open, 0) + approvalCount;
|
|
197
|
+
const unreadCount = counts.reduce((sum, count) => sum + count.unread, 0);
|
|
198
|
+
const unreadLatests = latests.filter((latest) => latest?.unread ?? false);
|
|
199
|
+
const latest = unreadLatests.length > 0 ? newestLatest(unreadLatests) : newestLatest(latests);
|
|
200
|
+
const merged = mergeAttentionPages(pages, limit);
|
|
231
201
|
return context.json({
|
|
232
|
-
items:
|
|
202
|
+
items: merged.items,
|
|
233
203
|
openCount,
|
|
234
204
|
approvalCount,
|
|
235
|
-
badgeCount,
|
|
205
|
+
badgeCount: unreadCount + approvalCount,
|
|
236
206
|
unreadCount,
|
|
237
|
-
latestOccurrenceKey:
|
|
238
|
-
latestOccurrenceAt:
|
|
239
|
-
latestOccurrenceUnread:
|
|
240
|
-
hasMore,
|
|
241
|
-
...
|
|
207
|
+
latestOccurrenceKey: latest?.key ?? null,
|
|
208
|
+
latestOccurrenceAt: latest?.at.toISOString() ?? null,
|
|
209
|
+
latestOccurrenceUnread: latest?.unread ?? false,
|
|
210
|
+
hasMore: merged.hasMore,
|
|
211
|
+
...merged.nextCursor ? { nextCursor: merged.nextCursor } : {}
|
|
242
212
|
});
|
|
243
213
|
}
|
|
244
214
|
}),
|
|
@@ -249,48 +219,28 @@ function buildAttentionRoutes(dependencies) {
|
|
|
249
219
|
const resolved = await dependencies.resolveProject(context);
|
|
250
220
|
if ("response" in resolved) return resolved.response;
|
|
251
221
|
const cursorRaw = context.req.query("before");
|
|
252
|
-
const
|
|
253
|
-
if (cursorRaw && !
|
|
222
|
+
const before = parseAttentionCursor(cursorRaw);
|
|
223
|
+
if (cursorRaw && !before) return context.json({ error: "invalid_cursor" }, 400);
|
|
254
224
|
await workItems.ensureReady();
|
|
255
|
-
|
|
256
|
-
|
|
225
|
+
await comments.ensureReady();
|
|
226
|
+
const now = /* @__PURE__ */ new Date();
|
|
227
|
+
const active = providers.filter((provider) => !before || before.has(provider.kind));
|
|
228
|
+
const nextCursors = /* @__PURE__ */ new Map();
|
|
257
229
|
let hasMore = false;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
263
|
-
before,
|
|
264
|
-
limit: MAX_PAGE_SIZE
|
|
265
|
-
});
|
|
266
|
-
pages += 1;
|
|
267
|
-
if (page.decisions.length === 0) break;
|
|
268
|
-
await workItems.markAttentionReceiptsRead({
|
|
269
|
-
orgId: resolved.orgId,
|
|
270
|
-
factoryProjectId: resolved.factoryProjectId,
|
|
271
|
-
userId: resolved.userId,
|
|
272
|
-
occurrences: page.decisions.map((decision) => ({
|
|
273
|
-
decisionId: decision.id,
|
|
274
|
-
failureOccurrence: decision.failureOccurrence
|
|
275
|
-
})),
|
|
276
|
-
now: /* @__PURE__ */ new Date()
|
|
230
|
+
for (const provider of active) {
|
|
231
|
+
const result = await provider.markAllRead(resolved, {
|
|
232
|
+
before: before?.get(provider.kind),
|
|
233
|
+
now
|
|
277
234
|
});
|
|
278
|
-
|
|
279
|
-
if (!page.hasMore || !last) break;
|
|
280
|
-
if (pages === MAX_RECEIPT_SCAN_PAGES) {
|
|
235
|
+
if (result.hasMore) {
|
|
281
236
|
hasMore = true;
|
|
282
|
-
|
|
283
|
-
break;
|
|
237
|
+
if (result.continuation) nextCursors.set(provider.kind, result.continuation);
|
|
284
238
|
}
|
|
285
|
-
before = {
|
|
286
|
-
occurredAt: failureOccurredAt(last),
|
|
287
|
-
id: last.id
|
|
288
|
-
};
|
|
289
239
|
}
|
|
290
240
|
return context.json({
|
|
291
241
|
ok: true,
|
|
292
242
|
hasMore,
|
|
293
|
-
...
|
|
243
|
+
...hasMore && nextCursors.size > 0 ? { nextCursor: encodeAttentionCursor(nextCursors) } : {}
|
|
294
244
|
});
|
|
295
245
|
}
|
|
296
246
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attention.js","names":[],"sources":["../../src/routes/attention.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\n\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n FactoryAttentionReceiptAction,\n FactoryAttentionReceiptRecord,\n FactoryDeferredDecisionRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport { factoryAttentionKey, factoryDecisionAttentionIdentity } from '../storage/domains/work-items/base.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst DEFAULT_PAGE_SIZE = 25;\nconst MAX_PAGE_SIZE = 50;\n// Receipt filtering is bounded to 200 failed decisions per request; the response cursor resumes after the last scan.\nconst MAX_RECEIPT_SCAN_PAGES = 4;\n\ntype FactoryAttentionView = 'open' | 'unread' | 'archived';\n\ninterface ResolvedAttentionProject {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n}\n\ninterface AttentionRouteDependencies {\n workItems: WorkItemsStorage;\n resolveProject(context: unknown): Promise<ResolvedAttentionProject | { response: Response }>;\n}\n\nexport function factoryDecisionType(decision: FactoryDeferredDecisionRecord): string {\n return typeof decision.decision.type === 'string' ? decision.decision.type.slice(0, 64) : 'unknown';\n}\n\nfunction parseAttentionView(raw: string | undefined): FactoryAttentionView | undefined {\n if (!raw || raw === 'open') return 'open';\n if (raw === 'unread' || raw === 'archived') return raw;\n return undefined;\n}\n\nfunction parseAttentionLimit(raw: string | undefined): number {\n const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_PAGE_SIZE;\n if (!Number.isFinite(parsed)) return DEFAULT_PAGE_SIZE;\n return Math.max(1, Math.min(MAX_PAGE_SIZE, parsed));\n}\n\nfunction attentionIdentity(decision: FactoryDeferredDecisionRecord) {\n return factoryDecisionAttentionIdentity(decision.id, decision.failureOccurrence);\n}\n\nfunction attentionKey(factoryProjectId: string, decision: FactoryDeferredDecisionRecord): string {\n return factoryAttentionKey(factoryProjectId, attentionIdentity(decision));\n}\n\nfunction failureOccurredAt(decision: FactoryDeferredDecisionRecord): Date {\n return decision.completedAt ?? decision.updatedAt;\n}\n\nfunction encodeAttentionCursor(decision: FactoryDeferredDecisionRecord): string {\n return Buffer.from(JSON.stringify([failureOccurredAt(decision).toISOString(), decision.id]), 'utf8').toString(\n 'base64url',\n );\n}\n\nfunction parseAttentionCursor(raw: string | undefined): { occurredAt: Date; id: string } | undefined {\n if (!raw) return undefined;\n try {\n const decoded: unknown = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8'));\n if (\n !Array.isArray(decoded) ||\n decoded.length !== 2 ||\n typeof decoded[0] !== 'string' ||\n typeof decoded[1] !== 'string'\n ) {\n return undefined;\n }\n const occurredAt = new Date(decoded[0]);\n if (Number.isNaN(occurredAt.getTime()) || !UUID_RE.test(decoded[1])) return undefined;\n return { occurredAt, id: decoded[1] };\n } catch {\n return undefined;\n }\n}\n\nfunction parseFailureOccurrence(raw: string | undefined): number | undefined {\n if (!raw || !/^(0|[1-9]\\d*)$/.test(raw)) return undefined;\n const occurrence = Number(raw);\n return Number.isSafeInteger(occurrence) ? occurrence : undefined;\n}\n\nfunction attentionTarget(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined) {\n if (!item) return { kind: 'rules' as const };\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : undefined;\n const session = role ? item.sessions[role] : undefined;\n if (session) {\n return {\n kind: 'thread' as const,\n sessionId: session.sessionId,\n threadId: session.threadId,\n };\n }\n const review = item.externalSource?.integrationId === 'github' && item.externalSource.type === 'pull-request';\n return {\n kind: 'work-item' as const,\n workItemId: item.id,\n board: review ? ('review' as const) : ('work' as const),\n };\n}\n\nfunction attentionItem(\n factoryProjectId: string,\n decision: FactoryDeferredDecisionRecord,\n item: WorkItemRow | undefined,\n receipt: FactoryAttentionReceiptRecord | undefined,\n) {\n const failure = factoryDispatchFailureMetadata(decision.failureCode);\n return {\n key: attentionKey(factoryProjectId, decision),\n kind: 'automation-failed' as const,\n decisionId: decision.id,\n occurrence: decision.failureOccurrence,\n workItemId: decision.workItemId,\n title: item?.title ?? failure.label,\n detail: decision.lastError?.slice(0, 512) ?? failure.label,\n decisionType: factoryDecisionType(decision),\n failureCode: decision.failureCode,\n canRetry: failure.canRetry,\n occurredAt: failureOccurredAt(decision).toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: attentionTarget(decision, item),\n };\n}\n\nfunction receiptRoute(\n dependencies: AttentionRouteDependencies,\n verb: 'read' | 'archive' | 'restore',\n action: FactoryAttentionReceiptAction,\n): ApiRoute {\n return registerApiRoute(`/web/factory/projects/:id/attention/automation-failed/:decisionId/:occurrence/${verb}`, {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n const failureOccurrence = parseFailureOccurrence(context.req.param('occurrence'));\n if (!decisionId || !UUID_RE.test(decisionId) || failureOccurrence === undefined) {\n return context.json({ error: 'invalid_attention_item' }, 422);\n }\n await dependencies.workItems.ensureReady();\n const receipt = await dependencies.workItems.setAttentionReceipt({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n decisionId,\n failureOccurrence,\n action,\n now: new Date(),\n });\n if (!receipt) return context.json({ error: 'attention_item_not_current' }, 409);\n return context.json({\n receipt: {\n key: factoryAttentionKey(resolved.factoryProjectId, receipt),\n state: receipt.state,\n readAt: receipt.readAt.toISOString(),\n archivedAt: receipt.archivedAt?.toISOString() ?? null,\n },\n });\n },\n });\n}\n\nexport function buildAttentionRoutes(dependencies: AttentionRouteDependencies): ApiRoute[] {\n const { workItems } = dependencies;\n return [\n registerApiRoute('/web/factory/projects/:id/attention', {\n method: 'GET',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const view = parseAttentionView(context.req.query('view'));\n if (view === undefined) return context.json({ error: 'invalid_attention_view' }, 400);\n const cursorRaw = context.req.query('before');\n const before = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !before) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n const [failedCount, approvalCount, receiptCount, archivedCount, newestPage] = await Promise.all([\n workItems.countDeferredDecisionsByStatuses({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: ['failed'],\n }),\n workItems.countDeferredDecisionsByStatuses({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: ['proposed'],\n }),\n workItems.countAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n }),\n workItems.countAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n state: 'archived',\n }),\n workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n limit: 1,\n }),\n ]);\n const failureOpenCount = Math.max(0, failedCount - archivedCount);\n const openCount = failureOpenCount + approvalCount;\n const unreadCount = Math.max(0, failedCount - receiptCount);\n const badgeCount = unreadCount + approvalCount;\n const newestFailure = newestPage.decisions[0];\n const newestReceipt = newestFailure\n ? (\n await workItems.listAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n identities: [attentionIdentity(newestFailure)],\n })\n )[0]\n : undefined;\n const search = context.req.query('search')?.trim().toLowerCase().slice(0, 200);\n const requestedLimit = parseAttentionLimit(context.req.query('limit'));\n const visible: Array<{\n decision: FactoryDeferredDecisionRecord;\n item: WorkItemRow | undefined;\n receipt: FactoryAttentionReceiptRecord | undefined;\n }> = [];\n let scanBefore = before;\n let cursorDecision: FactoryDeferredDecisionRecord | undefined;\n let continuationDecision: FactoryDeferredDecisionRecord | undefined;\n let scannedPages = 0;\n let hasMore = false;\n\n scan: while (\n (view === 'open' && failureOpenCount > 0) ||\n (view === 'unread' && unreadCount > 0) ||\n (view === 'archived' && archivedCount > 0)\n ) {\n const page = await workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n before: scanBefore,\n limit: MAX_PAGE_SIZE,\n });\n scannedPages += 1;\n if (page.decisions.length === 0) break;\n const receipts = await workItems.listAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n identities: page.decisions.map(attentionIdentity),\n });\n const receiptByKey = new Map(\n receipts.map(receipt => [factoryAttentionKey(resolved.factoryProjectId, receipt), receipt]),\n );\n const linkedItems = await workItems.listByIds({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n ids: page.decisions.flatMap(decision => (decision.workItemId ? [decision.workItemId] : [])),\n });\n const itemById = new Map(linkedItems.map(item => [item.id, item]));\n for (const decision of page.decisions) {\n const receipt = receiptByKey.get(attentionKey(resolved.factoryProjectId, decision));\n if (\n view === 'archived'\n ? receipt?.state !== 'archived'\n : view === 'unread'\n ? receipt\n : receipt?.state === 'archived'\n ) {\n continue;\n }\n const item = decision.workItemId ? itemById.get(decision.workItemId) : undefined;\n if (\n search &&\n item?.title.toLowerCase().includes(search) !== true &&\n decision.lastError?.toLowerCase().includes(search) !== true &&\n !factoryDecisionType(decision).toLowerCase().includes(search)\n ) {\n continue;\n }\n if (visible.length === requestedLimit) {\n hasMore = true;\n continuationDecision = cursorDecision;\n break scan;\n }\n visible.push({ decision, item, receipt });\n if (visible.length === requestedLimit) cursorDecision = decision;\n }\n const lastScanned = page.decisions.at(-1);\n if (!page.hasMore || !lastScanned) break;\n if (scannedPages === MAX_RECEIPT_SCAN_PAGES) {\n hasMore = true;\n continuationDecision = lastScanned;\n break;\n }\n scanBefore = { occurredAt: failureOccurredAt(lastScanned), id: lastScanned.id };\n }\n\n return context.json({\n items: visible.map(({ decision, item, receipt }) =>\n attentionItem(resolved.factoryProjectId, decision, item, receipt),\n ),\n openCount,\n approvalCount,\n badgeCount,\n unreadCount,\n latestOccurrenceKey: newestFailure ? attentionKey(resolved.factoryProjectId, newestFailure) : null,\n latestOccurrenceAt: newestFailure ? failureOccurredAt(newestFailure).toISOString() : null,\n latestOccurrenceUnread: newestFailure !== undefined && newestReceipt === undefined,\n hasMore,\n ...(hasMore && continuationDecision ? { nextCursor: encodeAttentionCursor(continuationDecision) } : {}),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/attention/read-all', {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const cursorRaw = context.req.query('before');\n const initialBefore = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !initialBefore) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n let before = initialBefore;\n let pages = 0;\n let hasMore = false;\n let nextCursor: string | undefined;\n while (pages < MAX_RECEIPT_SCAN_PAGES) {\n const page = await workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n before,\n limit: MAX_PAGE_SIZE,\n });\n pages += 1;\n if (page.decisions.length === 0) break;\n await workItems.markAttentionReceiptsRead({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n occurrences: page.decisions.map(decision => ({\n decisionId: decision.id,\n failureOccurrence: decision.failureOccurrence,\n })),\n now: new Date(),\n });\n const last = page.decisions.at(-1);\n if (!page.hasMore || !last) break;\n if (pages === MAX_RECEIPT_SCAN_PAGES) {\n hasMore = true;\n nextCursor = encodeAttentionCursor(last);\n break;\n }\n before = { occurredAt: failureOccurredAt(last), id: last.id };\n }\n return context.json({ ok: true, hasMore, ...(nextCursor ? { nextCursor } : {}) });\n },\n }),\n receiptRoute(dependencies, 'read', 'read'),\n receiptRoute(dependencies, 'archive', 'archive'),\n receiptRoute(dependencies, 'restore', 'restore'),\n ];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"attention.js","names":[],"sources":["../../src/routes/attention.ts"],"sourcesContent":["/**\n * Attention routes: k-way merge of the per-kind providers on `occurredAt desc`.\n * The wire cursor is a per-kind map — each kind's stream resumes independently,\n * `null` meaning \"not started yet\", an absent kind meaning \"exhausted\".\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\n\nimport type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';\nimport type {\n FactoryAttentionKind,\n FactoryAttentionReceiptAction,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport { factoryAttentionKey } from '../storage/domains/work-items/base.js';\nimport type {\n AttentionLatest,\n AttentionPageResult,\n AttentionProvider,\n AttentionScope,\n AttentionStreamPosition,\n FactoryAttentionView,\n} from './attention-providers.js';\nimport { AutomationFailedAttentionProvider, MentionAttentionProvider } from './attention-providers.js';\n\nexport { factoryDecisionType } from './attention-providers.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst DEFAULT_PAGE_SIZE = 25;\nconst MAX_PAGE_SIZE = 50;\n\ninterface AttentionRouteDependencies {\n workItems: WorkItemsStorage;\n comments: WorkItemCommentsStorage;\n resolveProject(context: unknown): Promise<AttentionScope | { response: Response }>;\n}\n\ntype AttentionCursorMap = Map<FactoryAttentionKind, AttentionStreamPosition | undefined>;\n\nfunction parseAttentionView(raw: string | undefined): FactoryAttentionView | undefined {\n if (!raw || raw === 'open') return 'open';\n if (raw === 'unread' || raw === 'archived') return raw;\n return undefined;\n}\n\nfunction parseAttentionLimit(raw: string | undefined): number {\n const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_PAGE_SIZE;\n if (!Number.isFinite(parsed)) return DEFAULT_PAGE_SIZE;\n return Math.max(1, Math.min(MAX_PAGE_SIZE, parsed));\n}\n\nfunction isAttentionKind(value: string): value is FactoryAttentionKind {\n return value === 'automation-failed' || value === 'mention';\n}\n\nfunction encodeAttentionCursor(cursors: AttentionCursorMap): string {\n const wire: Record<string, [string, string] | null> = {};\n for (const [kind, position] of cursors) {\n wire[kind] = position ? [position.occurredAt.toISOString(), position.id] : null;\n }\n return Buffer.from(JSON.stringify(wire), 'utf8').toString('base64url');\n}\n\nfunction parseStreamPosition(value: unknown): AttentionStreamPosition | undefined {\n if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== 'string' || typeof value[1] !== 'string') {\n return undefined;\n }\n const occurredAt = new Date(value[0]);\n if (Number.isNaN(occurredAt.getTime()) || !UUID_RE.test(value[1])) return undefined;\n return { occurredAt, id: value[1] };\n}\n\nfunction parseAttentionCursor(raw: string | undefined): AttentionCursorMap | undefined {\n if (!raw) return undefined;\n try {\n const decoded: unknown = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8'));\n // Cursors minted before the inbox merged kinds are a bare position over the\n // only stream there was. Held by anyone mid-list when this deploys, so they\n // resume that stream rather than 400: mentions arrive on their next load.\n if (Array.isArray(decoded)) {\n const legacy = parseStreamPosition(decoded);\n return legacy ? new Map([['automation-failed', legacy]]) : undefined;\n }\n if (!decoded || typeof decoded !== 'object') return undefined;\n const cursors: AttentionCursorMap = new Map();\n for (const [kind, value] of Object.entries(decoded)) {\n if (!isAttentionKind(kind)) return undefined;\n if (value === null) {\n cursors.set(kind, undefined);\n continue;\n }\n const position = parseStreamPosition(value);\n if (!position) return undefined;\n cursors.set(kind, position);\n }\n return cursors.size > 0 ? cursors : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction parseOccurrence(raw: string | undefined): number | undefined {\n if (!raw || !/^(0|[1-9]\\d*)$/.test(raw)) return undefined;\n const occurrence = Number(raw);\n return Number.isSafeInteger(occurrence) ? occurrence : undefined;\n}\n\ninterface MergedAttentionPage {\n items: Array<Record<string, unknown>>;\n hasMore: boolean;\n nextCursor?: string;\n}\n\n/**\n * Take the newest `limit` entries across provider pages. Each kind's next\n * cursor is the resume position of its last consumed entry; a kind consumed to\n * the end inherits the provider's own continuation.\n */\nfunction mergeAttentionPages(\n pages: Array<{\n kind: FactoryAttentionKind;\n incoming: AttentionStreamPosition | undefined;\n result: AttentionPageResult;\n }>,\n limit: number,\n): MergedAttentionPage {\n const consumed = new Map(pages.map(page => [page.kind, 0]));\n const items: Array<Record<string, unknown>> = [];\n while (items.length < limit) {\n let best: { kind: FactoryAttentionKind; at: number } | undefined;\n for (const page of pages) {\n const next = page.result.entries[consumed.get(page.kind) ?? 0];\n if (!next) continue;\n const at = next.occurredAt.getTime();\n if (!best || at > best.at) best = { kind: page.kind, at };\n }\n if (!best) break;\n const index = consumed.get(best.kind) ?? 0;\n const entry = pages.find(page => page.kind === best.kind)?.result.entries[index];\n if (!entry) break;\n items.push(entry.item);\n consumed.set(best.kind, index + 1);\n }\n\n const nextCursors: AttentionCursorMap = new Map();\n let hasMore = false;\n for (const page of pages) {\n const used = consumed.get(page.kind) ?? 0;\n const entries = page.result.entries;\n if (used < entries.length) {\n hasMore = true;\n const lastConsumed = used > 0 ? entries[used - 1] : undefined;\n nextCursors.set(page.kind, lastConsumed ? lastConsumed.resumeCursor : page.incoming);\n continue;\n }\n if (page.result.hasMore) {\n hasMore = true;\n nextCursors.set(page.kind, page.result.continuation ?? entries.at(-1)?.resumeCursor ?? page.incoming);\n }\n }\n return {\n items,\n hasMore,\n ...(hasMore && nextCursors.size > 0 ? { nextCursor: encodeAttentionCursor(nextCursors) } : {}),\n };\n}\n\nfunction newestLatest(latests: Array<AttentionLatest | null>): AttentionLatest | null {\n let newest: AttentionLatest | null = null;\n for (const latest of latests) {\n if (!latest) continue;\n if (!newest || latest.at.getTime() > newest.at.getTime()) newest = latest;\n }\n return newest;\n}\n\nfunction receiptRoute(\n dependencies: AttentionRouteDependencies,\n verb: 'read' | 'archive' | 'restore',\n action: FactoryAttentionReceiptAction,\n): ApiRoute {\n return registerApiRoute(`/web/factory/projects/:id/attention/:kind/:sourceId/:occurrence/${verb}`, {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const kind = context.req.param('kind');\n const sourceId = context.req.param('sourceId');\n const occurrence = parseOccurrence(context.req.param('occurrence'));\n if (!kind || !isAttentionKind(kind) || !sourceId || !UUID_RE.test(sourceId) || occurrence === undefined) {\n return context.json({ error: 'invalid_attention_item' }, 422);\n }\n await dependencies.workItems.ensureReady();\n const receipt = await dependencies.workItems.setAttentionReceipt({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n identity: { kind, sourceId, occurrence },\n action,\n now: new Date(),\n });\n if (!receipt) return context.json({ error: 'attention_item_not_current' }, 409);\n return context.json({\n receipt: {\n key: factoryAttentionKey(resolved.factoryProjectId, receipt),\n state: receipt.state,\n readAt: receipt.readAt.toISOString(),\n archivedAt: receipt.archivedAt?.toISOString() ?? null,\n },\n });\n },\n });\n}\n\nexport function buildAttentionRoutes(dependencies: AttentionRouteDependencies): ApiRoute[] {\n const { workItems, comments } = dependencies;\n const providers: AttentionProvider[] = [\n new AutomationFailedAttentionProvider({ workItems }),\n new MentionAttentionProvider({ workItems, comments }),\n ];\n\n return [\n registerApiRoute('/web/factory/projects/:id/attention', {\n method: 'GET',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const view = parseAttentionView(context.req.query('view'));\n if (view === undefined) return context.json({ error: 'invalid_attention_view' }, 400);\n const cursorRaw = context.req.query('before');\n const before = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !before) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n await comments.ensureReady();\n\n const search = context.req.query('search')?.trim().toLowerCase().slice(0, 200);\n const limit = parseAttentionLimit(context.req.query('limit'));\n const active = providers.filter(provider => !before || before.has(provider.kind));\n\n const [approvalCount, counts, latests, pages] = await Promise.all([\n workItems.countDeferredDecisionsByStatuses({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: ['proposed'],\n }),\n Promise.all(providers.map(provider => provider.counts(resolved))),\n Promise.all(providers.map(provider => provider.latest(resolved))),\n Promise.all(\n active.map(async provider => ({\n kind: provider.kind,\n incoming: before?.get(provider.kind),\n result: await provider.page(resolved, {\n view,\n ...(search ? { search } : {}),\n before: before?.get(provider.kind),\n limit,\n }),\n })),\n ),\n ]);\n\n const openCount = counts.reduce((sum, count) => sum + count.open, 0) + approvalCount;\n const unreadCount = counts.reduce((sum, count) => sum + count.unread, 0);\n // An unread item must never be masked by a newer already-read one of\n // another kind — the streams are independent.\n const unreadLatests = latests.filter(latest => latest?.unread ?? false);\n const latest = unreadLatests.length > 0 ? newestLatest(unreadLatests) : newestLatest(latests);\n const merged = mergeAttentionPages(pages, limit);\n\n return context.json({\n items: merged.items,\n openCount,\n approvalCount,\n badgeCount: unreadCount + approvalCount,\n unreadCount,\n latestOccurrenceKey: latest?.key ?? null,\n latestOccurrenceAt: latest?.at.toISOString() ?? null,\n latestOccurrenceUnread: latest?.unread ?? false,\n hasMore: merged.hasMore,\n ...(merged.nextCursor ? { nextCursor: merged.nextCursor } : {}),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/attention/read-all', {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const cursorRaw = context.req.query('before');\n const before = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !before) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n await comments.ensureReady();\n\n const now = new Date();\n const active = providers.filter(provider => !before || before.has(provider.kind));\n const nextCursors: AttentionCursorMap = new Map();\n let hasMore = false;\n for (const provider of active) {\n const result = await provider.markAllRead(resolved, { before: before?.get(provider.kind), now });\n if (result.hasMore) {\n hasMore = true;\n if (result.continuation) nextCursors.set(provider.kind, result.continuation);\n }\n }\n return context.json({\n ok: true,\n hasMore,\n ...(hasMore && nextCursors.size > 0 ? { nextCursor: encodeAttentionCursor(nextCursors) } : {}),\n });\n },\n }),\n receiptRoute(dependencies, 'read', 'read'),\n receiptRoute(dependencies, 'archive', 'archive'),\n receiptRoute(dependencies, 'restore', 'restore'),\n ];\n}\n"],"mappings":"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"}
|
package/dist/routes/surface.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { SandboxFleet } from '../sandbox/fleet.js';
|
|
|
14
14
|
import type { StateSigner } from '../state-signing.js';
|
|
15
15
|
import type { AuditEmitter } from '../storage/domains/audit/domain.js';
|
|
16
16
|
import type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';
|
|
17
|
+
import type { WorkItemCommentsStorage } from '../storage/domains/comments/base.js';
|
|
17
18
|
import type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';
|
|
18
19
|
import type { CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';
|
|
19
20
|
import type { FilesystemStorage } from '../storage/domains/filesystem/base.js';
|
|
@@ -63,6 +64,7 @@ export interface FactoryApiRoutesDeps {
|
|
|
63
64
|
queueHealth: QueueHealthStorage;
|
|
64
65
|
workItems: WorkItemsStorage;
|
|
65
66
|
channelIdentity: ChannelIdentityStorage;
|
|
67
|
+
comments: WorkItemCommentsStorage;
|
|
66
68
|
};
|
|
67
69
|
integrations?: IntegrationRegistration[];
|
|
68
70
|
intakeReady: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/surface.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getGithubFeatureDiagnostics } from "../integrations/github/config.js";
|
|
|
4
4
|
import { invalidateCustomProvidersSnapshots } from "./custom-provider-source.js";
|
|
5
5
|
import { FactoryDispatchError } from "../rules/dispatch-errors.js";
|
|
6
6
|
import { FactorySourceSessionResolutionError, ensureFactorySourceSession, resolveFactoryDefaultModelId } from "../session/factory-session.js";
|
|
7
|
+
import { FactoryFeedReader } from "../storage/domains/comments/feed-context.js";
|
|
7
8
|
import { FactoryStartCoordinator } from "../rules/start-coordinator.js";
|
|
8
9
|
import { FactoryTransitionService } from "../rules/transition-service.js";
|
|
9
10
|
import { LiveSessions } from "../session/live-sessions.js";
|
|
@@ -254,7 +255,7 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
254
255
|
rules: deps.rules,
|
|
255
256
|
storage: deps.domains.workItems
|
|
256
257
|
}) : void 0;
|
|
257
|
-
const startCoordinator = transitionService ? new FactoryStartCoordinator(deps.controller, deps.domains.workItems, transitionService, githubIntegration?.sourceControlStorage, deps.domains.memorySettings) : void 0;
|
|
258
|
+
const startCoordinator = transitionService ? new FactoryStartCoordinator(deps.controller, deps.domains.workItems, transitionService, githubIntegration?.sourceControlStorage, deps.domains.memorySettings, new FactoryFeedReader(deps.domains.comments)) : void 0;
|
|
258
259
|
if (transitionService && startCoordinator) deps.onFactoryRuntime?.({
|
|
259
260
|
transitionService,
|
|
260
261
|
...githubIntegration ? { prepareBinding: (input) => prepareFactoryRuleBinding(githubIntegration, startCoordinator, deps.domains.projects, input) } : {}
|
|
@@ -319,6 +320,7 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
319
320
|
audit: deps.audit,
|
|
320
321
|
projects: deps.domains.projects,
|
|
321
322
|
workItems: deps.domains.workItems,
|
|
323
|
+
comments: deps.domains.comments,
|
|
322
324
|
queueHealth: deps.domains.queueHealth,
|
|
323
325
|
transitionService,
|
|
324
326
|
startCoordinator,
|