@stigmer/react 3.1.7 → 3.1.8

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 (195) hide show
  1. package/agent/AgentDetailView.d.ts +9 -1
  2. package/agent/AgentDetailView.d.ts.map +1 -1
  3. package/agent/AgentDetailView.js +15 -4
  4. package/agent/AgentDetailView.js.map +1 -1
  5. package/environment/EnvironmentListPanel.d.ts.map +1 -1
  6. package/environment/EnvironmentListPanel.js +11 -3
  7. package/environment/EnvironmentListPanel.js.map +1 -1
  8. package/environment/EnvironmentVariableEditor.d.ts.map +1 -1
  9. package/environment/EnvironmentVariableEditor.js +9 -3
  10. package/environment/EnvironmentVariableEditor.js.map +1 -1
  11. package/environment/index.d.ts +1 -0
  12. package/environment/index.d.ts.map +1 -1
  13. package/environment/index.js +1 -0
  14. package/environment/index.js.map +1 -1
  15. package/environment/shareRestriction.d.ts +13 -0
  16. package/environment/shareRestriction.d.ts.map +1 -0
  17. package/environment/shareRestriction.js +15 -0
  18. package/environment/shareRestriction.js.map +1 -0
  19. package/execution/ToolCallItem.d.ts.map +1 -1
  20. package/execution/ToolCallItem.js +12 -0
  21. package/execution/ToolCallItem.js.map +1 -1
  22. package/execution/ToolRunGroup.js +4 -0
  23. package/execution/ToolRunGroup.js.map +1 -1
  24. package/execution/WriteBackCard.d.ts +14 -5
  25. package/execution/WriteBackCard.d.ts.map +1 -1
  26. package/execution/WriteBackCard.js +103 -32
  27. package/execution/WriteBackCard.js.map +1 -1
  28. package/execution/WriteBacksWidget.d.ts +1 -1
  29. package/execution/WriteBacksWidget.js +2 -2
  30. package/execution/WriteBacksWidget.js.map +1 -1
  31. package/execution/index.d.ts +2 -0
  32. package/execution/index.d.ts.map +1 -1
  33. package/execution/index.js +1 -0
  34. package/execution/index.js.map +1 -1
  35. package/execution/write-back-utils.d.ts +57 -0
  36. package/execution/write-back-utils.d.ts.map +1 -0
  37. package/execution/write-back-utils.js +81 -0
  38. package/execution/write-back-utils.js.map +1 -0
  39. package/index.d.ts +6 -2
  40. package/index.d.ts.map +1 -1
  41. package/index.js +6 -1
  42. package/index.js.map +1 -1
  43. package/internal/dev/dom-counter.d.ts.map +1 -1
  44. package/internal/dev/dom-counter.js +2 -2
  45. package/internal/dev/dom-counter.js.map +1 -1
  46. package/internal/dev/enabled.d.ts +7 -0
  47. package/internal/dev/enabled.d.ts.map +1 -0
  48. package/internal/dev/enabled.js +38 -0
  49. package/internal/dev/enabled.js.map +1 -0
  50. package/internal/dev/profiler-wrapper.d.ts +3 -3
  51. package/internal/dev/profiler-wrapper.d.ts.map +1 -1
  52. package/internal/dev/profiler-wrapper.js +5 -5
  53. package/internal/dev/profiler-wrapper.js.map +1 -1
  54. package/internal/dev/use-key-stability.d.ts.map +1 -1
  55. package/internal/dev/use-key-stability.js +2 -2
  56. package/internal/dev/use-key-stability.js.map +1 -1
  57. package/internal/dev/use-render-tracer.d.ts +2 -2
  58. package/internal/dev/use-render-tracer.d.ts.map +1 -1
  59. package/internal/dev/use-render-tracer.js +4 -4
  60. package/internal/dev/use-render-tracer.js.map +1 -1
  61. package/internal/dev/use-stream-rate.d.ts +2 -1
  62. package/internal/dev/use-stream-rate.d.ts.map +1 -1
  63. package/internal/dev/use-stream-rate.js +4 -3
  64. package/internal/dev/use-stream-rate.js.map +1 -1
  65. package/library/ResourceVisibilityControl.d.ts +3 -0
  66. package/library/ResourceVisibilityControl.d.ts.map +1 -1
  67. package/library/ResourceVisibilityControl.js +22 -8
  68. package/library/ResourceVisibilityControl.js.map +1 -1
  69. package/library/useUpdateVisibility.d.ts +5 -3
  70. package/library/useUpdateVisibility.d.ts.map +1 -1
  71. package/library/useUpdateVisibility.js +7 -2
  72. package/library/useUpdateVisibility.js.map +1 -1
  73. package/library/visibilityLevels.d.ts +17 -0
  74. package/library/visibilityLevels.d.ts.map +1 -1
  75. package/library/visibilityLevels.js +32 -0
  76. package/library/visibilityLevels.js.map +1 -1
  77. package/package.json +4 -4
  78. package/session/NewSessionViewer.d.ts +11 -1
  79. package/session/NewSessionViewer.d.ts.map +1 -1
  80. package/session/NewSessionViewer.js +28 -8
  81. package/session/NewSessionViewer.js.map +1 -1
  82. package/session/SessionViewer.d.ts +9 -2
  83. package/session/SessionViewer.d.ts.map +1 -1
  84. package/session/SessionViewer.js +26 -12
  85. package/session/SessionViewer.js.map +1 -1
  86. package/session/audience.d.ts +12 -4
  87. package/session/audience.d.ts.map +1 -1
  88. package/session/facets/ChangesTab.d.ts.map +1 -1
  89. package/session/facets/ChangesTab.js +3 -1
  90. package/session/facets/ChangesTab.js.map +1 -1
  91. package/session/useNewSessionFlow.d.ts +12 -0
  92. package/session/useNewSessionFlow.d.ts.map +1 -1
  93. package/session/useNewSessionFlow.js +13 -1
  94. package/session/useNewSessionFlow.js.map +1 -1
  95. package/session/useSessionPageFlow.d.ts +13 -0
  96. package/session/useSessionPageFlow.d.ts.map +1 -1
  97. package/session/useSessionPageFlow.js +7 -2
  98. package/session/useSessionPageFlow.js.map +1 -1
  99. package/sharing/ShareAgentDialog.d.ts +55 -0
  100. package/sharing/ShareAgentDialog.d.ts.map +1 -0
  101. package/sharing/ShareAgentDialog.js +393 -0
  102. package/sharing/ShareAgentDialog.js.map +1 -0
  103. package/sharing/SharedAgentChat.d.ts +84 -0
  104. package/sharing/SharedAgentChat.d.ts.map +1 -0
  105. package/sharing/SharedAgentChat.js +80 -0
  106. package/sharing/SharedAgentChat.js.map +1 -0
  107. package/sharing/index.d.ts +16 -0
  108. package/sharing/index.d.ts.map +1 -0
  109. package/sharing/index.js +11 -0
  110. package/sharing/index.js.map +1 -0
  111. package/sharing/useRotateShareLink.d.ts +42 -0
  112. package/sharing/useRotateShareLink.d.ts.map +1 -0
  113. package/sharing/useRotateShareLink.js +55 -0
  114. package/sharing/useRotateShareLink.js.map +1 -0
  115. package/sharing/useShareAgent.d.ts +68 -0
  116. package/sharing/useShareAgent.d.ts.map +1 -0
  117. package/sharing/useShareAgent.js +41 -0
  118. package/sharing/useShareAgent.js.map +1 -0
  119. package/sharing/useShareToolReadiness.d.ts +48 -0
  120. package/sharing/useShareToolReadiness.d.ts.map +1 -0
  121. package/sharing/useShareToolReadiness.js +93 -0
  122. package/sharing/useShareToolReadiness.js.map +1 -0
  123. package/sharing/useSharedAgentProfile.d.ts +84 -0
  124. package/sharing/useSharedAgentProfile.d.ts.map +1 -0
  125. package/sharing/useSharedAgentProfile.js +72 -0
  126. package/sharing/useSharedAgentProfile.js.map +1 -0
  127. package/sharing/useUpdateAgentSharing.d.ts +86 -0
  128. package/sharing/useUpdateAgentSharing.d.ts.map +1 -0
  129. package/sharing/useUpdateAgentSharing.js +81 -0
  130. package/sharing/useUpdateAgentSharing.js.map +1 -0
  131. package/src/agent/AgentDetailView.tsx +28 -3
  132. package/src/environment/EnvironmentListPanel.tsx +47 -18
  133. package/src/environment/EnvironmentVariableEditor.tsx +27 -17
  134. package/src/environment/__tests__/shareRestriction.test.ts +35 -0
  135. package/src/environment/index.ts +5 -0
  136. package/src/environment/shareRestriction.ts +22 -0
  137. package/src/execution/ToolCallItem.tsx +19 -1
  138. package/src/execution/ToolRunGroup.tsx +4 -0
  139. package/src/execution/WriteBackCard.tsx +285 -88
  140. package/src/execution/WriteBacksWidget.tsx +5 -5
  141. package/src/execution/__tests__/ToolCallItem.test.tsx +51 -0
  142. package/src/execution/__tests__/ToolRunGroup.test.tsx +22 -0
  143. package/src/execution/__tests__/WriteBackCard.test.tsx +64 -10
  144. package/src/execution/__tests__/write-back-utils.test.ts +135 -0
  145. package/src/execution/index.ts +7 -0
  146. package/src/execution/write-back-utils.ts +107 -0
  147. package/src/index.ts +34 -0
  148. package/src/internal/dev/__tests__/use-key-stability.test.ts +4 -0
  149. package/src/internal/dev/__tests__/use-render-tracer.test.ts +4 -0
  150. package/src/internal/dev/dom-counter.ts +2 -3
  151. package/src/internal/dev/enabled.ts +39 -0
  152. package/src/internal/dev/profiler-wrapper.tsx +5 -6
  153. package/src/internal/dev/use-key-stability.ts +2 -3
  154. package/src/internal/dev/use-render-tracer.ts +4 -5
  155. package/src/internal/dev/use-stream-rate.ts +4 -4
  156. package/src/library/ResourceVisibilityControl.tsx +23 -7
  157. package/src/library/__tests__/visibilityLevels.test.ts +33 -0
  158. package/src/library/useUpdateVisibility.ts +9 -3
  159. package/src/library/visibilityLevels.ts +36 -0
  160. package/src/session/NewSessionViewer.tsx +67 -31
  161. package/src/session/SessionViewer.tsx +67 -36
  162. package/src/session/__tests__/audienceWiring.test.tsx +99 -0
  163. package/src/session/__tests__/useNewSessionFlow.test.tsx +75 -1
  164. package/src/session/__tests__/useSessionPageFlow.guest.test.tsx +112 -0
  165. package/src/session/audience.ts +12 -4
  166. package/src/session/facets/ChangesTab.tsx +6 -4
  167. package/src/session/useNewSessionFlow.ts +26 -1
  168. package/src/session/useSessionPageFlow.ts +24 -2
  169. package/src/sharing/ShareAgentDialog.tsx +1255 -0
  170. package/src/sharing/SharedAgentChat.tsx +283 -0
  171. package/src/sharing/__tests__/ShareAgentDialog.test.tsx +745 -0
  172. package/src/sharing/__tests__/SharedAgentChat.test.tsx +222 -0
  173. package/src/sharing/__tests__/useRotateShareLink.test.tsx +96 -0
  174. package/src/sharing/__tests__/useShareAgent.test.tsx +127 -0
  175. package/src/sharing/__tests__/useShareToolReadiness.test.tsx +182 -0
  176. package/src/sharing/__tests__/useSharedAgentProfile.test.tsx +201 -0
  177. package/src/sharing/__tests__/useUpdateAgentSharing.test.tsx +200 -0
  178. package/src/sharing/index.ts +30 -0
  179. package/src/sharing/useRotateShareLink.ts +76 -0
  180. package/src/sharing/useShareAgent.tsx +106 -0
  181. package/src/sharing/useShareToolReadiness.ts +129 -0
  182. package/src/sharing/useSharedAgentProfile.ts +131 -0
  183. package/src/sharing/useUpdateAgentSharing.ts +151 -0
  184. package/src/switch/Switch.tsx +92 -0
  185. package/src/switch/__tests__/Switch.test.tsx +88 -0
  186. package/src/switch/index.ts +2 -0
  187. package/styles.css +1 -1
  188. package/switch/Switch.d.ts +47 -0
  189. package/switch/Switch.d.ts.map +1 -0
  190. package/switch/Switch.js +36 -0
  191. package/switch/Switch.js.map +1 -0
  192. package/switch/index.d.ts +3 -0
  193. package/switch/index.d.ts.map +1 -0
  194. package/switch/index.js +2 -0
  195. package/switch/index.js.map +1 -0
