@arbidocs/blocks 0.4.136 → 0.4.138

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 (110) hide show
  1. package/dist/agents/index.cjs +384 -353
  2. package/dist/agents/index.js +8 -9
  3. package/dist/agents/skills/SkillLibraryDetail.d.ts +26 -0
  4. package/dist/agents/skills/index.d.ts +2 -0
  5. package/dist/agents/skills/skillLibrary.d.ts +46 -0
  6. package/dist/agents/workflows/WorkflowDocumentsField.d.ts +42 -5
  7. package/dist/agents/workflows/index.d.ts +2 -2
  8. package/dist/agents/workflows/workspaceScope.d.ts +12 -0
  9. package/dist/calls/index.cjs +3 -3
  10. package/dist/calls/index.js +2 -2
  11. package/dist/chunk-2KUCVSFF.js +51 -0
  12. package/dist/chunk-2KUCVSFF.js.map +1 -0
  13. package/dist/{chunk-LIHRGG4M.cjs → chunk-3BI6EWKP.cjs} +2022 -89
  14. package/dist/chunk-3BI6EWKP.cjs.map +1 -0
  15. package/dist/{chunk-3FFRYZKV.js → chunk-3RJN2VTL.js} +36 -6
  16. package/dist/chunk-3RJN2VTL.js.map +1 -0
  17. package/dist/{chunk-OR4CO743.cjs → chunk-AAFX64WD.cjs} +3 -3
  18. package/dist/{chunk-OR4CO743.cjs.map → chunk-AAFX64WD.cjs.map} +1 -1
  19. package/dist/chunk-CQCC7LE7.js +210 -0
  20. package/dist/chunk-CQCC7LE7.js.map +1 -0
  21. package/dist/{chunk-ZAVEMQ37.cjs → chunk-DWZXSPMB.cjs} +3 -3
  22. package/dist/{chunk-ZAVEMQ37.cjs.map → chunk-DWZXSPMB.cjs.map} +1 -1
  23. package/dist/{chunk-VIU7AMHF.js → chunk-E5OYHJMU.js} +3 -3
  24. package/dist/{chunk-VIU7AMHF.js.map → chunk-E5OYHJMU.js.map} +1 -1
  25. package/dist/{chunk-RUB62J2X.js → chunk-ED4HHTIA.js} +3 -3
  26. package/dist/{chunk-RUB62J2X.js.map → chunk-ED4HHTIA.js.map} +1 -1
  27. package/dist/{chunk-5HFPRI3W.js → chunk-FUTMU4FR.js} +3 -3
  28. package/dist/{chunk-5HFPRI3W.js.map → chunk-FUTMU4FR.js.map} +1 -1
  29. package/dist/{chunk-C44D4WFG.cjs → chunk-HLB4DIYV.cjs} +3 -3
  30. package/dist/{chunk-C44D4WFG.cjs.map → chunk-HLB4DIYV.cjs.map} +1 -1
  31. package/dist/{chunk-N55UIXVT.cjs → chunk-JVL7HUIQ.cjs} +5 -5
  32. package/dist/{chunk-N55UIXVT.cjs.map → chunk-JVL7HUIQ.cjs.map} +1 -1
  33. package/dist/{chunk-6S5FLMWG.js → chunk-KSHKTOY2.js} +203 -7
  34. package/dist/chunk-KSHKTOY2.js.map +1 -0
  35. package/dist/{chunk-OZLWSNUC.cjs → chunk-MJKBVIV3.cjs} +83 -1650
  36. package/dist/chunk-MJKBVIV3.cjs.map +1 -0
  37. package/dist/{chunk-DJM7DLCT.cjs → chunk-OPTBQSCV.cjs} +3 -3
  38. package/dist/{chunk-DJM7DLCT.cjs.map → chunk-OPTBQSCV.cjs.map} +1 -1
  39. package/dist/{chunk-GFYGYME3.js → chunk-OQ7RUNOC.js} +3 -3
  40. package/dist/{chunk-GFYGYME3.js.map → chunk-OQ7RUNOC.js.map} +1 -1
  41. package/dist/chunk-RFIBUWLF.cjs +232 -0
  42. package/dist/chunk-RFIBUWLF.cjs.map +1 -0
  43. package/dist/{chunk-N26CXGXC.cjs → chunk-RQ2Y7USX.cjs} +201 -3
  44. package/dist/chunk-RQ2Y7USX.cjs.map +1 -0
  45. package/dist/{chunk-LT2X5N37.js → chunk-UBEI6WFJ.js} +58 -1592
  46. package/dist/chunk-UBEI6WFJ.js.map +1 -0
  47. package/dist/chunk-UUHUQHXR.cjs +56 -0
  48. package/dist/chunk-UUHUQHXR.cjs.map +1 -0
  49. package/dist/{chunk-ECERC6TI.js → chunk-VHJHY7TT.js} +3 -3
  50. package/dist/{chunk-ECERC6TI.js.map → chunk-VHJHY7TT.js.map} +1 -1
  51. package/dist/{chunk-44R3CCZT.cjs → chunk-WS5EX7DN.cjs} +37 -6
  52. package/dist/chunk-WS5EX7DN.cjs.map +1 -0
  53. package/dist/{chunk-IQU2GI6V.js → chunk-XVME4XYD.js} +1982 -93
  54. package/dist/chunk-XVME4XYD.js.map +1 -0
  55. package/dist/collaboration/index.cjs +42 -42
  56. package/dist/collaboration/index.js +2 -2
  57. package/dist/connect/docx.cjs +5 -5
  58. package/dist/connect/docx.js +4 -4
  59. package/dist/connect/index.cjs +710 -51
  60. package/dist/connect/index.cjs.map +1 -1
  61. package/dist/connect/index.d.ts +4 -0
  62. package/dist/connect/index.js +655 -20
  63. package/dist/connect/index.js.map +1 -1
  64. package/dist/connection/ConnectedSkillsLibrary.d.ts +26 -0
  65. package/dist/connection/ConnectedWorkflowBuilder.d.ts +1 -1
  66. package/dist/connection/ConnectedWorkflowLauncher.d.ts +35 -0
  67. package/dist/connection/ConnectedWorkflowsTable.d.ts +62 -0
  68. package/dist/connection/workspaceData.d.ts +45 -0
  69. package/dist/facts/index.cjs +2 -2
  70. package/dist/facts/index.js +2 -2
  71. package/dist/files/index.cjs +3 -3
  72. package/dist/files/index.js +3 -3
  73. package/dist/home/index.cjs +22 -23
  74. package/dist/home/index.cjs.map +1 -1
  75. package/dist/home/index.js +4 -5
  76. package/dist/home/index.js.map +1 -1
  77. package/dist/index.cjs +503 -472
  78. package/dist/index.cjs.map +1 -1
  79. package/dist/index.js +12 -13
  80. package/dist/index.js.map +1 -1
  81. package/dist/retrieval/index.cjs +1 -1
  82. package/dist/retrieval/index.js +1 -1
  83. package/dist/settings/index.cjs +49 -49
  84. package/dist/settings/index.js +6 -6
  85. package/dist/shell/index.cjs +2 -2
  86. package/dist/shell/index.js +2 -2
  87. package/dist/studio/index.cjs +2 -2
  88. package/dist/studio/index.js +2 -2
  89. package/dist/workflow-canvas/index.cjs +32 -30
  90. package/dist/workflow-canvas/index.cjs.map +1 -1
  91. package/dist/workflow-canvas/index.js +20 -18
  92. package/dist/workflow-canvas/index.js.map +1 -1
  93. package/node_modules/@arbidocs/coedit/package.json +1 -1
  94. package/package.json +4 -4
  95. package/dist/chunk-3FFRYZKV.js.map +0 -1
  96. package/dist/chunk-44R3CCZT.cjs.map +0 -1
  97. package/dist/chunk-4XJZ6AAY.js +0 -407
  98. package/dist/chunk-4XJZ6AAY.js.map +0 -1
  99. package/dist/chunk-6S5FLMWG.js.map +0 -1
  100. package/dist/chunk-7D737FLE.cjs +0 -43
  101. package/dist/chunk-7D737FLE.cjs.map +0 -1
  102. package/dist/chunk-IQU2GI6V.js.map +0 -1
  103. package/dist/chunk-LIHRGG4M.cjs.map +0 -1
  104. package/dist/chunk-LT2X5N37.js.map +0 -1
  105. package/dist/chunk-N26CXGXC.cjs.map +0 -1
  106. package/dist/chunk-OZLWSNUC.cjs.map +0 -1
  107. package/dist/chunk-S5HZBXZA.cjs +0 -430
  108. package/dist/chunk-S5HZBXZA.cjs.map +0 -1
  109. package/dist/chunk-XD2M6LIG.js +0 -41
  110. package/dist/chunk-XD2M6LIG.js.map +0 -1
@@ -1,12 +1,19 @@
1
1
  "use client";
2
- import { FoldSection } from './chunk-XD2M6LIG.js';
2
+ import { FoldSection, QuickActionDialogHeader, AddFilesFirst, StartTiles, ScopePicker } from './chunk-3RJN2VTL.js';
3
+ import { DocumentPicker, TriStateFilterMenu } from './chunk-KSHKTOY2.js';
4
+ import { EmptyState } from './chunk-P6GHJSLR.js';
3
5
  import { UploadDropzone } from './chunk-EVUEIBNU.js';
4
- import { workflowSteps, canAssign, fieldToFrontmatter, workflowFromFrontmatter, nextAction, validateStep, assigneeName, runFieldsOf, failedUpstream, stepPhase, useElementSize, sendBackTargets, formatDate, shownValue, stepKindOf, stepLevels, waitingDocuments, stepDependencies, reviewInstructions, splitDocRef, isPersonStep, eventWaitingDocuments, waitingForDocumentsText, StepBadgeMark, StepChip, STEP_KIND_ICONS, DEFAULT_STEP_KIND_LABELS, composeStepPrompt, isRunFinished, runFields, isWorkflowScope } from './chunk-Y5KTM3V5.js';
6
+ import { SelectionBar, SelectionBarAction } from './chunk-NUNKSZKU.js';
7
+ import { FilterInput } from './chunk-YU2KUMGE.js';
8
+ import { GridView } from './chunk-N5YEF3LC.js';
9
+ import { TabHeader } from './chunk-4WWM3GQ5.js';
10
+ import { defaultInputs, validateInputs, workflowSteps, canAssign, fieldToFrontmatter, workflowFromFrontmatter, nextAction, validateStep, assigneeName, runFieldsOf, failedUpstream, stepPhase, useElementSize, sendBackTargets, formatDate, shownValue, stepKindOf, stepLevels, waitingDocuments, stepDependencies, reviewInstructions, splitDocRef, isPersonStep, mergeSkillWorkflows, eventWaitingDocuments, waitingForDocumentsText, StepBadgeMark, StepChip, buildLaunch, STEP_KIND_ICONS, DEFAULT_STEP_KIND_LABELS, composeStepPrompt, isRunFinished, runFields, isWorkflowScope } from './chunk-Y5KTM3V5.js';
5
11
  import { TONE_DOT, TONE_SURFACE, TONE_SOLID_BORDER, TONE_BORDER } from './chunk-AUS3KBHJ.js';
6
12
  import { testIdOr, prefixedTestId } from './chunk-7F6CBP3Z.js';
7
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
8
- import { Loader2, UserRound, CornerUpLeft, ShieldCheck, MessageSquare, Check, Hourglass, Clock, PenLine, X, ExternalLink, Mail, Table2, CheckCircle2, ArrowLeft, ArrowRight, RotateCw, GitBranch, AlertTriangle, ChevronLeft, ChevronRight, FileText, ArrowUpRight, BellRing, Flag, RotateCcw, Play, Disc, Phone, UserRoundCog, Sparkles, MessageSquareReply, Square, KeyRound, Lock } from 'lucide-react';
9
- import { cn, Popover, PopoverTrigger, PopoverContent, Button, Textarea, Card, Label, Input, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Switch } from '@arbidocs/react/ui';
13
+ import { initials } from './chunk-OXF2IFXG.js';
14
+ import { useState, useRef, useCallback, useId, useMemo, useEffect } from 'react';
15
+ import { Loader2, Play, FolderPlus, ImageIcon, X, UserRound, CornerUpLeft, ShieldCheck, MessageSquare, Check, Hourglass, Clock, PenLine, ExternalLink, Mail, Table2, CheckCircle2, ArrowLeft, ArrowRight, RotateCw, GitBranch, AlertTriangle, Globe, Wand2, Copy, Pencil, Lock, Download, Trash2, MoreHorizontal, History, Workflow, LayoutGrid, List, ChevronLeft, ChevronRight, FileText, Link2, Bell, MousePointerClick, ArrowUpRight, BellRing, Flag, RotateCcw, Disc, Phone, UserRoundCog, Sparkles, MessageSquareReply, Square, KeyRound } from 'lucide-react';
16
+ import { cn, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Dialog, DialogContent, Button, getEntityColor, Popover, PopoverTrigger, PopoverContent, Textarea, Card, Tooltip, TooltipTrigger, TooltipContent, Input, UserEntity, getEntityColorStyles, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, badgeVariants, VisibilityToggle, Switch } from '@arbidocs/react/ui';
10
17
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
18
  import { unzip, strFromU8 } from 'fflate';
12
19
 
