@stigmer/react 0.0.46 → 0.0.48
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.
- package/agent/AgentPicker.d.ts +12 -2
- package/agent/AgentPicker.d.ts.map +1 -1
- package/agent/AgentPicker.js +2 -2
- package/agent/AgentPicker.js.map +1 -1
- package/agent/useAgentSearch.d.ts +9 -1
- package/agent/useAgentSearch.d.ts.map +1 -1
- package/agent/useAgentSearch.js +9 -1
- package/agent/useAgentSearch.js.map +1 -1
- package/composer/ContextChip.d.ts +3 -3
- package/composer/ContextChip.d.ts.map +1 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +84 -11
- package/composer/SessionComposer.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts +12 -2
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +2 -2
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/useTriggerApprovalPolicySession.d.ts +6 -0
- package/mcp-server/useTriggerApprovalPolicySession.d.ts.map +1 -1
- package/mcp-server/useTriggerApprovalPolicySession.js +76 -5
- package/mcp-server/useTriggerApprovalPolicySession.js.map +1 -1
- package/package.json +4 -4
- package/search/useResourceSearch.d.ts +10 -0
- package/search/useResourceSearch.d.ts.map +1 -1
- package/search/useResourceSearch.js +4 -2
- package/search/useResourceSearch.js.map +1 -1
- package/skill/SkillPicker.d.ts +12 -2
- package/skill/SkillPicker.d.ts.map +1 -1
- package/skill/SkillPicker.js +2 -2
- package/skill/SkillPicker.js.map +1 -1
- package/src/agent/AgentPicker.tsx +13 -2
- package/src/agent/useAgentSearch.ts +9 -1
- package/src/composer/ContextChip.tsx +3 -3
- package/src/composer/SessionComposer.tsx +117 -7
- package/src/mcp-server/McpServerPicker.tsx +13 -2
- package/src/mcp-server/useTriggerApprovalPolicySession.ts +100 -5
- package/src/search/useResourceSearch.ts +14 -2
- package/src/skill/SkillPicker.tsx +13 -2
|
@@ -568,7 +568,11 @@ export function SessionComposer({
|
|
|
568
568
|
setConfigOpen(open);
|
|
569
569
|
if (!open) {
|
|
570
570
|
configMcpInitialServerKeyRef.current = undefined;
|
|
571
|
-
if (
|
|
571
|
+
if (
|
|
572
|
+
configActivePanel === "agent" &&
|
|
573
|
+
agentSetup.state.status !== "needsEnvVars" &&
|
|
574
|
+
agentSetup.state.status !== "submitting"
|
|
575
|
+
) {
|
|
572
576
|
agentSetup.reset();
|
|
573
577
|
}
|
|
574
578
|
}
|
|
@@ -578,7 +582,12 @@ export function SessionComposer({
|
|
|
578
582
|
|
|
579
583
|
const handleConfigActivePanelChange = useCallback(
|
|
580
584
|
(panel: string | null) => {
|
|
581
|
-
if (
|
|
585
|
+
if (
|
|
586
|
+
configActivePanel === "agent" &&
|
|
587
|
+
panel !== "agent" &&
|
|
588
|
+
agentSetup.state.status !== "needsEnvVars" &&
|
|
589
|
+
agentSetup.state.status !== "submitting"
|
|
590
|
+
) {
|
|
582
591
|
agentSetup.reset();
|
|
583
592
|
}
|
|
584
593
|
setConfigActivePanel(panel);
|
|
@@ -651,6 +660,12 @@ export function SessionComposer({
|
|
|
651
660
|
onAgentResolutionChange?.(null);
|
|
652
661
|
}, [onAgentRefChange, onAgentResolutionChange]);
|
|
653
662
|
|
|
663
|
+
const handlePendingAgentChipRemove = useCallback(() => {
|
|
664
|
+
agentSetup.reset();
|
|
665
|
+
onAgentRefChange?.(null);
|
|
666
|
+
onAgentResolutionChange?.(null);
|
|
667
|
+
}, [agentSetup, onAgentRefChange, onAgentResolutionChange]);
|
|
668
|
+
|
|
654
669
|
// ---------------------------------------------------------------------------
|
|
655
670
|
// Initial agent: auto-resolve on mount when initialAgentRef is provided
|
|
656
671
|
// ---------------------------------------------------------------------------
|
|
@@ -661,12 +676,40 @@ export function SessionComposer({
|
|
|
661
676
|
const initialAgentHandled = useRef(false);
|
|
662
677
|
|
|
663
678
|
useEffect(() => {
|
|
664
|
-
if (initialAgentRef
|
|
665
|
-
|
|
666
|
-
handleAgentSelectRef.current(initialAgentRef);
|
|
679
|
+
if (!initialAgentRef || !showAgent || !org || initialAgentHandled.current) {
|
|
680
|
+
return;
|
|
667
681
|
}
|
|
682
|
+
|
|
683
|
+
let cancelled = false;
|
|
684
|
+
initialAgentHandled.current = true;
|
|
685
|
+
|
|
686
|
+
handleAgentSelectRef.current(initialAgentRef).catch(() => {
|
|
687
|
+
if (!cancelled) {
|
|
688
|
+
initialAgentHandled.current = false;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
return () => {
|
|
693
|
+
cancelled = true;
|
|
694
|
+
};
|
|
668
695
|
}, [initialAgentRef, showAgent, org]);
|
|
669
696
|
|
|
697
|
+
// ---------------------------------------------------------------------------
|
|
698
|
+
// Initial agent: auto-open Configure > Agent when env vars are needed
|
|
699
|
+
// ---------------------------------------------------------------------------
|
|
700
|
+
|
|
701
|
+
const initialAgentConfigAutoOpened = useRef(false);
|
|
702
|
+
|
|
703
|
+
useEffect(() => {
|
|
704
|
+
if (initialAgentConfigAutoOpened.current) return;
|
|
705
|
+
if (!initialAgentRef) return;
|
|
706
|
+
if (agentSetup.state.status !== "needsEnvVars") return;
|
|
707
|
+
|
|
708
|
+
initialAgentConfigAutoOpened.current = true;
|
|
709
|
+
setConfigOpen(true);
|
|
710
|
+
setConfigActivePanel("agent");
|
|
711
|
+
}, [initialAgentRef, agentSetup.state.status]);
|
|
712
|
+
|
|
670
713
|
// ---------------------------------------------------------------------------
|
|
671
714
|
// Initial attachments: upload files on mount when provided
|
|
672
715
|
// ---------------------------------------------------------------------------
|
|
@@ -727,6 +770,38 @@ export function SessionComposer({
|
|
|
727
770
|
type: "agent",
|
|
728
771
|
onRemove: handleAgentChipRemove,
|
|
729
772
|
});
|
|
773
|
+
} else if (
|
|
774
|
+
agentSetup.state.status === "needsEnvVars" ||
|
|
775
|
+
agentSetup.state.status === "resolving" ||
|
|
776
|
+
agentSetup.state.status === "submitting"
|
|
777
|
+
) {
|
|
778
|
+
const st = agentSetup.state;
|
|
779
|
+
const ref = st.agentRef;
|
|
780
|
+
const refStr = `${ref.org}/${ref.slug}`;
|
|
781
|
+
const name =
|
|
782
|
+
st.status !== "resolving"
|
|
783
|
+
? st.agentName
|
|
784
|
+
: (displayNames.get(refStr) ?? ref.slug);
|
|
785
|
+
|
|
786
|
+
items.push({
|
|
787
|
+
key: `agent:${refStr}`,
|
|
788
|
+
label: name,
|
|
789
|
+
type: "agent",
|
|
790
|
+
onRemove: handlePendingAgentChipRemove,
|
|
791
|
+
status:
|
|
792
|
+
st.status === "resolving"
|
|
793
|
+
? "loading"
|
|
794
|
+
: st.status === "submitting"
|
|
795
|
+
? "submitting"
|
|
796
|
+
: "needsSetup",
|
|
797
|
+
onClick:
|
|
798
|
+
st.status === "needsEnvVars"
|
|
799
|
+
? () => {
|
|
800
|
+
setConfigOpen(true);
|
|
801
|
+
setConfigActivePanel("agent");
|
|
802
|
+
}
|
|
803
|
+
: undefined,
|
|
804
|
+
});
|
|
730
805
|
}
|
|
731
806
|
|
|
732
807
|
if (workspace) {
|
|
@@ -808,7 +883,9 @@ export function SessionComposer({
|
|
|
808
883
|
return items;
|
|
809
884
|
}, [
|
|
810
885
|
agentRef,
|
|
886
|
+
agentSetup.state,
|
|
811
887
|
handleAgentChipRemove,
|
|
888
|
+
handlePendingAgentChipRemove,
|
|
812
889
|
workspace,
|
|
813
890
|
showMcp,
|
|
814
891
|
mcpSetup.entries,
|
|
@@ -858,11 +935,17 @@ export function SessionComposer({
|
|
|
858
935
|
const configureItems = useMemo((): ConfigureMenuItem[] => {
|
|
859
936
|
const items: ConfigureMenuItem[] = [];
|
|
860
937
|
if (showAgent) {
|
|
938
|
+
const agentPending =
|
|
939
|
+
!agentRef &&
|
|
940
|
+
(agentSetup.state.status === "needsEnvVars" ||
|
|
941
|
+
agentSetup.state.status === "resolving" ||
|
|
942
|
+
agentSetup.state.status === "submitting");
|
|
861
943
|
items.push({
|
|
862
944
|
id: "agent",
|
|
863
945
|
icon: <AgentIcon />,
|
|
864
946
|
label: "Agent",
|
|
865
|
-
count: agentRef ? 1 : 0,
|
|
947
|
+
count: agentRef || agentPending ? 1 : 0,
|
|
948
|
+
hasWarning: agentPending && agentSetup.state.status === "needsEnvVars",
|
|
866
949
|
});
|
|
867
950
|
}
|
|
868
951
|
if (showMcp) {
|
|
@@ -891,7 +974,7 @@ export function SessionComposer({
|
|
|
891
974
|
});
|
|
892
975
|
}
|
|
893
976
|
return items;
|
|
894
|
-
}, [showAgent, agentRef, showMcp, mcpCount, mcpSetup.needsSetupCount, showSkills, skillCount, showSessionVars, sessionVarCount]);
|
|
977
|
+
}, [showAgent, agentRef, agentSetup.state, showMcp, mcpCount, mcpSetup.needsSetupCount, showSkills, skillCount, showSessionVars, sessionVarCount]);
|
|
895
978
|
|
|
896
979
|
const renderConfigPanel = useCallback(
|
|
897
980
|
(panelId: string): React.ReactNode => {
|
|
@@ -924,6 +1007,7 @@ export function SessionComposer({
|
|
|
924
1007
|
<div className="relative">
|
|
925
1008
|
<AgentPicker
|
|
926
1009
|
org={org!}
|
|
1010
|
+
scope="all"
|
|
927
1011
|
value={agentRef ?? null}
|
|
928
1012
|
onChange={handleAgentSelect}
|
|
929
1013
|
onDisplayNameResolved={handleDisplayNameResolved}
|
|
@@ -944,6 +1028,7 @@ export function SessionComposer({
|
|
|
944
1028
|
return (
|
|
945
1029
|
<McpServerPicker
|
|
946
1030
|
org={org!}
|
|
1031
|
+
scope="all"
|
|
947
1032
|
setup={{
|
|
948
1033
|
entries: mcpSetup.entries,
|
|
949
1034
|
onServerAdded: (ref) => mcpSetup.addServer(ref),
|
|
@@ -966,6 +1051,7 @@ export function SessionComposer({
|
|
|
966
1051
|
return (
|
|
967
1052
|
<SkillPicker
|
|
968
1053
|
org={org!}
|
|
1054
|
+
scope="all"
|
|
969
1055
|
value={skillRefs ?? []}
|
|
970
1056
|
onChange={onSkillRefsChange!}
|
|
971
1057
|
onDisplayNameResolved={handleDisplayNameResolved}
|
|
@@ -1087,6 +1173,30 @@ export function SessionComposer({
|
|
|
1087
1173
|
/>
|
|
1088
1174
|
)}
|
|
1089
1175
|
|
|
1176
|
+
{/* Zone 2.7: Agent setup warning */}
|
|
1177
|
+
{showAgent &&
|
|
1178
|
+
agentSetup.state.status === "needsEnvVars" &&
|
|
1179
|
+
!agentRef && (
|
|
1180
|
+
<div
|
|
1181
|
+
role="status"
|
|
1182
|
+
className="mx-3 mb-2 flex items-center gap-2 rounded-md bg-warning/10 px-2.5 py-1.5 text-xs text-warning"
|
|
1183
|
+
>
|
|
1184
|
+
<AlertTriangleIcon />
|
|
1185
|
+
<span>Agent needs configuration before use</span>
|
|
1186
|
+
<button
|
|
1187
|
+
type="button"
|
|
1188
|
+
onClick={() => {
|
|
1189
|
+
setConfigOpen(true);
|
|
1190
|
+
setConfigActivePanel("agent");
|
|
1191
|
+
}}
|
|
1192
|
+
disabled={isDisabled}
|
|
1193
|
+
className="ml-auto shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium hover:bg-warning/20 disabled:pointer-events-none disabled:opacity-50"
|
|
1194
|
+
>
|
|
1195
|
+
Configure
|
|
1196
|
+
</button>
|
|
1197
|
+
</div>
|
|
1198
|
+
)}
|
|
1199
|
+
|
|
1090
1200
|
{/* Zone 2.75: MCP setup warning */}
|
|
1091
1201
|
{showMcp && mcpSetup.needsSetupCount > 0 && (
|
|
1092
1202
|
<div
|
|
@@ -69,8 +69,18 @@ export interface McpServerSetupIntegration {
|
|
|
69
69
|
// ---------------------------------------------------------------------------
|
|
70
70
|
|
|
71
71
|
export interface McpServerPickerProps {
|
|
72
|
-
/** Organization slug
|
|
72
|
+
/** Organization slug used as the default search scope. */
|
|
73
73
|
readonly org: string;
|
|
74
|
+
/**
|
|
75
|
+
* Controls search scope.
|
|
76
|
+
*
|
|
77
|
+
* - `"org"` — search only within the provided organization.
|
|
78
|
+
* - `"all"` — search all organizations the caller can access,
|
|
79
|
+
* including public/platform MCP servers from other orgs.
|
|
80
|
+
*
|
|
81
|
+
* @default "org"
|
|
82
|
+
*/
|
|
83
|
+
readonly scope?: "org" | "all";
|
|
74
84
|
/**
|
|
75
85
|
* Currently selected MCP server usages.
|
|
76
86
|
*
|
|
@@ -227,6 +237,7 @@ function slugFromServerKey(key: string): string {
|
|
|
227
237
|
*/
|
|
228
238
|
export function McpServerPicker({
|
|
229
239
|
org,
|
|
240
|
+
scope,
|
|
230
241
|
value,
|
|
231
242
|
onChange,
|
|
232
243
|
onDisplayNameResolved,
|
|
@@ -240,7 +251,7 @@ export function McpServerPicker({
|
|
|
240
251
|
const listId = `${instanceId}-list`;
|
|
241
252
|
|
|
242
253
|
const { results, isLoading, error, query, setQuery } =
|
|
243
|
-
useMcpServerSearch(org);
|
|
254
|
+
useMcpServerSearch(org, { scope });
|
|
244
255
|
|
|
245
256
|
const [focusIndex, setFocusIndex] = useState(-1);
|
|
246
257
|
const [view, setView] = useState<PickerView>(() =>
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { create } from "@bufbuild/protobuf";
|
|
5
5
|
import { UploadAttachmentRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
6
|
+
import { ListAgentInstancesRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/io_pb";
|
|
7
|
+
import { ListEnvironmentsRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/io_pb";
|
|
8
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
6
9
|
import { PENDING_SUBJECT } from "@stigmer/sdk";
|
|
10
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
7
11
|
import { useStigmer } from "../hooks";
|
|
8
12
|
import { toError } from "../internal/toError";
|
|
13
|
+
import { buildPersonalInstanceInput } from "../agent-instance/buildPersonalInstanceInput";
|
|
9
14
|
|
|
10
15
|
export interface TriggerApprovalPolicyResult {
|
|
11
16
|
readonly sessionId: string;
|
|
@@ -36,6 +41,56 @@ export interface UseTriggerApprovalPolicySessionReturn {
|
|
|
36
41
|
const MCP_SERVER_CREATOR_SLUG = "mcp-server-creator";
|
|
37
42
|
const MCP_SERVER_CREATOR_ORG = "stigmer";
|
|
38
43
|
|
|
44
|
+
const PERSONAL_LABEL = "stigmer.ai/personal";
|
|
45
|
+
const FOR_AGENT_LABEL = "stigmer.ai/for-agent";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Finds an existing personal agent instance for the given agent, or
|
|
49
|
+
* creates one linked to the caller's personal environment.
|
|
50
|
+
*
|
|
51
|
+
* Re-checks immediately before creation to narrow the race window
|
|
52
|
+
* when multiple tabs trigger simultaneously (same guard used by
|
|
53
|
+
* `useAgentSetup.findOrCreatePersonalInstance`).
|
|
54
|
+
*/
|
|
55
|
+
async function findOrCreatePersonalInstance(
|
|
56
|
+
stigmer: Stigmer,
|
|
57
|
+
params: {
|
|
58
|
+
org: string;
|
|
59
|
+
agentId: string;
|
|
60
|
+
agentSlug: string;
|
|
61
|
+
personalEnvSlug: string;
|
|
62
|
+
},
|
|
63
|
+
) {
|
|
64
|
+
const agentLabel = `${MCP_SERVER_CREATOR_ORG}/${params.agentSlug}`;
|
|
65
|
+
|
|
66
|
+
const existing = await stigmer.agentInstance.list(
|
|
67
|
+
create(ListAgentInstancesRequestSchema, {
|
|
68
|
+
org: params.org,
|
|
69
|
+
labels: {
|
|
70
|
+
[PERSONAL_LABEL]: "true",
|
|
71
|
+
[FOR_AGENT_LABEL]: agentLabel,
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (existing.items.length > 0) {
|
|
77
|
+
return existing.items[0];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return stigmer.agentInstance.create(
|
|
81
|
+
buildPersonalInstanceInput({
|
|
82
|
+
org: params.org,
|
|
83
|
+
agentId: params.agentId,
|
|
84
|
+
agentSlug: params.agentSlug,
|
|
85
|
+
environmentRef: {
|
|
86
|
+
org: params.org,
|
|
87
|
+
slug: params.personalEnvSlug,
|
|
88
|
+
kind: ApiResourceKind.environment,
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
39
94
|
/**
|
|
40
95
|
* Orchestration hook that auto-creates a session with the `mcp-server-creator`
|
|
41
96
|
* agent and starts an execution with a pre-filled prompt and YAML attachment.
|
|
@@ -45,6 +100,12 @@ const MCP_SERVER_CREATOR_ORG = "stigmer";
|
|
|
45
100
|
* and generate `default_tool_approvals` entries with appropriate approval
|
|
46
101
|
* messages. The agent applies the result using the `apply_mcp_server` tool.
|
|
47
102
|
*
|
|
103
|
+
* The session is created with a **personal** agent instance whose
|
|
104
|
+
* `environment_refs` include the caller's personal environment. This
|
|
105
|
+
* ensures MCP server credentials (e.g. `STIGMER_API_KEY`) are available
|
|
106
|
+
* through the standard environment merge chain, consistent with how
|
|
107
|
+
* `SessionComposer` / `useAgentSetup` provisions sessions.
|
|
108
|
+
*
|
|
48
109
|
* @example
|
|
49
110
|
* ```tsx
|
|
50
111
|
* const { trigger, isTriggering, result } = useTriggerApprovalPolicySession();
|
|
@@ -79,19 +140,53 @@ export function useTriggerApprovalPolicySession(): UseTriggerApprovalPolicySessi
|
|
|
79
140
|
slug: MCP_SERVER_CREATOR_SLUG,
|
|
80
141
|
});
|
|
81
142
|
|
|
82
|
-
const
|
|
83
|
-
if (!
|
|
143
|
+
const agentId = agent.metadata?.id;
|
|
144
|
+
if (!agentId) {
|
|
84
145
|
throw new Error(
|
|
85
|
-
`Agent "${MCP_SERVER_CREATOR_ORG}/${MCP_SERVER_CREATOR_SLUG}"
|
|
86
|
-
"
|
|
146
|
+
`Agent "${MCP_SERVER_CREATOR_ORG}/${MCP_SERVER_CREATOR_SLUG}" not found. ` +
|
|
147
|
+
"Ensure the mcp-server-creator agent is properly set up.",
|
|
87
148
|
);
|
|
88
149
|
}
|
|
89
150
|
|
|
151
|
+
// Resolve the agent instance to use for the session. Prefer a
|
|
152
|
+
// personal instance linked to the caller's personal environment
|
|
153
|
+
// so MCP server credentials flow through environment_refs. Fall
|
|
154
|
+
// back to the default instance when no personal environment exists.
|
|
155
|
+
let instanceId: string;
|
|
156
|
+
|
|
157
|
+
const envList = await stigmer.environment.list(
|
|
158
|
+
create(ListEnvironmentsRequestSchema, {
|
|
159
|
+
org,
|
|
160
|
+
labels: { [PERSONAL_LABEL]: "true" },
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (envList.items.length > 0) {
|
|
165
|
+
const personalEnvSlug = envList.items[0].metadata!.slug;
|
|
166
|
+
const instance = await findOrCreatePersonalInstance(stigmer, {
|
|
167
|
+
org,
|
|
168
|
+
agentId,
|
|
169
|
+
agentSlug: MCP_SERVER_CREATOR_SLUG,
|
|
170
|
+
personalEnvSlug,
|
|
171
|
+
});
|
|
172
|
+
instanceId = instance.metadata!.id;
|
|
173
|
+
} else {
|
|
174
|
+
const defaultInstanceId = agent.status?.defaultInstanceId;
|
|
175
|
+
if (!defaultInstanceId) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Agent "${MCP_SERVER_CREATOR_ORG}/${MCP_SERVER_CREATOR_SLUG}" does not have a ` +
|
|
178
|
+
"default instance and no personal environment exists. " +
|
|
179
|
+
"Add credentials to your personal environment in Settings first.",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
instanceId = defaultInstanceId;
|
|
183
|
+
}
|
|
184
|
+
|
|
90
185
|
const session = await stigmer.session.create({
|
|
91
186
|
name: `approval-policy-${mcpServerSlug}-${Date.now()}`,
|
|
92
187
|
org,
|
|
93
188
|
subject: PENDING_SUBJECT,
|
|
94
|
-
agentInstanceId:
|
|
189
|
+
agentInstanceId: instanceId,
|
|
95
190
|
});
|
|
96
191
|
|
|
97
192
|
const sessionId = session.metadata!.id;
|
|
@@ -9,6 +9,16 @@ export interface UseResourceSearchOptions {
|
|
|
9
9
|
readonly pageSize?: number;
|
|
10
10
|
/** Debounce delay for query changes in milliseconds. @default 300 */
|
|
11
11
|
readonly debounceMs?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Controls search scope.
|
|
14
|
+
*
|
|
15
|
+
* - `"org"` — search only within the provided organization.
|
|
16
|
+
* - `"all"` — search all organizations the caller can access,
|
|
17
|
+
* including public/platform resources from other orgs.
|
|
18
|
+
*
|
|
19
|
+
* @default "org"
|
|
20
|
+
*/
|
|
21
|
+
readonly scope?: "org" | "all";
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
export interface UseResourceSearchReturn {
|
|
@@ -46,6 +56,7 @@ export function useResourceSearch(
|
|
|
46
56
|
|
|
47
57
|
const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
48
58
|
const debounceMs = options?.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
59
|
+
const scope = options?.scope ?? "org";
|
|
49
60
|
|
|
50
61
|
// Debounce query changes
|
|
51
62
|
useEffect(() => {
|
|
@@ -61,8 +72,9 @@ export function useResourceSearch(
|
|
|
61
72
|
setError(null);
|
|
62
73
|
|
|
63
74
|
const params: ListParams = {
|
|
64
|
-
org,
|
|
75
|
+
org: scope === "all" ? "" : org,
|
|
65
76
|
query: debouncedQuery || undefined,
|
|
77
|
+
excludePublic: false,
|
|
66
78
|
page: { num: 1, size: pageSize },
|
|
67
79
|
};
|
|
68
80
|
|
|
@@ -84,7 +96,7 @@ export function useResourceSearch(
|
|
|
84
96
|
return () => {
|
|
85
97
|
cancelled.current = true;
|
|
86
98
|
};
|
|
87
|
-
}, [listFn, org, debouncedQuery, pageSize, fetchKey]);
|
|
99
|
+
}, [listFn, org, debouncedQuery, pageSize, scope, fetchKey]);
|
|
88
100
|
|
|
89
101
|
return { results, isLoading, error, query, setQuery, refetch };
|
|
90
102
|
}
|
|
@@ -17,8 +17,18 @@ import { useScrollShadows } from "../internal/useScrollShadows";
|
|
|
17
17
|
import { ScrollFade } from "../internal/ScrollFade";
|
|
18
18
|
|
|
19
19
|
export interface SkillPickerProps {
|
|
20
|
-
/** Organization slug
|
|
20
|
+
/** Organization slug used as the default search scope. */
|
|
21
21
|
readonly org: string;
|
|
22
|
+
/**
|
|
23
|
+
* Controls search scope.
|
|
24
|
+
*
|
|
25
|
+
* - `"org"` — search only within the provided organization.
|
|
26
|
+
* - `"all"` — search all organizations the caller can access,
|
|
27
|
+
* including public/platform skills from other orgs.
|
|
28
|
+
*
|
|
29
|
+
* @default "org"
|
|
30
|
+
*/
|
|
31
|
+
readonly scope?: "org" | "all";
|
|
22
32
|
/** Currently selected skill references. */
|
|
23
33
|
readonly value: ResourceRef[];
|
|
24
34
|
/** Called when the selection changes. */
|
|
@@ -48,13 +58,14 @@ const LIST_ID = "stgm-skill-list";
|
|
|
48
58
|
*/
|
|
49
59
|
export function SkillPicker({
|
|
50
60
|
org,
|
|
61
|
+
scope,
|
|
51
62
|
value,
|
|
52
63
|
onChange,
|
|
53
64
|
onDisplayNameResolved,
|
|
54
65
|
disabled,
|
|
55
66
|
className,
|
|
56
67
|
}: SkillPickerProps) {
|
|
57
|
-
const { results, isLoading, error, query, setQuery } = useSkillSearch(org);
|
|
68
|
+
const { results, isLoading, error, query, setQuery } = useSkillSearch(org, { scope });
|
|
58
69
|
|
|
59
70
|
const [focusIndex, setFocusIndex] = useState(-1);
|
|
60
71
|
const searchRef = useRef<HTMLInputElement>(null);
|