@@ -0,0 +1,86 @@
1
+ import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
2
+ import { AgentSharingAudience } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
3
+ /**
4
+ * Who can chat with a shared agent over its hosted link.
5
+ *
6
+ * - `"public"` — anyone with the link; visitors are anonymous guests.
7
+ * - `"org"` — signed-in members of the owning organization only.
8
+ * Membership is checked on every conversation turn, so access ends the
9
+ * moment a member leaves the org.
10
+ */
11
+ export type SharingAudience = "public" | "org";
12
+ /**
13
+ * A complete sharing configuration to persist.
14
+ *
15
+ * Every field is required by design: the `updateSharing` RPC **replaces**
16
+ * the agent's whole `spec.sharing` block, so a caller must always supply
17
+ * the full configuration. A partial type here would let a toggle change
18
+ * silently wipe `allowedOrigins`, `messages`, or the `audience` — the
19
+ * required shape makes that mistake unrepresentable.
20
+ */
21
+ export interface AgentSharingDraft {
22
+ /** Whether hosted-chat access for the configured audience is enabled. */
23
+ readonly enabled: boolean;
24
+ /** Who can chat: anyone with the link, or org members only. */
25
+ readonly audience: SharingAudience;
26
+ /**
27
+ * Exact web origins allowed to embed the shared agent
28
+ * (e.g. `https://example.com`). Stored now; enforced by the embed
29
+ * widget's Origin check. Embedding is public-audience only.
30
+ */
31
+ readonly allowedOrigins: readonly string[];
32
+ /** Owner-customized visitor refusal copy. Empty strings mean platform defaults. */
33
+ readonly messages: {
34
+ /** Shown when a visitor exceeds the message rate limit. */
35
+ readonly rateLimited: string;
36
+ /** Shown when the org's credits are exhausted (sharing fails closed). */
37
+ readonly unavailable: string;
38
+ /** Shown when a conversation hits its turn limit or inactivity timeout. */
39
+ readonly conversationEnded: string;
40
+ };
41
+ }
42
+ /**
43
+ * Maps a proto {@link AgentSharingAudience} to the SDK's string union.
44
+ * Unspecified means public by contract (pre-audience shares keep their
45
+ * anyone-with-link behavior).
46
+ */
47
+ export declare function sharingAudienceFromProto(audience: AgentSharingAudience | undefined): SharingAudience;
48
+ /** Return value of {@link useUpdateAgentSharing}. */
49
+ export interface UseUpdateAgentSharingReturn {
50
+ /**
51
+ * Persist the complete sharing configuration via the `updateSharing`
52
+ * RPC. Resolves with the updated agent.
53
+ */
54
+ readonly updateSharing: (draft: AgentSharingDraft) => Promise<Agent | undefined>;
55
+ /** `true` while the RPC is in flight. */
56
+ readonly isPending: boolean;
57
+ /** The last error from the RPC, or `null`. */
58
+ readonly error: Error | null;
59
+ }
60
+ /**
61
+ * Behavior hook that updates an agent's sharing configuration.
62
+ *
63
+ * Wraps the targeted `stigmer.agent.updateSharing()` RPC (authorized by
64
+ * `can_edit`) with loading and error state management. The hook is
65
+ * stateless with respect to the agent — the caller refreshes the resource
66
+ * after a successful update (e.g. via `refetch` from `useAgent`), matching
67
+ * the {@link useUpdateVisibility} convention.
68
+ *
69
+ * Pass `null` for `agentId` to produce a stable no-op (useful while the
70
+ * agent is still loading).
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * const { updateSharing, isPending } = useUpdateAgentSharing(agent.metadata.id);
75
+ *
76
+ * await updateSharing({
77
+ * enabled: true,
78
+ * audience: "public",
79
+ * allowedOrigins: ["https://example.com"],
80
+ * messages: { rateLimited: "", unavailable: "", conversationEnded: "" },
81
+ * });
82
+ * refetch();
83
+ * ```
84
+ */
85
+ export declare function useUpdateAgentSharing(agentId: string | null): UseUpdateAgentSharingReturn;
86
+ //# sourceMappingURL=useUpdateAgentSharing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUpdateAgentSharing.d.ts","sourceRoot":"","sources":["../../src/sharing/useUpdateAgentSharing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAEhF,OAAO,EACL,oBAAoB,EAGrB,MAAM,qDAAqD,CAAC;AAI7D;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE;QACjB,2DAA2D;QAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,yEAAyE;QACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,2EAA2E;QAC3E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;KACpC,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,GACzC,eAAe,CAEjB;AAQD,qDAAqD;AACrD,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IACjF,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,2BAA2B,CAsC7B"}
@@ -0,0 +1,81 @@
1
+ "use client";
2
+ import { useCallback, useState } from "react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import { UpdateAgentSharingInputSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/io_pb";
5
+ import { AgentSharingAudience, AgentSharingSchema, AgentSharingMessagesSchema, } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
6
+ import { useStigmer } from "../hooks.js";
7
+ import { toError } from "../internal/toError.js";
8
+ /**
9
+ * Maps a proto {@link AgentSharingAudience} to the SDK's string union.
10
+ * Unspecified means public by contract (pre-audience shares keep their
11
+ * anyone-with-link behavior).
12
+ */
13
+ export function sharingAudienceFromProto(audience) {
14
+ return audience === AgentSharingAudience.org ? "org" : "public";
15
+ }
16
+ function sharingAudienceToProto(audience) {
17
+ return audience === "org"
18
+ ? AgentSharingAudience.org
19
+ : AgentSharingAudience.public;
20
+ }
21
+ /**
22
+ * Behavior hook that updates an agent's sharing configuration.
23
+ *
24
+ * Wraps the targeted `stigmer.agent.updateSharing()` RPC (authorized by
25
+ * `can_edit`) with loading and error state management. The hook is
26
+ * stateless with respect to the agent — the caller refreshes the resource
27
+ * after a successful update (e.g. via `refetch` from `useAgent`), matching
28
+ * the {@link useUpdateVisibility} convention.
29
+ *
30
+ * Pass `null` for `agentId` to produce a stable no-op (useful while the
31
+ * agent is still loading).
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * const { updateSharing, isPending } = useUpdateAgentSharing(agent.metadata.id);
36
+ *
37
+ * await updateSharing({
38
+ * enabled: true,
39
+ * audience: "public",
40
+ * allowedOrigins: ["https://example.com"],
41
+ * messages: { rateLimited: "", unavailable: "", conversationEnded: "" },
42
+ * });
43
+ * refetch();
44
+ * ```
45
+ */
46
+ export function useUpdateAgentSharing(agentId) {
47
+ const stigmer = useStigmer();
48
+ const [isPending, setIsPending] = useState(false);
49
+ const [error, setError] = useState(null);
50
+ const updateSharing = useCallback(async (draft) => {
51
+ if (!agentId)
52
+ return undefined;
53
+ setIsPending(true);
54
+ setError(null);
55
+ try {
56
+ const input = create(UpdateAgentSharingInputSchema, {
57
+ resourceId: agentId,
58
+ sharing: create(AgentSharingSchema, {
59
+ enabled: draft.enabled,
60
+ audience: sharingAudienceToProto(draft.audience),
61
+ allowedOrigins: [...draft.allowedOrigins],
62
+ messages: create(AgentSharingMessagesSchema, {
63
+ rateLimited: draft.messages.rateLimited,
64
+ unavailable: draft.messages.unavailable,
65
+ conversationEnded: draft.messages.conversationEnded,
66
+ }),
67
+ }),
68
+ });
69
+ return await stigmer.agent.updateSharing(input);
70
+ }
71
+ catch (err) {
72
+ setError(toError(err));
73
+ throw err;
74
+ }
75
+ finally {
76
+ setIsPending(false);
77
+ }
78
+ }, [agentId, stigmer]);
79
+ return { updateSharing, isPending, error };
80
+ }
81
+ //# sourceMappingURL=useUpdateAgentSharing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUpdateAgentSharing.js","sourceRoot":"","sources":["../../src/sharing/useUpdateAgentSharing.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,OAAO,EAAE,6BAA6B,EAAE,MAAM,mDAAmD,CAAC;AAClG,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AA2CjD;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAA0C;IAE1C,OAAO,QAAQ,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClE,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAyB;IACvD,OAAO,QAAQ,KAAK,KAAK;QACvB,CAAC,CAAC,oBAAoB,CAAC,GAAG;QAC1B,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC;AAClC,CAAC;AAeD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAsB;IAEtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,aAAa,GAAG,WAAW,CAC/B,KAAK,EAAE,KAAwB,EAA8B,EAAE;QAC7D,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAE/B,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEf,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,6BAA6B,EAAE;gBAClD,UAAU,EAAE,OAAO;gBACnB,OAAO,EAAE,MAAM,CAAC,kBAAkB,EAAE;oBAClC,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,QAAQ,EAAE,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC;oBAChD,cAAc,EAAE,CAAC,GAAG,KAAK,CAAC,cAAc,CAAC;oBACzC,QAAQ,EAAE,MAAM,CAAC,0BAA0B,EAAE;wBAC3C,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW;wBACvC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW;wBACvC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,iBAAiB;qBACpD,CAAC;iBACH,CAAC;aACH,CAAC,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YACvB,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAC;IAEF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC"}
@@ -17,6 +17,7 @@ import { agentToInput } from "./internal/agentToInput.js";
17
17
  import { ErrorMessage } from "../error/ErrorMessage.js";
