@stigmer/react 0.0.39 → 0.0.41

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 (104) hide show
  1. package/agent/AgentDetailView.d.ts +14 -3
  2. package/agent/AgentDetailView.d.ts.map +1 -1
  3. package/agent/AgentDetailView.js +8 -7
  4. package/agent/AgentDetailView.js.map +1 -1
  5. package/agent/agentSetupReducer.d.ts +1 -0
  6. package/agent/agentSetupReducer.d.ts.map +1 -1
  7. package/agent/agentSetupReducer.js +11 -0
  8. package/agent/agentSetupReducer.js.map +1 -1
  9. package/agent/useAgentSetup.d.ts.map +1 -1
  10. package/agent/useAgentSetup.js +41 -6
  11. package/agent/useAgentSetup.js.map +1 -1
  12. package/api-key/ApiKeyCreatedAlert.d.ts +33 -0
  13. package/api-key/ApiKeyCreatedAlert.d.ts.map +1 -0
  14. package/api-key/ApiKeyCreatedAlert.js +61 -0
  15. package/api-key/ApiKeyCreatedAlert.js.map +1 -0
  16. package/api-key/ApiKeyListPanel.d.ts +30 -0
  17. package/api-key/ApiKeyListPanel.d.ts.map +1 -0
  18. package/api-key/ApiKeyListPanel.js +126 -0
  19. package/api-key/ApiKeyListPanel.js.map +1 -0
  20. package/api-key/CreateApiKeyForm.d.ts +35 -0
  21. package/api-key/CreateApiKeyForm.d.ts.map +1 -0
  22. package/api-key/CreateApiKeyForm.js +81 -0
  23. package/api-key/CreateApiKeyForm.js.map +1 -0
  24. package/api-key/index.d.ts +13 -0
  25. package/api-key/index.d.ts.map +1 -0
  26. package/api-key/index.js +7 -0
  27. package/api-key/index.js.map +1 -0
  28. package/api-key/useApiKeyList.d.ts +28 -0
  29. package/api-key/useApiKeyList.d.ts.map +1 -0
  30. package/api-key/useApiKeyList.js +52 -0
  31. package/api-key/useApiKeyList.js.map +1 -0
  32. package/api-key/useCreateApiKey.d.ts +40 -0
  33. package/api-key/useCreateApiKey.d.ts.map +1 -0
  34. package/api-key/useCreateApiKey.js +56 -0
  35. package/api-key/useCreateApiKey.js.map +1 -0
  36. package/api-key/useDeleteApiKey.d.ts +26 -0
  37. package/api-key/useDeleteApiKey.d.ts.map +1 -0
  38. package/api-key/useDeleteApiKey.js +43 -0
  39. package/api-key/useDeleteApiKey.js.map +1 -0
  40. package/composer/SessionComposer.d.ts.map +1 -1
  41. package/composer/SessionComposer.js +12 -0
  42. package/composer/SessionComposer.js.map +1 -1
  43. package/index.d.ts +5 -3
  44. package/index.d.ts.map +1 -1
  45. package/index.js +5 -3
  46. package/index.js.map +1 -1
  47. package/library/VisibilityToggle.d.ts +41 -0
  48. package/library/VisibilityToggle.d.ts.map +1 -0
  49. package/library/VisibilityToggle.js +80 -0
  50. package/library/VisibilityToggle.js.map +1 -0
  51. package/library/index.d.ts +4 -0
  52. package/library/index.d.ts.map +1 -1
  53. package/library/index.js +2 -0
  54. package/library/index.js.map +1 -1
  55. package/library/useUpdateVisibility.d.ts +40 -0
  56. package/library/useUpdateVisibility.d.ts.map +1 -0
  57. package/library/useUpdateVisibility.js +67 -0
  58. package/library/useUpdateVisibility.js.map +1 -0
  59. package/mcp-server/McpServerDetailView.d.ts +12 -1
  60. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  61. package/mcp-server/McpServerDetailView.js +6 -5
  62. package/mcp-server/McpServerDetailView.js.map +1 -1
  63. package/package.json +4 -4
  64. package/search/useResourceCount.d.ts +2 -2
  65. package/search/useResourceCount.js +2 -2
  66. package/search/useResourceCount.js.map +1 -1
  67. package/search/useResourceList.d.ts +8 -3
  68. package/search/useResourceList.d.ts.map +1 -1
  69. package/search/useResourceList.js +2 -2
  70. package/search/useResourceList.js.map +1 -1
  71. package/session/index.d.ts +1 -0
  72. package/session/index.d.ts.map +1 -1
  73. package/session/index.js +2 -0
  74. package/session/index.js.map +1 -1
  75. package/session/useCreateSession.d.ts +1 -1
  76. package/session/useCreateSession.d.ts.map +1 -1
  77. package/session/useCreateSession.js +2 -1
  78. package/session/useCreateSession.js.map +1 -1
  79. package/skill/SkillDetailView.d.ts +12 -1
  80. package/skill/SkillDetailView.d.ts.map +1 -1
  81. package/skill/SkillDetailView.js +6 -5
  82. package/skill/SkillDetailView.js.map +1 -1
  83. package/src/agent/AgentDetailView.tsx +34 -8
  84. package/src/agent/agentSetupReducer.ts +12 -0
  85. package/src/agent/useAgentSetup.ts +69 -19
  86. package/src/api-key/ApiKeyCreatedAlert.tsx +184 -0
  87. package/src/api-key/ApiKeyListPanel.tsx +359 -0
  88. package/src/api-key/CreateApiKeyForm.tsx +250 -0
  89. package/src/api-key/index.ts +12 -0
  90. package/src/api-key/useApiKeyList.ts +68 -0
  91. package/src/api-key/useCreateApiKey.ts +71 -0
  92. package/src/api-key/useDeleteApiKey.ts +57 -0
  93. package/src/composer/SessionComposer.tsx +13 -0
  94. package/src/index.ts +26 -1
  95. package/src/library/VisibilityToggle.tsx +205 -0
  96. package/src/library/index.ts +9 -0
  97. package/src/library/useUpdateVisibility.ts +94 -0
  98. package/src/mcp-server/McpServerDetailView.tsx +32 -6
  99. package/src/search/useResourceCount.ts +4 -4
  100. package/src/search/useResourceList.ts +10 -5
  101. package/src/session/index.ts +3 -0
  102. package/src/session/useCreateSession.ts +6 -5
  103. package/src/skill/SkillDetailView.tsx +32 -6
  104. package/styles.css +1 -1
