@stigmer/react 3.1.13 → 3.1.15

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 (139) hide show
  1. package/agent/AgentDetailView.d.ts +9 -9
  2. package/agent/AgentDetailView.d.ts.map +1 -1
  3. package/agent/AgentDetailView.js +23 -31
  4. package/agent/AgentDetailView.js.map +1 -1
  5. package/agent-instance/AgentInstanceEmptyState.d.ts +4 -1
  6. package/agent-instance/AgentInstanceEmptyState.d.ts.map +1 -1
  7. package/agent-instance/AgentInstanceEmptyState.js +12 -5
  8. package/agent-instance/AgentInstanceEmptyState.js.map +1 -1
  9. package/agent-instance/AgentInstanceList.d.ts +13 -2
  10. package/agent-instance/AgentInstanceList.d.ts.map +1 -1
  11. package/agent-instance/AgentInstanceList.js +8 -4
  12. package/agent-instance/AgentInstanceList.js.map +1 -1
  13. package/agent-instance/useAgentInstances.d.ts +10 -3
  14. package/agent-instance/useAgentInstances.d.ts.map +1 -1
  15. package/agent-instance/useAgentInstances.js +12 -4
  16. package/agent-instance/useAgentInstances.js.map +1 -1
  17. package/button/Button.d.ts +54 -0
  18. package/button/Button.d.ts.map +1 -0
  19. package/button/Button.js +42 -0
  20. package/button/Button.js.map +1 -0
  21. package/button/index.d.ts +3 -0
  22. package/button/index.d.ts.map +1 -0
  23. package/button/index.js +2 -0
  24. package/button/index.js.map +1 -0
  25. package/empty-state/EmptyState.d.ts.map +1 -1
  26. package/empty-state/EmptyState.js +8 -3
  27. package/empty-state/EmptyState.js.map +1 -1
  28. package/empty-state/types.d.ts +2 -0
  29. package/empty-state/types.d.ts.map +1 -1
  30. package/execution/index.d.ts +1 -1
  31. package/execution/index.d.ts.map +1 -1
  32. package/execution/index.js.map +1 -1
  33. package/execution/useCreateAgentExecution.d.ts +81 -9
  34. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  35. package/execution/useCreateAgentExecution.js +42 -4
  36. package/execution/useCreateAgentExecution.js.map +1 -1
  37. package/index.d.ts +5 -3
  38. package/index.d.ts.map +1 -1
  39. package/index.js +4 -2
  40. package/index.js.map +1 -1
  41. package/package.json +4 -4
  42. package/session/useNewSessionFlow.d.ts +6 -3
  43. package/session/useNewSessionFlow.d.ts.map +1 -1
  44. package/session/useNewSessionFlow.js +34 -35
  45. package/session/useNewSessionFlow.js.map +1 -1
  46. package/sharing/AgentShareList.d.ts +46 -0
  47. package/sharing/AgentShareList.d.ts.map +1 -0
  48. package/sharing/AgentShareList.js +159 -0
  49. package/sharing/AgentShareList.js.map +1 -0
  50. package/sharing/ShareAgentDialog.d.ts +38 -25
  51. package/sharing/ShareAgentDialog.d.ts.map +1 -1
  52. package/sharing/ShareAgentDialog.js +131 -62
  53. package/sharing/ShareAgentDialog.js.map +1 -1
  54. package/sharing/index.d.ts +10 -8
  55. package/sharing/index.d.ts.map +1 -1
  56. package/sharing/index.js +5 -4
  57. package/sharing/index.js.map +1 -1
  58. package/sharing/useAgentShares.d.ts +48 -0
  59. package/sharing/useAgentShares.d.ts.map +1 -0
  60. package/sharing/useAgentShares.js +44 -0
  61. package/sharing/useAgentShares.js.map +1 -0
  62. package/sharing/useCanCreateAgentShare.d.ts +43 -0
  63. package/sharing/useCanCreateAgentShare.d.ts.map +1 -0
  64. package/sharing/useCanCreateAgentShare.js +44 -0
  65. package/sharing/useCanCreateAgentShare.js.map +1 -0
  66. package/sharing/useDeleteAgentShare.d.ts +42 -0
  67. package/sharing/useDeleteAgentShare.d.ts.map +1 -0
  68. package/sharing/useDeleteAgentShare.js +45 -0
  69. package/sharing/useDeleteAgentShare.js.map +1 -0
  70. package/sharing/useSaveAgentShare.d.ts +43 -18
  71. package/sharing/useSaveAgentShare.d.ts.map +1 -1
  72. package/sharing/useSaveAgentShare.js +55 -22
  73. package/sharing/useSaveAgentShare.js.map +1 -1
  74. package/src/agent/AgentDetailView.tsx +37 -43
  75. package/src/agent-instance/AgentInstanceEmptyState.tsx +24 -43
  76. package/src/agent-instance/AgentInstanceList.tsx +29 -16
  77. package/src/agent-instance/__tests__/agent-instance-hooks.test.tsx +19 -0
  78. package/src/agent-instance/useAgentInstances.ts +12 -3
  79. package/src/button/Button.tsx +104 -0
  80. package/src/button/__tests__/Button.test.tsx +82 -0
  81. package/src/button/index.ts +2 -0
  82. package/src/empty-state/EmptyState.tsx +8 -10
  83. package/src/empty-state/types.ts +2 -0
  84. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +92 -0
  85. package/src/execution/index.ts +2 -0
  86. package/src/execution/useCreateAgentExecution.ts +116 -10
  87. package/src/index.ts +17 -9
  88. package/src/session/__tests__/useNewSessionFlow.test.tsx +121 -52
  89. package/src/session/useNewSessionFlow.ts +38 -37
  90. package/src/sharing/AgentShareList.tsx +502 -0
  91. package/src/sharing/ShareAgentDialog.tsx +310 -112
  92. package/src/sharing/__tests__/AgentShareList.test.tsx +388 -0
  93. package/src/sharing/__tests__/ShareAgentDialog.test.tsx +311 -312
  94. package/src/sharing/__tests__/useAgentShares.test.tsx +186 -0
  95. package/src/sharing/__tests__/useCanCreateAgentShare.test.tsx +190 -0
  96. package/src/sharing/index.ts +10 -12
  97. package/src/sharing/useAgentShares.ts +74 -0
  98. package/src/sharing/useCanCreateAgentShare.ts +74 -0
  99. package/src/sharing/useDeleteAgentShare.ts +73 -0
  100. package/src/sharing/useSaveAgentShare.ts +73 -23
  101. package/src/workflow/WorkflowDetailView.tsx +9 -0
  102. package/src/workflow/instance/WorkflowInstanceEmptyState.tsx +24 -43
  103. package/src/workflow/instance/WorkflowInstanceList.tsx +29 -16
  104. package/src/workflow/useWorkflowInstances.ts +10 -3
  105. package/styles.css +1 -1
  106. package/workflow/WorkflowDetailView.d.ts +8 -1
  107. package/workflow/WorkflowDetailView.d.ts.map +1 -1
  108. package/workflow/WorkflowDetailView.js +2 -2
  109. package/workflow/WorkflowDetailView.js.map +1 -1
  110. package/workflow/instance/WorkflowInstanceEmptyState.d.ts +4 -1
  111. package/workflow/instance/WorkflowInstanceEmptyState.d.ts.map +1 -1
  112. package/workflow/instance/WorkflowInstanceEmptyState.js +12 -5
  113. package/workflow/instance/WorkflowInstanceEmptyState.js.map +1 -1
  114. package/workflow/instance/WorkflowInstanceList.d.ts +13 -2
  115. package/workflow/instance/WorkflowInstanceList.d.ts.map +1 -1
  116. package/workflow/instance/WorkflowInstanceList.js +8 -4
  117. package/workflow/instance/WorkflowInstanceList.js.map +1 -1
  118. package/workflow/useWorkflowInstances.d.ts +8 -3
  119. package/workflow/useWorkflowInstances.d.ts.map +1 -1
  120. package/workflow/useWorkflowInstances.js +10 -4
  121. package/workflow/useWorkflowInstances.js.map +1 -1
  122. package/sharing/useAgentShare.d.ts +0 -49
  123. package/sharing/useAgentShare.d.ts.map +0 -1
  124. package/sharing/useAgentShare.js +0 -64
  125. package/sharing/useAgentShare.js.map +0 -1
  126. package/sharing/useCreateExternalShareLink.d.ts +0 -90
  127. package/sharing/useCreateExternalShareLink.d.ts.map +0 -1
  128. package/sharing/useCreateExternalShareLink.js +0 -65
  129. package/sharing/useCreateExternalShareLink.js.map +0 -1
  130. package/sharing/useShareAgent.d.ts +0 -69
  131. package/sharing/useShareAgent.d.ts.map +0 -1
  132. package/sharing/useShareAgent.js +0 -42
  133. package/sharing/useShareAgent.js.map +0 -1
  134. package/src/sharing/__tests__/useAgentShare.test.tsx +0 -210
  135. package/src/sharing/__tests__/useCreateExternalShareLink.test.tsx +0 -194
  136. package/src/sharing/__tests__/useShareAgent.test.tsx +0 -131
  137. package/src/sharing/useAgentShare.ts +0 -105
  138. package/src/sharing/useCreateExternalShareLink.tsx +0 -141
  139. package/src/sharing/useShareAgent.tsx +0 -107
