@tangle-network/agent-app 0.43.67 → 0.43.68
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/dist/assistant/index.d.ts +4 -2
- package/dist/assistant/index.js +6 -3
- package/dist/assistant/index.js.map +1 -1
- package/dist/{attachment-validation-B2FFna9E.d.ts → attachment-validation-Dv1A_Puy.d.ts} +1 -1
- package/dist/chat-routes/index.d.ts +4 -3
- package/dist/chat-routes/index.js +6 -4
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +3 -2
- package/dist/chat-store/index.js +4 -1
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-JWBZ74TW.js → chunk-7ESQUSAC.js} +5 -3
- package/dist/{chunk-JWBZ74TW.js.map → chunk-7ESQUSAC.js.map} +1 -1
- package/dist/{chunk-X3N2H6JE.js → chunk-AFNTRJQ7.js} +10 -1
- package/dist/chunk-AFNTRJQ7.js.map +1 -0
- package/dist/chunk-F2CBC4DY.js +193 -0
- package/dist/chunk-F2CBC4DY.js.map +1 -0
- package/dist/chunk-HRH7ASAG.js +759 -0
- package/dist/chunk-HRH7ASAG.js.map +1 -0
- package/dist/{chunk-YTSEDJWA.js → chunk-RXOTWZ4G.js} +22 -6
- package/dist/chunk-RXOTWZ4G.js.map +1 -0
- package/dist/chunk-UDSY2F6N.js +331 -0
- package/dist/chunk-UDSY2F6N.js.map +1 -0
- package/dist/chunk-UOAYS72M.js +80 -0
- package/dist/chunk-UOAYS72M.js.map +1 -0
- package/dist/chunk-UP33Z633.js +141 -0
- package/dist/chunk-UP33Z633.js.map +1 -0
- package/dist/{chunk-FMDMI25K.js → chunk-V55WJSR4.js} +2 -2
- package/dist/{chunk-YKBDH2UY.js → chunk-WL7XHLDK.js} +2 -2
- package/dist/{chunk-7CTIUCQ4.js → chunk-YEFFHORB.js} +2 -73
- package/dist/chunk-YEFFHORB.js.map +1 -0
- package/dist/eval-campaign/index.d.ts +2 -81
- package/dist/index.d.ts +5 -1
- package/dist/index.js +62 -8
- package/dist/{parts-Bg8qcDvB.d.ts → parts-2ymE5cs-.d.ts} +15 -2
- package/dist/queue-C24V13h9.d.ts +68 -0
- package/dist/runtime/index.js +3 -2
- package/dist/sandbox/index.js +3 -2
- package/dist/teams/index.js +5 -5
- package/dist/teams/invitations-api.js +4 -4
- package/dist/teams-react/index.js +3 -3
- package/dist/tools/index.js +8 -6
- package/dist/trust-gate-Dcm5xSva.d.ts +83 -0
- package/dist/types-CEchbvgz.d.ts +268 -0
- package/dist/web-react/index.d.ts +97 -5
- package/dist/web-react/index.js +31 -4
- package/dist/work-product/index.d.ts +331 -0
- package/dist/work-product/index.js +54 -0
- package/dist/work-product/index.js.map +1 -0
- package/dist/work-product-react/index.d.ts +36 -0
- package/dist/work-product-react/index.js +180 -0
- package/dist/work-product-react/index.js.map +1 -0
- package/package.json +15 -1
- package/dist/chunk-7CTIUCQ4.js.map +0 -1
- package/dist/chunk-X3N2H6JE.js.map +0 -1
- package/dist/chunk-YTSEDJWA.js.map +0 -1
- /package/dist/{chunk-FMDMI25K.js.map → chunk-V55WJSR4.js.map} +0 -0
- /package/dist/{chunk-YKBDH2UY.js.map → chunk-WL7XHLDK.js.map} +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseReviewQueueItem
|
|
3
|
+
} from "./chunk-UP33Z633.js";
|
|
4
|
+
import {
|
|
5
|
+
persistedPartToWorkProduct
|
|
6
|
+
} from "./chunk-F2CBC4DY.js";
|
|
7
|
+
|
|
8
|
+
// src/web-react/work-product.tsx
|
|
9
|
+
import { useCallback, useEffect, useState } from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var STATE_LABELS = {
|
|
12
|
+
intake: "Intake",
|
|
13
|
+
missing_info: "Missing info",
|
|
14
|
+
working: "Working",
|
|
15
|
+
ready_for_review: "Ready for review",
|
|
16
|
+
changes_requested: "Changes requested",
|
|
17
|
+
approved: "Approved",
|
|
18
|
+
blocked: "Blocked"
|
|
19
|
+
};
|
|
20
|
+
var STATE_TONES = {
|
|
21
|
+
intake: "bg-muted/40 text-muted-foreground",
|
|
22
|
+
missing_info: "bg-warning/10 text-warning",
|
|
23
|
+
working: "bg-primary/10 text-primary",
|
|
24
|
+
ready_for_review: "bg-success/10 text-success",
|
|
25
|
+
changes_requested: "bg-warning/10 text-warning",
|
|
26
|
+
approved: "bg-success/10 text-success",
|
|
27
|
+
blocked: "bg-destructive/10 text-destructive"
|
|
28
|
+
};
|
|
29
|
+
var STATUS_LABELS = {
|
|
30
|
+
draft: "Draft",
|
|
31
|
+
blocked: "Blocked",
|
|
32
|
+
ready: "Ready for review",
|
|
33
|
+
changes_requested: "Changes requested",
|
|
34
|
+
approved: "Approved",
|
|
35
|
+
superseded: "Superseded"
|
|
36
|
+
};
|
|
37
|
+
var STATUS_TONES = {
|
|
38
|
+
draft: "bg-primary/10 text-primary",
|
|
39
|
+
blocked: "bg-destructive/10 text-destructive",
|
|
40
|
+
ready: "bg-success/10 text-success",
|
|
41
|
+
changes_requested: "bg-warning/10 text-warning",
|
|
42
|
+
approved: "bg-success/10 text-success",
|
|
43
|
+
superseded: "bg-muted/40 text-muted-foreground"
|
|
44
|
+
};
|
|
45
|
+
function reviewQueueStateLabel(state) {
|
|
46
|
+
return STATE_LABELS[state];
|
|
47
|
+
}
|
|
48
|
+
function workProductStatusLabel(status) {
|
|
49
|
+
return STATUS_LABELS[status];
|
|
50
|
+
}
|
|
51
|
+
function StatePill({ state }) {
|
|
52
|
+
return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATE_TONES[state]}`, children: STATE_LABELS[state] });
|
|
53
|
+
}
|
|
54
|
+
function StatusPill({ status }) {
|
|
55
|
+
return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATUS_TONES[status]}`, children: STATUS_LABELS[status] });
|
|
56
|
+
}
|
|
57
|
+
function workProductPartsFromMessageParts(parts) {
|
|
58
|
+
if (!parts) return [];
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const part of parts) {
|
|
61
|
+
const typed = persistedPartToWorkProduct(part);
|
|
62
|
+
if (typed) out.push(typed);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function WorkProductCard({ part, onOpen, className }) {
|
|
67
|
+
return /* @__PURE__ */ jsxs(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
className: `flex items-center gap-3 rounded-xl border border-border bg-card px-4 py-3 ${className ?? ""}`,
|
|
71
|
+
"data-work-product-id": part.ref.id,
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
74
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
75
|
+
/* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" }),
|
|
76
|
+
/* @__PURE__ */ jsx("path", { d: "m9 15 2 2 4-4" })
|
|
77
|
+
] }) }),
|
|
78
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
79
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-foreground", children: part.title }),
|
|
80
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
81
|
+
part.kind && /* @__PURE__ */ jsx("span", { className: "font-mono", children: part.kind }),
|
|
82
|
+
part.kind && " \xB7 ",
|
|
83
|
+
"v",
|
|
84
|
+
part.ref.version
|
|
85
|
+
] })
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsx(StatusPill, { status: part.status }),
|
|
88
|
+
onOpen && /* @__PURE__ */ jsx(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
onClick: () => onOpen(part),
|
|
93
|
+
className: "rounded-md border border-border px-2.5 py-1 text-xs font-medium text-foreground transition hover:bg-accent/30",
|
|
94
|
+
children: "Review"
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
function mergeReviewQueuePages(existing, incoming) {
|
|
102
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const item of existing) byScope.set(item.scopeKey, item);
|
|
104
|
+
for (const item of incoming) byScope.set(item.scopeKey, item);
|
|
105
|
+
return [...byScope.values()].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
106
|
+
}
|
|
107
|
+
function ReviewQueuePanel({
|
|
108
|
+
fetchQueue,
|
|
109
|
+
onSelect,
|
|
110
|
+
title = "Review queue",
|
|
111
|
+
emptyLabel = "Nothing awaiting review.",
|
|
112
|
+
className
|
|
113
|
+
}) {
|
|
114
|
+
const [items, setItems] = useState([]);
|
|
115
|
+
const [cursor, setCursor] = useState(void 0);
|
|
116
|
+
const [loading, setLoading] = useState(false);
|
|
117
|
+
const [error, setError] = useState(null);
|
|
118
|
+
const load = useCallback(
|
|
119
|
+
async (from) => {
|
|
120
|
+
setLoading(true);
|
|
121
|
+
setError(null);
|
|
122
|
+
try {
|
|
123
|
+
const page = await fetchQueue(from);
|
|
124
|
+
const validated = page.items.map((item) => parseReviewQueueItem(item)).filter((item) => item !== null);
|
|
125
|
+
setItems((prev) => mergeReviewQueuePages(from === void 0 ? [] : prev, validated));
|
|
126
|
+
setCursor(page.nextCursor);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
129
|
+
} finally {
|
|
130
|
+
setLoading(false);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
[fetchQueue]
|
|
134
|
+
);
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
void load();
|
|
137
|
+
}, [load]);
|
|
138
|
+
return /* @__PURE__ */ jsxs("div", { className: `space-y-2 ${className ?? ""}`, children: [
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
140
|
+
/* @__PURE__ */ jsx("h2", { className: "flex-1 text-sm font-semibold", children: title }),
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
"button",
|
|
143
|
+
{
|
|
144
|
+
type: "button",
|
|
145
|
+
onClick: () => void load(),
|
|
146
|
+
disabled: loading,
|
|
147
|
+
"aria-label": "Refresh",
|
|
148
|
+
className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50",
|
|
149
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: `h-3.5 w-3.5 ${loading ? "animate-spin" : ""}`, fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
150
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-2.64-6.36" }),
|
|
151
|
+
/* @__PURE__ */ jsx("polyline", { points: "21 3 21 9 15 9" })
|
|
152
|
+
] })
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
] }),
|
|
156
|
+
error && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
157
|
+
!error && items.length === 0 && !loading && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-4 text-center text-xs text-muted-foreground", children: emptyLabel }),
|
|
158
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: items.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
type: "button",
|
|
162
|
+
onClick: () => onSelect?.(item),
|
|
163
|
+
className: "flex w-full items-center gap-3 rounded-lg border border-border bg-card px-3 py-2.5 text-left transition hover:border-primary/40 hover:bg-accent/20",
|
|
164
|
+
children: [
|
|
165
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
166
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-sm font-medium text-foreground", children: [
|
|
167
|
+
item.workProduct?.title ?? item.scopeKey,
|
|
168
|
+
item.workProduct && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-[11px] text-muted-foreground", children: [
|
|
169
|
+
"v",
|
|
170
|
+
item.workProduct.version
|
|
171
|
+
] })
|
|
172
|
+
] }),
|
|
173
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 text-[11px] text-muted-foreground", children: [
|
|
174
|
+
item.workProduct?.kind && /* @__PURE__ */ jsx("span", { className: "font-mono", children: item.workProduct.kind }),
|
|
175
|
+
item.pendingAsk && /* @__PURE__ */ jsxs("span", { children: [
|
|
176
|
+
"asks: ",
|
|
177
|
+
item.pendingAsk.title
|
|
178
|
+
] }),
|
|
179
|
+
item.blockingExceptions > 0 && /* @__PURE__ */ jsxs("span", { className: "text-destructive", children: [
|
|
180
|
+
item.blockingExceptions,
|
|
181
|
+
" blocking"
|
|
182
|
+
] }),
|
|
183
|
+
item.failedChecks > 0 && /* @__PURE__ */ jsxs("span", { className: "text-warning", children: [
|
|
184
|
+
item.failedChecks,
|
|
185
|
+
" failed checks"
|
|
186
|
+
] }),
|
|
187
|
+
item.provenance && item.provenance.profileHash && /* @__PURE__ */ jsx("span", { className: "font-mono", children: item.provenance.profileHash.slice(0, 8) })
|
|
188
|
+
] })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsx(StatePill, { state: item.state })
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
) }, item.scopeKey)) }),
|
|
194
|
+
cursor && /* @__PURE__ */ jsx(
|
|
195
|
+
"button",
|
|
196
|
+
{
|
|
197
|
+
type: "button",
|
|
198
|
+
onClick: () => void load(cursor),
|
|
199
|
+
disabled: loading,
|
|
200
|
+
className: "w-full rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent/30 disabled:opacity-50",
|
|
201
|
+
children: "Load more"
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
] });
|
|
205
|
+
}
|
|
206
|
+
function locatorLabel(entry) {
|
|
207
|
+
const parts = [];
|
|
208
|
+
if (entry.locator.page !== void 0) parts.push(`p.${entry.locator.page}`);
|
|
209
|
+
if (entry.locator.range) parts.push(entry.locator.range);
|
|
210
|
+
return parts.length ? parts.join(" \xB7 ") : null;
|
|
211
|
+
}
|
|
212
|
+
function EvidenceLineageTable({ evidence, resolveSourceUrl, className }) {
|
|
213
|
+
if (evidence.length === 0) {
|
|
214
|
+
return /* @__PURE__ */ jsx("p", { className: `text-xs text-muted-foreground ${className ?? ""}`, children: "No evidence recorded." });
|
|
215
|
+
}
|
|
216
|
+
return /* @__PURE__ */ jsx("div", { className: `overflow-x-auto ${className ?? ""}`, children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-sm", children: [
|
|
217
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground", children: [
|
|
218
|
+
/* @__PURE__ */ jsx("th", { className: "py-1.5 pr-3 font-medium", children: "Target" }),
|
|
219
|
+
/* @__PURE__ */ jsx("th", { className: "py-1.5 pr-3 font-medium", children: "Claim" }),
|
|
220
|
+
/* @__PURE__ */ jsx("th", { className: "py-1.5 font-medium", children: "Source" })
|
|
221
|
+
] }) }),
|
|
222
|
+
/* @__PURE__ */ jsx("tbody", { children: evidence.map((entry) => {
|
|
223
|
+
const url = resolveSourceUrl?.(entry);
|
|
224
|
+
const locator = locatorLabel(entry);
|
|
225
|
+
return /* @__PURE__ */ jsxs("tr", { className: "border-b border-border/50 align-top", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("td", { className: "py-2 pr-3 font-mono text-xs text-foreground", children: entry.target }),
|
|
227
|
+
/* @__PURE__ */ jsxs("td", { className: "py-2 pr-3 text-[13px] leading-snug text-foreground", children: [
|
|
228
|
+
entry.claim,
|
|
229
|
+
entry.locator.quote && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block border-l-2 border-border pl-2 text-xs italic text-muted-foreground", children: [
|
|
230
|
+
"\u201C",
|
|
231
|
+
entry.locator.quote,
|
|
232
|
+
"\u201D"
|
|
233
|
+
] })
|
|
234
|
+
] }),
|
|
235
|
+
/* @__PURE__ */ jsxs("td", { className: "py-2 text-xs", children: [
|
|
236
|
+
url ? /* @__PURE__ */ jsx(
|
|
237
|
+
"a",
|
|
238
|
+
{
|
|
239
|
+
href: url,
|
|
240
|
+
target: "_blank",
|
|
241
|
+
rel: "noreferrer",
|
|
242
|
+
className: "font-mono text-primary underline-offset-2 hover:underline",
|
|
243
|
+
children: entry.sourceRef
|
|
244
|
+
}
|
|
245
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground", children: entry.sourceRef }),
|
|
246
|
+
locator && /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-muted-foreground", children: locator })
|
|
247
|
+
] })
|
|
248
|
+
] }, entry.id);
|
|
249
|
+
}) })
|
|
250
|
+
] }) });
|
|
251
|
+
}
|
|
252
|
+
var SEVERITY_TONES = {
|
|
253
|
+
blocking: "bg-destructive/10 text-destructive",
|
|
254
|
+
material: "bg-warning/10 text-warning",
|
|
255
|
+
advisory: "bg-muted/40 text-muted-foreground"
|
|
256
|
+
};
|
|
257
|
+
function ExceptionList({ exceptions, className }) {
|
|
258
|
+
if (exceptions.length === 0) {
|
|
259
|
+
return /* @__PURE__ */ jsx("p", { className: `text-xs text-muted-foreground ${className ?? ""}`, children: "No exceptions flagged." });
|
|
260
|
+
}
|
|
261
|
+
return /* @__PURE__ */ jsx("ul", { className: `space-y-1.5 ${className ?? ""}`, children: exceptions.map((entry) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2.5 rounded-lg border border-border bg-card px-3 py-2", children: [
|
|
262
|
+
/* @__PURE__ */ jsx("span", { className: `mt-0.5 inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${SEVERITY_TONES[entry.severity]}`, children: entry.severity }),
|
|
263
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
264
|
+
/* @__PURE__ */ jsx("p", { className: `text-[13px] leading-snug ${entry.resolved ? "text-muted-foreground line-through" : "text-foreground"}`, children: entry.message }),
|
|
265
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 flex flex-wrap gap-x-2 text-[11px] text-muted-foreground", children: [
|
|
266
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: entry.kind }),
|
|
267
|
+
entry.targets && entry.targets.length > 0 && /* @__PURE__ */ jsx("span", { className: "font-mono", children: entry.targets.join(", ") }),
|
|
268
|
+
entry.resolved && /* @__PURE__ */ jsxs("span", { children: [
|
|
269
|
+
"resolved",
|
|
270
|
+
entry.resolvedBy ? ` by ${entry.resolvedBy}` : ""
|
|
271
|
+
] }),
|
|
272
|
+
entry.resolutionNote && /* @__PURE__ */ jsx("span", { children: entry.resolutionNote })
|
|
273
|
+
] })
|
|
274
|
+
] })
|
|
275
|
+
] }, entry.id)) });
|
|
276
|
+
}
|
|
277
|
+
function QualityCheckList({ checks, className }) {
|
|
278
|
+
if (checks.length === 0) {
|
|
279
|
+
return /* @__PURE__ */ jsx("p", { className: `text-xs text-muted-foreground ${className ?? ""}`, children: "No checks recorded." });
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ jsx("ul", { className: `space-y-1 ${className ?? ""}`, children: checks.map((check) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 rounded-md px-1 py-1", children: [
|
|
282
|
+
check.passed ? /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-success", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-destructive", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
283
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
284
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
285
|
+
] }),
|
|
286
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
287
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[13px] text-foreground", children: [
|
|
288
|
+
check.name,
|
|
289
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1.5 text-[10px] uppercase tracking-wide text-muted-foreground", children: check.source })
|
|
290
|
+
] }),
|
|
291
|
+
check.detail && /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: check.detail })
|
|
292
|
+
] })
|
|
293
|
+
] }, check.id)) });
|
|
294
|
+
}
|
|
295
|
+
function ProvenanceStamp({ provenance, backtest, className }) {
|
|
296
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex flex-wrap items-center gap-x-2.5 gap-y-1 text-[11px] text-muted-foreground ${className ?? ""}`, children: [
|
|
297
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono", title: provenance.profileHash, children: [
|
|
298
|
+
"profile ",
|
|
299
|
+
provenance.profileHash ? provenance.profileHash.slice(0, 10) : "\u2014"
|
|
300
|
+
] }),
|
|
301
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono", title: provenance.runId, children: [
|
|
302
|
+
"run ",
|
|
303
|
+
provenance.runId ? provenance.runId.slice(0, 10) : "\u2014"
|
|
304
|
+
] }),
|
|
305
|
+
provenance.servingModels.length > 0 ? provenance.servingModels.map((model) => /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted/40 px-2 py-0.5 font-mono", children: model }, model)) : /* @__PURE__ */ jsx("span", { className: "italic", children: "serving model pending" }),
|
|
306
|
+
typeof provenance.costUsd === "number" && /* @__PURE__ */ jsxs("span", { children: [
|
|
307
|
+
"$",
|
|
308
|
+
provenance.costUsd.toFixed(provenance.costUsd < 0.01 ? 4 : 2)
|
|
309
|
+
] }),
|
|
310
|
+
backtest && (backtest.trust === "pass" ? /* @__PURE__ */ jsxs("span", { className: "text-success", children: [
|
|
311
|
+
backtest.cases,
|
|
312
|
+
" backtest cases \xB7 composite ",
|
|
313
|
+
backtest.composite.toFixed(2),
|
|
314
|
+
" \xB7 trust PASS"
|
|
315
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-warning", title: backtest.trustReasons.join("; "), children: "quality: unverified" }))
|
|
316
|
+
] });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export {
|
|
320
|
+
reviewQueueStateLabel,
|
|
321
|
+
workProductStatusLabel,
|
|
322
|
+
workProductPartsFromMessageParts,
|
|
323
|
+
WorkProductCard,
|
|
324
|
+
mergeReviewQueuePages,
|
|
325
|
+
ReviewQueuePanel,
|
|
326
|
+
EvidenceLineageTable,
|
|
327
|
+
ExceptionList,
|
|
328
|
+
QualityCheckList,
|
|
329
|
+
ProvenanceStamp
|
|
330
|
+
};
|
|
331
|
+
//# sourceMappingURL=chunk-UDSY2F6N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web-react/work-product.tsx"],"sourcesContent":["/**\n * Work-product review surfaces — the sandbox-ui-FREE half of the vertical:\n *\n * - {@link ReviewQueuePanel}: the practice/matters/campaign review queue over\n * a `fetchQueue(cursor)` data port (the `AgentActivityPanel` shape).\n * - {@link WorkProductCard}: the persisted `type:'work_product'` transcript\n * anchor rendered inside `ChatMessages` — chat stays the driver surface.\n * - {@link EvidenceLineageTable}: target → claim → source rows with\n * click-through via an injected `resolveSourceUrl` (the\n * `MessageAttachments.resolveFileUrl` pattern).\n * - {@link ExceptionList} / {@link QualityCheckList}: severity-badged\n * exception rows and pass/fail check rows.\n * - {@link ProvenanceStamp}: profileHash prefix + runId + serving-model\n * chips + the optional backtest slot (\"182 cases · composite 0.81 ·\n * trust PASS\" — or \"quality: unverified\" when the trust gate failed).\n *\n * Anything hosting sandbox-ui (DiffView, CodeView, PillTabs) lives in\n * `./work-product-react` instead — this module must stay importable without\n * the optional sandbox-ui peer.\n */\n\nimport { useCallback, useEffect, useState } from 'react'\n\nimport {\n parseReviewQueueItem,\n type ReviewQueueItem,\n type ReviewQueueState,\n} from '../work-product/queue'\nimport type {\n EvidenceEntry,\n ExceptionEntry,\n ProfileBacktestSummary,\n QualityCheck,\n WorkProductPersistedPart,\n WorkProductProvenance,\n WorkProductStatus,\n} from '../work-product/types'\nimport { persistedPartToWorkProduct } from '../work-product/types'\n\nexport { parseReviewQueueItem }\nexport type { ReviewQueueItem, ReviewQueueState }\n\n// ── shared presentation vocabulary ────────────────────────────────────────\n\nconst STATE_LABELS: Record<ReviewQueueState, string> = {\n intake: 'Intake',\n missing_info: 'Missing info',\n working: 'Working',\n ready_for_review: 'Ready for review',\n changes_requested: 'Changes requested',\n approved: 'Approved',\n blocked: 'Blocked',\n}\n\nconst STATE_TONES: Record<ReviewQueueState, string> = {\n intake: 'bg-muted/40 text-muted-foreground',\n missing_info: 'bg-warning/10 text-warning',\n working: 'bg-primary/10 text-primary',\n ready_for_review: 'bg-success/10 text-success',\n changes_requested: 'bg-warning/10 text-warning',\n approved: 'bg-success/10 text-success',\n blocked: 'bg-destructive/10 text-destructive',\n}\n\nconst STATUS_LABELS: Record<WorkProductStatus, string> = {\n draft: 'Draft',\n blocked: 'Blocked',\n ready: 'Ready for review',\n changes_requested: 'Changes requested',\n approved: 'Approved',\n superseded: 'Superseded',\n}\n\nconst STATUS_TONES: Record<WorkProductStatus, string> = {\n draft: 'bg-primary/10 text-primary',\n blocked: 'bg-destructive/10 text-destructive',\n ready: 'bg-success/10 text-success',\n changes_requested: 'bg-warning/10 text-warning',\n approved: 'bg-success/10 text-success',\n superseded: 'bg-muted/40 text-muted-foreground',\n}\n\n/** Human label for a queue state. */\nexport function reviewQueueStateLabel(state: ReviewQueueState): string {\n return STATE_LABELS[state]\n}\n\n/** Human label for a work-product status. */\nexport function workProductStatusLabel(status: WorkProductStatus): string {\n return STATUS_LABELS[status]\n}\n\nfunction StatePill({ state }: { state: ReviewQueueState }) {\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATE_TONES[state]}`}>\n {STATE_LABELS[state]}\n </span>\n )\n}\n\nfunction StatusPill({ status }: { status: WorkProductStatus }) {\n return (\n <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${STATUS_TONES[status]}`}>\n {STATUS_LABELS[status]}\n </span>\n )\n}\n\n// ── transcript anchor helpers + card ──────────────────────────────────────\n\n/** Every persisted work-product anchor on one message, re-validated. */\nexport function workProductPartsFromMessageParts(\n parts: ReadonlyArray<Record<string, unknown>> | null | undefined,\n): WorkProductPersistedPart[] {\n if (!parts) return []\n const out: WorkProductPersistedPart[] = []\n for (const part of parts) {\n const typed = persistedPartToWorkProduct(part)\n if (typed) out.push(typed)\n }\n return out\n}\n\n/** Properties for the transcript anchor card rendered in chat */\nexport interface WorkProductCardProps {\n part: WorkProductPersistedPart\n /** Open the work product (queue detail / pane) — the card's only action. */\n onOpen?: (part: WorkProductPersistedPart) => void\n className?: string\n}\n\n/** The chat transcript anchor card for one work-product version — a compact,\n * system-authored pointer, not a parallel review surface. */\nexport function WorkProductCard({ part, onOpen, className }: WorkProductCardProps) {\n return (\n <div\n className={`flex items-center gap-3 rounded-xl border border-border bg-card px-4 py-3 ${className ?? ''}`}\n data-work-product-id={part.ref.id}\n >\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n <path d=\"m9 15 2 2 4-4\" />\n </svg>\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{part.title}</p>\n <p className=\"text-[11px] text-muted-foreground\">\n {part.kind && <span className=\"font-mono\">{part.kind}</span>}\n {part.kind && ' · '}v{part.ref.version}\n </p>\n </div>\n <StatusPill status={part.status} />\n {onOpen && (\n <button\n type=\"button\"\n onClick={() => onOpen(part)}\n className=\"rounded-md border border-border px-2.5 py-1 text-xs font-medium text-foreground transition hover:bg-accent/30\"\n >\n Review\n </button>\n )}\n </div>\n )\n}\n\n// ── review queue panel ────────────────────────────────────────────────────\n\n/** One fetched page of queue items with an optional continuation cursor */\nexport interface ReviewQueuePage {\n items: ReviewQueueItem[]\n /** Opaque continuation token; absent ⇒ no further pages. */\n nextCursor?: string\n}\n\n/** Properties for the review queue panel over a fetch data port */\nexport interface ReviewQueuePanelProps {\n /** Data port — fetch one page of queue items (the projection's output,\n * re-validated here at the JSON boundary). */\n fetchQueue: (cursor?: string) => Promise<ReviewQueuePage>\n /** Open one queue item (navigate to its thread / detail pane). */\n onSelect?: (item: ReviewQueueItem) => void\n title?: string\n emptyLabel?: string\n className?: string\n}\n\n/** Merge a fetched page into held rows: dedupe by scopeKey, incoming wins,\n * newest updatedAt first. Exported for tests. */\nexport function mergeReviewQueuePages(existing: ReviewQueueItem[], incoming: ReviewQueueItem[]): ReviewQueueItem[] {\n const byScope = new Map<string, ReviewQueueItem>()\n for (const item of existing) byScope.set(item.scopeKey, item)\n for (const item of incoming) byScope.set(item.scopeKey, item)\n return [...byScope.values()].sort((a, b) => b.updatedAt - a.updatedAt)\n}\n\n/** The workspace review queue — cursor-paged, refreshable, selection via\n * callback. States, counts, and provenance render from the projection; the\n * panel holds no domain logic. */\nexport function ReviewQueuePanel({\n fetchQueue,\n onSelect,\n title = 'Review queue',\n emptyLabel = 'Nothing awaiting review.',\n className,\n}: ReviewQueuePanelProps) {\n const [items, setItems] = useState<ReviewQueueItem[]>([])\n const [cursor, setCursor] = useState<string | undefined>(undefined)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const load = useCallback(\n async (from?: string) => {\n setLoading(true)\n setError(null)\n try {\n const page = await fetchQueue(from)\n const validated = page.items\n .map((item) => parseReviewQueueItem(item))\n .filter((item): item is ReviewQueueItem => item !== null)\n setItems((prev) => mergeReviewQueuePages(from === undefined ? [] : prev, validated))\n setCursor(page.nextCursor)\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e))\n } finally {\n setLoading(false)\n }\n },\n [fetchQueue],\n )\n\n useEffect(() => {\n void load()\n }, [load])\n\n return (\n <div className={`space-y-2 ${className ?? ''}`}>\n <div className=\"flex items-center gap-2\">\n <h2 className=\"flex-1 text-sm font-semibold\">{title}</h2>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\n aria-label=\"Refresh\"\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50\"\n >\n <svg viewBox=\"0 0 24 24\" className={`h-3.5 w-3.5 ${loading ? 'animate-spin' : ''}`} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" />\n <polyline points=\"21 3 21 9 15 9\" />\n </svg>\n </button>\n </div>\n {error && <p className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">{error}</p>}\n {!error && items.length === 0 && !loading && (\n <p className=\"rounded-md border border-dashed border-border px-3 py-4 text-center text-xs text-muted-foreground\">{emptyLabel}</p>\n )}\n <ul className=\"space-y-1.5\">\n {items.map((item) => (\n <li key={item.scopeKey}>\n <button\n type=\"button\"\n onClick={() => onSelect?.(item)}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border bg-card px-3 py-2.5 text-left transition hover:border-primary/40 hover:bg-accent/20\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-foreground\">\n {item.workProduct?.title ?? item.scopeKey}\n {item.workProduct && <span className=\"ml-1.5 text-[11px] text-muted-foreground\">v{item.workProduct.version}</span>}\n </p>\n <p className=\"mt-0.5 flex flex-wrap items-center gap-x-2 text-[11px] text-muted-foreground\">\n {item.workProduct?.kind && <span className=\"font-mono\">{item.workProduct.kind}</span>}\n {item.pendingAsk && <span>asks: {item.pendingAsk.title}</span>}\n {item.blockingExceptions > 0 && (\n <span className=\"text-destructive\">{item.blockingExceptions} blocking</span>\n )}\n {item.failedChecks > 0 && <span className=\"text-warning\">{item.failedChecks} failed checks</span>}\n {item.provenance && item.provenance.profileHash && (\n <span className=\"font-mono\">{item.provenance.profileHash.slice(0, 8)}</span>\n )}\n </p>\n </div>\n <StatePill state={item.state} />\n </button>\n </li>\n ))}\n </ul>\n {cursor && (\n <button\n type=\"button\"\n onClick={() => void load(cursor)}\n disabled={loading}\n className=\"w-full rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent/30 disabled:opacity-50\"\n >\n Load more\n </button>\n )}\n </div>\n )\n}\n\n// ── evidence lineage table ────────────────────────────────────────────────\n\n/** Properties for the evidence lineage table with source click-through */\nexport interface EvidenceLineageTableProps {\n evidence: readonly EvidenceEntry[]\n /** Resolve one entry's source document to an openable URL (signed\n * object-store/vault download) — the `resolveFileUrl` pattern. Absent →\n * sources render as plain refs without links. */\n resolveSourceUrl?: (entry: EvidenceEntry) => string\n className?: string\n}\n\nfunction locatorLabel(entry: EvidenceEntry): string | null {\n const parts: string[] = []\n if (entry.locator.page !== undefined) parts.push(`p.${entry.locator.page}`)\n if (entry.locator.range) parts.push(entry.locator.range)\n return parts.length ? parts.join(' · ') : null\n}\n\n/** Target → claim → source lineage rows: the \"every material value traceable\n * to source evidence\" surface. Each row names the artifact target, the\n * claim, and the source document location supporting it. */\nexport function EvidenceLineageTable({ evidence, resolveSourceUrl, className }: EvidenceLineageTableProps) {\n if (evidence.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No evidence recorded.</p>\n }\n return (\n <div className={`overflow-x-auto ${className ?? ''}`}>\n <table className=\"w-full border-collapse text-left text-sm\">\n <thead>\n <tr className=\"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground\">\n <th className=\"py-1.5 pr-3 font-medium\">Target</th>\n <th className=\"py-1.5 pr-3 font-medium\">Claim</th>\n <th className=\"py-1.5 font-medium\">Source</th>\n </tr>\n </thead>\n <tbody>\n {evidence.map((entry) => {\n const url = resolveSourceUrl?.(entry)\n const locator = locatorLabel(entry)\n return (\n <tr key={entry.id} className=\"border-b border-border/50 align-top\">\n <td className=\"py-2 pr-3 font-mono text-xs text-foreground\">{entry.target}</td>\n <td className=\"py-2 pr-3 text-[13px] leading-snug text-foreground\">\n {entry.claim}\n {entry.locator.quote && (\n <span className=\"mt-0.5 block border-l-2 border-border pl-2 text-xs italic text-muted-foreground\">\n “{entry.locator.quote}”\n </span>\n )}\n </td>\n <td className=\"py-2 text-xs\">\n {url ? (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"font-mono text-primary underline-offset-2 hover:underline\"\n >\n {entry.sourceRef}\n </a>\n ) : (\n <span className=\"font-mono text-muted-foreground\">{entry.sourceRef}</span>\n )}\n {locator && <span className=\"ml-1.5 text-muted-foreground\">{locator}</span>}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n}\n\n// ── exceptions + checks ───────────────────────────────────────────────────\n\nconst SEVERITY_TONES: Record<ExceptionEntry['severity'], string> = {\n blocking: 'bg-destructive/10 text-destructive',\n material: 'bg-warning/10 text-warning',\n advisory: 'bg-muted/40 text-muted-foreground',\n}\n\n/** Properties for the severity-badged exception list */\nexport interface ExceptionListProps {\n exceptions: readonly ExceptionEntry[]\n className?: string\n}\n\n/** Severity-badged exception rows with resolution state. */\nexport function ExceptionList({ exceptions, className }: ExceptionListProps) {\n if (exceptions.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No exceptions flagged.</p>\n }\n return (\n <ul className={`space-y-1.5 ${className ?? ''}`}>\n {exceptions.map((entry) => (\n <li key={entry.id} className=\"flex items-start gap-2.5 rounded-lg border border-border bg-card px-3 py-2\">\n <span className={`mt-0.5 inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${SEVERITY_TONES[entry.severity]}`}>\n {entry.severity}\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className={`text-[13px] leading-snug ${entry.resolved ? 'text-muted-foreground line-through' : 'text-foreground'}`}>\n {entry.message}\n </p>\n <p className=\"mt-0.5 flex flex-wrap gap-x-2 text-[11px] text-muted-foreground\">\n <span className=\"font-mono\">{entry.kind}</span>\n {entry.targets && entry.targets.length > 0 && <span className=\"font-mono\">{entry.targets.join(', ')}</span>}\n {entry.resolved && <span>resolved{entry.resolvedBy ? ` by ${entry.resolvedBy}` : ''}</span>}\n {entry.resolutionNote && <span>{entry.resolutionNote}</span>}\n </p>\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n/** Properties for the pass/fail quality check list */\nexport interface QualityCheckListProps {\n checks: readonly QualityCheck[]\n className?: string\n}\n\n/** Pass/fail quality-check rows tagged with their source (agent self-report,\n * platform gate, judge ensemble). */\nexport function QualityCheckList({ checks, className }: QualityCheckListProps) {\n if (checks.length === 0) {\n return <p className={`text-xs text-muted-foreground ${className ?? ''}`}>No checks recorded.</p>\n }\n return (\n <ul className={`space-y-1 ${className ?? ''}`}>\n {checks.map((check) => (\n <li key={check.id} className=\"flex items-start gap-2 rounded-md px-1 py-1\">\n {check.passed ? (\n <svg viewBox=\"0 0 24 24\" className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-success\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n ) : (\n <svg viewBox=\"0 0 24 24\" className=\"mt-0.5 h-3.5 w-3.5 shrink-0 text-destructive\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-[13px] text-foreground\">\n {check.name}\n <span className=\"ml-1.5 text-[10px] uppercase tracking-wide text-muted-foreground\">{check.source}</span>\n </p>\n {check.detail && <p className=\"text-[11px] leading-snug text-muted-foreground\">{check.detail}</p>}\n </div>\n </li>\n ))}\n </ul>\n )\n}\n\n// ── provenance stamp ──────────────────────────────────────────────────────\n\n/** Properties for the provenance stamp with the optional backtest slot */\nexport interface ProvenanceStampProps {\n provenance: Pick<WorkProductProvenance, 'profileHash' | 'runId' | 'servingModels'> &\n Partial<Pick<WorkProductProvenance, 'costUsd' | 'producedAt'>>\n /** The product-resolved backtest summary for this profile hash. When its\n * trust gate failed, the composite renders as \"quality: unverified\" —\n * never a naked number. */\n backtest?: ProfileBacktestSummary\n className?: string\n}\n\n/** The audit line a reviewer approves against: which configuration produced\n * this document, what served it, and how that configuration measured on its\n * backtest. */\nexport function ProvenanceStamp({ provenance, backtest, className }: ProvenanceStampProps) {\n return (\n <div className={`flex flex-wrap items-center gap-x-2.5 gap-y-1 text-[11px] text-muted-foreground ${className ?? ''}`}>\n <span className=\"font-mono\" title={provenance.profileHash}>\n profile {provenance.profileHash ? provenance.profileHash.slice(0, 10) : '—'}\n </span>\n <span className=\"font-mono\" title={provenance.runId}>\n run {provenance.runId ? provenance.runId.slice(0, 10) : '—'}\n </span>\n {provenance.servingModels.length > 0 ? (\n provenance.servingModels.map((model) => (\n <span key={model} className=\"rounded-full bg-muted/40 px-2 py-0.5 font-mono\">\n {model}\n </span>\n ))\n ) : (\n <span className=\"italic\">serving model pending</span>\n )}\n {typeof provenance.costUsd === 'number' && <span>${provenance.costUsd.toFixed(provenance.costUsd < 0.01 ? 4 : 2)}</span>}\n {backtest &&\n (backtest.trust === 'pass' ? (\n <span className=\"text-success\">\n {backtest.cases} backtest cases · composite {backtest.composite.toFixed(2)} · trust PASS\n </span>\n ) : (\n <span className=\"text-warning\" title={backtest.trustReasons.join('; ')}>\n quality: unverified\n </span>\n ))}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToolInputError,
|
|
3
|
+
findCustomTool,
|
|
4
|
+
isAppToolName
|
|
5
|
+
} from "./chunk-YEFFHORB.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/dispatch.ts
|
|
8
|
+
async function dispatchAppTool(toolName, rawArgs, ctx, opts) {
|
|
9
|
+
try {
|
|
10
|
+
if (!isAppToolName(toolName)) {
|
|
11
|
+
const custom = findCustomTool(toolName, opts.customTools);
|
|
12
|
+
if (!custom) return { ok: false, code: "unknown_tool", message: `${toolName} is not an app tool.` };
|
|
13
|
+
const result = await custom.execute(rawArgs, ctx);
|
|
14
|
+
return { ok: true, result };
|
|
15
|
+
}
|
|
16
|
+
if (toolName === "submit_proposal") {
|
|
17
|
+
const type = String(rawArgs.type ?? "").trim();
|
|
18
|
+
const title = String(rawArgs.title ?? "").trim();
|
|
19
|
+
if (!type || !opts.taxonomy.proposalTypes.includes(type)) {
|
|
20
|
+
return { ok: false, code: "invalid_type", message: `type must be one of: ${opts.taxonomy.proposalTypes.join(", ")}.` };
|
|
21
|
+
}
|
|
22
|
+
if (!title) return { ok: false, code: "missing_title", message: "title is required." };
|
|
23
|
+
const description = rawArgs.description == null ? null : String(rawArgs.description);
|
|
24
|
+
let regulated = opts.taxonomy.regulatedTypes.includes(type);
|
|
25
|
+
if (opts.needsApproval) {
|
|
26
|
+
try {
|
|
27
|
+
regulated = await opts.needsApproval(type, { title, description }, ctx);
|
|
28
|
+
} catch {
|
|
29
|
+
regulated = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const r2 = await opts.handlers.submitProposal({ type, title, description, regulated }, ctx);
|
|
33
|
+
const { proposalId, deduped, status, ...extra } = r2;
|
|
34
|
+
const effectiveStatus = status ?? "queued_for_approval";
|
|
35
|
+
opts.onProduced?.({
|
|
36
|
+
type: "proposal_created",
|
|
37
|
+
proposalId,
|
|
38
|
+
title,
|
|
39
|
+
status: effectiveStatus === "executed" ? "executed" : "pending",
|
|
40
|
+
content: description ?? void 0
|
|
41
|
+
});
|
|
42
|
+
return { ok: true, result: { ...extra, status: effectiveStatus, proposalId, deduped, regulated } };
|
|
43
|
+
}
|
|
44
|
+
if (toolName === "schedule_followup") {
|
|
45
|
+
const r2 = await opts.handlers.scheduleFollowup(
|
|
46
|
+
{ title: String(rawArgs.title ?? ""), dueDate: String(rawArgs.dueDate ?? ""), priority: rawArgs.priority },
|
|
47
|
+
ctx
|
|
48
|
+
);
|
|
49
|
+
return { ok: true, result: { followupId: r2.id, dueDate: r2.dueDate, deduped: r2.deduped } };
|
|
50
|
+
}
|
|
51
|
+
if (toolName === "render_ui") {
|
|
52
|
+
const r2 = await opts.handlers.renderUi({ title: String(rawArgs.title ?? ""), schema: rawArgs.schema }, ctx);
|
|
53
|
+
opts.onProduced?.({ type: "artifact", path: r2.path, content: r2.content });
|
|
54
|
+
return { ok: true, result: { path: r2.path } };
|
|
55
|
+
}
|
|
56
|
+
const r = await opts.handlers.addCitation(
|
|
57
|
+
{ path: String(rawArgs.path ?? ""), quote: String(rawArgs.quote ?? ""), label: rawArgs.label },
|
|
58
|
+
ctx
|
|
59
|
+
);
|
|
60
|
+
return { ok: true, result: { citationId: r.citationId, path: r.path } };
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (err instanceof ToolInputError) return { ok: false, code: err.code, message: err.message, status: err.status };
|
|
63
|
+
return { ok: false, code: "app_tool_error", message: err instanceof Error ? err.message : String(err), status: 500 };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function outcomeStatus(outcome) {
|
|
67
|
+
return outcome.status ?? 400;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/tools/runtime.ts
|
|
71
|
+
function createAppToolRuntimeExecutor(opts) {
|
|
72
|
+
return ({ toolName, args }) => dispatchAppTool(toolName, args, opts.ctx, opts);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
dispatchAppTool,
|
|
77
|
+
outcomeStatus,
|
|
78
|
+
createAppToolRuntimeExecutor
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=chunk-UOAYS72M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/dispatch.ts","../src/tools/runtime.ts"],"sourcesContent":["import { ToolInputError } from './errors'\nimport { isAppToolName } from './openai'\nimport { findCustomTool, type AppToolDefinition } from './registry'\nimport type {\n AppToolContext,\n AppToolHandlers,\n AppToolOutcome,\n AppToolProducedEvent,\n AppToolTaxonomy,\n} from './types'\n\n/** Define options for dispatching tools including handlers, taxonomy, custom tools, and approval policies */\nexport interface DispatchOptions {\n handlers: AppToolHandlers\n taxonomy: AppToolTaxonomy\n /** Product-registered tools beyond the four built-ins. A called name that is\n * not a built-in is dispatched to the matching {@link AppToolDefinition.execute}\n * through this same validation/outcome path. */\n customTools?: readonly AppToolDefinition[]\n /** Per-call approval policy. When provided it OVERRIDES the static\n * `taxonomy.regulatedTypes` membership check, so products can gate by\n * cost threshold, environment, or first-use instead of always/never.\n * Fail-closed: a predicate that throws counts as \"approval required\". */\n needsApproval?: (type: string, args: { title: string; description: string | null }, ctx: AppToolContext) => boolean | Promise<boolean>\n /** Called at the real side-effect site for proposals (proposal_created) and\n * generated views (artifact) so a consumer's completion oracle credits\n * persisted state. Omit when produced state isn't tracked. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\n/**\n * The ONE place an app-tool call is validated, dispatched to the product's\n * handler, and turned into an {@link AppToolOutcome} + produced events. Shared\n * by the HTTP route layer and the agent-runtime executor so both paths apply\n * identical validation and identical side effects. A {@link ToolInputError}\n * (bad input the agent can correct) and any other throw both become\n * `{ ok: false }` — a tool call never silently \"succeeds\" without its effect.\n */\nexport async function dispatchAppTool(\n toolName: string,\n rawArgs: Record<string, unknown>,\n ctx: AppToolContext,\n opts: DispatchOptions,\n): Promise<AppToolOutcome> {\n try {\n if (!isAppToolName(toolName)) {\n const custom = findCustomTool(toolName, opts.customTools)\n if (!custom) return { ok: false, code: 'unknown_tool', message: `${toolName} is not an app tool.` }\n // Custom tools own their own arg validation (their execute throws\n // ToolInputError for correctable input); the outer try/catch maps it.\n const result = await custom.execute(rawArgs, ctx)\n return { ok: true, result }\n }\n\n if (toolName === 'submit_proposal') {\n const type = String(rawArgs.type ?? '').trim()\n const title = String(rawArgs.title ?? '').trim()\n if (!type || !opts.taxonomy.proposalTypes.includes(type)) {\n return { ok: false, code: 'invalid_type', message: `type must be one of: ${opts.taxonomy.proposalTypes.join(', ')}.` }\n }\n if (!title) return { ok: false, code: 'missing_title', message: 'title is required.' }\n const description = rawArgs.description == null ? null : String(rawArgs.description)\n // Approval policy runs BEFORE the handler so the decision can gate the\n // side effect itself, not merely re-label it afterwards.\n let regulated = opts.taxonomy.regulatedTypes.includes(type)\n if (opts.needsApproval) {\n try {\n regulated = await opts.needsApproval(type, { title, description }, ctx)\n } catch {\n regulated = true // fail-closed: a broken policy means approval required\n }\n }\n const r = await opts.handlers.submitProposal({ type, title, description, regulated }, ctx)\n // Pass the handler's result through: products with immediate-execute\n // proposal types return status 'executed' plus their own fields\n // (e.g. datasetId) — the model must see what actually happened, not a\n // hard-coded \"queued for approval\".\n const { proposalId, deduped, status, ...extra } = r\n const effectiveStatus = status ?? 'queued_for_approval'\n opts.onProduced?.({\n type: 'proposal_created',\n proposalId,\n title,\n status: effectiveStatus === 'executed' ? 'executed' : 'pending',\n content: description ?? undefined,\n })\n return { ok: true, result: { ...extra, status: effectiveStatus, proposalId, deduped, regulated } }\n }\n\n if (toolName === 'schedule_followup') {\n const r = await opts.handlers.scheduleFollowup(\n { title: String(rawArgs.title ?? ''), dueDate: String(rawArgs.dueDate ?? ''), priority: rawArgs.priority as string | undefined },\n ctx,\n )\n return { ok: true, result: { followupId: r.id, dueDate: r.dueDate, deduped: r.deduped } }\n }\n\n if (toolName === 'render_ui') {\n const r = await opts.handlers.renderUi({ title: String(rawArgs.title ?? ''), schema: rawArgs.schema }, ctx)\n opts.onProduced?.({ type: 'artifact', path: r.path, content: r.content })\n return { ok: true, result: { path: r.path } }\n }\n\n // add_citation\n const r = await opts.handlers.addCitation(\n { path: String(rawArgs.path ?? ''), quote: String(rawArgs.quote ?? ''), label: rawArgs.label as string | undefined },\n ctx,\n )\n return { ok: true, result: { citationId: r.citationId, path: r.path } }\n } catch (err) {\n if (err instanceof ToolInputError) return { ok: false, code: err.code, message: err.message, status: err.status }\n return { ok: false, code: 'app_tool_error', message: err instanceof Error ? err.message : String(err), status: 500 }\n }\n}\n\n/** HTTP status for a failed outcome — the handler's `ToolInputError.status`\n * when present, else 400 for a validation reject. */\nexport function outcomeStatus(outcome: Extract<AppToolOutcome, { ok: false }>): number {\n return outcome.status ?? 400\n}\n","import { dispatchAppTool, type DispatchOptions } from './dispatch'\nimport type { AppToolContext, AppToolOutcome } from './types'\n\n/** Executes an app-tool call the model emits on the agent-runtime chat path.\n * Plug into `runChatThroughRuntime({ appToolExecutor })` (or any loop that\n * dispatches function tool_calls). */\nexport type AppToolRuntimeExecutor = (call: {\n toolName: string\n args: Record<string, unknown>\n}) => Promise<AppToolOutcome>\n\n/** Define options for executing runtime tasks with a trusted per-turn context */\nexport interface RuntimeExecutorOptions extends DispatchOptions {\n /** The trusted per-turn context — supplied directly (not from headers), since\n * the runtime path has no HTTP request. */\n ctx: AppToolContext\n}\n\n/**\n * Build the runtime executor for one turn. The agent-runtime backend must also\n * advertise the tools (`buildAppToolOpenAITools(taxonomy)` on the backend's\n * `tools`) for the model to call them; this executor fulfils each call against\n * the product's handlers and emits produced events via `opts.onProduced`.\n */\nexport function createAppToolRuntimeExecutor(opts: RuntimeExecutorOptions): AppToolRuntimeExecutor {\n return ({ toolName, args }) => dispatchAppTool(toolName, args, opts.ctx, opts)\n}\n"],"mappings":"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","names":["r"]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
unresolvedBlockingExceptions
|
|
3
|
+
} from "./chunk-F2CBC4DY.js";
|
|
4
|
+
|
|
5
|
+
// src/work-product/queue.ts
|
|
6
|
+
var OPEN_STATUSES = /* @__PURE__ */ new Set(["draft", "blocked", "ready", "changes_requested"]);
|
|
7
|
+
function currentRecordPerScope(records) {
|
|
8
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const record of records) {
|
|
10
|
+
if (record.status === "superseded") continue;
|
|
11
|
+
const held = byScope.get(record.scopeKey);
|
|
12
|
+
if (!held) {
|
|
13
|
+
byScope.set(record.scopeKey, record);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const heldOpen = OPEN_STATUSES.has(held.status);
|
|
17
|
+
const recordOpen = OPEN_STATUSES.has(record.status);
|
|
18
|
+
if (recordOpen !== heldOpen) {
|
|
19
|
+
if (recordOpen) byScope.set(record.scopeKey, record);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (record.version > held.version || record.version === held.version && record.updatedAt > held.updatedAt) {
|
|
23
|
+
byScope.set(record.scopeKey, record);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return byScope;
|
|
27
|
+
}
|
|
28
|
+
function stateOf(record, pendingAsk) {
|
|
29
|
+
switch (record.status) {
|
|
30
|
+
case "ready":
|
|
31
|
+
return "ready_for_review";
|
|
32
|
+
case "changes_requested":
|
|
33
|
+
return "changes_requested";
|
|
34
|
+
case "approved":
|
|
35
|
+
return "approved";
|
|
36
|
+
case "blocked":
|
|
37
|
+
return pendingAsk ? "missing_info" : "blocked";
|
|
38
|
+
case "draft":
|
|
39
|
+
return pendingAsk ? "missing_info" : "working";
|
|
40
|
+
// 'superseded' is filtered before this switch.
|
|
41
|
+
default:
|
|
42
|
+
return "working";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function projectReviewQueue(inputs) {
|
|
46
|
+
const asksByThread = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const ask of inputs.pendingAsks ?? []) {
|
|
48
|
+
if (!asksByThread.has(ask.threadId)) asksByThread.set(ask.threadId, ask);
|
|
49
|
+
}
|
|
50
|
+
const items = [];
|
|
51
|
+
const byScope = currentRecordPerScope(inputs.workProducts);
|
|
52
|
+
for (const [scopeKey, record] of byScope) {
|
|
53
|
+
const pendingAsk = record.threadId ? asksByThread.get(record.threadId) : void 0;
|
|
54
|
+
const item = {
|
|
55
|
+
scopeKey,
|
|
56
|
+
state: stateOf(record, pendingAsk),
|
|
57
|
+
threadId: record.threadId,
|
|
58
|
+
workProduct: {
|
|
59
|
+
id: record.id,
|
|
60
|
+
version: record.version,
|
|
61
|
+
title: record.artifact?.title ?? scopeKey,
|
|
62
|
+
kind: record.artifact?.kind ?? ""
|
|
63
|
+
},
|
|
64
|
+
blockingExceptions: unresolvedBlockingExceptions(record.exceptions).length,
|
|
65
|
+
failedChecks: record.checks.filter((check) => !check.passed).length,
|
|
66
|
+
provenance: { profileHash: record.provenance.profileHash, servingModels: record.provenance.servingModels },
|
|
67
|
+
updatedAt: record.updatedAt
|
|
68
|
+
};
|
|
69
|
+
if (pendingAsk) item.pendingAsk = { interactionId: pendingAsk.interactionId, title: pendingAsk.title };
|
|
70
|
+
items.push(item);
|
|
71
|
+
}
|
|
72
|
+
for (const thread of inputs.threads ?? []) {
|
|
73
|
+
if (byScope.has(thread.scopeKey)) continue;
|
|
74
|
+
if (items.some((item) => item.scopeKey === thread.scopeKey)) continue;
|
|
75
|
+
items.push({
|
|
76
|
+
scopeKey: thread.scopeKey,
|
|
77
|
+
state: "intake",
|
|
78
|
+
threadId: thread.threadId,
|
|
79
|
+
blockingExceptions: 0,
|
|
80
|
+
failedChecks: 0,
|
|
81
|
+
updatedAt: thread.updatedAt
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return items.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
85
|
+
}
|
|
86
|
+
function parseReviewQueueItem(raw) {
|
|
87
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
88
|
+
const record = raw;
|
|
89
|
+
const states = [
|
|
90
|
+
"intake",
|
|
91
|
+
"missing_info",
|
|
92
|
+
"working",
|
|
93
|
+
"ready_for_review",
|
|
94
|
+
"changes_requested",
|
|
95
|
+
"approved",
|
|
96
|
+
"blocked"
|
|
97
|
+
];
|
|
98
|
+
if (typeof record.scopeKey !== "string" || record.scopeKey.length === 0) return null;
|
|
99
|
+
if (!states.includes(record.state)) return null;
|
|
100
|
+
if (record.threadId !== null && typeof record.threadId !== "string") return null;
|
|
101
|
+
if (typeof record.blockingExceptions !== "number" || typeof record.failedChecks !== "number") return null;
|
|
102
|
+
if (typeof record.updatedAt !== "number") return null;
|
|
103
|
+
const item = {
|
|
104
|
+
scopeKey: record.scopeKey,
|
|
105
|
+
state: record.state,
|
|
106
|
+
threadId: record.threadId,
|
|
107
|
+
blockingExceptions: record.blockingExceptions,
|
|
108
|
+
failedChecks: record.failedChecks,
|
|
109
|
+
updatedAt: record.updatedAt
|
|
110
|
+
};
|
|
111
|
+
const workProduct = record.workProduct;
|
|
112
|
+
if (workProduct && typeof workProduct === "object") {
|
|
113
|
+
if (typeof workProduct.id === "string" && typeof workProduct.version === "number" && typeof workProduct.title === "string" && typeof workProduct.kind === "string") {
|
|
114
|
+
item.workProduct = {
|
|
115
|
+
id: workProduct.id,
|
|
116
|
+
version: workProduct.version,
|
|
117
|
+
title: workProduct.title,
|
|
118
|
+
kind: workProduct.kind
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const pendingAsk = record.pendingAsk;
|
|
123
|
+
if (pendingAsk && typeof pendingAsk === "object") {
|
|
124
|
+
if (typeof pendingAsk.interactionId === "string" && typeof pendingAsk.title === "string") {
|
|
125
|
+
item.pendingAsk = { interactionId: pendingAsk.interactionId, title: pendingAsk.title };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const provenance = record.provenance;
|
|
129
|
+
if (provenance && typeof provenance === "object") {
|
|
130
|
+
if (typeof provenance.profileHash === "string" && Array.isArray(provenance.servingModels) && provenance.servingModels.every((model) => typeof model === "string")) {
|
|
131
|
+
item.provenance = { profileHash: provenance.profileHash, servingModels: provenance.servingModels };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return item;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
projectReviewQueue,
|
|
139
|
+
parseReviewQueueItem
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=chunk-UP33Z633.js.map
|