@arbidocs/blocks 0.4.75 → 0.4.76

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 (48) hide show
  1. package/dist/agents/index.cjs +265 -253
  2. package/dist/agents/index.js +3 -3
  3. package/dist/agents/tasks/TaskActivityTimeline.d.ts +1 -1
  4. package/dist/agents/tasks/WorkflowThreadStrip.d.ts +38 -1
  5. package/dist/agents/tasks/index.d.ts +2 -1
  6. package/dist/agents/tasks/linkedRuns.d.ts +31 -0
  7. package/dist/agents/workflows/index.d.ts +1 -1
  8. package/dist/agents/workflows/planSteps.d.ts +3 -1
  9. package/dist/agents/workflows/types.d.ts +27 -0
  10. package/dist/{chunk-YWYVIQ7T.js → chunk-22P5KNIM.js} +24 -4
  11. package/dist/chunk-22P5KNIM.js.map +1 -0
  12. package/dist/{chunk-F4LUD3YC.cjs → chunk-7FKARMA3.cjs} +158 -53
  13. package/dist/chunk-7FKARMA3.cjs.map +1 -0
  14. package/dist/{chunk-LT2UORRG.js → chunk-TKB7TVMW.js} +132 -29
  15. package/dist/chunk-TKB7TVMW.js.map +1 -0
  16. package/dist/{chunk-P2RBE3FD.cjs → chunk-VYRKO3XU.cjs} +24 -3
  17. package/dist/chunk-VYRKO3XU.cjs.map +1 -0
  18. package/dist/connect/index.cjs +10 -10
  19. package/dist/connect/index.js +3 -3
  20. package/dist/facts/index.cjs +1 -1
  21. package/dist/facts/index.js +1 -1
  22. package/dist/files/index.cjs +1 -1
  23. package/dist/files/index.js +1 -1
  24. package/dist/home/index.cjs +1 -1
  25. package/dist/home/index.js +1 -1
  26. package/dist/index.cjs +430 -418
  27. package/dist/index.js +7 -7
  28. package/dist/settings/index.cjs +1 -1
  29. package/dist/settings/index.js +1 -1
  30. package/dist/shell/index.cjs +1 -1
  31. package/dist/shell/index.js +1 -1
  32. package/dist/studio/index.cjs +1 -1
  33. package/dist/studio/index.js +1 -1
  34. package/dist/workflow-canvas/SignalsEditor.d.ts +41 -0
  35. package/dist/workflow-canvas/StepInspector.d.ts +4 -1
  36. package/dist/workflow-canvas/TriggerInspector.d.ts +11 -2
  37. package/dist/workflow-canvas/WorkflowBuilder.d.ts +7 -2
  38. package/dist/workflow-canvas/index.cjs +443 -149
  39. package/dist/workflow-canvas/index.cjs.map +1 -1
  40. package/dist/workflow-canvas/index.d.ts +2 -1
  41. package/dist/workflow-canvas/index.js +410 -123
  42. package/dist/workflow-canvas/index.js.map +1 -1
  43. package/dist/workflow-canvas/trigger.d.ts +20 -1
  44. package/package.json +4 -4
  45. package/dist/chunk-F4LUD3YC.cjs.map +0 -1
  46. package/dist/chunk-LT2UORRG.js.map +0 -1
  47. package/dist/chunk-P2RBE3FD.cjs.map +0 -1
  48. package/dist/chunk-YWYVIQ7T.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { useConnection } from '../chunk-OYMWV45N.js';
3
- import { describeRRule, RecurrenceBuilder, AgentSelect, useWorkflowPreview, workflowWhen, isoToLocalInput, localInputToIso } from '../chunk-LT2UORRG.js';
3
+ import { workflowWhen, isoToLocalInput, describeRRule, RecurrenceBuilder, AgentSelect, useWorkflowPreview, localInputToIso } from '../chunk-TKB7TVMW.js';
4
4
  import '../chunk-CKB64Z4R.js';
5
5
  import '../chunk-P6GHJSLR.js';
6
- import { workflowSteps, dependsOn, stepLevels, stepDependencies, runFields, validateWorkflowGraph, WorkflowRunner, fromPlanSteps, layoutFromPlanSteps, skillToWorkflow, orderSteps, toPlanSteps } from '../chunk-YWYVIQ7T.js';
6
+ import { workflowSteps, dependsOn, stepLevels, isIncompleteSignal, stepDependencies, runFields, fromPlanSteps, layoutFromPlanSteps, validateWorkflowGraph, WorkflowRunner, skillToWorkflow, orderSteps, toPlanSteps } from '../chunk-22P5KNIM.js';
7
7
  import '../chunk-4WWM3GQ5.js';
8
8
  import '../chunk-RFXJYIDV.js';
9
9
  import '../chunk-DPLRHEJZ.js';
@@ -14,19 +14,19 @@ import '../chunk-ZPLG7QYF.js';
14
14
  import '../chunk-DDF5GPIJ.js';
