@stigmer/react 3.12.7 → 3.12.9
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 -3
- package/composer/ComposerToolbar.d.ts +1 -19
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +2 -7
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +0 -10
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +2 -2
- package/composer/SessionComposer.js.map +1 -1
- package/composer/index.d.ts +0 -1
- package/composer/index.d.ts.map +1 -1
- package/composer/index.js.map +1 -1
- package/conversation/ConversationTimelineView.d.ts +15 -1
- package/conversation/ConversationTimelineView.d.ts.map +1 -1
- package/conversation/ConversationTimelineView.js +3 -2
- package/conversation/ConversationTimelineView.js.map +1 -1
- package/conversation/ConversationsWorkbench.d.ts +14 -1
- package/conversation/ConversationsWorkbench.d.ts.map +1 -1
- package/conversation/ConversationsWorkbench.js +11 -2
- package/conversation/ConversationsWorkbench.js.map +1 -1
- package/execution/MessageThread.d.ts +33 -2
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +44 -6
- package/execution/MessageThread.js.map +1 -1
- package/execution/RecalledMemoriesCard.d.ts +47 -0
- package/execution/RecalledMemoriesCard.d.ts.map +1 -0
- package/execution/RecalledMemoriesCard.js +58 -0
- package/execution/RecalledMemoriesCard.js.map +1 -0
- package/execution/ResultView.d.ts.map +1 -1
- package/execution/ResultView.js +6 -0
- package/execution/ResultView.js.map +1 -1
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +13 -2
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/index.d.ts +2 -0
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +1 -0
- package/execution/index.js.map +1 -1
- package/execution/tool-categories.d.ts +1 -1
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +6 -0
- package/execution/tool-categories.js.map +1 -1
- package/identity-account/AccountPreferencesPanel.d.ts +2 -1
- package/identity-account/AccountPreferencesPanel.d.ts.map +1 -1
- package/identity-account/AccountPreferencesPanel.js +41 -3
- package/identity-account/AccountPreferencesPanel.js.map +1 -1
- package/identity-account/useAccountExecutionDefaults.d.ts +7 -0
- package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -1
- package/identity-account/useAccountExecutionDefaults.js +5 -2
- package/identity-account/useAccountExecutionDefaults.js.map +1 -1
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +2 -2
- package/index.js.map +1 -1
- package/internal/MemoryEnabledRow.d.ts +37 -0
- package/internal/MemoryEnabledRow.d.ts.map +1 -0
- package/internal/MemoryEnabledRow.js +25 -0
- package/internal/MemoryEnabledRow.js.map +1 -0
- package/internal/VirtualizedThread.d.ts +3 -1
- package/internal/VirtualizedThread.d.ts.map +1 -1
- package/internal/VirtualizedThread.js +3 -1
- package/internal/VirtualizedThread.js.map +1 -1
- package/internal/useAutoScroll.d.ts +18 -0
- package/internal/useAutoScroll.d.ts.map +1 -1
- package/internal/useAutoScroll.js +50 -0
- package/internal/useAutoScroll.js.map +1 -1
- package/memory/MemoryProposalCard.d.ts +35 -0
- package/memory/MemoryProposalCard.d.ts.map +1 -0
- package/memory/MemoryProposalCard.js +73 -0
- package/memory/MemoryProposalCard.js.map +1 -0
- package/memory/index.d.ts +2 -0
- package/memory/index.d.ts.map +1 -1
- package/memory/index.js +2 -0
- package/memory/index.js.map +1 -1
- package/memory/useMemory.d.ts +39 -0
- package/memory/useMemory.d.ts.map +1 -0
- package/memory/useMemory.js +43 -0
- package/memory/useMemory.js.map +1 -0
- package/organization/OrgPreferencesPanel.d.ts.map +1 -1
- package/organization/OrgPreferencesPanel.js +36 -4
- package/organization/OrgPreferencesPanel.js.map +1 -1
- package/package.json +4 -4
- package/session/AutoApproveIndicator.d.ts +18 -0
- package/session/AutoApproveIndicator.d.ts.map +1 -0
- package/session/AutoApproveIndicator.js +23 -0
- package/session/AutoApproveIndicator.js.map +1 -0
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +10 -9
- package/session/NewSessionViewer.js.map +1 -1
- package/session/SessionViewer.d.ts +21 -9
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +31 -21
- package/session/SessionViewer.js.map +1 -1
- package/session/facets/SetupTab.d.ts +43 -6
- package/session/facets/SetupTab.d.ts.map +1 -1
- package/session/facets/SetupTab.js +32 -36
- package/session/facets/SetupTab.js.map +1 -1
- package/session/facets/UsageTab.d.ts +2 -1
- package/session/facets/UsageTab.d.ts.map +1 -1
- package/session/facets/UsageTab.js +11 -5
- package/session/facets/UsageTab.js.map +1 -1
- package/session/facets/index.d.ts +1 -1
- package/session/facets/index.d.ts.map +1 -1
- package/session/facets/primitives.d.ts +58 -0
- package/session/facets/primitives.d.ts.map +1 -0
- package/session/facets/primitives.js +59 -0
- package/session/facets/primitives.js.map +1 -0
- package/session/index.d.ts +1 -1
- package/session/index.d.ts.map +1 -1
- package/session/index.js.map +1 -1
- package/session/useNewSessionFlow.d.ts +9 -4
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +20 -9
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionPageFlow.d.ts +28 -10
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +16 -10
- package/session/useSessionPageFlow.js.map +1 -1
- package/settings/MemorySection.d.ts.map +1 -1
- package/settings/MemorySection.js +1 -1
- package/settings/MemorySection.js.map +1 -1
- package/src/attachment/__tests__/prepare-image.test.ts +12 -6
- package/src/composer/ComposerToolbar.tsx +0 -57
- package/src/composer/SessionComposer.tsx +1 -13
- package/src/composer/index.ts +0 -1
- package/src/conversation/ConversationTimelineView.tsx +17 -1
- package/src/conversation/ConversationsWorkbench.tsx +25 -0
- package/src/conversation/__tests__/ConversationTimelineView.layout.test.tsx +37 -1
- package/src/execution/MessageThread.tsx +75 -3
- package/src/execution/RecalledMemoriesCard.tsx +164 -0
- package/src/execution/ResultView.tsx +6 -0
- package/src/execution/ToolCallItem.tsx +18 -1
- package/src/execution/__tests__/RecalledMemoriesCard.test.tsx +123 -0
- package/src/execution/__tests__/message-thread-recalled-memories.test.tsx +216 -0
- package/src/execution/__tests__/message-thread-scroll-on-send.test.tsx +141 -0
- package/src/execution/__tests__/message-thread-slots.test.tsx +36 -0
- package/src/execution/index.ts +2 -0
- package/src/execution/tool-categories.ts +7 -0
- package/src/identity-account/AccountPreferencesPanel.tsx +89 -2
- package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +112 -0
- package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +34 -1
- package/src/identity-account/useAccountExecutionDefaults.ts +12 -2
- package/src/index.ts +8 -1
- package/src/internal/MemoryEnabledRow.tsx +92 -0
- package/src/internal/VirtualizedThread.tsx +5 -0
- package/src/internal/__tests__/useAutoScroll.layout.test.tsx +105 -4
- package/src/internal/__tests__/useAutoScroll.test.tsx +41 -1
- package/src/internal/useAutoScroll.ts +54 -0
- package/src/memory/MemoryProposalCard.tsx +181 -0
- package/src/memory/__tests__/MemoryProposalCard.test.tsx +236 -0
- package/src/memory/__tests__/memoryHooks.test.tsx +63 -0
- package/src/memory/index.ts +5 -0
- package/src/memory/useMemory.ts +80 -0
- package/src/organization/OrgPreferencesPanel.tsx +52 -3
- package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +65 -0
- package/src/session/AutoApproveIndicator.tsx +45 -0
- package/src/session/NewSessionViewer.tsx +18 -15
- package/src/session/SessionViewer.tsx +70 -32
- package/src/session/__tests__/NewSessionViewer.test.tsx +32 -0
- package/src/session/__tests__/SessionViewer-autoApprove.test.tsx +244 -0
- package/src/session/__tests__/SessionViewer-transcriptExport.test.tsx +60 -22
- package/src/session/__tests__/useNewSessionFlow.test.tsx +56 -0
- package/src/session/__tests__/useSessionPageFlow.autoApprove.test.tsx +87 -0
- package/src/session/facets/SetupTab.tsx +193 -157
- package/src/session/facets/UsageTab.tsx +38 -38
- package/src/session/facets/index.ts +1 -0
- package/src/session/facets/primitives.tsx +169 -0
- package/src/session/index.ts +1 -0
- package/src/session/useNewSessionFlow.ts +30 -15
- package/src/session/useSessionPageFlow.ts +44 -20
- package/src/settings/MemorySection.tsx +4 -2
- package/src/switch/Switch.tsx +4 -0
- package/src/workflow/__tests__/WorkflowExecutionViewer.approvals.test.tsx +26 -7
- package/src/workflow/__tests__/task-presentation.test.ts +49 -12
- package/src/workflow/__tests__/useWorkflowCanvas.dirty.test.tsx +7 -2
- package/src/workflow/__tests__/useWorkflowCanvas.drag.test.tsx +7 -2
- package/src/workflow/__tests__/workflow-thread-scroll-on-send.test.tsx +145 -0
- package/src/workflow/thread/WorkflowTaskThread.tsx +59 -32
- package/src/workflow/thread/task-presentation.ts +52 -7
- package/src/workspace/WorkspaceSurface.tsx +26 -3
- package/styles.css +1 -1
- package/switch/Switch.d.ts +3 -1
- package/switch/Switch.d.ts.map +1 -1
- package/switch/Switch.js +2 -2
- package/switch/Switch.js.map +1 -1
- package/workflow/thread/WorkflowTaskThread.d.ts +13 -0
- package/workflow/thread/WorkflowTaskThread.d.ts.map +1 -1
- package/workflow/thread/WorkflowTaskThread.js +33 -25
- package/workflow/thread/WorkflowTaskThread.js.map +1 -1
- package/workflow/thread/task-presentation.d.ts.map +1 -1
- package/workflow/thread/task-presentation.js +50 -7
- package/workflow/thread/task-presentation.js.map +1 -1
- package/workspace/WorkspaceSurface.js +11 -2
- package/workspace/WorkspaceSurface.js.map +1 -1
- package/src/composer/__tests__/ComposerToolbar-autoApprove.test.tsx +0 -80
- package/src/session/__tests__/SessionViewer-autoApproveToggle.test.tsx +0 -178
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Shared visual vocabulary for the session-panel facets (Config / Changes /
|
|
4
|
+
// Artifacts / Usage): one dense, VS Code-style row-and-section language so the
|
|
5
|
+
// facets read as one surface instead of three eras of styling. The Artifacts
|
|
6
|
+
// facet's `ArtifactRowView` set the idiom — compact rows, quiet metadata,
|
|
7
|
+
// hover/focus-revealed actions — and these primitives carry it to the
|
|
8
|
+
// non-file-list facets. All visual properties flow through `--stgm-*`
|
|
9
|
+
// tokens (DD-005).
|
|
10
|
+
|
|
11
|
+
import { cn } from "@stigmer/theme";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A titled facet section: uppercase micro-heading over flush content.
|
|
15
|
+
* `count` renders the "(N)" suffix the retired inspector used; `annotation`
|
|
16
|
+
* renders a normal-case, muted trailing note (e.g. "next message only").
|
|
17
|
+
*/
|
|
18
|
+
export function FacetSection({
|
|
19
|
+
heading,
|
|
20
|
+
count,
|
|
21
|
+
annotation,
|
|
22
|
+
children,
|
|
23
|
+
}: {
|
|
24
|
+
readonly heading: string;
|
|
25
|
+
readonly count?: number;
|
|
26
|
+
readonly annotation?: string;
|
|
27
|
+
readonly children: React.ReactNode;
|
|
28
|
+
}) {
|
|
29
|
+
return (
|
|
30
|
+
<section className="stg:flex stg:flex-col stg:gap-1">
|
|
31
|
+
<h3 className="stg:px-2 stg:text-[0.65rem] stg:font-semibold stg:uppercase stg:tracking-wider stg:text-muted-foreground">
|
|
32
|
+
{heading}
|
|
33
|
+
{count != null && count > 0 && (
|
|
34
|
+
<span className="stg:ml-1 stg:text-muted-foreground-faint">({count})</span>
|
|
35
|
+
)}
|
|
36
|
+
{annotation && (
|
|
37
|
+
<span className="stg:ml-1 stg:font-normal stg:normal-case stg:tracking-normal stg:text-muted-foreground-faint">
|
|
38
|
+
({annotation})
|
|
39
|
+
</span>
|
|
40
|
+
)}
|
|
41
|
+
</h3>
|
|
42
|
+
{children}
|
|
43
|
+
</section>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One dense, non-interactive facet row: leading content, optional
|
|
49
|
+
* right-aligned metadata, and hover/focus-revealed trailing actions
|
|
50
|
+
* (the `group` the actions' reveal keys on). Interactive rows (whole-row
|
|
51
|
+
* buttons) build on {@link FACET_ROW_BUTTON} instead.
|
|
52
|
+
*/
|
|
53
|
+
export function FacetRow({
|
|
54
|
+
children,
|
|
55
|
+
meta,
|
|
56
|
+
actions,
|
|
57
|
+
className,
|
|
58
|
+
}: {
|
|
59
|
+
readonly children: React.ReactNode;
|
|
60
|
+
/** Right-aligned quiet metadata (counts, sizes, values). */
|
|
61
|
+
readonly meta?: React.ReactNode;
|
|
62
|
+
/** Hover/focus-revealed trailing controls — siblings, never nested. */
|
|
63
|
+
readonly actions?: React.ReactNode;
|
|
64
|
+
readonly className?: string;
|
|
65
|
+
}) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
className={cn(
|
|
69
|
+
"stg:group stg:flex stg:items-center stg:gap-2 stg:px-2 stg:py-1 stg:text-xs",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
<span className="stg:flex stg:min-w-0 stg:flex-1 stg:items-center stg:gap-2 stg:text-foreground">
|
|
74
|
+
{children}
|
|
75
|
+
</span>
|
|
76
|
+
{meta && (
|
|
77
|
+
<span className="stg:shrink-0 stg:text-[0.65rem] stg:text-muted-foreground">
|
|
78
|
+
{meta}
|
|
79
|
+
</span>
|
|
80
|
+
)}
|
|
81
|
+
{actions}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Class string for a whole-row interactive facet action (the
|
|
88
|
+
* `ArtifactRowView` open-button idiom): dense row metrics plus hover and
|
|
89
|
+
* focus-visible treatments. A string constant (not a component) so callers
|
|
90
|
+
* keep their own element type and extra classes.
|
|
91
|
+
*/
|
|
92
|
+
export const FACET_ROW_BUTTON = cn(
|
|
93
|
+
"stg:flex stg:w-full stg:min-w-0 stg:items-center stg:gap-2 stg:px-2 stg:py-1 stg:text-left stg:text-xs stg:text-muted-foreground stg:transition-colors",
|
|
94
|
+
"stg:hover:bg-muted stg:hover:text-foreground",
|
|
95
|
+
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
|
|
96
|
+
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Hover/focus-revealed remove control for a {@link FacetRow} — the
|
|
101
|
+
* `ArtifactRowView` download-button reveal pattern applied to removal.
|
|
102
|
+
* Always reachable by keyboard (focus reveals it without hover).
|
|
103
|
+
*/
|
|
104
|
+
export function FacetRemoveButton({
|
|
105
|
+
onClick,
|
|
106
|
+
label,
|
|
107
|
+
}: {
|
|
108
|
+
readonly onClick: () => void;
|
|
109
|
+
readonly label: string;
|
|
110
|
+
}) {
|
|
111
|
+
return (
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={onClick}
|
|
115
|
+
aria-label={label}
|
|
116
|
+
className={cn(
|
|
117
|
+
"stg:flex stg:shrink-0 stg:items-center stg:text-muted-foreground stg:opacity-0 stg:transition-opacity",
|
|
118
|
+
"stg:group-hover:opacity-100 stg:focus-visible:opacity-100",
|
|
119
|
+
"stg:hover:text-destructive",
|
|
120
|
+
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
<FacetXIcon />
|
|
124
|
+
</button>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Muted empty-state line for a facet section with nothing to list. */
|
|
129
|
+
export function FacetEmptyHint({ children }: { readonly children: React.ReactNode }) {
|
|
130
|
+
return (
|
|
131
|
+
<p className="stg:px-2 stg:text-xs stg:text-muted-foreground-faint">{children}</p>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A key→value config row ("Harness Native"): muted label left, value
|
|
137
|
+
* right — the dense inverse of a definition list, sized like every other
|
|
138
|
+
* facet row. `children` is the value; pass a control (e.g. a Switch) for
|
|
139
|
+
* interactive config rows.
|
|
140
|
+
*/
|
|
141
|
+
export function FacetKeyValueRow({
|
|
142
|
+
label,
|
|
143
|
+
labelId,
|
|
144
|
+
children,
|
|
145
|
+
}: {
|
|
146
|
+
readonly label: string;
|
|
147
|
+
/** Optional id for the label span — point a control's `aria-labelledby` here. */
|
|
148
|
+
readonly labelId?: string;
|
|
149
|
+
readonly children: React.ReactNode;
|
|
150
|
+
}) {
|
|
151
|
+
return (
|
|
152
|
+
<div className="stg:flex stg:items-center stg:justify-between stg:gap-2 stg:px-2 stg:py-1 stg:text-xs">
|
|
153
|
+
<span id={labelId} className="stg:shrink-0 stg:text-muted-foreground">
|
|
154
|
+
{label}
|
|
155
|
+
</span>
|
|
156
|
+
<span className="stg:flex stg:min-w-0 stg:items-center stg:gap-1.5 stg:truncate stg:text-foreground">
|
|
157
|
+
{children}
|
|
158
|
+
</span>
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function FacetXIcon() {
|
|
164
|
+
return (
|
|
165
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
166
|
+
<path d="M4 4L10 10M10 4L4 10" />
|
|
167
|
+
</svg>
|
|
168
|
+
);
|
|
169
|
+
}
|
package/src/session/index.ts
CHANGED
|
@@ -116,6 +116,9 @@ export interface UseNewSessionFlowOptions {
|
|
|
116
116
|
* - model: stored per-harness choice > the account's model for the
|
|
117
117
|
* active harness (validated against the registry; a stale model
|
|
118
118
|
* silently falls through) > the harness default.
|
|
119
|
+
* - autoApprove: explicit in-session flip > the account's
|
|
120
|
+
* `default_auto_approve` > the host's `approvalDefaults` (derived,
|
|
121
|
+
* so a late-arriving value applies without a touched-ref effect).
|
|
119
122
|
*
|
|
120
123
|
* Seeded values are never persisted to localStorage — the account
|
|
121
124
|
* preference keeps applying until the user decides. Ignored for the
|
|
@@ -221,10 +224,12 @@ export interface UseNewSessionFlowReturn {
|
|
|
221
224
|
/**
|
|
222
225
|
* Pre-arm "auto-approve tool calls" for the session this surface will
|
|
223
226
|
* create (stigmer/stigmer#816, the walk-away scenario). Seeded from the
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
227
|
+
* account's `default_auto_approve` preference and the host's
|
|
228
|
+
* `StigmerProvider` `approvalDefaults` (#302) — guests inherit neither;
|
|
229
|
+
* the user's explicit flip (the Config facet switch) wins from then on.
|
|
230
|
+
* Carried into the bootstrap create as `spec.auto_approve_all` — the
|
|
231
|
+
* whole-run server-side bypass — so the first run stays covered even if
|
|
232
|
+
* the user closes the tab.
|
|
228
233
|
*/
|
|
229
234
|
readonly autoApproveAll: boolean;
|
|
230
235
|
/** Set the user's explicit auto-approve choice for the created session. */
|
|
@@ -323,18 +328,28 @@ export function useNewSessionFlow(
|
|
|
323
328
|
const contextTarget = useExecutionTarget();
|
|
324
329
|
const executionTarget = options.executionTarget ?? contextTarget;
|
|
325
330
|
const adapter = useRunnerAdapter();
|
|
326
|
-
// Auto-approve for the session about to be created (#816):
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
331
|
+
// Auto-approve for the session about to be created (#816): derived like
|
|
332
|
+
// useSessionPageFlow's — the user's explicit flip wins, otherwise the
|
|
333
|
+
// truth is computed from its seeds:
|
|
334
|
+
//
|
|
335
|
+
// effective = userChoice ?? (accountSeed || hostSeed)
|
|
336
|
+
//
|
|
337
|
+
// Derived rather than useState-initialized because the account preference
|
|
338
|
+
// (default_auto_approve) resolves async with whoAmI — a mount-time
|
|
339
|
+
// initializer would silently miss it (the harness seed's late-arrival
|
|
340
|
+
// problem, solved here by derivation instead of a touched-ref effect).
|
|
341
|
+
// Guests inherit neither seed — a share-link visitor is not the operator
|
|
342
|
+
// either trust judgment covers (the GUEST_HARNESS fixed-platform-policy
|
|
343
|
+
// reasoning). Carried into the bootstrap create as spec.auto_approve_all,
|
|
344
|
+
// the whole-run server-side bypass — the created run stays covered even
|
|
345
|
+
// if this tab dies before its first gate.
|
|
334
346
|
const approvalDefaults = useApprovalDefaults();
|
|
335
|
-
const [
|
|
336
|
-
|
|
337
|
-
|
|
347
|
+
const [autoApproveChoice, setAutoApproveAll] = useState<boolean | null>(null);
|
|
348
|
+
const autoApproveAll =
|
|
349
|
+
autoApproveChoice ??
|
|
350
|
+
(!isGuest &&
|
|
351
|
+
((accountDefaults?.autoApprove ?? false) ||
|
|
352
|
+
(approvalDefaults?.autoApproveAll ?? false)));
|
|
338
353
|
|
|
339
354
|
const [harness, setHarnessRaw] = useState<HarnessOption>(() => {
|
|
340
355
|
// Guests get the fixed platform policy (see GUEST_HARNESS) and never
|
|
@@ -22,6 +22,7 @@ import { toSessionUpdateInput } from "@stigmer/sdk";
|
|
|
22
22
|
import type { SessionAudience } from "./audience.js";
|
|
23
23
|
import { isChannelOriginSession } from "./channelOrigin.js";
|
|
24
24
|
import { assertValidRunConfig, type SessionRunConfig } from "./run-config.js";
|
|
25
|
+
import type { AccountExecutionDefaults } from "../identity-account/useAccountExecutionDefaults.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Well-known Daytona sandbox workspace root. Used as the SDK safety-net
|
|
@@ -69,6 +70,18 @@ export interface UseSessionPageFlowOptions {
|
|
|
69
70
|
* config). See {@link SessionRunConfig}.
|
|
70
71
|
*/
|
|
71
72
|
readonly runConfig?: SessionRunConfig;
|
|
73
|
+
/**
|
|
74
|
+
* The user's account-level execution defaults, typically from
|
|
75
|
+
* `useAccountExecutionDefaults()`. On the session page only
|
|
76
|
+
* `autoApprove` applies — it seeds the session-scoped auto-approve
|
|
77
|
+
* state so a user whose `default_auto_approve` preference is ON gets
|
|
78
|
+
* armed follow-ups on every session, not just newly created ones. An
|
|
79
|
+
* explicit in-session flip always wins for this session; the seed may
|
|
80
|
+
* arrive after mount (whoAmI resolves async) and still applies, being
|
|
81
|
+
* part of the derivation rather than an initializer. Ignored for the
|
|
82
|
+
* `"guest"` audience (guests inherit no identity-derived preference).
|
|
83
|
+
*/
|
|
84
|
+
readonly accountDefaults?: AccountExecutionDefaults;
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
/** Return value of {@link useSessionPageFlow}. */
|
|
@@ -146,28 +159,33 @@ export interface UseSessionPageFlowReturn {
|
|
|
146
159
|
* `false` by default. `true` when any of its sources arms it — the user's
|
|
147
160
|
* explicit choice always winning:
|
|
148
161
|
*
|
|
149
|
-
* 1. the user's
|
|
162
|
+
* 1. the user's Config facet switch ({@link setAutoApproveAll});
|
|
150
163
|
* 2. "Approve & don't ask again" at an approval gate (see
|
|
151
164
|
* {@link submitApproval});
|
|
152
|
-
* 3. the
|
|
165
|
+
* 3. the account's `default_auto_approve` preference
|
|
166
|
+
* (`UseSessionPageFlowOptions.accountDefaults`, guests excluded);
|
|
167
|
+
* 4. the host's app-wide `StigmerProvider` `approvalDefaults` (#302,
|
|
153
168
|
* guests excluded);
|
|
154
|
-
*
|
|
169
|
+
* 5. the active in-flight execution having been created with
|
|
155
170
|
* `spec.auto_approve_all` (e.g. armed on the new-session surface), so
|
|
156
171
|
* the state survives the launcher → session-page handoff.
|
|
157
172
|
*
|
|
158
173
|
* While `true`, follow-ups carry `auto_approve_all` ({@link handleSubmit})
|
|
159
174
|
* and gates appearing in the in-flight run are auto-released (never for
|
|
160
|
-
* guest/observer/channel-origin surfaces).
|
|
161
|
-
* reload back to the
|
|
175
|
+
* guest/observer/channel-origin surfaces). The explicit per-session flip is
|
|
176
|
+
* held in memory only — reset on reload back to the seeds; the account
|
|
177
|
+
* preference is the one persisted default, declared by the user in
|
|
178
|
+
* Account Preferences.
|
|
162
179
|
*/
|
|
163
180
|
readonly autoApproveAll: boolean;
|
|
164
181
|
/**
|
|
165
182
|
* Set the user's explicit session-scoped auto-approve choice. Wired to the
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
183
|
+
* Config facet's switch; an explicit `false` wins over both seeds and over
|
|
184
|
+
* an armed in-flight run for everything the client controls (follow-up
|
|
185
|
+
* carry, gate auto-release) — an already-armed run's server-side bypass
|
|
186
|
+
* cannot be revoked mid-run, exactly as with today's gate-time "Approve &
|
|
187
|
+
* don't ask again". Never written back to the account preference: the
|
|
188
|
+
* flip is scoped to THIS conversation.
|
|
171
189
|
*/
|
|
172
190
|
readonly setAutoApproveAll: (value: boolean) => void;
|
|
173
191
|
|
|
@@ -340,27 +358,33 @@ export function useSessionPageFlow(
|
|
|
340
358
|
// the user's explicit in-session decision wins, otherwise the truth is
|
|
341
359
|
// computed from its sources — never an effect-synced copy:
|
|
342
360
|
//
|
|
343
|
-
// effective = userChoice ?? (hostSeed || runArmed)
|
|
361
|
+
// effective = userChoice ?? (accountSeed || hostSeed || runArmed)
|
|
344
362
|
//
|
|
345
|
-
// - `userChoice`: the
|
|
346
|
-
// ask again" (which has always escalated the session preference).
|
|
363
|
+
// - `userChoice`: the Config facet's switch, or the gate-time "Approve &
|
|
364
|
+
// don't ask again" (which has always escalated the session preference).
|
|
365
|
+
// - `accountSeed`: the account's default_auto_approve preference — the
|
|
366
|
+
// user's own persisted walk-away default. Resolves async with whoAmI;
|
|
367
|
+
// being part of the derivation (not an initializer), a late arrival
|
|
368
|
+
// still applies unless the user has already flipped this session.
|
|
347
369
|
// - `hostSeed`: the host's provider-level approvalDefaults (#302) — an
|
|
348
|
-
// app-level trust judgment. Guests
|
|
349
|
-
// visitor is not the operator
|
|
370
|
+
// app-level trust judgment. Guests inherit neither seed (a share-link
|
|
371
|
+
// visitor is not the operator either trust judgment covers).
|
|
350
372
|
// - `runArmed`: the ACTIVE execution was created with
|
|
351
|
-
// spec.auto_approve_all, so the
|
|
373
|
+
// spec.auto_approve_all, so the switch reflects an armed in-flight run —
|
|
352
374
|
// this is what carries the state across the new-session → session-page
|
|
353
375
|
// handoff. Deliberately derived from the active execution ONLY, never
|
|
354
376
|
// from history: deriving from past executions would silently survive a
|
|
355
|
-
// reload
|
|
377
|
+
// reload independent of the seeds above.
|
|
356
378
|
//
|
|
357
|
-
//
|
|
358
|
-
// re-applies the
|
|
379
|
+
// The explicit choice lives only in memory for the life of this page —
|
|
380
|
+
// reset on reload re-applies the seeds (the account preference now being
|
|
381
|
+
// the persisted one the user actually declared), never a per-session flip.
|
|
359
382
|
const approvalDefaults = useApprovalDefaults();
|
|
360
383
|
const [autoApproveChoice, setAutoApproveChoice] = useState<boolean | null>(null);
|
|
384
|
+
const accountSeed = !isGuest && (options.accountDefaults?.autoApprove ?? false);
|
|
361
385
|
const hostSeed = !isGuest && (approvalDefaults?.autoApproveAll ?? false);
|
|
362
386
|
const runArmed = conv.activeStreamExecution?.spec?.autoApproveAll === true;
|
|
363
|
-
const autoApproveAll = autoApproveChoice ?? (hostSeed || runArmed);
|
|
387
|
+
const autoApproveAll = autoApproveChoice ?? (accountSeed || hostSeed || runArmed);
|
|
364
388
|
const setAutoApproveAll = useCallback((value: boolean) => {
|
|
365
389
|
setAutoApproveChoice(value);
|
|
366
390
|
}, []);
|
|
@@ -29,8 +29,10 @@ export function MemorySection() {
|
|
|
29
29
|
<p className="stg:text-muted-foreground stg:mb-6 stg:text-xs">
|
|
30
30
|
Facts agents proposed to remember about you in this organization.
|
|
31
31
|
Confirmed memories are shared with agents in your future sessions
|
|
32
|
-
and appear in those executions' records
|
|
33
|
-
|
|
32
|
+
and appear in those executions' records; once you have many,
|
|
33
|
+
each conversation recalls the most relevant ones, shown on the
|
|
34
|
+
execution. Only you can see them here, and you can edit or delete
|
|
35
|
+
any of them at any time.
|
|
34
36
|
</p>
|
|
35
37
|
|
|
36
38
|
{!org ? (
|
package/src/switch/Switch.tsx
CHANGED
|
@@ -17,6 +17,8 @@ export interface SwitchProps {
|
|
|
17
17
|
readonly "aria-label"?: string;
|
|
18
18
|
/** ID of the element that labels this switch. */
|
|
19
19
|
readonly "aria-labelledby"?: string;
|
|
20
|
+
/** ID of the element that describes this switch (helper copy). */
|
|
21
|
+
readonly "aria-describedby"?: string;
|
|
20
22
|
/** Element ID, for association with an external `<label htmlFor>`. */
|
|
21
23
|
readonly id?: string;
|
|
22
24
|
/** Additional CSS classes for the root button. */
|
|
@@ -54,6 +56,7 @@ export function Switch({
|
|
|
54
56
|
disabled,
|
|
55
57
|
"aria-label": ariaLabel,
|
|
56
58
|
"aria-labelledby": ariaLabelledby,
|
|
59
|
+
"aria-describedby": ariaDescribedby,
|
|
57
60
|
id,
|
|
58
61
|
className,
|
|
59
62
|
}: SwitchProps) {
|
|
@@ -65,6 +68,7 @@ export function Switch({
|
|
|
65
68
|
aria-checked={checked}
|
|
66
69
|
aria-label={ariaLabel}
|
|
67
70
|
aria-labelledby={ariaLabelledby}
|
|
71
|
+
aria-describedby={ariaDescribedby}
|
|
68
72
|
disabled={disabled}
|
|
69
73
|
onClick={() => onCheckedChange(!checked)}
|
|
70
74
|
className={cn(
|
|
@@ -31,7 +31,11 @@ import {
|
|
|
31
31
|
} from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
|
|
32
32
|
import type { WorkflowExecutionEvent } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
|
|
33
33
|
import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
ApprovalAction,
|
|
36
|
+
ExecutionPhase as AgentExecutionPhase,
|
|
37
|
+
FileDecisionAction,
|
|
38
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
35
39
|
import type { DerivedCostSummary } from "../../internal/store/workflow-execution-event-store";
|
|
36
40
|
import { WorkflowExecutionEventStore } from "../../internal/store/workflow-execution-event-store";
|
|
37
41
|
import { useWorkflowExecution } from "../useWorkflowExecution";
|
|
@@ -303,14 +307,29 @@ describe("WorkflowExecutionViewer in-thread HITL (S10/T06/T07)", () => {
|
|
|
303
307
|
const actions = arrange();
|
|
304
308
|
render(<WorkflowExecutionViewer executionId="wex_1" />);
|
|
305
309
|
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
+
// The transcript submits through the SAME workflow-level handlers
|
|
311
|
+
// every other surface uses, so in-flight and error state can never
|
|
312
|
+
// fork — and never the child's agentExecution.* path (checked
|
|
313
|
+
// exhaustively in WorkflowAgentCallTranscript.test.tsx). State fields
|
|
314
|
+
// pass by IDENTITY; the submit fns are asserted by DELEGATION since
|
|
315
|
+
// the thread's scroll-on-send wrapper (stigmer-cloud#267) pins the
|
|
316
|
+
// view before handing each call to the single instance — a per-card
|
|
317
|
+
// duplicate would still fail here, because the delegate IS the
|
|
318
|
+
// viewer's own spy.
|
|
319
|
+
expect(mockedTranscript.mock.calls.length).toBeGreaterThan(0);
|
|
310
320
|
for (const call of mockedTranscript.mock.calls) {
|
|
311
321
|
const hitl = call[0].hitl!;
|
|
312
|
-
|
|
313
|
-
expect(
|
|
322
|
+
hitl.submitApproval("tc-probe", ApprovalAction.APPROVE);
|
|
323
|
+
expect(actions.submitApproval).toHaveBeenLastCalledWith(
|
|
324
|
+
"tc-probe",
|
|
325
|
+
ApprovalAction.APPROVE,
|
|
326
|
+
);
|
|
327
|
+
hitl.submitFileDecision("agx-probe", "cs-probe", FileDecisionAction.APPROVE);
|
|
328
|
+
expect(actions.submitFileDecision).toHaveBeenLastCalledWith(
|
|
329
|
+
"agx-probe",
|
|
330
|
+
"cs-probe",
|
|
331
|
+
FileDecisionAction.APPROVE,
|
|
332
|
+
);
|
|
314
333
|
expect(hitl.approvalSubmittingToolCallIds).toBe(
|
|
315
334
|
actions.approvalSubmittingToolCallIds,
|
|
316
335
|
);
|
|
@@ -286,21 +286,55 @@ describe("per-kind preview lines", () => {
|
|
|
286
286
|
overrides: { agentSlug: "blog-writer", messagesCount: 12, toolCallsCount: 5 },
|
|
287
287
|
expected: "blog-writer · 12 msgs · 5 tools",
|
|
288
288
|
},
|
|
289
|
+
{
|
|
290
|
+
label: "switch_case settled on a target branch (R6-5)",
|
|
291
|
+
kind: WorkflowTaskKind.switch_case,
|
|
292
|
+
overrides: { outputSummary: { __flow_directive__: "approved-path" } },
|
|
293
|
+
expected: "→ approved-path",
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
label: "switch_case settled on a termination directive",
|
|
297
|
+
kind: WorkflowTaskKind.switch_case,
|
|
298
|
+
overrides: { outputSummary: { __flow_directive__: "exit" } },
|
|
299
|
+
expected: "→ exit",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
label: "try_catch recovered via catch retry (R6-5)",
|
|
303
|
+
kind: WorkflowTaskKind.try_catch,
|
|
304
|
+
overrides: { attemptNumber: 3, outputSummary: { result: "ok" } },
|
|
305
|
+
expected: "recovered after 3 attempts",
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
label: "try_catch settled block result (first-attempt success)",
|
|
309
|
+
kind: WorkflowTaskKind.try_catch,
|
|
310
|
+
overrides: { outputSummary: { result: 42 } },
|
|
311
|
+
expected: "42",
|
|
312
|
+
},
|
|
289
313
|
])("$label", ({ kind, overrides, expected }) => {
|
|
290
314
|
const { previewLine } = resolveTaskPreview(state({ taskKind: kind, ...overrides }));
|
|
291
315
|
expect(previewLine).toBe(expected);
|
|
292
316
|
});
|
|
293
317
|
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
318
|
+
// switch_case degrades to status-only when no case matched: the executor
|
|
319
|
+
// returns no directive, so there is honestly nothing to say (R6-5).
|
|
320
|
+
it("switch_case yields an empty line without a flow directive", () => {
|
|
321
|
+
const { previewLine } = resolveTaskPreview(
|
|
322
|
+
state({
|
|
323
|
+
taskKind: WorkflowTaskKind.switch_case,
|
|
324
|
+
outputSummary: { some: "data" },
|
|
325
|
+
}),
|
|
326
|
+
);
|
|
327
|
+
expect(previewLine).toBe("");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Kinds that deliberately stay status-only: remaining control flow
|
|
331
|
+
// (for/fork detail needs graph topology the thread lacks — deferred),
|
|
332
|
+
// raise_error (the failure precedence carries the message; its preview
|
|
333
|
+
// BODY carries the detail), the invocation kinds (no verified output
|
|
334
|
+
// envelope — backend follow-up), and unspecified (the snapshot fallback).
|
|
299
335
|
it.each([
|
|
300
|
-
WorkflowTaskKind.switch_case,
|
|
301
336
|
WorkflowTaskKind.for_each,
|
|
302
337
|
WorkflowTaskKind.fork,
|
|
303
|
-
WorkflowTaskKind.try_catch,
|
|
304
338
|
WorkflowTaskKind.raise_error,
|
|
305
339
|
WorkflowTaskKind.http_call,
|
|
306
340
|
WorkflowTaskKind.grpc_call,
|
|
@@ -396,9 +430,12 @@ type JsonValueLike = Parameters<typeof valueSnippet>[0];
|
|
|
396
430
|
describe("defaultDisclosureForKind", () => {
|
|
397
431
|
// T05 (DD-T05-5): every kind whose output CAN matter is a preview kind —
|
|
398
432
|
// the showBody gate keeps output-less cards as one-line rows, so preview
|
|
399
|
-
// disclosure costs nothing until the runner writes real output.
|
|
400
|
-
//
|
|
401
|
-
//
|
|
433
|
+
// disclosure costs nothing until the runner writes real output. R6-5
|
|
434
|
+
// adds try_catch (its output is the block's settled result) and
|
|
435
|
+
// raise_error (always fails; the preview body IS the always-visible
|
|
436
|
+
// failure detail). Only genuinely body-less kinds remain summary —
|
|
437
|
+
// including switch_case DELIBERATELY: its whole content is the one-word
|
|
438
|
+
// directive on its preview line; a body would only repeat it.
|
|
402
439
|
it.each([
|
|
403
440
|
[WorkflowTaskKind.transform, "preview"],
|
|
404
441
|
[WorkflowTaskKind.validate, "preview"],
|
|
@@ -413,13 +450,13 @@ describe("defaultDisclosureForKind", () => {
|
|
|
413
450
|
[WorkflowTaskKind.grpc_call, "preview"],
|
|
414
451
|
[WorkflowTaskKind.activity_call, "preview"],
|
|
415
452
|
[WorkflowTaskKind.run_workflow, "preview"],
|
|
453
|
+
[WorkflowTaskKind.try_catch, "preview"],
|
|
454
|
+
[WorkflowTaskKind.raise_error, "preview"],
|
|
416
455
|
[WorkflowTaskKind.wait, "summary"],
|
|
417
456
|
[WorkflowTaskKind.switch_case, "summary"],
|
|
418
457
|
[WorkflowTaskKind.fork, "summary"],
|
|
419
|
-
[WorkflowTaskKind.try_catch, "summary"],
|
|
420
458
|
[WorkflowTaskKind.for_each, "summary"],
|
|
421
459
|
[WorkflowTaskKind.listen, "summary"],
|
|
422
|
-
[WorkflowTaskKind.raise_error, "summary"],
|
|
423
460
|
[WorkflowTaskKind.workflow_task_kind_unspecified, "summary"],
|
|
424
461
|
] as const)("kind %d defaults to %s", (kind, expected) => {
|
|
425
462
|
expect(defaultDisclosureForKind(kind)).toBe(expected);
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { renderHook, act } from "@testing-library/react";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { ReactFlowProvider } from "@xyflow/react";
|
|
5
|
-
import type { Node } from "@xyflow/react";
|
|
5
|
+
import type { Node, OnNodeDrag } from "@xyflow/react";
|
|
6
6
|
import { useWorkflowCanvas } from "../useWorkflowCanvas";
|
|
7
7
|
|
|
8
8
|
// Regression tests for oss#609: isDirty was a model-reference comparison
|
|
@@ -54,7 +54,12 @@ function renderCanvas() {
|
|
|
54
54
|
|
|
55
55
|
type CanvasResult = ReturnType<typeof renderCanvas>["result"];
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
// The drag fixture derives its type from the handler itself, so it tracks
|
|
58
|
+
// whatever event type the installed @xyflow/react declares (the pinned
|
|
59
|
+
// 12.10.2 takes the React synthetic event; newer majors take the native
|
|
60
|
+
// event) — a dependency bump can never strand this cast again (#821's
|
|
61
|
+
// hand-written native-MouseEvent cast did not compile under npm ci).
|
|
62
|
+
const DRAG_EVENT = {} as Parameters<OnNodeDrag>[0];
|
|
58
63
|
|
|
59
64
|
/** Drives one full drag gesture the way React Flow produces it. */
|
|
60
65
|
function drag(result: CanvasResult, id: string, to: { x: number; y: number }) {
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import { renderHook, act } from "@testing-library/react";
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { ReactFlowProvider } from "@xyflow/react";
|
|
5
|
-
import type { Node } from "@xyflow/react";
|
|
5
|
+
import type { Node, OnNodeDrag } from "@xyflow/react";
|
|
6
6
|
import { useWorkflowCanvas } from "../useWorkflowCanvas";
|
|
7
7
|
|
|
8
8
|
// Regression tests for oss#602: node drags updated only React Flow's copy
|
|
@@ -51,7 +51,12 @@ function renderCanvas() {
|
|
|
51
51
|
|
|
52
52
|
type CanvasResult = ReturnType<typeof renderCanvas>["result"];
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
// The drag fixture derives its type from the handler itself, so it tracks
|
|
55
|
+
// whatever event type the installed @xyflow/react declares (the pinned
|
|
56
|
+
// 12.10.2 takes the React synthetic event; newer majors take the native
|
|
57
|
+
// event) — a dependency bump can never strand this cast again (#821's
|
|
58
|
+
// hand-written native-MouseEvent cast did not compile under npm ci).
|
|
59
|
+
const DRAG_EVENT = {} as Parameters<OnNodeDrag>[0];
|
|
55
60
|
|
|
56
61
|
function canvasPosition(result: CanvasResult, id: string): { x: number; y: number } {
|
|
57
62
|
const node = result.current.nodes.find((n) => n.id === id);
|