@stigmer/react 3.1.10 → 3.1.11
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/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +3 -1
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/provider.d.ts +33 -1
- package/provider.d.ts.map +1 -1
- package/provider.js +4 -2
- package/provider.js.map +1 -1
- package/src/index.ts +10 -0
- package/src/provider.tsx +46 -8
- package/src/workflow/PendingApprovalsWidget.tsx +10 -3
- package/src/workflow/ReviewRendererContext.ts +90 -0
- package/src/workflow/WorkflowExecutionTimelineEvent.tsx +9 -2
- package/src/workflow/WorkflowTaskApprovalCard.tsx +32 -0
- package/src/workflow/WorkflowTaskReviewGate.tsx +232 -0
- package/src/workflow/__tests__/PendingApprovalsWidget.test.tsx +80 -0
- package/src/workflow/__tests__/WorkflowTaskApprovalCard.test.tsx +27 -0
- package/src/workflow/__tests__/WorkflowTaskReviewGate.test.tsx +283 -0
- package/src/workflow/__tests__/derive-task-detail.test.ts +78 -0
- package/src/workflow/execution-inspector/ExecutionInspector.tsx +8 -3
- package/src/workflow/execution-inspector/derive-task-detail.ts +34 -2
- package/src/workflow/index.ts +16 -0
- package/src/workflow/useReviewPayload.ts +115 -0
- package/workflow/PendingApprovalsWidget.d.ts.map +1 -1
- package/workflow/PendingApprovalsWidget.js +1 -1
- package/workflow/PendingApprovalsWidget.js.map +1 -1
- package/workflow/ReviewRendererContext.d.ts +71 -0
- package/workflow/ReviewRendererContext.d.ts.map +1 -0
- package/workflow/ReviewRendererContext.js +23 -0
- package/workflow/ReviewRendererContext.js.map +1 -0
- package/workflow/WorkflowExecutionTimelineEvent.d.ts.map +1 -1
- package/workflow/WorkflowExecutionTimelineEvent.js +7 -3
- package/workflow/WorkflowExecutionTimelineEvent.js.map +1 -1
- package/workflow/WorkflowTaskApprovalCard.d.ts +9 -0
- package/workflow/WorkflowTaskApprovalCard.d.ts.map +1 -1
- package/workflow/WorkflowTaskApprovalCard.js +8 -2
- package/workflow/WorkflowTaskApprovalCard.js.map +1 -1
- package/workflow/WorkflowTaskReviewGate.d.ts +54 -0
- package/workflow/WorkflowTaskReviewGate.d.ts.map +1 -0
- package/workflow/WorkflowTaskReviewGate.js +56 -0
- package/workflow/WorkflowTaskReviewGate.js.map +1 -0
- package/workflow/execution-inspector/ExecutionInspector.d.ts.map +1 -1
- package/workflow/execution-inspector/ExecutionInspector.js +5 -3
- package/workflow/execution-inspector/ExecutionInspector.js.map +1 -1
- package/workflow/execution-inspector/derive-task-detail.d.ts +20 -1
- package/workflow/execution-inspector/derive-task-detail.d.ts.map +1 -1
- package/workflow/execution-inspector/derive-task-detail.js +14 -0
- package/workflow/execution-inspector/derive-task-detail.js.map +1 -1
- package/workflow/index.d.ts +3 -0
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +4 -0
- package/workflow/index.js.map +1 -1
- package/workflow/useReviewPayload.d.ts +41 -0
- package/workflow/useReviewPayload.d.ts.map +1 -0
- package/workflow/useReviewPayload.js +79 -0
- package/workflow/useReviewPayload.js.map +1 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach, beforeEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { StigmerContext } from "../../context";
|
|
5
|
+
import { FetchCacheContext } from "../../internal/FetchCacheProvider";
|
|
6
|
+
import { ReviewRendererContext, type ReviewRenderers } from "../ReviewRendererContext";
|
|
7
|
+
import { WorkflowTaskReviewGate } from "../WorkflowTaskReviewGate";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal Stigmer client stub. `artifact.getContent` only matters for
|
|
11
|
+
* artifact-backed payload tests; inline tests never call it.
|
|
12
|
+
*/
|
|
13
|
+
function createMockStigmer(overrides: {
|
|
14
|
+
getContent?: (...args: unknown[]) => Promise<unknown>;
|
|
15
|
+
} = {}) {
|
|
16
|
+
return {
|
|
17
|
+
artifact: {
|
|
18
|
+
getContent:
|
|
19
|
+
overrides.getContent ??
|
|
20
|
+
vi.fn().mockRejectedValue(new Error("getContent not stubbed")),
|
|
21
|
+
},
|
|
22
|
+
} as never;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function jsonContentResponse(value: unknown) {
|
|
26
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
27
|
+
return {
|
|
28
|
+
content: bytes,
|
|
29
|
+
contentType: "application/json",
|
|
30
|
+
totalSizeBytes: BigInt(bytes.length),
|
|
31
|
+
truncated: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function wrapper(client: unknown, renderers: ReviewRenderers = {}) {
|
|
36
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
37
|
+
return (
|
|
38
|
+
<FetchCacheContext.Provider value={null}>
|
|
39
|
+
<StigmerContext.Provider value={client as never}>
|
|
40
|
+
<ReviewRendererContext.Provider value={renderers}>
|
|
41
|
+
{children}
|
|
42
|
+
</ReviewRendererContext.Provider>
|
|
43
|
+
</StigmerContext.Provider>
|
|
44
|
+
</FetchCacheContext.Provider>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const defaultProps = {
|
|
50
|
+
taskName: "editorial_review",
|
|
51
|
+
prompt: "Review the proposed revision before publishing.",
|
|
52
|
+
outcomes: [
|
|
53
|
+
{ name: "approve", label: "Approve" },
|
|
54
|
+
{ name: "request_changes", label: "Request Changes" },
|
|
55
|
+
],
|
|
56
|
+
onSubmit: vi.fn().mockResolvedValue(undefined),
|
|
57
|
+
isSubmitting: false,
|
|
58
|
+
error: null,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe("WorkflowTaskReviewGate", () => {
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
vi.restoreAllMocks();
|
|
64
|
+
});
|
|
65
|
+
afterEach(cleanup);
|
|
66
|
+
|
|
67
|
+
describe("renderer dispatch", () => {
|
|
68
|
+
it("renders the built-in card when the gate has no ui_hint", () => {
|
|
69
|
+
render(<WorkflowTaskReviewGate {...defaultProps} />, {
|
|
70
|
+
wrapper: wrapper(createMockStigmer()),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(
|
|
74
|
+
screen.getByRole("form", { name: /approval decision for editorial_review/i }),
|
|
75
|
+
).toBeTruthy();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("falls back to the built-in card for an unregistered ui_hint (portability)", () => {
|
|
79
|
+
render(
|
|
80
|
+
<WorkflowTaskReviewGate
|
|
81
|
+
{...defaultProps}
|
|
82
|
+
uiHint="article-diff"
|
|
83
|
+
payload={{ title: "Draft v2" }}
|
|
84
|
+
/>,
|
|
85
|
+
{ wrapper: wrapper(createMockStigmer(), {}) },
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(
|
|
89
|
+
screen.getByRole("form", { name: /approval decision for editorial_review/i }),
|
|
90
|
+
).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("renders the registered custom renderer for a matching ui_hint", () => {
|
|
94
|
+
const renderers: ReviewRenderers = {
|
|
95
|
+
"article-diff": ({ payload }) => (
|
|
96
|
+
<div data-testid="custom-renderer">
|
|
97
|
+
{(payload as { title: string }).title}
|
|
98
|
+
</div>
|
|
99
|
+
),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
render(
|
|
103
|
+
<WorkflowTaskReviewGate
|
|
104
|
+
{...defaultProps}
|
|
105
|
+
uiHint="article-diff"
|
|
106
|
+
payload={{ title: "Draft v2" }}
|
|
107
|
+
/>,
|
|
108
|
+
{ wrapper: wrapper(createMockStigmer(), renderers) },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(screen.getByTestId("custom-renderer").textContent).toBe("Draft v2");
|
|
112
|
+
expect(screen.queryByRole("form")).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("passes formSchema and outcomes through to the custom renderer", () => {
|
|
116
|
+
const seen: { formSchema?: unknown; outcomes?: unknown } = {};
|
|
117
|
+
const renderers: ReviewRenderers = {
|
|
118
|
+
"article-diff": ({ formSchema, outcomes }) => {
|
|
119
|
+
seen.formSchema = formSchema;
|
|
120
|
+
seen.outcomes = outcomes;
|
|
121
|
+
return <div data-testid="custom-renderer" />;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const formSchema = { type: "object", properties: { notes: { type: "string" } } };
|
|
126
|
+
render(
|
|
127
|
+
<WorkflowTaskReviewGate
|
|
128
|
+
{...defaultProps}
|
|
129
|
+
uiHint="article-diff"
|
|
130
|
+
payload={{ title: "Draft" }}
|
|
131
|
+
formSchema={formSchema}
|
|
132
|
+
/>,
|
|
133
|
+
{ wrapper: wrapper(createMockStigmer(), renderers) },
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
expect(seen.formSchema).toEqual(formSchema);
|
|
137
|
+
expect(seen.outcomes).toEqual(defaultProps.outcomes);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("binds taskName into the renderer's submit callback", () => {
|
|
141
|
+
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
|
142
|
+
const renderers: ReviewRenderers = {
|
|
143
|
+
"article-diff": ({ submit }) => (
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
onClick={() => submit("approve", { notes: "lgtm" }, "ship it")}
|
|
147
|
+
>
|
|
148
|
+
Decide
|
|
149
|
+
</button>
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
render(
|
|
154
|
+
<WorkflowTaskReviewGate
|
|
155
|
+
{...defaultProps}
|
|
156
|
+
onSubmit={onSubmit}
|
|
157
|
+
uiHint="article-diff"
|
|
158
|
+
payload={{ title: "Draft" }}
|
|
159
|
+
/>,
|
|
160
|
+
{ wrapper: wrapper(createMockStigmer(), renderers) },
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
fireEvent.click(screen.getByRole("button", { name: "Decide" }));
|
|
164
|
+
|
|
165
|
+
expect(onSubmit).toHaveBeenCalledWith(
|
|
166
|
+
"editorial_review",
|
|
167
|
+
"approve",
|
|
168
|
+
{ notes: "lgtm" },
|
|
169
|
+
"ship it",
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe("inline payload fallback display", () => {
|
|
175
|
+
it("shows the payload as structured data in the built-in card", () => {
|
|
176
|
+
render(
|
|
177
|
+
<WorkflowTaskReviewGate
|
|
178
|
+
{...defaultProps}
|
|
179
|
+
payload={{ severity: "P1", summary: "Database migration plan" }}
|
|
180
|
+
/>,
|
|
181
|
+
{ wrapper: wrapper(createMockStigmer()) },
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(screen.getByLabelText("Review material for editorial_review")).toBeTruthy();
|
|
185
|
+
expect(screen.getByText("Database migration plan")).toBeTruthy();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("omits the review material section when the gate has no payload", () => {
|
|
189
|
+
render(<WorkflowTaskReviewGate {...defaultProps} />, {
|
|
190
|
+
wrapper: wrapper(createMockStigmer()),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(screen.queryByLabelText("Review material for editorial_review")).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("artifact-backed payload", () => {
|
|
198
|
+
it("shows a loading state, then the custom renderer with fetched content", async () => {
|
|
199
|
+
const getContent = vi
|
|
200
|
+
.fn()
|
|
201
|
+
.mockResolvedValue(jsonContentResponse({ records: ["r1", "r2"] }));
|
|
202
|
+
const renderers: ReviewRenderers = {
|
|
203
|
+
"infra-proposal": ({ payload }) => (
|
|
204
|
+
<div data-testid="custom-renderer">
|
|
205
|
+
{(payload as { records: string[] }).records.length} records
|
|
206
|
+
</div>
|
|
207
|
+
),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
render(
|
|
211
|
+
<WorkflowTaskReviewGate
|
|
212
|
+
{...defaultProps}
|
|
213
|
+
uiHint="infra-proposal"
|
|
214
|
+
payloadArtifactId="art_review123"
|
|
215
|
+
/>,
|
|
216
|
+
{ wrapper: wrapper(createMockStigmer({ getContent }), renderers) },
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(
|
|
220
|
+
screen.getByRole("status", { name: /loading review material/i }),
|
|
221
|
+
).toBeTruthy();
|
|
222
|
+
|
|
223
|
+
await waitFor(() =>
|
|
224
|
+
expect(screen.getByTestId("custom-renderer").textContent).toBe("2 records"),
|
|
225
|
+
);
|
|
226
|
+
expect(getContent).toHaveBeenCalledWith(
|
|
227
|
+
expect.objectContaining({ artifactId: "art_review123" }),
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("shows an error state with retry when the artifact fetch fails", async () => {
|
|
232
|
+
const getContent = vi.fn().mockRejectedValue(new Error("storage unavailable"));
|
|
233
|
+
|
|
234
|
+
render(
|
|
235
|
+
<WorkflowTaskReviewGate {...defaultProps} payloadArtifactId="art_review123" />,
|
|
236
|
+
{ wrapper: wrapper(createMockStigmer({ getContent })) },
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
await waitFor(() => expect(screen.getByRole("alert")).toBeTruthy());
|
|
240
|
+
expect(screen.getByText(/storage unavailable/)).toBeTruthy();
|
|
241
|
+
expect(screen.getByRole("button", { name: "Retry" })).toBeTruthy();
|
|
242
|
+
// The gate must never offer a decision without its review material.
|
|
243
|
+
expect(screen.queryByRole("form")).toBeNull();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("retries the fetch when the reviewer clicks Retry", async () => {
|
|
247
|
+
const getContent = vi
|
|
248
|
+
.fn()
|
|
249
|
+
.mockRejectedValueOnce(new Error("transient"))
|
|
250
|
+
.mockResolvedValue(jsonContentResponse({ ok: true }));
|
|
251
|
+
|
|
252
|
+
render(
|
|
253
|
+
<WorkflowTaskReviewGate {...defaultProps} payloadArtifactId="art_review123" />,
|
|
254
|
+
{ wrapper: wrapper(createMockStigmer({ getContent })) },
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
await waitFor(() => expect(screen.getByRole("alert")).toBeTruthy());
|
|
258
|
+
fireEvent.click(screen.getByRole("button", { name: "Retry" }));
|
|
259
|
+
|
|
260
|
+
await waitFor(() =>
|
|
261
|
+
expect(screen.getByRole("form", { name: /approval decision/i })).toBeTruthy(),
|
|
262
|
+
);
|
|
263
|
+
expect(getContent).toHaveBeenCalledTimes(2);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("surfaces a parse failure as an error instead of rendering garbage", async () => {
|
|
267
|
+
const bytes = new TextEncoder().encode("{not json");
|
|
268
|
+
const getContent = vi.fn().mockResolvedValue({
|
|
269
|
+
content: bytes,
|
|
270
|
+
contentType: "application/json",
|
|
271
|
+
totalSizeBytes: BigInt(bytes.length),
|
|
272
|
+
truncated: false,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
render(
|
|
276
|
+
<WorkflowTaskReviewGate {...defaultProps} payloadArtifactId="art_review123" />,
|
|
277
|
+
{ wrapper: wrapper(createMockStigmer({ getContent })) },
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
await waitFor(() => expect(screen.getByRole("alert")).toBeTruthy());
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { fromJson } from "@bufbuild/protobuf";
|
|
3
|
+
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
2
4
|
import type { WorkflowExecutionEvent } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
|
|
3
5
|
import type { WorkflowTask } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
4
6
|
import type { DerivedTaskState } from "../../internal/store/workflow-execution-event-store";
|
|
@@ -615,6 +617,82 @@ describe("deriveTaskDetail", () => {
|
|
|
615
617
|
expect(result!.approval!.decision).toBeNull();
|
|
616
618
|
});
|
|
617
619
|
|
|
620
|
+
// 13b. Review payload fields (issue #234)
|
|
621
|
+
it("unwraps an inline review payload with ui_hint from the approvalRequested event", () => {
|
|
622
|
+
const derived = makeDerived({ status: "waiting_approval" });
|
|
623
|
+
const events = [
|
|
624
|
+
makeEvent("my-task", 1, "2026-01-01T00:00:00Z", {
|
|
625
|
+
case: "approvalRequested",
|
|
626
|
+
value: {
|
|
627
|
+
prompt: "Review the draft",
|
|
628
|
+
approvers: [],
|
|
629
|
+
timeoutSeconds: 0,
|
|
630
|
+
outcomes: [],
|
|
631
|
+
formSchema: null,
|
|
632
|
+
// Real events carry a google.protobuf.Value message.
|
|
633
|
+
payload: fromJson(ValueSchema, { title: "Q3 plan", items: [1, 2] }),
|
|
634
|
+
uiHint: "plan-review",
|
|
635
|
+
payloadArtifactId: "",
|
|
636
|
+
},
|
|
637
|
+
}),
|
|
638
|
+
];
|
|
639
|
+
|
|
640
|
+
const result = deriveTaskDetail("my-task", events, undefined, derived);
|
|
641
|
+
|
|
642
|
+
expect(result!.approval!.payload).toEqual({ title: "Q3 plan", items: [1, 2] });
|
|
643
|
+
expect(result!.approval!.uiHint).toBe("plan-review");
|
|
644
|
+
expect(result!.approval!.payloadArtifactId).toBeNull();
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it("carries the artifact reference instead of inline data for promoted payloads", () => {
|
|
648
|
+
const derived = makeDerived({ status: "waiting_approval" });
|
|
649
|
+
const events = [
|
|
650
|
+
makeEvent("my-task", 1, "2026-01-01T00:00:00Z", {
|
|
651
|
+
case: "approvalRequested",
|
|
652
|
+
value: {
|
|
653
|
+
prompt: "Review the proposal",
|
|
654
|
+
approvers: [],
|
|
655
|
+
timeoutSeconds: 0,
|
|
656
|
+
outcomes: [],
|
|
657
|
+
formSchema: null,
|
|
658
|
+
payload: undefined,
|
|
659
|
+
uiHint: "infra-proposal",
|
|
660
|
+
payloadArtifactId: "art_review123",
|
|
661
|
+
},
|
|
662
|
+
}),
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
const result = deriveTaskDetail("my-task", events, undefined, derived);
|
|
666
|
+
|
|
667
|
+
expect(result!.approval!.payload).toBeNull();
|
|
668
|
+
expect(result!.approval!.uiHint).toBe("infra-proposal");
|
|
669
|
+
expect(result!.approval!.payloadArtifactId).toBe("art_review123");
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it("defaults review payload fields when the gate carries none (pre-#234 events)", () => {
|
|
673
|
+
const derived = makeDerived({ status: "waiting_approval" });
|
|
674
|
+
const events = [
|
|
675
|
+
makeEvent("my-task", 1, "2026-01-01T00:00:00Z", {
|
|
676
|
+
case: "approvalRequested",
|
|
677
|
+
value: {
|
|
678
|
+
prompt: "Continue?",
|
|
679
|
+
approvers: [],
|
|
680
|
+
timeoutSeconds: 0,
|
|
681
|
+
outcomes: [],
|
|
682
|
+
formSchema: null,
|
|
683
|
+
uiHint: "",
|
|
684
|
+
payloadArtifactId: "",
|
|
685
|
+
},
|
|
686
|
+
}),
|
|
687
|
+
];
|
|
688
|
+
|
|
689
|
+
const result = deriveTaskDetail("my-task", events, undefined, derived);
|
|
690
|
+
|
|
691
|
+
expect(result!.approval!.payload).toBeNull();
|
|
692
|
+
expect(result!.approval!.uiHint).toBe("");
|
|
693
|
+
expect(result!.approval!.payloadArtifactId).toBeNull();
|
|
694
|
+
});
|
|
695
|
+
|
|
618
696
|
// 14. Approval resolved — event-only (the brief "finalizing" window before
|
|
619
697
|
// the task-output snapshot reflects the decision)
|
|
620
698
|
it("populates approval decision from the event when the task output is not yet available", () => {
|
|
@@ -16,7 +16,7 @@ import { RetriesTab } from "./RetriesTab.js";
|
|
|
16
16
|
import { AgentCallTab } from "./AgentCallTab.js";
|
|
17
17
|
import { EventLogTab } from "./EventLogTab.js";
|
|
18
18
|
import { WorkflowExecutionApprovalCard } from "../WorkflowExecutionApprovalCard.js";
|
|
19
|
-
import {
|
|
19
|
+
import { WorkflowTaskReviewGate } from "../WorkflowTaskReviewGate.js";
|
|
20
20
|
import { WorkflowTaskApprovalSummary } from "../WorkflowTaskApprovalSummary.js";
|
|
21
21
|
|
|
22
22
|
/** Props for {@link ExecutionInspector}. */
|
|
@@ -250,12 +250,17 @@ export const ExecutionInspector = memo(function ExecutionInspector({
|
|
|
250
250
|
)}
|
|
251
251
|
{effectiveTab === "approval" && detail.approval && (
|
|
252
252
|
detail.status === "waiting_approval" && onSubmitTaskApproval ? (
|
|
253
|
-
// Gate still awaiting a decision — collect one.
|
|
254
|
-
|
|
253
|
+
// Gate still awaiting a decision — collect one. The gate
|
|
254
|
+
// resolves artifact-backed payloads and dispatches to a
|
|
255
|
+
// registered review renderer (by ui_hint) or the built-in card.
|
|
256
|
+
<WorkflowTaskReviewGate
|
|
255
257
|
taskName={detail.taskName}
|
|
256
258
|
prompt={detail.approval.prompt}
|
|
257
259
|
outcomes={detail.approval.outcomes}
|
|
258
260
|
formSchema={detail.approval.formSchema ?? undefined}
|
|
261
|
+
payload={detail.approval.payload}
|
|
262
|
+
uiHint={detail.approval.uiHint}
|
|
263
|
+
payloadArtifactId={detail.approval.payloadArtifactId}
|
|
259
264
|
onSubmit={onSubmitTaskApproval}
|
|
260
265
|
isSubmitting={taskApprovalSubmittingTaskNames?.has(detail.taskName) ?? false}
|
|
261
266
|
error={taskApprovalErrorsByTaskName?.get(detail.taskName) ?? null}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
import type { WorkflowExecutionEvent } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
|
|
12
12
|
import type { WorkflowTask } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
13
13
|
import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
|
|
14
|
-
import type
|
|
14
|
+
import { toJson, type JsonObject, type JsonValue } from "@bufbuild/protobuf";
|
|
15
|
+
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
15
16
|
import type { DerivedTaskState } from "../../internal/store/workflow-execution-event-store.js";
|
|
16
17
|
import { kindToDisplayName } from "../kind-metadata.js";
|
|
17
18
|
import { taskKindToString } from "../workflow-graph-conversions.js";
|
|
@@ -96,6 +97,25 @@ export interface TaskDetailApproval {
|
|
|
96
97
|
readonly outcomes: readonly { readonly name: string; readonly label: string }[];
|
|
97
98
|
readonly formSchema: JsonObject | null;
|
|
98
99
|
readonly timeoutSeconds: number;
|
|
100
|
+
/**
|
|
101
|
+
* Resolved review payload the gate presented — the material under
|
|
102
|
+
* review (issue #234). `null` when the gate carries no payload or when
|
|
103
|
+
* the payload is artifact-backed (see {@link payloadArtifactId}).
|
|
104
|
+
*/
|
|
105
|
+
readonly payload: JsonValue | null;
|
|
106
|
+
/**
|
|
107
|
+
* Renderer discriminator from the task config's `ui_hint`. Consumers
|
|
108
|
+
* with a registered review renderer for this hint present domain-native
|
|
109
|
+
* UI; everything else falls back to structured-data display. The empty
|
|
110
|
+
* string when the task config sets no hint.
|
|
111
|
+
*/
|
|
112
|
+
readonly uiHint: string;
|
|
113
|
+
/**
|
|
114
|
+
* Artifact holding the payload when it exceeded the inline promotion
|
|
115
|
+
* threshold. Mutually exclusive with {@link payload}; resolve the
|
|
116
|
+
* content via `stigmer.artifact.getContent`. `null` when inline.
|
|
117
|
+
*/
|
|
118
|
+
readonly payloadArtifactId: string | null;
|
|
99
119
|
readonly decision: TaskDetailApprovalDecision | null;
|
|
100
120
|
}
|
|
101
121
|
|
|
@@ -204,7 +224,7 @@ interface EventBuckets {
|
|
|
204
224
|
agentStarted: { childExecutionId: string; agentSlug: string; messageSummary: string } | null;
|
|
205
225
|
agentProgress: { childExecutionId: string; agentPhase: number; currentToolName: string; tokensConsumed: bigint; messagesCount: number; toolCallsCount: number } | null;
|
|
206
226
|
agentCompleted: { durationMs: number; tokensConsumed: bigint; costMicros: bigint; error: string; agentPhase: number } | null;
|
|
207
|
-
approvalRequested: { prompt: string; approvers: string[]; timeoutSeconds: number; outcomes: Array<{ name: string; label: string }>; formSchema: JsonObject | null } | null;
|
|
227
|
+
approvalRequested: { prompt: string; approvers: string[]; timeoutSeconds: number; outcomes: Array<{ name: string; label: string }>; formSchema: JsonObject | null; payload: JsonValue | null; uiHint: string; payloadArtifactId: string | null } | null;
|
|
208
228
|
approvalResolved: { action: number; resolvedBy: string; comment: string; waitDurationMs: number } | null;
|
|
209
229
|
inputSummary: JsonObject | null;
|
|
210
230
|
outputSummary: JsonObject | null;
|
|
@@ -315,6 +335,12 @@ function bucketEvents(taskEvents: readonly WorkflowExecutionEvent[]): EventBucke
|
|
|
315
335
|
timeoutSeconds: p.value.timeoutSeconds,
|
|
316
336
|
outcomes: (p.value.outcomes ?? []).map((o) => ({ name: o.name, label: o.label })),
|
|
317
337
|
formSchema: p.value.formSchema ? (p.value.formSchema as unknown as JsonObject) : null,
|
|
338
|
+
// The payload rides the event as a google.protobuf.Value message;
|
|
339
|
+
// unwrap it to plain JSON once here so all consumers downstream
|
|
340
|
+
// (renderers, fallback card) work with ordinary values.
|
|
341
|
+
payload: p.value.payload ? (toJson(ValueSchema, p.value.payload) as JsonValue) : null,
|
|
342
|
+
uiHint: p.value.uiHint,
|
|
343
|
+
payloadArtifactId: p.value.payloadArtifactId || null,
|
|
318
344
|
};
|
|
319
345
|
break;
|
|
320
346
|
|
|
@@ -515,6 +541,9 @@ function buildApproval(
|
|
|
515
541
|
outcomes: req.outcomes,
|
|
516
542
|
formSchema: req.formSchema,
|
|
517
543
|
timeoutSeconds: req.timeoutSeconds,
|
|
544
|
+
payload: req.payload,
|
|
545
|
+
uiHint: req.uiHint,
|
|
546
|
+
payloadArtifactId: req.payloadArtifactId,
|
|
518
547
|
decision: null,
|
|
519
548
|
};
|
|
520
549
|
}
|
|
@@ -525,6 +554,9 @@ function buildApproval(
|
|
|
525
554
|
outcomes: req.outcomes,
|
|
526
555
|
formSchema: req.formSchema,
|
|
527
556
|
timeoutSeconds: req.timeoutSeconds,
|
|
557
|
+
payload: req.payload,
|
|
558
|
+
uiHint: req.uiHint,
|
|
559
|
+
payloadArtifactId: req.payloadArtifactId,
|
|
528
560
|
decision: {
|
|
529
561
|
outcome: outputOutcome,
|
|
530
562
|
reviewer: readSnapshotString(taskOutput, "reviewer") || (res?.resolvedBy ?? ""),
|
package/src/workflow/index.ts
CHANGED
|
@@ -220,6 +220,22 @@ export {
|
|
|
220
220
|
type TaskOutcome,
|
|
221
221
|
} from "./WorkflowTaskApprovalCard.js";
|
|
222
222
|
|
|
223
|
+
// Review payloads (issue #234): custom renderers for human_input gates
|
|
224
|
+
export {
|
|
225
|
+
WorkflowTaskReviewGate,
|
|
226
|
+
type WorkflowTaskReviewGateProps,
|
|
227
|
+
} from "./WorkflowTaskReviewGate.js";
|
|
228
|
+
export {
|
|
229
|
+
ReviewRendererContext,
|
|
230
|
+
useReviewRenderer,
|
|
231
|
+
type ReviewRendererProps,
|
|
232
|
+
type ReviewRenderers,
|
|
233
|
+
} from "./ReviewRendererContext.js";
|
|
234
|
+
export {
|
|
235
|
+
useReviewPayload,
|
|
236
|
+
type UseReviewPayloadReturn,
|
|
237
|
+
} from "./useReviewPayload.js";
|
|
238
|
+
|
|
223
239
|
export {
|
|
224
240
|
WorkflowTaskApprovalSummary,
|
|
225
241
|
type WorkflowTaskApprovalSummaryProps,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import type { JsonValue } from "@bufbuild/protobuf";
|
|
6
|
+
import { GetArtifactContentRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/artifact/v1/io_pb";
|
|
7
|
+
import { StigmerContext } from "../context.js";
|
|
8
|
+
import { useFetch } from "../internal/useFetch.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Request cap for artifact-backed review payloads.
|
|
12
|
+
*
|
|
13
|
+
* Matches the artifact store's 50MB creation limit
|
|
14
|
+
* (`CreateArtifactInput.content` max_len), so a promoted payload can
|
|
15
|
+
* never legitimately exceed this. Unlike preview flows, a review
|
|
16
|
+
* renderer needs the complete payload — a truncated JSON document
|
|
17
|
+
* would not even parse — so we request the full artifact rather than
|
|
18
|
+
* the server's 512KB preview default.
|
|
19
|
+
*/
|
|
20
|
+
const REVIEW_PAYLOAD_MAX_BYTES = 50 * 1024 * 1024;
|
|
21
|
+
|
|
22
|
+
/** Return value of {@link useReviewPayload}. */
|
|
23
|
+
export interface UseReviewPayloadReturn {
|
|
24
|
+
/**
|
|
25
|
+
* The materialized review payload: the inline value when the gate
|
|
26
|
+
* carried it directly, or the fetched-and-parsed artifact content when
|
|
27
|
+
* it was artifact-backed. `null` while loading, on error, or when the
|
|
28
|
+
* gate has no payload at all.
|
|
29
|
+
*/
|
|
30
|
+
readonly payload: JsonValue | null;
|
|
31
|
+
/** `true` while an artifact-backed payload fetch is in flight. */
|
|
32
|
+
readonly isLoading: boolean;
|
|
33
|
+
/** Error from a failed artifact fetch or parse, or `null`. */
|
|
34
|
+
readonly error: Error | null;
|
|
35
|
+
/** Re-fetch an artifact-backed payload after a failure. */
|
|
36
|
+
readonly refetch: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Behavior hook that materializes a human_input gate's review payload.
|
|
41
|
+
*
|
|
42
|
+
* The `approval_requested` event carries the payload in one of two forms
|
|
43
|
+
* (mutually exclusive): inline JSON for small payloads, or an artifact
|
|
44
|
+
* reference for payloads that exceeded the runner's promotion threshold.
|
|
45
|
+
* This hook collapses the difference — consumers (custom review renderers,
|
|
46
|
+
* the built-in approval card) always receive plain JSON plus loading and
|
|
47
|
+
* error states, and never deal with artifact plumbing.
|
|
48
|
+
*
|
|
49
|
+
* Artifact content is read through `stigmer.artifact.getContent`, which
|
|
50
|
+
* proxies bytes through the Stigmer API — embedded SDK consumers on
|
|
51
|
+
* third-party origins avoid the CORS exposure of presigned URLs.
|
|
52
|
+
*
|
|
53
|
+
* Inline payloads resolve synchronously: `isLoading` is `false` and
|
|
54
|
+
* `payload` is available on first render.
|
|
55
|
+
*
|
|
56
|
+
* @param inlinePayload - Inline payload from `TaskDetailApproval.payload`,
|
|
57
|
+
* or `null`.
|
|
58
|
+
* @param payloadArtifactId - Artifact reference from
|
|
59
|
+
* `TaskDetailApproval.payloadArtifactId`, or `null`.
|
|
60
|
+
*/
|
|
61
|
+
export function useReviewPayload(
|
|
62
|
+
inlinePayload: JsonValue | null,
|
|
63
|
+
payloadArtifactId: string | null,
|
|
64
|
+
): UseReviewPayloadReturn {
|
|
65
|
+
// Nullable on purpose: gates without an artifact-backed payload (no
|
|
66
|
+
// payload, or an inline one) must keep working outside StigmerProvider,
|
|
67
|
+
// as the approval surfaces always have. The client is a requirement of
|
|
68
|
+
// the artifact fetch, not of the hook — enforced below with a
|
|
69
|
+
// descriptive error (DD-006) only when a fetch is actually needed.
|
|
70
|
+
const stigmer = useContext(StigmerContext);
|
|
71
|
+
|
|
72
|
+
const { data, isLoading, error, refetch } = useFetch<JsonValue | null>(
|
|
73
|
+
payloadArtifactId
|
|
74
|
+
? async () => {
|
|
75
|
+
if (!stigmer) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"This gate's review payload is artifact-backed and needs a " +
|
|
78
|
+
"Stigmer client to fetch. Wrap your component tree with " +
|
|
79
|
+
"<StigmerProvider client={stigmerClient}>.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const response = await stigmer.artifact.getContent(
|
|
83
|
+
create(GetArtifactContentRequestSchema, {
|
|
84
|
+
artifactId: payloadArtifactId,
|
|
85
|
+
maxBytes: BigInt(REVIEW_PAYLOAD_MAX_BYTES),
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
// Defensive: cannot happen while the promotion path and this cap
|
|
89
|
+
// both honor the 50MB artifact limit, but a truncated JSON body
|
|
90
|
+
// must fail loudly rather than parse into partial review material.
|
|
91
|
+
if (response.truncated) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Review payload artifact ${payloadArtifactId} exceeds the ` +
|
|
94
|
+
`${REVIEW_PAYLOAD_MAX_BYTES} byte limit and was truncated`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const text = new TextDecoder().decode(response.content);
|
|
98
|
+
return JSON.parse(text) as JsonValue;
|
|
99
|
+
}
|
|
100
|
+
: null,
|
|
101
|
+
[payloadArtifactId, stigmer],
|
|
102
|
+
null,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (!payloadArtifactId) {
|
|
106
|
+
return {
|
|
107
|
+
payload: inlinePayload,
|
|
108
|
+
isLoading: false,
|
|
109
|
+
error: null,
|
|
110
|
+
refetch,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { payload: data, isLoading, error, refetch };
|
|
115
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingApprovalsWidget.d.ts","sourceRoot":"","sources":["../../src/workflow/PendingApprovalsWidget.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+DAA+D,CAAC;AAIrG,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"PendingApprovalsWidget.d.ts","sourceRoot":"","sources":["../../src/workflow/PendingApprovalsWidget.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+DAA+D,CAAC;AAIrG,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,mEAyFjC,CAAC"}
|
|
@@ -29,7 +29,7 @@ export const PendingApprovalsWidget = memo(function PendingApprovalsWidget({ app
|
|
|
29
29
|
const requestedAt = approval.requestedAt
|
|
30
30
|
? timestampDate(approval.requestedAt)
|
|
31
31
|
: null;
|
|
32
|
-
return (_jsx("li", { className: "rounded-lg border border-border px-3 py-2.5", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium text-foreground", children: approval.workflowName || approval.executionId }), _jsxs("p", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: ["Task: ", approval.taskName, requestedAt && (_jsxs(_Fragment, { children: [" \u00B7 ", formatTimeAgo(requestedAt)] }))] })] }), onReviewClick && (_jsx("button", { type: "button", onClick: () => onReviewClick(approval.executionId), className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Review" }))] }) }, `${approval.executionId}-${approval.taskName}`));
|
|
32
|
+
return (_jsx("li", { className: "rounded-lg border border-border px-3 py-2.5", children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("p", { className: "truncate text-sm font-medium text-foreground", children: approval.workflowName || approval.executionId }), approval.uiHint && (_jsx("span", { className: "shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: approval.uiHint }))] }), _jsxs("p", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: ["Task: ", approval.taskName, requestedAt && (_jsxs(_Fragment, { children: [" \u00B7 ", formatTimeAgo(requestedAt)] }))] })] }), onReviewClick && (_jsx("button", { type: "button", onClick: () => onReviewClick(approval.executionId), className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Review" }))] }) }, `${approval.executionId}-${approval.taskName}`));
|
|
33
33
|
}) }))] }));
|
|
34
34
|
});
|
|
35
35
|
//# sourceMappingURL=PendingApprovalsWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingApprovalsWidget.js","sourceRoot":"","sources":["../../src/workflow/PendingApprovalsWidget.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAWpC,SAAS,aAAa,CAAC,IAAU;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACjE,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,UAAU,CAAC;IACpC,IAAI,OAAO,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;IAC9D,IAAI,OAAO,GAAG,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACjE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,SAAS,sBAAsB,CAAC,EACzE,SAAS,EACT,UAAU,EACV,SAAS,EACT,aAAa,EACb,SAAS,GACmB;IAC5B,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,eAAY,MAAM,aAC1D,eAAK,SAAS,EAAC,mCAAmC,aAChD,cAAK,SAAS,EAAC,yCAAyC,GAAG,EAC3D,cAAK,SAAS,EAAC,wCAAwC,GAAG,IACtD,EACL,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACnC,cAEE,SAAS,EAAC,gEAAgE,IADrE,CAAC,CAEN,CACH,CAAC,IACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,aACxC,eAAK,SAAS,EAAC,mCAAmC,aAChD,aAAI,SAAS,EAAC,uCAAuC,kCAEhD,EACJ,UAAU,GAAG,CAAC,IAAI,CACjB,eAAM,SAAS,EAAC,yEAAyE,YACtF,UAAU,GACN,CACR,IACG,EAEL,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACxB,YAAG,SAAS,EAAC,gDAAgD,qCAEzD,CACL,CAAC,CAAC,CAAC,CACF,aAAI,SAAS,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,YAClC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;oBAC1B,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW;wBACtC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;wBACrC,CAAC,CAAC,IAAI,CAAC;oBAET,OAAO,CACL,aAEE,SAAS,EAAC,6CAA6C,YAEvD,eAAK,SAAS,EAAC,wCAAwC,aACrD,eAAK,SAAS,EAAC,gBAAgB,aAC7B,YAAG,SAAS,EAAC,8CAA8C,YACxD,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,WAAW,GAC5C,
|
|
1
|
+
{"version":3,"file":"PendingApprovalsWidget.js","sourceRoot":"","sources":["../../src/workflow/PendingApprovalsWidget.tsx"],"names":[],"mappings":"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"}
|