@stigmer/react 3.6.0 → 3.8.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/billing/CreditLedgerTable.d.ts.map +1 -1
- package/billing/CreditLedgerTable.js +2 -7
- package/billing/CreditLedgerTable.js.map +1 -1
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +21 -2
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/ChannelConversationsDialog.d.ts +12 -4
- package/channel/ChannelConversationsDialog.d.ts.map +1 -1
- package/channel/ChannelConversationsDialog.js +18 -24
- package/channel/ChannelConversationsDialog.js.map +1 -1
- package/channel/providers.d.ts +23 -0
- package/channel/providers.d.ts.map +1 -1
- package/channel/providers.js +4 -0
- package/channel/providers.js.map +1 -1
- package/channel/useSaveAgentChannel.d.ts +5 -4
- package/channel/useSaveAgentChannel.d.ts.map +1 -1
- package/channel/useSaveAgentChannel.js +18 -4
- package/channel/useSaveAgentChannel.js.map +1 -1
- package/composer/ComposerToolbar.d.ts +6 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +2 -2
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +9 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +13 -2
- package/composer/SessionComposer.js.map +1 -1
- package/conversation/ConversationAttentionBanner.d.ts +22 -0
- package/conversation/ConversationAttentionBanner.d.ts.map +1 -0
- package/conversation/ConversationAttentionBanner.js +25 -0
- package/conversation/ConversationAttentionBanner.js.map +1 -0
- package/conversation/ConversationComposer.d.ts +41 -0
- package/conversation/ConversationComposer.d.ts.map +1 -0
- package/conversation/ConversationComposer.js +57 -0
- package/conversation/ConversationComposer.js.map +1 -0
- package/conversation/ConversationControlBanner.d.ts +52 -0
- package/conversation/ConversationControlBanner.d.ts.map +1 -0
- package/conversation/ConversationControlBanner.js +59 -0
- package/conversation/ConversationControlBanner.js.map +1 -0
- package/conversation/ConversationListPane.d.ts +68 -0
- package/conversation/ConversationListPane.d.ts.map +1 -0
- package/conversation/ConversationListPane.js +120 -0
- package/conversation/ConversationListPane.js.map +1 -0
- package/conversation/ConversationTimelineView.d.ts +46 -0
- package/conversation/ConversationTimelineView.d.ts.map +1 -0
- package/conversation/ConversationTimelineView.js +166 -0
- package/conversation/ConversationTimelineView.js.map +1 -0
- package/conversation/ConversationsWorkbench.d.ts +63 -0
- package/conversation/ConversationsWorkbench.d.ts.map +1 -0
- package/conversation/ConversationsWorkbench.js +165 -0
- package/conversation/ConversationsWorkbench.js.map +1 -0
- package/conversation/conversationPresentation.d.ts +137 -0
- package/conversation/conversationPresentation.d.ts.map +1 -0
- package/conversation/conversationPresentation.js +230 -0
- package/conversation/conversationPresentation.js.map +1 -0
- package/conversation/index.d.ts +26 -0
- package/conversation/index.d.ts.map +1 -0
- package/conversation/index.js +14 -0
- package/conversation/index.js.map +1 -0
- package/conversation/polling.d.ts +39 -0
- package/conversation/polling.d.ts.map +1 -0
- package/conversation/polling.js +39 -0
- package/conversation/polling.js.map +1 -0
- package/conversation/useConversation.d.ts +61 -0
- package/conversation/useConversation.d.ts.map +1 -0
- package/conversation/useConversation.js +153 -0
- package/conversation/useConversation.js.map +1 -0
- package/conversation/useConversationList.d.ts +98 -0
- package/conversation/useConversationList.d.ts.map +1 -0
- package/conversation/useConversationList.js +200 -0
- package/conversation/useConversationList.js.map +1 -0
- package/conversation/useConversationParticipation.d.ts +63 -0
- package/conversation/useConversationParticipation.d.ts.map +1 -0
- package/conversation/useConversationParticipation.js +84 -0
- package/conversation/useConversationParticipation.js.map +1 -0
- package/conversation/useConversationTimeline.d.ts +71 -0
- package/conversation/useConversationTimeline.d.ts.map +1 -0
- package/conversation/useConversationTimeline.js +152 -0
- package/conversation/useConversationTimeline.js.map +1 -0
- package/conversation/useConversationsWantsHumanCount.d.ts +42 -0
- package/conversation/useConversationsWantsHumanCount.d.ts.map +1 -0
- package/conversation/useConversationsWantsHumanCount.js +40 -0
- package/conversation/useConversationsWantsHumanCount.js.map +1 -0
- package/execution/useCreateAgentExecution.d.ts +14 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +6 -2
- package/index.d.ts.map +1 -1
- package/index.js +14 -5
- package/index.js.map +1 -1
- package/internal/Pagination.d.ts +15 -0
- package/internal/Pagination.d.ts.map +1 -0
- package/internal/Pagination.js +20 -0
- package/internal/Pagination.js.map +1 -0
- package/internal/tooltip.d.ts.map +1 -1
- package/internal/tooltip.js +7 -1
- package/internal/tooltip.js.map +1 -1
- package/internal/useAutoScroll.d.ts +25 -4
- package/internal/useAutoScroll.d.ts.map +1 -1
- package/internal/useAutoScroll.js +55 -15
- package/internal/useAutoScroll.js.map +1 -1
- package/internal/useFetch.d.ts.map +1 -1
- package/internal/useFetch.js +14 -4
- package/internal/useFetch.js.map +1 -1
- package/models/ModelSelector.d.ts +45 -1
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +49 -6
- package/models/ModelSelector.js.map +1 -1
- package/models/index.d.ts +2 -0
- package/models/index.d.ts.map +1 -1
- package/models/index.js +1 -0
- package/models/index.js.map +1 -1
- package/models/registry.d.ts +11 -0
- package/models/registry.d.ts.map +1 -1
- package/models/registry.js +3 -0
- package/models/registry.js.map +1 -1
- package/models/service-tier.d.ts +27 -0
- package/models/service-tier.d.ts.map +1 -0
- package/models/service-tier.js +34 -0
- package/models/service-tier.js.map +1 -0
- package/package.json +4 -4
- package/schedule/ScheduleDetailView.d.ts +31 -8
- package/schedule/ScheduleDetailView.d.ts.map +1 -1
- package/schedule/ScheduleDetailView.js +441 -29
- package/schedule/ScheduleDetailView.js.map +1 -1
- package/schedule/ScheduleForm.d.ts +7 -4
- package/schedule/ScheduleForm.d.ts.map +1 -1
- package/schedule/ScheduleForm.js +94 -8
- package/schedule/ScheduleForm.js.map +1 -1
- package/schedule/ScheduleRunsTable.d.ts +72 -0
- package/schedule/ScheduleRunsTable.d.ts.map +1 -0
- package/schedule/ScheduleRunsTable.js +161 -0
- package/schedule/ScheduleRunsTable.js.map +1 -0
- package/schedule/index.d.ts +6 -0
- package/schedule/index.d.ts.map +1 -1
- package/schedule/index.js +3 -0
- package/schedule/index.js.map +1 -1
- package/schedule/useScheduleRuns.d.ts +44 -0
- package/schedule/useScheduleRuns.d.ts.map +1 -0
- package/schedule/useScheduleRuns.js +48 -0
- package/schedule/useScheduleRuns.js.map +1 -0
- package/schedule/useTriggerSchedule.d.ts +23 -10
- package/schedule/useTriggerSchedule.d.ts.map +1 -1
- package/schedule/useTriggerSchedule.js +28 -9
- package/schedule/useTriggerSchedule.js.map +1 -1
- package/schedule/useUpdateScheduleSpec.d.ts +49 -0
- package/schedule/useUpdateScheduleSpec.d.ts.map +1 -0
- package/schedule/useUpdateScheduleSpec.js +70 -0
- package/schedule/useUpdateScheduleSpec.js.map +1 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +1 -0
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionConversation.d.ts +11 -0
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +1 -0
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/sharing/useSaveAgentShare.d.ts +10 -1
- package/sharing/useSaveAgentShare.d.ts.map +1 -1
- package/sharing/useSaveAgentShare.js +9 -0
- package/sharing/useSaveAgentShare.js.map +1 -1
- package/sidebar/WorkspaceSidebar.d.ts +12 -4
- package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
- package/sidebar/WorkspaceSidebar.js +19 -7
- package/sidebar/WorkspaceSidebar.js.map +1 -1
- package/src/billing/CreditLedgerTable.tsx +3 -42
- package/src/channel/AgentChannelsPanel.tsx +33 -2
- package/src/channel/ChannelConversationsDialog.tsx +22 -25
- package/src/channel/__tests__/AgentChannelsPanel.test.tsx +36 -5
- package/src/channel/__tests__/ChannelConversationsDialog.test.tsx +38 -1
- package/src/channel/__tests__/useSaveAgentChannel.test.tsx +26 -0
- package/src/channel/providers.ts +27 -0
- package/src/channel/useSaveAgentChannel.ts +18 -4
- package/src/composer/ComposerToolbar.tsx +9 -0
- package/src/composer/SessionComposer.tsx +24 -1
- package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +157 -0
- package/src/conversation/ConversationAttentionBanner.tsx +77 -0
- package/src/conversation/ConversationComposer.tsx +156 -0
- package/src/conversation/ConversationControlBanner.tsx +193 -0
- package/src/conversation/ConversationListPane.tsx +459 -0
- package/src/conversation/ConversationTimelineView.tsx +435 -0
- package/src/conversation/ConversationsWorkbench.tsx +378 -0
- package/src/conversation/__tests__/ConversationAttentionBanner.test.tsx +82 -0
- package/src/conversation/__tests__/ConversationComposer.test.tsx +160 -0
- package/src/conversation/__tests__/ConversationControlBanner.test.tsx +190 -0
- package/src/conversation/__tests__/ConversationListPane.test.tsx +372 -0
- package/src/conversation/__tests__/ConversationTimelineView.layout.test.tsx +160 -0
- package/src/conversation/__tests__/ConversationTimelineView.test.tsx +405 -0
- package/src/conversation/__tests__/ConversationsWorkbench.test.tsx +860 -0
- package/src/conversation/__tests__/a11y/conversation-surfaces.a11y.test.tsx +235 -0
- package/src/conversation/__tests__/a11y/harness.tsx +100 -0
- package/src/conversation/__tests__/conversation-tooltips.layout.test.tsx +167 -0
- package/src/conversation/__tests__/conversationPollingComposition.test.tsx +138 -0
- package/src/conversation/__tests__/conversationPresentation.test.ts +289 -0
- package/src/conversation/__tests__/useConversation.test.tsx +243 -0
- package/src/conversation/__tests__/useConversationList.test.tsx +492 -0
- package/src/conversation/__tests__/useConversationParticipation.test.tsx +190 -0
- package/src/conversation/__tests__/useConversationTimeline.test.tsx +258 -0
- package/src/conversation/__tests__/useConversationsWantsHumanCount.test.tsx +102 -0
- package/src/conversation/conversationPresentation.ts +305 -0
- package/src/conversation/index.ts +80 -0
- package/src/conversation/polling.ts +41 -0
- package/src/conversation/useConversation.ts +221 -0
- package/src/conversation/useConversationList.ts +328 -0
- package/src/conversation/useConversationParticipation.ts +178 -0
- package/src/conversation/useConversationTimeline.ts +244 -0
- package/src/conversation/useConversationsWantsHumanCount.ts +86 -0
- package/src/execution/useCreateAgentExecution.ts +18 -0
- package/src/index.ts +76 -4
- package/src/internal/Pagination.tsx +72 -0
- package/src/internal/__tests__/fake-timer-slices.ts +29 -0
- package/src/internal/__tests__/useAutoScroll.layout.test.tsx +143 -0
- package/src/internal/__tests__/useAutoScroll.test.tsx +62 -5
- package/src/internal/__tests__/useFetch-poll-under-render-pressure.test.tsx +79 -0
- package/src/internal/tooltip.tsx +6 -1
- package/src/internal/useAutoScroll.ts +66 -20
- package/src/internal/useFetch.ts +15 -4
- package/src/models/ModelSelector.tsx +133 -4
- package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +256 -0
- package/src/models/__tests__/useModelRegistry.test.tsx +15 -0
- package/src/models/index.ts +2 -0
- package/src/models/registry.ts +17 -0
- package/src/models/service-tier.ts +49 -0
- package/src/schedule/ScheduleDetailView.tsx +1179 -111
- package/src/schedule/ScheduleForm.tsx +218 -7
- package/src/schedule/ScheduleRunsTable.tsx +424 -0
- package/src/schedule/__tests__/ScheduleDetailView.test.tsx +431 -8
- package/src/schedule/__tests__/scheduleCreation.test.tsx +197 -0
- package/src/schedule/__tests__/scheduleHooks.test.tsx +94 -7
- package/src/schedule/index.ts +12 -0
- package/src/schedule/useScheduleRuns.ts +93 -0
- package/src/schedule/useTriggerSchedule.ts +39 -14
- package/src/schedule/useUpdateScheduleSpec.ts +108 -0
- package/src/session/useNewSessionFlow.ts +1 -0
- package/src/session/useSessionConversation.ts +12 -0
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/sharing/__tests__/useSaveAgentShare.test.tsx +55 -1
- package/src/sharing/__tests__/useShareToolReadiness.test.tsx +1 -0
- package/src/sharing/useSaveAgentShare.ts +19 -1
- package/src/sidebar/WorkspaceSidebar.tsx +62 -3
- package/src/sidebar/__tests__/WorkspaceSidebar.test.tsx +56 -1
- package/src/workflow/__tests__/inspector-forms.test.tsx +119 -2
- package/src/workflow/inspector/forms/AgentCallForm.tsx +260 -37
- package/src/workflow/inspector/tabs/RuntimeTab.tsx +72 -33
- package/src/workflow/starter-workflow-yaml.ts +2 -1
- package/styles.css +1 -1
- package/workflow/inspector/forms/AgentCallForm.d.ts +18 -2
- package/workflow/inspector/forms/AgentCallForm.d.ts.map +1 -1
- package/workflow/inspector/forms/AgentCallForm.js +87 -9
- package/workflow/inspector/forms/AgentCallForm.js.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts +2 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.js +31 -5
- package/workflow/inspector/tabs/RuntimeTab.js.map +1 -1
- package/workflow/starter-workflow-yaml.d.ts +1 -1
- package/workflow/starter-workflow-yaml.d.ts.map +1 -1
- package/workflow/starter-workflow-yaml.js +2 -1
- package/workflow/starter-workflow-yaml.js.map +1 -1
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
WorkspaceEntryInput,
|
|
18
18
|
} from "@stigmer/sdk";
|
|
19
19
|
import { isTerminalPhase } from "../execution/execution-phases.js";
|
|
20
|
+
import type { ServiceTierOption } from "../models/service-tier.js";
|
|
20
21
|
import { useStigmer } from "../hooks.js";
|
|
21
22
|
import { toError } from "../internal/toError.js";
|
|
22
23
|
import { useConversationStoreRef } from "../internal/store/index.js";
|
|
@@ -96,6 +97,16 @@ export interface SendFollowUpOptions {
|
|
|
96
97
|
* - `"plan"`: read-only analysis, no file mutations.
|
|
97
98
|
*/
|
|
98
99
|
readonly interactionMode?: "agent" | "plan";
|
|
100
|
+
/**
|
|
101
|
+
* Service tier for this execution's model calls (stigmer/stigmer#357).
|
|
102
|
+
*
|
|
103
|
+
* Only set when the user actively selected `"fast"`; `undefined` preserves
|
|
104
|
+
* the unspecified-vs-explicit distinction (unset resolves to standard on
|
|
105
|
+
* the platform side — never the provider account default).
|
|
106
|
+
*
|
|
107
|
+
* @see {@link CreateAgentExecutionInput.serviceTier}
|
|
108
|
+
*/
|
|
109
|
+
readonly serviceTier?: ServiceTierOption;
|
|
99
110
|
/**
|
|
100
111
|
* Marks this execution as a Build-from-plan turn.
|
|
101
112
|
*
|
|
@@ -642,6 +653,7 @@ export function useSessionConversation(
|
|
|
642
653
|
runtimeEnv: options?.runtimeEnv,
|
|
643
654
|
attachments: options?.attachments,
|
|
644
655
|
interactionMode: options?.interactionMode,
|
|
656
|
+
serviceTier: options?.serviceTier,
|
|
645
657
|
buildFromPlan: options?.buildFromPlan,
|
|
646
658
|
autoApproveAll: options?.autoApproveAll,
|
|
647
659
|
workspaceFileRefs: options?.workspaceFileRefs,
|
|
@@ -438,6 +438,7 @@ export function useSessionPageFlow(
|
|
|
438
438
|
runtimeEnv,
|
|
439
439
|
attachments: context?.attachments,
|
|
440
440
|
interactionMode: context?.interactionMode,
|
|
441
|
+
serviceTier: context?.serviceTier,
|
|
441
442
|
buildFromPlan: context?.buildFromPlan,
|
|
442
443
|
// Sourced from the session-scoped preference set at the approval gate,
|
|
443
444
|
// not from the composer (the pre-arm toggle was removed).
|
|
@@ -6,6 +6,7 @@ import type { AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v
|
|
|
6
6
|
import type { AgentShareInput } from "@stigmer/sdk";
|
|
7
7
|
import { StigmerContext } from "../../context";
|
|
8
8
|
import {
|
|
9
|
+
draftFromShare,
|
|
9
10
|
sharingAudienceFromProto,
|
|
10
11
|
useSaveAgentShare,
|
|
11
12
|
type AgentShareDraft,
|
|
@@ -50,6 +51,7 @@ const FULL_DRAFT: AgentShareDraft = {
|
|
|
50
51
|
conversationEnded: "This conversation has ended.",
|
|
51
52
|
},
|
|
52
53
|
environmentRefs: [{ org: "acme", slug: "github-org-shared" }],
|
|
54
|
+
runConfig: { modelName: "gpt-5-mini", maxCostUsd: 0.25 },
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
describe("useSaveAgentShare", () => {
|
|
@@ -88,6 +90,7 @@ describe("useSaveAgentShare", () => {
|
|
|
88
90
|
expect(input.environmentRefs).toEqual([
|
|
89
91
|
{ org: "acme", slug: "github-org-shared" },
|
|
90
92
|
]);
|
|
93
|
+
expect(input.runConfig).toEqual({ modelName: "gpt-5-mini", maxCostUsd: 0.25 });
|
|
91
94
|
});
|
|
92
95
|
|
|
93
96
|
it("keeps the existing share's identity when editing (a renamed share is never forked)", async () => {
|
|
@@ -123,7 +126,15 @@ describe("useSaveAgentShare", () => {
|
|
|
123
126
|
|
|
124
127
|
await act(() =>
|
|
125
128
|
result.current.save(
|
|
126
|
-
|
|
129
|
+
// An org-audience share carries no environmentRefs or runConfig
|
|
130
|
+
// (both are public-audience-only by the proto CEL rules).
|
|
131
|
+
{
|
|
132
|
+
...FULL_DRAFT,
|
|
133
|
+
enabled: false,
|
|
134
|
+
audience: "org",
|
|
135
|
+
environmentRefs: [],
|
|
136
|
+
runConfig: undefined,
|
|
137
|
+
},
|
|
127
138
|
null,
|
|
128
139
|
),
|
|
129
140
|
);
|
|
@@ -138,6 +149,49 @@ describe("useSaveAgentShare", () => {
|
|
|
138
149
|
expect(input.audience).toBe(AgentShareAudience.org);
|
|
139
150
|
});
|
|
140
151
|
|
|
152
|
+
it("a pause/resume toggle built from draftFromShare carries run_config (never wipes an API-set override)", async () => {
|
|
153
|
+
const apply = vi.fn().mockResolvedValue({});
|
|
154
|
+
const client = createMockStigmer({ apply });
|
|
155
|
+
// A share whose run_config was set via CLI/API — the console list's
|
|
156
|
+
// toggle rebuilds the full spec from draftFromShare, so the override
|
|
157
|
+
// must ride through even though no console surface edits it.
|
|
158
|
+
const share = {
|
|
159
|
+
metadata: { id: "ash_1", org: "acme", slug: "support-agent", name: "Support Agent" },
|
|
160
|
+
spec: {
|
|
161
|
+
enabled: true,
|
|
162
|
+
allowedOrigins: ["https://example.com"],
|
|
163
|
+
environmentRefs: [],
|
|
164
|
+
runConfig: { modelName: "gpt-5-mini", maxCostUsd: 0.25, maxToolRounds: 8 },
|
|
165
|
+
},
|
|
166
|
+
} as unknown as AgentShare;
|
|
167
|
+
|
|
168
|
+
const { result } = renderHook(() => useSaveAgentShare(AGENT), {
|
|
169
|
+
wrapper: wrapper(client),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await act(() =>
|
|
173
|
+
result.current.save({ ...draftFromShare(share), enabled: false }, share),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const input = apply.mock.calls[0][0] as AgentShareInput;
|
|
177
|
+
expect(input.enabled).toBe(false);
|
|
178
|
+
expect(input.runConfig).toEqual({
|
|
179
|
+
modelName: "gpt-5-mini",
|
|
180
|
+
maxCostUsd: 0.25,
|
|
181
|
+
maxToolRounds: 8,
|
|
182
|
+
serviceTier: undefined,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("draftFromShare leaves run_config undefined when the share carries none", () => {
|
|
187
|
+
expect(draftFromShare(null).runConfig).toBeUndefined();
|
|
188
|
+
const bare = {
|
|
189
|
+
metadata: { id: "ash_2", org: "acme", slug: "bare" },
|
|
190
|
+
spec: { enabled: true },
|
|
191
|
+
} as AgentShare;
|
|
192
|
+
expect(draftFromShare(bare).runConfig).toBeUndefined();
|
|
193
|
+
});
|
|
194
|
+
|
|
141
195
|
it("maps the audience union to the proto enum, writing public explicitly", async () => {
|
|
142
196
|
const apply = vi.fn().mockResolvedValue({});
|
|
143
197
|
const client = createMockStigmer({ apply });
|
|
@@ -4,7 +4,7 @@ import { useCallback, useState } from "react";
|
|
|
4
4
|
import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
5
5
|
import type { AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
|
|
6
6
|
import { AgentShareAudience } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/spec_pb";
|
|
7
|
-
import type { ResourceRef } from "@stigmer/sdk";
|
|
7
|
+
import type { ResourceRef, RunConfigInput } from "@stigmer/sdk";
|
|
8
8
|
import { useStigmer } from "../hooks.js";
|
|
9
9
|
import { toError } from "../internal/toError.js";
|
|
10
10
|
|
|
@@ -57,6 +57,15 @@ export interface AgentShareDraft {
|
|
|
57
57
|
* sessions carry no share linkage in Phase A — decision 011 addendum).
|
|
58
58
|
*/
|
|
59
59
|
readonly environmentRefs: readonly ResourceRef[];
|
|
60
|
+
/**
|
|
61
|
+
* The owner's per-share execution override (model / cost cap / tool
|
|
62
|
+
* rounds / service tier), merged over the platform guest profile at
|
|
63
|
+
* run time — stigmer/stigmer#360. The console does not edit it (yet):
|
|
64
|
+
* it rides through every save opaquely so a toggle never wipes an
|
|
65
|
+
* override set via the CLI or API. Public-audience only (the same
|
|
66
|
+
* proto CEL rule as environmentRefs, for the same reason).
|
|
67
|
+
*/
|
|
68
|
+
readonly runConfig: RunConfigInput | undefined;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
/**
|
|
@@ -103,6 +112,14 @@ export function draftFromShare(share: AgentShare | null): AgentShareDraft {
|
|
|
103
112
|
org: ref.org,
|
|
104
113
|
slug: ref.slug,
|
|
105
114
|
})),
|
|
115
|
+
runConfig: spec?.runConfig
|
|
116
|
+
? {
|
|
117
|
+
modelName: spec.runConfig.modelName,
|
|
118
|
+
maxCostUsd: spec.runConfig.maxCostUsd,
|
|
119
|
+
maxToolRounds: spec.runConfig.maxToolRounds,
|
|
120
|
+
serviceTier: spec.runConfig.serviceTier,
|
|
121
|
+
}
|
|
122
|
+
: undefined,
|
|
106
123
|
};
|
|
107
124
|
}
|
|
108
125
|
|
|
@@ -229,6 +246,7 @@ export function useSaveAgentShare(
|
|
|
229
246
|
org: ref.org,
|
|
230
247
|
slug: ref.slug,
|
|
231
248
|
})),
|
|
249
|
+
runConfig: draft.runConfig,
|
|
232
250
|
});
|
|
233
251
|
} catch (err) {
|
|
234
252
|
setError(toError(err));
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
LayoutDashboard,
|
|
7
7
|
Library,
|
|
8
8
|
MessageSquare,
|
|
9
|
+
MessagesSquare,
|
|
9
10
|
Workflow,
|
|
10
11
|
type LucideIcon,
|
|
11
12
|
} from "lucide-react";
|
|
@@ -29,7 +30,11 @@ import type { RenderSidebarLink } from "./types.js";
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
|
|
31
32
|
/** Primary navigation destinations in the workspace sidebar. */
|
|
32
|
-
export type WorkspaceNavId =
|
|
33
|
+
export type WorkspaceNavId =
|
|
34
|
+
| "new-session"
|
|
35
|
+
| "dashboard"
|
|
36
|
+
| "conversations"
|
|
37
|
+
| "library";
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Recent-activity data for the sidebar's Recents section — the return
|
|
@@ -66,6 +71,14 @@ export interface WorkspaceSidebarProps {
|
|
|
66
71
|
* timestamp column, inside the row.
|
|
67
72
|
*/
|
|
68
73
|
readonly renderEntryAccessory?: (entry: RecentActivityEntry) => ReactNode;
|
|
74
|
+
/**
|
|
75
|
+
* Count for the Conversations row's badge: how many conversations
|
|
76
|
+
* want a human right now (`useConversationsWantsHumanCount`). Data as
|
|
77
|
+
* props per the chrome contract — hosts fetch, deterministic hosts
|
|
78
|
+
* (tours) pass a fixture or omit. Hidden at `0`/omitted; the display
|
|
79
|
+
* caps at "99+" while the accessible name keeps the real number.
|
|
80
|
+
*/
|
|
81
|
+
readonly conversationsBadgeCount?: number;
|
|
69
82
|
/** Footer content — typically the host app's `UserMenu` wrapper. */
|
|
70
83
|
readonly footer: ReactNode;
|
|
71
84
|
/** Reflected as `aria-expanded` on the collapse toggle. @default true */
|
|
@@ -85,8 +98,8 @@ export interface WorkspaceSidebarProps {
|
|
|
85
98
|
|
|
86
99
|
/**
|
|
87
100
|
* The console's workspace-zone sidebar: org switcher, primary navigation
|
|
88
|
-
* (New Session / Dashboard / Library), time-bucketed
|
|
89
|
-
* and a user footer.
|
|
101
|
+
* (New Session / Dashboard / Conversations / Library), time-bucketed
|
|
102
|
+
* recent activity, and a user footer.
|
|
90
103
|
*
|
|
91
104
|
* This is the same component the Stigmer web console and desktop app
|
|
92
105
|
* render — hosts differ only in what they return from `renderLink`
|
|
@@ -117,6 +130,7 @@ export function WorkspaceSidebar({
|
|
|
117
130
|
activeSessionId = null,
|
|
118
131
|
activeExecutionId = null,
|
|
119
132
|
renderEntryAccessory,
|
|
133
|
+
conversationsBadgeCount,
|
|
120
134
|
footer,
|
|
121
135
|
isOpen = true,
|
|
122
136
|
onCollapse,
|
|
@@ -154,6 +168,22 @@ export function WorkspaceSidebar({
|
|
|
154
168
|
active={activeNav === "dashboard"}
|
|
155
169
|
renderLink={renderLink}
|
|
156
170
|
/>
|
|
171
|
+
{/* MessagesSquare, deliberately not MessageSquare — the singular
|
|
172
|
+
mark is the session glyph in Recents, and the two areas must
|
|
173
|
+
not read as one. */}
|
|
174
|
+
<PrimaryNavRow
|
|
175
|
+
id="conversations"
|
|
176
|
+
href="/conversations"
|
|
177
|
+
label="Conversations"
|
|
178
|
+
icon={MessagesSquare}
|
|
179
|
+
active={activeNav === "conversations"}
|
|
180
|
+
renderLink={renderLink}
|
|
181
|
+
accessory={
|
|
182
|
+
conversationsBadgeCount !== undefined && conversationsBadgeCount > 0 ? (
|
|
183
|
+
<WantsHumanBadge count={conversationsBadgeCount} />
|
|
184
|
+
) : null
|
|
185
|
+
}
|
|
186
|
+
/>
|
|
157
187
|
<PrimaryNavRow
|
|
158
188
|
id="library"
|
|
159
189
|
href="/library"
|
|
@@ -204,6 +234,7 @@ function PrimaryNavRow({
|
|
|
204
234
|
icon: Icon,
|
|
205
235
|
active,
|
|
206
236
|
renderLink,
|
|
237
|
+
accessory = null,
|
|
207
238
|
}: {
|
|
208
239
|
readonly id: WorkspaceNavId;
|
|
209
240
|
readonly href: string;
|
|
@@ -211,6 +242,8 @@ function PrimaryNavRow({
|
|
|
211
242
|
readonly icon: LucideIcon;
|
|
212
243
|
readonly active: boolean;
|
|
213
244
|
readonly renderLink: RenderSidebarLink;
|
|
245
|
+
/** Trailing row content (e.g. the Conversations count pill). */
|
|
246
|
+
readonly accessory?: ReactNode;
|
|
214
247
|
}) {
|
|
215
248
|
return (
|
|
216
249
|
<div className="flex-none px-3 py-1">
|
|
@@ -224,6 +257,7 @@ function PrimaryNavRow({
|
|
|
224
257
|
<>
|
|
225
258
|
<Icon className="size-4 shrink-0" />
|
|
226
259
|
{label}
|
|
260
|
+
{accessory}
|
|
227
261
|
</>
|
|
228
262
|
),
|
|
229
263
|
})}
|
|
@@ -231,6 +265,31 @@ function PrimaryNavRow({
|
|
|
231
265
|
);
|
|
232
266
|
}
|
|
233
267
|
|
|
268
|
+
/**
|
|
269
|
+
* The Conversations row's count pill: how many conversations want a
|
|
270
|
+
* human right now (DD-011 D-f — `needs_attention` OR awaiting-reply
|
|
271
|
+
* while human-held). The visible number caps at "99+"; the accessible
|
|
272
|
+
* name states the meaning with the real number, because a bare "104"
|
|
273
|
+
* read aloud after "Conversations" says nothing.
|
|
274
|
+
*/
|
|
275
|
+
function WantsHumanBadge({ count }: { readonly count: number }) {
|
|
276
|
+
return (
|
|
277
|
+
<span className="ml-auto flex shrink-0 items-center">
|
|
278
|
+
<span
|
|
279
|
+
aria-hidden="true"
|
|
280
|
+
className="bg-sidebar-primary text-sidebar-primary-foreground rounded-full px-1.5 py-0.5 text-[10px] leading-none font-semibold tabular-nums"
|
|
281
|
+
>
|
|
282
|
+
{count > 99 ? "99+" : count}
|
|
283
|
+
</span>
|
|
284
|
+
<span className="sr-only">
|
|
285
|
+
{count === 1
|
|
286
|
+
? "1 conversation needs a human"
|
|
287
|
+
: `${count} conversations need a human`}
|
|
288
|
+
</span>
|
|
289
|
+
</span>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
234
293
|
function ActivityGroupList({
|
|
235
294
|
groups,
|
|
236
295
|
activeSessionId,
|
|
@@ -109,14 +109,69 @@ function baseProps() {
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
describe("WorkspaceSidebar — Conversations badge (channel-conversations DD-011 D-f)", () => {
|
|
113
|
+
const conversationsRow = (container: HTMLElement) =>
|
|
114
|
+
container.querySelector<HTMLAnchorElement>('[data-row-id="conversations"]')!;
|
|
115
|
+
|
|
116
|
+
it("shows the wants-human count on the Conversations row with a meaning-stating accessible name", () => {
|
|
117
|
+
const { container } = renderSidebar(
|
|
118
|
+
<WorkspaceSidebar {...baseProps()} conversationsBadgeCount={3} />,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const row = conversationsRow(container);
|
|
122
|
+
expect(within(row).getByText("3")).toBeDefined();
|
|
123
|
+
expect(within(row).getByText("3 conversations need a human")).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("speaks the singular for one conversation", () => {
|
|
127
|
+
const { container } = renderSidebar(
|
|
128
|
+
<WorkspaceSidebar {...baseProps()} conversationsBadgeCount={1} />,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(
|
|
132
|
+
within(conversationsRow(container)).getByText("1 conversation needs a human"),
|
|
133
|
+
).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("hides the badge at zero and when the host passes no count", () => {
|
|
137
|
+
const { container } = renderSidebar(
|
|
138
|
+
<WorkspaceSidebar {...baseProps()} conversationsBadgeCount={0} />,
|
|
139
|
+
);
|
|
140
|
+
expect(
|
|
141
|
+
within(conversationsRow(container)).queryByText(/need(s)? a human/),
|
|
142
|
+
).toBeNull();
|
|
143
|
+
|
|
144
|
+
cleanup();
|
|
145
|
+
|
|
146
|
+
const { container: without } = renderSidebar(
|
|
147
|
+
<WorkspaceSidebar {...baseProps()} />,
|
|
148
|
+
);
|
|
149
|
+
expect(
|
|
150
|
+
within(conversationsRow(without)).queryByText(/need(s)? a human/),
|
|
151
|
+
).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("caps the visible number at 99+ while the accessible name keeps the real count", () => {
|
|
155
|
+
const { container } = renderSidebar(
|
|
156
|
+
<WorkspaceSidebar {...baseProps()} conversationsBadgeCount={104} />,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const row = conversationsRow(container);
|
|
160
|
+
expect(within(row).getByText("99+")).toBeDefined();
|
|
161
|
+
expect(within(row).queryByText("104")).toBeNull();
|
|
162
|
+
expect(within(row).getByText("104 conversations need a human")).toBeDefined();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
112
166
|
describe("WorkspaceSidebar — primary navigation", () => {
|
|
113
|
-
it("renders the
|
|
167
|
+
it("renders the four primary rows through renderLink with stable ids and hrefs", () => {
|
|
114
168
|
const { container } = renderSidebar(<WorkspaceSidebar {...baseProps()} />);
|
|
115
169
|
|
|
116
170
|
const row = (id: string) =>
|
|
117
171
|
container.querySelector<HTMLAnchorElement>(`[data-row-id="${id}"]`);
|
|
118
172
|
expect(row("new-session")?.getAttribute("href")).toBe("/");
|
|
119
173
|
expect(row("dashboard")?.getAttribute("href")).toBe("/dashboard");
|
|
174
|
+
expect(row("conversations")?.getAttribute("href")).toBe("/conversations");
|
|
120
175
|
expect(row("library")?.getAttribute("href")).toBe("/library");
|
|
121
176
|
});
|
|
122
177
|
|
|
@@ -34,15 +34,132 @@ describe("AgentCallForm", () => {
|
|
|
34
34
|
expect(textarea.value).toBe("Hello world");
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it("renders model input from
|
|
37
|
+
it("renders model input from the run_config block", () => {
|
|
38
38
|
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
39
39
|
agent: "x",
|
|
40
40
|
message: "y",
|
|
41
|
-
|
|
41
|
+
run_config: { model_name: "claude-sonnet-4", max_cost_usd: 0.5 },
|
|
42
42
|
});
|
|
43
43
|
render(<AgentCallForm node={node} onFieldChange={vi.fn()} />);
|
|
44
44
|
const input = screen.getByTestId("agent-call-model-input") as HTMLInputElement;
|
|
45
45
|
expect(input.value).toBe("claude-sonnet-4");
|
|
46
|
+
const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
|
|
47
|
+
expect(budget.value).toBe("0.5");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("emits an undefined run_config when the last field is cleared", () => {
|
|
51
|
+
const onFieldChange = vi.fn();
|
|
52
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
53
|
+
agent: "x",
|
|
54
|
+
message: "y",
|
|
55
|
+
run_config: { max_cost_usd: 0.5 },
|
|
56
|
+
});
|
|
57
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
58
|
+
|
|
59
|
+
const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
|
|
60
|
+
fireEvent.change(budget, { target: { value: "" } });
|
|
61
|
+
|
|
62
|
+
// Empty means omit: a blank field must not persist a zero override.
|
|
63
|
+
expect(onFieldChange).toHaveBeenCalledWith("run_config", undefined);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("writes budget changes into run_config.max_cost_usd", () => {
|
|
67
|
+
const onFieldChange = vi.fn();
|
|
68
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
69
|
+
agent: "x",
|
|
70
|
+
message: "y",
|
|
71
|
+
run_config: { model_name: "claude-sonnet-4" },
|
|
72
|
+
});
|
|
73
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
74
|
+
|
|
75
|
+
const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
|
|
76
|
+
fireEvent.change(budget, { target: { value: "1.25" } });
|
|
77
|
+
|
|
78
|
+
expect(onFieldChange).toHaveBeenCalledWith("run_config", {
|
|
79
|
+
model_name: "claude-sonnet-4",
|
|
80
|
+
max_cost_usd: 1.25,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("renders workspace entries and edits the git url in place", () => {
|
|
85
|
+
const onFieldChange = vi.fn();
|
|
86
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
87
|
+
agent: "x",
|
|
88
|
+
message: "y",
|
|
89
|
+
workspace_entries: [
|
|
90
|
+
{ name: "app", source: { git_repo: { url: "https://github.com/acme/app", branch: "main" } } },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
93
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
94
|
+
|
|
95
|
+
const url = screen.getByTestId("agent-call-workspace-url-0") as HTMLInputElement;
|
|
96
|
+
expect(url.value).toBe("https://github.com/acme/app");
|
|
97
|
+
|
|
98
|
+
fireEvent.change(url, { target: { value: "https://github.com/acme/other" } });
|
|
99
|
+
expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", [
|
|
100
|
+
{ name: "app", source: { git_repo: { url: "https://github.com/acme/other", branch: "main" } } },
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("adds a workspace entry row", () => {
|
|
105
|
+
const onFieldChange = vi.fn();
|
|
106
|
+
const node = makeNode(WorkflowTaskKind.agent_call, { agent: "x", message: "y" });
|
|
107
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
108
|
+
|
|
109
|
+
fireEvent.click(screen.getByTestId("agent-call-workspace-add"));
|
|
110
|
+
expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", [
|
|
111
|
+
{ source: { git_repo: { url: "" } } },
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("emits undefined when the last workspace entry is removed", () => {
|
|
116
|
+
const onFieldChange = vi.fn();
|
|
117
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
118
|
+
agent: "x",
|
|
119
|
+
message: "y",
|
|
120
|
+
workspace_entries: [
|
|
121
|
+
{ source: { git_repo: { url: "https://github.com/acme/app" } } },
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
125
|
+
|
|
126
|
+
fireEvent.click(screen.getByLabelText("Remove workspace entry 1"));
|
|
127
|
+
expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", undefined);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("edits environment references as org/slug rows with empty-org omitted", () => {
|
|
131
|
+
const onFieldChange = vi.fn();
|
|
132
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
133
|
+
agent: "x",
|
|
134
|
+
message: "y",
|
|
135
|
+
environment_refs: [{ slug: "shared-secrets" }],
|
|
136
|
+
});
|
|
137
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
138
|
+
|
|
139
|
+
const slug = screen.getByTestId("agent-call-envref-slug-0") as HTMLInputElement;
|
|
140
|
+
expect(slug.value).toBe("shared-secrets");
|
|
141
|
+
|
|
142
|
+
fireEvent.change(slug, { target: { value: "other-secrets" } });
|
|
143
|
+
// Empty org stays omitted — an empty string must not become a stored
|
|
144
|
+
// org override (relative refs resolve to the workflow's own org).
|
|
145
|
+
expect(onFieldChange).toHaveBeenCalledWith("environment_refs", [
|
|
146
|
+
{ slug: "other-secrets" },
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("adds and removes environment reference rows", () => {
|
|
151
|
+
const onFieldChange = vi.fn();
|
|
152
|
+
const node = makeNode(WorkflowTaskKind.agent_call, {
|
|
153
|
+
agent: "x",
|
|
154
|
+
message: "y",
|
|
155
|
+
environment_refs: [{ slug: "a" }, { org: "acme", slug: "b" }],
|
|
156
|
+
});
|
|
157
|
+
render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
|
|
158
|
+
|
|
159
|
+
fireEvent.click(screen.getByLabelText("Remove environment reference 1"));
|
|
160
|
+
expect(onFieldChange).toHaveBeenCalledWith("environment_refs", [
|
|
161
|
+
{ org: "acme", slug: "b" },
|
|
162
|
+
]);
|
|
46
163
|
});
|
|
47
164
|
|
|
48
165
|
it("shows structured output section when output is present", () => {
|