15
15
  import '../chunk-XD2M6LIG.js';
16
16
  import '../chunk-S7PSMMRG.js';
17
+ import '../chunk-76JDDMSP.js';
17
18
  import '../chunk-HOXKR26S.js';
18
19
  import '../chunk-AA3PT6KR.js';
19
20
  import '../chunk-SLSBJRWW.js';
20
21
  import '../chunk-NPLYHR2E.js';
21
- import '../chunk-76JDDMSP.js';
22
22
  import '../chunk-OXF2IFXG.js';
23
23
  import '../chunk-TX2ZICS7.js';
24
24
  import '../chunk-7F6CBP3Z.js';
25
25
  import '../chunk-VUNV25KB.js';
26
26
  import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
27
27
  import { useWorkflows, useSkills, useAgents, useConversations, useCreateWorkflow, useUpdateWorkflow, useRunWorkflow } from '@arbidocs/react';
28
- import { Input, Textarea, Checkbox, Button, cn, Label, Popover, PopoverTrigger, PopoverContent } from '@arbidocs/react/ui';
29
- import { X, Trash2, Plus, Maximize, ClipboardCheck, Bot, Upload, FileInput, PencilRuler, Eye, LayoutGrid, Settings2, Play, Loader2, Save, Zap, GitBranch, Check, AlertTriangle, MinusCircle } from 'lucide-react';
28
+ import { Label, Input, cn, Button, Textarea, Checkbox, Popover, PopoverTrigger, PopoverContent } from '@arbidocs/react/ui';
29
+ import { X, Plus, Trash2, Maximize, ClipboardCheck, Bot, Upload, FileInput, PencilRuler, Eye, LayoutGrid, Settings2, Play, Loader2, Save, Zap, GitBranch, Check, AlertTriangle, MinusCircle } from 'lucide-react';
30
30
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
31
31
  import { MarkerType, applyNodeChanges, ReactFlow, Background, BackgroundVariant, Controls, ControlButton, MiniMap, Handle, Position } from '@xyflow/react';
32
32
 
@@ -165,6 +165,222 @@ function layoutWithDefaults(workflow) {
165
165
  const auto = autoLayout(workflowSteps(workflow));
166
166
  return { ...auto, ...layoutOf(workflow) };
167
167
  }