@@ -0,0 +1,205 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef, useState } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
6
+
7
+ export interface VisibilityToggleProps {
8
+ /** Current visibility of the resource. */
9
+ readonly visibility: ApiResourceVisibility;
10
+ /**
11
+ * Called when the user confirms a visibility change.
12
+ * The toggle shows an inline confirmation before invoking this
13
+ * callback when switching to PUBLIC.
14
+ */
15
+ readonly onVisibilityChange: (v: ApiResourceVisibility) => void;
16
+ /** Shows a spinner/disabled state while the RPC is in flight. */
17
+ readonly isPending?: boolean;
18
+ /** Disables all interaction (e.g., when the user lacks can_edit). */
19
+ readonly disabled?: boolean;
20
+ /** Additional CSS classes applied to the root element. */
21
+ readonly className?: string;
22
+ }
23
+
24
+ const OPTIONS: readonly {
25
+ readonly value: ApiResourceVisibility;
26
+ readonly label: string;
27
+ }[] = [
28
+ { value: ApiResourceVisibility.visibility_private, label: "Private" },
29
+ { value: ApiResourceVisibility.visibility_public, label: "Public" },
30
+ ];
31
+
32
+ /**
33
+ * Segmented control for toggling resource visibility between
34
+ * Private and Public.
35
+ *
36
+ * Switching to PUBLIC shows a brief inline confirmation prompt
37
+ * since making a resource publicly visible is a consequential
38
+ * action. Switching to PRIVATE applies immediately without
39
+ * confirmation (revoking access is always safe).
40
+ *
41
+ * Follows the same visual pattern as {@link ScopeToggle} —
42
+ * WAI-ARIA Radio Group with roving tabindex. All visual
43
+ * properties flow through `--stgm-*` design tokens.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * <VisibilityToggle
48
+ * visibility={skill.metadata.visibility}
49
+ * onVisibilityChange={handleVisibilityChange}
50
+ * isPending={isPending}
51
+ * />
52
+ * ```
53
+ */
54
+ export function VisibilityToggle({
55
+ visibility,
56
+ onVisibilityChange,
57
+ isPending = false,
58
+ disabled = false,
59
+ className,
60
+ }: VisibilityToggleProps) {
61
+ const [confirming, setConfirming] = useState(false);
62
+ const optionRefs = useRef<(HTMLButtonElement | null)[]>([]);
63
+
64
+ const isPublic =
65
+ visibility === ApiResourceVisibility.visibility_public;
66
+ const effectivelyDisabled = disabled || isPending;
67
+
68
+ const handleSelect = useCallback(
69
+ (value: ApiResourceVisibility) => {
70
+ if (value === visibility) return;
71
+
72
+ if (value === ApiResourceVisibility.visibility_public) {
73
+ setConfirming(true);
74
+ return;
75
+ }
76
+
77
+ onVisibilityChange(value);
78
+ },
79
+ [visibility, onVisibilityChange],
80
+ );
81
+
82
+ const confirmPublic = useCallback(() => {
83
+ setConfirming(false);
84
+ onVisibilityChange(ApiResourceVisibility.visibility_public);
85
+ }, [onVisibilityChange]);
86
+
87
+ const cancelConfirm = useCallback(() => {
88
+ setConfirming(false);
89
+ }, []);
90
+
91
+ const handleKeyDown = useCallback(
92
+ (e: React.KeyboardEvent<HTMLButtonElement>, index: number) => {
93
+ let nextIndex: number | null = null;
94
+
95
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
96
+ e.preventDefault();
97
+ nextIndex = (index + 1) % OPTIONS.length;
98
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
99
+ e.preventDefault();
100
+ nextIndex = (index - 1 + OPTIONS.length) % OPTIONS.length;
101
+ }
102
+
103
+ if (nextIndex !== null) {
104
+ optionRefs.current[nextIndex]?.focus();
105
+ handleSelect(OPTIONS[nextIndex].value);
106
+ }
107
+ },
108
+ [handleSelect],
109
+ );
110
+
111
+ const currentIndex = OPTIONS.findIndex((o) => o.value === visibility);
112
+
113
+ return (
114
+ <div className={cn("inline-flex flex-col gap-1.5", className)}>
115
+ <div
116
+ role="radiogroup"
117
+ aria-label="Resource visibility"
118
+ aria-disabled={effectivelyDisabled || undefined}
119
+ className={cn(
120
+ "inline-flex rounded-md bg-muted p-0.5",
121
+ effectivelyDisabled && "pointer-events-none opacity-50",
122
+ )}
123
+ >
124
+ {OPTIONS.map((option, index) => {
125
+ const isSelected = visibility === option.value;
126
+
127
+ return (
128
+ <button
129
+ key={option.value}
130
+ ref={(el) => {
131
+ optionRefs.current[index] = el;
132
+ }}
133
+ type="button"
134
+ role="radio"
135
+ aria-checked={isSelected}
136
+ tabIndex={isSelected ? 0 : -1}
137
+ disabled={effectivelyDisabled}
138
+ onClick={() => handleSelect(option.value)}
139
+ onKeyDown={(e) => handleKeyDown(e, index)}
140
+ className={cn(
141
+ "cursor-pointer rounded-sm px-3 py-1 text-xs font-medium transition-colors",
142
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
143
+ isSelected && option.value === ApiResourceVisibility.visibility_public
144
+ ? "bg-emerald-100 text-emerald-800 shadow-sm dark:bg-emerald-900/40 dark:text-emerald-300"
145
+ : isSelected
146
+ ? "bg-background text-foreground shadow-sm"
147
+ : "text-muted-foreground hover:text-foreground",
148
+ )}
149
+ >
150
+ {isPending && isSelected ? (
151
+ <span className="inline-flex items-center gap-1">
152
+ <span
153
+ className="inline-block h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent"
154
+ aria-hidden="true"
155
+ />
156
+ {option.label}
157
+ </span>
158
+ ) : (
159
+ option.label
160
+ )}
161
+ </button>
162
+ );
163
+ })}
164
+ </div>
165
+
166
+ {confirming && (
167
+ <div
168
+ className={cn(
169
+ "flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-1.5 text-xs",
170
+ "dark:border-amber-800/50 dark:bg-amber-950/30",
171
+ )}
172
+ role="alert"
173
+ >
174
+ <span className="text-amber-800 dark:text-amber-200">
175
+ Make visible to all users?
176
+ </span>
177
+ <button
178
+ type="button"
179
+ onClick={confirmPublic}
180
+ className={cn(
181
+ "rounded px-2 py-0.5 text-xs font-medium",
182
+ "bg-amber-600 text-white hover:bg-amber-700",
183
+ "dark:bg-amber-600 dark:hover:bg-amber-500",
184
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
185
+ )}
186
+ >
187
+ Confirm
188
+ </button>
189
+ <button
190
+ type="button"
191
+ onClick={cancelConfirm}
192
+ className={cn(
193
+ "rounded px-2 py-0.5 text-xs font-medium",
194
+ "text-amber-700 hover:text-amber-900",
195
+ "dark:text-amber-300 dark:hover:text-amber-100",
196
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
197
+ )}
198
+ >
199
+ Cancel
200
+ </button>
201
+ </div>
202
+ )}
203
+ </div>
204
+ );
205
+ }
@@ -39,4 +39,13 @@ export type {
39
39
  PushSkillParams,
40
40
  } from "./useApplyResource";
