@burdenoff/fluidgrids-fe-sdk 2026.1111.22 → 2026.1111.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/{AiStudioPage-DIfF4LhQ.js → AiStudioPage-CLrMe8v8.js} +1 -1
  2. package/dist/{ApprovalInboxPage-dbBDSJ-f.js → ApprovalInboxPage-D8vhnloO.js} +1 -1
  3. package/dist/{ExecutionDetailPage-DYLWtHRG.js → ExecutionDetailPage-DmICvIpH.js} +7 -2
  4. package/dist/{HumanRequestCard-D-ooVPDn.js → HumanRequestCard-mOhr-Ic9.js} +239 -31
  5. package/dist/{TemplatePreview-t3251W_n.js → TemplatePreview-Bb2f4enx.js} +1 -1
  6. package/dist/{WorkflowBuilder-DdvSGsxr.js → WorkflowBuilder-CabLzwS5.js} +1 -1
  7. package/dist/{WorkflowBuilder-C-hgvww2.js → WorkflowBuilder-TgKSGNlu.js} +1 -1
  8. package/dist/{agentTools-BjhxAdlt.js → agentTools-BU5crBtm.js} +1 -1
  9. package/dist/{canvasNoteTools-CZEFFhRp.js → canvasNoteTools-DeSl4BAF.js} +1 -1
  10. package/dist/executions.js +1 -1
  11. package/dist/{flow-builder-CE8cNXNi.js → flow-builder-xokwiaT1.js} +5 -5
  12. package/dist/{index-7eJQwsNS.d.ts → index-D507rsk2.d.ts} +17 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +10 -10
  15. package/dist/{node-detail-view-BoR0vYZ3.js → node-detail-view-DGrDIjS-.js} +2 -2
  16. package/dist/{node-editor-tab-body-DxMbKspp.js → node-editor-tab-body-eOsmnUvx.js} +1 -1
  17. package/dist/observability.d.ts +1 -1
  18. package/dist/{quick-config-panel-DXvLJdeW.js → quick-config-panel-Bc7isfv0.js} +2 -2
  19. package/dist/{schedule-panel-CY6S4JMd.js → schedule-panel-BnqaUnnz.js} +148 -2
  20. package/dist/{sidebar-node-editor-FU3fvOjh.js → sidebar-node-editor-CdlEDGKh.js} +2 -2
  21. package/dist/templates.js +1 -1
  22. package/dist/workflow-builder.js +2 -2
  23. package/dist/workflow-pages.d.ts +1 -1
  24. package/dist/workflow-pages.js +5 -5
  25. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { t as ThemeProvider } from "./theme-provider-B3mIuz2d.js";
4
- import { a as useAiWorkflowTemplates, i as enrichDraftNodes, n as AI_DRAFT_MAX_PROMPT_LENGTH, r as draftToCanvas, t as FlowBuilderPage } from "./flow-builder-CE8cNXNi.js";
4
+ import { a as useAiWorkflowTemplates, i as enrichDraftNodes, n as AI_DRAFT_MAX_PROMPT_LENGTH, r as draftToCanvas, t as FlowBuilderPage } from "./flow-builder-xokwiaT1.js";
5
5
  import { Ot as builderPathOf, kt as classifyCreateRefusal, u as ensureNodeTypesLoaded, z as buildOperationSeed } from "./FluidGridsProvider-CWyQ8ziM.js";
