@opengeni/react 3.8.0-canary.1 → 4.0.1-canary.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/README.md +10 -24
- package/dist/artifacts.js +7 -7
- package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
- package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-7W6PVVTK.js → chunk-CQG7YUJB.js} +2582 -1037
- package/dist/chunk-CQG7YUJB.js.map +1 -0
- package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
- package/dist/chunk-HUMIRRYI.js.map +1 -0
- package/dist/{chunk-7BWFU6H4.js → chunk-Z7WBDB4E.js} +395 -287
- package/dist/chunk-Z7WBDB4E.js.map +1 -0
- package/dist/components/human-input-form.d.ts +3 -1
- package/dist/components/human-input-surface.d.ts +3 -2
- package/dist/components/markdown-table-layout.d.ts +9 -0
- package/dist/components/session-conversation.d.ts +2 -0
- package/dist/composer.js +2 -3
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-codex-accounts.d.ts +11 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -34
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/markdown-table-layout-3Z3AWBY7.js +71 -0
- package/dist/markdown-table-layout-3Z3AWBY7.js.map +1 -0
- package/dist/older-history.d.ts +3 -2
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
- package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
- package/dist/realtime.js +3 -3
- package/dist/session-ui.js +6 -9
- package/dist/session.js +1 -1
- package/dist/timeline/types.d.ts +3 -0
- package/package.json +2 -6
- package/src/components/human-input-form.tsx +185 -53
- package/src/components/human-input-surface.tsx +3 -0
- package/src/components/markdown-table-layout.ts +85 -0
- package/src/components/markdown.tsx +20 -2
- package/src/components/message-timeline.tsx +47 -7
- package/src/components/session-commands-panel.tsx +7 -1
- package/src/components/session-conversation.tsx +4 -1
- package/src/hooks/use-codex-accounts.ts +32 -5
- package/src/hooks/use-session-events.ts +16 -277
- package/src/index.ts +0 -1
- package/src/older-history.ts +22 -4
- package/src/timeline/platform-activity-row.tsx +4 -1
- package/src/timeline/projection.ts +99 -9
- package/src/timeline/types.ts +3 -0
- package/dist/chat.d.ts +0 -37
- package/dist/chat.js +0 -315
- package/dist/chat.js.map +0 -1
- package/dist/chunk-7BWFU6H4.js.map +0 -1
- package/dist/chunk-7W6PVVTK.js.map +0 -1
- package/dist/chunk-BXJOQFCD.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-YAIPFO5D.js +0 -1416
- package/dist/chunk-YAIPFO5D.js.map +0 -1
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/src/chat.tsx +0 -393
- /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
- /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function markdownTableWidth({
|
|
2
|
+
columnLeft,
|
|
3
|
+
columnWidth,
|
|
4
|
+
contentLeft,
|
|
5
|
+
contentRight,
|
|
6
|
+
preferredWidth,
|
|
7
|
+
}: {
|
|
8
|
+
columnLeft: number;
|
|
9
|
+
columnWidth: number;
|
|
10
|
+
contentLeft: number;
|
|
11
|
+
contentRight: number;
|
|
12
|
+
preferredWidth: number;
|
|
13
|
+
}): number {
|
|
14
|
+
const center = columnLeft + columnWidth / 2;
|
|
15
|
+
const available = 2 * Math.min(center - contentLeft, contentRight - center);
|
|
16
|
+
return Math.max(columnWidth, Math.min(preferredWidth, available));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Loaded only for assistant tables. Keep prose and nested scroll owners intact. */
|
|
20
|
+
export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLTableElement) {
|
|
21
|
+
const body = wrapper.parentElement;
|
|
22
|
+
const scroller = body
|
|
23
|
+
?.closest("[data-og-wide-table-message]")
|
|
24
|
+
?.closest<HTMLElement>("[data-og-timeline-scroller]");
|
|
25
|
+
if (!body?.classList.contains("og-markdown-body") || !scroller) return;
|
|
26
|
+
|
|
27
|
+
let width: number | null = null;
|
|
28
|
+
const reset = () => {
|
|
29
|
+
width = null;
|
|
30
|
+
wrapper.style.width = "";
|
|
31
|
+
wrapper.style.maxWidth = "";
|
|
32
|
+
wrapper.style.marginInline = "";
|
|
33
|
+
};
|
|
34
|
+
const measure = () => {
|
|
35
|
+
// Intermediate clipped/scrollable surfaces own their own content bounds.
|
|
36
|
+
for (
|
|
37
|
+
let ancestor: HTMLElement | null = body;
|
|
38
|
+
ancestor && ancestor !== scroller;
|
|
39
|
+
ancestor = ancestor.parentElement
|
|
40
|
+
) {
|
|
41
|
+
if (getComputedStyle(ancestor).overflowX !== "visible") {
|
|
42
|
+
reset();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const column = body.getBoundingClientRect();
|
|
47
|
+
const panel = scroller.getBoundingClientRect();
|
|
48
|
+
const panelStyle = getComputedStyle(scroller);
|
|
49
|
+
const left = panel.left + scroller.clientLeft + parseFloat(panelStyle.paddingLeft);
|
|
50
|
+
const right =
|
|
51
|
+
panel.left + scroller.clientLeft + scroller.clientWidth - parseFloat(panelStyle.paddingRight);
|
|
52
|
+
|
|
53
|
+
// Measure the original table, preserving its wrapping and TSV copy DOM.
|
|
54
|
+
const previousWidth = table.style.width;
|
|
55
|
+
let preferred: number;
|
|
56
|
+
try {
|
|
57
|
+
table.style.width = "max-content";
|
|
58
|
+
preferred = table.getBoundingClientRect().width;
|
|
59
|
+
} finally {
|
|
60
|
+
table.style.width = previousWidth;
|
|
61
|
+
}
|
|
62
|
+
const next = markdownTableWidth({
|
|
63
|
+
columnLeft: column.left,
|
|
64
|
+
columnWidth: column.width,
|
|
65
|
+
contentLeft: left,
|
|
66
|
+
contentRight: right,
|
|
67
|
+
preferredWidth: preferred,
|
|
68
|
+
});
|
|
69
|
+
if (width !== null && Math.abs(width - next) < 0.5) return;
|
|
70
|
+
width = next;
|
|
71
|
+
wrapper.style.width = `${width}px`;
|
|
72
|
+
wrapper.style.maxWidth = "none";
|
|
73
|
+
wrapper.style.marginInline = `calc((100% - ${width}px) / 2)`;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
measure();
|
|
77
|
+
const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(measure);
|
|
78
|
+
observer?.observe(scroller);
|
|
79
|
+
observer?.observe(body);
|
|
80
|
+
observer?.observe(table);
|
|
81
|
+
return () => {
|
|
82
|
+
observer?.disconnect();
|
|
83
|
+
reset();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -411,9 +411,27 @@ function MarkdownCodeBlock({ children }: { children?: ReactNode }) {
|
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
function MarkdownTable({ children, className, ...props }: ComponentPropsWithoutRef<"table">) {
|
|
414
|
-
const
|
|
414
|
+
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
415
|
+
const tableRef = useRef<HTMLTableElement>(null);
|
|
416
|
+
useEffect(() => {
|
|
417
|
+
const wrapper = wrapperRef.current;
|
|
418
|
+
const table = tableRef.current;
|
|
419
|
+
if (!wrapper?.closest("[data-og-wide-table-message]") || !table) return;
|
|
420
|
+
let disposed = false;
|
|
421
|
+
let cleanup: (() => void) | undefined;
|
|
422
|
+
// Ordinary tables remain usable even if this optional layout chunk fails.
|
|
423
|
+
void import("./markdown-table-layout")
|
|
424
|
+
.then(({ observeMarkdownTableLayout }) => {
|
|
425
|
+
if (!disposed) cleanup = observeMarkdownTableLayout(wrapper, table);
|
|
426
|
+
})
|
|
427
|
+
.catch(() => {});
|
|
428
|
+
return () => {
|
|
429
|
+
disposed = true;
|
|
430
|
+
cleanup?.();
|
|
431
|
+
};
|
|
432
|
+
}, [children]);
|
|
415
433
|
return (
|
|
416
|
-
<div className="group/copy relative mt-3 max-w-full first:mt-0">
|
|
434
|
+
<div ref={wrapperRef} className="group/copy relative mt-3 max-w-full first:mt-0">
|
|
417
435
|
<div className="pointer-events-none absolute top-0 right-0 z-10">
|
|
418
436
|
<div className="pointer-events-auto">
|
|
419
437
|
<CopyButton
|
|
@@ -293,13 +293,18 @@ function invokeOlderLoad(
|
|
|
293
293
|
load: OlderHistoryLoader,
|
|
294
294
|
noProgress: () => void,
|
|
295
295
|
attempt: OlderLoadAttempt,
|
|
296
|
+
preserveTail = false,
|
|
296
297
|
): 1 | undefined {
|
|
297
298
|
try {
|
|
298
299
|
// Receipt creation is captured synchronously through legacy wrappers such
|
|
299
300
|
// as `() => void loadOlder()`, even when the wrapper discards the return.
|
|
300
|
-
const result = invokeOlderHistoryLoaderWithReceiptCapture(
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
const result = invokeOlderHistoryLoaderWithReceiptCapture(
|
|
302
|
+
load,
|
|
303
|
+
(receipt) => {
|
|
304
|
+
attempt[2] = receipt;
|
|
305
|
+
},
|
|
306
|
+
preserveTail,
|
|
307
|
+
) as OlderHistoryLoadReceipt | PromiseLike<unknown> | unknown;
|
|
303
308
|
const receipt =
|
|
304
309
|
attempt[2] ??
|
|
305
310
|
(typeof (result as { committed?: unknown } | undefined)?.committed === "boolean"
|
|
@@ -978,7 +983,7 @@ export function MessageTimeline({
|
|
|
978
983
|
// exact `false` is the first-party request-not-accepted receipt.
|
|
979
984
|
// All other fulfillment retains this exact owner until its prepend
|
|
980
985
|
// boundary commits; promise settlement alone cannot prove progress.
|
|
981
|
-
invokeOlderLoad(onLoadOlder, noProgress, attempt);
|
|
986
|
+
invokeOlderLoad(onLoadOlder, noProgress, attempt, !retry);
|
|
982
987
|
},
|
|
983
988
|
[hasOlder, loadingOlder, olderBoundaryKey, onLoadOlder],
|
|
984
989
|
);
|
|
@@ -1536,7 +1541,9 @@ export function MessageTimeline({
|
|
|
1536
1541
|
}
|
|
1537
1542
|
const observer = new IntersectionObserver(
|
|
1538
1543
|
(entries) => {
|
|
1539
|
-
|
|
1544
|
+
// An underfilled history window has both sentinels visible. Advancing
|
|
1545
|
+
// it automatically would undo an explicit older-page navigation.
|
|
1546
|
+
if (maxScrollOf(root) > 1 && entries.some((entry) => entry.isIntersecting)) {
|
|
1540
1547
|
onLoadNewer();
|
|
1541
1548
|
}
|
|
1542
1549
|
},
|
|
@@ -1959,7 +1966,9 @@ export function MessageTimeline({
|
|
|
1959
1966
|
className="pointer-events-auto rounded-full border border-og-border px-3 py-1.5 text-og-control"
|
|
1960
1967
|
>
|
|
1961
1968
|
{underfillRetryReady
|
|
1962
|
-
?
|
|
1969
|
+
? underfillSettledAttempt?.[2]?.tailPreserved
|
|
1970
|
+
? "Load earlier activity"
|
|
1971
|
+
: "Retry earlier activity"
|
|
1963
1972
|
: "Jump to start"}
|
|
1964
1973
|
</button>
|
|
1965
1974
|
) : null}
|
|
@@ -3264,7 +3273,12 @@ function AgentMessageRow({
|
|
|
3264
3273
|
)
|
|
3265
3274
|
}
|
|
3266
3275
|
>
|
|
3267
|
-
<div
|
|
3276
|
+
<div
|
|
3277
|
+
data-og-wide-table-message=""
|
|
3278
|
+
data-og-annotation-source-key={item.annotationSource?.eventId}
|
|
3279
|
+
>
|
|
3280
|
+
{body}
|
|
3281
|
+
</div>
|
|
3268
3282
|
</CopyHoverFrame>
|
|
3269
3283
|
);
|
|
3270
3284
|
}
|
|
@@ -3702,6 +3716,32 @@ function formatVideoDuration(seconds: number): string {
|
|
|
3702
3716
|
|
|
3703
3717
|
function NoticeRow({ item }: { item: NoticeItem }) {
|
|
3704
3718
|
const enter = useEntranceAnimation();
|
|
3719
|
+
if (item.recordedOutcome) {
|
|
3720
|
+
return (
|
|
3721
|
+
<details
|
|
3722
|
+
className="group text-og-sm text-og-fg-muted"
|
|
3723
|
+
role="note"
|
|
3724
|
+
data-og-recorded-outcome="wait"
|
|
3725
|
+
>
|
|
3726
|
+
<summary className="flex cursor-pointer list-none items-center gap-2 py-1 [&::-webkit-details-marker]:hidden">
|
|
3727
|
+
<ChevronRightIcon
|
|
3728
|
+
aria-hidden
|
|
3729
|
+
className="size-3.5 transition-transform group-open:rotate-90"
|
|
3730
|
+
/>
|
|
3731
|
+
<span>
|
|
3732
|
+
Wait recorded ·{" "}
|
|
3733
|
+
<time dateTime={item.occurredAt}>
|
|
3734
|
+
{new Date(item.occurredAt).toLocaleString(undefined, {
|
|
3735
|
+
dateStyle: "medium",
|
|
3736
|
+
timeStyle: "short",
|
|
3737
|
+
})}
|
|
3738
|
+
</time>
|
|
3739
|
+
</span>
|
|
3740
|
+
</summary>
|
|
3741
|
+
<p className="mt-1 whitespace-pre-wrap break-words pl-5 text-og-fg-muted">{item.text}</p>
|
|
3742
|
+
</details>
|
|
3743
|
+
);
|
|
3744
|
+
}
|
|
3705
3745
|
const tone =
|
|
3706
3746
|
item.tone === "failed"
|
|
3707
3747
|
? "border-og-status-failed/35 bg-og-status-failed/10 text-og-status-failed"
|
|
@@ -80,7 +80,13 @@ export function SessionCommandsPanel({
|
|
|
80
80
|
</div>
|
|
81
81
|
</details>
|
|
82
82
|
<p className="mt-1 text-og-fg-subtle">
|
|
83
|
-
{command.
|
|
83
|
+
{command.observationStatus === "unavailable"
|
|
84
|
+
? command.state === "stopping"
|
|
85
|
+
? "Stop requested · status unavailable"
|
|
86
|
+
: "Command status unavailable"
|
|
87
|
+
: command.state === "stopping"
|
|
88
|
+
? "Stopping…"
|
|
89
|
+
: "Running"}
|
|
84
90
|
</p>
|
|
85
91
|
</div>
|
|
86
92
|
{!readOnly ? (
|
|
@@ -9,13 +9,14 @@ import { useComposer } from "../hooks/use-composer";
|
|
|
9
9
|
import { useHumanInputRequests } from "../hooks/use-human-input";
|
|
10
10
|
import { ChatComposer, type ChatComposerProps } from "./chat-composer";
|
|
11
11
|
import { QueueSurface } from "./queue-surface";
|
|
12
|
-
import { HumanInputSurface } from "./human-input-surface";
|
|
12
|
+
import { HumanInputSurface, type HumanInputSurfaceProps } from "./human-input-surface";
|
|
13
13
|
import { MessageTimeline } from "./message-timeline";
|
|
14
14
|
import { conversationTimeline } from "../conversation-timeline";
|
|
15
15
|
import { cn } from "../lib/cn";
|
|
16
16
|
|
|
17
17
|
export type SessionConversationProps = ClientOverride & {
|
|
18
18
|
sessionId: string;
|
|
19
|
+
loadSkillReview?: HumanInputSurfaceProps["loadSkillReview"];
|
|
19
20
|
className?: string;
|
|
20
21
|
/** Defaults to filling the host. The host owns available height. */
|
|
21
22
|
height?: CSSProperties["height"];
|
|
@@ -31,6 +32,7 @@ export function SessionConversation(props: SessionConversationProps) {
|
|
|
31
32
|
|
|
32
33
|
function Conversation({
|
|
33
34
|
sessionId,
|
|
35
|
+
loadSkillReview,
|
|
34
36
|
client,
|
|
35
37
|
workspaceId,
|
|
36
38
|
className,
|
|
@@ -89,6 +91,7 @@ function Conversation({
|
|
|
89
91
|
/>
|
|
90
92
|
<div className="min-h-0 max-h-[40%] shrink-0 overflow-y-auto" data-og-conversation-inputs="">
|
|
91
93
|
<HumanInputSurface
|
|
94
|
+
loadSkillReview={loadSkillReview}
|
|
92
95
|
requests={human.requests}
|
|
93
96
|
onSubmit={async (id, response) => {
|
|
94
97
|
await human.respond(id, response);
|
|
@@ -15,7 +15,16 @@ import {
|
|
|
15
15
|
|
|
16
16
|
/** Events that change which Codex account a session runs on (or just ran). */
|
|
17
17
|
export function isCodexAccountEvent(event: Pick<SessionEvent, "type">): boolean {
|
|
18
|
-
return
|
|
18
|
+
return [
|
|
19
|
+
"codex.account.switched",
|
|
20
|
+
"codex.account.selection.changed",
|
|
21
|
+
"codex.capacity.waiting",
|
|
22
|
+
"codex.capacity.resumed",
|
|
23
|
+
"codex.capacity.superseded",
|
|
24
|
+
"turn.completed",
|
|
25
|
+
"turn.failed",
|
|
26
|
+
"turn.cancelled",
|
|
27
|
+
].includes(event.type);
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
/**
|
|
@@ -30,12 +39,16 @@ export type CodexAccountsClientLike = {
|
|
|
30
39
|
getSession?: (
|
|
31
40
|
workspaceId: string,
|
|
32
41
|
sessionId: string,
|
|
33
|
-
) => Promise<{
|
|
42
|
+
) => Promise<{
|
|
43
|
+
codexPinnedCredentialId?: string | null;
|
|
44
|
+
codexLastCredentialId?: string | null;
|
|
45
|
+
codexCurrentSelection?: { credentialId: string | null; waiting: boolean } | null;
|
|
46
|
+
}>;
|
|
34
47
|
pinSessionCodexAccount?: (
|
|
35
48
|
workspaceId: string,
|
|
36
49
|
sessionId: string,
|
|
37
50
|
target: string,
|
|
38
|
-
) => Promise<{ pinned: string }>;
|
|
51
|
+
) => Promise<{ pinned: string; appliedTo?: "waiting_turn" | "next_turn" }>;
|
|
39
52
|
/** Optional (absent ⇒ the card hides live refresh): batched live /wham/usage refresh. */
|
|
40
53
|
refreshCodexUsage?: (workspaceId: string) => Promise<{ usage: Record<string, unknown> }>;
|
|
41
54
|
};
|
|
@@ -55,8 +68,10 @@ export type UseCodexAccountsResult = {
|
|
|
55
68
|
activeAccountId: string | null;
|
|
56
69
|
/** The session's PINNED account (null ⇒ following workspace active). */
|
|
57
70
|
pinnedAccountId: string | null;
|
|
58
|
-
/**
|
|
71
|
+
/** Current preference only; automatic allocation can choose another account. */
|
|
59
72
|
effectiveAccountId: string | null;
|
|
73
|
+
currentSelection: { credentialId: string | null; waiting: boolean } | null;
|
|
74
|
+
switchAppliedTo: "waiting_turn" | "next_turn" | null;
|
|
60
75
|
/** The account the session's last turn ACTUALLY ran on (the "Running on:" source). */
|
|
61
76
|
lastAccountId: string | null;
|
|
62
77
|
settings: CodexRotationSettings;
|
|
@@ -85,6 +100,7 @@ const EMPTY_SETTINGS: CodexRotationSettings = {
|
|
|
85
100
|
};
|
|
86
101
|
|
|
87
102
|
type CodexAccountsState = {
|
|
103
|
+
currentSelection: { credentialId: string | null; waiting: boolean } | null;
|
|
88
104
|
accounts: CodexAccount[];
|
|
89
105
|
activeAccountId: string | null;
|
|
90
106
|
settings: CodexRotationSettings;
|
|
@@ -93,6 +109,7 @@ type CodexAccountsState = {
|
|
|
93
109
|
};
|
|
94
110
|
|
|
95
111
|
const EMPTY_STATE: CodexAccountsState = {
|
|
112
|
+
currentSelection: null,
|
|
96
113
|
accounts: [],
|
|
97
114
|
activeAccountId: null,
|
|
98
115
|
settings: EMPTY_SETTINGS,
|
|
@@ -121,6 +138,7 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
121
138
|
: Promise.resolve(null);
|
|
122
139
|
const [acc, session] = await Promise.all([accountsP, sessionP]);
|
|
123
140
|
return {
|
|
141
|
+
currentSelection: session?.codexCurrentSelection ?? null,
|
|
124
142
|
accounts: acc.accounts,
|
|
125
143
|
activeAccountId: acc.activeAccountId,
|
|
126
144
|
settings: acc.settings,
|
|
@@ -140,6 +158,10 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
140
158
|
const { run: runMutation, mutating: pinning, mutationError } = useMutationRunner();
|
|
141
159
|
const { run: runUsageMutation, mutating: refreshingUsage } = useMutationRunner();
|
|
142
160
|
const [pinningTarget, setPinningTarget] = useState<string | null>(null);
|
|
161
|
+
const [switchReceipt, setSwitchReceipt] = useState<{
|
|
162
|
+
sessionId: string;
|
|
163
|
+
appliedTo: "waiting_turn" | "next_turn";
|
|
164
|
+
} | null>(null);
|
|
143
165
|
|
|
144
166
|
// Refresh only after the durable post-selection event. `turn.started` is
|
|
145
167
|
// emitted before account selection settles and races this authoritative read.
|
|
@@ -161,8 +183,10 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
161
183
|
return false;
|
|
162
184
|
}
|
|
163
185
|
setPinningTarget(target);
|
|
186
|
+
setSwitchReceipt(null);
|
|
164
187
|
const result = await runMutation(async () => {
|
|
165
|
-
await codexClient.pinSessionCodexAccount!(workspaceId, sessionId, target);
|
|
188
|
+
const receipt = await codexClient.pinSessionCodexAccount!(workspaceId, sessionId, target);
|
|
189
|
+
setSwitchReceipt({ sessionId, appliedTo: receipt.appliedTo ?? "next_turn" });
|
|
166
190
|
return true;
|
|
167
191
|
});
|
|
168
192
|
setPinningTarget(null);
|
|
@@ -191,6 +215,9 @@ export function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCode
|
|
|
191
215
|
const effectiveAccountId = data.pinnedAccountId ?? data.activeAccountId;
|
|
192
216
|
|
|
193
217
|
return {
|
|
218
|
+
currentSelection: data.currentSelection,
|
|
219
|
+
switchAppliedTo:
|
|
220
|
+
switchReceipt?.sessionId === sessionId ? (switchReceipt?.appliedTo ?? null) : null,
|
|
194
221
|
accounts: data.accounts,
|
|
195
222
|
activeAccountId: data.activeAccountId,
|
|
196
223
|
pinnedAccountId: data.pinnedAccountId,
|