@@ -319,6 +326,610 @@ function SchemaForm({
319
326
  )
320
327
  ] });
321
328
  }
329
+ function useControllableState(value, defaultValue, onChange) {
330
+ const [inner, setInner] = useState(defaultValue);
331
+ const isControlled = value !== void 0;
332
+ const current = isControlled ? value : inner;
333
+ const onChangeRef = useRef(onChange);
334
+ onChangeRef.current = onChange;
335
+ const set = useCallback(
336
+ (next) => {
337
+ if (!isControlled) setInner(next);
338
+ onChangeRef.current?.(next);
339
+ },
340
+ [isControlled]
341
+ );
342
+ return [current, set];
343
+ }
344
+ var DEFAULT_AGENT_SELECT_LABELS = {
345
+ label: "Run as",
346
+ myAssistant: "My assistant"
347
+ };
348
+ var ME = "__me__";
349
+ function AgentSelect({
350
+ agents,
351
+ value,
352
+ defaultValue = "",
353
+ onValueChange,
354
+ hideLabel,
355
+ disabled,
356
+ className,
357
+ labels,
358
+ testId,
359
+ optionTestId
360
+ }) {
361
+ const l = { ...DEFAULT_AGENT_SELECT_LABELS, ...labels };
362
+ const triggerId = useId();
363
+ const [current, setCurrent] = useControllableState(value, defaultValue, onValueChange);
364
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), "data-slot": "agent-select", children: [
365
+ /* @__PURE__ */ jsx(Label, { htmlFor: triggerId, className: hideLabel ? "sr-only" : void 0, children: l.label }),
366
+ /* @__PURE__ */ jsxs(
367
+ Select,
368
+ {
369
+ value: current === "" ? ME : current,
370
+ onValueChange: (v) => setCurrent(v === ME ? "" : v),
371
+ disabled,
372
+ children: [
373
+ /* @__PURE__ */ jsx(
374
+ SelectTrigger,
375
+ {
376
+ id: triggerId,
377
+ "data-testid": testIdOr(testId, { testId: "agent-select" }),
378
+ className: "pointer-coarse:min-h-11",
379
+ children: /* @__PURE__ */ jsx(SelectValue, {})
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
383
+ /* @__PURE__ */ jsx(
384
+ SelectItem,
385
+ {
386
+ value: ME,
387
+ "data-testid": prefixedTestId(optionTestId, "me", {
388
+ testId: "agent-select-option-me"
389
+ }),
390
+ children: l.myAssistant
391
+ }
392
+ ),
393
+ agents.map((agent) => /* @__PURE__ */ jsx(
394
+ SelectItem,
395
+ {
396
+ value: agent.id,
397
+ "data-testid": prefixedTestId(optionTestId, agent.id, {
398
+ testId: `agent-select-option-${agent.id}`
399
+ }),
400
+ children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
401
+ agent.avatar,
402
+ /* @__PURE__ */ jsx("span", { children: agent.label }),
403
+ agent.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: agent.description })
404
+ ] })
405
+ },
406
+ agent.id
407
+ ))
408
+ ] })
409
+ ]
410
+ }
411
+ )
412
+ ] });
413
+ }
414
+ var DEFAULT_WORKFLOW_CONFIG_DIALOG_LABELS = {
415
+ start: "Start",
416
+ starting: "Starting\u2026",
417
+ addFilesReason: "This reads your files, and there is nothing to read yet.",
418
+ agentLabel: "Run as"
419
+ };
420
+ function WorkflowConfigDialog({
421
+ workflow,
422
+ open,
423
+ onOpenChange,
424
+ onLaunch,
425
+ initialInputs,
426
+ inputs: inputsProp,
427
+ onInputsChange,
428
+ scopeSource,
429
+ renderScope,
430
+ renderDocuments,
431
+ renderIcon,
432
+ addFilesAction,
433
+ agents,
434
+ agentId: agentIdProp,
435
+ onAgentChange,
436
+ isLaunching,
437
+ submitLabel,
438
+ fieldPlaceholder,
439
+ renderNotice,
440
+ labels,
441
+ formLabels,
442
+ agentLabels,
443
+ testId,
444
+ startTestId,
445
+ className
446
+ }) {
447
+ const l = { ...DEFAULT_WORKFLOW_CONFIG_DIALOG_LABELS, ...labels };
448
+ const initialKey = JSON.stringify(initialInputs ?? {});
449
+ const seedKey = JSON.stringify({ ...defaultInputs(workflow), ...JSON.parse(initialKey) });
450
+ const seed = useMemo(() => JSON.parse(seedKey), [seedKey]);
451
+ const [inputs, setInputs] = useControllableState(inputsProp, seed, onInputsChange);
452
+ const [agentId, setAgentId] = useControllableState(
453
+ agentIdProp,
454
+ workflow.defaultAgentId ?? "",
455
+ onAgentChange
456
+ );
457
+ useEffect(() => {
458
+ if (open && inputsProp === void 0) setInputs(seed);
459
+ }, [open, seed]);
460
+ const setField = (name, value) => setInputs({ ...inputs, [name]: value });
461
+ const errors = validateInputs(workflow, inputs);
462
+ const valid = Object.keys(errors).length === 0;
463
+ const visibleErrors = Object.fromEntries(
464
+ Object.entries(errors).filter(([, e]) => e !== "required")
465
+ );
466
+ const scopeField = workflow.inputs.find((f) => f.type === "scope");
467
+ const rawScope = scopeField ? inputs[scopeField.name] : void 0;
468
+ const scopeValue = rawScope && typeof rawScope === "object" ? rawScope : { folders: [], labels: [] };
469
+ const matchedCount = scopeSource && scopeField ? scopeSource.countMatching(scopeValue) : null;
470
+ const noFiles = Boolean(workflow.requiresFiles && scopeSource && scopeSource.totalCount === 0);
471
+ const blockedByScope = matchedCount !== null && matchedCount === 0 && Boolean(workflow.requiresFiles);
472
+ const showAgent = workflow.run.kind === "prompt" && workflow.options?.agent !== false && agents !== void 0 && agents.length > 0;
473
+ const launch = (values) => {
474
+ Promise.resolve(onLaunch(buildLaunch(workflow, values, agentId))).catch(() => {
475
+ });
476
+ };
477
+ const submit = () => {
478
+ if (!valid || noFiles || blockedByScope || isLaunching) return;
479
+ launch(inputs);
480
+ };
481
+ const fields = useMemo(
482
+ () => workflow.inputs.map((f) => {
483
+ const placeholder = fieldPlaceholder?.(f, inputs, workflow);
484
+ return placeholder === void 0 ? f : { ...f, placeholder };
485
+ }),
486
+ [workflow, fieldPlaceholder, inputs]
487
+ );
488
+ const id = testIdOr(testId, { testId: "workflow-dialog" });
489
+ const formRef = useRef(null);
490
+ const firstField = workflow.inputs.find((f) => !f.advanced);
491
+ const focusPromptFirst = firstField?.type === "text" || firstField?.type === "longtext";
492
+ const defaultScopeRender = scopeSource ? (_field, value, onChange) => /* @__PURE__ */ jsx(
493
+ ScopePicker,
494
+ {
495
+ folders: scopeSource.folders,
496
+ unfiledCount: scopeSource.unfiledCount,
497
+ labels: scopeSource.labels,
498
+ totalCount: scopeSource.totalCount,
499
+ matchedCount: scopeSource.countMatching(value),
500
+ scope: value,
501
+ onScopeChange: onChange
502
+ }
503
+ ) : void 0;
504
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
505
+ DialogContent,
506
+ {
507
+ presentation: "sheet",
508
+ className: `max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-xl ${className ?? ""}`,
509
+ "data-testid": id,
510
+ "data-workflow": workflow.id,
511
+ onOpenAutoFocus: (event) => {
512
+ if (!focusPromptFirst || !firstField) return;
513
+ const control = formRef.current?.querySelector(
514
+ `[data-testid="${id}-field-${firstField.name}"]`
515
+ );
516
+ if (!control) return;
517
+ event.preventDefault();
518
+ control.focus();
519
+ if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) {
520
+ const end = control.value.length;
521
+ control.setSelectionRange(end, end);
522
+ }
523
+ },
524
+ children: [
525
+ /* @__PURE__ */ jsx(
526
+ QuickActionDialogHeader,
527
+ {
528
+ icon: workflow.icon && renderIcon ? renderIcon(workflow.icon) : null,
529
+ title: workflow.title,
530
+ description: workflow.details ?? workflow.description,
531
+ descriptionTestId: `${id}-description`
532
+ }
533
+ ),
534
+ /* @__PURE__ */ jsx(
535
+ "form",
536
+ {
537
+ ref: formRef,
538
+ className: "mt-2 flex flex-col gap-4",
539
+ onSubmit: (event) => {
540
+ event.preventDefault();
541
+ submit();
542
+ },
543
+ children: noFiles ? /* @__PURE__ */ jsx(
544
+ AddFilesFirst,
545
+ {
546
+ reason: l.addFilesReason,
547
+ action: addFilesAction,
548
+ testId: `${id}-empty-state`
549
+ }
550
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
551
+ /* @__PURE__ */ jsx(
552
+ SchemaForm,
553
+ {
554
+ fields,
555
+ values: inputs,
556
+ onChange: setField,
557
+ errors: visibleErrors,
558
+ renderScope: renderScope ?? defaultScopeRender,
559
+ renderDocuments,
560
+ renderIcon,
561
+ labels: formLabels,
562
+ onEnterSubmit: submit,
563
+ testIdPrefix: `${id}-field`
564
+ }
565
+ ),
566
+ renderNotice?.({ workflow, inputs }),
567
+ /* @__PURE__ */ jsxs(
568
+ "div",
569
+ {
570
+ className: "flex flex-col gap-3 border-t border-border pt-3 sm:flex-row sm:items-end sm:justify-between",
571
+ "data-slot": "workflow-footer",
572
+ children: [
573
+ showAgent ? /* @__PURE__ */ jsx(
574
+ AgentSelect,
575
+ {
576
+ agents: agents ?? [],
577
+ value: agentId,
578
+ onValueChange: setAgentId,
579
+ labels: { label: l.agentLabel, ...agentLabels },
580
+ testId: `${id}-agent`,
581
+ className: "sm:w-56"
582
+ }
583
+ ) : /* @__PURE__ */ jsx("span", {}),
584
+ /* @__PURE__ */ jsxs(
585
+ Button,
586
+ {
587
+ type: "submit",
588
+ className: "gap-1.5 px-5 pointer-coarse:min-h-11",
589
+ disabled: !valid || blockedByScope || isLaunching,
590
+ "data-testid": testIdOr(startTestId, { testId: "workflow-start-button" }),
591
+ children: [
592
+ isLaunching ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "size-4" }),
593
+ isLaunching ? l.starting : submitLabel?.({ workflow, inputs, matchedCount }) ?? workflow.startLabel ?? l.start
594
+ ]
595
+ }
596
+ )
597
+ ]
598
+ }
599
+ )
600
+ ] })
601
+ }
602
+ )
603
+ ]
604
+ }
605
+ ) });
606
+ }
607
+ function WorkflowLauncher({
608
+ workflows,
609
+ tileWorkflows,
610
+ onLaunch,
611
+ showTiles = true,
612
+ caption,
613
+ captionTestId,
614
+ tileTestId,
615
+ disabledReason,
616
+ activeId: activeIdProp,
617
+ onActiveIdChange,
618
+ initialInputsFor,
619
+ testId,
620
+ extraTiles = [],
621
+ className,
622
+ children,
623
+ renderIcon,
624
+ ...dialog
625
+ }) {
626
+ const [activeId, setActiveId] = useControllableState(
627
+ activeIdProp,
628
+ null,
629
+ onActiveIdChange
630
+ );
631
+ const [isLaunching, setIsLaunching] = useState(false);
632
+ const active = workflows.find((w) => w.id === activeId) ?? null;
633
+ const run = async (launch) => {
634
+ setIsLaunching(true);
635
+ try {
636
+ await onLaunch(launch);
637
+ setActiveId(null);
638
+ } finally {
639
+ setIsLaunching(false);
640
+ }
641
+ };
642
+ const select = (workflow) => {
643
+ if (workflow.run.kind === "operation" && workflow.inputs.length === 0) {
644
+ const inputs = defaultInputs(workflow);
645
+ if (Object.keys(validateInputs(workflow, inputs)).length === 0) {
646
+ Promise.resolve(onLaunch(buildLaunch(workflow, inputs, void 0))).catch(() => {
647
+ });
648
+ return;
649
+ }
650
+ }
651
+ setActiveId(workflow.id);
652
+ };
653
+ const tiles = (tileWorkflows ?? workflows).map((workflow) => {
654
+ const reason = disabledReason?.(workflow);
655
+ return {
656
+ id: workflow.id,
657
+ iconNode: workflow.icon && renderIcon ? renderIcon(workflow.icon) : void 0,
658
+ title: workflow.title,
659
+ description: workflow.description,
660
+ onSelect: () => select(workflow),
661
+ disabled: reason !== void 0,
662
+ disabledReason: reason,
663
+ testId: tileTestId?.(workflow) ?? `workflow-tile-${workflow.id}`
664
+ };
665
+ });
666
+ return /* @__PURE__ */ jsxs("div", { className, "data-slot": "workflow-launcher", children: [
667
+ showTiles && /* @__PURE__ */ jsx(
668
+ StartTiles,
669
+ {
670
+ items: [...tiles, ...extraTiles],
671
+ caption,
672
+ captionTestId,
673
+ testId
674
+ }
675
+ ),
676
+ children,
677
+ active && /* @__PURE__ */ jsx(
678
+ WorkflowConfigDialog,
679
+ {
680
+ ...dialog,
681
+ renderIcon,
682
+ workflow: active,
683
+ open: true,
684
+ onOpenChange: (open) => !open && setActiveId(null),
685
+ onLaunch: run,
686
+ isLaunching,
687
+ initialInputs: initialInputsFor?.(active)
688
+ }
689
+ )
690
+ ] });
691
+ }
692
+ function documentScopeLabels(tags) {
693
+ return tags.filter((tag) => {
694
+ if (tag.kind === "search") return false;
695
+ const onDocs = (tag.doctag_count ?? 0) > 0;
696
+ const onFacts = (tag.facttag_count ?? 0) > 0;
697
+ return onDocs || !onFacts;
698
+ });
699
+ }
700
+ var canonical = (path) => (path ?? "").split("/").map((part) => part.trim()).filter(Boolean).join("/");
701
+ function matchWorkflowScope(docs, scope) {
702
+ if (scope.documents?.length) {
703
+ const ids = new Set(scope.documents);
704
+ return docs.filter((doc) => ids.has(doc.external_id));
705
+ }
706
+ if (!scope.folders.length && !scope.labels.length) return docs;
707
+ const folders = scope.folders.map(canonical);
708
+ const labels = new Set(scope.labels);
709
+ return docs.filter((doc) => {
710
+ const folder = canonical(doc.folder);
711
+ return folders.some(
712
+ (path) => path === "" ? folder === "" : folder === path || folder.startsWith(`${path}/`)
713
+ ) || doc.doctags?.some((tag) => labels.has(tag.tag_ext_id));
714
+ });
715
+ }
716
+ function buildWorkflowScopeSource(docs, labels = []) {
717
+ const counts = /* @__PURE__ */ new Map();
718
+ for (const doc of docs) {
719
+ const parts = canonical(doc.folder).split("/").filter(Boolean);
720
+ for (let i = 1; i <= parts.length; i++) {
721
+ const path = parts.slice(0, i).join("/");
722
+ counts.set(path, (counts.get(path) ?? 0) + 1);
723
+ }
724
+ }
725
+ return {
726
+ folders: [...counts].sort(([a], [b]) => a.localeCompare(b)).map(([path, count]) => ({
727
+ path,
728
+ count,
729
+ label: path.split("/").at(-1),
730
+ depth: path.split("/").length - 1
731
+ })),
732
+ unfiledCount: docs.filter((doc) => !canonical(doc.folder)).length,
733
+ labels: labels.map((label) => ({
734
+ id: label.external_id,
735
+ name: label.name,
736
+ color: label.color ?? getEntityColor(label.external_id, "medium"),
737
+ count: docs.filter((doc) => doc.doctags?.some((tag) => tag.tag_ext_id === label.external_id)).length
738
+ })),
739
+ totalCount: docs.length,
740
+ countMatching: (scope) => matchWorkflowScope(docs, scope).length,
741
+ resolveDocuments: (scope) => matchWorkflowScope(docs, scope).map((doc) => doc.external_id)
742
+ };
743
+ }
744
+ var DEFAULT_WORKFLOW_DOCUMENTS_FIELD_LABELS = {
745
+ pickOne: "Pick a file",
746
+ pickMany: "Pick files",
747
+ allDocuments: "All documents",
748
+ addFiles: "Add files",
749
+ adding: "Adding\u2026",
750
+ empty: "No files in this workspace yet. Drop one here, or add it.",
751
+ remove: (name) => `Remove ${name}`,
752
+ pending: (name, status) => `${name}: ${status}. You can still pick it.`,
753
+ status: {
754
+ queued: "Queued",
755
+ uploading: "Uploading",
756
+ encrypting: "Encrypting",
757
+ processing: "Processing",
758
+ parsing: "Processing",
759
+ indexing: "Indexing",
760
+ analysing: "Analysing"
761
+ }
762
+ };
763
+ var isReady = (status) => !status || ["completed", "done", "ready", "success"].includes(status.toLowerCase());
764
+ var byTitle = (a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase());
765
+ function WorkflowDocumentsField({
766
+ field,
767
+ value,
768
+ onChange,
769
+ documents,
770
+ loaded = true,
771
+ thumbnailOf,
772
+ onAddFiles,
773
+ adding,
774
+ labels
775
+ }) {
776
+ const l = { ...DEFAULT_WORKFLOW_DOCUMENTS_FIELD_LABELS, ...labels };
777
+ const ids = value.documents ?? [];
778
+ const max = field.max ?? 1;
779
+ const multiple = max > 1;
780
+ const [isOver, setIsOver] = useState(false);
781
+ const docOf = (id) => documents.find((d) => d.id === id);
782
+ const nameOf = (id) => docOf(id)?.title || id;
783
+ const set = (next) => onChange({ ...value, documents: next });
784
+ const toggle = (id) => set(ids.includes(id) ? ids.filter((d) => d !== id) : [...ids, id].slice(-max));
785
+ const offered = useMemo(
786
+ () => field.accept === "image" ? documents.filter((d) => d.isImage) : documents,
787
+ [documents, field.accept]
788
+ );
789
+ const awaitingAdd = useRef(false);
790
+ const known = useRef(null);
791
+ const latest = useRef({ ids, set });
792
+ latest.current = { ids, set };
793
+ useEffect(() => {
794
+ if (!loaded) return;
795
+ const current = new Set(documents.map((d) => d.id));
796
+ const before = known.current;
797
+ known.current = current;
798
+ if (!before || !awaitingAdd.current) return;
799
+ const fresh = [...current].filter((id) => !before.has(id));
800
+ if (fresh.length === 0) return;
801
+ awaitingAdd.current = false;
802
+ latest.current.set([...latest.current.ids, ...fresh].slice(-max));
803
+ }, [documents, loaded, max]);
804
+ const addFiles = (files) => {
805
+ if (!onAddFiles) return;
806
+ awaitingAdd.current = true;
807
+ onAddFiles(files?.length ? files : void 0);
808
+ };
809
+ const dropProps = onAddFiles ? {
810
+ onDragOver: (e) => {
811
+ e.preventDefault();
812
+ setIsOver(true);
813
+ },
814
+ onDragLeave: () => setIsOver(false),
815
+ onDrop: (e) => {
816
+ e.preventDefault();
817
+ setIsOver(false);
818
+ const files = Array.from(e.dataTransfer?.files ?? []);
819
+ if (files.length) addFiles(files);
820
+ }
821
+ } : {};
822
+ const dropClass = isOver ? "ring-2 ring-primary" : "";
823
+ const addButton = (className, variant = "outline") => onAddFiles && /* @__PURE__ */ jsx(
824
+ Button,
825
+ {
826
+ type: "button",
827
+ size: "sm",
828
+ variant,
829
+ className,
830
+ disabled: adding,
831
+ onClick: () => addFiles(),
832
+ "data-testid": "workflow-add-files",
833
+ children: adding ? l.adding : l.addFiles
834
+ }
835
+ );
836
+ if (loaded && documents.length === 0) {
837
+ return /* @__PURE__ */ jsxs(
838
+ "div",
839
+ {
840
+ className: cn(
841
+ "flex flex-col items-center gap-2 rounded-lg border border-dashed border-border bg-muted/40 px-4 py-6 text-center",
842
+ dropClass
843
+ ),
844
+ "data-testid": "workflow-documents-empty",
845
+ ...dropProps,
846
+ children: [
847
+ /* @__PURE__ */ jsx(FolderPlus, { className: "size-5 text-muted-foreground", "aria-hidden": true }),
848
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.empty }),
849
+ addButton(void 0, "default")
850
+ ]
851
+ }
852
+ );
853
+ }
854
+ const pending = ids.flatMap((id) => {
855
+ const status = docOf(id)?.status;
856
+ return isReady(status) ? [] : [{ id, label: l.status[String(status).toLowerCase()] ?? String(status) }];
857
+ });
858
+ const selected = multiple ? void 0 : docOf(ids[0] ?? "");
859
+ return /* @__PURE__ */ jsxs(
860
+ "div",
861
+ {
862
+ className: cn("flex flex-col gap-3 rounded-lg", dropClass),
863
+ "data-testid": `workflow-documents-${field.name}`,
864
+ ...dropProps,
865
+ children: [
866
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
867
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
868
+ DocumentPicker,
869
+ {
870
+ groups: [{ id: "all", heading: l.allDocuments, docs: [...offered].sort(byTitle) }],
871
+ value: multiple ? "" : ids[0] ?? "",
872
+ onValueChange: (id) => multiple ? toggle(id) : set([id]),
873
+ selected,
874
+ selectedIds: multiple ? ids : void 0,
875
+ labels: { placeholder: field.placeholder ?? (multiple ? l.pickMany : l.pickOne) }
876
+ }
877
+ ) }),
878
+ addButton("shrink-0 pointer-coarse:h-11")
879
+ ] }),
880
+ pending.map((p) => /* @__PURE__ */ jsx(
881
+ "p",
882
+ {
883
+ className: "text-xs text-muted-foreground",
884
+ "data-testid": `workflow-document-status-${p.id}`,
885
+ children: l.pending(nameOf(p.id), p.label)
886
+ },
887
+ p.id
888
+ )),
889
+ multiple && ids.length > 0 && /* @__PURE__ */ jsx(
890
+ "div",
891
+ {
892
+ className: "grid grid-cols-2 gap-3 sm:grid-cols-3",
893
+ "data-testid": "workflow-documents-gallery",
894
+ children: ids.map((id) => {
895
+ const thumbnail = thumbnailOf?.(id);
896
+ return /* @__PURE__ */ jsxs(
897
+ "figure",
898
+ {
899
+ className: "relative overflow-hidden rounded-lg border border-border bg-muted/40",
900
+ "data-testid": `workflow-document-${id}`,
901
+ children: [
902
+ thumbnail ? /* @__PURE__ */ jsx(
903
+ "img",
904
+ {
905
+ src: thumbnail,
906
+ alt: nameOf(id),
907
+ className: "aspect-square w-full object-cover"
908
+ }
909
+ ) : /* @__PURE__ */ jsx("div", { className: "flex aspect-square w-full items-center justify-center text-muted-foreground/50", children: /* @__PURE__ */ jsx(ImageIcon, { className: "size-8" }) }),
910
+ /* @__PURE__ */ jsx("figcaption", { className: "truncate bg-background/90 px-2 py-1 text-xs text-muted-foreground", children: nameOf(id) }),
911
+ /* @__PURE__ */ jsx(
912
+ "button",
913
+ {
914
+ type: "button",
915
+ onClick: () => toggle(id),
916
+ "aria-label": l.remove(nameOf(id)),
917
+ className: "absolute right-1 top-1 flex size-6 items-center justify-center rounded-full bg-background/80 text-muted-foreground hover:text-foreground pointer-coarse:size-11",
918
+ "data-testid": `workflow-document-remove-${id}`,
919
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
920
+ }
921
+ )
922
+ ]
923
+ },
924
+ id
925
+ );
926
+ })
927
+ }
928
+ )
929
+ ]
930
+ }
931
+ );
932
+ }
322
933
 