6
6
  import { _ as throwIfResultError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
7
7
  import { a as notifyFluidGridsAiCreditsUpdated, t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
4
- import { r as usePendingHumanRequests, t as HumanRequestCard } from "./HumanRequestCard-D-ooVPDn.js";
4
+ import { i as usePendingHumanRequests, t as HumanRequestCard } from "./HumanRequestCard-mOhr-Ic9.js";
5
5
  import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
6
6
  import { _ as throwIfResultError, h as isForbiddenError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
7
7
  import { i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
@@ -11,7 +11,7 @@ import { _ as throwIfResultError, m as describeOperationError, n as getFluidGrid
11
11
  import { Yr as WorkflowRunHierarchyDocument, dn as RunAiUsageDocument, o as AgentStepUpdatedDocument, s as AgentTraceDocument, ut as ExplainRunDocument, xn as RunLogsDocument, yn as RunLogAppendedDocument } from "./graphql-operation-types-kX-MMjgW.js";
12
12
  import { i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
13
13
  import { t as EntityTagPicker } from "./EntityTagPicker-BqRNsLCQ.js";
14
- import { a as useRunHumanRequests, n as channelLabel, o as useRecipientLabel } from "./HumanRequestCard-D-ooVPDn.js";
14
+ import { n as channelLabel, o as useRunHumanRequests, r as ApprovalApprovers, s as useRecipientLabel } from "./HumanRequestCard-mOhr-Ic9.js";
15
15
  import { a as encodedPayloadBytes, f as EffectBadge, h as useRunPolicyDecisions, i as decodePayloads, l as formatBudgetUsd, n as checkCodecServerUrl, o as isEncodedPayload, r as containsEncodedPayload, t as CodecServerError } from "./codec-client-CJ95Rmqo.js";
16
16
  import { n as useWorkflowProposal, o as canSaveRunAsTestCase, r as saveRunAsTestCase, t as WorkflowProposalCard } from "./WorkflowProposalCard-qrEeNyYG.js";
17
17
  import { t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
@@ -1355,7 +1355,8 @@ function answerRows(request, tr) {
1355
1355
  // src/modules/human-response/components/RunHumanRequests.tsx
1356
1356
  /**
1357
1357
  * Run detail: the asks a "Send and Wait for Response" node made (BOFF-7651) — who was asked, by
1358
- * which channel, until when, and what came back. Renders nothing for runs that asked nobody.
1358
+ * which channel, until when, and what came back. An approval lists its approvers with each one's
1359
+ * answer and comment (BOFF-9956). Renders nothing for runs that asked nobody.
1359
1360
  */
1360
1361
  function outcome(request, tr) {
1361
1362
  const when = request.respondedAt ? new Date(request.respondedAt).toLocaleString() : "";
@@ -1405,6 +1406,10 @@ function RunHumanRequestRow({ request }) {
1405
1406
  outcome(request, tr)
1406
1407
  ]
1407
1408
  }),
1409
+ (request.approvals?.length ?? 0) > 0 && /* @__PURE__ */ jsx("div", {
1410
+ className: "mt-1 w-full sm:mt-0",
1411
+ children: /* @__PURE__ */ jsx(ApprovalApprovers, { request })
1412
+ }),
1408
1413
  answer.length > 0 && /* @__PURE__ */ jsx("dl", {
1409
1414
  className: "mt-1 w-full space-y-0.5 text-xs sm:mt-0",
1410
1415
  "data-testid": "run-human-answer",
@@ -3,7 +3,7 @@ import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
3
3
  import { _ as throwIfResultError, n as getFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  import { Icon } from "@iconify/react";
6
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { toast } from "sonner";
8
8
  import { gql } from "@apollo/client";
9
9
  import { Badge, Card, CardContent, CardHeader, Input, Label, Textarea } from "@burdenoff/fe-libs/ui";
@@ -90,6 +90,19 @@ const HUMAN_REQUEST_FIELDS = gql`
90
90
  respondedAt
91
91
  createdAt
92
92
  canRespond
93
+ quorum
94
+ requiredApprovals
95
+ myDecision
96
+ approvals {
97
+ id
98
+ requestId
99
+ approverId
100
+ assignedVia
101
+ assignedFrom
102
+ decision
103
+ comment
104
+ answeredAt
105
+ }
93
106
  }
94
107
  `;
95
108
  const GET_PENDING_HUMAN_REQUESTS = gql`
@@ -223,7 +236,11 @@ function useRunHumanRequests(runId, options) {
223
236
  refetch: fetchData
224
237
  };
225
238
  }
226
- /** Answer an in-app ask. Resolves to the decision; throws with the service's reason. */
239
+ /**
240
+ * Answer an in-app ask. Resolves to the decision the run continued with, or null when the answer
241
+ * was recorded and an approval quorum still needs more answers (BOFF-9956). Throws with the
242
+ * service's reason.
243
+ */
227
244
  function useRespondToHumanRequest() {
228
245
  const [loading, setLoading] = useState(false);
229
246
  return {
@@ -247,11 +264,163 @@ function useRespondToHumanRequest() {
247
264
  };
248
265
  }
249
266
 
267
+ // src/modules/human-response/utils/approval-progress.ts
268
+ function approvalCounts(approvals) {
269
+ let approved = 0;
270
+ let rejected = 0;
271
+ for (const approval of approvals) if (approval.decision === "APPROVED") approved++;
272
+ else if (approval.decision === "REJECTED") rejected++;
273
+ return {
274
+ approved,
275
+ rejected,
276
+ total: approvals.length
277
+ };
278
+ }
279
+ /** True for an approval that counts several answers (all, or N of them). */
280
+ function hasQuorum(request) {
281
+ return request.responseType === "APPROVAL" && (request.quorum === "all" || request.quorum === "count");
282
+ }
283
+ /** "1 of 2 approvals · 1 declined", or null for an ask without a quorum. */
284
+ function approvalProgressText(request, tr) {
285
+ if (!hasQuorum(request)) return null;
286
+ const counts = approvalCounts(request.approvals ?? []);
287
+ const required = request.requiredApprovals ?? counts.total;
288
+ const progress = request.quorum === "all" ? tr("fluidgrids.humanResponse.approvalProgressAll", "{{approved}} of {{required}} approvals (everyone must approve)", {
289
+ approved: counts.approved,
290
+ required
291
+ }) : tr("fluidgrids.humanResponse.approvalProgressCount", "{{approved}} of {{required}} approvals needed, from {{total}} approvers", {
292
+ approved: counts.approved,
293
+ required,
294
+ total: counts.total
295
+ });
296
+ if (counts.rejected === 0) return progress;
297
+ return `${progress} · ${tr("fluidgrids.humanResponse.approvalDeclinedCount", "{{count}} declined", { count: counts.rejected })}`;
298
+ }
299
+ /** An approver's answer in words. A missing answer reads differently once the ask closed. */
300
+ function approverDecisionLabel(decision, askOpen, tr) {
301
+ if (decision === "APPROVED") return tr("fluidgrids.humanResponse.approverApproved", "Approved");
302
+ if (decision === "REJECTED") return tr("fluidgrids.humanResponse.approverDeclined", "Declined");
303
+ return askOpen ? tr("fluidgrids.humanResponse.approverWaiting", "Not answered yet") : tr("fluidgrids.humanResponse.approverNoAnswer", "Did not answer");
304
+ }
305
+ /** The toast after an in-app answer: the run continued, or the answer waits for the others. */
306
+ function answeredToastText(decision, tr) {
307
+ if (decision === null) return tr("fluidgrids.humanResponse.approvalRecordedToast", "Your answer is recorded. The workflow continues once enough approvers have answered.");
308
+ return decision === "declined" ? tr("fluidgrids.humanResponse.declinedToast", "Declined — the workflow is continuing") : tr("fluidgrids.humanResponse.answeredToast", "Response sent — the workflow is continuing");
309
+ }
310
+ /**
311
+ * Why the caller cannot answer this ask here: they already answered it, it went to someone else,
312
+ * or it is answered through the link in its message.
313
+ */
314
+ function notAnswerableText(request, channelName, tr) {
315
+ if (request.myDecision === "APPROVED") return tr("fluidgrids.humanResponse.youApproved", "You approved this. It is waiting for the other approvers.");
316
+ if (request.myDecision === "REJECTED") return tr("fluidgrids.humanResponse.youDeclined", "You declined this. It is waiting for the other approvers.");
317
+ return request.channel === "IN_APP" ? tr("fluidgrids.humanResponse.notForYou", "This request was sent to someone else.") : tr("fluidgrids.humanResponse.answerByLink", "Answered through the link in the {{channel}} message.", { channel: channelName });
318
+ }
319
+
320
+ // src/modules/human-response/hooks/useApproverNames.ts
321
+ /**
322
+ * Names for the approvers of an approval (BOFF-9956). Approvers are stored as user ids; they are
323
+ * resolved through the shared actor-profile cache, the same lookup the run page uses for
324
+ * "triggered by" and `useRecipientLabel` uses for who an ask went to.
325
+ */
326
+ /** Display name per user id; ids the profile service does not know get the shared fallback. */
327
+ function useApproverNames(userIds) {
328
+ const idsKey = [...new Set(userIds)].join(",");
329
+ const inputs = useMemo(() => idsKey ? idsKey.split(",").map((actorId) => ({
330
+ actorId,
331
+ actorType: "user"
332
+ })) : [], [idsKey]);
333
+ const config = getFluidGridsConfig();
334
+ const profiles = useActorProfiles(inputs, {
335
+ workspaceId: config.workspaceId || void 0,
336
+ orgId: config.organizationId,
337
+ authToken: config.authToken
338
+ });
339
+ return useMemo(() => {
340
+ const names = {};
341
+ for (const input of inputs) names[input.actorId] = profiles[input.actorId]?.displayName ?? buildActorFallback({
342
+ actorId: input.actorId,
343
+ actorType: "user"
344
+ }).displayName;
345
+ return names;
346
+ }, [inputs, profiles]);
347
+ }
348
+
349
+ // src/modules/human-response/components/ApprovalApprovers.tsx
350
+ /**
351
+ * The approvers of an in-app approval (BOFF-9956): where the quorum stands, and who was asked, how
352
+ * (by name or through a role), what each answered, when, and their comment. Shown on the run page
353
+ * and in the Approvals inbox. Renders nothing for an ask that named no approvers and has no answer.
354
+ */
355
+ const DECISION_ICON = {
356
+ APPROVED: "mdi:check-circle-outline",
357
+ REJECTED: "mdi:close-circle-outline",
358
+ PENDING: "mdi:clock-outline"
359
+ };
360
+ const DECISION_TONE = {
361
+ APPROVED: "text-status-success-text",
362
+ REJECTED: "text-status-error-text",
363
+ PENDING: "text-text-secondary"
364
+ };
365
+ /** "1 of 2 approvals needed, from 3 approvers"; nothing for an ask without a quorum. */
366
+ function ApprovalProgress({ request }) {
367
+ const tr = useTr();
368
+ const text = approvalProgressText(request, tr);
369
+ if (!text) return null;
370
+ return /* @__PURE__ */ jsx("p", {
371
+ className: "text-xs font-medium text-text-primary",
372
+ "data-testid": "approval-progress",
373
+ children: text
374
+ });
375
+ }
376
+ function ApprovalApprovers({ request }) {
377
+ const tr = useTr();
378
+ const approvals = request.approvals ?? [];
379
+ const names = useApproverNames(approvals.map((approval) => approval.approverId));
380
+ if (approvals.length === 0) return null;
381
+ const askOpen = request.status === "PENDING";
382
+ return /* @__PURE__ */ jsxs("div", {
383
+ className: "w-full space-y-1.5",
384
+ "data-testid": "approval-approvers",
385
+ children: [/* @__PURE__ */ jsx(ApprovalProgress, { request }), /* @__PURE__ */ jsx("ul", {
386
+ className: "space-y-1",
387
+ children: approvals.map((approval) => /* @__PURE__ */ jsxs("li", {
388
+ className: "flex flex-col gap-0.5 text-xs sm:flex-row sm:flex-wrap sm:items-baseline sm:gap-x-2",
389
+ "data-testid": "approval-approver",
390
+ "data-decision": approval.decision,
391
+ children: [
392
+ /* @__PURE__ */ jsxs("span", {
393
+ className: "flex items-center gap-1 font-medium text-text-primary",
394
+ children: [/* @__PURE__ */ jsx(Icon, {
395
+ icon: DECISION_ICON[approval.decision],
396
+ width: 14,
397
+ className: DECISION_TONE[approval.decision]
398
+ }), names[approval.approverId] ?? approval.approverId]
399
+ }),
400
+ /* @__PURE__ */ jsxs("span", {
401
+ className: "text-text-secondary",
402
+ children: [
403
+ approverDecisionLabel(approval.decision, askOpen, tr),
404
+ approval.answeredAt && ` · ${new Date(approval.answeredAt).toLocaleString()}`,
405
+ approval.assignedFrom && ` · ${tr("fluidgrids.humanResponse.approverViaRole", "asked as {{role}}", { role: approval.assignedFrom })}`
406
+ ]
407
+ }),
408
+ approval.comment && /* @__PURE__ */ jsx("q", {
409
+ className: "w-full whitespace-pre-wrap break-words text-text-primary",
410
+ children: approval.comment
411
+ })
412
+ ]
413
+ }, approval.id))
414
+ })]
415
+ });
416
+ }
417
+
250
418
  // src/modules/human-response/components/HumanRequestCard.tsx
251
419
  /**
252
420
  * One "Send and Wait for Response" ask in the in-app inbox (BOFF-7651): what was asked, of whom,
253
421
  * through which channel and until when — and, when the caller may answer it in-app, the controls
254
- * to do so (Approve / Decline, a text box, or the custom form).
422
+ * to do so (Approve / Decline, a text box, or the custom form). An approval with several approvers
423
+ * (BOFF-9956) also shows where its quorum stands and takes an optional comment with the answer.
255
424
  */
256
425
  const CHANNEL_ICON = {
257
426
  EMAIL: "mdi:email-outline",
@@ -315,6 +484,62 @@ function FormControls({ requestId, fields, values, onChange }) {
315
484
  })
316
485
  });
317
486
  }
487
+ /** Approve / Decline, with an optional comment that is recorded with the answer (BOFF-9956). */
488
+ function ApprovalControls({ request, loading, onAnswer }) {
489
+ const tr = useTr();
490
+ const [comment, setComment] = useState("");
491
+ const note = comment.trim() ? { comment: comment.trim() } : {};
492
+ const commentId = `human-comment-${request.id}`;
493
+ return /* @__PURE__ */ jsxs("div", {
494
+ className: "space-y-2 pt-1",
495
+ children: [/* @__PURE__ */ jsxs("div", {
496
+ className: "space-y-1.5",
497
+ children: [/* @__PURE__ */ jsx(Label, {
498
+ htmlFor: commentId,
499
+ children: tr("fluidgrids.humanResponse.commentLabel", "Comment (optional)")
500
+ }), /* @__PURE__ */ jsx(Textarea, {
501
+ id: commentId,
502
+ value: comment,
503
+ maxLength: 2e3,
504
+ placeholder: tr("fluidgrids.humanResponse.commentPlaceholder", "Why you approve or decline. It is saved with your answer on the run."),
505
+ onChange: (event) => setComment(event.target.value),
506
+ "data-testid": "human-request-comment"
507
+ })]
508
+ }), /* @__PURE__ */ jsxs("div", {
509
+ className: "flex flex-wrap items-center gap-2",
510
+ children: [/* @__PURE__ */ jsxs(Button$1, {
511
+ "data-agent": "confirm",
512
+ size: "sm",
513
+ disabled: loading,
514
+ onClick: () => onAnswer({
515
+ action: "approve",
516
+ ...note
517
+ }),
518
+ "data-testid": "human-request-approve",
519
+ children: [/* @__PURE__ */ jsx(Icon, {
520
+ icon: "mdi:check",
521
+ className: "mr-1.5",
522
+ width: 16
523
+ }), request.approveLabel]
524
+ }), request.approvalType === "double" && /* @__PURE__ */ jsxs(Button$1, {
525
+ "data-agent": "confirm",
526
+ size: "sm",
527
+ variant: "destructive",
528
+ disabled: loading,
529
+ onClick: () => onAnswer({
530
+ action: "decline",
531
+ ...note
532
+ }),
533
+ "data-testid": "human-request-decline",
534
+ children: [/* @__PURE__ */ jsx(Icon, {
535
+ icon: "mdi:close",
536
+ className: "mr-1.5",
537
+ width: 16
538
+ }), request.declineLabel]
539
+ })]
540
+ })]
541
+ });
542
+ }
318
543
  function HumanRequestCard({ request, onAnswered }) {
319
544
  const tr = useTr();
320
545
  const { respond, loading } = useRespondToHumanRequest();
@@ -323,7 +548,7 @@ function HumanRequestCard({ request, onAnswered }) {
323
548
  const submit = async (answer) => {
324
549
  try {
325
550
  const decision = await respond(request.id, answer);
326
- toast.success(decision === "declined" ? tr("fluidgrids.humanResponse.declinedToast", "Declined — the workflow is continuing") : tr("fluidgrids.humanResponse.answeredToast", "Response sent — the workflow is continuing"));
551
+ toast.success(answeredToastText(decision, tr));
327
552
  onAnswered();
328
553
  } catch (err) {
329
554
  toast.error(err instanceof Error ? err.message : tr("fluidgrids.humanResponse.failed", "Your response could not be recorded"));
@@ -369,9 +594,10 @@ function HumanRequestCard({ request, onAnswered }) {
369
594
  className: "whitespace-pre-wrap break-words text-sm text-text-primary",
370
595
  children: request.message
371
596
  }),
597
+ /* @__PURE__ */ jsx(ApprovalApprovers, { request }),
372
598
  !request.canRespond && /* @__PURE__ */ jsx("p", {
373
599
  className: "text-xs text-text-secondary",
374
- children: request.channel === "IN_APP" ? tr("fluidgrids.humanResponse.notForYou", "This request was sent to someone else.") : tr("fluidgrids.humanResponse.answerByLink", "Answered through the link in the {{channel}} message.", { channel: channelLabel(request.channel, tr) })
600
+ children: notAnswerableText(request, channelLabel(request.channel, tr), tr)
375
601
  }),
376
602
  request.canRespond && request.responseType === "FREE_TEXT" && /* @__PURE__ */ jsxs("div", {
377
603
  className: "space-y-1.5",
@@ -393,32 +619,14 @@ function HumanRequestCard({ request, onAnswered }) {
393
619
  [name]: value
394
620
  }))
395
621
  }),
396
- request.canRespond && /* @__PURE__ */ jsx("div", {
622
+ request.canRespond && request.responseType === "APPROVAL" && /* @__PURE__ */ jsx(ApprovalControls, {
623
+ request,
624
+ loading,
625
+ onAnswer: submit
626
+ }),
627
+ request.canRespond && request.responseType !== "APPROVAL" && /* @__PURE__ */ jsx("div", {
397
628
  className: "flex flex-wrap items-center gap-2 pt-1",
398
- children: request.responseType === "APPROVAL" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button$1, {
399
- "data-agent": "confirm",
400
- size: "sm",
401
- disabled: loading,
402
- onClick: () => submit({ action: "approve" }),
403
- "data-testid": "human-request-approve",
404
- children: [/* @__PURE__ */ jsx(Icon, {
405
- icon: "mdi:check",
406
- className: "mr-1.5",
407
- width: 16
408
- }), request.approveLabel]
409
- }), request.approvalType === "double" && /* @__PURE__ */ jsxs(Button$1, {
410
- "data-agent": "confirm",
411
- size: "sm",
412
- variant: "destructive",
413
- disabled: loading,
414
- onClick: () => submit({ action: "decline" }),
415
- "data-testid": "human-request-decline",
416
- children: [/* @__PURE__ */ jsx(Icon, {
417
- icon: "mdi:close",
418
- className: "mr-1.5",
419
- width: 16
420
- }), request.declineLabel]
421
- })] }) : /* @__PURE__ */ jsxs(Button$1, {
629
+ children: /* @__PURE__ */ jsxs(Button$1, {
422
630
  "data-agent": "confirm",
423
631
  size: "sm",
424
632
  disabled: loading || missingRequired || emptyText,
@@ -436,4 +644,4 @@ function HumanRequestCard({ request, onAnswered }) {
436
644
  });
437
645
  }
438
646
 
439
- export { useRunHumanRequests as a, useRespondToHumanRequest as i, channelLabel as n, useRecipientLabel as o, usePendingHumanRequests as r, HumanRequestCard as t };
647
+ export { useRespondToHumanRequest as a, usePendingHumanRequests as i, channelLabel as n, useRunHumanRequests as o, ApprovalApprovers as r, useRecipientLabel as s, HumanRequestCard as t };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { t as ThemeProvider } from "./theme-provider-B3mIuz2d.js";
4
- import { t as FlowBuilderPage } from "./flow-builder-CE8cNXNi.js";
4
+ import { t as FlowBuilderPage } from "./flow-builder-xokwiaT1.js";
5
5
  import { a as useTemplateImportFlow, l as useImportWorkflowTemplate } from "./TemplateCard-6W2q2wiK.js";
6
6
  import { t as QuotaExhaustedDialog } from "./QuotaExhaustedDialog-ChBoJnRo.js";
7
7
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { d as releaseLeaseKeptForReload, f as useWorkflowReloadStore, t as FlowBuilderPage } from "./flow-builder-CE8cNXNi.js";
3
+ import { d as releaseLeaseKeptForReload, f as useWorkflowReloadStore, t as FlowBuilderPage } from "./flow-builder-xokwiaT1.js";
4
4
  import { n as registerLiveCanvasHandler } from "./liveCanvas-D5ytXDp8.js";
5
5
  import { _ as throwIfResultError, n as getFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
6
6
  import { Er as WorkflowDefinitionDocument } from "./graphql-operation-types-kX-MMjgW.js";
@@ -6,7 +6,7 @@ import { d as Input } from "./dialog-Cjb4lB4D.js";
6
6
  import { b as Card } from "./TemplateCard-6W2q2wiK.js";
7
7
  import { t as cn } from "./cn-QqidFIgr.js";
8
8
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
9
- import { _ as useNodeSelectedEvent, v as FluidGridsMark } from "./flow-builder-CE8cNXNi.js";
9
+ import { _ as useNodeSelectedEvent, v as FluidGridsMark } from "./flow-builder-xokwiaT1.js";
10
10
  import { useCallback, useEffect, useRef, useState } from "react";
11
11
  import { Background, Controls, Handle, MarkerType, MiniMap, Position, ReactFlow, ReactFlowProvider, useReactFlow } from "@xyflow/react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -2,7 +2,7 @@
2
2
  import { B as createConfiguredNode, Nt as useSaveStatusStore, Q as selectedOperationOf, R as SENSITIVE_PARAM, Y as nodeTitle, Z as paramsFromOperation, _ as registryNodeData, f as isNodeConfigsInitialized, gt as autosaveAllowedFor, o as NODE_CONFIGS, q as fixedParameterState, wt as isFlowComplete } from "./FluidGridsProvider-CWyQ8ziM.js";
3
3
  import { E as resolveOutputHandles, H as useFlowStore, T as resolveInputHandles } from "./node-operation-selection--oy-yEq_.js";
4
4
  import { j as isNodeTypeHiddenFromPalette } from "./TemplateCard-6W2q2wiK.js";
5
- import { g as computeAttachPosition } from "./flow-builder-CE8cNXNi.js";
5
+ import { g as computeAttachPosition } from "./flow-builder-xokwiaT1.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -2,7 +2,7 @@
2
2
  import { n as useAgentMarksStore, r as toPlainText } from "./marksStore-DGCXjTmZ.js";
3
3
  import { Q as selectedOperationOf, Y as nodeTitle, Z as paramsFromOperation } from "./FluidGridsProvider-CWyQ8ziM.js";
4
4
  import { H as useFlowStore } from "./node-operation-selection--oy-yEq_.js";
5
- import { p as notesKeyOf } from "./flow-builder-CE8cNXNi.js";
5
+ import { p as notesKeyOf } from "./flow-builder-xokwiaT1.js";
6
6
  import { AgentToolError, defineAgentTool } from "@burdenoff/fe-libs/shared/agent-tools";
7
7
  import { z } from "zod";
8
8
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
2
  import { t as ExecutionsListPage } from "./ExecutionsListPage-D63aCXJH.js";
3
- import { t as ExecutionDetailPage } from "./ExecutionDetailPage-DYLWtHRG.js";
3
+ import { t as ExecutionDetailPage } from "./ExecutionDetailPage-DmICvIpH.js";
4
4
 
5
5
  export { ExecutionDetailPage, ExecutionsListPage };
@@ -260,9 +260,9 @@ function IntroductionPropertyPanel() {
260
260
  * waiting for its chunk and React's Suspense throttle (about 0.5 s on first
261
261
  * open even with the chunk cached).
262
262
  */
263
- const NodeDetailViewOverlay = preloadableLazy(() => import("./node-detail-view-BoR0vYZ3.js").then((m) => m.NodeDetailView), "NodeDetailView");
264
- const QuickConfigPanelOverlay = preloadableLazy(() => import("./quick-config-panel-DXvLJdeW.js").then((m) => m.QuickConfigPanel), "QuickConfigPanel");
265
- const SidebarNodeEditorOverlay = preloadableLazy(() => import("./sidebar-node-editor-FU3fvOjh.js").then((m) => m.SidebarNodeEditor), "SidebarNodeEditor");
263
+ const NodeDetailViewOverlay = preloadableLazy(() => import("./node-detail-view-DGrDIjS-.js").then((m) => m.NodeDetailView), "NodeDetailView");
264
+ const QuickConfigPanelOverlay = preloadableLazy(() => import("./quick-config-panel-Bc7isfv0.js").then((m) => m.QuickConfigPanel), "QuickConfigPanel");
265
+ const SidebarNodeEditorOverlay = preloadableLazy(() => import("./sidebar-node-editor-CdlEDGKh.js").then((m) => m.SidebarNodeEditor), "SidebarNodeEditor");
266
266
  const WorkflowShareDialogOverlay = preloadableLazy(() => import("./WorkflowShareDialog-mbIwRQUY.js").then((n) => n.n).then((m) => m.WorkflowShareDialog), "WorkflowShareDialog");
267
267
  const ConnectionFormDialogOverlay = preloadableLazy(() => import("./ConnectionFormDialog-Bg9_OdHX.js").then((n) => n.n).then((m) => m.ConnectionFormDialog), "ConnectionFormDialog");
268
268
  /**
@@ -17443,7 +17443,7 @@ const FlowBuilder = ({ readOnly = false }) => {
17443
17443
  paletteGatesRef.current = paletteGates;
17444
17444
  }, [paletteGates]);
17445
17445
  useWarmBuilderOverlays();
17446
- useLazyAgentTools(() => import("./agentTools-BjhxAdlt.js").then(({ createBuilderAgentTools }) => createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current })), !editingBlocked, "Agent tools");
17446
+ useLazyAgentTools(() => import("./agentTools-BU5crBtm.js").then(({ createBuilderAgentTools }) => createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current })), !editingBlocked, "Agent tools");
17447
17447
  const [nodes, edges, onNodesChange, onEdgesChange, onConnect] = useFlowStore(useShallow((s) => {
17448
17448
  return [
17449
17449
  s.workflow.nodes,
@@ -17485,7 +17485,7 @@ const FlowBuilder = ({ readOnly = false }) => {
17485
17485
  useEffect(() => {
17486
17486
  fitViewRef.current = fitView;
17487
17487
  }, [fitView]);
17488
- useLazyAgentTools(() => import("./canvasNoteTools-CZEFFhRp.js").then(({ createCanvasNoteTools }) => createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
17488
+ useLazyAgentTools(() => import("./canvasNoteTools-DeSl4BAF.js").then(({ createCanvasNoteTools }) => createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
17489
17489
  nodes: [{ id: nodeId }],
17490
17490
  duration: 400,
17491
17491
  padding: .6,