18
18
  import { VisibilityBadge } from "../library/VisibilitySelector.js";
19
19
  import { useManageAccess } from "../access/useManageAccess.js";
20
+ import { useShareAgent } from "../sharing/useShareAgent.js";
20
21
  import { ResourceDetailShell } from "../resource-detail/ResourceDetailShell.js";
21
22
  import { Section } from "../resource-detail/Section.js";
22
23
  import { useDetailTabs } from "../resource-detail/useDetailTabs.js";
@@ -127,6 +128,14 @@ export interface AgentDetailViewProps {
127
128
  * Opens the create instance dialog.
128
129
  */
129
130
  readonly onCreateInstanceClick?: () => void;
131
+ /**
132
+ * Builds the absolute public chat URL shown in the Share dialog.
133
+ * The host application owns URL construction — its configured public
134
+ * origin may differ from the rendering origin (e.g. the desktop app).
135
+ * When omitted, the dialog falls back to the relative
136
+ * `/chat/<org>/<slug>` path.
137
+ */
138
+ readonly buildShareUrl?: (org: string, slug: string) => string;
130
139
  /**
131
140
  * Called when the user clicks an instance row in the Instances tab.
132
141
  * Typically opens an instance detail panel.
@@ -204,6 +213,7 @@ export function AgentDetailView({
204
213
  defaultTab,
205
214
  editable = false,
206
215
  onResourceUpdated,
216
+ buildShareUrl,
207
217
  onCreateInstanceClick,
208
218
  onInstanceClick,
209
219
  onInstanceStartSessionClick,
@@ -306,6 +316,15 @@ export function AgentDetailView({
306
316
  : undefined,
307
317
  });
308
318
 
319
+ // Share — the sibling consent to Manage access: visibility governs who
320
+ // can read the blueprint; sharing governs who can chat with the running
321
+ // agent (billed to the owning org). Same null-until-ready contract.
322
+ const share = useShareAgent({
323
+ agent,
324
+ buildShareUrl,
325
+ onSharingChanged: refetch,
326
+ });
327
+
309
328
  if (isLoading) return <LoadingSkeleton className={className} />;
310
329
  if (error)
311
330
  return <ErrorMessage error={error} retry={refetch} className={className} />;
@@ -344,9 +363,14 @@ export function AgentDetailView({
344
363
  <VisibilityBadge visibility={meta.visibility} />
345
364
  ) : undefined;
346
365
 
347
- const mergedActions = access.action
348
- ? [...(actions ?? []), access.action]
349
- : actions;
366
+ // Share precedes Manage access within the "sharing" group.
367
+ const injectedActions = [share.action, access.action].filter(
368
+ (a): a is NonNullable<typeof a> => a != null,
369
+ );
370
+ const mergedActions =
371
+ injectedActions.length > 0
372
+ ? [...(actions ?? []), ...injectedActions]
373
+ : actions;
350
374
 
351
375
  let tabContent: React.ReactNode;
352
376
  if (activeAdditionalTab) {
@@ -402,6 +426,7 @@ export function AgentDetailView({
402
426
  {tabContent}
403
427
  </ResourceDetailShell>
404
428
  {access.dialog}
429
+ {share.dialog}
405
430
  </>
406
431
  );
407
432
  }
@@ -4,8 +4,11 @@ import { useCallback, useMemo, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
5
  import { getUserMessage } from "@stigmer/sdk";
6
6
  import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
7
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
8
+ import { ResourceVisibilityControl } from "../library/ResourceVisibilityControl.js";
7
9
  import { useEnvironmentList } from "./useEnvironmentList.js";
8
10
  import { EnvironmentVariableEditor } from "./EnvironmentVariableEditor.js";
11
+ import { isShareRestrictedEnvironment } from "./shareRestriction.js";
9
12
 
10
13
  // ---------------------------------------------------------------------------
11
14
  // Public API
@@ -175,6 +178,7 @@ export function EnvironmentListPanel({
175
178
  isExpanded={isExpanded}
176
179
  onToggle={() => handleToggle(env)}
177
180
  readOnly={readOnly}
181
+ onVisibilityChanged={refetch}
178
182
  />
179
183
  );
180
184
  })}
@@ -191,11 +195,13 @@ function EnvironmentCard({
191
195
  isExpanded,
192
196
  onToggle,
193
197
  readOnly,
198
+ onVisibilityChanged,
194
199
  }: {
195
200
  environment: Environment;
196
201
  isExpanded: boolean;
197
202
  onToggle: () => void;
198
203
  readOnly: boolean;
204
+ onVisibilityChanged: () => void;
199
205
  }) {
200
206
  const name =
201
207
  environment.metadata?.name || environment.metadata?.slug || "Unnamed";
@@ -203,6 +209,12 @@ function EnvironmentCard({
203
209
  const variableCount = Object.keys(environment.spec?.data ?? {}).length;
204
210
  const environmentId = environment.metadata?.id ?? "";
205
211
 
212
+ // Personal and OAuth-managed environments are never org-shareable
213
+ // (the backend rejects the transition) — offering the control would
214
+ // be an error trap, so it is structurally absent for them.
215
+ const showVisibilityControl =
216
+ !readOnly && environmentId !== "" && !isShareRestrictedEnvironment(environment);
217
+
206
218
  return (
207
219
  <div
208
220
  role="listitem"
@@ -213,30 +225,47 @@ function EnvironmentCard({
213
225
  : "border-border-muted hover:border-border",
214
226
  )}
215
227
  >
216
- {/* Header — always visible */}
217
- <button
218
- type="button"
219
- onClick={onToggle}
220
- aria-expanded={isExpanded}
221
- className="flex w-full items-center gap-3 px-3 py-2.5 text-left"
222
- >
223
- <ChevronIcon expanded={isExpanded} />
228
+ {/* Header — always visible. The expand toggle and the visibility
229
+ control are sibling interactive elements (nesting the selector
230
+ inside the toggle button would be invalid markup). */}
231
+ <div className="flex w-full items-center gap-3 px-3 py-2.5">
232
+ <button
233
+ type="button"
234
+ onClick={onToggle}
235
+ aria-expanded={isExpanded}
236
+ className="flex min-w-0 flex-1 items-center gap-3 text-left"
237
+ >
238
+ <ChevronIcon expanded={isExpanded} />
224
239
 
