@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
|
@@ -16,6 +16,6 @@ import { useActiveOrgSlug } from "../organization/OrgProvider.js";
|
|
|
16
16
|
export function MemorySection() {
|
|
17
17
|
const headingId = useId();
|
|
18
18
|
const org = useActiveOrgSlug();
|
|
19
|
-
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:mb-1 stg:text-sm stg:font-semibold", children: "Memory" }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-6 stg:text-xs", children: "Facts agents proposed to remember about you in this organization. Confirmed memories are shared with agents in your future sessions and appear in those executions' records. Only you can see them here, and you can edit or delete any of them at any time." }), !org ? (_jsx("p", { className: "stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs", children: "Select an organization to view its memories." })) : (_jsx(MemoryListPanel, { org: org }))] }));
|
|
19
|
+
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:mb-1 stg:text-sm stg:font-semibold", children: "Memory" }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-6 stg:text-xs", children: "Facts agents proposed to remember about you in this organization. Confirmed memories are shared with agents in your future sessions and appear in those executions' records; once you have many, each conversation recalls the most relevant ones, shown on the execution. Only you can see them here, and you can edit or delete any of them at any time." }), !org ? (_jsx("p", { className: "stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs", children: "Select an organization to view its memories." })) : (_jsx(MemoryListPanel, { org: org }))] }));
|
|
20
20
|
}
|
|
21
21
|
//# sourceMappingURL=MemorySection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemorySection.js","sourceRoot":"","sources":["../../src/settings/MemorySection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAE/B,OAAO,CACL,sCAA0B,SAAS,aACjC,aACE,EAAE,EAAE,SAAS,EACb,SAAS,EAAC,4DAA4D,uBAGnE,EACL,YAAG,SAAS,EAAC,gDAAgD,
|
|
1
|
+
{"version":3,"file":"MemorySection.js","sourceRoot":"","sources":["../../src/settings/MemorySection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAE/B,OAAO,CACL,sCAA0B,SAAS,aACjC,aACE,EAAE,EAAE,SAAS,EACb,SAAS,EAAC,4DAA4D,uBAGnE,EACL,YAAG,SAAS,EAAC,gDAAgD,2WAOzD,EAEH,CAAC,GAAG,CAAC,CAAC,CAAC,CACN,YAAG,SAAS,EAAC,gEAAgE,6DAEzE,CACL,CAAC,CAAC,CAAC,CACF,KAAC,eAAe,IAAC,GAAG,EAAE,GAAG,GAAI,CAC9B,IACO,CACX,CAAC;AACJ,CAAC"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
2
|
import { prepareImageForVision } from "../prepare-image.js";
|
|
3
3
|
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
vi.unstubAllGlobals();
|
|
6
|
+
});
|
|
7
|
+
|
|
4
8
|
/**
|
|
5
9
|
* happy-dom half of the prepare-image coverage: the guard rails that make
|
|
6
10
|
* the function safe to call from any environment. The actual pixel work
|
|
@@ -17,11 +21,13 @@ describe("prepareImageForVision — environment guard rails", () => {
|
|
|
17
21
|
});
|
|
18
22
|
|
|
19
23
|
it("returns an image untouched when the canvas pipeline is unavailable", async () => {
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
24
|
+
// The situation of an old browser or a privacy extension that blocks
|
|
25
|
+
// canvas. Stubbed explicitly rather than relying on the DOM shim
|
|
26
|
+
// lacking createImageBitmap — newer shims ship one, and this guard
|
|
27
|
+
// must hold regardless of the test environment's capabilities. The
|
|
28
|
+
// paste must still work end to end with the original bytes; it merely
|
|
29
|
+
// degrades at the runner with the standard disclosure.
|
|
30
|
+
vi.stubGlobal("createImageBitmap", undefined);
|
|
25
31
|
|
|
26
32
|
const png = new File([new Uint8Array([0x89, 0x50, 0x4e, 0x47])], "shot.png", {
|
|
27
33
|
type: "image/png",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useId } from "react";
|
|
4
3
|
import { cn } from "@stigmer/theme";
|
|
5
4
|
import {
|
|
6
5
|
Tooltip,
|
|
@@ -8,7 +7,6 @@ import {
|
|
|
8
7
|
TooltipProvider,
|
|
9
8
|
TooltipTrigger,
|
|
10
9
|
} from "../internal/tooltip.js";
|
|
11
|
-
import { Switch } from "../switch/Switch.js";
|
|
12
10
|
import { ContextPopover } from "./ContextPopover.js";
|
|
13
11
|
import { ConfigureMenu, type ConfigureMenuItem } from "./ConfigureMenu.js";
|
|
14
12
|
import { ModelSelector } from "../models/ModelSelector.js";
|
|
@@ -25,18 +23,6 @@ import {
|
|
|
25
23
|
} from "./icons.js";
|
|
26
24
|
import { SpinnerIcon } from "../internal/SpinnerIcon.js";
|
|
27
25
|
|
|
28
|
-
/**
|
|
29
|
-
* Wiring for the always-available "auto-approve tool calls" toggle
|
|
30
|
-
* (stigmer/stigmer#816). Presence renders the control; omission keeps the
|
|
31
|
-
* toolbar byte-identical for consumers that don't wire it (DD-011).
|
|
32
|
-
*/
|
|
33
|
-
export interface ComposerAutoApproveProps {
|
|
34
|
-
/** Whether auto-approve is currently armed for this conversation. */
|
|
35
|
-
readonly armed: boolean;
|
|
36
|
-
/** Called with the next value when the user flips the toggle. */
|
|
37
|
-
readonly onChange: (armed: boolean) => void;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
26
|
export interface ComposerToolbarProps {
|
|
41
27
|
readonly disabled: boolean;
|
|
42
28
|
readonly isSubmitting: boolean;
|
|
@@ -61,14 +47,6 @@ export interface ComposerToolbarProps {
|
|
|
61
47
|
readonly interactionMode?: InteractionModeOption;
|
|
62
48
|
readonly onInteractionModeChange: (mode: InteractionModeOption) => void;
|
|
63
49
|
|
|
64
|
-
/**
|
|
65
|
-
* Always-available auto-approve toggle (#816). Deliberately NOT bound to
|
|
66
|
-
* {@link disabled}: the walk-away user flips it exactly while a turn
|
|
67
|
-
* streams, so it stays interactive under the composer lock — the same
|
|
68
|
-
* exemption the Stop button carries.
|
|
69
|
-
*/
|
|
70
|
-
readonly autoApprove?: ComposerAutoApproveProps;
|
|
71
|
-
|
|
72
50
|
readonly showModelSelector: boolean;
|
|
73
51
|
readonly modelId?: string;
|
|
74
52
|
readonly onModelChange: (id: string) => void;
|
|
@@ -150,7 +128,6 @@ export function ComposerToolbar({
|
|
|
150
128
|
showInteractionModePicker,
|
|
151
129
|
interactionMode,
|
|
152
130
|
onInteractionModeChange,
|
|
153
|
-
autoApprove,
|
|
154
131
|
showModelSelector,
|
|
155
132
|
modelId,
|
|
156
133
|
onModelChange,
|
|
@@ -160,7 +137,6 @@ export function ComposerToolbar({
|
|
|
160
137
|
onThinkingModeChange,
|
|
161
138
|
}: ComposerToolbarProps) {
|
|
162
139
|
const showHarnessSeparate = showHarnessSelector && !showModelSelector;
|
|
163
|
-
const autoApproveLabelId = useId();
|
|
164
140
|
|
|
165
141
|
return (
|
|
166
142
|
<div className="stg:flex stg:items-center stg:justify-between stg:gap-2 stg:border-t stg:border-border-muted stg:px-3 stg:py-2">
|
|
@@ -198,39 +174,6 @@ export function ComposerToolbar({
|
|
|
198
174
|
/>
|
|
199
175
|
)}
|
|
200
176
|
|
|
201
|
-
{/* Auto-approve (#816): primary state like Mode and Model — armed
|
|
202
|
-
vs gated is something the user glances at before walking away.
|
|
203
|
-
Never bound to `disabled` (see ComposerAutoApproveProps): the
|
|
204
|
-
whole point is flipping it while a turn streams. Scoped provider:
|
|
205
|
-
this tooltip does not share the icon cluster's hover-delay group. */}
|
|
206
|
-
{autoApprove && (
|
|
207
|
-
<TooltipProvider>
|
|
208
|
-
<Tooltip>
|
|
209
|
-
<TooltipTrigger render={<span className="stg:inline-flex" />}>
|
|
210
|
-
<span className="stg:flex stg:shrink-0 stg:items-center stg:gap-1.5 stg:px-1">
|
|
211
|
-
<label
|
|
212
|
-
id={autoApproveLabelId}
|
|
213
|
-
htmlFor={`${autoApproveLabelId}-switch`}
|
|
214
|
-
className="stg:cursor-pointer stg:select-none stg:text-xs stg:text-muted-foreground"
|
|
215
|
-
>
|
|
216
|
-
Auto-approve
|
|
217
|
-
</label>
|
|
218
|
-
<Switch
|
|
219
|
-
id={`${autoApproveLabelId}-switch`}
|
|
220
|
-
checked={autoApprove.armed}
|
|
221
|
-
onCheckedChange={autoApprove.onChange}
|
|
222
|
-
aria-labelledby={autoApproveLabelId}
|
|
223
|
-
/>
|
|
224
|
-
</span>
|
|
225
|
-
</TooltipTrigger>
|
|
226
|
-
<TooltipContent side="top">
|
|
227
|
-
{autoApprove.armed
|
|
228
|
-
? "Tool calls run without asking — this conversation only. Turn off to be asked again."
|
|
229
|
-
: "Run this conversation's tool calls without asking for approval, including the run in progress."}
|
|
230
|
-
</TooltipContent>
|
|
231
|
-
</Tooltip>
|
|
232
|
-
</TooltipProvider>
|
|
233
|
-
)}
|
|
234
177
|
</div>
|
|
235
178
|
|
|
236
179
|
{/* ---- Right group: Secondary actions (icon-only) + Send ----
|
|
@@ -4,7 +4,7 @@ import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo,
|
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import { getUserMessage, type AttachmentInput, type EnvVarInput, type McpServerUsageInput, type ResourceRef } from "@stigmer/sdk";
|
|
6
6
|
import { useComposer } from "./useComposer.js";
|
|
7
|
-
import { ComposerToolbar
|
|
7
|
+
import { ComposerToolbar } from "./ComposerToolbar.js";
|
|
8
8
|
import { type ConfigureMenuItem } from "./ConfigureMenu.js";
|
|
9
9
|
import type { HarnessOption } from "../models/harness.js";
|
|
10
10
|
import { FAST_SERVICE_TIER, type ServiceTierOption } from "../models/service-tier.js";
|
|
@@ -300,16 +300,6 @@ export interface SessionComposerProps {
|
|
|
300
300
|
/** Show the model selector. @default true */
|
|
301
301
|
readonly showModelSelector?: boolean;
|
|
302
302
|
|
|
303
|
-
/**
|
|
304
|
-
* Always-visible "auto-approve tool calls" toggle in the toolbar
|
|
305
|
-
* (stigmer/stigmer#816). Presence renders it; omission keeps the composer
|
|
306
|
-
* byte-identical (DD-011). Stays interactive while the composer is
|
|
307
|
-
* `disabled` — the walk-away user flips it exactly while a turn streams —
|
|
308
|
-
* so consumers must only wire it on surfaces whose viewer may submit
|
|
309
|
-
* approvals (never guest/observer). See {@link ComposerAutoApproveProps}.
|
|
310
|
-
*/
|
|
311
|
-
readonly autoApprove?: ComposerAutoApproveProps;
|
|
312
|
-
|
|
313
303
|
/**
|
|
314
304
|
* Workspace state managed by {@link useWorkspaceEntries}.
|
|
315
305
|
* When provided, renders a workspace trigger in the toolbar
|
|
@@ -627,7 +617,6 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
627
617
|
defaultModelId,
|
|
628
618
|
onModelChange,
|
|
629
619
|
showModelSelector = true,
|
|
630
|
-
autoApprove,
|
|
631
620
|
workspace,
|
|
632
621
|
gitHubConnection,
|
|
633
622
|
enableGitHub = true,
|
|
@@ -1931,7 +1920,6 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1931
1920
|
interactionMode={interactionMode}
|
|
1932
1921
|
onInteractionModeChange={handleInteractionModeChange}
|
|
1933
1922
|
showModelSelector={showModelSelector}
|
|
1934
|
-
autoApprove={autoApprove}
|
|
1935
1923
|
// The pill renders the effective selection — the same value the
|
|
1936
1924
|
// submit payload carries (#663), never a fallback of its own.
|
|
1937
1925
|
modelId={effective.modelId}
|
package/src/composer/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
TooltipProvider,
|
|
18
18
|
TooltipTrigger,
|
|
19
19
|
} from "../internal/tooltip.js";
|
|
20
|
-
import { useAutoScroll } from "../internal/useAutoScroll.js";
|
|
20
|
+
import { useAutoScroll, usePinToLatestOnSignal } from "../internal/useAutoScroll.js";
|
|
21
21
|
import { ConversationMediaAttachment } from "./ConversationMediaAttachment.js";
|
|
22
22
|
import {
|
|
23
23
|
attemptExplanationOf,
|
|
@@ -69,6 +69,20 @@ export interface ConversationTimelineViewProps {
|
|
|
69
69
|
readonly provider: ChannelProviderId | null;
|
|
70
70
|
/** Frozen instant for deterministic hosts (tests, documentation tours). */
|
|
71
71
|
readonly now?: Date;
|
|
72
|
+
/**
|
|
73
|
+
* Scroll-on-send signal (stigmer-cloud#267): increment this counter when
|
|
74
|
+
* the reader dispatches a reply and the view pins to the latest content,
|
|
75
|
+
* re-engaging follow mode so the item stays in view when the timeline
|
|
76
|
+
* refetch delivers it — even for a reader who had deliberately scrolled
|
|
77
|
+
* up. The view stays presentational: it has no composer and deliberately
|
|
78
|
+
* renders no optimistic item (the SDK never fabricates what the provider
|
|
79
|
+
* might not have done), so the send moment must arrive from the host —
|
|
80
|
+
* `ConversationsWorkbench` wires its own composer's send here; headless
|
|
81
|
+
* hosts composing their own composer get the same seam. Omit to keep
|
|
82
|
+
* today's leave-the-reader-alone behavior. Incoming items never move a
|
|
83
|
+
* scrolled-up reader either way.
|
|
84
|
+
*/
|
|
85
|
+
readonly pinToLatestSignal?: number;
|
|
72
86
|
/** Additional classes for the view container. */
|
|
73
87
|
readonly className?: string;
|
|
74
88
|
}
|
|
@@ -100,10 +114,12 @@ export function ConversationTimelineView({
|
|
|
100
114
|
isLoadingOlder,
|
|
101
115
|
provider,
|
|
102
116
|
now,
|
|
117
|
+
pinToLatestSignal,
|
|
103
118
|
className,
|
|
104
119
|
}: ConversationTimelineViewProps) {
|
|
105
120
|
const { scrollRef, sentinelRef, contentRef, isFollowing, jumpToLatest } =
|
|
106
121
|
useAutoScroll();
|
|
122
|
+
usePinToLatestOnSignal(pinToLatestSignal, jumpToLatest);
|
|
107
123
|
|
|
108
124
|
const days = useMemo(() => groupByDay(items, now), [items, now]);
|
|
109
125
|
const descriptor = channelProviderOf(provider ?? undefined);
|
|
@@ -82,6 +82,19 @@ export interface ConversationsWorkbenchProps {
|
|
|
82
82
|
readonly channelHref?: (channel: AgentChannel) => string | null;
|
|
83
83
|
/** Frozen instant for deterministic hosts (tests, documentation tours). */
|
|
84
84
|
readonly now?: Date;
|
|
85
|
+
/**
|
|
86
|
+
* Scroll to the reader's own reply when they send one from a scrolled-up
|
|
87
|
+
* position (stigmer-cloud#267): an accepted send pins the timeline to the
|
|
88
|
+
* latest content and re-engages follow mode, so the reply stays in view
|
|
89
|
+
* when the refetch delivers its real ledger item. Refused sends never pin
|
|
90
|
+
* — the composer restores the draft and there is nothing to show. Default
|
|
91
|
+
* `true` on all three SDK thread surfaces at once (the ratified DD-011
|
|
92
|
+
* divergence — cross-surface consistency is the point); set `false` to
|
|
93
|
+
* keep today's leave-the-reader-alone behavior.
|
|
94
|
+
*
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
97
|
+
readonly scrollOnSend?: boolean;
|
|
85
98
|
/** Additional classes for the workbench container. */
|
|
86
99
|
readonly className?: string;
|
|
87
100
|
}
|
|
@@ -103,6 +116,7 @@ export function ConversationsWorkbench({
|
|
|
103
116
|
headerAccessory,
|
|
104
117
|
channelHref,
|
|
105
118
|
now,
|
|
119
|
+
scrollOnSend = true,
|
|
106
120
|
className,
|
|
107
121
|
}: ConversationsWorkbenchProps) {
|
|
108
122
|
const [channelFilter, setChannelFilter] = useState("");
|
|
@@ -233,6 +247,12 @@ export function ConversationsWorkbench({
|
|
|
233
247
|
}
|
|
234
248
|
}, [timeline.items, settlingItemId]);
|
|
235
249
|
|
|
250
|
+
// Scroll-on-send (stigmer-cloud#267): a monotonic counter the timeline
|
|
251
|
+
// view pins on. Lives outside the conversation-keyed column, which is
|
|
252
|
+
// safe by the signal contract: the view initializes to the current value
|
|
253
|
+
// on (re)mount and pins only on increments it observes.
|
|
254
|
+
const [sendPinSignal, setSendPinSignal] = useState(0);
|
|
255
|
+
|
|
236
256
|
const handleSend = useCallback(
|
|
237
257
|
async (payload: ConversationReplyPayload) => {
|
|
238
258
|
const output = await participation.reply(payload);
|
|
@@ -252,6 +272,10 @@ export function ConversationsWorkbench({
|
|
|
252
272
|
selectionKey,
|
|
253
273
|
itemId: outboundItemIdOf(output.outboundMessageId),
|
|
254
274
|
});
|
|
275
|
+
// The reply produced a ledger item to show — pin the timeline so
|
|
276
|
+
// it lands in view (a refusal pins nothing: the draft came back
|
|
277
|
+
// and the reader's position should not move for it).
|
|
278
|
+
setSendPinSignal((n) => n + 1);
|
|
255
279
|
}
|
|
256
280
|
timeline.refetch();
|
|
257
281
|
return output;
|
|
@@ -379,6 +403,7 @@ export function ConversationsWorkbench({
|
|
|
379
403
|
isLoadingOlder={timeline.isLoadingOlder}
|
|
380
404
|
provider={descriptor?.id ?? null}
|
|
381
405
|
now={now}
|
|
406
|
+
pinToLatestSignal={scrollOnSend ? sendPinSignal : undefined}
|
|
382
407
|
/>
|
|
383
408
|
|
|
384
409
|
<ConversationComposer
|
|
@@ -35,7 +35,13 @@ function items(count: number): ConversationTimelineItem[] {
|
|
|
35
35
|
? ConversationItemAuthor.author_customer
|
|
36
36
|
: ConversationItemAuthor.author_agent,
|
|
37
37
|
text: `message ${i}`,
|
|
38
|
-
|
|
38
|
+
// Anchored by INDEX, never by count: growth must only APPEND rows,
|
|
39
|
+
// as production items keep their timestamps. A count-relative time
|
|
40
|
+
// rewrote every existing row on growth — the resulting full-thread
|
|
41
|
+
// reflow could clamp scrollTop mid-pin and trip the hook's
|
|
42
|
+
// reader-took-control guard (measured flaky ~1-in-5 on the
|
|
43
|
+
// scroll-on-send case).
|
|
44
|
+
at: timestampFromDate(new Date(NOW.getTime() - (1_000 - i) * 60_000)),
|
|
39
45
|
}),
|
|
40
46
|
);
|
|
41
47
|
}
|
|
@@ -43,6 +49,7 @@ function items(count: number): ConversationTimelineItem[] {
|
|
|
43
49
|
function view(props: {
|
|
44
50
|
readonly items: readonly ConversationTimelineItem[];
|
|
45
51
|
readonly isLoading: boolean;
|
|
52
|
+
readonly pinToLatestSignal?: number;
|
|
46
53
|
}): ReactElement {
|
|
47
54
|
return (
|
|
48
55
|
<ConversationTimelineView
|
|
@@ -54,6 +61,7 @@ function view(props: {
|
|
|
54
61
|
isLoadingOlder={false}
|
|
55
62
|
provider="whatsapp"
|
|
56
63
|
now={NOW}
|
|
64
|
+
pinToLatestSignal={props.pinToLatestSignal}
|
|
57
65
|
/>
|
|
58
66
|
);
|
|
59
67
|
}
|
|
@@ -157,4 +165,32 @@ describe("ConversationTimelineView scroll behavior (F-09)", () => {
|
|
|
157
165
|
expect(jumpButton().getAttribute("aria-hidden")).toBe("true");
|
|
158
166
|
});
|
|
159
167
|
});
|
|
168
|
+
|
|
169
|
+
it("pins a scrolled-up reader on their OWN send via pinToLatestSignal, and keeps following as the reply's real item lands (stigmer-cloud#267)", async () => {
|
|
170
|
+
const { rerender } = renderInPane(
|
|
171
|
+
view({ items: [], isLoading: true, pinToLatestSignal: 0 }),
|
|
172
|
+
);
|
|
173
|
+
rerender(view({ items: items(40), isLoading: false, pinToLatestSignal: 0 }));
|
|
174
|
+
await settled(() => expect(isPinnedToBottom(scroller())).toBe(true));
|
|
175
|
+
|
|
176
|
+
// The reader scrolls up to read history.
|
|
177
|
+
scroller().scrollTop = 0;
|
|
178
|
+
await settled(() =>
|
|
179
|
+
expect(jumpButton().getAttribute("aria-hidden")).toBe("false"),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// They send a reply: the workbench increments the signal on the
|
|
183
|
+
// accepted send. The view pins and re-engages follow — the timeline
|
|
184
|
+
// has no optimistic item, so re-engaged follow is what carries the
|
|
185
|
+
// reply's REAL ledger item into view when the refetch delivers it.
|
|
186
|
+
rerender(view({ items: items(40), isLoading: false, pinToLatestSignal: 1 }));
|
|
187
|
+
await settled(() => {
|
|
188
|
+
expect(isPinnedToBottom(scroller())).toBe(true);
|
|
189
|
+
expect(jumpButton().getAttribute("aria-hidden")).toBe("true");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// The refetch delivers the item — still pinned, the reply visible.
|
|
193
|
+
rerender(view({ items: items(41), isLoading: false, pinToLatestSignal: 1 }));
|
|
194
|
+
await settled(() => expect(isPinnedToBottom(scroller())).toBe(true));
|
|
195
|
+
});
|
|
160
196
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { lazy, memo, Suspense, useCallback, useMemo, type ComponentType } from "react";
|
|
3
|
+
import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from "react";
|
|
4
4
|
import { create } from "@bufbuild/protobuf";
|
|
5
|
-
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
5
|
+
import type { AgentExecution, RecalledMemoriesReport } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
6
|
+
import type { RecalledMemoryFact } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
6
7
|
import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
7
8
|
import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
8
9
|
import type { SubAgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
@@ -29,6 +30,7 @@ import { ToolCallGroup } from "./ToolCallGroup.js";
|
|
|
29
30
|
import { SubAgentSection } from "./SubAgentSection.js";
|
|
30
31
|
import { ExecutionPhaseBadge } from "./ExecutionPhaseBadge.js";
|
|
31
32
|
import { SetupProgress, type SetupProgressProps } from "./SetupProgress.js";
|
|
33
|
+
import { RecalledMemoriesCard, type RecalledMemoriesCardProps } from "./RecalledMemoriesCard.js";
|
|
32
34
|
import {
|
|
33
35
|
LivenessStatusLine,
|
|
34
36
|
type LivenessStatusLineProps,
|
|
@@ -56,7 +58,7 @@ import {
|
|
|
56
58
|
unwrapEnclosingMarkdownFence,
|
|
57
59
|
} from "../internal/markdown-components.js";
|
|
58
60
|
import { useRenderTracer, useKeyStability, useDomNodeCount, DevProfiler } from "../internal/dev/index.js";
|
|
59
|
-
import { useAutoScroll } from "../internal/useAutoScroll.js";
|
|
61
|
+
import { useAutoScroll, usePinToLatestOnSignal } from "../internal/useAutoScroll.js";
|
|
60
62
|
import { JumpToLatestButton } from "../internal/JumpToLatestButton.js";
|
|
61
63
|
import { ApprovalPeekBar } from "../internal/ApprovalPeekBar.js";
|
|
62
64
|
import { ThreadItemWrapper } from "../internal/ThreadItemWrapper.js";
|
|
@@ -113,6 +115,11 @@ export interface MessageThreadSlots {
|
|
|
113
115
|
readonly PlanStreamingCard?: ComponentType<PlanStreamingCardProps>;
|
|
114
116
|
/** Pre-first-token setup / "Thinking…" indicator. */
|
|
115
117
|
readonly SetupProgress?: ComponentType<SetupProgressProps>;
|
|
118
|
+
/**
|
|
119
|
+
* The retriever transparency card at a selection-active execution's
|
|
120
|
+
* segment start — "Recalled N of M memories" (DD-008 D5).
|
|
121
|
+
*/
|
|
122
|
+
readonly RecalledMemoriesCard?: ComponentType<RecalledMemoriesCardProps>;
|
|
116
123
|
/**
|
|
117
124
|
* The terminal execution-failure notice. Receives the raw server-reported
|
|
118
125
|
* reason and the retry wiring, so a host can turn the failure into its own
|
|
@@ -170,6 +177,25 @@ export interface MessageThreadProps {
|
|
|
170
177
|
* "Retry" control when {@link pendingMessageFailed} is `true`.
|
|
171
178
|
*/
|
|
172
179
|
readonly onRetrySend?: () => void;
|
|
180
|
+
/**
|
|
181
|
+
* Scroll to the reader's own message when they send one from a
|
|
182
|
+
* scrolled-up position (stigmer-cloud#267 — the WhatsApp convention:
|
|
183
|
+
* showing the result of the reader's own action is Nielsen #1
|
|
184
|
+
* system-status feedback). The send moment is the
|
|
185
|
+
* {@link pendingUserMessage} transition from empty to present; the pin
|
|
186
|
+
* re-engages follow mode, so the optimistic bubble and the streamed
|
|
187
|
+
* reply stay in view. Incoming content is unaffected — it still never
|
|
188
|
+
* moves a scrolled-up reader.
|
|
189
|
+
*
|
|
190
|
+
* Default `true` on all three SDK thread surfaces at once — a deliberate,
|
|
191
|
+
* ratified divergence from DD-011's opt-in default: the issue's whole
|
|
192
|
+
* point is cross-surface consistency, and a per-surface opt-in would
|
|
193
|
+
* re-create the inconsistency it fixes. Set `false` to keep today's
|
|
194
|
+
* leave-the-reader-alone behavior.
|
|
195
|
+
*
|
|
196
|
+
* @default true
|
|
197
|
+
*/
|
|
198
|
+
readonly scrollOnSend?: boolean;
|
|
173
199
|
/**
|
|
174
200
|
* When provided, the in-flight human turn (the active execution's prompt)
|
|
175
201
|
* shows a hover "Edit" affordance. Clicking it invokes this callback with
|
|
@@ -379,6 +405,7 @@ export type ThreadItem =
|
|
|
379
405
|
| { readonly kind: "approval-request"; readonly pendingApproval: PendingApproval; readonly key: string }
|
|
380
406
|
| { readonly kind: "file-review-record"; readonly fileChangeSet: FileChangeSet; readonly key: string }
|
|
381
407
|
| { readonly kind: "setup-progress"; readonly workspaceEntries: readonly WorkspaceEntry[]; readonly serverPhase?: string; readonly isAwaitingResponse?: boolean; readonly key: string }
|
|
408
|
+
| { readonly kind: "recalled-memories"; readonly report: RecalledMemoriesReport; readonly facts: readonly RecalledMemoryFact[]; readonly key: string }
|
|
382
409
|
| { readonly kind: "context-compacted"; readonly event: SummarizationEventView; readonly key: string }
|
|
383
410
|
| {
|
|
384
411
|
readonly kind: "todos";
|
|
@@ -805,6 +832,24 @@ export function buildThreadItems(
|
|
|
805
832
|
});
|
|
806
833
|
}
|
|
807
834
|
|
|
835
|
+
// The retriever transparency card (stigmer#293 Phase 3a, DD-008 D5):
|
|
836
|
+
// this execution ran semantic selection over its memory snapshot, so
|
|
837
|
+
// its segment discloses the subset right after the user's turn —
|
|
838
|
+
// spec.message is the query the retriever embedded. Absent report or
|
|
839
|
+
// selection_active=false means wholesale (unchanged Phase 2 behavior)
|
|
840
|
+
// and renders nothing. Gated on the REPORT, not the prompt bubble:
|
|
841
|
+
// syntheticUserPrompt deliberately skips some user turns (empty
|
|
842
|
+
// prompt, build-from-plan) that can still be selection-active.
|
|
843
|
+
const recalledReport = exec.status?.recalledMemoriesReport;
|
|
844
|
+
if (recalledReport?.selectionActive) {
|
|
845
|
+
items.push({
|
|
846
|
+
kind: "recalled-memories",
|
|
847
|
+
report: recalledReport,
|
|
848
|
+
facts: exec.spec?.recalledMemories?.facts ?? [],
|
|
849
|
+
key: `${execId}-recalled-memories`,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
808
853
|
for (let mi = 0; mi < messages.length; mi++) {
|
|
809
854
|
const msg = messages[mi];
|
|
810
855
|
|
|
@@ -1163,6 +1208,7 @@ export function MessageThread({
|
|
|
1163
1208
|
pendingAttachments,
|
|
1164
1209
|
pendingMessageFailed = false,
|
|
1165
1210
|
onRetrySend,
|
|
1211
|
+
scrollOnSend = true,
|
|
1166
1212
|
onRetryExecution,
|
|
1167
1213
|
onEditMessage,
|
|
1168
1214
|
className,
|
|
@@ -1199,6 +1245,22 @@ export function MessageThread({
|
|
|
1199
1245
|
|
|
1200
1246
|
useKeyStability(items);
|
|
1201
1247
|
|
|
1248
|
+
// Scroll-on-send (stigmer-cloud#267): the send moment is the optimistic
|
|
1249
|
+
// message's empty→present transition — the one signal both render paths
|
|
1250
|
+
// share. A monotonic counter (not the message text) carries it down, so
|
|
1251
|
+
// repeated sends of identical text still pin and a retry of a FAILED send
|
|
1252
|
+
// (pending stays present throughout) deliberately does not re-pin.
|
|
1253
|
+
const [sendSignal, setSendSignal] = useState(0);
|
|
1254
|
+
const wasPendingRef = useRef(false);
|
|
1255
|
+
useEffect(() => {
|
|
1256
|
+
const isPending = !!pendingUserMessage;
|
|
1257
|
+
if (isPending && !wasPendingRef.current) {
|
|
1258
|
+
setSendSignal((n) => n + 1);
|
|
1259
|
+
}
|
|
1260
|
+
wasPendingRef.current = isPending;
|
|
1261
|
+
}, [pendingUserMessage]);
|
|
1262
|
+
const pinToLatestSignal = scrollOnSend ? sendSignal : undefined;
|
|
1263
|
+
|
|
1202
1264
|
const filePathCtx = useMemo<FilePathContextValue>(
|
|
1203
1265
|
() => ({
|
|
1204
1266
|
workspaceEntries: workspaceEntries ?? [],
|
|
@@ -1288,6 +1350,7 @@ export function MessageThread({
|
|
|
1288
1350
|
onRetryExecution={onRetryExecution}
|
|
1289
1351
|
onEditMessage={onEditMessage}
|
|
1290
1352
|
slots={slots}
|
|
1353
|
+
pinToLatestSignal={pinToLatestSignal}
|
|
1291
1354
|
/>
|
|
1292
1355
|
</Suspense>
|
|
1293
1356
|
</div>
|
|
@@ -1317,6 +1380,7 @@ export function MessageThread({
|
|
|
1317
1380
|
onRetryExecution={onRetryExecution}
|
|
1318
1381
|
onEditMessage={onEditMessage}
|
|
1319
1382
|
slots={slots}
|
|
1383
|
+
pinToLatestSignal={pinToLatestSignal}
|
|
1320
1384
|
/>
|
|
1321
1385
|
);
|
|
1322
1386
|
}
|
|
@@ -1352,6 +1416,8 @@ interface NonVirtualizedThreadProps {
|
|
|
1352
1416
|
readonly onRetryExecution?: (message: string) => void;
|
|
1353
1417
|
readonly onEditMessage?: (text: string) => void;
|
|
1354
1418
|
readonly slots?: MessageThreadSlots;
|
|
1419
|
+
/** Scroll-on-send counter from the parent (see `usePinToLatestOnSignal`). */
|
|
1420
|
+
readonly pinToLatestSignal?: number;
|
|
1355
1421
|
}
|
|
1356
1422
|
|
|
1357
1423
|
function NonVirtualizedThread({
|
|
@@ -1376,9 +1442,11 @@ function NonVirtualizedThread({
|
|
|
1376
1442
|
onRetryExecution,
|
|
1377
1443
|
onEditMessage,
|
|
1378
1444
|
slots,
|
|
1445
|
+
pinToLatestSignal,
|
|
1379
1446
|
}: NonVirtualizedThreadProps) {
|
|
1380
1447
|
const { scrollRef, sentinelRef, contentRef, isFollowing, jumpToLatest } =
|
|
1381
1448
|
useAutoScroll();
|
|
1449
|
+
usePinToLatestOnSignal(pinToLatestSignal, jumpToLatest);
|
|
1382
1450
|
|
|
1383
1451
|
useDomNodeCount(scrollRef, "MessageThread");
|
|
1384
1452
|
|
|
@@ -1593,6 +1661,10 @@ export function ThreadItemRenderer({
|
|
|
1593
1661
|
/>
|
|
1594
1662
|
);
|
|
1595
1663
|
}
|
|
1664
|
+
case "recalled-memories": {
|
|
1665
|
+
const Recalled = slots?.RecalledMemoriesCard ?? RecalledMemoriesCard;
|
|
1666
|
+
return <Recalled report={item.report} facts={item.facts} />;
|
|
1667
|
+
}
|
|
1596
1668
|
case "context-compacted":
|
|
1597
1669
|
return <SummarizationCard event={item.event} />;
|
|
1598
1670
|
case "liveness": {
|