@stigmer/react 3.3.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/useAgentCount.d.ts +2 -0
- package/agent/useAgentCount.d.ts.map +1 -1
- package/agent/useAgentCount.js.map +1 -1
- package/api-key/CreateApiKeyForm.d.ts +7 -1
- package/api-key/CreateApiKeyForm.d.ts.map +1 -1
- package/api-key/CreateApiKeyForm.js +2 -2
- package/api-key/CreateApiKeyForm.js.map +1 -1
- package/billing/index.d.ts +0 -18
- package/billing/index.d.ts.map +1 -1
- package/billing/index.js +4 -9
- package/billing/index.js.map +1 -1
- package/cursor-accounts/CursorAccountEditor.d.ts +7 -1
- package/cursor-accounts/CursorAccountEditor.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountEditor.js +21 -5
- package/cursor-accounts/CursorAccountEditor.js.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.d.ts +9 -5
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.js +34 -140
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
- package/cursor-accounts/MemberCoverageTable.d.ts +29 -0
- package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -0
- package/cursor-accounts/MemberCoverageTable.js +150 -0
- package/cursor-accounts/MemberCoverageTable.js.map +1 -0
- package/cursor-accounts/MemberKeysPanel.d.ts +25 -0
- package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -0
- package/cursor-accounts/MemberKeysPanel.js +115 -0
- package/cursor-accounts/MemberKeysPanel.js.map +1 -0
- package/cursor-accounts/badges.d.ts +9 -0
- package/cursor-accounts/badges.d.ts.map +1 -0
- package/cursor-accounts/badges.js +10 -0
- package/cursor-accounts/badges.js.map +1 -0
- package/cursor-accounts/cursor-account-coverage.d.ts +41 -0
- package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -0
- package/cursor-accounts/cursor-account-coverage.js +26 -0
- package/cursor-accounts/cursor-account-coverage.js.map +1 -0
- package/cursor-accounts/cursor-account-format.d.ts +13 -0
- package/cursor-accounts/cursor-account-format.d.ts.map +1 -1
- package/cursor-accounts/cursor-account-format.js +16 -1
- package/cursor-accounts/cursor-account-format.js.map +1 -1
- package/datastore/CollectionRecordsBrowser.js +18 -4
- package/datastore/CollectionRecordsBrowser.js.map +1 -1
- package/datastore/RecordFilterBuilder.d.ts +8 -1
- package/datastore/RecordFilterBuilder.d.ts.map +1 -1
- package/datastore/RecordFilterBuilder.js +2 -2
- package/datastore/RecordFilterBuilder.js.map +1 -1
- package/datastore/recordFilter.d.ts +9 -1
- package/datastore/recordFilter.d.ts.map +1 -1
- package/datastore/recordFilter.js +12 -1
- package/datastore/recordFilter.js.map +1 -1
- package/datastore/useDatastoreCount.d.ts +2 -0
- package/datastore/useDatastoreCount.d.ts.map +1 -1
- package/datastore/useDatastoreCount.js.map +1 -1
- package/execution/ExecutionProgress.d.ts.map +1 -1
- package/execution/ExecutionProgress.js +13 -4
- package/execution/ExecutionProgress.js.map +1 -1
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +10 -2
- package/execution/MessageThread.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +31 -1
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +2 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +8 -6
- package/index.d.ts.map +1 -1
- package/index.js +8 -4
- package/index.js.map +1 -1
- package/internal/form-primitives.d.ts +10 -0
- package/internal/form-primitives.d.ts.map +1 -0
- package/{billing → internal}/form-primitives.js +5 -9
- package/internal/form-primitives.js.map +1 -0
- package/manifest/ApplyManifestDialog.d.ts +10 -5
- package/manifest/ApplyManifestDialog.d.ts.map +1 -1
- package/manifest/ApplyManifestDialog.js +30 -13
- package/manifest/ApplyManifestDialog.js.map +1 -1
- package/manifest/useApplyManifest.d.ts +6 -2
- package/manifest/useApplyManifest.d.ts.map +1 -1
- package/manifest/useApplyManifest.js +5 -5
- package/manifest/useApplyManifest.js.map +1 -1
- package/mcp-server/McpServerDetailView.d.ts +38 -2
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +21 -5
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/index.d.ts +7 -0
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +9 -0
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/steps/EnvironmentAuthStep.d.ts +4 -0
- package/mcp-server/steps/EnvironmentAuthStep.d.ts.map +1 -1
- package/mcp-server/steps/EnvironmentAuthStep.js +4 -0
- package/mcp-server/steps/EnvironmentAuthStep.js.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.d.ts +6 -0
- package/mcp-server/steps/IdentityTransportStep.d.ts.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.js +7 -1
- package/mcp-server/steps/IdentityTransportStep.js.map +1 -1
- package/mcp-server/steps/ReviewStep.d.ts +5 -0
- package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
- package/mcp-server/steps/ReviewStep.js +16 -2
- package/mcp-server/steps/ReviewStep.js.map +1 -1
- package/mcp-server/useMcpServerCount.d.ts +2 -0
- package/mcp-server/useMcpServerCount.d.ts.map +1 -1
- package/mcp-server/useMcpServerCount.js.map +1 -1
- package/package.json +4 -4
- package/pricing-governance/BaselineEditor.d.ts.map +1 -0
- package/{billing → pricing-governance}/BaselineEditor.js +19 -2
- package/pricing-governance/BaselineEditor.js.map +1 -0
- package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -0
- package/pricing-governance/ModelCatalogPanel.js.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.d.ts.map +1 -0
- package/pricing-governance/ModelGovernanceDetail.js.map +1 -0
- package/pricing-governance/OperatorAccessNotice.d.ts.map +1 -0
- package/pricing-governance/OperatorAccessNotice.js.map +1 -0
- package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -0
- package/{billing → pricing-governance}/PricingGovernanceConsole.js +1 -1
- package/pricing-governance/PricingGovernanceConsole.js.map +1 -0
- package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -0
- package/pricing-governance/PricingGovernancePanel.js.map +1 -0
- package/pricing-governance/RetireConfirm.d.ts.map +1 -0
- package/{billing → pricing-governance}/RetireConfirm.js +1 -1
- package/pricing-governance/RetireConfirm.js.map +1 -0
- package/pricing-governance/governance-primitives.d.ts.map +1 -0
- package/pricing-governance/governance-primitives.js.map +1 -0
- package/pricing-governance/index.d.ts +19 -0
- package/pricing-governance/index.d.ts.map +1 -0
- package/pricing-governance/index.js +20 -0
- package/pricing-governance/index.js.map +1 -0
- package/pricing-governance/pricing-format.d.ts.map +1 -0
- package/pricing-governance/pricing-format.js.map +1 -0
- package/pricing-governance/useDecidePricingOverride.d.ts.map +1 -0
- package/pricing-governance/useDecidePricingOverride.js.map +1 -0
- package/pricing-governance/useModelGovernanceView.d.ts.map +1 -0
- package/pricing-governance/useModelGovernanceView.js.map +1 -0
- package/pricing-governance/useModelPricingBaselines.d.ts.map +1 -0
- package/pricing-governance/useModelPricingBaselines.js.map +1 -0
- package/pricing-governance/usePricingGovernance.d.ts.map +1 -0
- package/pricing-governance/usePricingGovernance.js.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useRetireModelPricingBaseline.js.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.d.ts.map +1 -0
- package/pricing-governance/useUpsertModelPricingBaseline.js.map +1 -0
- package/resource-creation/CreationPicker.d.ts.map +1 -1
- package/resource-creation/CreationPicker.js +3 -3
- package/resource-creation/CreationPicker.js.map +1 -1
- package/resource-creation/WizardNav.d.ts.map +1 -1
- package/resource-creation/WizardNav.js +1 -1
- package/resource-creation/WizardNav.js.map +1 -1
- package/resource-detail/Section.d.ts +8 -1
- package/resource-detail/Section.d.ts.map +1 -1
- package/resource-detail/Section.js +2 -2
- package/resource-detail/Section.js.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts +9 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.js +2 -1
- package/resource-workbench/components/ResourceWorkbench.js.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.d.ts +9 -0
- package/resource-workbench/hooks/useResourceCollection.d.ts.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.js +2 -2
- package/resource-workbench/hooks/useResourceCollection.js.map +1 -1
- package/search/useResourceCount.d.ts +7 -0
- package/search/useResourceCount.d.ts.map +1 -1
- package/search/useResourceCount.js +2 -1
- package/search/useResourceCount.js.map +1 -1
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +17 -3
- package/session/NewSessionViewer.js.map +1 -1
- package/session/SessionViewer.d.ts +2 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +19 -11
- package/session/SessionViewer.js.map +1 -1
- package/session/SessionViewerLayout.d.ts +90 -0
- package/session/SessionViewerLayout.d.ts.map +1 -0
- package/session/SessionViewerLayout.js +32 -0
- package/session/SessionViewerLayout.js.map +1 -0
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +10 -0
- package/session/index.js.map +1 -1
- package/session/useCreateSession.d.ts +28 -0
- package/session/useCreateSession.d.ts.map +1 -1
- package/session/useCreateSession.js +2 -1
- package/session/useCreateSession.js.map +1 -1
- package/session/useNewSessionFlow.d.ts +30 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +7 -1
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionRailViews.d.ts +2 -2
- package/session/useUpdateSession.d.ts +29 -10
- package/session/useUpdateSession.d.ts.map +1 -1
- package/session/useUpdateSession.js +29 -10
- package/session/useUpdateSession.js.map +1 -1
- package/skill/useSkillCount.d.ts +2 -0
- package/skill/useSkillCount.d.ts.map +1 -1
- package/skill/useSkillCount.js.map +1 -1
- package/src/agent/useAgentCount.ts +2 -0
- package/src/api-key/CreateApiKeyForm.tsx +8 -1
- package/src/api-key/__tests__/CreateApiKeyForm.test.tsx +58 -0
- package/src/billing/index.ts +5 -37
- package/src/cursor-accounts/CursorAccountEditor.tsx +61 -13
- package/src/cursor-accounts/CursorAccountsConsole.tsx +44 -400
- package/src/cursor-accounts/MemberCoverageTable.tsx +439 -0
- package/src/cursor-accounts/MemberKeysPanel.tsx +278 -0
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +306 -10
- package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +73 -0
- package/src/cursor-accounts/badges.tsx +26 -0
- package/src/cursor-accounts/cursor-account-coverage.ts +71 -0
- package/src/cursor-accounts/cursor-account-format.ts +17 -1
- package/src/datastore/CollectionRecordsBrowser.tsx +29 -3
- package/src/datastore/RecordFilterBuilder.tsx +12 -1
- package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +38 -0
- package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +14 -0
- package/src/datastore/__tests__/recordFilter.test.ts +13 -0
- package/src/datastore/recordFilter.ts +15 -1
- package/src/datastore/useDatastoreCount.ts +2 -0
- package/src/execution/ExecutionProgress.tsx +14 -4
- package/src/execution/MessageThread.tsx +10 -2
- package/src/execution/__tests__/ExecutionProgress.test.tsx +67 -0
- package/src/execution/__tests__/MessageThread.test.tsx +44 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +86 -0
- package/src/execution/useCreateAgentExecution.ts +41 -6
- package/src/index.ts +43 -24
- package/src/{billing → internal}/form-primitives.tsx +4 -32
- package/src/manifest/ApplyManifestDialog.tsx +40 -16
- package/src/manifest/__tests__/ApplyManifestDialog.test.tsx +147 -0
- package/src/manifest/__tests__/useApplyManifest.test.tsx +6 -6
- package/src/manifest/useApplyManifest.ts +12 -8
- package/src/mcp-server/McpServerDetailView.tsx +52 -4
- package/src/mcp-server/__tests__/McpServerDetailView.test.tsx +345 -0
- package/src/mcp-server/index.ts +13 -0
- package/src/mcp-server/steps/EnvironmentAuthStep.tsx +4 -0
- package/src/mcp-server/steps/IdentityTransportStep.tsx +9 -1
- package/src/mcp-server/steps/ReviewStep.tsx +18 -1
- package/src/mcp-server/useMcpServerCount.ts +2 -0
- package/src/{billing → pricing-governance}/BaselineEditor.tsx +36 -3
- package/src/{billing → pricing-governance}/PricingGovernanceConsole.tsx +1 -1
- package/src/{billing → pricing-governance}/RetireConfirm.tsx +1 -1
- package/src/pricing-governance/index.ts +50 -0
- package/src/resource-creation/CreationPicker.tsx +7 -0
- package/src/resource-creation/WizardNav.tsx +1 -0
- package/src/resource-detail/Section.tsx +9 -1
- package/src/resource-workbench/components/ResourceWorkbench.tsx +13 -0
- package/src/resource-workbench/hooks/useResourceCollection.ts +11 -1
- package/src/search/useResourceCount.ts +9 -1
- package/src/session/NewSessionViewer.tsx +48 -61
- package/src/session/SessionViewer.tsx +71 -86
- package/src/session/SessionViewerLayout.tsx +131 -0
- package/src/session/__tests__/SessionViewerLayout.test.tsx +140 -0
- package/src/session/__tests__/useCreateSession.test.tsx +94 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
- package/src/session/index.ts +14 -0
- package/src/session/useCreateSession.ts +30 -0
- package/src/session/useNewSessionFlow.ts +45 -1
- package/src/session/useSessionRailViews.tsx +2 -2
- package/src/session/useUpdateSession.ts +29 -10
- package/src/skill/useSkillCount.ts +2 -0
- package/src/test/__tests__/samples.test.ts +159 -2
- package/src/test/index.ts +1 -1
- package/src/test/samples.ts +109 -14
- package/src/workflow/useWorkflowCount.ts +2 -0
- package/styles.css +1 -1
- package/test/index.d.ts +1 -1
- package/test/index.d.ts.map +1 -1
- package/test/index.js +1 -1
- package/test/index.js.map +1 -1
- package/test/samples.d.ts +79 -7
- package/test/samples.d.ts.map +1 -1
- package/test/samples.js +101 -14
- package/test/samples.js.map +1 -1
- package/workflow/useWorkflowCount.d.ts +2 -0
- package/workflow/useWorkflowCount.d.ts.map +1 -1
- package/workflow/useWorkflowCount.js.map +1 -1
- package/billing/BaselineEditor.d.ts.map +0 -1
- package/billing/BaselineEditor.js.map +0 -1
- package/billing/ModelCatalogPanel.d.ts.map +0 -1
- package/billing/ModelCatalogPanel.js.map +0 -1
- package/billing/ModelGovernanceDetail.d.ts.map +0 -1
- package/billing/ModelGovernanceDetail.js.map +0 -1
- package/billing/OperatorAccessNotice.d.ts.map +0 -1
- package/billing/OperatorAccessNotice.js.map +0 -1
- package/billing/PricingGovernanceConsole.d.ts.map +0 -1
- package/billing/PricingGovernanceConsole.js.map +0 -1
- package/billing/PricingGovernancePanel.d.ts.map +0 -1
- package/billing/PricingGovernancePanel.js.map +0 -1
- package/billing/RetireConfirm.d.ts.map +0 -1
- package/billing/RetireConfirm.js.map +0 -1
- package/billing/form-primitives.d.ts +0 -18
- package/billing/form-primitives.d.ts.map +0 -1
- package/billing/form-primitives.js.map +0 -1
- package/billing/governance-primitives.d.ts.map +0 -1
- package/billing/governance-primitives.js.map +0 -1
- package/billing/pricing-format.d.ts.map +0 -1
- package/billing/pricing-format.js.map +0 -1
- package/billing/useDecidePricingOverride.d.ts.map +0 -1
- package/billing/useDecidePricingOverride.js.map +0 -1
- package/billing/useModelGovernanceView.d.ts.map +0 -1
- package/billing/useModelGovernanceView.js.map +0 -1
- package/billing/useModelPricingBaselines.d.ts.map +0 -1
- package/billing/useModelPricingBaselines.js.map +0 -1
- package/billing/usePricingGovernance.d.ts.map +0 -1
- package/billing/usePricingGovernance.js.map +0 -1
- package/billing/useRetireModelPricingBaseline.d.ts.map +0 -1
- package/billing/useRetireModelPricingBaseline.js.map +0 -1
- package/billing/useUpsertModelPricingBaseline.d.ts.map +0 -1
- package/billing/useUpsertModelPricingBaseline.js.map +0 -1
- /package/{billing → pricing-governance}/BaselineEditor.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelCatalogPanel.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelCatalogPanel.js +0 -0
- /package/{billing → pricing-governance}/ModelGovernanceDetail.d.ts +0 -0
- /package/{billing → pricing-governance}/ModelGovernanceDetail.js +0 -0
- /package/{billing → pricing-governance}/OperatorAccessNotice.d.ts +0 -0
- /package/{billing → pricing-governance}/OperatorAccessNotice.js +0 -0
- /package/{billing → pricing-governance}/PricingGovernanceConsole.d.ts +0 -0
- /package/{billing → pricing-governance}/PricingGovernancePanel.d.ts +0 -0
- /package/{billing → pricing-governance}/PricingGovernancePanel.js +0 -0
- /package/{billing → pricing-governance}/RetireConfirm.d.ts +0 -0
- /package/{billing → pricing-governance}/governance-primitives.d.ts +0 -0
- /package/{billing → pricing-governance}/governance-primitives.js +0 -0
- /package/{billing → pricing-governance}/pricing-format.d.ts +0 -0
- /package/{billing → pricing-governance}/pricing-format.js +0 -0
- /package/{billing → pricing-governance}/useDecidePricingOverride.d.ts +0 -0
- /package/{billing → pricing-governance}/useDecidePricingOverride.js +0 -0
- /package/{billing → pricing-governance}/useModelGovernanceView.d.ts +0 -0
- /package/{billing → pricing-governance}/useModelGovernanceView.js +0 -0
- /package/{billing → pricing-governance}/useModelPricingBaselines.d.ts +0 -0
- /package/{billing → pricing-governance}/useModelPricingBaselines.js +0 -0
- /package/{billing → pricing-governance}/usePricingGovernance.d.ts +0 -0
- /package/{billing → pricing-governance}/usePricingGovernance.js +0 -0
- /package/{billing → pricing-governance}/useRetireModelPricingBaseline.d.ts +0 -0
- /package/{billing → pricing-governance}/useRetireModelPricingBaseline.js +0 -0
- /package/{billing → pricing-governance}/useUpsertModelPricingBaseline.d.ts +0 -0
- /package/{billing → pricing-governance}/useUpsertModelPricingBaseline.js +0 -0
- /package/src/{billing → pricing-governance}/ModelCatalogPanel.tsx +0 -0
- /package/src/{billing → pricing-governance}/ModelGovernanceDetail.tsx +0 -0
- /package/src/{billing → pricing-governance}/OperatorAccessNotice.tsx +0 -0
- /package/src/{billing → pricing-governance}/PricingGovernancePanel.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/ModelCatalogPanel.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/PricingGovernanceConsole.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/__tests__/useModelGovernanceView.test.tsx +0 -0
- /package/src/{billing → pricing-governance}/governance-primitives.tsx +0 -0
- /package/src/{billing → pricing-governance}/pricing-format.ts +0 -0
- /package/src/{billing → pricing-governance}/useDecidePricingOverride.ts +0 -0
- /package/src/{billing → pricing-governance}/useModelGovernanceView.ts +0 -0
- /package/src/{billing → pricing-governance}/useModelPricingBaselines.ts +0 -0
- /package/src/{billing → pricing-governance}/usePricingGovernance.ts +0 -0
- /package/src/{billing → pricing-governance}/useRetireModelPricingBaseline.ts +0 -0
- /package/src/{billing → pricing-governance}/useUpsertModelPricingBaseline.ts +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { ResizableSplit } from "../internal/ResizableSplit.js";
|
|
6
|
+
|
|
7
|
+
/** Props for {@link SessionViewerLayout}. */
|
|
8
|
+
export interface SessionViewerLayoutProps {
|
|
9
|
+
/**
|
|
10
|
+
* The conversation column — the layout's primary (left) pane. Stays
|
|
11
|
+
* mounted at a stable tree position across every panel open/close, so
|
|
12
|
+
* streaming and scroll state survive the toggle.
|
|
13
|
+
*/
|
|
14
|
+
readonly conversation: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The session panel content, or `null` while the panel is collapsed.
|
|
17
|
+
* This is the single source of truth for the collapsed state: passing
|
|
18
|
+
* `null` collapses the split (the conversation fills the row) and hides
|
|
19
|
+
* the drag separator; passing content expands the panel into the
|
|
20
|
+
* flexible region. Hosts derive it from their panel controller, e.g.
|
|
21
|
+
* `panel.isOpen ? <WorkspaceSurface … /> : null`.
|
|
22
|
+
*/
|
|
23
|
+
readonly panel: ReactNode | null;
|
|
24
|
+
/**
|
|
25
|
+
* The panel's toggle chip, rendered in the layout's top-right corner
|
|
26
|
+
* (typically the session panel chip, {@link PanelChip}). Omit it to
|
|
27
|
+
* render no toggle — how hosts express audiences that must never open
|
|
28
|
+
* the panel (the guest case in {@link SessionViewer} and
|
|
29
|
+
* {@link NewSessionViewer}).
|
|
30
|
+
*/
|
|
31
|
+
readonly chip?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Host-injected header actions, rendered beside the chip in the
|
|
34
|
+
* top-right corner (slot injection, as {@link SessionViewerProps.headerActions}).
|
|
35
|
+
*/
|
|
36
|
+
readonly headerActions?: ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* `localStorage` key persisting the chat pane's drag-resized width
|
|
39
|
+
* across sessions. Each host passes its own key so the two shipped
|
|
40
|
+
* viewers keep distinct widths. **Omit it in deterministic hosts**
|
|
41
|
+
* (documentation embeds, video export): with no key the width always
|
|
42
|
+
* starts at the default, so every render of the same input is
|
|
43
|
+
* pixel-identical instead of depending on previously persisted state.
|
|
44
|
+
*/
|
|
45
|
+
readonly splitStorageKey?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the conversation pane collapses below the `lg` breakpoint
|
|
48
|
+
* while the panel is open, leaving the panel full-width — the shipped
|
|
49
|
+
* console behavior on narrow windows.
|
|
50
|
+
*
|
|
51
|
+
* Pass `false` in fixed-canvas hosts, where the media query is
|
|
52
|
+
* meaningless or actively wrong: a documentation embed renders in an
|
|
53
|
+
* iframe whose viewport is the docs column (below `lg` on most pages),
|
|
54
|
+
* so the default would hide the conversation on exactly the beats that
|
|
55
|
+
* open the panel.
|
|
56
|
+
*
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
readonly responsive?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Accessible label for the split's drag separator. Name the pane being
|
|
62
|
+
* resized in the host's vocabulary (the session viewer's chat pane vs
|
|
63
|
+
* the launcher's composer pane).
|
|
64
|
+
*
|
|
65
|
+
* @default "Resize chat panel"
|
|
66
|
+
*/
|
|
67
|
+
readonly resizeAriaLabel?: string;
|
|
68
|
+
/** Additional CSS classes for the root container. */
|
|
69
|
+
readonly className?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The one conversation-plus-panel frame every session viewer shares —
|
|
74
|
+
* {@link SessionViewer} and {@link NewSessionViewer} both render it, so the
|
|
75
|
+
* two surfaces cannot drift apart (their behave-identically contract), and
|
|
76
|
+
* platform builders composing a custom session surface from
|
|
77
|
+
* {@link WorkspaceSurface}, {@link PanelChip}, and the session facets get
|
|
78
|
+
* the same geometry the shipped console uses.
|
|
79
|
+
*
|
|
80
|
+
* The layout owns exactly three things:
|
|
81
|
+
*
|
|
82
|
+
* - **The top-right control corner** — the host's `headerActions` beside
|
|
83
|
+
* the panel toggle `chip`, absolutely positioned over the conversation.
|
|
84
|
+
* - **The split geometry** — chat fills the row while the panel is
|
|
85
|
+
* collapsed; opening makes the chat the fixed, drag-resizable pane
|
|
86
|
+
* (420px default, 320–640px) and hands the flexible region to the panel.
|
|
87
|
+
* - **The collapse mechanics** — collapse goes through the split's
|
|
88
|
+
* `collapsedPane` (CSS, not conditional structure), so the conversation
|
|
89
|
+
* is always the same first child and an open/close toggle never
|
|
90
|
+
* remounts it.
|
|
91
|
+
*
|
|
92
|
+
* Everything rendered inside the panes belongs to the host: the layout
|
|
93
|
+
* takes opaque `ReactNode`s and never inspects them.
|
|
94
|
+
*/
|
|
95
|
+
export function SessionViewerLayout({
|
|
96
|
+
conversation,
|
|
97
|
+
panel,
|
|
98
|
+
chip,
|
|
99
|
+
headerActions,
|
|
100
|
+
splitStorageKey,
|
|
101
|
+
responsive = true,
|
|
102
|
+
resizeAriaLabel = "Resize chat panel",
|
|
103
|
+
className,
|
|
104
|
+
}: SessionViewerLayoutProps) {
|
|
105
|
+
const isPanelOpen = panel != null;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className={cn("relative flex h-full w-full flex-col", className)}>
|
|
109
|
+
{(headerActions != null || chip != null) && (
|
|
110
|
+
<div className="absolute top-2 right-6 z-10 flex items-center gap-2">
|
|
111
|
+
{headerActions}
|
|
112
|
+
{chip}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
|
|
116
|
+
<ResizableSplit
|
|
117
|
+
resizablePane="primary"
|
|
118
|
+
collapsedPane={isPanelOpen ? "none" : "secondary"}
|
|
119
|
+
defaultSize={420}
|
|
120
|
+
minSize={320}
|
|
121
|
+
maxSize={640}
|
|
122
|
+
storageKey={splitStorageKey}
|
|
123
|
+
responsiveCollapse={responsive && isPanelOpen ? "primary" : "none"}
|
|
124
|
+
ariaLabel={resizeAriaLabel}
|
|
125
|
+
className="min-h-0 flex-1"
|
|
126
|
+
primary={conversation}
|
|
127
|
+
secondary={panel}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
3
|
+
import { SessionViewerLayout } from "../SessionViewerLayout";
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// The one conversation-plus-panel frame both shipped viewers render
|
|
7
|
+
// (SessionViewer + NewSessionViewer), so the two surfaces cannot drift.
|
|
8
|
+
// The contract under test:
|
|
9
|
+
// - `panel` is the single source of truth for collapsed state
|
|
10
|
+
// - the conversation never remounts across an open/close toggle
|
|
11
|
+
// - `responsive={false}` disables the below-`lg` conversation collapse
|
|
12
|
+
// (fixed-canvas hosts: docs embeds, video export)
|
|
13
|
+
// - no `splitStorageKey` means no localStorage read (deterministic hosts)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
cleanup();
|
|
18
|
+
vi.restoreAllMocks();
|
|
19
|
+
localStorage.clear();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function conversation() {
|
|
23
|
+
return <div data-testid="conversation">chat</div>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function panel() {
|
|
27
|
+
return <div data-testid="panel">panel</div>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("SessionViewerLayout", () => {
|
|
31
|
+
it("collapsed (panel null): conversation renders, panel absent, no separator exposed", () => {
|
|
32
|
+
render(<SessionViewerLayout conversation={conversation()} panel={null} />);
|
|
33
|
+
expect(screen.getByTestId("conversation").textContent).toBe("chat");
|
|
34
|
+
expect(screen.queryByTestId("panel")).toBeNull();
|
|
35
|
+
// The drag separator is CSS-hidden and aria-hidden while collapsed.
|
|
36
|
+
expect(screen.queryByRole("separator")).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("open (panel provided): panel renders and the separator is operable", () => {
|
|
40
|
+
render(<SessionViewerLayout conversation={conversation()} panel={panel()} />);
|
|
41
|
+
expect(screen.getByTestId("panel").textContent).toBe("panel");
|
|
42
|
+
const separator = screen.getByRole("separator", { name: "Resize chat panel" });
|
|
43
|
+
expect(separator.getAttribute("tabindex")).toBe("0");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("resizeAriaLabel names the separator in the host's vocabulary", () => {
|
|
47
|
+
render(
|
|
48
|
+
<SessionViewerLayout
|
|
49
|
+
conversation={conversation()}
|
|
50
|
+
panel={panel()}
|
|
51
|
+
resizeAriaLabel="Resize composer panel"
|
|
52
|
+
/>,
|
|
53
|
+
);
|
|
54
|
+
expect(
|
|
55
|
+
screen.queryByRole("separator", { name: "Resize composer panel" }),
|
|
56
|
+
).not.toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("never remounts the conversation across an open/close toggle", () => {
|
|
60
|
+
const { rerender } = render(
|
|
61
|
+
<SessionViewerLayout conversation={conversation()} panel={null} />,
|
|
62
|
+
);
|
|
63
|
+
const node = screen.getByTestId("conversation");
|
|
64
|
+
|
|
65
|
+
rerender(<SessionViewerLayout conversation={conversation()} panel={panel()} />);
|
|
66
|
+
expect(screen.getByTestId("conversation")).toBe(node);
|
|
67
|
+
|
|
68
|
+
rerender(<SessionViewerLayout conversation={conversation()} panel={null} />);
|
|
69
|
+
expect(screen.getByTestId("conversation")).toBe(node);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("open panel sizes the conversation pane at the shared default width", () => {
|
|
73
|
+
render(<SessionViewerLayout conversation={conversation()} panel={panel()} />);
|
|
74
|
+
const pane = screen.getByTestId("conversation").parentElement!;
|
|
75
|
+
expect(pane.style.width).toBe("420px");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("responsive (default): the open-panel conversation pane collapses below lg", () => {
|
|
79
|
+
const { container } = render(
|
|
80
|
+
<SessionViewerLayout conversation={conversation()} panel={panel()} />,
|
|
81
|
+
);
|
|
82
|
+
const pane = screen.getByTestId("conversation").parentElement!;
|
|
83
|
+
expect(pane.className).toContain("max-lg:hidden");
|
|
84
|
+
// The separator hides with it, so the panel is the full row below lg.
|
|
85
|
+
expect(container.querySelectorAll('[class*="max-lg:hidden"]').length).toBe(2);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("responsive={false}: no below-lg collapse anywhere (fixed-canvas hosts)", () => {
|
|
89
|
+
const { container } = render(
|
|
90
|
+
<SessionViewerLayout
|
|
91
|
+
conversation={conversation()}
|
|
92
|
+
panel={panel()}
|
|
93
|
+
responsive={false}
|
|
94
|
+
/>,
|
|
95
|
+
);
|
|
96
|
+
expect(container.querySelector('[class*="max-lg:hidden"]')).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("reads no localStorage without a splitStorageKey (deterministic hosts)", () => {
|
|
100
|
+
const getItem = vi.spyOn(Storage.prototype, "getItem");
|
|
101
|
+
render(<SessionViewerLayout conversation={conversation()} panel={panel()} />);
|
|
102
|
+
expect(getItem).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("seeds the pane width from the host's splitStorageKey when given", () => {
|
|
106
|
+
localStorage.setItem("stgm-test-chat-width", "512");
|
|
107
|
+
render(
|
|
108
|
+
<SessionViewerLayout
|
|
109
|
+
conversation={conversation()}
|
|
110
|
+
panel={panel()}
|
|
111
|
+
splitStorageKey="stgm-test-chat-width"
|
|
112
|
+
/>,
|
|
113
|
+
);
|
|
114
|
+
const pane = screen.getByTestId("conversation").parentElement!;
|
|
115
|
+
expect(pane.style.width).toBe("512px");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("renders headerActions beside the chip in the top-right corner", () => {
|
|
119
|
+
render(
|
|
120
|
+
<SessionViewerLayout
|
|
121
|
+
conversation={conversation()}
|
|
122
|
+
panel={null}
|
|
123
|
+
headerActions={<button type="button">Share</button>}
|
|
124
|
+
chip={<button type="button">Toggle panel</button>}
|
|
125
|
+
/>,
|
|
126
|
+
);
|
|
127
|
+
const actions = screen.getByRole("button", { name: "Share" });
|
|
128
|
+
const chip = screen.getByRole("button", { name: "Toggle panel" });
|
|
129
|
+
expect(actions.parentElement).toBe(chip.parentElement);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("renders no control corner when both chip and headerActions are omitted", () => {
|
|
133
|
+
const { container } = render(
|
|
134
|
+
<SessionViewerLayout conversation={conversation()} panel={null} />,
|
|
135
|
+
);
|
|
136
|
+
// The corner is the only `top-2`-positioned element in the layout (the
|
|
137
|
+
// separator's inner hit target is also `absolute`, so match the corner).
|
|
138
|
+
expect(container.querySelector('[class*="top-2"]')).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -193,6 +193,100 @@ describe("useCreateSession", () => {
|
|
|
193
193
|
expect(callArg.executionTarget).toBeUndefined();
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
describe("session metadata and sessionContext", () => {
|
|
197
|
+
it("passes the metadata map through to stigmer.session.create()", async () => {
|
|
198
|
+
const create = vi.fn().mockResolvedValue({ metadata: { id: "ses-md" } });
|
|
199
|
+
const client = createMockStigmer({ create });
|
|
200
|
+
|
|
201
|
+
const { result } = renderHook(() => useCreateSession(), {
|
|
202
|
+
wrapper: wrapper(client),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
await act(async () => {
|
|
206
|
+
await result.current.create({
|
|
207
|
+
org: "acme",
|
|
208
|
+
agentInstanceId: "ain-123",
|
|
209
|
+
metadata: { "acme/tenant": "t-1" },
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(create).toHaveBeenCalledWith(
|
|
214
|
+
expect.objectContaining({
|
|
215
|
+
metadata: { "acme/tenant": "t-1" },
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("maps the typed sessionContext onto the reserved metadata key", async () => {
|
|
221
|
+
const create = vi.fn().mockResolvedValue({ metadata: { id: "ses-sc" } });
|
|
222
|
+
const client = createMockStigmer({ create });
|
|
223
|
+
|
|
224
|
+
const { result } = renderHook(() => useCreateSession(), {
|
|
225
|
+
wrapper: wrapper(client),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await act(async () => {
|
|
229
|
+
await result.current.create({
|
|
230
|
+
org: "acme",
|
|
231
|
+
agentInstanceId: "ain-123",
|
|
232
|
+
sessionContext: "Role: platform admin",
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(create).toHaveBeenCalledWith(
|
|
237
|
+
expect.objectContaining({
|
|
238
|
+
metadata: { "stigmer.ai/session-context": "Role: platform admin" },
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("lets the typed sessionContext win over a raw entry under the reserved key", async () => {
|
|
244
|
+
const create = vi.fn().mockResolvedValue({ metadata: { id: "ses-both" } });
|
|
245
|
+
const client = createMockStigmer({ create });
|
|
246
|
+
|
|
247
|
+
const { result } = renderHook(() => useCreateSession(), {
|
|
248
|
+
wrapper: wrapper(client),
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await act(async () => {
|
|
252
|
+
await result.current.create({
|
|
253
|
+
org: "acme",
|
|
254
|
+
agentInstanceId: "ain-123",
|
|
255
|
+
metadata: {
|
|
256
|
+
"acme/tenant": "t-1",
|
|
257
|
+
"stigmer.ai/session-context": "stale raw value",
|
|
258
|
+
},
|
|
259
|
+
sessionContext: "typed value",
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(create).toHaveBeenCalledWith(
|
|
264
|
+
expect.objectContaining({
|
|
265
|
+
metadata: {
|
|
266
|
+
"acme/tenant": "t-1",
|
|
267
|
+
"stigmer.ai/session-context": "typed value",
|
|
268
|
+
},
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("omits metadata entirely when neither field is provided", async () => {
|
|
274
|
+
const create = vi.fn().mockResolvedValue({ metadata: { id: "ses-none" } });
|
|
275
|
+
const client = createMockStigmer({ create });
|
|
276
|
+
|
|
277
|
+
const { result } = renderHook(() => useCreateSession(), {
|
|
278
|
+
wrapper: wrapper(client),
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
await act(async () => {
|
|
282
|
+
await result.current.create({ org: "acme", agentInstanceId: "ain-123" });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const callArg = create.mock.calls[0][0];
|
|
286
|
+
expect(callArg.metadata).toBeUndefined();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
196
290
|
it("clearError resets the error state", async () => {
|
|
197
291
|
const create = vi.fn().mockRejectedValue(new Error("fail"));
|
|
198
292
|
const client = createMockStigmer({ create });
|
|
@@ -334,6 +334,39 @@ describe("useNewSessionFlow", () => {
|
|
|
334
334
|
expect(execInput.sessionSpec.harness).toBe("cursor");
|
|
335
335
|
});
|
|
336
336
|
|
|
337
|
+
it("forwards metadata and sessionContext verbatim in the sessionSpec", async () => {
|
|
338
|
+
// The typed-wins merge onto the reserved key happens downstream in
|
|
339
|
+
// useCreateAgentExecution (covered by its own tests); the flow's job
|
|
340
|
+
// is faithful forwarding.
|
|
341
|
+
const opts = {
|
|
342
|
+
...defaultOptions(),
|
|
343
|
+
metadata: { "acme/tenant": "t-1" },
|
|
344
|
+
sessionContext: "Role: platform admin",
|
|
345
|
+
};
|
|
346
|
+
const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
|
|
347
|
+
|
|
348
|
+
await act(async () => {
|
|
349
|
+
await result.current.submit("Hello");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const execInput = mockCreateExecution.mock.calls[0][0];
|
|
353
|
+
expect(execInput.sessionSpec.metadata).toEqual({ "acme/tenant": "t-1" });
|
|
354
|
+
expect(execInput.sessionSpec.sessionContext).toBe("Role: platform admin");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("leaves metadata and sessionContext undefined when not provided", async () => {
|
|
358
|
+
const opts = defaultOptions();
|
|
359
|
+
const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
|
|
360
|
+
|
|
361
|
+
await act(async () => {
|
|
362
|
+
await result.current.submit("Hello");
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const execInput = mockCreateExecution.mock.calls[0][0];
|
|
366
|
+
expect(execInput.sessionSpec.metadata).toBeUndefined();
|
|
367
|
+
expect(execInput.sessionSpec.sessionContext).toBeUndefined();
|
|
368
|
+
});
|
|
369
|
+
|
|
337
370
|
it("calls onSessionCreated with the server-assigned session id", async () => {
|
|
338
371
|
const opts = defaultOptions();
|
|
339
372
|
const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
|
package/src/session/index.ts
CHANGED
|
@@ -109,6 +109,12 @@ export type { SessionViewerProps } from "./SessionViewer.js";
|
|
|
109
109
|
export { NewSessionViewer } from "./NewSessionViewer.js";
|
|
110
110
|
export type { NewSessionViewerProps } from "./NewSessionViewer.js";
|
|
111
111
|
|
|
112
|
+
// SessionViewerLayout — the conversation-plus-panel frame both viewers
|
|
113
|
+
// share, exported for platform builders composing a custom session surface
|
|
114
|
+
// at the shipped console's geometry.
|
|
115
|
+
export { SessionViewerLayout } from "./SessionViewerLayout.js";
|
|
116
|
+
export type { SessionViewerLayoutProps } from "./SessionViewerLayout.js";
|
|
117
|
+
|
|
112
118
|
// Unified session-panel controller — drives the WorkspaceSurface open-editor
|
|
113
119
|
// group, the collapsed/open state, and the rail-view FSM (for platform
|
|
114
120
|
// builders embedding the surface directly).
|
|
@@ -118,6 +124,14 @@ export type {
|
|
|
118
124
|
UseSessionPanelOptions,
|
|
119
125
|
} from "./useSessionPanel.js";
|
|
120
126
|
|
|
127
|
+
// Session rail views — composes the session facets (Config / Changes /
|
|
128
|
+
// Artifacts / Usage) as the panel's rail, with the console's own labels,
|
|
129
|
+
// icons, badges, and contextual-visibility rules. Exported so custom
|
|
130
|
+
// session surfaces inherit the rail by construction instead of
|
|
131
|
+
// re-declaring it.
|
|
132
|
+
export { useSessionRailViews } from "./useSessionRailViews.js";
|
|
133
|
+
export type { UseSessionRailViewsOptions } from "./useSessionRailViews.js";
|
|
134
|
+
|
|
121
135
|
// Plan draft — viewer-owned in-place edit of the session's current plan
|
|
122
136
|
// (edit-as-input; the published plan.md artifact is never mutated).
|
|
123
137
|
export { usePlanDraft, planDraftKey } from "./usePlanDraft.js";
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import {
|
|
5
5
|
PENDING_SUBJECT,
|
|
6
|
+
mergeSessionContext,
|
|
6
7
|
type McpServerUsageInput,
|
|
7
8
|
type ResourceRef,
|
|
8
9
|
type WorkspaceEntryInput,
|
|
@@ -25,6 +26,34 @@ export interface SharedSessionFields {
|
|
|
25
26
|
readonly mcpServerUsages?: McpServerUsageInput[];
|
|
26
27
|
/** Skill references to enable for executions in this session. */
|
|
27
28
|
readonly skillRefs?: ResourceRef[];
|
|
29
|
+
/**
|
|
30
|
+
* Custom key-value pairs stored on `SessionSpec.metadata`.
|
|
31
|
+
*
|
|
32
|
+
* A passthrough for embedder-owned keys (correlation IDs, tenant tags)
|
|
33
|
+
* and for platform-reserved `stigmer.ai/*` keys set explicitly. For the
|
|
34
|
+
* common case — standing user context injected into the agent's prompt —
|
|
35
|
+
* prefer the typed {@link sessionContext} field, which maps onto the
|
|
36
|
+
* reserved `stigmer.ai/session-context` key and wins over a raw entry
|
|
37
|
+
* under that key when both are provided.
|
|
38
|
+
*/
|
|
39
|
+
readonly metadata?: Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* Standing, per-user context the agent receives on every turn but the
|
|
42
|
+
* conversation UI never renders — who the caller is, their experience
|
|
43
|
+
* level, their standing instructions (stigmer/stigmer#286).
|
|
44
|
+
*
|
|
45
|
+
* Stored on `SessionSpec.metadata` under `stigmer.ai/session-context`;
|
|
46
|
+
* the agent runner injects it into the system prompt as already-known
|
|
47
|
+
* background, so agents can greet the user by name and calibrate
|
|
48
|
+
* depth/defaults from the first turn without a visible context preamble.
|
|
49
|
+
*
|
|
50
|
+
* Personalization, not authorization: anyone who can create the session
|
|
51
|
+
* can set this (the same trust level as authoring the first message).
|
|
52
|
+
* Hidden from the conversation thread, not from the API — `session.get`
|
|
53
|
+
* returns it, so never put secrets here; secrets belong in `runtimeEnv`
|
|
54
|
+
* or Environment resources. Large values bloat every prompt.
|
|
55
|
+
*/
|
|
56
|
+
readonly sessionContext?: string;
|
|
28
57
|
/**
|
|
29
58
|
* Execution harness for this session.
|
|
30
59
|
*
|
|
@@ -172,6 +201,7 @@ export function useCreateSession(): UseCreateSessionReturn {
|
|
|
172
201
|
workspaceEntries: input.workspaceEntries,
|
|
173
202
|
mcpServerUsages: input.mcpServerUsages,
|
|
174
203
|
skillRefs: input.skillRefs,
|
|
204
|
+
metadata: mergeSessionContext(input.metadata, input.sessionContext),
|
|
175
205
|
agentInstanceId: resolvedInstanceId,
|
|
176
206
|
harness: input.harness ? toProtoHarness(input.harness) : undefined,
|
|
177
207
|
executionTarget: resolvedTarget
|
|
@@ -111,6 +111,36 @@ export interface UseNewSessionFlowOptions {
|
|
|
111
111
|
* @default "integrator"
|
|
112
112
|
*/
|
|
113
113
|
readonly audience?: SessionAudience;
|
|
114
|
+
/**
|
|
115
|
+
* Custom key-value pairs stored on the created session's
|
|
116
|
+
* `SessionSpec.metadata`.
|
|
117
|
+
*
|
|
118
|
+
* A passthrough for embedder-owned keys (correlation IDs, tenant tags)
|
|
119
|
+
* and for platform-reserved `stigmer.ai/*` keys set explicitly. For the
|
|
120
|
+
* common case — standing user context injected into the agent's prompt —
|
|
121
|
+
* prefer the typed {@link sessionContext} option, which maps onto the
|
|
122
|
+
* reserved `stigmer.ai/session-context` key and wins over a raw entry
|
|
123
|
+
* under that key when both are provided.
|
|
124
|
+
*/
|
|
125
|
+
readonly metadata?: Record<string, string>;
|
|
126
|
+
/**
|
|
127
|
+
* Standing, per-user context the agent receives on every turn but the
|
|
128
|
+
* conversation UI never renders — who the caller is, their experience
|
|
129
|
+
* level, their standing instructions (stigmer/stigmer#286).
|
|
130
|
+
*
|
|
131
|
+
* Stored on the created session's `SessionSpec.metadata` under
|
|
132
|
+
* `stigmer.ai/session-context`; the agent runner injects it into the
|
|
133
|
+
* system prompt as already-known background, so agents can greet the
|
|
134
|
+
* user by name and calibrate depth/defaults from the first turn without
|
|
135
|
+
* a visible context preamble.
|
|
136
|
+
*
|
|
137
|
+
* Personalization, not authorization: anyone who can create the session
|
|
138
|
+
* can set this (the same trust level as authoring the first message).
|
|
139
|
+
* Hidden from the conversation thread, not from the API — `session.get`
|
|
140
|
+
* returns it, so never put secrets here; secrets belong in `runtimeEnv`
|
|
141
|
+
* or Environment resources. Large values bloat every prompt.
|
|
142
|
+
*/
|
|
143
|
+
readonly sessionContext?: string;
|
|
114
144
|
}
|
|
115
145
|
|
|
116
146
|
/** Return value of {@link useNewSessionFlow}. */
|
|
@@ -217,7 +247,15 @@ export interface UseNewSessionFlowReturn {
|
|
|
217
247
|
export function useNewSessionFlow(
|
|
218
248
|
options: UseNewSessionFlowOptions,
|
|
219
249
|
): UseNewSessionFlowReturn {
|
|
220
|
-
const {
|
|
250
|
+
const {
|
|
251
|
+
org,
|
|
252
|
+
onSessionCreated,
|
|
253
|
+
onError,
|
|
254
|
+
getRuntimeEnv,
|
|
255
|
+
defaultHarness,
|
|
256
|
+
metadata,
|
|
257
|
+
sessionContext,
|
|
258
|
+
} = options;
|
|
221
259
|
const isGuest = options.audience === "guest";
|
|
222
260
|
const contextTarget = useExecutionTarget();
|
|
223
261
|
const executionTarget = options.executionTarget ?? contextTarget;
|
|
@@ -336,6 +374,10 @@ export function useNewSessionFlow(
|
|
|
336
374
|
: undefined,
|
|
337
375
|
mcpServerUsages: mcpServerUsages.length > 0 ? mcpServerUsages : undefined,
|
|
338
376
|
skillRefs: skillRefs.length > 0 ? skillRefs : undefined,
|
|
377
|
+
// The typed-wins merge happens downstream in useCreateAgentExecution;
|
|
378
|
+
// both fields are forwarded verbatim here.
|
|
379
|
+
metadata,
|
|
380
|
+
sessionContext,
|
|
339
381
|
harness,
|
|
340
382
|
executionTarget,
|
|
341
383
|
};
|
|
@@ -440,6 +482,8 @@ export function useNewSessionFlow(
|
|
|
440
482
|
workspace,
|
|
441
483
|
mcpServerUsages,
|
|
442
484
|
skillRefs,
|
|
485
|
+
metadata,
|
|
486
|
+
sessionContext,
|
|
443
487
|
agentRef,
|
|
444
488
|
resolution,
|
|
445
489
|
defaultAgent,
|
|
@@ -55,13 +55,13 @@ export interface UseSessionRailViewsOptions {
|
|
|
55
55
|
* (a cloud session with git workspace entries). When `true` the Changes
|
|
56
56
|
* facet is offered from the start — with an honest pre-push state — instead
|
|
57
57
|
* of only materializing once a write-back exists, so "where does my work
|
|
58
|
-
* go?" always has an answer
|
|
58
|
+
* go?" always has an answer (the facet's `expectsWriteBack` prop).
|
|
59
59
|
* @default false
|
|
60
60
|
*/
|
|
61
61
|
readonly expectsWriteBack?: boolean;
|
|
62
62
|
/**
|
|
63
63
|
* Whether the session's latest execution is settled (terminal) — forwarded
|
|
64
|
-
* to the Changes facet's pre-push states
|
|
64
|
+
* to the Changes facet's pre-push states (its `isSettled` prop).
|
|
65
65
|
* @default false
|
|
66
66
|
*/
|
|
67
67
|
readonly changesSettled?: boolean;
|
|
@@ -24,28 +24,47 @@ export interface UseUpdateSessionReturn {
|
|
|
24
24
|
* {@link SessionInput} including the session's `name` and `org` (used
|
|
25
25
|
* by the backend to identify the resource) and all spec fields.
|
|
26
26
|
*
|
|
27
|
+
* **Full-replace semantics.** The backend replaces the entire spec with
|
|
28
|
+
* what you send — there is no field mask. Round-trip every spec field
|
|
29
|
+
* you do not intend to change, *including `metadata`*: an update built
|
|
30
|
+
* from a reconstructed partial spec silently wipes the fields it omits
|
|
31
|
+
* (workspace entries, MCP servers, and `stigmer.ai/*` metadata keys such
|
|
32
|
+
* as the embedder session context). To change only the subject, prefer
|
|
33
|
+
* the field-level `session.updateSubject()` RPC instead of this hook.
|
|
34
|
+
*
|
|
27
35
|
* Typically composed into higher-level hooks like
|
|
28
|
-
* {@link useSessionConversation}
|
|
29
|
-
*
|
|
30
|
-
* standalone.
|
|
36
|
+
* {@link useSessionConversation} (whose update path round-trips the
|
|
37
|
+
* fetched spec) rather than used directly. Platform builders who need
|
|
38
|
+
* direct session mutation can use this hook standalone.
|
|
31
39
|
*
|
|
32
40
|
* @example
|
|
33
41
|
* ```tsx
|
|
34
|
-
* function
|
|
35
|
-
* const { update, isUpdating
|
|
42
|
+
* function TagSession({ session }: { session: Session }) {
|
|
43
|
+
* const { update, isUpdating } = useUpdateSession();
|
|
36
44
|
*
|
|
37
|
-
* async function
|
|
45
|
+
* async function handleTag() {
|
|
46
|
+
* const spec = session.spec;
|
|
38
47
|
* await update({
|
|
39
48
|
* name: session.metadata!.name,
|
|
40
49
|
* org: session.metadata!.org,
|
|
41
|
-
*
|
|
42
|
-
*
|
|
50
|
+
* // Full replace: carry over every fetched spec field...
|
|
51
|
+
* agentInstanceId: spec?.agentInstanceId,
|
|
52
|
+
* subject: spec?.subject,
|
|
53
|
+
* harnessStateId: spec?.harnessStateId,
|
|
54
|
+
* harness: spec?.harness,
|
|
55
|
+
* cursorMode: spec?.cursorMode,
|
|
56
|
+
* executionTarget: spec?.executionTarget,
|
|
57
|
+
* // ...including the spec lists (workspaceEntries, mcpServerUsages,
|
|
58
|
+
* // skillRefs), converted from the fetched proto exactly as
|
|
59
|
+
* // useSessionConversation's update path does...
|
|
60
|
+
* // ...then apply the one change on top of the carried metadata.
|
|
61
|
+
* metadata: { ...spec?.metadata, "acme/reviewed": "true" },
|
|
43
62
|
* });
|
|
44
63
|
* }
|
|
45
64
|
*
|
|
46
65
|
* return (
|
|
47
|
-
* <button onClick={
|
|
48
|
-
*
|
|
66
|
+
* <button onClick={handleTag} disabled={isUpdating}>
|
|
67
|
+
* Mark reviewed
|
|
49
68
|
* </button>
|
|
50
69
|
* );
|
|
51
70
|
* }
|
|
@@ -17,6 +17,8 @@ export interface UseSkillCountOptions {
|
|
|
17
17
|
* @default "org"
|
|
18
18
|
*/
|
|
19
19
|
readonly scope?: ResourceListScope;
|
|
20
|
+
/** Opaque token that forces a recount when its value changes. */
|
|
21
|
+
readonly refetchToken?: unknown;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/** Return value of {@link useSkillCount}. */
|