225
- <div className="min-w-0 flex-1">
226
- <span className="block truncate text-sm font-medium text-foreground">
227
- {name}
228
- </span>
229
- {description && (
230
- <span className="block truncate text-xs text-muted-foreground">
231
- {description}
240
+ <div className="min-w-0 flex-1">
241
+ <span className="block truncate text-sm font-medium text-foreground">
242
+ {name}
232
243
  </span>
233
- )}
234
- </div>
244
+ {description && (
245
+ <span className="block truncate text-xs text-muted-foreground">
246
+ {description}
247
+ </span>
248
+ )}
249
+ </div>
250
+ </button>
251
+
252
+ {showVisibilityControl && (
253
+ <ResourceVisibilityControl
254
+ kind="environment"
255
+ resourceId={environmentId}
256
+ visibility={
257
+ environment.metadata?.visibility ??
258
+ ApiResourceVisibility.visibility_private
259
+ }
260
+ onChanged={onVisibilityChanged}
261
+ className="shrink-0"
262
+ />
263
+ )}
235
264
 
236
265
  <span className="shrink-0 text-xs text-muted-foreground">
237
266
  {variableCount} {variableCount === 1 ? "variable" : "variables"}
238
267
  </span>
239
- </button>
268
+ </div>
240
269
 
241
270
  {/* Expanded content — variable editor */}