@@ -76,6 +76,48 @@ function sharingAudienceToProto(audience: SharingAudience): AgentShareAudience {
76
76
  : AgentShareAudience.public;
77
77
  }
78
78
 
79
+ /**
80
+ * Projects a share into the editable {@link AgentShareDraft} — the
81
+ * single constructor for the full-spec drafts every save requires.
82
+ * `null` (no share yet) seeds the same defaults the server would apply
83
+ * on first create.
84
+ *
85
+ * Shared by the Share dialog (draft seeding) and the share list's
86
+ * pause/resume toggle, so a toggle rebuilds the complete spec with only
87
+ * `enabled` flipped and can never wipe origins, messages, or credential
88
+ * bindings — the fails-closed posture {@link AgentShareDraft} exists to
89
+ * enforce.
90
+ */
91
+ export function draftFromShare(share: AgentShare | null): AgentShareDraft {
92
+ const spec = share?.spec;
93
+ return {
94
+ enabled: spec?.enabled ?? false,
95
+ audience: sharingAudienceFromProto(spec?.audience),
96
+ allowedOrigins: spec?.allowedOrigins ?? [],
97
+ messages: {
98
+ rateLimited: spec?.messages?.rateLimited ?? "",
99
+ unavailable: spec?.messages?.unavailable ?? "",
100
+ conversationEnded: spec?.messages?.conversationEnded ?? "",
101
+ },
102
+ environmentRefs: (spec?.environmentRefs ?? []).map((ref) => ({
103
+ org: ref.org,
104
+ slug: ref.slug,
105
+ })),
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Identity for a share being created: its display name and URL slug in
111
+ * the sharing org's namespace. Only consulted when `current` is `null`
112
+ * — an existing share's identity is immutable (decision 011 D2).
113
+ */
114
+ export interface AgentShareCreateIdentity {
115
+ /** Display name for the new share. */
116
+ readonly name: string;
117
+ /** URL slug for the new share — appears in `/chat/<org>/<slug>`. */
118
+ readonly slug: string;
119
+ }
120
+
79
121
  /** Return value of {@link useSaveAgentShare}. */
80
122
  export interface UseSaveAgentShareReturn {
81
123
  /**
@@ -83,12 +125,14 @@ export interface UseSaveAgentShareReturn {
83
125
  * persisted {@link AgentShare} — adopt it as the new baseline (its
84
126
  * `status.shareLinkToken` carries the live link token).
85
127
  *
86
- * `current` is the share being edited, or `null` when the agent has
87
- * never been shared the save then creates the canonical share.
128
+ * `current` is the share being edited, or `null` to create one
129
+ * named by `createIdentity` when given, else the server's defaults
130
+ * (the agent's own name and slug).
88
131
  */
89
132
  readonly save: (
90
133
  draft: AgentShareDraft,
91
134
  current: AgentShare | null,
135
+ createIdentity?: AgentShareCreateIdentity,
92
136
  ) => Promise<AgentShare | undefined>;
93
137
  /** `true` while the RPC is in flight. */
94
138
  readonly isPending: boolean;
@@ -97,36 +141,36 @@ export interface UseSaveAgentShareReturn {
97
141
  }
98
142
 
99
143
  /**
100
- * Behavior hook that persists an agent's canonical share configuration.
144
+ * Behavior hook that persists an agent share's configuration.
101
145
  *
102
146
  * Commits via `stigmer.agentShare.apply()` — an idempotent upsert keyed
103
147
  * on the share's `(org, slug)` identity — so one code path serves both
104
- * the first enable (creates the share; the server authorizes on the
105
- * referenced agent's `can_edit`) and every later edit. There is
106
- * deliberately no create/update branching: the share slug is unique per
107
- * org and defaults to the agent's slug, which makes apply-by-identity
108
- * exact, and the generated `AgentShareInput` carries no `metadata.id`
109
- * for an update to route by anyway.
148
+ * creation (the server authorizes on the referenced agent's `can_edit`)
149
+ * and every later edit. There is deliberately no create/update
150
+ * branching: the share slug is unique per org, which makes
151
+ * apply-by-identity exact, and the generated `AgentShareInput` carries
152
+ * no `metadata.id` for an update to route by anyway. A create that
153
+ * collides on `(org, slug)` rejects with an already-exists error rather
154
+ * than touching the existing share.
110
155
  *
111
156
  * Disabling is a save with `enabled: false` — a config-preserving pause
112
157
  * (decision 011 D1). Deleting the share is a separate, destructive
113
- * operation this hook does not perform.
158
+ * operation ({@link useDeleteAgentShare}).
114
159
  *
115
- * `shareOrg` selects which org's channel a first save creates and
116
- * defaults to the agent's own org. Pass the viewer's org to create a
117
- * **cross-org share** (decision 013) — the server then authorizes on the
118
- * public agent's `can_execute` plus `can_create_agent_share` in the
119
- * sharing org, and bills that org.
160
+ * `shareOrg` selects which org owns a created share and defaults to the
161
+ * agent's own org. Pass the viewer's org to create a **cross-org
162
+ * share** (decision 013) — the server then authorizes on the public
163
+ * agent's `can_execute` plus `can_create_agent_share` in the sharing
164
+ * org, and bills that org.
120
165
  *
121
166
  * Pass `null` for `agent` to produce a stable no-op (useful while the
122
167
  * agent is still loading).
123
168
  *
124
169
  * @example
125
170
  * ```tsx
126
- * const { share } = useAgentShare(agent);
127
171
  * const { save, isPending } = useSaveAgentShare(agent);
128
172
  *
129
- * const persisted = await save({ ...draft, enabled: true }, share);
173
+ * const persisted = await save({ ...draftFromShare(share), enabled: true }, share);
130
174
  * // persisted.status.shareLinkToken is the live link token
131
175
  * ```
132
176
  */
@@ -147,6 +191,7 @@ export function useSaveAgentShare(
147
191
  async (
148
192
  draft: AgentShareDraft,
149
193
  current: AgentShare | null,
194
+ createIdentity?: AgentShareCreateIdentity,
150
195
  ): Promise<AgentShare | undefined> => {
151
196
  if (!agentOrg || !agentSlug) return undefined;
152
197
 
@@ -156,13 +201,18 @@ export function useSaveAgentShare(
156
201
  try {
157
202
  return await stigmer.agentShare.apply({
158
203
  // Identity: the existing share's org/slug when editing (a
159
- // manifest-created share may carry a non-default slug — apply
160
- // with the agent's slug would create a SECOND share), the
161
- // sharing org + the agent's slug when creating (the server's
162
- // own D2 default, made explicit).
204
+ // share may carry a non-default slug — apply with the agent's
205
+ // slug would create a SECOND share); when creating, the
206
+ // sharing org + the caller-chosen identity, falling back to
207
+ // the agent's own slug/name (the server's D2 default, made
208
+ // explicit).
163
209
  org: current?.metadata?.org || resolvedShareOrg,
164
- slug: current?.metadata?.slug || agentSlug,
165
- name: current?.metadata?.name || agentName || agentSlug,
210
+ slug: current?.metadata?.slug || createIdentity?.slug || agentSlug,
211
+ name:
212
+ current?.metadata?.name ||
213
+ createIdentity?.name ||
214
+ agentName ||
215
+ agentSlug,
166
216
  agentRef: { org: agentOrg, slug: agentSlug },
167
217
  enabled: draft.enabled,
168
218
  // Written as the explicit enum value, never left unspecified —
@@ -76,6 +76,13 @@ export interface WorkflowDetailViewProps {
76
76
  * Receives the execution ID — use for navigation to the execution viewer.
77
77
  */
78
78
  readonly onExecutionClick?: (executionId: string) => void;
79
+ /**
80
+ * The viewer's active organization slug, feeding the Instances tab:
81
+ * it scopes the list to this org's instances of the workflow, so a
82
+ * member of several orgs sees the current org context only. Omit to
83
+ * default to the workflow's own org.
84
+ */
85
+ readonly viewerOrg?: string;
79
86
  /**
80
87
  * Called when the user clicks "Create Instance" in the Instances tab.
81
88
  * Opens the create instance dialog.
@@ -157,6 +164,7 @@ export function WorkflowDetailView({
157
164
  onTabChange,
158
165
  defaultTab,
159
166
  onExecutionClick,
167
+ viewerOrg,
160
168
  onCreateInstanceClick,
161
169
  onInstanceClick,
162
170
  onInstanceRunClick,
@@ -300,6 +308,7 @@ export function WorkflowDetailView({
300
308
  workflowId={meta?.id ?? ""}
301
309
  defaultInstanceId={workflow?.status?.defaultInstanceId}
302
310
  org={org}
311
+ viewerOrg={viewerOrg}
303
312
  onCreateClick={onCreateInstanceClick}
304
313
  onInstanceClick={onInstanceClick}
305
314
  onRunClick={onInstanceRunClick}
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
 
3
- import { cn } from "@stigmer/theme";
3
+ import { EmptyState } from "../../empty-state/EmptyState.js";
4
4
 
5
5
  /** Props for {@link WorkflowInstanceEmptyState}. */
6
6
  export interface WorkflowInstanceEmptyStateProps {
7
- /** Called when the user clicks "Create Instance". */
7
+ /** Called when the user clicks "Create instance". */
8
8
  readonly onCreateClick?: () => void;
9
9
  /** Additional CSS class names. */
10
10
  readonly className?: string;
@@ -15,62 +15,43 @@ export interface WorkflowInstanceEmptyStateProps {
15
15
  *
16
16
  * Communicates the value proposition of workflow instances without
17
17
  * mentioning the platform-managed default instance (implementation detail).
18
+ *
19
+ * A thin composition over the shared {@link EmptyState} primitive: only the
20
+ * icon and copy are specific to workflow instances.
18
21
  */
19
22
  export function WorkflowInstanceEmptyState({
20
23
  onCreateClick,
21
24
  className,
22
25
  }: WorkflowInstanceEmptyStateProps) {
23
26
  return (
24
- <div
25
- className={cn(
26
- "flex flex-col items-center justify-center py-12 px-6 text-center",
27
- className,
28
- )}
29
- >
30
- <div className="mb-4 rounded-full bg-muted p-3">
31
- <LayersIcon />
32
- </div>
33
-
34
- <h3 className="text-sm font-semibold text-foreground mb-1">
35
- No instances yet
36
- </h3>
37
-
38
- <p className="text-sm text-muted-foreground max-w-sm mb-6">
39
- Create instances to run this workflow with different configurations
40
- and access controls. Each instance binds environments (credentials,
41
- secrets, settings) and can be shared independently.
42
- </p>
43
-
44
- {onCreateClick && (
45
- <button
46
- type="button"
47
- onClick={onCreateClick}
48
- className={cn(
49
- "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5",
50
- "text-sm font-medium",
51
- "bg-primary text-primary-foreground",
52
- "hover:bg-primary/90",
53
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
54
- )}
55
- >
56
- <PlusIcon />
57
- Create Instance
58
- </button>
59
- )}
60
- </div>
27
+ <EmptyState
28
+ variant="first-use"
29
+ icon={<LayersIcon />}
30
+ title="No instances yet"
31
+ description={
32
+ "Create instances to run this workflow with different configurations " +
33
+ "and access controls. Each instance binds environments (credentials, " +
34
+ "secrets, settings) and can be shared independently."
35
+ }
36
+ action={
37
+ onCreateClick
38
+ ? { label: "Create instance", onClick: onCreateClick, icon: <PlusIcon /> }
39
+ : undefined
40
+ }
41
+ className={className}
42
+ />
61
43
  );
62
44
  }
63
45
 
64
46
  function LayersIcon() {
65
47
  return (
66
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
48
+ <svg width="24" height="24" viewBox="0 0 20 20" fill="none" aria-hidden="true">
67
49
  <path
68
50
  d="M10 2L2 6l8 4 8-4-8-4zM2 10l8 4 8-4M2 14l8 4 8-4"
69
51
  stroke="currentColor"
70
52
  strokeWidth="1.5"
71
53
  strokeLinecap="round"
72
54
  strokeLinejoin="round"
73
- className="text-muted-foreground"
74
55
  />
75
56
  </svg>
76
57
  );
@@ -78,8 +59,8 @@ function LayersIcon() {
78
59
 
79
60
  function PlusIcon() {
80
61
  return (
81
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
82
- <path d="M7 2v10M2 7h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
62
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
63
+ <path d="M6 2v8M2 6h8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
83
64
  </svg>
84
65
  );
85
66
  }
@@ -5,6 +5,7 @@ import { cn } from "@stigmer/theme";
5
5
  import type { WorkflowInstance } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
6
6
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
7
7
  import { useWorkflowInstances } from "../useWorkflowInstances.js";
8
+ import { Button } from "../../button/Button.js";
8
9
  import { useEnvironmentList } from "../../environment/useEnvironmentList.js";
9
10
  import { ResourceVisibilityControl } from "../../library/ResourceVisibilityControl.js";
10
11
  import { PermissionGate } from "../../iam-policy/PermissionGate.js";
@@ -16,8 +17,19 @@ export interface WorkflowInstanceListProps {
16
17
  readonly workflowId: string;
17
18
  /** The default instance ID (from workflow.status.defaultInstanceId) — filtered out of the list. */
18
19
  readonly defaultInstanceId?: string;
19
- /** Organization slug (needed for environment resolution and create flow). */
20
+ /**
21
+ * The WORKFLOW's organization slug (needed for environment resolution
22
+ * and the create flow). Not the list scope — that is `viewerOrg`,
23
+ * which differs from `org` when viewing another org's workflow.
24
+ */
20
25
  readonly org: string;
26
+ /**
27
+ * The viewer's active organization slug. Scopes the list to this
28
+ * org's instances of the workflow, so a member of several orgs sees
29
+ * exactly the current org context's instances. Omit to default to
30
+ * the workflow's own org.
31
+ */
32
+ readonly viewerOrg?: string;
21
33
  /** Called when the user wants to create a new instance. */
22
34
  readonly onCreateClick?: () => void;
23
35
  /** Called when the user clicks an instance row. */
@@ -45,6 +57,7 @@ export function WorkflowInstanceList({
45
57
  workflowId,
46
58
  defaultInstanceId,
47
59
  org,
60
+ viewerOrg,
48
61
  onCreateClick,
49
62
  onInstanceClick,
50
63
  onRunClick,
@@ -52,7 +65,13 @@ export function WorkflowInstanceList({
52
65
  refreshKey,
53
66
  className,
54
67
  }: WorkflowInstanceListProps) {
55
- const { instances, isLoading, error, refetch } = useWorkflowInstances(workflowId);
68
+ // Scope the list to the org whose context the viewer is in; when the
69
+ // host passes no viewerOrg the scope falls back to the workflow's own
70
+ // org (the same-org owner flow).
71
+ const { instances, isLoading, error, refetch } = useWorkflowInstances(
72
+ workflowId,
73
+ viewerOrg || org,
74
+ );
56
75
  const { environments } = useEnvironmentList(org);
57
76
 
58
77
  // Refetch when refreshKey changes (signals external mutation like create/delete)
@@ -100,27 +119,21 @@ export function WorkflowInstanceList({
100
119
  {userInstances.length} {userInstances.length === 1 ? "instance" : "instances"}
101
120
  </h3>
102
121
  {onCreateClick && (
103
- <button
104
- type="button"
122
+ <Button
123
+ variant="outline"
124
+ size="xs"
125
+ icon={<PlusIcon />}
105
126
  onClick={onCreateClick}
106
- className={cn(
107
- "inline-flex items-center gap-1 rounded-md px-2.5 py-1",
108
- "text-xs font-medium",
109
- "border border-border text-foreground",
110
- "hover:bg-accent-hover",
111
- "focus:outline-none focus:ring-2 focus:ring-ring",
112
- )}
113
127
  >
114
- <PlusIcon />
115
- Create
116
- </button>
128
+ Create instance
129
+ </Button>
117
130
  )}
118
131
  </div>
119
132
 
120
133
  <div className="overflow-hidden rounded-lg border border-border">
121
134
  <table className="w-full text-sm">
122
135
  <thead>
123
- <tr className="border-b border-border bg-muted/50">
136
+ <tr className="border-b border-border bg-muted-subtle">
124
137
  <th className="px-4 py-2 text-left font-medium text-muted-foreground">Name</th>
125
138
  <th className="px-4 py-2 text-left font-medium text-muted-foreground">Environments</th>
126
139
  <th className="px-4 py-2 text-left font-medium text-muted-foreground">Visibility</th>
@@ -267,7 +280,7 @@ function LoadingSkeleton() {
267
280
  return (
268
281
  <div className="space-y-2 py-4">
269
282
  {[1, 2, 3].map((i) => (
270
- <div key={i} className="h-12 rounded-md bg-muted/50 animate-pulse" />
283
+ <div key={i} className="h-12 animate-pulse rounded-md bg-muted-faint" />
271
284
  ))}
272
285
  </div>
273
286
  );
@@ -21,21 +21,27 @@ export interface UseWorkflowInstancesReturn {
21
21
  }
22
22
 
23
23
  /**
24
- * Data hook that fetches all instances for a specific workflow.
24
+ * Data hook that fetches instances for a specific workflow.
25
25
  *
26
26
  * Wraps `stigmer.workflowInstance.getByWorkflow()` with loading
27
27
  * and error state management. Used on the Workflow detail page
28
28
  * "Instances" tab to show environment-bound deployments.
29
29
  *
30
+ * Pass `org` to scope the list to one organization's instances — the
31
+ * org-context view a console tab needs. The server applies the scope
32
+ * (it only ever narrows the permission-bounded view); omit `org` for
33
+ * the full permission-bounded list.
34
+ *
30
35
  * Pass `null` as `workflowId` to skip fetching (stable no-op).
31
36
  *
32
37
  * @example
33
38
  * ```tsx
34
- * const { instances, isLoading } = useWorkflowInstances(workflow.metadata?.id);
39
+ * const { instances, isLoading } = useWorkflowInstances(workflow.metadata?.id, viewerOrg);
35
40
  * ```
36
41
  */
37
42
  export function useWorkflowInstances(
38
43
  workflowId: string | null | undefined,
44
+ org?: string,
39
45
  ): UseWorkflowInstancesReturn {
40
46
  const stigmer = useStigmer();
41
47
 
@@ -44,6 +50,7 @@ export function useWorkflowInstances(
44
50
  const resp = await stigmer.workflowInstance.getByWorkflow(
45
51
  create(GetWorkflowInstancesByWorkflowRequestSchema, {
46
52
  workflowId,
53
+ org: org ?? "",
47
54
  }),
48
55
  );
49
56
  return resp.entries ? [...resp.entries] : [];
@@ -56,7 +63,7 @@ export function useWorkflowInstances(
56
63
  isRefetching,
57
64
  error,
58
65
  refetch,
59
- } = useFetch<readonly WorkflowInstance[]>(fetchFn, [workflowId, stigmer], []);
66
+ } = useFetch<readonly WorkflowInstance[]>(fetchFn, [workflowId, org, stigmer], []);
60
67
 
61
68
  return { instances, isLoading, isRefetching, error, refetch };
62
69
  }