41
41
 
42
+ export { VisibilityToggle } from "./VisibilityToggle";
43
+ export type { VisibilityToggleProps } from "./VisibilityToggle";
44
+
45
+ export { useUpdateVisibility } from "./useUpdateVisibility";
46
+ export type {
47
+ VisibilityResourceKind,
48
+ UseUpdateVisibilityReturn,
49
+ } from "./useUpdateVisibility";
50
+
42
51
  export type { ResourceListScope } from "../search";
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
6
+ import { UpdateVisibilityInputSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
7
+ import { useStigmer } from "../hooks";
8
+ import { toError } from "../internal/toError";
9
+
10
+ /** Resource kinds that support the `updateVisibility` RPC. */
11
+ export type VisibilityResourceKind = "skill" | "agent" | "mcpServer";
12
+
13
+ export interface UseUpdateVisibilityReturn {
14
+ /**
15
+ * Call the `updateVisibility` RPC for the specified resource.
16
+ * Resolves when the server confirms the change.
17
+ */
18
+ readonly updateVisibility: (
19
+ visibility: ApiResourceVisibility,
20
+ ) => Promise<void>;
21
+ /** `true` while the RPC is in flight. */
22
+ readonly isPending: boolean;
23
+ /** The last error from the RPC, or `null`. */
24
+ readonly error: Error | null;
25
+ }
26
+
27
+ /**
28
+ * Behavior hook that updates the visibility of a Skill, Agent, or
29
+ * MCP Server.
30
+ *
31
+ * Wraps the generated `stigmer.{kind}.updateVisibility()` SDK method
32
+ * with loading and error state management. The hook is stateless with
33
+ * respect to the resource — the caller is responsible for refreshing
34
+ * the resource after a successful update (e.g., via `refetch` from
35
+ * the corresponding data hook).
36
+ *
37
+ * Pass `null` for `resourceId` to produce a stable no-op (useful when
38
+ * the resource hasn't loaded yet).
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * const { updateVisibility, isPending } = useUpdateVisibility("skill", skill.metadata.id);
43
+ *
44
+ * <VisibilityToggle
45
+ * visibility={skill.metadata.visibility}
46
+ * onVisibilityChange={updateVisibility}
47
+ * isPending={isPending}
48
+ * />
49
+ * ```
50
+ */
51
+ export function useUpdateVisibility(
52
+ kind: VisibilityResourceKind,
53
+ resourceId: string | null,
54
+ ): UseUpdateVisibilityReturn {
55
+ const stigmer = useStigmer();
56
+ const [isPending, setIsPending] = useState(false);
57
+ const [error, setError] = useState<Error | null>(null);
58
+
59
+ const updateVisibility = useCallback(
60
+ async (visibility: ApiResourceVisibility) => {
61
+ if (!resourceId) return;
62
+
63
+ setIsPending(true);
64
+ setError(null);
65
+
66
+ try {
67
+ const input = create(UpdateVisibilityInputSchema, {
68
+ resourceId,
69
+ visibility,
70
+ });
71
+
72
+ switch (kind) {
73
+ case "skill":
74
+ await stigmer.skill.updateVisibility(input);
75
+ break;
76
+ case "agent":
77
+ await stigmer.agent.updateVisibility(input);
78
+ break;
79
+ case "mcpServer":
80
+ await stigmer.mcpServer.updateVisibility(input);
81
+ break;
82
+ }
83
+ } catch (err) {
84
+ setError(toError(err));
85
+ throw err;
86
+ } finally {
87
+ setIsPending(false);
88
+ }
89
+ },
90
+ [kind, resourceId, stigmer],
91
+ );
92
+
93
+ return { updateVisibility, isPending, error };
94
+ }
@@ -13,6 +13,7 @@ import type { EnvironmentValue } from "@stigmer/protos/ai/stigmer/agentic/enviro
13
13
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
14
14
  import { useMcpServer } from "./useMcpServer";
15
15
  import { ErrorMessage } from "../error/ErrorMessage";
16
+ import { VisibilityToggle } from "../library/VisibilityToggle";
16
17
 
17
18
  export interface McpServerDetailViewProps {
18
19
  /** Organization slug that owns the MCP server. */
@@ -27,7 +28,16 @@ export interface McpServerDetailViewProps {
27
28
  *
28
29
  * Not called on error or not-found states.
29
30
  */
30
- readonly onResourceLoad?: (meta: { name: string }) => void;
31
+ readonly onResourceLoad?: (meta: { name: string; id: string }) => void;
32
+ /**
33
+ * Called when the user toggles visibility via the inline control.
34
+ * When provided, the header renders an interactive
35
+ * {@link VisibilityToggle} instead of a read-only badge.
36
+ * When omitted, visibility is displayed as a static "Public" pill.
37
+ */
38
+ readonly onVisibilityChange?: (v: ApiResourceVisibility) => void;
39
+ /** `true` while a visibility update RPC is in flight. */
40
+ readonly isVisibilityPending?: boolean;
31
41
  /** Additional CSS classes for the root container. */
32
42
  readonly className?: string;
33
43
  }
@@ -58,6 +68,8 @@ export function McpServerDetailView({
58
68
  org,
59
69
  slug,
60
70
  onResourceLoad,
71
+ onVisibilityChange,
72
+ isVisibilityPending,
61
73
  className,
62
74
  }: McpServerDetailViewProps) {
63
75
  const { mcpServer, isLoading, error, refetch } = useMcpServer(org, slug);
@@ -67,7 +79,7 @@ export function McpServerDetailView({
67
79
 
68
80
  useEffect(() => {
69
81
  if (mcpServer?.metadata?.name) {
70
- onResourceLoadRef.current?.({ name: mcpServer.metadata.name });
82
+ onResourceLoadRef.current?.({ name: mcpServer.metadata.name, id: mcpServer.metadata.id });
71
83
  }
72
84
  }, [mcpServer]);
73
85
 
@@ -101,6 +113,8 @@ export function McpServerDetailView({
101
113
  ? timestampDate(capabilities.lastDiscoveredAt)
102
114
  : null
103
115
  }
116
+ onVisibilityChange={onVisibilityChange}
117
+ isVisibilityPending={isVisibilityPending}
104
118
  />
105
119
 
106
120
  {spec?.serverType.case && (
@@ -152,11 +166,15 @@ function Header({
152
166
  createdAt,
153
167
  updatedAt,
154
168
  lastDiscoveredAt,
169
+ onVisibilityChange,
170
+ isVisibilityPending,
155
171
  }: {
156
172
  readonly server: McpServer;
157
173
  readonly createdAt: Date | null;
158
174
  readonly updatedAt: Date | null;
159
175
  readonly lastDiscoveredAt: Date | null;
176
+ readonly onVisibilityChange?: (v: ApiResourceVisibility) => void;
177
+ readonly isVisibilityPending?: boolean;
160
178
  }) {
161
179
  const meta = server.metadata;
162
180
  const spec = server.spec;
@@ -181,10 +199,18 @@ function Header({
181
199
  <h2 className="truncate text-lg font-semibold text-foreground">
182
200
  {displayName}
183
201
  </h2>
184
- {isPublic && (
185
- <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
186
- Public
187
- </span>
202
+ {onVisibilityChange && meta ? (
203
+ <VisibilityToggle
204
+ visibility={meta.visibility}
205
+ onVisibilityChange={onVisibilityChange}
206
+ isPending={isVisibilityPending}
207
+ />
208
+ ) : (
209
+ isPublic && (
210
+ <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
211
+ Public
212
+ </span>
213
+ )
188
214
  )}
189
215
  </div>
190
216
  <div className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted-foreground">
@@ -10,8 +10,8 @@ export interface UseResourceCountOptions {
10
10
  /**
11
11
  * Controls resource visibility scope.
12
12
  *
13
- * - `"org"` — only resources owned by the given organization (excludes public/platform resources).
14
- * - `"all"` — includes public/platform resources alongside the org's own resources.
13
+ * - `"org"` — all resources owned by the given organization, regardless of visibility.
14
+ * - `"all"` — all resources the caller is authorized to access, across all organizations.
15
15
  *
16
16
  * @default "org"
17
17
  */
@@ -72,9 +72,9 @@ export function useResourceCount(
72
72
  setError(null);
73
73
 
74
74
  const params: ListParams = {
75
- org,
75
+ org: scope === "all" ? "" : org,
76
76
  query: query || undefined,
77
- excludePublic: scope === "org",
77
+ excludePublic: false,
78
78
  page: { num: 1, size: 1 },
79
79
  };
80
80
 
@@ -4,7 +4,12 @@ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import type { ListParams, ListResult } from "@stigmer/sdk";
5
5
  import type { SearchResult } from "@stigmer/protos/ai/stigmer/search/v1/io_pb";
6
6
 
7
- /** Scope controls whether only the org's own resources or all accessible resources are returned. */
7
+ /**
8
+ * Scope controls resource listing boundaries.
9
+ *
10
+ * - `"org"` — resources owned by the active organization (public and private).
11
+ * - `"all"` — all resources the caller can access, across every organization.
12
+ */
8
13
  export type ResourceListScope = "org" | "all";
9
14
 
10
15
  export interface UseResourceListOptions {
@@ -17,8 +22,8 @@ export interface UseResourceListOptions {
17
22
  /**
18
23
  * Controls resource visibility scope.
19
24
  *
20
- * - `"org"` — only resources owned by the given organization (excludes public/platform resources).
21
- * - `"all"` — includes public/platform resources alongside the org's own resources.
25
+ * - `"org"` — all resources owned by the given organization, regardless of visibility.
26
+ * - `"all"` — all resources the caller is authorized to access, across all organizations.
22
27
  *
23
28
  * @default "org"
24
29
  */
@@ -83,9 +88,9 @@ export function useResourceList(
83
88
  setError(null);
84
89
 
85
90
  const params: ListParams = {
86
- org,
91
+ org: scope === "all" ? "" : org,
87
92
  query: query || undefined,
88
- excludePublic: scope === "org",
93
+ excludePublic: false,
89
94
  page: { num: page, size: pageSize },
90
95
  };
91
96
 
@@ -31,3 +31,6 @@ export type { UseAgentRefFromSessionReturn } from "./useAgentRefFromSession";
31
31
 
32
32
  export { groupSessionsByTime } from "./group-sessions";
33
33
  export type { SessionGroup } from "./group-sessions";
34
+
35
+ // Session utilities (re-exported from @stigmer/sdk)
36
+ export { PENDING_SUBJECT, resolvedSubject } from "@stigmer/sdk";
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import { useCallback, useState } from "react";
4
- import type {
5
- McpServerUsageInput,
6
- ResourceRef,
7
- WorkspaceEntryInput,
4
+ import {
5
+ PENDING_SUBJECT,
6
+ type McpServerUsageInput,
7
+ type ResourceRef,
8
+ type WorkspaceEntryInput,
8
9
  } from "@stigmer/sdk";
9
10
  import { useStigmer } from "../hooks";
10
11
  import { toError } from "../internal/toError";
@@ -119,7 +120,7 @@ export function useCreateSession(): UseCreateSessionReturn {
119
120
  const session = await stigmer.session.create({
120
121
  name: `session-${Date.now()}`,
121
122
  org: input.org,
122
- subject: input.subject,
123
+ subject: input.subject ?? PENDING_SUBJECT,
123
124
  workspaceEntries: input.workspaceEntries,
124
125
  mcpServerUsages: input.mcpServerUsages,
125
126
  skillRefs: input.skillRefs,
@@ -10,6 +10,7 @@ import { SkillState } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/status_p
10
10
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
11
11
  import { useSkill } from "./useSkill";
12
12
  import { ErrorMessage } from "../error/ErrorMessage";
13
+ import { VisibilityToggle } from "../library/VisibilityToggle";
13
14
  import { MARKDOWN_COMPONENTS, REMARK_PLUGINS } from "../internal/markdown-components";
14
15
 
15
16
  export interface SkillDetailViewProps {
@@ -27,7 +28,16 @@ export interface SkillDetailViewProps {
27
28
  *
28
29
  * Not called on error or not-found states.
29
30
  */
30
- readonly onResourceLoad?: (meta: { name: string }) => void;
31
+ readonly onResourceLoad?: (meta: { name: string; id: string }) => void;
32
+ /**
33
+ * Called when the user toggles visibility via the inline control.
34
+ * When provided, the header renders an interactive
35
+ * {@link VisibilityToggle} instead of a read-only badge.
36
+ * When omitted, visibility is displayed as a static "Public" pill.
37
+ */
38
+ readonly onVisibilityChange?: (v: ApiResourceVisibility) => void;
39
+ /** `true` while a visibility update RPC is in flight. */
40
+ readonly isVisibilityPending?: boolean;
31
41
  /** Additional CSS classes for the root container. */
32
42
  readonly className?: string;
33
43
  }
@@ -65,6 +75,8 @@ export function SkillDetailView({
65
75
  slug,
66
76
  version,
67
77
  onResourceLoad,
78
+ onVisibilityChange,
79
+ isVisibilityPending,
68
80
  className,
69
81
  }: SkillDetailViewProps) {
70
82
  const { skill, isLoading, error, refetch } = useSkill(org, slug, version);
@@ -74,7 +86,7 @@ export function SkillDetailView({
74
86
 
75
87
  useEffect(() => {
76
88
  if (skill?.metadata?.name) {
77
- onResourceLoadRef.current?.({ name: skill.metadata.name });
89
+ onResourceLoadRef.current?.({ name: skill.metadata.name, id: skill.metadata.id });
78
90
  }
79
91
  }, [skill]);
80
92
 
@@ -97,6 +109,8 @@ export function SkillDetailView({
97
109
  updatedAt={
98
110
  specAudit?.updatedAt ? timestampDate(specAudit.updatedAt) : null
99
111
  }
112
+ onVisibilityChange={onVisibilityChange}
113
+ isVisibilityPending={isVisibilityPending}
100
114
  />
101
115
 
102
116
  {spec?.skillMd && (
@@ -121,10 +135,14 @@ function Header({
121
135
  skill,
122
136
  createdAt,
123
137
  updatedAt,
138
+ onVisibilityChange,
139
+ isVisibilityPending,
124
140
  }: {
125
141
  readonly skill: Skill;
126
142
  readonly createdAt: Date | null;
127
143
  readonly updatedAt: Date | null;
144
+ readonly onVisibilityChange?: (v: ApiResourceVisibility) => void;
145
+ readonly isVisibilityPending?: boolean;
128
146
  }) {
129
147
  const meta = skill.metadata;
130
148
  const spec = skill.spec;
@@ -141,10 +159,18 @@ function Header({
141
159
  <h2 className="truncate text-lg font-semibold text-foreground">
142
160
  {displayName}
143
161
  </h2>
144
- {isPublic && (
145
- <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
146
- Public
147
- </span>
162
+ {onVisibilityChange && meta ? (
163
+ <VisibilityToggle
164
+ visibility={meta.visibility}
165
+ onVisibilityChange={onVisibilityChange}
166
+ isPending={isVisibilityPending}
167
+ />
168
+ ) : (
169
+ isPublic && (
170
+ <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
171
+ Public
172
+ </span>
173
+ )
148
174
  )}
149
175
  </div>
150
176
  <div className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted-foreground">