242
271
  {isExpanded && environmentId && (
@@ -12,6 +12,7 @@ import { getUserMessage, type EnvVarInput } from "@stigmer/sdk";
12
12
  import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
13
13
  import { useStigmer } from "../hooks.js";
14
14
  import { toError } from "../internal/toError.js";
15
+ import { PermissionGate } from "../iam-policy/PermissionGate.js";
15
16
  import { useUpdateEnvironmentVariables } from "./useUpdateEnvironmentVariables.js";
16
17
  import { useRemoveEnvironmentVariables } from "./useRemoveEnvironmentVariables.js";
17
18
  import { useRevealSecretValue } from "./useRevealSecretValue.js";
@@ -438,24 +439,33 @@ function VariableRow({
438
439
  !readOnly && (
439
440
  <div className="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
440
441
  {variable.isSecret && (
441
- <ActionButton
442
- onClick={toggleReveal}
443
- disabled={isRevealing}
444
- label={
445
- revealedValue !== null
446
- ? `Hide ${variable.key}`
447
- : `Reveal ${variable.key}`
448
- }
449
- variant="muted"
442
+ /* Secret reveal is creator-only (can_read_secrets) at every
443
+ visibility level — org sharing never widens it. Hide the
444
+ affordance for everyone else instead of offering a button
445
+ that is guaranteed to fail (error prevention). */
446
+ <PermissionGate
447
+ resource={{ kind: "environment", id: environmentId }}
448
+ relation="can_read_secrets"
450
449
  >
451
- {isRevealing ? (
452
- <SpinnerIcon />
453
- ) : revealedValue !== null ? (
454
- <EyeOffIcon />
455
- ) : (
456
- <EyeIcon />
457
- )}
458
- </ActionButton>
450
+ <ActionButton
451
+ onClick={toggleReveal}
452
+ disabled={isRevealing}
453
+ label={
454
+ revealedValue !== null
455
+ ? `Hide ${variable.key}`
456
+ : `Reveal ${variable.key}`
457
+ }
458
+ variant="muted"
459
+ >
460
+ {isRevealing ? (
461
+ <SpinnerIcon />
462
+ ) : revealedValue !== null ? (
463
+ <EyeOffIcon />
464
+ ) : (
465
+ <EyeIcon />
466
+ )}
467
+ </ActionButton>
468
+ </PermissionGate>
459
469
  )}
460
470
  <ActionButton
461
471
  onClick={startEdit}
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { create } from "@bufbuild/protobuf";
3
+ import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
4
+ import { isShareRestrictedEnvironment } from "../shareRestriction";
5
+
6
+ function env(labels: Record<string, string>) {
7
+ return create(EnvironmentSchema, {
8
+ metadata: { id: "env_1", org: "acme", labels },
9
+ });
10
+ }
11
+
12
+ describe("isShareRestrictedEnvironment", () => {
13
+ it("restricts personal environments", () => {
14
+ expect(
15
+ isShareRestrictedEnvironment(env({ "stigmer.ai/personal": "true" })),
16
+ ).toBe(true);
17
+ });
18
+
19
+ it("restricts OAuth-managed environments", () => {
20
+ expect(
21
+ isShareRestrictedEnvironment(env({ "stigmer.ai/managed": "true" })),
22
+ ).toBe(true);
23
+ });
24
+
25
+ it("does not restrict ordinary environments", () => {
26
+ expect(isShareRestrictedEnvironment(env({}))).toBe(false);
27
+ expect(isShareRestrictedEnvironment(env({ team: "payments" }))).toBe(false);
28
+ });
29
+
30
+ it("only the literal 'true' value restricts", () => {
31
+ expect(
32
+ isShareRestrictedEnvironment(env({ "stigmer.ai/personal": "false" })),
33
+ ).toBe(false);
34
+ });
35
+ });
@@ -50,3 +50,8 @@ export {
50
50
  } from "./systemEnvVars.js";
51
51
  export { EnvironmentPicker } from "./EnvironmentPicker.js";
52
52
  export type { EnvironmentPickerProps } from "./EnvironmentPicker.js";
53
+ export {
54
+ PERSONAL_ENV_LABEL,
55
+ MANAGED_ENV_LABEL,
56
+ isShareRestrictedEnvironment,
57
+ } from "./shareRestriction.js";
@@ -0,0 +1,22 @@
1
+ import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
2
+
3
+ /** Label marking a user's personal credential-fallback environment. */
4
+ export const PERSONAL_ENV_LABEL = "stigmer.ai/personal";
5
+
6
+ /** Label marking a system-managed environment holding per-user OAuth tokens. */
7
+ export const MANAGED_ENV_LABEL = "stigmer.ai/managed";
8
+
9
+ /**
10
+ * Whether an environment is share-restricted — personal and OAuth-managed
11
+ * environments must never carry org visibility, so surfaces hide the
12
+ * sharing control for them entirely (error prevention: the backend
13
+ * rejects the transition with FAILED_PRECONDITION in both editions).
14
+ */
15
+ export function isShareRestrictedEnvironment(
16
+ environment: Environment,
17
+ ): boolean {
18
+ const labels = environment.metadata?.labels ?? {};
19
+ return (
20
+ labels[PERSONAL_ENV_LABEL] === "true" || labels[MANAGED_ENV_LABEL] === "true"
21
+ );
22
+ }
@@ -454,7 +454,7 @@ const REVIEW_BADGE: Record<FileReviewRowState, ApprovalBadgeInfo> = {
454
454
  // Status mapping
455
455
  // ---------------------------------------------------------------------------
456
456
 
457
- type ItemStatus = "running" | "waiting" | "failed" | "completed" | "pending";
457
+ type ItemStatus = "running" | "waiting" | "failed" | "completed" | "pending" | "interrupted";
458
458
 
459
459
  function mapToolCallStatus(toolCall: ToolCall): ItemStatus {
460
460
  switch (toolCall.status) {
@@ -474,6 +474,12 @@ function mapToolCallStatus(toolCall: ToolCall): ItemStatus {
474
474
  case ToolCallStatus.TOOL_CALL_COMPLETED:
475
475
  case ToolCallStatus.TOOL_CALL_SKIPPED:
476
476
  return "completed";
477
+ // Platform-settled when the execution terminalized mid-call (issue #207).
478
+ // A settled, neutral state: not a spinner (nothing is running), not a
479
+ // failure (the tool never errored), not a grey pending dot (it will never
480
+ // start).
481
+ case ToolCallStatus.TOOL_CALL_INTERRUPTED:
482
+ return "interrupted";
477
483
  default:
478
484
  return "pending";
479
485
  }
@@ -485,6 +491,7 @@ const STATUS_COLOR: Record<ItemStatus, string> = {
485
491
  failed: "text-destructive",
486
492
  completed: "text-success",
487
493
  pending: "text-muted-foreground",
494
+ interrupted: "text-muted-foreground",
488
495
  };
489
496
 
490
497
  // ---------------------------------------------------------------------------
@@ -626,6 +633,7 @@ const STATUS_ICON: Record<ItemStatus, () => React.JSX.Element> = {
626
633
  failed: XCircleIcon,
627
634
  completed: CheckCircleIcon,
628
635
  pending: DotIcon,
636
+ interrupted: SlashCircleIcon,
629
637
  };
630
638
 
631
639
  function SpinnerIcon() {
@@ -671,6 +679,16 @@ function DotIcon() {
671
679
  );
672
680
  }
673
681
 
682
+ /** Slashed circle for an interrupted call — settled, neutral, visibly "cut short". */
683
+ function SlashCircleIcon() {
684
+ return (
685
+ <svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
686
+ <circle cx="6" cy="6" r="4.5" />
687
+ <path d="M2.8 9.2L9.2 2.8" />
688
+ </svg>
689
+ );
690
+ }
691
+
674
692
  function InspectIcon() {
675
693
  return (
676
694
  <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
@@ -202,6 +202,10 @@ function deriveAggregateStatus(toolCalls: readonly ToolCall[]): AggregateStatus
202
202
  break;
203
203
  case ToolCallStatus.TOOL_CALL_COMPLETED:
204
204
  case ToolCallStatus.TOOL_CALL_SKIPPED:
205
+ // INTERRUPTED is terminal (platform-settled when the execution
206
+ // terminalized mid-call, issue #207): the chip must settle, not pin to
207
+ // "pending" forever.
208
+ case ToolCallStatus.TOOL_CALL_INTERRUPTED:
205
209
  break;
206
210
  default:
207
211
  allTerminal = false;