323
934
  // src/agents/workflows/planSteps.ts
324
935
  function toPlanAssignee(a) {
@@ -2361,95 +2972,496 @@ function StepNotices({
2361
2972
  ))
2362
2973
  ] });
2363
2974
  }
2364
-
2365
- // src/agents/skills/skillFiles.ts
2366
- var SKILL_TYPES = ["markdown", "python", "shell", "javascript", "typescript"];
2367
- var SKILL_MAIN_FILENAME = "SKILL.md";
2368
- var MAIN_NAMES = /* @__PURE__ */ new Set(["skill.md", "skill.py", "skill.sh", "skill.js", "skill.ts"]);
2369
- function isMainSkillFile(filename, skillName) {
2370
- const lower = filename.toLowerCase();
2371
- if (MAIN_NAMES.has(lower)) return true;
2372
- const stem = lower.includes(".") ? lower.slice(0, lower.lastIndexOf(".")) : lower;
2373
- return stem === skillName;
2374
- }
2375
- function detectTypeFromContent(content, filename) {
2376
- if (filename) {
2377
- const lower = filename.toLowerCase();
2378
- if (lower.endsWith(".py")) return "python";
2379
- if (lower.endsWith(".sh")) return "shell";
2380
- if (lower.endsWith(".js")) return "javascript";
2381
- if (lower.endsWith(".ts")) return "typescript";
2382
- }
2383
- if (content) {
2384
- const firstLine = content.split("\n", 1)[0].trim();
2385
- if (firstLine.startsWith("#!")) {
2386
- if (firstLine.includes("python")) return "python";
2387
- if (firstLine.includes("bash") || firstLine.includes("/sh")) return "shell";
2388
- if (firstLine.includes("ts-node")) return "typescript";
2389
- if (firstLine.includes("node")) return "javascript";
2975
+ function SkillTypeBadge({ type, className }) {
2976
+ return /* @__PURE__ */ jsx(
2977
+ "span",
2978
+ {
2979
+ className: cn(
2980
+ "shrink-0 rounded bg-background px-1.5 py-0.5 text-xs uppercase tracking-wide text-muted-foreground",
2981
+ className
2982
+ ),
2983
+ "data-slot": "skill-type-badge",
2984
+ children: type
2390
2985
  }
2391
- }
2392
- return "markdown";
2986
+ );
2393
2987
  }
2394
- function parseSkillFrontmatter(raw, filename = "") {
2395
- const config = {};
2396
- let name = "";
2397
- let description = "";
2398
- let skillType = "markdown";
2399
- if (!raw.startsWith("---")) {
2400
- return {
2401
- content: raw,
2402
- name,
2403
- description,
2404
- config,
2405
- skillType: detectTypeFromContent(raw, filename)
2406
- };
2407
- }
2408
- const end = raw.indexOf("---", 3);
2409
- if (end === -1) {
2410
- return {
2411
- content: raw,
2412
- name,
2413
- description,
2414
- config,
2415
- skillType: detectTypeFromContent(raw, filename)
2416
- };
2417
- }
2418
- const frontmatter = raw.slice(3, end).trim();
2419
- const body = raw.slice(end + 3).trim();
2420
- for (const rawLine of frontmatter.split("\n")) {
2421
- const line = rawLine.trim();
2422
- if (!line || !line.includes(":")) continue;
2423
- const idx = line.indexOf(":");
2424
- const key = line.slice(0, idx).trim().toLowerCase();
2425
- const value = line.slice(idx + 1).trim();
2426
- if (key === "name" && value) {
2427
- name = value;
2428
- } else if (key === "description" && value) {
2429
- description = value;
2430
- } else if (key === "model" && value) {
2431
- config.model = value;
2432
- } else if (key === "temperature") {
2433
- const t = Number.parseFloat(value);
2434
- if (!Number.isNaN(t) && t >= 0 && t <= 2) config.temperature = t;
2435
- } else if (key === "agent") {
2436
- config.agent = ["true", "yes", "1"].includes(value.toLowerCase());
2437
- } else if (key === "tools" && value) {
2438
- config.tools = value.split(",").map((t) => t.trim()).filter(Boolean);
2439
- } else if (key === "type" && value) {
2440
- const v = value.toLowerCase();
2441
- if (v === "python" || v === "py") skillType = "python";
2442
- else if (v === "shell" || v === "sh" || v === "bash") skillType = "shell";
2443
- else if (v === "javascript" || v === "js" || v === "node") skillType = "javascript";
2444
- else if (v === "typescript" || v === "ts") skillType = "typescript";
2988
+ var DEFAULT_SKILL_GLOBAL_BADGE_LABELS = {
2989
+ global: "Global",
2990
+ globalTitle: "Shared with everyone in the marketplace"
2991
+ };
2992
+ function SkillGlobalBadge({
2993
+ className,
2994
+ labels
2995
+ }) {
2996
+ const l = { ...DEFAULT_SKILL_GLOBAL_BADGE_LABELS, ...labels };
2997
+ return /* @__PURE__ */ jsxs(
2998
+ "span",
2999
+ {
3000
+ className: cn(
3001
+ "inline-flex shrink-0 items-center gap-1 rounded bg-accent px-1.5 py-0.5 text-xs uppercase tracking-wide text-accent-foreground",
3002
+ className
3003
+ ),
3004
+ title: l.globalTitle,
3005
+ "data-slot": "skill-global-badge",
3006
+ children: [
3007
+ /* @__PURE__ */ jsx(Globe, { className: "size-3" }),
3008
+ l.global
3009
+ ]
2445
3010
  }
2446
- }
2447
- if (skillType === "markdown") {
2448
- skillType = detectTypeFromContent(body, filename);
2449
- }
2450
- return { content: body, name, description, config, skillType };
3011
+ );
2451
3012
  }
2452
- function serializeSkill(input) {
3013
+ function SkillActionButton({
3014
+ label,
3015
+ tooltip,
3016
+ onClick,
3017
+ children,
3018
+ testId
3019
+ }) {
3020
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
3021
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, onFocusCapture: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
3022
+ Button,
3023
+ {
3024
+ variant: "ghost",
3025
+ size: "icon",
3026
+ className: "size-7 shrink-0 pointer-coarse:size-11",
3027
+ onClick: (e) => {
3028
+ e.stopPropagation();
3029
+ onClick(e);
3030
+ },
3031
+ "aria-label": label,
3032
+ "data-testid": testId,
3033
+ children
3034
+ }
3035
+ ) }),
3036
+ /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: tooltip ?? label }) })
3037
+ ] });
3038
+ }
3039
+ function activateOnKey(handler) {
3040
+ return (e) => {
3041
+ if (e.target !== e.currentTarget) return;
3042
+ if (e.key === "Enter" || e.key === " ") {
3043
+ e.preventDefault();
3044
+ handler();
3045
+ }
3046
+ };
3047
+ }
3048
+ var DEFAULT_SKILL_GENERATE_FORM_LABELS = {
3049
+ title: "Generate with AI",
3050
+ hint: "Describe what the skill should do. The assistant drafts it in chat, then you can edit it here.",
3051
+ placeholder: "e.g. Summarize a contract into key obligations, dates, and parties",
3052
+ generate: "Generate with AI",
3053
+ writeManually: "\u2026or write it manually"
3054
+ };
3055
+ function SkillGenerateForm({
3056
+ onGenerate,
3057
+ onManual,
3058
+ icon,
3059
+ labels,
3060
+ testIds
3061
+ }) {
3062
+ const l = { ...DEFAULT_SKILL_GENERATE_FORM_LABELS, ...labels };
3063
+ const [desc, setDesc] = useState("");
3064
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-4 overflow-y-auto p-4", "data-slot": "skill-generate-form", children: [
3065
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-muted p-4", children: [
3066
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-2", children: [
3067
+ /* @__PURE__ */ jsx(Wand2, { className: "size-4 text-primary" }),
3068
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: l.title })
3069
+ ] }),
3070
+ /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: l.hint }),
3071
+ /* @__PURE__ */ jsx(
3072
+ Textarea,
3073
+ {
3074
+ value: desc,
3075
+ onChange: (e) => setDesc(e.target.value),
3076
+ rows: 4,
3077
+ placeholder: l.placeholder,
3078
+ "data-testid": testIdOr(testIds?.description, { testId: "skills-panel-ai-desc" })
3079
+ }
3080
+ ),
3081
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex justify-end", children: /* @__PURE__ */ jsxs(
3082
+ Button,
3083
+ {
3084
+ size: "sm",
3085
+ disabled: !desc.trim(),
3086
+ onClick: () => onGenerate(desc.trim()),
3087
+ "data-testid": testIdOr(testIds?.generate, { testId: "skills-panel-ai-generate" }),
3088
+ children: [
3089
+ icon && /* @__PURE__ */ jsx("span", { className: "mr-1 [&>svg]:size-4", children: icon }),
3090
+ l.generate
3091
+ ]
3092
+ }
3093
+ ) })
3094
+ ] }),
3095
+ /* @__PURE__ */ jsx(
3096
+ "button",
3097
+ {
3098
+ type: "button",
3099
+ className: "self-start text-sm text-muted-foreground underline-offset-2 hover:text-foreground hover:underline pointer-coarse:min-h-11",
3100
+ onClick: onManual,
3101
+ "data-testid": testIdOr(testIds?.writeManually, { testId: "skills-panel-write-manually" }),
3102
+ children: l.writeManually
3103
+ }
3104
+ )
3105
+ ] });
3106
+ }
3107
+ var DEFAULT_SKILL_EDIT_FORM_LABELS = {
3108
+ name: "Name",
3109
+ namePlaceholder: "contract-summary",
3110
+ invokedAs: (name) => /* @__PURE__ */ jsxs(Fragment, { children: [
3111
+ "Invoked as ",
3112
+ /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3113
+ "/",
3114
+ name || "skill-name"
3115
+ ] })
3116
+ ] }),
3117
+ description: "Description",
3118
+ descriptionPlaceholder: "What it does and when to use it",
3119
+ instructions: "Instructions",
3120
+ cancel: "Cancel",
3121
+ saving: "Saving\u2026",
3122
+ saveChanges: "Save changes",
3123
+ create: "Create skill"
3124
+ };
3125
+ var FIELD_LABEL = "text-xs font-semibold uppercase tracking-wide text-muted-foreground";
3126
+ function SkillEditForm({
3127
+ name,
3128
+ onNameChange,
3129
+ isNameLocked,
3130
+ nameError,
3131
+ description,
3132
+ onDescriptionChange,
3133
+ editor,
3134
+ isSaving,
3135
+ isEditing,
3136
+ onSave,
3137
+ onCancel,
3138
+ labels,
3139
+ testIds
3140
+ }) {
3141
+ const l = { ...DEFAULT_SKILL_EDIT_FORM_LABELS, ...labels };
3142
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col", "data-slot": "skill-edit-form", children: [
3143
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4", children: [
3144
+ /* @__PURE__ */ jsxs("div", { children: [
3145
+ /* @__PURE__ */ jsx(Label, { htmlFor: "sp-name", className: FIELD_LABEL, children: l.name }),
3146
+ /* @__PURE__ */ jsx(
3147
+ Input,
3148
+ {
3149
+ id: "sp-name",
3150
+ value: name,
3151
+ disabled: isNameLocked,
3152
+ placeholder: l.namePlaceholder,
3153
+ onChange: (e) => onNameChange(e.target.value),
3154
+ className: "mt-1",
3155
+ "data-testid": testIdOr(testIds?.name, { testId: "skills-panel-edit-name" })
3156
+ }
3157
+ ),
3158
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: l.invokedAs(name) }),
3159
+ nameError && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: nameError })
3160
+ ] }),
3161
+ /* @__PURE__ */ jsxs("div", { children: [
3162
+ /* @__PURE__ */ jsx(Label, { htmlFor: "sp-desc", className: FIELD_LABEL, children: l.description }),
3163
+ /* @__PURE__ */ jsx(
3164
+ Input,
3165
+ {
3166
+ id: "sp-desc",
3167
+ value: description,
3168
+ placeholder: l.descriptionPlaceholder,
3169
+ onChange: (e) => onDescriptionChange(e.target.value),
3170
+ className: "mt-1",
3171
+ "data-testid": testIdOr(testIds?.description, { testId: "skills-panel-edit-desc" })
3172
+ }
3173
+ )
3174
+ ] }),
3175
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3176
+ /* @__PURE__ */ jsx(Label, { className: `${FIELD_LABEL} mb-1`, children: l.instructions }),
3177
+ /* @__PURE__ */ jsx("div", { className: "min-h-[240px] flex-1 overflow-hidden rounded-md border border-border", children: editor })
3178
+ ] })
3179
+ ] }),
3180
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end gap-2 border-t border-border px-4 py-3", children: [
3181
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, children: l.cancel }),
3182
+ /* @__PURE__ */ jsx(
3183
+ Button,
3184
+ {
3185
+ size: "sm",
3186
+ onClick: onSave,
3187
+ disabled: isSaving,
3188
+ "data-testid": testIdOr(testIds?.save, { testId: "skills-panel-edit-save" }),
3189
+ children: isSaving ? l.saving : isEditing ? l.saveChanges : l.create
3190
+ }
3191
+ )
3192
+ ] })
3193
+ ] });
3194
+ }
3195
+ function SkillFilterChips({
3196
+ options,
3197
+ value,
3198
+ onChange,
3199
+ label,
3200
+ testIdPrefix = "skills-filter",
3201
+ className
3202
+ }) {
3203
+ return /* @__PURE__ */ jsx(
3204
+ "div",
3205
+ {
3206
+ role: "radiogroup",
3207
+ "aria-label": label,
3208
+ className: cn("flex flex-wrap gap-2", className),
3209
+ "data-slot": "skill-filter-chips",
3210
+ children: options.map((option) => {
3211
+ const selected = option.id === value;
3212
+ return /* @__PURE__ */ jsxs(
3213
+ "button",
3214
+ {
3215
+ type: "button",
3216
+ role: "radio",
3217
+ "aria-checked": selected,
3218
+ onClick: () => onChange(option.id),
3219
+ "data-testid": `${testIdPrefix}-${option.id}`,
3220
+ className: cn(
3221
+ "inline-flex min-h-8 items-center gap-1.5 rounded-full border px-3 text-sm transition-colors pointer-coarse:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3222
+ selected ? "border-primary bg-primary/10 text-foreground" : "border-border bg-background text-muted-foreground [&:hover]:bg-muted"
3223
+ ),
3224
+ children: [
3225
+ option.label,
3226
+ option.count !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.count })
3227
+ ]
3228
+ },
3229
+ option.id
3230
+ );
3231
+ })
3232
+ }
3233
+ );
3234
+ }
3235
+ var DEFAULT_SKILL_VIEW_TOGGLE_LABELS = {
3236
+ group: "View",
3237
+ gallery: "Gallery",
3238
+ list: "List"
3239
+ };
3240
+ function SkillViewToggle({ value, onChange, labels, testId }) {
3241
+ const l = { ...DEFAULT_SKILL_VIEW_TOGGLE_LABELS, ...labels };
3242
+ const item = (view, label, icon) => /* @__PURE__ */ jsx(
3243
+ "button",
3244
+ {
3245
+ type: "button",
3246
+ role: "radio",
3247
+ "aria-checked": value === view,
3248
+ "aria-label": label,
3249
+ title: label,
3250
+ onClick: () => onChange(view),
3251
+ "data-testid": `${testId ?? "skills-view-toggle"}-${view}`,
3252
+ className: cn(
3253
+ "flex size-8 items-center justify-center rounded-md pointer-coarse:size-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3254
+ value === view ? "bg-background text-foreground shadow-sm" : "text-muted-foreground"
3255
+ ),
3256
+ children: icon
3257
+ }
3258
+ );
3259
+ return /* @__PURE__ */ jsxs(
3260
+ "div",
3261
+ {
3262
+ role: "radiogroup",
3263
+ "aria-label": l.group,
3264
+ className: "inline-flex shrink-0 gap-0.5 rounded-lg bg-muted p-0.5",
3265
+ "data-testid": testId ?? "skills-view-toggle",
3266
+ children: [
3267
+ item("gallery", l.gallery, /* @__PURE__ */ jsx(LayoutGrid, { className: "size-4" })),
3268
+ item("list", l.list, /* @__PURE__ */ jsx(List, { className: "size-4" }))
3269
+ ]
3270
+ }
3271
+ );
3272
+ }
3273
+ function SkillTile({
3274
+ name,
3275
+ description,
3276
+ sourceLabel,
3277
+ type,
3278
+ icon,
3279
+ layout,
3280
+ selected,
3281
+ onSelect,
3282
+ testId
3283
+ }) {
3284
+ const isRow = layout === "list";
3285
+ return /* @__PURE__ */ jsxs(
3286
+ "div",
3287
+ {
3288
+ role: "button",
3289
+ tabIndex: 0,
3290
+ "aria-pressed": selected,
3291
+ onClick: onSelect,
3292
+ onKeyDown: activateOnKey(onSelect),
3293
+ "data-testid": testId ?? `skill-tile-${name}`,
3294
+ "data-skill-name": name,
3295
+ "data-slot": "skill-tile",
3296
+ className: cn(
3297
+ "flex cursor-pointer rounded-lg border p-3 text-left transition-colors pointer-coarse:min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3298
+ isRow ? "items-center gap-3" : "h-full flex-col gap-2",
3299
+ selected ? "border-primary bg-primary/5" : "border-transparent bg-muted [&:hover]:bg-accent"
3300
+ ),
3301
+ children: [
3302
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
3303
+ icon && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-ai-ink [&>svg]:size-5", children: icon }),
3304
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-sm font-semibold text-foreground", children: name })
3305
+ ] }),
3306
+ /* @__PURE__ */ jsx(
3307
+ "p",
3308
+ {
3309
+ className: cn(
3310
+ "min-w-0 text-sm text-muted-foreground",
3311
+ isRow ? "flex-1 truncate max-sm:hidden" : "line-clamp-3"
3312
+ ),
3313
+ children: description
3314
+ }
3315
+ ),
3316
+ /* @__PURE__ */ jsxs("div", { className: cn("flex shrink-0 items-center gap-1.5", isRow && "ml-auto"), children: [
3317
+ type && type !== "markdown" && /* @__PURE__ */ jsx("span", { className: "rounded bg-background px-1.5 py-0.5 text-xs uppercase tracking-wide text-muted-foreground", children: type }),
3318
+ /* @__PURE__ */ jsx("span", { className: "rounded bg-background px-1.5 py-0.5 text-xs text-muted-foreground", children: sourceLabel })
3319
+ ] })
3320
+ ]
3321
+ }
3322
+ );
3323
+ }
3324
+ function SkillDetail({
3325
+ title,
3326
+ sourceLabel,
3327
+ description,
3328
+ icon,
3329
+ notice,
3330
+ children,
3331
+ actions,
3332
+ testId
3333
+ }) {
3334
+ return /* @__PURE__ */ jsxs(
3335
+ "section",
3336
+ {
3337
+ className: "flex min-h-0 flex-1 flex-col",
3338
+ "data-testid": testId ?? "skill-detail",
3339
+ "data-slot": "skill-detail",
3340
+ "aria-label": title,
3341
+ children: [
3342
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-4 overflow-y-auto p-4", children: [
3343
+ /* @__PURE__ */ jsxs("header", { className: "space-y-1", children: [
3344
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3345
+ icon && /* @__PURE__ */ jsx("span", { className: "text-ai-ink [&>svg]:size-5", children: icon }),
3346
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-foreground", children: title }),
3347
+ /* @__PURE__ */ jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: sourceLabel })
3348
+ ] }),
3349
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }),
3350
+ notice && /* @__PURE__ */ jsx("p", { className: "text-sm text-foreground", children: notice })
3351
+ ] }),
3352
+ children
3353
+ ] }),
3354
+ actions && /* @__PURE__ */ jsx(
3355
+ "div",
3356
+ {
3357
+ className: "flex flex-wrap gap-2 border-t border-border bg-background p-3",
3358
+ "data-slot": "skill-detail-actions",
3359
+ children: actions
3360
+ }
3361
+ )
3362
+ ]
3363
+ }
3364
+ );
3365
+ }
3366
+ function SkillDetailSection({
3367
+ title,
3368
+ children,
3369
+ testId
3370
+ }) {
3371
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [
3372
+ /* @__PURE__ */ jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: title }),
3373
+ children
3374
+ ] });
3375
+ }
3376
+
3377
+ // src/agents/skills/skillFiles.ts
3378
+ var SKILL_TYPES = ["markdown", "python", "shell", "javascript", "typescript"];
3379
+ var SKILL_MAIN_FILENAME = "SKILL.md";
3380
+ var MAIN_NAMES = /* @__PURE__ */ new Set(["skill.md", "skill.py", "skill.sh", "skill.js", "skill.ts"]);
3381
+ function isMainSkillFile(filename, skillName) {
3382
+ const lower = filename.toLowerCase();
3383
+ if (MAIN_NAMES.has(lower)) return true;
3384
+ const stem = lower.includes(".") ? lower.slice(0, lower.lastIndexOf(".")) : lower;
3385
+ return stem === skillName;
3386
+ }
3387
+ function detectTypeFromContent(content, filename) {
3388
+ if (filename) {
3389
+ const lower = filename.toLowerCase();
3390
+ if (lower.endsWith(".py")) return "python";
3391
+ if (lower.endsWith(".sh")) return "shell";
3392
+ if (lower.endsWith(".js")) return "javascript";
3393
+ if (lower.endsWith(".ts")) return "typescript";
3394
+ }
3395
+ if (content) {
3396
+ const firstLine = content.split("\n", 1)[0].trim();
3397
+ if (firstLine.startsWith("#!")) {
3398
+ if (firstLine.includes("python")) return "python";
3399
+ if (firstLine.includes("bash") || firstLine.includes("/sh")) return "shell";
3400
+ if (firstLine.includes("ts-node")) return "typescript";
3401
+ if (firstLine.includes("node")) return "javascript";
3402
+ }
3403
+ }
3404
+ return "markdown";
3405
+ }
3406
+ function parseSkillFrontmatter(raw, filename = "") {
3407
+ const config = {};
3408
+ let name = "";
3409
+ let description = "";
3410
+ let skillType = "markdown";
3411
+ if (!raw.startsWith("---")) {
3412
+ return {
3413
+ content: raw,
3414
+ name,
3415
+ description,
3416
+ config,
3417
+ skillType: detectTypeFromContent(raw, filename)
3418
+ };
3419
+ }
3420
+ const end = raw.indexOf("---", 3);
3421
+ if (end === -1) {
3422
+ return {
3423
+ content: raw,
3424
+ name,
3425
+ description,
3426
+ config,
3427
+ skillType: detectTypeFromContent(raw, filename)
3428
+ };
3429
+ }
3430
+ const frontmatter = raw.slice(3, end).trim();
3431
+ const body = raw.slice(end + 3).trim();
3432
+ for (const rawLine of frontmatter.split("\n")) {
3433
+ const line = rawLine.trim();
3434
+ if (!line || !line.includes(":")) continue;
3435
+ const idx = line.indexOf(":");
3436
+ const key = line.slice(0, idx).trim().toLowerCase();
3437
+ const value = line.slice(idx + 1).trim();
3438
+ if (key === "name" && value) {
3439
+ name = value;
3440
+ } else if (key === "description" && value) {
3441
+ description = value;
3442
+ } else if (key === "model" && value) {
3443
+ config.model = value;
3444
+ } else if (key === "temperature") {
3445
+ const t = Number.parseFloat(value);
3446
+ if (!Number.isNaN(t) && t >= 0 && t <= 2) config.temperature = t;
3447
+ } else if (key === "agent") {
3448
+ config.agent = ["true", "yes", "1"].includes(value.toLowerCase());
3449
+ } else if (key === "tools" && value) {
3450
+ config.tools = value.split(",").map((t) => t.trim()).filter(Boolean);
3451
+ } else if (key === "type" && value) {
3452
+ const v = value.toLowerCase();
3453
+ if (v === "python" || v === "py") skillType = "python";
3454
+ else if (v === "shell" || v === "sh" || v === "bash") skillType = "shell";
3455
+ else if (v === "javascript" || v === "js" || v === "node") skillType = "javascript";
3456
+ else if (v === "typescript" || v === "ts") skillType = "typescript";
3457
+ }
3458
+ }
3459
+ if (skillType === "markdown") {
3460
+ skillType = detectTypeFromContent(body, filename);
3461
+ }
3462
+ return { content: body, name, description, config, skillType };
3463
+ }
3464
+ function serializeSkill(input) {
2453
3465
  const lines = ["---", `name: ${input.name}`, `description: ${input.description}`];
2454
3466
  if (input.type && input.type !== "markdown") {
2455
3467
  lines.push(`type: ${input.type}`);
@@ -2563,6 +3575,883 @@ async function importSkillFromUrl(url) {
2563
3575
  const last = parsed.pathname.split("/").filter(Boolean).pop() ?? "skill";
2564
3576
  return importMarkdownSkill(last.includes(".") ? last : `${last}.md`, text);
2565
3577
  }
3578
+
3579
+ // src/agents/skills/skillLibrary.ts
3580
+ var SKILL_SOURCE_LABELS = {
3581
+ builtin: "Built in",
3582
+ mine: "Yours",
3583
+ shared: "Shared"
3584
+ };
3585
+ var SKILL_TYPE_LABELS = {
3586
+ markdown: "Instructions",
3587
+ python: "Python",
3588
+ shell: "Shell",
3589
+ javascript: "JavaScript",
3590
+ typescript: "TypeScript"
3591
+ };
3592
+ var SKILL_FIELD_TYPE_LABELS = {
3593
+ text: "Short answer",
3594
+ longtext: "Free text",
3595
+ boolean: "Yes or no",
3596
+ select: "A choice",
3597
+ number: "A number",
3598
+ date: "A date",
3599
+ scope: "Which files",
3600
+ documents: "Pick files",
3601
+ person: "A person"
3602
+ };
3603
+ function buildLibrarySkills(builtins, listings) {
3604
+ const { host, skills } = mergeSkillWorkflows(builtins, listings);
3605
+ const builtinIds = new Set(builtins.map((w) => w.id));
3606
+ const stored = new Map(listings.filter((s) => s.doc_ext_id).map((s) => [s.slug, s]));
3607
+ return [...host, ...skills].flatMap((workflow) => {
3608
+ if (workflow.run.kind !== "prompt") return [];
3609
+ const listing = stored.get(workflow.id);
3610
+ if (!listing?.doc_ext_id) {
3611
+ if (!builtinIds.has(workflow.id)) return [];
3612
+ return [
3613
+ {
3614
+ slug: workflow.id,
3615
+ title: workflow.title,
3616
+ description: workflow.description,
3617
+ source: "builtin",
3618
+ type: "markdown",
3619
+ isShared: false,
3620
+ replacesBuiltin: false,
3621
+ workflow
3622
+ }
3623
+ ];
3624
+ }
3625
+ const mine = listing.is_owner ?? true;
3626
+ return [
3627
+ {
3628
+ slug: workflow.id,
3629
+ title: workflow.title,
3630
+ description: listing.description || workflow.description,
3631
+ source: mine ? "mine" : "shared",
3632
+ type: listing.skill_type ?? "markdown",
3633
+ isShared: mine && listing.visibility === "global",
3634
+ replacesBuiltin: mine && builtinIds.has(listing.slug),
3635
+ workflow,
3636
+ docExtId: listing.doc_ext_id
3637
+ }
3638
+ ];
3639
+ });
3640
+ }
3641
+ function filterLibrarySkills(skills, f) {
3642
+ const q = f.query.trim().toLowerCase();
3643
+ return skills.filter((s) => f.source === "all" || s.source === f.source).filter((s) => f.type === "all" || s.type === f.type).filter(
3644
+ (s) => !q || s.title.toLowerCase().includes(q) || s.slug.toLowerCase().includes(q) || s.description.toLowerCase().includes(q)
3645
+ );
3646
+ }
3647
+ var ACTION = "pointer-coarse:min-h-11";
3648
+ function SkillLibraryDetail({
3649
+ skill,
3650
+ instructions,
3651
+ isLoading,
3652
+ actions,
3653
+ icon
3654
+ }) {
3655
+ const fields = skill.workflow.inputs;
3656
+ const isOwner = skill.source === "mine";
3657
+ const canCopy = skill.source !== "mine";
3658
+ const notice = skill.replacesBuiltin ? "Your copy is used instead of the original; delete it to go back." : skill.isShared ? "Shared with everyone." : void 0;
3659
+ return /* @__PURE__ */ jsxs(
3660
+ SkillDetail,
3661
+ {
3662
+ title: skill.title,
3663
+ sourceLabel: SKILL_SOURCE_LABELS[skill.source],
3664
+ description: skill.description,
3665
+ icon,
3666
+ notice,
3667
+ testId: "skill-detail",
3668
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
3669
+ /* @__PURE__ */ jsxs(Button, { onClick: actions.onRun, className: ACTION, "data-testid": "skill-run", children: [
3670
+ /* @__PURE__ */ jsx(Play, { className: "mr-1.5 size-4" }),
3671
+ "Run"
3672
+ ] }),
3673
+ canCopy && /* @__PURE__ */ jsxs(
3674
+ Button,
3675
+ {
3676
+ variant: "outline",
3677
+ onClick: actions.onCopy,
3678
+ disabled: actions.isBusy,
3679
+ className: ACTION,
3680
+ "data-testid": "skill-copy",
3681
+ children: [
3682
+ /* @__PURE__ */ jsx(Copy, { className: "mr-1.5 size-4" }),
3683
+ "Make a copy"
3684
+ ]
3685
+ }
3686
+ ),
3687
+ isOwner && /* @__PURE__ */ jsxs(
3688
+ Button,
3689
+ {
3690
+ variant: "outline",
3691
+ onClick: actions.onEdit,
3692
+ className: ACTION,
3693
+ "data-testid": "skill-edit",
3694
+ children: [
3695
+ /* @__PURE__ */ jsx(Pencil, { className: "mr-1.5 size-4" }),
3696
+ "Edit"
3697
+ ]
3698
+ }
3699
+ ),
3700
+ isOwner && /* @__PURE__ */ jsxs(
3701
+ Button,
3702
+ {
3703
+ variant: "outline",
3704
+ onClick: actions.onToggleShare,
3705
+ disabled: actions.isBusy,
3706
+ className: ACTION,
3707
+ "data-testid": "skill-share",
3708
+ children: [
3709
+ skill.isShared ? /* @__PURE__ */ jsx(Lock, { className: "mr-1.5 size-4" }) : /* @__PURE__ */ jsx(Globe, { className: "mr-1.5 size-4" }),
3710
+ skill.isShared ? "Make private" : "Share with everyone"
3711
+ ]
3712
+ }
3713
+ ),
3714
+ /* @__PURE__ */ jsxs(
3715
+ Button,
3716
+ {
3717
+ variant: "outline",
3718
+ onClick: actions.onExport,
3719
+ className: ACTION,
3720
+ "data-testid": "skill-export",
3721
+ children: [
3722
+ /* @__PURE__ */ jsx(Download, { className: "mr-1.5 size-4" }),
3723
+ "Export"
3724
+ ]
3725
+ }
3726
+ ),
3727
+ isOwner && /* @__PURE__ */ jsxs(
3728
+ Button,
3729
+ {
3730
+ variant: "ghost",
3731
+ onClick: actions.onDelete,
3732
+ disabled: actions.isBusy,
3733
+ className: `${ACTION} ml-auto text-destructive`,
3734
+ "data-testid": "skill-delete",
3735
+ children: [
3736
+ /* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 size-4" }),
3737
+ "Delete"
3738
+ ]
3739
+ }
3740
+ )
3741
+ ] }),
3742
+ children: [
3743
+ /* @__PURE__ */ jsx(SkillDetailSection, { title: "What it asks you", testId: "skill-inputs", children: fields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Nothing: it starts right away." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-sm", children: fields.map((field) => /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-baseline gap-x-2", children: [
3744
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: field.label }),
3745
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3746
+ SKILL_FIELD_TYPE_LABELS[field.type],
3747
+ field.required ? "" : ", optional"
3748
+ ] })
3749
+ ] }, field.name)) }) }),
3750
+ /* @__PURE__ */ jsxs(
3751
+ SkillDetailSection,
3752
+ {
3753
+ title: skill.type === "markdown" ? "Instructions" : `Instructions (${SKILL_TYPE_LABELS[skill.type] ?? skill.type})`,
3754
+ children: [
3755
+ isLoading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading\u2026" }) : /* @__PURE__ */ jsx(
3756
+ "pre",
3757
+ {
3758
+ className: "max-h-72 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted p-3 font-mono text-xs",
3759
+ tabIndex: 0,
3760
+ "aria-label": "Instructions",
3761
+ "data-testid": "skill-instructions",
3762
+ children: instructions || "No instructions."
3763
+ }
3764
+ ),
3765
+ !isOwner && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "You can read these. Make a copy to change them." })
3766
+ ]
3767
+ }
3768
+ )
3769
+ ]
3770
+ }
3771
+ );
3772
+ }
3773
+ function PersonPill({ person, size = "sm", testId }) {
3774
+ return /* @__PURE__ */ jsx("span", { "data-testid": testId, className: "inline-flex min-w-0", children: /* @__PURE__ */ jsx(
3775
+ UserEntity,
3776
+ {
3777
+ variant: "pill",
3778
+ size,
3779
+ interactive: false,
3780
+ showTooltip: false,
3781
+ initials: initials(person.label),
3782
+ displayName: person.label,
3783
+ avatarStyle: person.id ? getEntityColorStyles(person.id) : void 0,
3784
+ isAgent: person.isAgent,
3785
+ isMe: person.isMe
3786
+ }
3787
+ ) });
3788
+ }
3789
+ var DEFAULT_TASK_TRIGGER_CHIP_LABELS = {
3790
+ after: (name) => `After \u201C${name}\u201D is done`,
3791
+ afterUnknown: "After another task is done",
3792
+ onDemand: "Manual"
3793
+ };
3794
+ function TriggerIcon({ type, chained }) {
3795
+ const cls = "size-3.5 shrink-0 text-muted-foreground";
3796
+ if (chained) return /* @__PURE__ */ jsx(Link2, { className: cls, "aria-hidden": true });
3797
+ switch (type) {
3798
+ case "on_demand":
3799
+ return /* @__PURE__ */ jsx(MousePointerClick, { className: cls, "aria-hidden": true });
3800
+ case "recurring":
3801
+ return /* @__PURE__ */ jsx(Clock, { className: cls, "aria-hidden": true });
3802
+ case "event":
3803
+ return /* @__PURE__ */ jsx(Bell, { className: cls, "aria-hidden": true });
3804
+ default:
3805
+ return /* @__PURE__ */ jsx(Clock, { className: cls, "aria-hidden": true });
3806
+ }
3807
+ }
3808
+ function TaskTriggerChip({
3809
+ id,
3810
+ triggerType,
3811
+ summary,
3812
+ afterTaskId,
3813
+ afterTaskName,
3814
+ testId,
3815
+ labels
3816
+ }) {
3817
+ const l = { ...DEFAULT_TASK_TRIGGER_CHIP_LABELS, ...labels };
3818
+ const chained = Boolean(afterTaskId);
3819
+ const text = chained ? afterTaskName ? l.after(afterTaskName) : l.afterUnknown : triggerType === "on_demand" ? l.onDemand : summary;
3820
+ return /* @__PURE__ */ jsxs(
3821
+ "span",
3822
+ {
3823
+ className: "inline-flex min-w-0 max-w-full items-center gap-1.5 text-sm font-normal text-foreground",
3824
+ "data-testid": testIdOr(testId, { testId: `task-trigger-${id}` }),
3825
+ "data-trigger": chained ? "task" : triggerType,
3826
+ children: [
3827
+ /* @__PURE__ */ jsx(TriggerIcon, { type: triggerType, chained }),
3828
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: text })
3829
+ ]
3830
+ }
3831
+ );
3832
+ }
3833
+
3834
+ // src/agents/tasks/workflowRows.ts
3835
+ var isSkillRow = (row) => row.kind === "skill";
3836
+ var hasTrigger = (row) => !row.on_demand && Boolean(row.rrule || row.run_at || row.event_kind || row.after_conversation_ext_id);
3837
+ function workflowStatus(row) {
3838
+ if (isSkillRow(row) || !hasTrigger(row)) return "on_demand";
3839
+ return row.active ? "active" : "paused";
3840
+ }
3841
+ function workflowTriggerType(workflow) {
3842
+ if (workflow.on_demand) return "on_demand";
3843
+ if (workflow.after_conversation_ext_id) return "after";
3844
+ if (workflow.event_kind) return "event";
3845
+ if (workflow.rrule) return "recurring";
3846
+ if (workflow.run_at) return "one_time";
3847
+ return "on_demand";
3848
+ }
3849
+ function workflowRunStatusLabel(status) {
3850
+ if (!status) return "None";
3851
+ const labels = {
3852
+ completed: "Success",
3853
+ success: "Success",
3854
+ failed: "Failed",
3855
+ running: "In progress",
3856
+ awaiting_authorization: "Awaiting access"
3857
+ };
3858
+ return labels[status] ?? status.replaceAll("_", " ");
3859
+ }
3860
+ function listedRunStatus(status, threadStatus) {
3861
+ if (threadStatus === "done" && status === "running") return "completed";
3862
+ return status;
3863
+ }
3864
+
3865
+ // src/agents/tasks/workflowColumns.tsx
3866
+ var WORKFLOW_COLUMN_ORDER = [
3867
+ "name",
3868
+ "trigger",
3869
+ "last_run",
3870
+ "result",
3871
+ "status",
3872
+ "shared",
3873
+ "actions"
3874
+ ];
3875
+ function cell(render) {
3876
+ return (params) => params.data ? render(params.data) : null;
3877
+ }
3878
+ var time = (iso) => iso ? Date.parse(iso) : Number.POSITIVE_INFINITY;
3879
+ function createWorkflowColumns(o) {
3880
+ const l = o.labels;
3881
+ const base = {
3882
+ sortable: false,
3883
+ cellStyle: { display: "flex", alignItems: "center" }
3884
+ };
3885
+ return [
3886
+ {
3887
+ ...base,
3888
+ colId: "name",
3889
+ sortable: true,
3890
+ headerName: l.colName,
3891
+ flex: 3,
3892
+ minWidth: 210,
3893
+ valueGetter: (p) => p.data?.name.toLowerCase() ?? "",
3894
+ cellRenderer: cell(o.renderName)
3895
+ },
3896
+ {
3897
+ ...base,
3898
+ colId: "trigger",
3899
+ headerName: l.colTrigger,
3900
+ flex: 2.4,
3901
+ minWidth: 185,
3902
+ valueGetter: (p) => p.data ? o.getTriggerText(p.data) : "",
3903
+ cellRenderer: cell(o.renderTrigger)
3904
+ },
3905
+ {
3906
+ ...base,
3907
+ colId: "last_run",
3908
+ sortable: true,
3909
+ headerName: l.colLastRun,
3910
+ flex: 1.4,
3911
+ minWidth: 140,
3912
+ cellStyle: { display: "flex", alignItems: "center", padding: 0 },
3913
+ valueGetter: (p) => time(p.data?.last_fired_at),
3914
+ cellRenderer: cell(o.renderLastRun)
3915
+ },
3916
+ {
3917
+ ...base,
3918
+ colId: "result",
3919
+ headerName: "Result",
3920
+ width: 130,
3921
+ valueGetter: (p) => p.data?.last_run_status ?? "",
3922
+ cellStyle: { display: "flex", alignItems: "center", padding: 0 },
3923
+ cellRenderer: cell(o.renderResult)
3924
+ },
3925
+ {
3926
+ ...base,
3927
+ colId: "status",
3928
+ sortable: true,
3929
+ headerName: l.colStatus,
3930
+ width: 120,
3931
+ valueGetter: (p) => p.data ? workflowStatus(p.data) : "",
3932
+ cellRenderer: cell(o.renderStatus)
3933
+ },
3934
+ {
3935
+ ...base,
3936
+ colId: "shared",
3937
+ headerName: l.colShared,
3938
+ headerTooltip: l.shared,
3939
+ width: 96,
3940
+ cellStyle: { display: "flex", alignItems: "center", justifyContent: "center" },
3941
+ valueGetter: (p) => p.data?.shared ?? false,
3942
+ cellRenderer: cell(o.renderShared)
3943
+ },
3944
+ {
3945
+ ...base,
3946
+ colId: "actions",
3947
+ headerName: "",
3948
+ width: 180,
3949
+ minWidth: 180,
3950
+ cellStyle: { display: "flex", alignItems: "center", justifyContent: "flex-end" },
3951
+ cellRenderer: cell(o.renderActions)
3952
+ }
3953
+ ];
3954
+ }
3955
+
3956
+ // src/agents/tasks/workflowTableLabels.ts
3957
+ var DEFAULT_WORKFLOW_LIST_LABELS = {
3958
+ colName: "Name",
3959
+ colType: "Type",
3960
+ colTrigger: "When",
3961
+ colAction: "What it does",
3962
+ colAgent: "Agent",
3963
+ colStatus: "Active",
3964
+ colShared: "Shared",
3965
+ colLastRun: "Last run",
3966
+ colNextRun: "Next run",
3967
+ shared: "Shared with the workspace",
3968
+ sharedOwnerOnly: "Only the creator can change who can see this",
3969
+ deleteOwnerOnly: "Only the creator or workspace owner can delete this",
3970
+ typeSkill: "Skill",
3971
+ typeWorkflow: "Workflow",
3972
+ statusActive: "Active",
3973
+ statusPaused: "Inactive",
3974
+ statusOnDemand: "On demand",
3975
+ activeSwitch: (name) => `Active: \u201C${name}\u201D`,
3976
+ agent: "Agent",
3977
+ myAssistant: "My assistant",
3978
+ me: "Me",
3979
+ someone: "Someone",
3980
+ never: "Never",
3981
+ none: "\u2014",
3982
+ reminder: "Reminder",
3983
+ failed: "Failed",
3984
+ lastRunFailed: "Last run failed",
3985
+ openLastRun: "Open the task this run created",
3986
+ editWorkflow: "Edit",
3987
+ run: "Run",
3988
+ runLater: "Run later",
3989
+ runNow: "Run now",
3990
+ runs: "Runs",
3991
+ delete: "Delete",
3992
+ moreActions: "More actions",
3993
+ searchLabel: "Search workflows",
3994
+ searchPlaceholder: "Search workflows and skills\u2026",
3995
+ emptyTitle: "Nothing to run yet",
3996
+ emptyMessage: "Skills and workflows you can run will appear here.",
3997
+ firstRun: "Run a skill now, or choose Run later to save it as a workflow that runs on its own.",
3998
+ noMatches: "Nothing in this view.",
3999
+ filterAll: "All",
4000
+ filterSkills: "Skills",
4001
+ filterWorkflows: "Workflows",
4002
+ filterActive: "Active",
4003
+ filterPaused: "Inactive",
4004
+ makeActive: "Make active",
4005
+ makePaused: "Deactivate",
4006
+ selectAll: "Select all",
4007
+ selectWorkflow: (name) => `Select ${name}`
4008
+ };
4009
+ var ROW_HEIGHT = 40;
4010
+ function WorkflowsTable(props) {
4011
+ const { workflows, isLoading, busyId, header, testId } = props;
4012
+ const l = { ...DEFAULT_WORKFLOW_LIST_LABELS, ...props.labels };
4013
+ const [filterStates, setFilterStates] = useState(/* @__PURE__ */ new Map());
4014
+ const [filterOpen, setFilterOpen] = useState(false);
4015
+ const [query, setQuery] = useState("");
4016
+ const [selected, setSelected] = useState([]);
4017
+ const apiRef = useRef(null);
4018
+ const q = query.trim().toLowerCase();
4019
+ const matches = (w) => !q || `${w.name} ${w.description ?? ""} ${w.prompt ?? ""} ${w.skill_ref ?? ""}`.toLowerCase().includes(q);
4020
+ const inFilter = (w, f) => f === "all" || f === "skills" && isSkillRow(w) || f === "workflows" && !isSkillRow(w) || f === "active" && workflowStatus(w) === "active" || f === "paused" && workflowStatus(w) === "paused";
4021
+ const counts = useMemo(() => {
4022
+ const out = { all: 0, skills: 0, workflows: 0, active: 0, paused: 0 };
4023
+ for (const w of workflows) {
4024
+ if (!matches(w)) continue;
4025
+ for (const f of Object.keys(out)) if (inFilter(w, f)) out[f] += 1;
4026
+ }
4027
+ return out;
4028
+ }, [workflows, q]);
4029
+ const shown = useMemo(
4030
+ () => workflows.filter((w) => {
4031
+ if (!matches(w)) return false;
4032
+ for (const group of [
4033
+ ["skills", "workflows"],
4034
+ ["active", "paused"]
4035
+ ]) {
4036
+ const included = group.filter((f) => filterStates.get(f) === "include");
4037
+ if (included.length && !included.some((f) => inFilter(w, f))) return false;
4038
+ if (group.some((f) => filterStates.get(f) === "exclude" && inFilter(w, f))) return false;
4039
+ }
4040
+ return true;
4041
+ }),
4042
+ [workflows, filterStates, q]
4043
+ );
4044
+ const renderPerson = props.renderPerson ?? ((p) => /* @__PURE__ */ jsx(PersonPill, { person: p }));
4045
+ const actionText = (w) => {
4046
+ if (w.action)
4047
+ return w.action.type === "email_documents" ? "Email latest documents" : "AI Review";
4048
+ if (isSkillRow(w))
4049
+ return w.description || w.skill_ref && props.skillNames?.[w.skill_ref] || w.name;
4050
+ return w.skill_ref ? props.skillNames?.[w.skill_ref] ?? w.skill_ref : w.prompt?.trim() || l.reminder;
4051
+ };
4052
+ const latest = useRef({ props, l, renderPerson, actionText });
4053
+ latest.current = { props, l, renderPerson, actionText };
4054
+ const columnDefs = useMemo(
4055
+ () => createWorkflowColumns({
4056
+ labels: l,
4057
+ getTriggerText: (w) => isSkillRow(w) ? l.statusOnDemand : latest.current.props.describeTrigger(w),
4058
+ renderName: (w) => {
4059
+ const { l: lb } = latest.current;
4060
+ const skill = isSkillRow(w);
4061
+ const text = skill ? latest.current.actionText(w) : w.description || latest.current.actionText(w);
4062
+ const stem = w.name.replace(/…$/, "").trim();
4063
+ const about = text && text.trim() !== w.name.trim() && text.trim() !== stem ? text : "";
4064
+ return /* @__PURE__ */ jsxs("div", { className: "min-w-0 leading-tight", children: [
4065
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
4066
+ /* @__PURE__ */ jsx(
4067
+ "span",
4068
+ {
4069
+ className: cn(
4070
+ "truncate text-sm font-normal",
4071
+ workflowStatus(w) === "paused" && "text-muted-foreground"
4072
+ ),
4073
+ "data-testid": `workflow-name-${w.external_id}`,
4074
+ children: w.name
4075
+ }
4076
+ ),
4077
+ skill && /* @__PURE__ */ jsx(
4078
+ "span",
4079
+ {
4080
+ className: "shrink-0 rounded-full border border-border px-1.5 text-[11px] text-muted-foreground",
4081
+ "data-testid": `workflow-type-${w.external_id}`,
4082
+ children: lb.typeSkill
4083
+ }
4084
+ )
4085
+ ] }),
4086
+ about && !latest.current.props.automationsOnly && /* @__PURE__ */ jsx(
4087
+ "p",
4088
+ {
4089
+ className: "truncate text-xs text-muted-foreground",
4090
+ "data-testid": `workflow-action-${w.external_id}`,
4091
+ children: about
4092
+ }
4093
+ )
4094
+ ] });
4095
+ },
4096
+ renderTrigger: (w) => {
4097
+ const { props: p } = latest.current;
4098
+ const after = w.after_conversation_ext_id;
4099
+ const skill = isSkillRow(w);
4100
+ return /* @__PURE__ */ jsx("div", { className: "min-w-0 leading-tight", children: /* @__PURE__ */ jsx(
4101
+ TaskTriggerChip,
4102
+ {
4103
+ id: w.external_id,
4104
+ testId: `workflow-trigger-${w.external_id}`,
4105
+ triggerType: workflowTriggerType(w),
4106
+ summary: skill ? "" : p.describeTrigger(w),
4107
+ afterTaskId: after,
4108
+ afterTaskName: after ? p.taskNames?.[after] : void 0,
4109
+ labels: p.triggerChipLabels
4110
+ }
4111
+ ) });
4112
+ },
4113
+ renderStatus: (w) => {
4114
+ const { props: p, l: lb } = latest.current;
4115
+ const canEdit = w.is_owner !== false || Boolean(p.canEditShared);
4116
+ return /* @__PURE__ */ jsx(
4117
+ Switch,
4118
+ {
4119
+ checked: hasTrigger(w) && !isSkillRow(w) && Boolean(w.active),
4120
+ disabled: !hasTrigger(w) || isSkillRow(w) || !canEdit || p.busyId === w.external_id,
4121
+ onCheckedChange: (next) => p.onToggleActive(w, next),
4122
+ className: "disabled:data-[state=unchecked]:bg-muted dark:disabled:data-[state=unchecked]:bg-muted",
4123
+ "aria-label": lb.activeSwitch(w.name),
4124
+ "data-testid": `workflow-active-${w.external_id}`
4125
+ }
4126
+ );
4127
+ },
4128
+ renderShared: (w) => {
4129
+ const { props: p, l: lb } = latest.current;
4130
+ if (!p.onToggleShared || isSkillRow(w)) return null;
4131
+ const isCreator = w.is_owner !== false;
4132
+ return /* @__PURE__ */ jsx(
4133
+ VisibilityToggle,
4134
+ {
4135
+ items: { shared: w.shared ?? false },
4136
+ onToggle: (share) => latest.current.props.onToggleShared?.(w, share),
4137
+ disabled: !isCreator || p.busyId === w.external_id,
4138
+ disabledTooltip: lb.sharedOwnerOnly,
4139
+ showLabel: false,
4140
+ "data-testid": `workflow-shared-${w.external_id}`
4141
+ }
4142
+ );
4143
+ },
4144
+ renderLastRun: (w) => {
4145
+ const { props: p, l: lb } = latest.current;
4146
+ const at = w.last_fired_at ?? p.lastRuns?.[w.external_id]?.at;
4147
+ return /* @__PURE__ */ jsx(
4148
+ "button",
4149
+ {
4150
+ type: "button",
4151
+ className: "h-full w-full truncate px-4 text-left text-sm hover:text-primary",
4152
+ onClick: () => p.onViewRuns?.(w),
4153
+ title: "View run history",
4154
+ "data-testid": `workflow-last-run-${w.external_id}`,
4155
+ children: at ? p.formatDate(at) : lb.never
4156
+ }
4157
+ );
4158
+ },
4159
+ renderResult: (w) => {
4160
+ const status = listedRunStatus(
4161
+ w.last_run_status,
4162
+ latest.current.props.lastRuns?.[w.external_id]?.threadStatus
4163
+ );
4164
+ const label = workflowRunStatusLabel(status);
4165
+ return /* @__PURE__ */ jsx(
4166
+ "button",
4167
+ {
4168
+ type: "button",
4169
+ onClick: () => latest.current.props.onViewRuns?.(w),
4170
+ title: "View run history",
4171
+ className: cn(
4172
+ badgeVariants({ variant: "outline" }),
4173
+ "mx-4 cursor-pointer text-xs font-medium",
4174
+ status === "failed" ? "border-red-border bg-red-bg text-red-text hover:bg-red-bg/80" : status === "completed" || status === "success" ? "border-green-border bg-green-bg text-green-text hover:bg-green-bg/80" : status ? "border-primary/20 bg-primary/10 text-primary hover:bg-primary/15" : "border-border bg-muted text-muted-foreground hover:bg-muted/80"
4175
+ ),
4176
+ "data-testid": `workflow-result-${w.external_id}`,
4177
+ children: label
4178
+ }
4179
+ );
4180
+ },
4181
+ renderActions: (w) => {
4182
+ const { props: p, l: lb } = latest.current;
4183
+ const id = w.external_id;
4184
+ const skill = isSkillRow(w);
4185
+ const busy = p.busyId === id;
4186
+ const isOwner = w.is_owner !== false;
4187
+ const canEdit = isOwner || Boolean(p.canEditShared);
4188
+ const canAdmin = isOwner || Boolean(p.isWorkspaceOwner);
4189
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1", children: [
4190
+ (skill || canEdit) && /* @__PURE__ */ jsxs(
4191
+ Button,
4192
+ {
4193
+ size: "sm",
4194
+ variant: "outline",
4195
+ className: "h-8 gap-1.5 pointer-coarse:h-11",
4196
+ disabled: busy,
4197
+ onClick: () => p.onRun(w),
4198
+ "data-testid": `workflow-run-btn-${id}`,
4199
+ children: [
4200
+ /* @__PURE__ */ jsx(Play, { className: "size-3.5", "aria-hidden": true }),
4201
+ lb.run
4202
+ ]
4203
+ }
4204
+ ),
4205
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
4206
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
4207
+ Button,
4208
+ {
4209
+ variant: "ghost",
4210
+ size: "icon",
4211
+ className: "size-8 pointer-coarse:size-11",
4212
+ "aria-label": lb.moreActions,
4213
+ "data-testid": `workflow-menu-${id}`,
4214
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4", "aria-hidden": true })
4215
+ }
4216
+ ) }),
4217
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
4218
+ skill && p.onRunLater && /* @__PURE__ */ jsxs(
4219
+ DropdownMenuItem,
4220
+ {
4221
+ onSelect: () => p.onRunLater?.(w),
4222
+ "data-testid": `workflow-run-later-${id}`,
4223
+ children: [
4224
+ /* @__PURE__ */ jsx(History, { className: "mr-2 size-4", "aria-hidden": true }),
4225
+ lb.runLater
4226
+ ]
4227
+ }
4228
+ ),
4229
+ !skill && canEdit && /* @__PURE__ */ jsxs(
4230
+ DropdownMenuItem,
4231
+ {
4232
+ onSelect: () => p.onEdit(w),
4233
+ "data-testid": `workflow-edit-${id}`,
4234
+ children: [
4235
+ /* @__PURE__ */ jsx(Pencil, { className: "mr-2 size-4", "aria-hidden": true }),
4236
+ lb.editWorkflow
4237
+ ]
4238
+ }
4239
+ ),
4240
+ !skill && p.onViewRuns && /* @__PURE__ */ jsxs(
4241
+ DropdownMenuItem,
4242
+ {
4243
+ onSelect: () => p.onViewRuns?.(w),
4244
+ "data-testid": `workflow-runs-${id}`,
4245
+ children: [
4246
+ /* @__PURE__ */ jsx(History, { className: "mr-2 size-4", "aria-hidden": true }),
4247
+ lb.runs
4248
+ ]
4249
+ }
4250
+ ),
4251
+ !skill && canEdit && /* @__PURE__ */ jsxs(Fragment, { children: [
4252
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
4253
+ /* @__PURE__ */ jsxs(
4254
+ DropdownMenuItem,
4255
+ {
4256
+ disabled: busy || !canAdmin,
4257
+ onSelect: () => p.onDelete(w),
4258
+ title: canAdmin ? void 0 : lb.deleteOwnerOnly,
4259
+ className: "text-destructive focus:text-destructive",
4260
+ "data-testid": `workflow-delete-${id}`,
4261
+ children: [
4262
+ /* @__PURE__ */ jsx(Trash2, { className: "mr-2 size-4", "aria-hidden": true }),
4263
+ lb.delete
4264
+ ]
4265
+ }
4266
+ )
4267
+ ] })
4268
+ ] })
4269
+ ] })
4270
+ ] });
4271
+ }
4272
+ }),
4273
+ // Stable on purpose: only the header texts rebuild the columns (see `latest`).
4274
+ // eslint-disable-next-line react-hooks/exhaustive-deps
4275
+ [
4276
+ l.colName,
4277
+ l.colType,
4278
+ l.colTrigger,
4279
+ l.colAction,
4280
+ l.colAgent,
4281
+ l.colStatus,
4282
+ l.colShared,
4283
+ l.shared,
4284
+ l.colLastRun,
4285
+ l.colNextRun
4286
+ ]
4287
+ );
4288
+ useEffect(() => {
4289
+ apiRef.current?.refreshCells({ force: true });
4290
+ }, [
4291
+ busyId,
4292
+ props.agentNames,
4293
+ props.skillNames,
4294
+ props.taskNames,
4295
+ props.lastRuns,
4296
+ props.renderPerson,
4297
+ props.labels,
4298
+ props.canEditShared,
4299
+ props.isWorkspaceOwner
4300
+ ]);
4301
+ const mine = selected.filter((w) => !isSkillRow(w));
4302
+ const adminSelected = mine.filter((w) => w.is_owner !== false || props.isWorkspaceOwner);
4303
+ const editableSelected = mine.filter(
4304
+ (w) => hasTrigger(w) && (w.is_owner !== false || props.canEditShared)
4305
+ );
4306
+ const clearSelection = () => setSelected([]);
4307
+ return /* @__PURE__ */ jsxs(
4308
+ "div",
4309
+ {
4310
+ className: "flex min-h-0 flex-1 flex-col",
4311
+ "data-testid": testIdOr(testId, { testId: "workflow-list" }),
4312
+ children: [
4313
+ /* @__PURE__ */ jsx(
4314
+ TabHeader,
4315
+ {
4316
+ testId: "automations-tab-header",
4317
+ tabs: [
4318
+ {
4319
+ id: "automations",
4320
+ label: "",
4321
+ count: shown.length,
4322
+ countIcon: /* @__PURE__ */ jsx(Workflow, { className: "size-3.5" }),
4323
+ countPlacement: "leading",
4324
+ presentation: "count",
4325
+ hasActiveFilters: !!q || [...filterStates.values()].some(Boolean)
4326
+ },
4327
+ ...props.tabs ?? []
4328
+ ],
4329
+ activeTabId: props.activeTabId ?? "automations",
4330
+ onTabChange: props.onTabChange ?? (() => {
4331
+ }),
4332
+ firstTabContent: /* @__PURE__ */ jsx(
4333
+ FilterInput,
4334
+ {
4335
+ value: query,
4336
+ onChange: setQuery,
4337
+ fields: ["text"],
4338
+ onFieldsChange: () => {
4339
+ },
4340
+ hideFieldPicker: true,
4341
+ ariaLabel: l.searchLabel,
4342
+ testIds: { input: "workflow-search" },
4343
+ filter: /* @__PURE__ */ jsx(
4344
+ TriStateFilterMenu,
4345
+ {
4346
+ testId: "automations-filter-button",
4347
+ open: filterOpen,
4348
+ onOpenChange: setFilterOpen,
4349
+ filterStates,
4350
+ onFiltersChange: setFilterStates,
4351
+ triggerClassName: "flex h-5 items-center gap-0.5 rounded px-1 text-muted-foreground hover:text-foreground pointer-coarse:h-11! pointer-coarse:min-w-11 pointer-coarse:justify-center",
4352
+ renderChevron: true,
4353
+ disableHoverOpen: true,
4354
+ options: [
4355
+ ...!props.automationsOnly ? [
4356
+ { id: "skills", label: l.filterSkills, count: counts.skills },
4357
+ {
4358
+ id: "workflows",
4359
+ label: l.filterWorkflows,
4360
+ count: counts.workflows
4361
+ }
4362
+ ] : [],
4363
+ { id: "active", label: l.filterActive, count: counts.active },
4364
+ { id: "paused", label: l.filterPaused, count: counts.paused }
4365
+ ]
4366
+ }
4367
+ )
4368
+ }
4369
+ ),
4370
+ rightContent: header
4371
+ }
4372
+ ),
4373
+ props.content ? props.content : workflows.length === 0 && !isLoading ? /* @__PURE__ */ jsx(
4374
+ EmptyState,
4375
+ {
4376
+ icon: Workflow,
4377
+ title: l.emptyTitle,
4378
+ description: l.emptyMessage,
4379
+ testId: "workflow-list-empty"
4380
+ }
4381
+ ) : /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
4382
+ GridView,
4383
+ {
4384
+ testId: "workflow-grid",
4385
+ gridId: props.gridId ?? "workflows-grid-v2",
4386
+ items: shown,
4387
+ columnDefs,
4388
+ getItemId: (w) => w.external_id,
4389
+ getRowTestId: (w) => `workflow-row-${w.external_id}`,
4390
+ isLoading,
4391
+ showEmpty: shown.length === 0,
4392
+ emptyState: { title: l.noMatches },
4393
+ rowHeight: ROW_HEIGHT,
4394
+ height: "100%",
4395
+ onGridReady: ({ api }) => {
4396
+ apiRef.current = api;
4397
+ },
4398
+ onRowClick: (row) => props.onOpenRow?.(row),
4399
+ selection: { mode: "multiple", selected, onSelectionChange: setSelected },
4400
+ gridOptions: GRID_OPTIONS
4401
+ }
4402
+ ) }),
4403
+ !props.content && /* @__PURE__ */ jsx(SelectionBar, { count: selected.length, onClear: clearSelection, children: (isCompact) => /* @__PURE__ */ jsxs(Fragment, { children: [
4404
+ props.onToggleActiveMany && /* @__PURE__ */ jsxs(Fragment, { children: [
4405
+ /* @__PURE__ */ jsx(
4406
+ SelectionBarAction,
4407
+ {
4408
+ icon: Play,
4409
+ label: l.makeActive,
4410
+ onClick: () => {
4411
+ props.onToggleActiveMany?.(editableSelected, true);
4412
+ clearSelection();
4413
+ },
4414
+ disabled: editableSelected.length === 0,
4415
+ isCompact,
4416
+ testId: "workflow-bulk-activate"
4417
+ }
4418
+ ),
4419
+ /* @__PURE__ */ jsx(
4420
+ SelectionBarAction,
4421
+ {
4422
+ icon: Workflow,
4423
+ label: l.makePaused,
4424
+ onClick: () => {
4425
+ props.onToggleActiveMany?.(editableSelected, false);
4426
+ clearSelection();
4427
+ },
4428
+ disabled: editableSelected.length === 0,
4429
+ isCompact,
4430
+ testId: "workflow-bulk-pause"
4431
+ }
4432
+ )
4433
+ ] }),
4434
+ props.onDeleteMany && /* @__PURE__ */ jsx(
4435
+ SelectionBarAction,
4436
+ {
4437
+ icon: Trash2,
4438
+ label: l.delete,
4439
+ onClick: () => {
4440
+ props.onDeleteMany?.(adminSelected);
4441
+ clearSelection();
4442
+ },
4443
+ disabled: adminSelected.length === 0,
4444
+ isCompact,
4445
+ className: "border-red-border text-red-text hover:bg-red-bg hover:text-red-text",
4446
+ testId: "workflow-bulk-delete"
4447
+ }
4448
+ )
4449
+ ] }) })
4450
+ ]
4451
+ }
4452
+ );
4453
+ }
4454
+ var GRID_OPTIONS = { suppressClickEdit: true };
2566
4455
  var DEFAULT_TASK_ACTIVITY_LABELS = {
2567
4456
  title: "Activity",
2568
4457
  empty: "Nothing has happened yet.",
@@ -2755,6 +4644,6 @@ function TaskActivityTimeline({
2755
4644
  }) });
