@stigmer/react 3.40.1 → 3.41.0
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/README.md +1 -0
- package/access/ManageAccessDialog.d.ts +1 -1
- package/access/ManageAccessDialog.js +1 -1
- package/access/types.d.ts +1 -1
- package/billing/ChangePlanDialog.d.ts.map +1 -1
- package/billing/ChangePlanDialog.js +13 -1
- package/billing/ChangePlanDialog.js.map +1 -1
- package/billing/ProviderKeysSection.d.ts +33 -0
- package/billing/ProviderKeysSection.d.ts.map +1 -0
- package/billing/ProviderKeysSection.js +143 -0
- package/billing/ProviderKeysSection.js.map +1 -0
- package/billing/UpgradeNotice.d.ts.map +1 -1
- package/billing/UpgradeNotice.js +1 -0
- package/billing/UpgradeNotice.js.map +1 -1
- package/billing/index.d.ts +4 -0
- package/billing/index.d.ts.map +1 -1
- package/billing/index.js +2 -0
- package/billing/index.js.map +1 -1
- package/billing/plan-features.d.ts +3 -2
- package/billing/plan-features.d.ts.map +1 -1
- package/billing/plan-features.js +4 -3
- package/billing/plan-features.js.map +1 -1
- package/billing/useProviderKeys.d.ts +67 -0
- package/billing/useProviderKeys.d.ts.map +1 -0
- package/billing/useProviderKeys.js +61 -0
- package/billing/useProviderKeys.js.map +1 -0
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +11 -9
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/connect/ServingAppSection.js +1 -1
- package/channel/connect/ServingAppSection.js.map +1 -1
- package/channel-app/ChannelAppDetailPanel.d.ts +2 -2
- package/channel-app/ChannelAppDetailPanel.js +17 -10
- package/channel-app/ChannelAppDetailPanel.js.map +1 -1
- package/channel-app/ChannelAppListPanel.d.ts +2 -2
- package/channel-app/CreateChannelAppForm.js +1 -1
- package/channel-app/CreateChannelAppForm.js.map +1 -1
- package/channel-app/copy.d.ts +3 -3
- package/channel-app/copy.js +3 -3
- package/channel-app/internal.d.ts +9 -0
- package/channel-app/internal.d.ts.map +1 -1
- package/channel-app/internal.js +9 -0
- package/channel-app/internal.js.map +1 -1
- package/channel-app/slackAppSetup.d.ts +3 -1
- package/channel-app/slackAppSetup.d.ts.map +1 -1
- package/channel-app/slackAppSetup.js +3 -1
- package/channel-app/slackAppSetup.js.map +1 -1
- package/channel-app/whatsappAppSetup.d.ts +3 -1
- package/channel-app/whatsappAppSetup.d.ts.map +1 -1
- package/channel-app/whatsappAppSetup.js +3 -1
- package/channel-app/whatsappAppSetup.js.map +1 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +5 -9
- package/composer/SessionComposer.js.map +1 -1
- package/environment/EnvironmentVariableEditor.js +1 -1
- package/environment/EnvironmentVariableEditor.js.map +1 -1
- package/environment/index.d.ts +1 -1
- package/environment/index.d.ts.map +1 -1
- package/environment/index.js +1 -1
- package/environment/index.js.map +1 -1
- package/environment/systemEnvVars.d.ts +4 -63
- package/environment/systemEnvVars.d.ts.map +1 -1
- package/environment/systemEnvVars.js +21 -113
- package/environment/systemEnvVars.js.map +1 -1
- package/execution/RecalledMemoriesCard.js +1 -1
- package/iam-policy/OrgMembersPanel.js +1 -1
- package/iam-policy/OrgMembersPanel.js.map +1 -1
- package/iam-policy/useCheckPermission.d.ts +4 -1
- package/iam-policy/useCheckPermission.d.ts.map +1 -1
- package/iam-policy/useCheckPermission.js +4 -1
- package/iam-policy/useCheckPermission.js.map +1 -1
- package/iam-policy/useGrantableRoles.d.ts +1 -1
- package/iam-policy/useGrantableRoles.js +1 -1
- package/iam-policy/useRevokeOrgAccess.d.ts +10 -8
- package/iam-policy/useRevokeOrgAccess.d.ts.map +1 -1
- package/iam-policy/useRevokeOrgAccess.js +4 -4
- package/iam-policy/useRevokeOrgAccess.js.map +1 -1
- package/identity-account/AccountPreferencesPanel.d.ts +6 -0
- package/identity-account/AccountPreferencesPanel.d.ts.map +1 -1
- package/identity-account/AccountPreferencesPanel.js +33 -2
- package/identity-account/AccountPreferencesPanel.js.map +1 -1
- package/identity-account/useAccountExecutionDefaults.d.ts +1 -1
- package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -1
- package/identity-account/useAccountExecutionDefaults.js +2 -3
- package/identity-account/useAccountExecutionDefaults.js.map +1 -1
- package/index.d.ts +3 -3
- package/index.d.ts.map +1 -1
- package/index.js +3 -3
- package/index.js.map +1 -1
- package/internal/UnknownPublicAddressNotice.d.ts +20 -0
- package/internal/UnknownPublicAddressNotice.d.ts.map +1 -0
- package/internal/UnknownPublicAddressNotice.js +19 -0
- package/internal/UnknownPublicAddressNotice.js.map +1 -0
- package/internal/features.js +2 -2
- package/internal/features.js.map +1 -1
- package/library/InstanceVisibilitySelector.d.ts +1 -1
- package/library/InstanceVisibilitySelector.d.ts.map +1 -1
- package/library/ResourceVisibilityControl.d.ts +6 -3
- package/library/ResourceVisibilityControl.d.ts.map +1 -1
- package/library/ResourceVisibilityControl.js +7 -4
- package/library/ResourceVisibilityControl.js.map +1 -1
- package/library/VisibilitySelector.d.ts +2 -2
- package/library/VisibilitySelector.d.ts.map +1 -1
- package/library/VisibilitySelector.js +1 -1
- package/licenses/LicenseIssueForm.js +1 -1
- package/licenses/LicenseIssueForm.js.map +1 -1
- package/licenses/license-format.d.ts +7 -5
- package/licenses/license-format.d.ts.map +1 -1
- package/licenses/license-format.js +1 -3
- package/licenses/license-format.js.map +1 -1
- package/mcp-server/McpServerConnectDialog.js +4 -5
- package/mcp-server/McpServerConnectDialog.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +3 -6
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +2 -6
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/McpToolSelector.js +1 -1
- package/mcp-server/McpToolSelector.js.map +1 -1
- package/mcp-server/useMcpServerConnect.d.ts +14 -25
- package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerConnect.js +7 -19
- package/mcp-server/useMcpServerConnect.js.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.d.ts +1 -3
- package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.js +6 -19
- package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
- package/models/HarnessSelector.d.ts.map +1 -1
- package/models/HarnessSelector.js +1 -2
- package/models/HarnessSelector.js.map +1 -1
- package/models/ModelSelector.d.ts +1 -1
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +1 -7
- package/models/ModelSelector.js.map +1 -1
- package/models/harness.d.ts +10 -6
- package/models/harness.d.ts.map +1 -1
- package/models/harness.js +35 -28
- package/models/harness.js.map +1 -1
- package/models/index.d.ts +1 -1
- package/models/index.d.ts.map +1 -1
- package/models/index.js +1 -1
- package/models/index.js.map +1 -1
- package/organization/OrgPreferencesPanel.d.ts.map +1 -1
- package/organization/OrgPreferencesPanel.js +13 -1
- package/organization/OrgPreferencesPanel.js.map +1 -1
- package/package.json +5 -5
- package/plugin/useMcpServerReadiness.js +2 -2
- package/plugin/useMcpServerReadiness.js.map +1 -1
- package/provider-standing/ProviderStandingConsole.js +1 -1
- package/provider.d.ts +27 -1
- package/provider.d.ts.map +1 -1
- package/provider.js +3 -2
- package/provider.js.map +1 -1
- package/public-base-url-context.d.ts +35 -0
- package/public-base-url-context.d.ts.map +1 -0
- package/public-base-url-context.js +62 -0
- package/public-base-url-context.js.map +1 -0
- package/resource-workbench/components/SelectionCheckbox.js +1 -1
- package/resource-workbench/components/SelectionCheckbox.js.map +1 -1
- package/session/useLocalSessionWorker.d.ts +19 -3
- package/session/useLocalSessionWorker.d.ts.map +1 -1
- package/session/useLocalSessionWorker.js +37 -4
- package/session/useLocalSessionWorker.js.map +1 -1
- package/settings/ChannelAppsSection.d.ts +4 -4
- package/settings/ChannelAppsSection.js +4 -4
- package/settings/ProviderKeysSection.d.ts +3 -0
- package/settings/ProviderKeysSection.d.ts.map +1 -0
- package/settings/ProviderKeysSection.js +3 -0
- package/settings/ProviderKeysSection.js.map +1 -0
- package/settings/index.d.ts +2 -0
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +1 -0
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.d.ts.map +1 -1
- package/settings/settings-nav.js +2 -1
- package/settings/settings-nav.js.map +1 -1
- package/sharing/ShareAgentDialog.d.ts.map +1 -1
- package/sharing/ShareAgentDialog.js +12 -8
- package/sharing/ShareAgentDialog.js.map +1 -1
- package/sharing/useCanCreateAgentShare.d.ts +3 -1
- package/sharing/useCanCreateAgentShare.d.ts.map +1 -1
- package/sharing/useCanCreateAgentShare.js +5 -3
- package/sharing/useCanCreateAgentShare.js.map +1 -1
- package/sharing/useSaveAgentShare.d.ts +1 -1
- package/sharing/useSaveAgentShare.js +1 -1
- package/src/access/ManageAccessDialog.tsx +1 -1
- package/src/access/types.ts +1 -1
- package/src/billing/ChangePlanDialog.tsx +14 -2
- package/src/billing/ProviderKeysSection.tsx +324 -0
- package/src/billing/UpgradeNotice.tsx +1 -0
- package/src/billing/index.ts +9 -0
- package/src/billing/plan-features.ts +4 -3
- package/src/billing/useProviderKeys.ts +129 -0
- package/src/channel/AgentChannelsPanel.tsx +11 -9
- package/src/channel/connect/ServingAppSection.tsx +1 -1
- package/src/channel-app/ChannelAppDetailPanel.tsx +37 -20
- package/src/channel-app/ChannelAppListPanel.tsx +2 -2
- package/src/channel-app/CreateChannelAppForm.tsx +0 -1
- package/src/channel-app/copy.ts +3 -3
- package/src/channel-app/internal.tsx +21 -0
- package/src/channel-app/slackAppSetup.ts +3 -1
- package/src/channel-app/whatsappAppSetup.ts +3 -1
- package/src/composer/SessionComposer.tsx +5 -14
- package/src/environment/EnvironmentVariableEditor.tsx +1 -1
- package/src/environment/index.ts +1 -7
- package/src/environment/systemEnvVars.ts +21 -133
- package/src/execution/RecalledMemoriesCard.tsx +1 -1
- package/src/iam-policy/OrgMembersPanel.tsx +3 -1
- package/src/iam-policy/useCheckPermission.ts +4 -1
- package/src/iam-policy/useGrantableRoles.ts +1 -1
- package/src/iam-policy/useRevokeOrgAccess.ts +10 -8
- package/src/identity-account/AccountPreferencesPanel.tsx +56 -9
- package/src/identity-account/useAccountExecutionDefaults.ts +2 -5
- package/src/index.ts +9 -5
- package/src/internal/UnknownPublicAddressNotice.tsx +31 -0
- package/src/internal/features.ts +2 -2
- package/src/library/InstanceVisibilitySelector.tsx +1 -1
- package/src/library/ResourceVisibilityControl.tsx +7 -4
- package/src/library/VisibilitySelector.tsx +2 -2
- package/src/licenses/LicenseIssueForm.tsx +1 -1
- package/src/licenses/license-format.ts +11 -7
- package/src/mcp-server/McpServerConnectDialog.tsx +4 -6
- package/src/mcp-server/McpServerDetailView.tsx +3 -6
- package/src/mcp-server/McpServerPicker.tsx +2 -12
- package/src/mcp-server/McpToolSelector.tsx +1 -1
- package/src/mcp-server/useMcpServerConnect.ts +14 -33
- package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -23
- package/src/models/HarnessSelector.tsx +1 -3
- package/src/models/ModelSelector.tsx +2 -7
- package/src/models/harness.ts +41 -39
- package/src/models/index.ts +1 -1
- package/src/organization/OrgPreferencesPanel.tsx +19 -1
- package/src/plugin/useMcpServerReadiness.ts +2 -2
- package/src/provider-standing/ProviderStandingConsole.tsx +1 -1
- package/src/provider.tsx +56 -26
- package/src/public-base-url-context.ts +70 -0
- package/src/resource-workbench/components/SelectionCheckbox.tsx +1 -1
- package/src/session/useLocalSessionWorker.ts +35 -4
- package/src/settings/ChannelAppsSection.tsx +4 -4
- package/src/settings/ProviderKeysSection.tsx +4 -0
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-nav.ts +2 -0
- package/src/sharing/ShareAgentDialog.tsx +30 -14
- package/src/sharing/useCanCreateAgentShare.ts +6 -4
- package/src/sharing/useSaveAgentShare.ts +1 -1
- package/src/styles.css +14 -4
- package/src/workflow/WorkflowTaskApprovalSummary.tsx +14 -7
- package/src/workflow/inspector/tabs/BranchesTab.tsx +2 -2
- package/src/workflow/inspector/tabs/CatchTab.tsx +1 -1
- package/src/workflow/inspector/tabs/IterationTab.tsx +1 -1
- package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +7 -6
- package/src/workflow/task-detail/task-approval.ts +20 -13
- package/styles.css +1 -1
- package/workflow/WorkflowTaskApprovalSummary.d.ts +7 -4
- package/workflow/WorkflowTaskApprovalSummary.d.ts.map +1 -1
- package/workflow/WorkflowTaskApprovalSummary.js +8 -3
- package/workflow/WorkflowTaskApprovalSummary.js.map +1 -1
- package/workflow/inspector/tabs/BranchesTab.js +1 -1
- package/workflow/inspector/tabs/BranchesTab.js.map +1 -1
- package/workflow/inspector/tabs/CatchTab.js +1 -1
- package/workflow/inspector/tabs/CatchTab.js.map +1 -1
- package/workflow/inspector/tabs/IterationTab.js +1 -1
- package/workflow/inspector/tabs/IterationTab.js.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.js +6 -5
- package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
- package/workflow/task-detail/task-approval.d.ts +13 -11
- package/workflow/task-detail/task-approval.d.ts.map +1 -1
- package/workflow/task-detail/task-approval.js +8 -4
- package/workflow/task-detail/task-approval.js.map +1 -1
package/src/provider.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { StigmerContext } from "./context.js";
|
|
|
15
15
|
import { DeploymentModeContext } from "./deployment-mode.js";
|
|
16
16
|
import { ExecutionTargetContext } from "./execution-target-context.js";
|
|
17
17
|
import { ApprovalDefaultsContext } from "./approval-defaults-context.js";
|
|
18
|
+
import { PublicBaseUrlContext } from "./public-base-url-context.js";
|
|
18
19
|
import type { ApprovalDefaults } from "./approval-defaults-context.js";
|
|
19
20
|
import type { ExecutionTargetOption } from "./session/execution-target.js";
|
|
20
21
|
import type { RunnerAdapter } from "./runner-adapter.js";
|
|
@@ -121,6 +122,32 @@ export interface StigmerProviderProps {
|
|
|
121
122
|
* ```
|
|
122
123
|
*/
|
|
123
124
|
readonly approvalDefaults?: ApprovalDefaults;
|
|
125
|
+
/**
|
|
126
|
+
* The address the Stigmer API is reached at from outside this page, for
|
|
127
|
+
* the URLs the SDK shows a user to copy (the Slack and WhatsApp webhook
|
|
128
|
+
* URLs a channel app registers, and the platform client snippet in the
|
|
129
|
+
* Share dialog). A session's MCP servers get their
|
|
130
|
+
* `STIGMER_SERVER_ADDRESS` from the platform, never from this value.
|
|
131
|
+
*
|
|
132
|
+
* Omit it when the client's `baseUrl` is absolute: that address is used,
|
|
133
|
+
* exactly as before. Pass it when the client talks to the server through
|
|
134
|
+
* a relative `baseUrl` (a same-origin proxy), because the page's own
|
|
135
|
+
* origin need not be where Slack, Meta, a backend or a remote MCP server
|
|
136
|
+
* can reach the API. Without it, the copy surfaces say they do not know
|
|
137
|
+
* the server's public address and offer nothing to copy. Only an
|
|
138
|
+
* absolute `http(s)` URL is used.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* // The browser reaches the API through the page's own origin
|
|
143
|
+
* const client = new Stigmer({ baseUrl: "/", getAccessToken });
|
|
144
|
+
*
|
|
145
|
+
* <StigmerProvider client={client} publicBaseUrl="https://api.example.com">
|
|
146
|
+
* <App />
|
|
147
|
+
* </StigmerProvider>
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
readonly publicBaseUrl?: string;
|
|
124
151
|
/**
|
|
125
152
|
* Built-in theme preset to apply.
|
|
126
153
|
*
|
|
@@ -288,6 +315,7 @@ export function StigmerProvider({
|
|
|
288
315
|
executionTarget,
|
|
289
316
|
runnerAdapter,
|
|
290
317
|
approvalDefaults,
|
|
318
|
+
publicBaseUrl,
|
|
291
319
|
preset,
|
|
292
320
|
className,
|
|
293
321
|
colorMode = "light",
|
|
@@ -310,32 +338,34 @@ export function StigmerProvider({
|
|
|
310
338
|
<ExecutionTargetContext.Provider value={executionTarget}>
|
|
311
339
|
<ApprovalDefaultsContext.Provider value={approvalDefaults}>
|
|
312
340
|
<RunnerAdapterContext.Provider value={runnerAdapter ?? null}>
|
|
313
|
-
<
|
|
314
|
-
<
|
|
315
|
-
<
|
|
316
|
-
<
|
|
317
|
-
<
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
341
|
+
<PublicBaseUrlContext.Provider value={publicBaseUrl}>
|
|
342
|
+
<ColorModeContext.Provider value={resolvedMode}>
|
|
343
|
+
<ModelRegistryContext.Provider value={registryState}>
|
|
344
|
+
<TaskKindRegistryContext.Provider value={taskKindRegistryState}>
|
|
345
|
+
<ReviewRendererContext.Provider value={reviewRenderers ?? EMPTY_REVIEW_RENDERERS}>
|
|
346
|
+
<PortalContainerContext.Provider value={portalContainer}>
|
|
347
|
+
{/*
|
|
348
|
+
The in-tree scoping container. `data-stgm-root` is a
|
|
349
|
+
stable public selector — the documented seam for hosts
|
|
350
|
+
to style THIS element only (e.g. the fixed-height
|
|
351
|
+
embedding recipe, #260). It must never be mirrored
|
|
352
|
+
onto the portal container, whose own `data-stgm-portal`
|
|
353
|
+
marker keeps host in-tree-only CSS away from the
|
|
354
|
+
off-tree element.
|
|
355
|
+
*/}
|
|
356
|
+
<div
|
|
357
|
+
className={scope.className}
|
|
358
|
+
data-stgm-root=""
|
|
359
|
+
data-stgm-color-mode={scope.colorMode}
|
|
360
|
+
>
|
|
361
|
+
{children}
|
|
362
|
+
</div>
|
|
363
|
+
</PortalContainerContext.Provider>
|
|
364
|
+
</ReviewRendererContext.Provider>
|
|
365
|
+
</TaskKindRegistryContext.Provider>
|
|
366
|
+
</ModelRegistryContext.Provider>
|
|
367
|
+
</ColorModeContext.Provider>
|
|
368
|
+
</PublicBaseUrlContext.Provider>
|
|
339
369
|
</RunnerAdapterContext.Provider>
|
|
340
370
|
</ApprovalDefaultsContext.Provider>
|
|
341
371
|
</ExecutionTargetContext.Provider>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
import { useStigmer } from "./hooks.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React context for the server's public base URL: the address the Stigmer
|
|
8
|
+
* API is reached at from outside the page, supplied by the host through
|
|
9
|
+
* `StigmerProvider`'s `publicBaseUrl`.
|
|
10
|
+
*
|
|
11
|
+
* Separated from the provider to mirror the `ExecutionTargetContext`
|
|
12
|
+
* pattern and avoid circular imports.
|
|
13
|
+
*
|
|
14
|
+
* `undefined` means the host named no public address; the client's own
|
|
15
|
+
* `baseUrl` then stands in when it is absolute.
|
|
16
|
+
*/
|
|
17
|
+
export const PublicBaseUrlContext = createContext<string | undefined>(
|
|
18
|
+
undefined,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The server's public base URL for URLs the SDK shows a user to copy (a
|
|
23
|
+
* webhook URL pasted into Slack or Meta, a platform client snippet run by
|
|
24
|
+
* a backend), or `null` when it cannot be known.
|
|
25
|
+
*
|
|
26
|
+
* The host owns public addresses, and the SDK never guesses one (the
|
|
27
|
+
* `buildShareUrl` precedent in `ShareAgentDialog`): the provider's
|
|
28
|
+
* `publicBaseUrl` wins, the client's `baseUrl` stands in for it, and only
|
|
29
|
+
* an absolute `http(s)` URL counts. A relative `baseUrl` (a same-origin
|
|
30
|
+
* proxy, the documentation tours) with no `publicBaseUrl` answers `null`,
|
|
31
|
+
* because the page's own origin need not be where the API is reached from
|
|
32
|
+
* outside; the surface then says the address is unknown instead of offering
|
|
33
|
+
* one nobody can call. A `publicBaseUrl` that is not absolute is a host
|
|
34
|
+
* mistake and counts for nothing. Trailing slashes are trimmed.
|
|
35
|
+
*/
|
|
36
|
+
export function usePublicBaseUrl(): string | null {
|
|
37
|
+
const hostPublicBaseUrl = useContext(PublicBaseUrlContext);
|
|
38
|
+
const { baseUrl } = useStigmer();
|
|
39
|
+
return resolvePublicBaseUrl(hostPublicBaseUrl, baseUrl);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The pure rule behind {@link usePublicBaseUrl}: the first of the host's
|
|
44
|
+
* public base URL and the client's base URL that is an absolute `http(s)` URL,
|
|
45
|
+
* trailing slashes trimmed, or `null` when neither is.
|
|
46
|
+
*/
|
|
47
|
+
export function resolvePublicBaseUrl(
|
|
48
|
+
hostPublicBaseUrl: string | undefined,
|
|
49
|
+
clientBaseUrl: string,
|
|
50
|
+
): string | null {
|
|
51
|
+
for (const candidate of [hostPublicBaseUrl, clientBaseUrl]) {
|
|
52
|
+
if (candidate !== undefined && isAbsoluteUrl(candidate)) {
|
|
53
|
+
return candidate.replace(/\/+$/, "");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Whether `value` is an absolute `http(s)` URL. The protocol check matters:
|
|
61
|
+
* `new URL("localhost:7234")` parses, with `localhost:` as its scheme.
|
|
62
|
+
*/
|
|
63
|
+
function isAbsoluteUrl(value: string): boolean {
|
|
64
|
+
try {
|
|
65
|
+
const { protocol } = new URL(value);
|
|
66
|
+
return protocol === "http:" || protocol === "https:";
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -43,7 +43,7 @@ export function SelectionCheckbox({
|
|
|
43
43
|
onChange={(e) => onChange(e.target.checked)}
|
|
44
44
|
aria-label={ariaLabel}
|
|
45
45
|
className={cn(
|
|
46
|
-
"stg:size-3.5 stg:shrink-0 stg:cursor-pointer stg:rounded stg:border stg:border-input
|
|
46
|
+
"stg:size-3.5 stg:shrink-0 stg:cursor-pointer stg:rounded stg:border stg:border-input",
|
|
47
47
|
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring stg:focus-visible:ring-offset-1",
|
|
48
48
|
className,
|
|
49
49
|
)}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useEffect, useMemo } from "react";
|
|
4
4
|
import type { Session } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
|
|
5
5
|
import { ExecutionTarget } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
6
|
+
import { useWhoAmI } from "../iam-policy/useWhoAmI.js";
|
|
6
7
|
import { useRunnerAdapter } from "../runner-adapter.js";
|
|
7
8
|
import { useExecutionTarget } from "../execution-target-context.js";
|
|
8
9
|
import { fromProtoExecutionTarget } from "./execution-target.js";
|
|
@@ -25,12 +26,28 @@ import { fromProtoExecutionTarget } from "./execution-target.js";
|
|
|
25
26
|
* free. It is a no-op unless a `runnerAdapter` is configured **and** the
|
|
26
27
|
* session resolves to local execution (cloud sessions are server-provisioned).
|
|
27
28
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* **Only the session's own person serves it.** Anyone a session is shared
|
|
30
|
+
* with can open it, and every desktop that attached would poll the same
|
|
31
|
+
* queue, each turn going to whichever worker asks first. A server with
|
|
32
|
+
* sign-in credentials a local session's runner for the person who created
|
|
33
|
+
* the session, so another viewer's desktop would take turns it cannot run
|
|
34
|
+
* (or, where it could, run someone else's turn on its own machine). The
|
|
35
|
+
* worker is therefore kept off exactly when the signed-in account is known
|
|
36
|
+
* and is not the session's creator — the creator stamp read the two ways
|
|
37
|
+
* the server reads it, as the account id and as the account's identity
|
|
38
|
+
* provider subject. Every other case attaches as before: while the account
|
|
39
|
+
* is loading the attach waits (as it waits for the session), and when there
|
|
40
|
+
* is no account (a single-user server answers `whoAmI` NOT_FOUND, and every
|
|
41
|
+
* session there is its operator's) the desktop serves the session.
|
|
42
|
+
*
|
|
43
|
+
* The effect depends only on stable primitives (`adapter`, `sessionId`, the
|
|
44
|
+
* derived `effectiveTarget` string and the derived `servesHere` flag) and
|
|
45
|
+
* **never on the `session` object**.
|
|
30
46
|
* `useSessionConversation` refetches the session frequently; keying the effect
|
|
31
47
|
* on the object would tear down and restart the worker on every refetch
|
|
32
48
|
* (DD-010 / reference-stability). `executionTarget` is immutable after the
|
|
33
|
-
* first execution,
|
|
49
|
+
* first execution, and a session's creator never changes, so both derived
|
|
50
|
+
* values stay stable across refetches.
|
|
34
51
|
*
|
|
35
52
|
* @param sessionId - The session being viewed, or `null` to skip.
|
|
36
53
|
* @param session - The loaded session, or `null` while loading. The worker is
|
|
@@ -43,6 +60,7 @@ export function useLocalSessionWorker(
|
|
|
43
60
|
): void {
|
|
44
61
|
const adapter = useRunnerAdapter();
|
|
45
62
|
const contextTarget = useExecutionTarget();
|
|
63
|
+
const { account, isLoading: accountLoading } = useWhoAmI();
|
|
46
64
|
|
|
47
65
|
// Resolve the session's effective execution target as a stable primitive.
|
|
48
66
|
// `undefined` until the session loads — so we never attach a local worker
|
|
@@ -59,8 +77,21 @@ export function useLocalSessionWorker(
|
|
|
59
77
|
);
|
|
60
78
|
}, [isLoaded, specTarget, contextTarget]);
|
|
61
79
|
|
|
80
|
+
// Whether this viewer's desktop may serve the session, as a stable
|
|
81
|
+
// primitive: `undefined` while the account is loading, `true` when there
|
|
82
|
+
// is no account to compare or the account is the session's creator.
|
|
83
|
+
const creatorStamp = session?.status?.audit?.specAudit?.createdBy?.id ?? "";
|
|
84
|
+
const accountId = account?.metadata?.id ?? "";
|
|
85
|
+
const accountSubject = account?.spec?.idpId ?? "";
|
|
86
|
+
const servesHere = useMemo(() => {
|
|
87
|
+
if (accountLoading) return undefined;
|
|
88
|
+
if (accountId === "" || creatorStamp === "") return true;
|
|
89
|
+
return creatorStamp === accountId || creatorStamp === accountSubject;
|
|
90
|
+
}, [accountLoading, accountId, accountSubject, creatorStamp]);
|
|
91
|
+
|
|
62
92
|
useEffect(() => {
|
|
63
93
|
if (!adapter || !sessionId || effectiveTarget !== "local") return;
|
|
94
|
+
if (servesHere !== true) return;
|
|
64
95
|
|
|
65
96
|
// Fire-and-forget: a runner-start failure must not crash the session view.
|
|
66
97
|
// The host adapter's add/remove are idempotent, so React's cleanup-driven
|
|
@@ -69,5 +100,5 @@ export function useLocalSessionWorker(
|
|
|
69
100
|
return () => {
|
|
70
101
|
adapter.onSessionClosed(sessionId).catch(() => {});
|
|
71
102
|
};
|
|
72
|
-
}, [adapter, sessionId, effectiveTarget]);
|
|
103
|
+
}, [adapter, sessionId, effectiveTarget, servesHere]);
|
|
73
104
|
}
|
|
@@ -41,10 +41,10 @@ type FlowState =
|
|
|
41
41
|
*
|
|
42
42
|
* "New channel app" is offered once the server confirms the caller holds
|
|
43
43
|
* `can_create_channel_app` on the organization. The list shows only the
|
|
44
|
-
* apps the caller may view (
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
44
|
+
* apps the caller may view (the organization's admins, and anyone granted
|
|
45
|
+
* viewer on an app), so a caller who may not create channel apps is told
|
|
46
|
+
* that the organization's admins manage them rather than that none is
|
|
47
|
+
* registered.
|
|
48
48
|
*/
|
|
49
49
|
export function ChannelAppsSection() {
|
|
50
50
|
const headingId = useId();
|
package/src/settings/index.ts
CHANGED
|
@@ -22,3 +22,5 @@ export { TeamsSection } from "./TeamsSection.js";
|
|
|
22
22
|
export { OAuthAppsSection } from "./OAuthAppsSection.js";
|
|
23
23
|
export { ChannelAppsSection } from "./ChannelAppsSection.js";
|
|
24
24
|
export { UsageSection } from "./UsageSection.js";
|
|
25
|
+
export { ProviderKeysSection } from "./ProviderKeysSection.js";
|
|
26
|
+
export type { ProviderKeysSectionProps } from "./ProviderKeysSection.js";
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
FileBadge,
|
|
11
11
|
Layers,
|
|
12
12
|
KeyRound,
|
|
13
|
+
KeySquare,
|
|
13
14
|
Link,
|
|
14
15
|
MessageSquare,
|
|
15
16
|
MousePointerClick,
|
|
@@ -102,6 +103,7 @@ export const SETTINGS_NAV_GROUPS: readonly SettingsNavGroup[] = [
|
|
|
102
103
|
items: [
|
|
103
104
|
{ href: "/settings/billing", label: "Billing", icon: CreditCard },
|
|
104
105
|
{ href: "/settings/usage", label: "Usage", icon: BarChart3 },
|
|
106
|
+
{ href: "/settings/provider-keys", label: "Provider keys", icon: KeySquare },
|
|
105
107
|
],
|
|
106
108
|
},
|
|
107
109
|
{
|
|
@@ -23,7 +23,8 @@ import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/ap
|
|
|
23
23
|
import { Switch } from "../switch/Switch.js";
|
|
24
24
|
import { Tabs, type TabItem } from "../tabs/Tabs.js";
|
|
25
25
|
import { toast } from "../feedback/toast.js";
|
|
26
|
-
import {
|
|
26
|
+
import { usePublicBaseUrl } from "../public-base-url-context.js";
|
|
27
|
+
import { UnknownPublicAddressNotice } from "../internal/UnknownPublicAddressNotice.js";
|
|
27
28
|
import { useCopyResource } from "../resource-detail/useCopyResource.js";
|
|
28
29
|
import { useDeploymentMode } from "../deployment-mode.js";
|
|
29
30
|
import { useBillingAccount } from "../billing/useBillingAccount.js";
|
|
@@ -1510,10 +1511,10 @@ function MessageField({
|
|
|
1510
1511
|
|
|
1511
1512
|
/**
|
|
1512
1513
|
* The platform client snippet, aimed at the server the console is connected
|
|
1513
|
-
* to
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
* string literal.
|
|
1514
|
+
* to — a self-hosted admin copies code for their own server, not Stigmer
|
|
1515
|
+
* Cloud. `apiBaseUrl` is the server's public base URL (`usePublicBaseUrl`),
|
|
1516
|
+
* the address the channel-app webhook URLs are built from too, JSON-quoted
|
|
1517
|
+
* so the line is always a valid string literal.
|
|
1517
1518
|
*/
|
|
1518
1519
|
function buildDeveloperSnippet(
|
|
1519
1520
|
apiBaseUrl: string,
|
|
@@ -1544,10 +1545,14 @@ function DeveloperTab({
|
|
|
1544
1545
|
readonly org: string;
|
|
1545
1546
|
readonly slug: string;
|
|
1546
1547
|
}) {
|
|
1547
|
-
const
|
|
1548
|
+
const publicBaseUrl = usePublicBaseUrl();
|
|
1549
|
+
// `null` while the server's public address is unknown: the field says so.
|
|
1548
1550
|
const snippet = useMemo(
|
|
1549
|
-
() =>
|
|
1550
|
-
|
|
1551
|
+
() =>
|
|
1552
|
+
publicBaseUrl === null
|
|
1553
|
+
? null
|
|
1554
|
+
: buildDeveloperSnippet(publicBaseUrl, org, slug),
|
|
1555
|
+
[publicBaseUrl, org, slug],
|
|
1551
1556
|
);
|
|
1552
1557
|
|
|
1553
1558
|
return (
|
|
@@ -1557,12 +1562,23 @@ function DeveloperTab({
|
|
|
1557
1562
|
integrate this agent with the Stigmer SDK — the same runtime, full
|
|
1558
1563
|
control.
|
|
1559
1564
|
</p>
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1565
|
+
{snippet !== null ? (
|
|
1566
|
+
<CopyField
|
|
1567
|
+
label="Platform client integration"
|
|
1568
|
+
value={snippet}
|
|
1569
|
+
copyLabel="Code"
|
|
1570
|
+
multiline
|
|
1571
|
+
/>
|
|
1572
|
+
) : (
|
|
1573
|
+
<section>
|
|
1574
|
+
<h3 className="stg:text-xs stg:font-medium stg:text-muted-foreground">
|
|
1575
|
+
Platform client integration
|
|
1576
|
+
</h3>
|
|
1577
|
+
<div className="stg:mt-1.5">
|
|
1578
|
+
<UnknownPublicAddressNotice subject="the integration code" />
|
|
1579
|
+
</div>
|
|
1580
|
+
</section>
|
|
1581
|
+
)}
|
|
1566
1582
|
<a
|
|
1567
1583
|
href={PLATFORM_CLIENT_DOCS_URL}
|
|
1568
1584
|
target="_blank"
|
|
@@ -18,7 +18,9 @@ export interface UseCanCreateAgentShareReturn {
|
|
|
18
18
|
* installed copy), and creating one asks two questions of that
|
|
19
19
|
* organization, both of which the create handler enforces:
|
|
20
20
|
*
|
|
21
|
-
* - `
|
|
21
|
+
* - `can_manage_audience` on the agent — a share puts the agent in front of a
|
|
22
|
+
* wider audience, so it is the owner's decision; an editor, who may change
|
|
23
|
+
* the definition, may not publish it.
|
|
22
24
|
* - `can_create_agent_share` on the organization (admin by default) — a
|
|
23
25
|
* share spends the organization's credits with anyone on the internet,
|
|
24
26
|
* so it is an admin-level decision. The org check uses the org slug as
|
|
@@ -46,14 +48,14 @@ export function useCanCreateAgentShare(
|
|
|
46
48
|
const agentId = agent?.metadata?.id ?? "";
|
|
47
49
|
const agentOrg = agent?.metadata?.org ?? "";
|
|
48
50
|
|
|
49
|
-
const { allowed:
|
|
51
|
+
const { allowed: canPublishAgent } = useCheckPermission(
|
|
50
52
|
agentId ? { kind: "agent", id: agentId } : null,
|
|
51
|
-
"
|
|
53
|
+
"can_manage_audience",
|
|
52
54
|
);
|
|
53
55
|
const { allowed: canCreateInOrg } = useCheckPermission(
|
|
54
56
|
agentOrg ? { kind: "organization", id: agentOrg } : null,
|
|
55
57
|
"can_create_agent_share",
|
|
56
58
|
);
|
|
57
59
|
|
|
58
|
-
return { allowed: !!agent &&
|
|
60
|
+
return { allowed: !!agent && canPublishAgent && canCreateInOrg };
|
|
59
61
|
}
|
|
@@ -162,7 +162,7 @@ export interface UseSaveAgentShareReturn {
|
|
|
162
162
|
*
|
|
163
163
|
* Commits via `stigmer.agentShare.apply()` — an idempotent upsert keyed
|
|
164
164
|
* on the share's `(org, slug)` identity — so one code path serves both
|
|
165
|
-
* creation (the server authorizes on the referenced agent's `
|
|
165
|
+
* creation (the server authorizes on the referenced agent's `can_manage_audience`
|
|
166
166
|
* and the organization's `can_create_agent_share`) and every later
|
|
167
167
|
* edit. There is deliberately no create/update
|
|
168
168
|
* branching: the share slug is unique per org, which makes
|
package/src/styles.css
CHANGED
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
self-referential `--stgm-font-sans: var(--stgm-font-sans)` cycle through
|
|
29
29
|
the `@theme inline` block below).
|
|
30
30
|
|
|
31
|
-
The `stg:` prefix contract is enforced by `scripts/
|
|
32
|
-
(
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
The `stg:` prefix contract is enforced by `scripts/prefix-classnames.ts
|
|
32
|
+
--check` (`npm run lint:prefix`: an unprefixed utility, or a prefix placed
|
|
33
|
+
after a variant, fails the lint) and by the dist isolation invariant test
|
|
34
|
+
(unscoped selectors in the built stylesheet fail the suite). The same
|
|
35
|
+
script, without `--check`, is the one-shot migration. */
|
|
35
36
|
@import "tailwindcss/theme.css" layer(stgm) prefix(stg);
|
|
36
37
|
@import "tailwindcss/utilities.css" layer(stgm) prefix(stg);
|
|
37
38
|
/* Enter/exit animation utilities (`stg:animate-in`, `stg:fade-in`,
|
|
@@ -294,6 +295,15 @@
|
|
|
294
295
|
color-scheme: dark;
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
/* The same holds for a checkbox's or radio's checked state: the browser
|
|
299
|
+
paints it from `accent-color` (its own blue by default), never from our
|
|
300
|
+
tokens (#1228). One inherited rule on both containers gives every native
|
|
301
|
+
control inside the scope the theme's primary, in both modes and every
|
|
302
|
+
preset, so no control needs its own accent class. */
|
|
303
|
+
.stgm {
|
|
304
|
+
accent-color: var(--stgm-primary);
|
|
305
|
+
}
|
|
306
|
+
|
|
297
307
|
/* The provider's in-tree container ([data-stgm-root]) ships with NO
|
|
298
308
|
layout styling — deliberately (#260, DD-019). A default
|
|
299
309
|
`height: 100%` looks benign ("resolves to auto under auto-height
|
|
@@ -31,10 +31,13 @@ export interface WorkflowTaskApprovalSummaryProps {
|
|
|
31
31
|
*/
|
|
32
32
|
readonly outcomes: readonly TaskOutcome[];
|
|
33
33
|
/**
|
|
34
|
-
* The recorded decision, sourced from the canonical task output
|
|
35
|
-
* `
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* The recorded decision, sourced from the canonical task output with the
|
|
35
|
+
* `approval_resolved` event standing in until the status snapshot
|
|
36
|
+
* catches up. When `null` — or when its `outcome` is still empty — the
|
|
37
|
+
* gate has been resolved but neither source carries the decision yet; a
|
|
38
|
+
* "finalizing" affordance is shown instead. The one exception is an
|
|
39
|
+
* auto-resolved decision with an empty outcome: the gate timed out under
|
|
40
|
+
* the fail policy and ended without a decision, which is final.
|
|
38
41
|
*/
|
|
39
42
|
readonly decision: TaskDetailApprovalDecision | null;
|
|
40
43
|
/** Additional CSS class names for the root container. */
|
|
@@ -73,13 +76,17 @@ export const WorkflowTaskApprovalSummary = memo(function WorkflowTaskApprovalSum
|
|
|
73
76
|
decision,
|
|
74
77
|
className,
|
|
75
78
|
}: WorkflowTaskApprovalSummaryProps) {
|
|
76
|
-
|
|
79
|
+
// A fail-policy timeout ends the gate with no outcome; that is the record,
|
|
80
|
+
// not a decision still on its way.
|
|
81
|
+
const timedOutUndecided = decision !== null && decision.autoResolved && decision.outcome === "";
|
|
82
|
+
const isFinalizing = !decision || (decision.outcome === "" && !timedOutUndecided);
|
|
77
83
|
|
|
78
84
|
const outcomeLabel = useMemo(() => {
|
|
85
|
+
if (timedOutUndecided) return "Timed out — no decision";
|
|
79
86
|
if (!decision || !decision.outcome) return "";
|
|
80
87
|
const match = outcomes.find((o) => o.name === decision.outcome);
|
|
81
88
|
return match?.label || capitalize(decision.outcome.replace(/_/g, " "));
|
|
82
|
-
}, [decision, outcomes]);
|
|
89
|
+
}, [decision, outcomes, timedOutUndecided]);
|
|
83
90
|
|
|
84
91
|
const tone: DecisionTone = useMemo(
|
|
85
92
|
() => (decision && !isFinalizing ? resolveTone(decision.outcome) : "neutral"),
|
|
@@ -116,7 +123,7 @@ export const WorkflowTaskApprovalSummary = memo(function WorkflowTaskApprovalSum
|
|
|
116
123
|
<ToneIcon tone={tone} />
|
|
117
124
|
{outcomeLabel}
|
|
118
125
|
</span>
|
|
119
|
-
{decision!.autoResolved && (
|
|
126
|
+
{decision!.autoResolved && !timedOutUndecided && (
|
|
120
127
|
<span className="stg:rounded stg:bg-muted stg:px-1.5 stg:py-0.5 stg:text-[10px] stg:font-medium stg:text-muted-foreground">
|
|
121
128
|
auto-resolved
|
|
122
129
|
</span>
|
|
@@ -295,7 +295,7 @@ function ForkBranches({
|
|
|
295
295
|
name={`join-policy-${node.id}`}
|
|
296
296
|
checked={!compete}
|
|
297
297
|
onChange={() => handleSetCompete(false)}
|
|
298
|
-
className="stg:h-3 stg:w-3
|
|
298
|
+
className="stg:h-3 stg:w-3"
|
|
299
299
|
/>
|
|
300
300
|
Wait for all branches
|
|
301
301
|
</label>
|
|
@@ -305,7 +305,7 @@ function ForkBranches({
|
|
|
305
305
|
name={`join-policy-${node.id}`}
|
|
306
306
|
checked={compete}
|
|
307
307
|
onChange={() => handleSetCompete(true)}
|
|
308
|
-
className="stg:h-3 stg:w-3
|
|
308
|
+
className="stg:h-3 stg:w-3"
|
|
309
309
|
/>
|
|
310
310
|
Race mode (first branch wins)
|
|
311
311
|
</label>
|
|
@@ -135,7 +135,7 @@ export const CatchTab = memo(function CatchTab({ node, mutations }: CatchTabProp
|
|
|
135
135
|
type="checkbox"
|
|
136
136
|
checked={catchBlock.compensate}
|
|
137
137
|
onChange={handleCompensateToggle}
|
|
138
|
-
className="stg:h-3 stg:w-3
|
|
138
|
+
className="stg:h-3 stg:w-3"
|
|
139
139
|
/>
|
|
140
140
|
Run compensation before catch block
|
|
141
141
|
</label>
|
|
@@ -194,7 +194,7 @@ export const IterationTab = memo(function IterationTab({
|
|
|
194
194
|
value={opt.value}
|
|
195
195
|
checked={onError === opt.value}
|
|
196
196
|
onChange={() => handleErrorPolicyChange(opt.value)}
|
|
197
|
-
className="stg:mt-0.5 stg:h-3 stg:w-3
|
|
197
|
+
className="stg:mt-0.5 stg:h-3 stg:w-3"
|
|
198
198
|
/>
|
|
199
199
|
<span>
|
|
200
200
|
<span className="stg:font-medium">{opt.label}</span>
|
|
@@ -68,15 +68,16 @@ export function WorkflowInstanceDetailPanel({
|
|
|
68
68
|
const visibility = meta?.visibility ?? ApiResourceVisibility.visibility_private;
|
|
69
69
|
// Run observability is a separate axis from instance visibility, and only
|
|
70
70
|
// meaningful while the instance is private (ORG/PUBLIC already expose runs
|
|
71
|
-
// by inheritance). It is offered only to those who
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
71
|
+
// by inheritance). It is offered only to those who may change who reaches
|
|
72
|
+
// the instance (can_manage_audience, the server's bar on
|
|
73
|
+
// updateExecutionVisibility), so it rides into the dialog as the
|
|
74
|
+
// resource-specific section — present only when both conditions hold.
|
|
75
|
+
const { allowed: canManageAudience } = useCheckPermission(
|
|
75
76
|
id ? { kind: "workflow_instance", id } : null,
|
|
76
|
-
"
|
|
77
|
+
"can_manage_audience",
|
|
77
78
|
);
|
|
78
79
|
const runVisibilitySection =
|
|
79
|
-
visibility === ApiResourceVisibility.visibility_private &&
|
|
80
|
+
visibility === ApiResourceVisibility.visibility_private && canManageAudience
|
|
80
81
|
? {
|
|
81
82
|
title: "Run visibility",
|
|
82
83
|
description:
|
|
@@ -50,15 +50,6 @@ export interface TaskApprovalRequestView {
|
|
|
50
50
|
readonly payloadArtifactId: string | null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/**
|
|
54
|
-
* A resolved human_input decision, sourced from the canonical task-output
|
|
55
|
-
* record (the runner stores the reviewer's full response as the task
|
|
56
|
-
* output) and enriched with timing from the `approval_resolved` event.
|
|
57
|
-
*
|
|
58
|
-
* `outcome` is the empty string during the brief window after a decision
|
|
59
|
-
* is signalled but before the status snapshot reflects the task output —
|
|
60
|
-
* consumers render a "finalizing" affordance in that state.
|
|
61
|
-
*/
|
|
62
53
|
/**
|
|
63
54
|
* Display identity of the reviewer, snapshotted server-side at decision
|
|
64
55
|
* time (see `reviewer_actor` in the human_input task-output contract).
|
|
@@ -95,6 +86,17 @@ export interface TaskReviewerView {
|
|
|
95
86
|
readonly isRawId: boolean;
|
|
96
87
|
}
|
|
97
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A resolved human_input decision, sourced from the canonical task-output
|
|
91
|
+
* record (the runner stores the reviewer's full response as the task
|
|
92
|
+
* output), with the `approval_resolved` event standing in for any field the
|
|
93
|
+
* status snapshot has not captured yet and supplying the wait duration.
|
|
94
|
+
*
|
|
95
|
+
* `outcome` is the empty string only when neither source carries it: in
|
|
96
|
+
* the brief window before the status snapshot reflects the task output,
|
|
97
|
+
* for an event recorded before the payload had an `outcome` field.
|
|
98
|
+
* Consumers render a "finalizing" affordance in that state.
|
|
99
|
+
*/
|
|
98
100
|
export interface TaskDetailApprovalDecision {
|
|
99
101
|
/** Chosen outcome identifier (e.g. "approve", "pause_campaigns"). */
|
|
100
102
|
readonly outcome: string;
|
|
@@ -166,7 +168,7 @@ export function deriveTaskApprovalDecision(
|
|
|
166
168
|
if (resolution === null && outputOutcome === "") return null;
|
|
167
169
|
|
|
168
170
|
return {
|
|
169
|
-
outcome: outputOutcome,
|
|
171
|
+
outcome: outputOutcome || (resolution?.outcome ?? ""),
|
|
170
172
|
reviewer:
|
|
171
173
|
readSnapshotString(taskOutput, "reviewer") || (resolution?.resolvedBy ?? ""),
|
|
172
174
|
reviewerActor: deriveReviewerActor(resolution, taskOutput),
|
|
@@ -175,15 +177,20 @@ export function deriveTaskApprovalDecision(
|
|
|
175
177
|
readSnapshotString(taskOutput, "comment") || (resolution?.comment ?? ""),
|
|
176
178
|
formData: readSnapshotObject(taskOutput, "form_data"),
|
|
177
179
|
waitDurationMs: resolution ? Number(resolution.waitDurationMs) : 0,
|
|
178
|
-
|
|
180
|
+
// Output first, like every field above: once the snapshot carries the
|
|
181
|
+
// decision it is the record, and the event only fills the window before.
|
|
182
|
+
autoResolved:
|
|
183
|
+
outputOutcome !== ""
|
|
184
|
+
? taskOutput?.["auto_resolved"] === true
|
|
185
|
+
: resolution?.autoResolved === true,
|
|
179
186
|
};
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
/**
|
|
183
190
|
* Collapses a decision's attribution into the render-ready reviewer view,
|
|
184
191
|
* or `null` when the decision carries no attribution at all (timeout
|
|
185
|
-
* auto-resolution
|
|
186
|
-
*
|
|
192
|
+
* auto-resolution) — consumers omit the "by …" segment entirely in that
|
|
193
|
+
* case.
|
|
187
194
|
*/
|
|
188
195
|
export function deriveTaskReviewer(
|
|
189
196
|
decision: TaskDetailApprovalDecision,
|