168
+ var DEFAULT_SIGNALS_EDITOR_LABELS = {
169
+ heading: "When done, signal\u2026",
170
+ hint: "Another workflow can start on a signal (\u201CWhen a step signals\u201D), as its own thread linked to this one.",
171
+ add: "Add signal",
172
+ name: "Signal",
173
+ namePlaceholder: "e.g. risk-high",
174
+ when: "When",
175
+ always: "Whenever it is done",
176
+ fieldEquals: "A field equals",
177
+ answerContains: "The answer contains",
178
+ answerMatches: "The answer matches (regex)",
179
+ approved: "It is approved",
180
+ field: "Field",
181
+ value: "Value",
182
+ phrase: "Phrase",
183
+ phrasePlaceholder: "e.g. Risk rating: High",
184
+ pattern: "Pattern",
185
+ remove: "Remove signal"
186
+ };
187
+ function signalConditions(kind) {
188
+ switch (kind) {
189
+ case "form":
190
+ return ["always", "field"];
191
+ case "agent":
192
+ return ["always", "contains", "matches"];
193
+ case "review":
194
+ return ["approved", "always"];
195
+ default:
196
+ return ["always"];
197
+ }
198
+ }
199
+ function conditionOf(when) {
200
+ if (!when) return "always";
201
+ if ("field" in when) return "field";
202
+ if ("contains" in when) return "contains";
203
+ if ("matches" in when) return "matches";
204
+ return "approved";
205
+ }
206
+ function signalSlug(text) {
207
+ return text.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^[^a-z0-9]+/, "").slice(0, 64);
208
+ }
209
+ function startCondition(kind, step) {
210
+ switch (kind) {
211
+ case "field":
212
+ return { field: step.kind === "form" ? step.inputs[0]?.name ?? "" : "", equals: "" };
213
+ case "contains":
214
+ return { contains: "" };
215
+ case "matches":
216
+ return { matches: "" };
217
+ case "approved":
218
+ return { approved: true };
219
+ default:
220
+ return void 0;
221
+ }
222
+ }
223
+ function newSignal(step, taken) {
224
+ const base = signalSlug(step.id) || "signal";
225
+ let name = `${base}-done`;
226
+ let n = 2;
227
+ while (taken.includes(name)) name = `${base}-done-${n++}`;
228
+ const when = startCondition(signalConditions(step.kind)[0], step);
229
+ return when ? { name, when } : { name };
230
+ }
231
+ var selectClass = "flex h-8 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
232
+ function SignalsEditor({
233
+ step,
234
+ onChange,
235
+ labels,
236
+ testId = "signals-editor"
237
+ }) {
238
+ const l = { ...DEFAULT_SIGNALS_EDITOR_LABELS, ...labels };
239
+ const signals = step.signals ?? [];
240
+ const conditions = signalConditions(step.kind);
241
+ const conditionLabel = {
242
+ always: l.always,
243
+ field: l.fieldEquals,
244
+ contains: l.answerContains,
245
+ matches: l.answerMatches,
246
+ approved: l.approved
247
+ };
248
+ const emit = (next) => onChange(next.length ? next : void 0);
249
+ const set = (i, signal) => emit(signals.map((s, j) => j === i ? signal : s));
250
+ const fields = step.kind === "form" ? step.inputs : [];
251
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [
252
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", children: l.heading }),
253
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
254
+ signals.map((signal, i) => {
255
+ const condition = conditionOf(signal.when);
256
+ const when = signal.when;
257
+ return /* @__PURE__ */ jsxs(
258
+ "div",
259
+ {
260
+ className: cn(
261
+ "space-y-1.5 rounded-md border p-2",
262
+ isIncompleteSignal(signal) ? "border-destructive/50" : "border-border"
263
+ ),
264
+ "data-incomplete": isIncompleteSignal(signal) || void 0,
265
+ "data-testid": `${testId}-${i}`,
266
+ children: [
267
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
268
+ /* @__PURE__ */ jsx(
269
+ Input,
270
+ {
271
+ value: signal.name,
272
+ "aria-label": l.name,
273
+ placeholder: l.namePlaceholder,
274
+ onChange: (e) => set(i, { ...signal, name: signalSlug(e.target.value) }),
275
+ className: "h-8 font-mono text-xs",
276
+ "data-testid": `${testId}-${i}-name`
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsx(
280
+ "button",
281
+ {
282
+ type: "button",
283
+ "aria-label": l.remove,
284
+ onClick: () => emit(signals.filter((_, j) => j !== i)),
285
+ "data-testid": `${testId}-${i}-remove`,
286
+ children: /* @__PURE__ */ jsx(X, { className: "size-4 text-muted-foreground" })
287
+ }
288
+ )
289
+ ] }),
290
+ /* @__PURE__ */ jsx(
291
+ "select",
292
+ {
293
+ "aria-label": l.when,
294
+ className: selectClass,
295
+ value: condition,
296
+ onChange: (e) => {
297
+ const nextWhen = startCondition(e.target.value, step);
298
+ set(i, nextWhen ? { name: signal.name, when: nextWhen } : { name: signal.name });
299
+ },
300
+ "data-testid": `${testId}-${i}-when`,
301
+ children: [...conditions, ...conditions.includes(condition) ? [] : [condition]].map(
302
+ (c) => /* @__PURE__ */ jsx("option", { value: c, children: conditionLabel[c] }, c)
303
+ )
304
+ }
305
+ ),
306
+ when && "field" in when && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
307
+ /* @__PURE__ */ jsxs(
308
+ "select",
309
+ {
310
+ "aria-label": l.field,
311
+ className: cn(selectClass, "flex-1"),
312
+ value: when.field,
313
+ onChange: (e) => set(i, { ...signal, when: { ...when, field: e.target.value } }),
314
+ "data-testid": `${testId}-${i}-field`,
315
+ children: [
316
+ fields.map((f) => /* @__PURE__ */ jsx("option", { value: f.name, children: f.label }, f.name)),
317
+ !fields.some((f) => f.name === when.field) && /* @__PURE__ */ jsx("option", { value: when.field, children: when.field || "\u2014" })
318
+ ]
319
+ }
320
+ ),
321
+ /* @__PURE__ */ jsx(
322
+ Input,
323
+ {
324
+ "aria-label": l.value,
325
+ placeholder: l.value,
326
+ value: String(when.equals),
327
+ onChange: (e) => set(i, { ...signal, when: { ...when, equals: e.target.value } }),
328
+ className: "h-8 flex-1",
329
+ "data-testid": `${testId}-${i}-equals`
330
+ }
331
+ )
332
+ ] }),
333
+ when && "contains" in when && /* @__PURE__ */ jsx(
334
+ Input,
335
+ {
336
+ "aria-label": l.phrase,
337
+ placeholder: l.phrasePlaceholder,
338
+ value: when.contains,
339
+ onChange: (e) => set(i, { ...signal, when: { contains: e.target.value } }),
340
+ className: "h-8",
341
+ "data-testid": `${testId}-${i}-contains`
342
+ }
343
+ ),
344
+ when && "matches" in when && /* @__PURE__ */ jsx(
345
+ Input,
346
+ {
347
+ "aria-label": l.pattern,
348
+ placeholder: "risk rating:\\s*high",
349
+ value: when.matches,
350
+ onChange: (e) => set(i, { ...signal, when: { matches: e.target.value } }),
351
+ className: "h-8 font-mono text-xs",
352
+ "data-testid": `${testId}-${i}-matches`
353
+ }
354
+ )
355
+ ]
356
+ },
357
+ i
358
+ );
359
+ }),
360
+ /* @__PURE__ */ jsxs(
361
+ Button,
362
+ {
363
+ variant: "outline",
364
+ size: "sm",
365
+ onClick: () => emit([
366
+ ...signals,
367
+ newSignal(
368
+ step,
369
+ signals.map((s) => s.name)
370
+ )
371
+ ]),
372
+ "data-testid": `${testId}-add`,
373
+ children: [
374
+ /* @__PURE__ */ jsx(Plus, { className: "size-4" }),
375
+ " ",
376
+ l.add
377
+ ]
378
+ }
379
+ )
380
+ ] }),
381
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.hint })
382
+ ] });
383
+ }
168
384
  var DEFAULT_STEP_INSPECTOR_LABELS = {
169
385
  workflow: "Workflow",
170
386
  title: "Title",
@@ -197,7 +413,7 @@ var DEFAULT_STEP_INSPECTOR_LABELS = {
197
413
  deleteStep: "Delete step"
198
414
  };
199
415
  var FIELD_TYPES = ["text", "longtext", "boolean", "number", "date", "scope"];
200
- var selectClass = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
416
+ var selectClass2 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
201
417
  function Field({ label, children, hint }) {
202
418
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
203
419
  /* @__PURE__ */ jsx(Label, { className: "text-xs", children: label }),
@@ -219,6 +435,7 @@ function StepInspector({
219
435
  onSelect,
220
436
  workflows = [],
221
437
  labels,
438
+ signalLabels,
222
439
  testId = "step-inspector"
223
440
  }) {
224
441
  const l = { ...DEFAULT_STEP_INSPECTOR_LABELS, ...labels };
@@ -351,6 +568,15 @@ function StepInspector({
351
568
  "data-testid": `${testId}-instructions`
352
569
  }
353
570
  ) }),
571
+ /* @__PURE__ */ jsx(
572
+ SignalsEditor,
573
+ {
574
+ step,
575
+ onChange: (signals) => patch({ signals }),
576
+ labels: signalLabels,
577
+ testId: `${testId}-signals`
578
+ }
579
+ ),
354
580
  /* @__PURE__ */ jsxs(
355
581
  Button,
356
582
  {
@@ -412,7 +638,7 @@ function FormFields({
412
638
  "select",
413
639
  {
414
640
  "aria-label": l.fieldType,
415
- className: cn(selectClass, "h-8"),
641
+ className: cn(selectClass2, "h-8"),
416
642
  value: f.type,
417
643
  onChange: (e) => set(i, { type: e.target.value }),
418
644
  children: FIELD_TYPES.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: t }, t))
@@ -479,7 +705,7 @@ function AgentSettings({
479
705
  /* @__PURE__ */ jsx(Field, { label: l.runs, children: /* @__PURE__ */ jsxs(
480
706
  "select",
481
707
  {
482
- className: selectClass,
708
+ className: selectClass2,
483
709
  value,
484
710
  onChange: (e) => {
485
711
  const id = e.target.value;
@@ -536,7 +762,7 @@ function AgentSettings({
536
762
  /* @__PURE__ */ jsx(Field, { label: l.thread, children: /* @__PURE__ */ jsxs(
537
763
  "select",
538
764
  {
539
- className: selectClass,
765
+ className: selectClass2,
540
766
  value: step.branch ? "branch" : "main",
541
767
  onChange: (e) => patch({ branch: e.target.value === "branch" || void 0 }),
542
768
  "data-testid": `${testId}-thread`,
@@ -549,7 +775,7 @@ function AgentSettings({
549
775
  /* @__PURE__ */ jsx(Field, { label: l.output, children: /* @__PURE__ */ jsxs(
550
776
  "select",
551
777
  {
552
- className: selectClass,
778
+ className: selectClass2,
553
779
  value: step.output ?? "answer",
554
780
  onChange: (e) => patch({ output: e.target.value === "artifact" ? "artifact" : void 0 }),
555
781
  children: [
@@ -560,6 +786,138 @@ function AgentSettings({
560
786
  ) })
561
787
  ] });
562
788
  }
789
+
790
+ // src/workflow-canvas/trigger.ts
791
+ var WORKFLOW_SIGNAL_EVENT = "workflow_signal";
792
+ var DEFAULT_WORKFLOW_TRIGGER = {
793
+ start: "manual",
794
+ runAt: "",
795
+ rrule: "",
796
+ timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
797
+ eventKind: "",
798
+ signal: "",
799
+ afterTaskId: "",
800
+ agentId: "",
801
+ access: "ask_each_run"
802
+ };
803
+ function triggerProblem(trigger) {
804
+ if (trigger.start === "once" && !trigger.runAt) return "runAt";
805
+ if (trigger.start === "repeat" && !trigger.rrule.trim()) return "rrule";
806
+ if (trigger.start === "event" && !trigger.eventKind.trim()) return "event";
807
+ if (isSignalTrigger(trigger) && !trigger.signal.trim()) return "signal";
808
+ if (trigger.start === "task" && !trigger.afterTaskId) return "task";
809
+ return null;
810
+ }
811
+ var isSignalTrigger = (trigger) => trigger.start === "event" && trigger.eventKind.trim() === WORKFLOW_SIGNAL_EVENT;
812
+ function eventFilter(trigger) {
813
+ if (trigger.start !== "event") return null;
814
+ if (isSignalTrigger(trigger)) return { signal: trigger.signal.trim() };
815
+ return trigger.eventFilter && Object.keys(trigger.eventFilter).length ? trigger.eventFilter : null;
816
+ }
817
+ function triggerFields(trigger, clearOthers) {
818
+ const none = clearOthers ? null : void 0;
819
+ const filter = eventFilter(trigger);
820
+ return {
821
+ timezone: trigger.timezone,
822
+ rrule: trigger.start === "repeat" ? trigger.rrule.trim() : none,
823
+ run_at: trigger.start === "once" ? localInputToIso(trigger.runAt) : none,
824
+ event_kind: trigger.start === "event" ? trigger.eventKind.trim() : none,
825
+ // A step signal is `workflow_signal` narrowed to one signal name.
826
+ event_filter: filter ?? none,
827
+ after_conversation_ext_id: trigger.start === "task" ? trigger.afterTaskId : none,
828
+ on_demand: trigger.start === "manual"
829
+ };
830
+ }
831
+ var authMode = (trigger) => trigger.start === "manual" ? "run_now_temp" : trigger.access === "standing" ? "scheduled_permanent" : "scheduled_require_auth";
832
+ var withoutUndefined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
833
+ function plan(value, options) {
834
+ return {
835
+ goal: value.workflow.description,
836
+ criteria: [],
837
+ max_passes: 3,
838
+ documents: [],
839
+ steps: toPlanSteps(value.workflow, options)
840
+ };
841
+ }
842
+ function builderToWorkflowRequest(value, workspaceId, options = {}) {
843
+ return withoutUndefined({
844
+ workspace_ext_id: workspaceId,
845
+ name: value.workflow.title,
846
+ description: value.workflow.description,
847
+ active: true,
848
+ shared: true,
849
+ ...triggerFields(value.trigger, false),
850
+ prompt: "",
851
+ skill_ref: null,
852
+ skill_inputs: null,
853
+ agent_ext_id: value.trigger.agentId || null,
854
+ auth_mode: authMode(value.trigger),
855
+ plan: plan(value, options)
856
+ });
857
+ }
858
+ function builderToWorkflowUpdate(value, options = {}) {
859
+ return withoutUndefined({
860
+ name: value.workflow.title,
861
+ description: value.workflow.description,
862
+ ...triggerFields(value.trigger, true),
863
+ // The steps are what runs (a classic automation's single prompt or skill becomes its one step).
864
+ prompt: "",
865
+ skill_ref: null,
866
+ skill_inputs: null,
867
+ agent_ext_id: value.trigger.agentId || null,
868
+ auth_mode: authMode(value.trigger),
869
+ plan: plan(value, options)
870
+ });
871
+ }
872
+ function workflowRecordToBuilder(record) {
873
+ const planned = record.plan?.steps ?? [];
874
+ const steps = planned.length ? fromPlanSteps(planned) : record.prompt || record.skill_ref ? [
875
+ {
876
+ id: "work",
877
+ kind: "agent",
878
+ title: record.name,
879
+ ...record.skill_ref ? { workflow: record.skill_ref } : { run: { kind: "prompt", promptTemplate: record.prompt } }
880
+ }
881
+ ] : [];
882
+ const when = workflowWhen(record);
883
+ return {
884
+ workflow: {
885
+ id: record.external_id,
886
+ title: record.name,
887
+ description: record.description ?? "",
888
+ inputs: [],
889
+ landing: "chat",
890
+ schedulable: false,
891
+ run: { kind: "steps", steps, layout: layoutFromPlanSteps(planned ?? []) }
892
+ },
893
+ trigger: {
894
+ ...DEFAULT_WORKFLOW_TRIGGER,
895
+ start: when === "now" ? "manual" : when,
896
+ runAt: isoToLocalInput(record.run_at),
897
+ rrule: record.rrule ?? "",
898
+ timezone: record.timezone || DEFAULT_WORKFLOW_TRIGGER.timezone,
899
+ eventKind: record.event_kind ?? "",
900
+ signal: record.event_kind === WORKFLOW_SIGNAL_EVENT && typeof record.event_filter?.signal === "string" ? record.event_filter.signal : "",
901
+ eventFilter: record.event_kind && record.event_kind !== WORKFLOW_SIGNAL_EVENT && record.event_filter ? record.event_filter : void 0,
902
+ afterTaskId: record.after_conversation_ext_id ?? "",
903
+ agentId: record.agent_ext_id ?? "",
904
+ access: record.auth_mode === "scheduled_permanent" ? "standing" : "ask_each_run"
905
+ }
906
+ };
907
+ }
908
+ function declaredSignals(records, current) {
909
+ const out = /* @__PURE__ */ new Map();
910
+ const add = (workflow, steps) => {
911
+ for (const step of steps)
912
+ for (const signal of step.signals ?? [])
913
+ if (signal.name && !out.has(signal.name))
914
+ out.set(signal.name, { value: signal.name, label: `${workflow} \u203A ${step.title}` });
915
+ };
916
+ for (const record of records)
917
+ add(record.name, record.plan?.steps ?? []);
918
+ if (current?.run.kind === "steps") add(current.title, current.run.steps);
919
+ return [...out.values()];
920
+ }
563
921
  var DEFAULT_TRIGGER_INSPECTOR_LABELS = {
564
922
  heading: "Start",
565
923
  start: "Starts",
@@ -573,6 +931,11 @@ var DEFAULT_TRIGGER_INSPECTOR_LABELS = {
573
931
  runAt: "When",
574
932
  event: "Event",
575
933
  eventPlaceholder: "Pick an event",
934
+ stepSignals: "When a step signals",
935
+ signal: "Signal",
936
+ signalPlaceholder: "e.g. risk-high",
937
+ signalHint: "Starts as its own thread, linked to the thread whose step raised it, with that run\u2019s inputs.",
938
+ signalSummary: "When a step signals \u201C{signal}\u201D",
576
939
  afterTask: "After this task",
577
940
  afterTaskPlaceholder: "Pick a task",
578
941
  agent: "Runs as",
@@ -588,6 +951,8 @@ function triggerSummary(trigger, options = {}) {
588
951
  case "repeat":
589
952
  return trigger.rrule ? describeRRule(trigger.rrule) : l.starts.repeat;
590
953
  case "event":
954
+ if (isSignalTrigger(trigger))
955
+ return trigger.signal ? l.signalSummary.replace("{signal}", trigger.signal) : l.stepSignals;
591
956
  return trigger.eventKind && options.eventLabel?.(trigger.eventKind) || trigger.eventKind || l.starts.event;
592
957
  case "task":
593
958
  return trigger.afterTaskId && options.taskLabel?.(trigger.afterTaskId) || l.starts.task;
@@ -595,7 +960,7 @@ function triggerSummary(trigger, options = {}) {
595
960
  return l.starts.manual;
596
961
  }
597
962
  }
598
- var selectClass2 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
963
+ var selectClass3 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
599
964
  var STARTS = ["manual", "once", "repeat", "event", "task"];
600
965
  function TriggerInspector({
601
966
  trigger,
@@ -603,11 +968,13 @@ function TriggerInspector({
603
968
  agents = [],
604
969
  eventKinds = [],
605
970
  tasks = [],
971
+ signals = [],
606
972
  labels,
607
973
  testId = "trigger-inspector"
608
974
  }) {
609
975
  const l = { ...DEFAULT_TRIGGER_INSPECTOR_LABELS, ...labels };
610
976
  const set = (patch) => onChange({ ...trigger, ...patch });
977
+ const events = eventKinds.some((k) => k.value === WORKFLOW_SIGNAL_EVENT) ? eventKinds : [...eventKinds, { value: WORKFLOW_SIGNAL_EVENT, label: l.stepSignals }];
611
978
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-testid": testId, children: [
612
979
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: l.heading }),
613
980
  /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
@@ -647,23 +1014,39 @@ function TriggerInspector({
647
1014
  /* @__PURE__ */ jsxs(
648
1015
  "select",
649
1016
  {
650
- className: selectClass2,
1017
+ className: selectClass3,
651
1018
  value: trigger.eventKind,
652
- onChange: (e) => set({ eventKind: e.target.value }),
1019
+ onChange: (e) => set({ eventKind: e.target.value, eventFilter: void 0 }),
653
1020
  "data-testid": `${testId}-event`,
654
1021
  children: [
655
1022
  /* @__PURE__ */ jsx("option", { value: "", children: l.eventPlaceholder }),
656
- eventKinds.map((k) => /* @__PURE__ */ jsx("option", { value: k.value, children: k.label }, k.value))
1023
+ events.map((k) => /* @__PURE__ */ jsx("option", { value: k.value, children: k.label }, k.value))
657
1024
  ]
658
1025
  }
659
1026
  )
660
1027
  ] }),
1028
+ isSignalTrigger(trigger) && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1029
+ /* @__PURE__ */ jsx(Label, { className: "text-xs", htmlFor: `${testId}-signal`, children: l.signal }),
1030
+ /* @__PURE__ */ jsx(
1031
+ Input,
1032
+ {
1033
+ id: `${testId}-signal`,
1034
+ list: `${testId}-signal-options`,
1035
+ value: trigger.signal,
1036
+ placeholder: l.signalPlaceholder,
1037
+ onChange: (e) => set({ signal: e.target.value.trim().toLowerCase() }),
1038
+ "data-testid": `${testId}-signal`
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsx("datalist", { id: `${testId}-signal-options`, children: signals.map((s) => /* @__PURE__ */ jsx("option", { value: s.value, children: s.label }, s.value)) }),
1042
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.signalHint })
1043
+ ] }),
661
1044
  trigger.start === "task" && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
662
1045
  /* @__PURE__ */ jsx(Label, { className: "text-xs", children: l.afterTask }),
663
1046
  /* @__PURE__ */ jsxs(
664
1047
  "select",
665
1048
  {
666
- className: selectClass2,
1049
+ className: selectClass3,
667
1050
  value: trigger.afterTaskId,
668
1051
  onChange: (e) => set({ afterTaskId: e.target.value }),
669
1052
  "data-testid": `${testId}-after-task`,
@@ -692,7 +1075,7 @@ function TriggerInspector({
692
1075
  /* @__PURE__ */ jsxs(
693
1076
  "select",
694
1077
  {
695
- className: selectClass2,
1078
+ className: selectClass3,
696
1079
  value: trigger.access,
697
1080
  onChange: (e) => set({ access: e.target.value === "standing" ? "standing" : "ask_each_run" }),
698
1081
  "data-testid": `${testId}-access`,
@@ -1188,6 +1571,7 @@ function WorkflowBuilder({
1188
1571
  agents = [],
1189
1572
  eventKinds = [],
1190
1573
  tasks = [],
1574
+ signals = [],
1191
1575
  compose,
1192
1576
  renderIcon,
1193
1577
  actions,
@@ -1370,6 +1754,12 @@ function WorkflowBuilder({
1370
1754
  agents,
1371
1755
  eventKinds,
1372
1756
  tasks,
1757
+ signals: [
1758
+ ...signals,
1759
+ ...declaredSignals([], workflow).filter(
1760
+ (d) => !signals.some((s) => s.value === d.value)
1761
+ )
1762
+ ],
1373
1763
  labels: triggerLabels,
1374
1764
  testId: `${testId}-trigger`
1375
1765
  }
@@ -1447,111 +1837,6 @@ function WorkflowPreview({
1447
1837
  }
1448
1838
  );
1449
1839
  }
1450
-
1451
- // src/workflow-canvas/trigger.ts
1452
- var DEFAULT_WORKFLOW_TRIGGER = {
1453
- start: "manual",
1454
- runAt: "",
1455
- rrule: "",
1456
- timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
1457
- eventKind: "",
1458
- afterTaskId: "",
1459
- agentId: "",
1460
- access: "ask_each_run"
1461
- };
1462
- function triggerProblem(trigger) {
1463
- if (trigger.start === "once" && !trigger.runAt) return "runAt";
1464
- if (trigger.start === "repeat" && !trigger.rrule.trim()) return "rrule";
1465
- if (trigger.start === "event" && !trigger.eventKind.trim()) return "event";
1466
- if (trigger.start === "task" && !trigger.afterTaskId) return "task";
1467
- return null;
1468
- }
1469
- function triggerFields(trigger, clearOthers) {
1470
- const none = clearOthers ? null : void 0;
1471
- return {
1472
- timezone: trigger.timezone,
1473
- rrule: trigger.start === "repeat" ? trigger.rrule.trim() : none,
1474
- run_at: trigger.start === "once" ? localInputToIso(trigger.runAt) : none,
1475
- event_kind: trigger.start === "event" ? trigger.eventKind.trim() : none,
1476
- after_conversation_ext_id: trigger.start === "task" ? trigger.afterTaskId : none,
1477
- on_demand: trigger.start === "manual"
1478
- };
1479
- }
1480
- var authMode = (trigger) => trigger.start === "manual" ? "run_now_temp" : trigger.access === "standing" ? "scheduled_permanent" : "scheduled_require_auth";
1481
- var withoutUndefined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
1482
- function plan(value, options) {
1483
- return {
1484
- goal: value.workflow.description,
1485
- criteria: [],
1486
- max_passes: 3,
1487
- documents: [],
1488
- steps: toPlanSteps(value.workflow, options)
1489
- };
1490
- }
1491
- function builderToWorkflowRequest(value, workspaceId, options = {}) {
1492
- return withoutUndefined({
1493
- workspace_ext_id: workspaceId,
1494
- name: value.workflow.title,
1495
- description: value.workflow.description,
1496
- active: true,
1497
- shared: true,
1498
- ...triggerFields(value.trigger, false),
1499
- prompt: "",
1500
- skill_ref: null,
1501
- skill_inputs: null,
1502
- agent_ext_id: value.trigger.agentId || null,
1503
- auth_mode: authMode(value.trigger),
1504
- plan: plan(value, options)
1505
- });
1506
- }
1507
- function builderToWorkflowUpdate(value, options = {}) {
1508
- return withoutUndefined({
1509
- name: value.workflow.title,
1510
- description: value.workflow.description,
1511
- ...triggerFields(value.trigger, true),
1512
- // The steps are what runs (a classic automation's single prompt or skill becomes its one step).
1513
- prompt: "",
1514
- skill_ref: null,
1515
- skill_inputs: null,
1516
- agent_ext_id: value.trigger.agentId || null,
1517
- auth_mode: authMode(value.trigger),
1518
- plan: plan(value, options)
1519
- });
1520
- }
1521
- function workflowRecordToBuilder(record) {
1522
- const planned = record.plan?.steps ?? [];
1523
- const steps = planned.length ? fromPlanSteps(planned) : record.prompt || record.skill_ref ? [
1524
- {
1525
- id: "work",
1526
- kind: "agent",
1527
- title: record.name,
1528
- ...record.skill_ref ? { workflow: record.skill_ref } : { run: { kind: "prompt", promptTemplate: record.prompt } }
1529
- }
1530
- ] : [];
1531
- const when = workflowWhen(record);
1532
- return {
1533
- workflow: {
1534
- id: record.external_id,
1535
- title: record.name,
1536
- description: record.description ?? "",
1537
- inputs: [],
1538
- landing: "chat",
1539
- schedulable: false,
1540
- run: { kind: "steps", steps, layout: layoutFromPlanSteps(planned ?? []) }
1541
- },
1542
- trigger: {
1543
- ...DEFAULT_WORKFLOW_TRIGGER,
1544
- start: when === "now" ? "manual" : when,
1545
- runAt: isoToLocalInput(record.run_at),
1546
- rrule: record.rrule ?? "",
1547
- timezone: record.timezone || DEFAULT_WORKFLOW_TRIGGER.timezone,
1548
- eventKind: record.event_kind ?? "",
1549
- afterTaskId: record.after_conversation_ext_id ?? "",
1550
- agentId: record.agent_ext_id ?? "",
1551
- access: record.auth_mode === "scheduled_permanent" ? "standing" : "ask_each_run"
1552
- }
1553
- };
1554
- }
1555
1840
  var DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS = {
1556
1841
  save: "Save",
1557
1842
  saving: "Saving\u2026",
@@ -1564,7 +1849,8 @@ var DEFAULT_WORKFLOW_EVENT_KINDS = [
1564
1849
  { value: "conversation_ready", label: "Work is ready for me" },
1565
1850
  { value: "mention", label: "Someone mentions me" },
1566
1851
  { value: "workflow_completed", label: "Another workflow finishes" },
1567
- { value: "workflow_failed", label: "Another workflow fails" }
1852
+ { value: "workflow_failed", label: "Another workflow fails" },
1853
+ { value: "workflow_signal", label: "When a step signals" }
1568
1854
  ];
1569
1855
  function ConnectedWorkflowBuilder({
1570
1856
  workflowId,
@@ -1641,6 +1927,7 @@ function ConnectedWorkflowBuilder({
1641
1927
  agents,
1642
1928
  eventKinds,
1643
1929
  tasks,
1930
+ signals: declaredSignals(saved.data ?? []),
1644
1931
  compose,
1645
1932
  testId,
1646
1933
  actions: /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1685,6 +1972,6 @@ function ConnectedWorkflowBuilder({
1685
1972
  );
1686
1973
  }
1687
1974
 
1688
- export { ConnectedWorkflowBuilder, DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, DEFAULT_STEP_INSPECTOR_LABELS, DEFAULT_TRIGGER_INSPECTOR_LABELS, DEFAULT_WORKFLOW_BUILDER_LABELS, DEFAULT_WORKFLOW_CANVAS_LABELS, DEFAULT_WORKFLOW_EVENT_KINDS, DEFAULT_WORKFLOW_TRIGGER, StepInspector, TRIGGER_NODE, TriggerInspector, WorkflowBuilder, WorkflowCanvas, addStep, autoLayout, builderToWorkflowRequest, builderToWorkflowUpdate, connectSteps, disconnectSteps, layoutWithDefaults, moveStep, newSteppedWorkflow, removeStep, stepIdFor, triggerProblem, triggerSummary, updateStep, workflowRecordToBuilder, wouldCycle };
1975
+ export { ConnectedWorkflowBuilder, DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, DEFAULT_SIGNALS_EDITOR_LABELS, DEFAULT_STEP_INSPECTOR_LABELS, DEFAULT_TRIGGER_INSPECTOR_LABELS, DEFAULT_WORKFLOW_BUILDER_LABELS, DEFAULT_WORKFLOW_CANVAS_LABELS, DEFAULT_WORKFLOW_EVENT_KINDS, DEFAULT_WORKFLOW_TRIGGER, SignalsEditor, StepInspector, TRIGGER_NODE, TriggerInspector, WORKFLOW_SIGNAL_EVENT, WorkflowBuilder, WorkflowCanvas, addStep, autoLayout, builderToWorkflowRequest, builderToWorkflowUpdate, connectSteps, declaredSignals, disconnectSteps, isSignalTrigger, layoutWithDefaults, moveStep, newSteppedWorkflow, removeStep, signalConditions, signalSlug, stepIdFor, triggerProblem, triggerSummary, updateStep, workflowRecordToBuilder, wouldCycle };
1689
1976
  //# sourceMappingURL=index.js.map
1690
1977
  //# sourceMappingURL=index.js.map