2756
4645
  }
2757
4646
 
2758
- export { ActionStepBody, ApprovalEvidence, AssigneeLine, DEFAULT_SCHEMA_FORM_LABELS, DEFAULT_STEP_BAR_LABELS, DEFAULT_TASK_ACTIVITY_LABELS, DEFAULT_WORKFLOW_RUNNER_LABELS, DEFAULT_WORKFLOW_STEP_PARTS_LABELS, SKILL_MAIN_FILENAME, SKILL_TYPES, STEP_LABEL, STEP_LABEL_CURRENT, STEP_LABEL_MAX, SchemaForm, SendBackControl, StepBarFrame, TaskActivityTimeline, WorkflowRunner, WorkflowStepBar, activeStepId, buildZipSkillImport, fromPlanAssignee, fromPlanDocumentRef, fromPlanSteps, importMarkdownSkill, importSkillFile, importSkillFromUrl, isIncompleteSignal, isMainSkillFile, layoutFromPlanSteps, parseSkillFrontmatter, readSkillArchive, serializeSkill, skillSlug, stepWindow, taskActivityHeadline, toPlanAssignee, toPlanDocumentRef, toPlanSteps, useWorkflowView, workflowFromSkillMarkdown, workflowToSkillMarkdown };
2759
- //# sourceMappingURL=chunk-IQU2GI6V.js.map
2760
- //# sourceMappingURL=chunk-IQU2GI6V.js.map
4647
+ export { ActionStepBody, AgentSelect, ApprovalEvidence, AssigneeLine, DEFAULT_AGENT_SELECT_LABELS, DEFAULT_SCHEMA_FORM_LABELS, DEFAULT_SKILL_EDIT_FORM_LABELS, DEFAULT_SKILL_GENERATE_FORM_LABELS, DEFAULT_SKILL_GLOBAL_BADGE_LABELS, DEFAULT_SKILL_VIEW_TOGGLE_LABELS, DEFAULT_STEP_BAR_LABELS, DEFAULT_TASK_ACTIVITY_LABELS, DEFAULT_TASK_TRIGGER_CHIP_LABELS, DEFAULT_WORKFLOW_CONFIG_DIALOG_LABELS, DEFAULT_WORKFLOW_DOCUMENTS_FIELD_LABELS, DEFAULT_WORKFLOW_LIST_LABELS, DEFAULT_WORKFLOW_RUNNER_LABELS, DEFAULT_WORKFLOW_STEP_PARTS_LABELS, PersonPill, SKILL_FIELD_TYPE_LABELS, SKILL_MAIN_FILENAME, SKILL_SOURCE_LABELS, SKILL_TYPES, SKILL_TYPE_LABELS, STEP_LABEL, STEP_LABEL_CURRENT, STEP_LABEL_MAX, SchemaForm, SendBackControl, SkillActionButton, SkillDetail, SkillDetailSection, SkillEditForm, SkillFilterChips, SkillGenerateForm, SkillGlobalBadge, SkillLibraryDetail, SkillTile, SkillTypeBadge, SkillViewToggle, StepBarFrame, TaskActivityTimeline, TaskTriggerChip, WORKFLOW_COLUMN_ORDER, WorkflowConfigDialog, WorkflowDocumentsField, WorkflowLauncher, WorkflowRunner, WorkflowStepBar, WorkflowsTable, activateOnKey, activeStepId, buildLibrarySkills, buildWorkflowScopeSource, buildZipSkillImport, createWorkflowColumns, documentScopeLabels, filterLibrarySkills, fromPlanAssignee, fromPlanDocumentRef, fromPlanSteps, hasTrigger, importMarkdownSkill, importSkillFile, importSkillFromUrl, isIncompleteSignal, isMainSkillFile, isSkillRow, layoutFromPlanSteps, matchWorkflowScope, parseSkillFrontmatter, readSkillArchive, serializeSkill, skillSlug, stepWindow, taskActivityHeadline, toPlanAssignee, toPlanDocumentRef, toPlanSteps, useControllableState, useWorkflowView, workflowFromSkillMarkdown, workflowRunStatusLabel, workflowStatus, workflowToSkillMarkdown, workflowTriggerType };
4648
+ //# sourceMappingURL=chunk-XVME4XYD.js.map
4649
+ //# sourceMappingURL=chunk-XVME4XYD.js.map