@opengeni/react 0.48.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -2
- package/dist/artifacts-document.d.ts +3 -0
- package/dist/artifacts-document.js +17 -0
- package/dist/artifacts-document.js.map +1 -0
- package/dist/artifacts-presentation.d.ts +3 -0
- package/dist/artifacts-presentation.js +17 -0
- package/dist/artifacts-presentation.js.map +1 -0
- package/dist/artifacts-spreadsheet.d.ts +3 -0
- package/dist/artifacts-spreadsheet.js +17 -0
- package/dist/artifacts-spreadsheet.js.map +1 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +199 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/{chunk-NJ7M2B6Z.js → chunk-5UQPNLIO.js} +16 -8
- package/dist/{chunk-NJ7M2B6Z.js.map → chunk-5UQPNLIO.js.map} +1 -1
- package/dist/{chunk-LHLYWG5R.js → chunk-6KLX2RH5.js} +461 -38
- package/dist/chunk-6KLX2RH5.js.map +1 -0
- package/dist/chunk-7WCLAEFV.js +2828 -0
- package/dist/chunk-7WCLAEFV.js.map +1 -0
- package/dist/{chunk-I4MPFDRP.js → chunk-B6WPZSTT.js} +104 -104
- package/dist/{chunk-I4MPFDRP.js.map → chunk-B6WPZSTT.js.map} +1 -1
- package/dist/{chunk-EKZH6IDG.js → chunk-C5ZPSSS5.js} +111 -1
- package/dist/chunk-C5ZPSSS5.js.map +1 -0
- package/dist/{chunk-OL6QVXGF.js → chunk-HA46CYQK.js} +152 -8
- package/dist/chunk-HA46CYQK.js.map +1 -0
- package/dist/chunk-HUIKIDM5.js +2148 -0
- package/dist/chunk-HUIKIDM5.js.map +1 -0
- package/dist/{chunk-FWXGSNXT.js → chunk-IGC6A2ZI.js} +585 -34
- package/dist/chunk-IGC6A2ZI.js.map +1 -0
- package/dist/chunk-JVYQRRC4.js +53 -0
- package/dist/chunk-JVYQRRC4.js.map +1 -0
- package/dist/chunk-KCGXPP5V.js +157 -0
- package/dist/chunk-KCGXPP5V.js.map +1 -0
- package/dist/chunk-LLOLUHVG.js +5937 -0
- package/dist/chunk-LLOLUHVG.js.map +1 -0
- package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
- package/dist/chunk-MKPIAGVL.js.map +1 -0
- package/dist/chunk-PEI6RANI.js +3234 -0
- package/dist/chunk-PEI6RANI.js.map +1 -0
- package/dist/chunk-R3AZOAB5.js +933 -0
- package/dist/chunk-R3AZOAB5.js.map +1 -0
- package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
- package/dist/chunk-WSK7G5LE.js.map +1 -0
- package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
- package/dist/chunk-XBZWOM2K.js.map +1 -0
- package/dist/{chunk-XCNTXWBB.js → chunk-Y7OEDUJ5.js} +856 -338
- package/dist/chunk-Y7OEDUJ5.js.map +1 -0
- package/dist/client.d.ts +10 -2
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +7 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/types.d.ts +1 -1
- package/dist/components/approval-surface.d.ts +2 -2
- package/dist/components/artifacts/artifact-surface.d.ts +18 -0
- package/dist/components/artifacts/document-editor.d.ts +172 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
- package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
- package/dist/components/artifacts/editable-document.d.ts +24 -0
- package/dist/components/artifacts/editable-presentation.d.ts +24 -0
- package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
- package/dist/components/artifacts/index.d.ts +9 -0
- package/dist/components/artifacts/presentation-editor.d.ts +297 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
- package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
- package/dist/components/browser-input.d.ts +7 -0
- package/dist/components/browser-viewer.d.ts +30 -0
- package/dist/components/chat-composer.d.ts +8 -8
- package/dist/components/code-editor.d.ts +25 -3
- package/dist/components/command-palette.d.ts +2 -2
- package/dist/components/composer-transcription-control.d.ts +2 -2
- package/dist/components/composer.d.ts +17 -17
- package/dist/components/computer-viewer.d.ts +24 -0
- package/dist/components/copy-button.d.ts +2 -2
- package/dist/components/desktop-viewer.d.ts +50 -22
- package/dist/components/diff-view.d.ts +1 -1
- package/dist/components/enrollment-consent.d.ts +1 -1
- package/dist/components/enrollment-device-flow.d.ts +1 -1
- package/dist/components/file-browser.d.ts +2 -2
- package/dist/components/fleet-tile.d.ts +1 -1
- package/dist/components/generated-video-player.d.ts +10 -0
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +2 -2
- package/dist/components/interaction-intervention-banner.d.ts +11 -0
- package/dist/components/interactive-workbench-viewers.d.ts +2 -0
- package/dist/components/machine-card.d.ts +2 -2
- package/dist/components/machine-dock-bar.d.ts +3 -3
- package/dist/components/machine-health-pill.d.ts +2 -2
- package/dist/components/machine-input-display.d.ts +1 -1
- package/dist/components/machine-metrics.d.ts +2 -2
- package/dist/components/machine-status-pill.d.ts +4 -4
- package/dist/components/machines/health.d.ts +1 -1
- package/dist/components/machines/machine-detail.d.ts +3 -3
- package/dist/components/machines/metric-history-chart.d.ts +1 -1
- package/dist/components/machines/metric-sparkline.d.ts +2 -2
- package/dist/components/machines/series.d.ts +2 -2
- package/dist/components/machines-dashboard.d.ts +2 -2
- package/dist/components/markdown.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +12 -5
- package/dist/components/model-picker.d.ts +2 -2
- package/dist/components/model-policy-picker.d.ts +7 -7
- package/dist/components/pierre-diff.d.ts +1 -1
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/queue-draft-policy.d.ts +1 -1
- package/dist/components/queue-surface-implementation.d.ts +3 -3
- package/dist/components/queue-surface-state.d.ts +4 -4
- package/dist/components/queue-surface.d.ts +5 -5
- package/dist/components/sandbox-files.d.ts +3 -3
- package/dist/components/sandbox-terminal.d.ts +24 -4
- package/dist/components/sandbox-workspace.d.ts +28 -9
- package/dist/components/session-chrome.d.ts +4 -4
- package/dist/components/session-status.d.ts +2 -2
- package/dist/components/timeline-annotation-selection.d.ts +9 -0
- package/dist/components/timeline-annotations-dialog.d.ts +16 -0
- package/dist/components/timeline-annotations.d.ts +13 -0
- package/dist/components/tooltip.d.ts +4 -4
- package/dist/components/user-message-body.d.ts +2 -2
- package/dist/components/workbench-changes.d.ts +2 -2
- package/dist/components/workspace-dock.d.ts +1 -1
- package/dist/composer.d.ts +11 -11
- package/dist/composer.js +7 -7
- package/dist/hooks/internal.d.ts +1 -1
- package/dist/hooks/use-attached-browsers.d.ts +21 -0
- package/dist/hooks/use-auth-runs.d.ts +32 -0
- package/dist/hooks/use-available-models.d.ts +2 -2
- package/dist/hooks/use-billing-usage.d.ts +1 -1
- package/dist/hooks/use-browser-downloads.d.ts +25 -0
- package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
- package/dist/hooks/use-browser-identities.d.ts +30 -0
- package/dist/hooks/use-browser-session.d.ts +29 -0
- package/dist/hooks/use-browser-sessions.d.ts +32 -0
- package/dist/hooks/use-channels.d.ts +26 -0
- package/dist/hooks/use-codex-accounts.d.ts +2 -2
- package/dist/hooks/use-composer.d.ts +36 -9
- package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
- package/dist/hooks/use-computer-session.d.ts +29 -0
- package/dist/hooks/use-computer-sessions.d.ts +28 -0
- package/dist/hooks/use-desktop-stream.d.ts +5 -1
- package/dist/hooks/use-environments.d.ts +1 -1
- package/dist/hooks/use-file-attachments.d.ts +1 -1
- package/dist/hooks/use-goal.d.ts +2 -2
- package/dist/hooks/use-human-input.d.ts +2 -2
- package/dist/hooks/use-interaction-interventions.d.ts +27 -0
- package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
- package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
- package/dist/hooks/use-machine-chip.d.ts +2 -2
- package/dist/hooks/use-machines.d.ts +4 -4
- package/dist/hooks/use-network-routes.d.ts +25 -0
- package/dist/hooks/use-packs.d.ts +7 -2
- package/dist/hooks/use-rigs.d.ts +1 -1
- package/dist/hooks/use-sandbox-files.d.ts +6 -4
- package/dist/hooks/use-sandbox-git.d.ts +6 -5
- package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
- package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
- package/dist/hooks/use-session-capabilities.d.ts +5 -4
- package/dist/hooks/use-session-control.d.ts +1 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/hooks/use-session-lineage.d.ts +1 -1
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session.d.ts +2 -2
- package/dist/hooks/use-site-auth-connections.d.ts +25 -0
- package/dist/hooks/use-slash-commands.d.ts +1 -1
- package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
- package/dist/hooks/use-terminal-stream.d.ts +11 -2
- package/dist/hooks/use-turn-queue.d.ts +2 -2
- package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
- package/dist/hooks/use-voice-input.d.ts +1 -1
- package/dist/hooks/use-workspace-capture.d.ts +1 -1
- package/dist/hooks/use-workspace-edit.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +1 -1
- package/dist/hooks/use-workspaces.d.ts +1 -1
- package/dist/index.d.ts +162 -155
- package/dist/index.js +1822 -1703
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +33 -0
- package/dist/interaction.js +550 -0
- package/dist/interaction.js.map +1 -0
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
- package/dist/interactive-workbench-viewers-TOHBXJEE.js.map +1 -0
- package/dist/lib/host-platform.d.ts +5 -0
- package/dist/lib/relay-wire.d.ts +24 -1
- package/dist/lib/sandbox-liveness.d.ts +9 -0
- package/dist/lib/terminal-capability.d.ts +5 -0
- package/dist/lib/xterm-theme.d.ts +1 -1
- package/dist/machines.d.ts +30 -30
- package/dist/machines.js +2 -2
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +6 -5
- package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
- package/dist/realtime/dropdown-menu.d.ts +8 -8
- package/dist/realtime/realtime-control.d.ts +8 -8
- package/dist/realtime.d.ts +4 -4
- package/dist/realtime.js +38 -11
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +28 -2
- package/dist/session-ui.d.ts +14 -14
- package/dist/session-ui.js +5 -4
- package/dist/session.d.ts +28 -28
- package/dist/session.js +10 -10
- package/dist/timeline/activity-rail.d.ts +5 -3
- package/dist/timeline/disclosure-context.d.ts +1 -1
- package/dist/timeline/fleet-decision-projection.d.ts +1 -1
- package/dist/timeline/fleet-decision-row.d.ts +3 -3
- package/dist/timeline/index.d.ts +19 -19
- package/dist/timeline/parsers.d.ts +4 -1
- package/dist/timeline/projection.d.ts +3 -3
- package/dist/timeline/registry.d.ts +8 -2
- package/dist/timeline/screenshot-lightbox.d.ts +2 -2
- package/dist/timeline/shared.d.ts +21 -10
- package/dist/timeline/tool-diff.d.ts +2 -2
- package/dist/timeline/tool-renderers.d.ts +1 -1
- package/dist/timeline/turn-summary.d.ts +3 -3
- package/dist/timeline/types.d.ts +44 -3
- package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
- package/package.json +36 -7
- package/src/artifacts-document.ts +31 -0
- package/src/artifacts-presentation.ts +36 -0
- package/src/artifacts-spreadsheet.ts +21 -0
- package/src/artifacts.ts +89 -0
- package/src/client.ts +157 -1
- package/src/clipboard.ts +2 -0
- package/src/components/artifacts/artifact-surface.tsx +93 -0
- package/src/components/artifacts/document-editor.tsx +2255 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
- package/src/components/artifacts/editable-document.tsx +657 -0
- package/src/components/artifacts/editable-presentation.tsx +1020 -0
- package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
- package/src/components/artifacts/index.ts +104 -0
- package/src/components/artifacts/presentation-editor.tsx +3148 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
- package/src/components/browser-input.ts +65 -0
- package/src/components/browser-viewer.tsx +2702 -0
- package/src/components/chat-composer.tsx +13 -0
- package/src/components/code-editor.tsx +89 -30
- package/src/components/composer-transcription-control.tsx +10 -6
- package/src/components/computer-viewer.tsx +1636 -0
- package/src/components/desktop-viewer.tsx +215 -74
- package/src/components/generated-video-player.tsx +89 -0
- package/src/components/interaction-intervention-banner.tsx +99 -0
- package/src/components/interactive-workbench-viewers.ts +2 -0
- package/src/components/machine-input-display.ts +3 -0
- package/src/components/machines/machine-detail.tsx +1 -1
- package/src/components/message-timeline.tsx +289 -32
- package/src/components/model-policy-picker.tsx +15 -0
- package/src/components/queue-surface-implementation.tsx +116 -60
- package/src/components/sandbox-files.tsx +2 -2
- package/src/components/sandbox-terminal.tsx +128 -46
- package/src/components/sandbox-workspace.tsx +256 -90
- package/src/components/session-chrome.tsx +53 -13
- package/src/components/timeline-annotation-selection.tsx +221 -0
- package/src/components/timeline-annotations-dialog.tsx +213 -0
- package/src/components/timeline-annotations.tsx +84 -0
- package/src/components/workbench-changes.tsx +188 -31
- package/src/components/workspace-dock.tsx +59 -5
- package/src/hooks/use-attached-browsers.ts +154 -0
- package/src/hooks/use-auth-runs.ts +278 -0
- package/src/hooks/use-browser-downloads.ts +218 -0
- package/src/hooks/use-browser-frame-stream.ts +425 -0
- package/src/hooks/use-browser-identities.ts +266 -0
- package/src/hooks/use-browser-session.ts +543 -0
- package/src/hooks/use-browser-sessions.ts +286 -0
- package/src/hooks/use-channels.ts +99 -0
- package/src/hooks/use-composer.ts +769 -24
- package/src/hooks/use-computer-frame-stream.ts +434 -0
- package/src/hooks/use-computer-session.ts +462 -0
- package/src/hooks/use-computer-sessions.ts +251 -0
- package/src/hooks/use-desktop-stream.ts +39 -7
- package/src/hooks/use-interaction-interventions.ts +242 -0
- package/src/hooks/use-interaction-invalidation.ts +66 -0
- package/src/hooks/use-machine-chip.ts +15 -3
- package/src/hooks/use-machines.ts +33 -3
- package/src/hooks/use-network-routes.ts +214 -0
- package/src/hooks/use-packs.ts +51 -0
- package/src/hooks/use-sandbox-files.ts +98 -55
- package/src/hooks/use-sandbox-git.ts +93 -38
- package/src/hooks/use-sandbox-terminal.ts +3 -2
- package/src/hooks/use-session-capabilities.ts +59 -46
- package/src/hooks/use-site-auth-connections.ts +226 -0
- package/src/hooks/use-supergrok-accounts.ts +81 -0
- package/src/hooks/use-terminal-stream.ts +359 -47
- package/src/hooks/use-video-artifact-playback.ts +28 -0
- package/src/hooks/use-workspace-edit.ts +2 -1
- package/src/index.ts +18 -0
- package/src/interaction.ts +101 -0
- package/src/lib/host-platform.ts +10 -0
- package/src/lib/relay-wire.ts +47 -4
- package/src/lib/sandbox-liveness.ts +11 -0
- package/src/lib/terminal-capability.ts +10 -0
- package/src/model-policy.ts +19 -4
- package/src/provider.tsx +133 -1
- package/src/realtime/realtime-control.tsx +34 -6
- package/src/session-context.ts +156 -1
- package/src/timeline/activity-rail.tsx +25 -3
- package/src/timeline/index.ts +3 -0
- package/src/timeline/parsers.ts +14 -1
- package/src/timeline/projection.ts +269 -14
- package/src/timeline/registry.ts +13 -1
- package/src/timeline/screenshot-lightbox.tsx +19 -9
- package/src/timeline/shared.tsx +56 -8
- package/src/timeline/tool-renderers.tsx +303 -50
- package/src/timeline/types.ts +57 -3
- package/styles/compiled.css +312 -21
- package/styles/index.css +15 -0
- package/styles/responsive.css +13 -1
- package/dist/chunk-23EJ676W.js.map +0 -1
- package/dist/chunk-EKZH6IDG.js.map +0 -1
- package/dist/chunk-FWXGSNXT.js.map +0 -1
- package/dist/chunk-LHLYWG5R.js.map +0 -1
- package/dist/chunk-O4VXOBGF.js.map +0 -1
- package/dist/chunk-OL6QVXGF.js.map +0 -1
- package/dist/chunk-WYA65Y3F.js.map +0 -1
- package/dist/chunk-XCNTXWBB.js.map +0 -1
- package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
- package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
- package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-KZ73RL76.js";
|
|
4
4
|
import {
|
|
5
5
|
useEmbeddedSession
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C5ZPSSS5.js";
|
|
7
7
|
|
|
8
8
|
// src/hooks/use-composer.ts
|
|
9
9
|
import {
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
} from "@opengeni/sdk";
|
|
12
12
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
13
13
|
var PENDING_COMPOSER_STORAGE_PREFIX = "opengeni.pending-composer.v1:";
|
|
14
|
+
var OPTIMISTIC_SEND_STORAGE_PREFIX = "opengeni.optimistic-sends.v1:";
|
|
14
15
|
var pendingComposerOperations = /* @__PURE__ */ new Map();
|
|
16
|
+
var optimisticSendOperations = /* @__PURE__ */ new Map();
|
|
15
17
|
function pendingComposerOperationKey(workspaceId, sessionId) {
|
|
16
18
|
return sessionId ? `${workspaceId}\0${sessionId}` : null;
|
|
17
19
|
}
|
|
@@ -25,6 +27,50 @@ function pendingComposerStorage() {
|
|
|
25
27
|
function pendingComposerStorageKey(key) {
|
|
26
28
|
return `${PENDING_COMPOSER_STORAGE_PREFIX}${encodeURIComponent(key)}`;
|
|
27
29
|
}
|
|
30
|
+
function optimisticSendStorageKey(key) {
|
|
31
|
+
return `${OPTIMISTIC_SEND_STORAGE_PREFIX}${encodeURIComponent(key)}`;
|
|
32
|
+
}
|
|
33
|
+
function restoreOptimisticSendOperations(key) {
|
|
34
|
+
if (!key) return [];
|
|
35
|
+
let stored = optimisticSendOperations.get(key);
|
|
36
|
+
if (!stored) {
|
|
37
|
+
const storage = pendingComposerStorage();
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(storage?.getItem(optimisticSendStorageKey(key)) ?? "[]");
|
|
40
|
+
stored = Array.isArray(parsed) ? parsed.filter(
|
|
41
|
+
(candidate) => typeof candidate === "object" && candidate !== null && typeof candidate.clientEventId === "string" && typeof candidate.text === "string" && typeof candidate.occurredAt === "string" && (candidate.state === "sending" || candidate.state === "failed") && typeof candidate.input === "object" && candidate.input !== null && typeof candidate.input.text === "string" && typeof candidate.hasMcpCredentialUpdates === "boolean"
|
|
42
|
+
) : [];
|
|
43
|
+
} catch {
|
|
44
|
+
stored = [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return stored.map(({ hasMcpCredentialUpdates, ...operation }) => ({
|
|
48
|
+
...operation,
|
|
49
|
+
newerShadow: operation.newerShadow ?? { text: "", resources: [], annotations: [] },
|
|
50
|
+
state: operation.state === "sending" || operation.state === "queued" ? "sending" : operation.state,
|
|
51
|
+
outcomeUnknown: operation.state === "sending" || operation.state === "queued" ? true : operation.outcomeUnknown,
|
|
52
|
+
error: operation.state === "sending" ? "Delivery was interrupted; retry to reconcile this message." : operation.error,
|
|
53
|
+
input: operation.input,
|
|
54
|
+
canRetry: !hasMcpCredentialUpdates
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
function rememberOptimisticSendOperations(key, operations) {
|
|
58
|
+
if (!key) return;
|
|
59
|
+
const stored = operations.filter((operation) => operation.state !== "queued").map((operation) => {
|
|
60
|
+
const { canRetry: _canRetry, input, ...rest } = operation;
|
|
61
|
+
const { mcpCredentialUpdates: _credentials, ...safeInput } = input;
|
|
62
|
+
return {
|
|
63
|
+
...rest,
|
|
64
|
+
input: safeInput,
|
|
65
|
+
hasMcpCredentialUpdates: input.mcpCredentialUpdates !== void 0
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
optimisticSendOperations.set(key, stored);
|
|
69
|
+
try {
|
|
70
|
+
pendingComposerStorage()?.setItem(optimisticSendStorageKey(key), JSON.stringify(stored));
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
28
74
|
function resourceList(value) {
|
|
29
75
|
return Array.isArray(value) && value.every((candidate) => {
|
|
30
76
|
if (typeof candidate !== "object" || candidate === null) return false;
|
|
@@ -32,6 +78,22 @@ function resourceList(value) {
|
|
|
32
78
|
return resource.kind === "file" ? typeof resource.fileId === "string" : resource.kind === "repository" && typeof resource.uri === "string" && typeof resource.ref === "string";
|
|
33
79
|
});
|
|
34
80
|
}
|
|
81
|
+
function timelineAnnotationList(value) {
|
|
82
|
+
return Array.isArray(value) && value.every((candidate) => {
|
|
83
|
+
if (typeof candidate !== "object" || candidate === null) return false;
|
|
84
|
+
const annotation = candidate;
|
|
85
|
+
const source = annotation.source;
|
|
86
|
+
if (typeof source !== "object" || source === null) return false;
|
|
87
|
+
const sourceRecord = source;
|
|
88
|
+
return typeof annotation.id === "string" && typeof annotation.quote === "string" && typeof annotation.note === "string" && typeof sourceRecord.eventId === "string" && typeof sourceRecord.eventType === "string" && typeof sourceRecord.sequence === "number" && (typeof sourceRecord.turnId === "string" || sourceRecord.turnId === null) && typeof sourceRecord.startOffset === "number" && typeof sourceRecord.endOffset === "number" && typeof sourceRecord.contextBefore === "string" && typeof sourceRecord.contextAfter === "string";
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function cloneAnnotations(annotations) {
|
|
92
|
+
return annotations.map((annotation) => ({
|
|
93
|
+
...annotation,
|
|
94
|
+
source: { ...annotation.source }
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
35
97
|
function readStoredPendingComposerOperation(key) {
|
|
36
98
|
const storage = key ? pendingComposerStorage() : null;
|
|
37
99
|
if (!storage || !key) return null;
|
|
@@ -46,10 +108,17 @@ function readStoredPendingComposerOperation(key) {
|
|
|
46
108
|
}
|
|
47
109
|
const inputRecord = input;
|
|
48
110
|
const shadowRecord = shadow;
|
|
49
|
-
if (record.delivery !== "send" && record.delivery !== "steer" || typeof record.draftAtSend !== "string" || !resourceList(record.resourcesAtSend) || typeof shadowRecord.text !== "string" || !resourceList(shadowRecord.resources) || typeof record.clearDraftOnAccept !== "boolean" || typeof record.hasMcpCredentialUpdates !== "boolean" || typeof inputRecord.text !== "string" || typeof inputRecord.clientEventId !== "string" || "mcpCredentialUpdates" in inputRecord || "resources" in inputRecord && !resourceList(inputRecord.resources)) {
|
|
111
|
+
if (record.delivery !== "send" && record.delivery !== "steer" || typeof record.draftAtSend !== "string" || !resourceList(record.resourcesAtSend) || record.annotationsAtSend !== void 0 && !timelineAnnotationList(record.annotationsAtSend) || typeof shadowRecord.text !== "string" || !resourceList(shadowRecord.resources) || shadowRecord.annotations !== void 0 && !timelineAnnotationList(shadowRecord.annotations) || typeof record.clearDraftOnAccept !== "boolean" || typeof record.hasMcpCredentialUpdates !== "boolean" || typeof inputRecord.text !== "string" || typeof inputRecord.clientEventId !== "string" || "mcpCredentialUpdates" in inputRecord || "resources" in inputRecord && !resourceList(inputRecord.resources) || "annotations" in inputRecord && !timelineAnnotationList(inputRecord.annotations)) {
|
|
50
112
|
return null;
|
|
51
113
|
}
|
|
52
|
-
return
|
|
114
|
+
return {
|
|
115
|
+
...record,
|
|
116
|
+
annotationsAtSend: timelineAnnotationList(record.annotationsAtSend) ? record.annotationsAtSend : [],
|
|
117
|
+
newerShadow: {
|
|
118
|
+
...shadow,
|
|
119
|
+
annotations: timelineAnnotationList(shadowRecord.annotations) ? shadowRecord.annotations : []
|
|
120
|
+
}
|
|
121
|
+
};
|
|
53
122
|
} catch {
|
|
54
123
|
return null;
|
|
55
124
|
}
|
|
@@ -70,7 +139,8 @@ function restorePendingComposerOperation(key) {
|
|
|
70
139
|
input: stored.input,
|
|
71
140
|
newerShadow: stored.newerShadow ?? {
|
|
72
141
|
text: stored.draftAtSend,
|
|
73
|
-
resources: stored.resourcesAtSend
|
|
142
|
+
resources: stored.resourcesAtSend,
|
|
143
|
+
annotations: stored.annotationsAtSend
|
|
74
144
|
},
|
|
75
145
|
canRetry: !stored.hasMcpCredentialUpdates
|
|
76
146
|
};
|
|
@@ -86,7 +156,8 @@ function rememberPendingComposerOperation(key, operation) {
|
|
|
86
156
|
hasMcpCredentialUpdates: operation.input.mcpCredentialUpdates !== void 0,
|
|
87
157
|
newerShadow: {
|
|
88
158
|
text: operation.newerShadow.text,
|
|
89
|
-
resources: [...operation.newerShadow.resources]
|
|
159
|
+
resources: [...operation.newerShadow.resources],
|
|
160
|
+
annotations: cloneAnnotations(operation.newerShadow.annotations)
|
|
90
161
|
}
|
|
91
162
|
};
|
|
92
163
|
pendingComposerOperations.set(key, stored);
|
|
@@ -104,7 +175,14 @@ function forgetPendingComposerOperation(key) {
|
|
|
104
175
|
}
|
|
105
176
|
function updatePendingComposerShadow(key, operation, shadow) {
|
|
106
177
|
if (!key || !operation) return operation;
|
|
107
|
-
const next = {
|
|
178
|
+
const next = {
|
|
179
|
+
...operation,
|
|
180
|
+
newerShadow: {
|
|
181
|
+
...shadow,
|
|
182
|
+
resources: [...shadow.resources],
|
|
183
|
+
annotations: cloneAnnotations(shadow.annotations)
|
|
184
|
+
}
|
|
185
|
+
};
|
|
108
186
|
rememberPendingComposerOperation(key, next);
|
|
109
187
|
return next;
|
|
110
188
|
}
|
|
@@ -140,10 +218,16 @@ function useComposer(sessionId, options = {}) {
|
|
|
140
218
|
const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${durableDrafts ? "durable" : "disabled"}`;
|
|
141
219
|
const pendingOperationKey = pendingComposerOperationKey(workspaceId, sessionId);
|
|
142
220
|
const initialPendingOperation = restorePendingComposerOperation(pendingOperationKey);
|
|
143
|
-
const
|
|
221
|
+
const initialOptimisticSends = restoreOptimisticSendOperations(pendingOperationKey);
|
|
222
|
+
const initialShadow = initialPendingOperation?.newerShadow ?? initialOptimisticSends.at(-1)?.newerShadow;
|
|
144
223
|
const [value, setValue] = useState(() => initialShadow?.text ?? "");
|
|
224
|
+
const [annotations, setAnnotations] = useState(
|
|
225
|
+
() => initialShadow?.annotations ?? []
|
|
226
|
+
);
|
|
227
|
+
const [annotationReviewTargetId, setAnnotationReviewTargetId] = useState(null);
|
|
145
228
|
const [stateTargetKey, setStateTargetKey] = useState(targetKey);
|
|
146
229
|
const [sending, setSending] = useState(false);
|
|
230
|
+
const [optimisticSends, setOptimisticSends] = useState(initialOptimisticSends);
|
|
147
231
|
const [steering, setSteering] = useState(
|
|
148
232
|
() => initialPendingOperation?.delivery === "steer" ? {
|
|
149
233
|
phase: "submitting",
|
|
@@ -154,6 +238,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
154
238
|
} : null
|
|
155
239
|
);
|
|
156
240
|
const [pausing, setPausing] = useState(false);
|
|
241
|
+
const [controlStopping, setControlStopping] = useState(null);
|
|
157
242
|
const [resuming, setResuming] = useState(false);
|
|
158
243
|
const [error, setError] = useState(null);
|
|
159
244
|
const [draft, setDraft] = useState(null);
|
|
@@ -164,12 +249,16 @@ function useComposer(sessionId, options = {}) {
|
|
|
164
249
|
() => initialShadow?.resources ?? []
|
|
165
250
|
);
|
|
166
251
|
const pendingOperationRef = useRef(initialPendingOperation);
|
|
252
|
+
const optimisticSendsRef = useRef(initialOptimisticSends);
|
|
253
|
+
const optimisticProcessorBusyRef = useRef(false);
|
|
254
|
+
const optimisticCallbackIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
167
255
|
const steeringSettlementEventsRef = useRef([]);
|
|
168
256
|
const steeringRef = useRef(steering);
|
|
169
257
|
const pendingClientEventId = useRef(
|
|
170
258
|
initialPendingOperation?.input.clientEventId ?? null
|
|
171
259
|
);
|
|
172
260
|
const valueRef = useRef(initialShadow?.text ?? "");
|
|
261
|
+
const annotationsRef = useRef(initialShadow?.annotations ?? []);
|
|
173
262
|
const draftRef = useRef(null);
|
|
174
263
|
const restoredResourcesRef = useRef(initialShadow?.resources ?? []);
|
|
175
264
|
const localEditRevision = useRef(initialShadow ? 1 : 0);
|
|
@@ -178,6 +267,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
178
267
|
const lastSavedSignature = useRef(null);
|
|
179
268
|
const saveChain = useRef(Promise.resolve());
|
|
180
269
|
const onSent = options.onSent;
|
|
270
|
+
const onSubmitted = options.onSubmitted;
|
|
181
271
|
const onDraftApplied = options.onDraftApplied;
|
|
182
272
|
const onDraftAppliedRef = useRef(onDraftApplied);
|
|
183
273
|
useLayoutEffect(() => {
|
|
@@ -198,18 +288,26 @@ function useComposer(sessionId, options = {}) {
|
|
|
198
288
|
targetGeneration.current += 1;
|
|
199
289
|
draftReadGeneration.current += 1;
|
|
200
290
|
pendingOperationRef.current = restorePendingComposerOperation(pendingOperationKey);
|
|
291
|
+
const restoredOptimisticSends = restoreOptimisticSendOperations(pendingOperationKey);
|
|
292
|
+
optimisticSendsRef.current = restoredOptimisticSends;
|
|
293
|
+
optimisticProcessorBusyRef.current = false;
|
|
294
|
+
optimisticCallbackIdsRef.current = /* @__PURE__ */ new Set();
|
|
201
295
|
steeringSettlementEventsRef.current = [];
|
|
202
296
|
pendingClientEventId.current = pendingOperationRef.current?.input.clientEventId ?? null;
|
|
203
|
-
const shadow = pendingOperationRef.current?.newerShadow;
|
|
297
|
+
const shadow = pendingOperationRef.current?.newerShadow ?? restoredOptimisticSends.at(-1)?.newerShadow;
|
|
204
298
|
localEditRevision.current = shadow ? 1 : 0;
|
|
205
299
|
valueRef.current = shadow?.text ?? "";
|
|
300
|
+
annotationsRef.current = shadow?.annotations ?? [];
|
|
206
301
|
draftRef.current = null;
|
|
207
302
|
restoredResourcesRef.current = shadow?.resources ?? [];
|
|
208
303
|
lastSavedSignature.current = null;
|
|
209
304
|
saveChain.current = Promise.resolve();
|
|
210
305
|
setStateTargetKey(targetKey);
|
|
211
306
|
setValue(shadow?.text ?? "");
|
|
307
|
+
setAnnotations(shadow?.annotations ?? []);
|
|
308
|
+
setAnnotationReviewTargetId(null);
|
|
212
309
|
setSending(false);
|
|
310
|
+
setOptimisticSends(restoredOptimisticSends);
|
|
213
311
|
setSteering(
|
|
214
312
|
pendingOperationRef.current?.delivery === "steer" ? {
|
|
215
313
|
phase: "submitting",
|
|
@@ -220,6 +318,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
220
318
|
} : null
|
|
221
319
|
);
|
|
222
320
|
setPausing(false);
|
|
321
|
+
setControlStopping(null);
|
|
223
322
|
setResuming(false);
|
|
224
323
|
setError(null);
|
|
225
324
|
setDraft(null);
|
|
@@ -228,17 +327,36 @@ function useComposer(sessionId, options = {}) {
|
|
|
228
327
|
setDraftConflict(null);
|
|
229
328
|
setRestoredResources(shadow?.resources ?? []);
|
|
230
329
|
}, [durableDrafts, pendingOperationKey, sessionId, targetKey]);
|
|
330
|
+
const setOptimisticDraftShadow = useCallback(
|
|
331
|
+
(shadow) => {
|
|
332
|
+
if (optimisticSendsRef.current.length === 0) return;
|
|
333
|
+
const next = optimisticSendsRef.current.map((operation) => ({
|
|
334
|
+
...operation,
|
|
335
|
+
newerShadow: {
|
|
336
|
+
text: shadow.text,
|
|
337
|
+
resources: [...shadow.resources],
|
|
338
|
+
annotations: cloneAnnotations(shadow.annotations)
|
|
339
|
+
}
|
|
340
|
+
}));
|
|
341
|
+
optimisticSendsRef.current = next;
|
|
342
|
+
rememberOptimisticSendOperations(pendingOperationKey, next);
|
|
343
|
+
setOptimisticSends(next);
|
|
344
|
+
},
|
|
345
|
+
[pendingOperationKey]
|
|
346
|
+
);
|
|
231
347
|
const applyDraft = useCallback(
|
|
232
348
|
(next) => {
|
|
233
349
|
if (targetKeyRef.current !== targetKey) return;
|
|
234
350
|
if (!durableDrafts) {
|
|
235
351
|
localEditRevision.current += 1;
|
|
236
352
|
valueRef.current = next.text;
|
|
353
|
+
annotationsRef.current = next.annotations ?? [];
|
|
237
354
|
restoredResourcesRef.current = next.resources;
|
|
238
355
|
draftRef.current = null;
|
|
239
356
|
lastSavedSignature.current = null;
|
|
240
357
|
setDraft(null);
|
|
241
358
|
setValue(next.text);
|
|
359
|
+
setAnnotations(next.annotations ?? []);
|
|
242
360
|
setRestoredResources(next.resources);
|
|
243
361
|
pendingOperationRef.current = updatePendingComposerShadow(
|
|
244
362
|
pendingOperationKey,
|
|
@@ -248,19 +366,30 @@ function useComposer(sessionId, options = {}) {
|
|
|
248
366
|
resources: mergeResources(
|
|
249
367
|
next.resources,
|
|
250
368
|
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
251
|
-
)
|
|
369
|
+
),
|
|
370
|
+
annotations: next.annotations ?? []
|
|
252
371
|
}
|
|
253
372
|
);
|
|
373
|
+
setOptimisticDraftShadow({
|
|
374
|
+
text: next.text,
|
|
375
|
+
resources: mergeResources(
|
|
376
|
+
next.resources,
|
|
377
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
378
|
+
),
|
|
379
|
+
annotations: next.annotations ?? []
|
|
380
|
+
});
|
|
254
381
|
setDraftConflict(null);
|
|
255
382
|
return;
|
|
256
383
|
}
|
|
257
384
|
valueRef.current = next.text;
|
|
385
|
+
annotationsRef.current = next.annotations ?? [];
|
|
258
386
|
draftRef.current = next;
|
|
259
387
|
restoredResourcesRef.current = next.resources;
|
|
260
388
|
lastSavedSignature.current = draftSignature(draftPayload(next));
|
|
261
389
|
localEditRevision.current += 1;
|
|
262
390
|
setDraft(next);
|
|
263
391
|
setValue(next.text);
|
|
392
|
+
setAnnotations(next.annotations ?? []);
|
|
264
393
|
setRestoredResources(next.resources);
|
|
265
394
|
pendingOperationRef.current = updatePendingComposerShadow(
|
|
266
395
|
pendingOperationKey,
|
|
@@ -270,13 +399,22 @@ function useComposer(sessionId, options = {}) {
|
|
|
270
399
|
resources: mergeResources(
|
|
271
400
|
next.resources,
|
|
272
401
|
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
273
|
-
)
|
|
402
|
+
),
|
|
403
|
+
annotations: next.annotations ?? []
|
|
274
404
|
}
|
|
275
405
|
);
|
|
406
|
+
setOptimisticDraftShadow({
|
|
407
|
+
text: next.text,
|
|
408
|
+
resources: mergeResources(
|
|
409
|
+
next.resources,
|
|
410
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
411
|
+
),
|
|
412
|
+
annotations: next.annotations ?? []
|
|
413
|
+
});
|
|
276
414
|
setDraftConflict(null);
|
|
277
415
|
onDraftAppliedRef.current?.(next);
|
|
278
416
|
},
|
|
279
|
-
[durableDrafts, pendingOperationKey, targetKey]
|
|
417
|
+
[durableDrafts, pendingOperationKey, setOptimisticDraftShadow, targetKey]
|
|
280
418
|
);
|
|
281
419
|
const loadDraft = useCallback(
|
|
282
420
|
async (replaceLocal) => {
|
|
@@ -295,6 +433,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
295
433
|
baseAtStart,
|
|
296
434
|
valueRef.current,
|
|
297
435
|
restoredResourcesRef.current,
|
|
436
|
+
annotationsRef.current,
|
|
298
437
|
extrasAtStart
|
|
299
438
|
)
|
|
300
439
|
) : null;
|
|
@@ -313,21 +452,25 @@ function useComposer(sessionId, options = {}) {
|
|
|
313
452
|
draftRef.current = fetched;
|
|
314
453
|
setDraft(fetched);
|
|
315
454
|
setDraftConflict(null);
|
|
316
|
-
const shadow = pendingOperationRef.current?.newerShadow;
|
|
455
|
+
const shadow = pendingOperationRef.current?.newerShadow ?? optimisticSendsRef.current.at(-1)?.newerShadow;
|
|
317
456
|
if (shadow) {
|
|
318
457
|
valueRef.current = shadow.text;
|
|
319
458
|
restoredResourcesRef.current = shadow.resources;
|
|
459
|
+
annotationsRef.current = shadow.annotations;
|
|
320
460
|
localEditRevision.current ||= 1;
|
|
321
461
|
setValue(shadow.text);
|
|
322
462
|
setRestoredResources(shadow.resources);
|
|
463
|
+
setAnnotations(shadow.annotations);
|
|
323
464
|
} else if (replaceLocal || !localWasDirtyAtStart && localAtStart === localEditRevision.current) {
|
|
324
465
|
const extrasNow = resolveSendExtras(sendExtrasRef.current);
|
|
325
466
|
const pickerChangedDuringFetch = extrasNow.model !== extrasAtStart.model || extrasNow.reasoningEffort !== extrasAtStart.reasoningEffort || extrasNow.latencyMode !== extrasAtStart.latencyMode;
|
|
326
467
|
valueRef.current = fetched.text;
|
|
327
468
|
restoredResourcesRef.current = fetched.resources;
|
|
469
|
+
annotationsRef.current = fetched.annotations ?? [];
|
|
328
470
|
lastSavedSignature.current = draftSignature(draftPayload(fetched));
|
|
329
471
|
setValue(fetched.text);
|
|
330
472
|
setRestoredResources(fetched.resources);
|
|
473
|
+
setAnnotations(fetched.annotations ?? []);
|
|
331
474
|
if (replaceLocal || !pickerChangedDuringFetch) {
|
|
332
475
|
onDraftAppliedRef.current?.(fetched);
|
|
333
476
|
}
|
|
@@ -436,13 +579,24 @@ function useComposer(sessionId, options = {}) {
|
|
|
436
579
|
steeringSettlementEventsRef.current = [];
|
|
437
580
|
setSteering(null);
|
|
438
581
|
}, [options.events, steering]);
|
|
582
|
+
useEffect(() => {
|
|
583
|
+
if (!controlStopping) return;
|
|
584
|
+
const effectiveControl = options.effectiveControl;
|
|
585
|
+
if (!effectiveControl || effectiveControl.controlVersion < controlStopping.controlVersion) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (effectiveControl.controlVersion === controlStopping.controlVersion && effectiveControl.controlEtag === controlStopping.controlEtag && effectiveControl.settlement !== null) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
setControlStopping((current) => current === controlStopping ? null : current);
|
|
592
|
+
}, [controlStopping, options.effectiveControl]);
|
|
439
593
|
const currentDraftPayload = useCallback(() => {
|
|
440
594
|
if (!durableDrafts || targetKeyRef.current !== targetKey) return null;
|
|
441
595
|
const base = draftRef.current;
|
|
442
596
|
if (!base) return null;
|
|
443
597
|
const extras = resolveSendExtras(sendExtrasRef.current);
|
|
444
|
-
return composerDraftPayload(base, value, restoredResources, extras);
|
|
445
|
-
}, [durableDrafts, restoredResources, targetKey, value]);
|
|
598
|
+
return composerDraftPayload(base, value, restoredResources, annotations, extras);
|
|
599
|
+
}, [annotations, durableDrafts, restoredResources, targetKey, value]);
|
|
446
600
|
const persistPayload = useCallback(
|
|
447
601
|
async (payload) => {
|
|
448
602
|
const ownedTargetKey = targetKey;
|
|
@@ -505,6 +659,152 @@ function useComposer(sessionId, options = {}) {
|
|
|
505
659
|
},
|
|
506
660
|
[client, durableDrafts, sessionId, targetKey, workspaceId]
|
|
507
661
|
);
|
|
662
|
+
const replaceOptimisticSends = useCallback(
|
|
663
|
+
(update) => {
|
|
664
|
+
const next = update(optimisticSendsRef.current);
|
|
665
|
+
optimisticSendsRef.current = next;
|
|
666
|
+
rememberOptimisticSendOperations(pendingOperationKey, next);
|
|
667
|
+
setOptimisticSends(next);
|
|
668
|
+
return next;
|
|
669
|
+
},
|
|
670
|
+
[pendingOperationKey]
|
|
671
|
+
);
|
|
672
|
+
const markOptimisticAccepted = useCallback(
|
|
673
|
+
(operation) => {
|
|
674
|
+
if (!optimisticCallbackIdsRef.current.has(operation.clientEventId)) {
|
|
675
|
+
optimisticCallbackIdsRef.current.add(operation.clientEventId);
|
|
676
|
+
onSent?.(operation.input.text, operation.input);
|
|
677
|
+
}
|
|
678
|
+
replaceOptimisticSends(
|
|
679
|
+
(current) => current.filter((candidate) => candidate.clientEventId !== operation.clientEventId)
|
|
680
|
+
);
|
|
681
|
+
},
|
|
682
|
+
[onSent, replaceOptimisticSends]
|
|
683
|
+
);
|
|
684
|
+
useEffect(() => {
|
|
685
|
+
const acceptedIds = new Set(
|
|
686
|
+
(options.events ?? []).filter((event) => event.type === "user.message" && event.clientEventId).map((event) => event.clientEventId)
|
|
687
|
+
);
|
|
688
|
+
if (acceptedIds.size === 0) return;
|
|
689
|
+
const accepted = optimisticSendsRef.current.filter(
|
|
690
|
+
(operation) => acceptedIds.has(operation.clientEventId)
|
|
691
|
+
);
|
|
692
|
+
for (const operation of accepted) markOptimisticAccepted(operation);
|
|
693
|
+
}, [markOptimisticAccepted, options.events]);
|
|
694
|
+
const processOptimisticSends = useCallback(() => {
|
|
695
|
+
if (!sessionId || optimisticProcessorBusyRef.current) return;
|
|
696
|
+
const currentOperations = optimisticSendsRef.current;
|
|
697
|
+
const operationIndex = currentOperations.findIndex(
|
|
698
|
+
(candidate) => candidate.state === "sending"
|
|
699
|
+
);
|
|
700
|
+
const operation = operationIndex < 0 ? void 0 : currentOperations[operationIndex];
|
|
701
|
+
if (!operation) return;
|
|
702
|
+
const ownedTargetKey = targetKey;
|
|
703
|
+
const ownedGeneration = targetGeneration.current;
|
|
704
|
+
optimisticProcessorBusyRef.current = true;
|
|
705
|
+
void (async () => {
|
|
706
|
+
try {
|
|
707
|
+
if (operation.outcomeUnknown) {
|
|
708
|
+
const events = await client.listEvents(workspaceId, sessionId, {
|
|
709
|
+
includeTypes: ["user.message"],
|
|
710
|
+
limit: 100,
|
|
711
|
+
payloadMode: "none"
|
|
712
|
+
});
|
|
713
|
+
if (events.some(
|
|
714
|
+
(event) => event.type === "user.message" && event.clientEventId === operation.clientEventId
|
|
715
|
+
)) {
|
|
716
|
+
markOptimisticAccepted(operation);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (!operation.canRetry) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
"OpenGeni cannot safely retry this uncertain request after remount; reconcile the session before sending again."
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
let expectedDraftRevision;
|
|
726
|
+
if (durableDrafts && operation.draftPayload) {
|
|
727
|
+
if (!await persistPayload(operation.draftPayload)) {
|
|
728
|
+
throw new Error("The message draft could not be saved before delivery.");
|
|
729
|
+
}
|
|
730
|
+
expectedDraftRevision = draftRef.current?.revision;
|
|
731
|
+
}
|
|
732
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const wireInput = {
|
|
736
|
+
...operation.input,
|
|
737
|
+
...expectedDraftRevision !== void 0 ? { expectedDraftRevision } : {}
|
|
738
|
+
};
|
|
739
|
+
await client.sendMessage(workspaceId, sessionId, wireInput);
|
|
740
|
+
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (options.events === void 0) {
|
|
744
|
+
markOptimisticAccepted({ ...operation, input: wireInput });
|
|
745
|
+
} else {
|
|
746
|
+
replaceOptimisticSends(
|
|
747
|
+
(current) => current.map(
|
|
748
|
+
(candidate) => candidate.clientEventId === operation.clientEventId ? { ...candidate, input: wireInput, state: "queued", error: void 0 } : candidate
|
|
749
|
+
)
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
if (!optimisticCallbackIdsRef.current.has(operation.clientEventId)) {
|
|
753
|
+
optimisticCallbackIdsRef.current.add(operation.clientEventId);
|
|
754
|
+
onSent?.(wireInput.text, wireInput);
|
|
755
|
+
}
|
|
756
|
+
if (durableDrafts && draftRef.current) {
|
|
757
|
+
const cleared = {
|
|
758
|
+
...draftRef.current,
|
|
759
|
+
revision: 0,
|
|
760
|
+
text: "",
|
|
761
|
+
resources: [],
|
|
762
|
+
annotations: [],
|
|
763
|
+
sourceTurnId: null,
|
|
764
|
+
sourceTurnVersion: null,
|
|
765
|
+
updatedAt: null
|
|
766
|
+
};
|
|
767
|
+
draftRef.current = cleared;
|
|
768
|
+
setDraft(cleared);
|
|
769
|
+
lastSavedSignature.current = draftSignature(draftPayload(cleared));
|
|
770
|
+
}
|
|
771
|
+
} catch (cause) {
|
|
772
|
+
const problem = asError(cause);
|
|
773
|
+
const outcomeUnknown = isOutcomeUnknownError(cause) || operation.outcomeUnknown === true;
|
|
774
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
775
|
+
replaceOptimisticSends(
|
|
776
|
+
(current) => current.map(
|
|
777
|
+
(candidate) => candidate.clientEventId === operation.clientEventId ? {
|
|
778
|
+
...candidate,
|
|
779
|
+
state: "failed",
|
|
780
|
+
error: problem.message,
|
|
781
|
+
outcomeUnknown
|
|
782
|
+
} : candidate
|
|
783
|
+
)
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
} finally {
|
|
787
|
+
optimisticProcessorBusyRef.current = false;
|
|
788
|
+
if (targetKeyRef.current === ownedTargetKey) {
|
|
789
|
+
setOptimisticSends([...optimisticSendsRef.current]);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
})();
|
|
793
|
+
}, [
|
|
794
|
+
client,
|
|
795
|
+
durableDrafts,
|
|
796
|
+
markOptimisticAccepted,
|
|
797
|
+
onSent,
|
|
798
|
+
options.events,
|
|
799
|
+
persistPayload,
|
|
800
|
+
replaceOptimisticSends,
|
|
801
|
+
sessionId,
|
|
802
|
+
targetKey,
|
|
803
|
+
workspaceId
|
|
804
|
+
]);
|
|
805
|
+
useEffect(() => {
|
|
806
|
+
processOptimisticSends();
|
|
807
|
+
}, [optimisticSends, processOptimisticSends]);
|
|
508
808
|
useEffect(() => {
|
|
509
809
|
const pending = pendingOperationRef.current;
|
|
510
810
|
if (pending) {
|
|
@@ -513,9 +813,10 @@ function useComposer(sessionId, options = {}) {
|
|
|
513
813
|
resources: mergeResources(
|
|
514
814
|
restoredResourcesRef.current,
|
|
515
815
|
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
516
|
-
)
|
|
816
|
+
),
|
|
817
|
+
annotations: annotationsRef.current
|
|
517
818
|
};
|
|
518
|
-
if (pending.newerShadow.text !== shadow.text || JSON.stringify(pending.newerShadow.resources) !== JSON.stringify(shadow.resources)) {
|
|
819
|
+
if (pending.newerShadow.text !== shadow.text || JSON.stringify(pending.newerShadow.resources) !== JSON.stringify(shadow.resources) || JSON.stringify(pending.newerShadow.annotations) !== JSON.stringify(shadow.annotations)) {
|
|
519
820
|
pendingOperationRef.current = updatePendingComposerShadow(
|
|
520
821
|
pendingOperationKey,
|
|
521
822
|
pending,
|
|
@@ -524,6 +825,23 @@ function useComposer(sessionId, options = {}) {
|
|
|
524
825
|
}
|
|
525
826
|
return;
|
|
526
827
|
}
|
|
828
|
+
const optimistic = optimisticSendsRef.current.at(-1);
|
|
829
|
+
if (optimistic) {
|
|
830
|
+
const shadow = {
|
|
831
|
+
text: valueRef.current,
|
|
832
|
+
resources: mergeResources(
|
|
833
|
+
restoredResourcesRef.current,
|
|
834
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
835
|
+
),
|
|
836
|
+
annotations: annotationsRef.current
|
|
837
|
+
};
|
|
838
|
+
if (optimistic.newerShadow.text !== shadow.text || JSON.stringify(optimistic.newerShadow.resources) !== JSON.stringify(shadow.resources) || JSON.stringify(optimistic.newerShadow.annotations) !== JSON.stringify(shadow.annotations)) {
|
|
839
|
+
setOptimisticDraftShadow(shadow);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (optimisticSendsRef.current.some((operation) => operation.state === "sending")) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
527
845
|
if (!durableDrafts || !sessionId || draftLoading || sending || !draftRef.current || draftConflict)
|
|
528
846
|
return;
|
|
529
847
|
const payload = currentDraftPayload();
|
|
@@ -538,6 +856,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
538
856
|
liveExtrasVersion,
|
|
539
857
|
pendingOperationKey,
|
|
540
858
|
persistPayload,
|
|
859
|
+
setOptimisticDraftShadow,
|
|
541
860
|
sending,
|
|
542
861
|
sessionId
|
|
543
862
|
]);
|
|
@@ -552,11 +871,16 @@ function useComposer(sessionId, options = {}) {
|
|
|
552
871
|
pendingClientEventId.current = pending.input.clientEventId ?? null;
|
|
553
872
|
}
|
|
554
873
|
const draftAtSend = value;
|
|
874
|
+
const annotationsAtSend = annotations;
|
|
555
875
|
const rawText = explicit ?? draftAtSend;
|
|
556
876
|
const hasText = rawText.trim().length > 0;
|
|
877
|
+
const hasAnnotations = annotationsAtSend.length > 0;
|
|
878
|
+
const annotationsComplete2 = annotationsAtSend.every(
|
|
879
|
+
(annotation) => annotation.note.trim().length > 0
|
|
880
|
+
);
|
|
557
881
|
const extras = pending ? {} : resolveSendExtras(sendExtrasRef.current);
|
|
558
882
|
const hasResources = restoredResources.length > 0 || (extras.resources?.length ?? 0) > 0;
|
|
559
|
-
if (!pending && !hasText && !hasResources || !sessionId || sending || sendBlockedRef.current?.() === true || targetKeyRef.current !== ownedTargetKey) {
|
|
883
|
+
if (!pending && (!annotationsComplete2 || !hasText && !hasResources && !hasAnnotations) || !sessionId || sending || sendBlockedRef.current?.() === true || targetKeyRef.current !== ownedTargetKey) {
|
|
560
884
|
return false;
|
|
561
885
|
}
|
|
562
886
|
const clearPending = () => {
|
|
@@ -569,6 +893,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
569
893
|
clearPending();
|
|
570
894
|
const draftWasUnchanged = valueRef.current === operation.draftAtSend;
|
|
571
895
|
const resourcesWereUnchanged = JSON.stringify(restoredResourcesRef.current) === JSON.stringify(operation.resourcesAtSend);
|
|
896
|
+
const annotationsWereUnchanged = JSON.stringify(annotationsRef.current) === JSON.stringify(operation.annotationsAtSend);
|
|
572
897
|
const previousDraft = draftRef.current;
|
|
573
898
|
if (previousDraft) {
|
|
574
899
|
const cleared = {
|
|
@@ -576,6 +901,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
576
901
|
revision: 0,
|
|
577
902
|
text: "",
|
|
578
903
|
resources: [],
|
|
904
|
+
annotations: [],
|
|
579
905
|
sourceTurnId: null,
|
|
580
906
|
sourceTurnVersion: null,
|
|
581
907
|
updatedAt: null
|
|
@@ -588,6 +914,11 @@ function useComposer(sessionId, options = {}) {
|
|
|
588
914
|
restoredResourcesRef.current = [];
|
|
589
915
|
setRestoredResources([]);
|
|
590
916
|
}
|
|
917
|
+
if (annotationsWereUnchanged) {
|
|
918
|
+
annotationsRef.current = [];
|
|
919
|
+
setAnnotations([]);
|
|
920
|
+
setAnnotationReviewTargetId(null);
|
|
921
|
+
}
|
|
591
922
|
if (operation.clearDraftOnAccept && draftWasUnchanged) {
|
|
592
923
|
valueRef.current = "";
|
|
593
924
|
setValue("");
|
|
@@ -664,7 +995,8 @@ function useComposer(sessionId, options = {}) {
|
|
|
664
995
|
text: pending.input.text,
|
|
665
996
|
clientEventId: pending.input.clientEventId ?? null,
|
|
666
997
|
triggerEventId: result.accepted.id,
|
|
667
|
-
turnId: result.turn.id
|
|
998
|
+
turnId: result.turn.id,
|
|
999
|
+
stoppingPreviousAttempt: result.replay !== true && (result.interruptionCount ?? 0) > 0
|
|
668
1000
|
});
|
|
669
1001
|
}
|
|
670
1002
|
} catch (cause) {
|
|
@@ -680,7 +1012,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
680
1012
|
settleAccepted(pending);
|
|
681
1013
|
return true;
|
|
682
1014
|
}
|
|
683
|
-
const sendText = hasText ? rawText : FILE_ONLY_MESSAGE_TEXT;
|
|
1015
|
+
const sendText = hasText ? rawText : hasAnnotations ? "" : FILE_ONLY_MESSAGE_TEXT;
|
|
684
1016
|
const currentPayload = currentDraftPayload();
|
|
685
1017
|
const payload = currentPayload ? { ...currentPayload, text: sendText } : null;
|
|
686
1018
|
if (payload && !await persistPayload(payload)) return false;
|
|
@@ -691,16 +1023,19 @@ function useComposer(sessionId, options = {}) {
|
|
|
691
1023
|
const input = composeSendInput(sendText, pendingClientEventId.current, extras, {
|
|
692
1024
|
...options.effectiveControl?.controlEtag ? { controlEtag: options.effectiveControl.controlEtag } : {},
|
|
693
1025
|
...durableDrafts && draftRef.current ? { expectedDraftRevision: draftRef.current.revision } : {},
|
|
694
|
-
resources: mergeResources(restoredResources, extras.resources ?? [])
|
|
1026
|
+
resources: mergeResources(restoredResources, extras.resources ?? []),
|
|
1027
|
+
annotations: cloneAnnotations(annotationsAtSend)
|
|
695
1028
|
});
|
|
696
1029
|
const operation = {
|
|
697
1030
|
delivery,
|
|
698
1031
|
input,
|
|
699
1032
|
draftAtSend,
|
|
700
1033
|
resourcesAtSend: [...restoredResources],
|
|
1034
|
+
annotationsAtSend: cloneAnnotations(annotationsAtSend),
|
|
701
1035
|
newerShadow: {
|
|
702
1036
|
text: draftAtSend,
|
|
703
|
-
resources: mergeResources(restoredResources, extras.resources ?? [])
|
|
1037
|
+
resources: mergeResources(restoredResources, extras.resources ?? []),
|
|
1038
|
+
annotations: cloneAnnotations(annotationsAtSend)
|
|
704
1039
|
},
|
|
705
1040
|
clearDraftOnAccept: explicit === void 0,
|
|
706
1041
|
canRetry: true
|
|
@@ -725,7 +1060,8 @@ function useComposer(sessionId, options = {}) {
|
|
|
725
1060
|
text: sendText,
|
|
726
1061
|
clientEventId: input.clientEventId ?? null,
|
|
727
1062
|
triggerEventId: result.accepted.id,
|
|
728
|
-
turnId: result.turn.id
|
|
1063
|
+
turnId: result.turn.id,
|
|
1064
|
+
stoppingPreviousAttempt: result.replay !== true && (result.interruptionCount ?? 0) > 0
|
|
729
1065
|
});
|
|
730
1066
|
}
|
|
731
1067
|
} catch (cause) {
|
|
@@ -759,6 +1095,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
759
1095
|
options.effectiveControl?.controlEtag,
|
|
760
1096
|
persistPayload,
|
|
761
1097
|
restoredResources,
|
|
1098
|
+
annotations,
|
|
762
1099
|
sending,
|
|
763
1100
|
sessionId,
|
|
764
1101
|
targetKey,
|
|
@@ -766,10 +1103,126 @@ function useComposer(sessionId, options = {}) {
|
|
|
766
1103
|
workspaceId
|
|
767
1104
|
]
|
|
768
1105
|
);
|
|
769
|
-
const send = useCallback(
|
|
1106
|
+
const send = useCallback(
|
|
1107
|
+
async (explicit) => {
|
|
1108
|
+
if (pendingOperationRef.current?.delivery === "send") {
|
|
1109
|
+
return await dispatch("send", explicit);
|
|
1110
|
+
}
|
|
1111
|
+
const rawText = explicit ?? valueRef.current;
|
|
1112
|
+
const annotationsAtSend = cloneAnnotations(annotationsRef.current);
|
|
1113
|
+
const hasText = rawText.trim().length > 0;
|
|
1114
|
+
const hasAnnotations = annotationsAtSend.length > 0;
|
|
1115
|
+
const annotationsComplete2 = annotationsAtSend.every(
|
|
1116
|
+
(annotation) => annotation.note.trim().length > 0
|
|
1117
|
+
);
|
|
1118
|
+
const extras = resolveSendExtras(sendExtrasRef.current);
|
|
1119
|
+
const resources = mergeResources(restoredResourcesRef.current, extras.resources ?? []);
|
|
1120
|
+
if (!sessionId || sending || !annotationsComplete2 || !hasText && !hasAnnotations && resources.length === 0 || sendBlockedRef.current?.() === true || targetKeyRef.current !== targetKey) {
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
const sendText = hasText ? rawText : hasAnnotations ? "" : FILE_ONLY_MESSAGE_TEXT;
|
|
1124
|
+
const clientEventId = generateClientEventId();
|
|
1125
|
+
const input = composeSendInput(sendText, clientEventId, extras, {
|
|
1126
|
+
...options.effectiveControl?.controlEtag ? { controlEtag: options.effectiveControl.controlEtag } : {},
|
|
1127
|
+
resources,
|
|
1128
|
+
annotations: annotationsAtSend
|
|
1129
|
+
});
|
|
1130
|
+
const currentPayload = currentDraftPayload();
|
|
1131
|
+
const operation = {
|
|
1132
|
+
clientEventId,
|
|
1133
|
+
text: sendText,
|
|
1134
|
+
annotations: annotationsAtSend,
|
|
1135
|
+
resources,
|
|
1136
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1137
|
+
state: "sending",
|
|
1138
|
+
input,
|
|
1139
|
+
draftPayload: currentPayload ? { ...currentPayload, text: sendText } : null,
|
|
1140
|
+
newerShadow: {
|
|
1141
|
+
text: explicit === void 0 ? "" : valueRef.current,
|
|
1142
|
+
resources: explicit === void 0 ? [] : [...restoredResourcesRef.current],
|
|
1143
|
+
annotations: explicit === void 0 ? [] : cloneAnnotations(annotationsRef.current)
|
|
1144
|
+
},
|
|
1145
|
+
canRetry: true
|
|
1146
|
+
};
|
|
1147
|
+
replaceOptimisticSends((current) => [...current, operation]);
|
|
1148
|
+
queueMicrotask(processOptimisticSends);
|
|
1149
|
+
setError(null);
|
|
1150
|
+
onSubmitted?.(sendText, input);
|
|
1151
|
+
if (explicit === void 0) {
|
|
1152
|
+
valueRef.current = "";
|
|
1153
|
+
annotationsRef.current = [];
|
|
1154
|
+
restoredResourcesRef.current = [];
|
|
1155
|
+
localEditRevision.current += 1;
|
|
1156
|
+
setValue("");
|
|
1157
|
+
setAnnotations([]);
|
|
1158
|
+
setAnnotationReviewTargetId(null);
|
|
1159
|
+
setRestoredResources([]);
|
|
1160
|
+
}
|
|
1161
|
+
return true;
|
|
1162
|
+
},
|
|
1163
|
+
[
|
|
1164
|
+
currentDraftPayload,
|
|
1165
|
+
dispatch,
|
|
1166
|
+
onSubmitted,
|
|
1167
|
+
options.effectiveControl?.controlEtag,
|
|
1168
|
+
processOptimisticSends,
|
|
1169
|
+
replaceOptimisticSends,
|
|
1170
|
+
sending,
|
|
1171
|
+
sessionId,
|
|
1172
|
+
targetKey
|
|
1173
|
+
]
|
|
1174
|
+
);
|
|
770
1175
|
const steer = useCallback(async (text) => await dispatch("steer", text), [dispatch]);
|
|
1176
|
+
const retryOptimisticMessage = useCallback(
|
|
1177
|
+
(clientEventId) => {
|
|
1178
|
+
replaceOptimisticSends(
|
|
1179
|
+
(current) => current.map((operation) => {
|
|
1180
|
+
if (operation.clientEventId !== clientEventId || operation.state !== "failed") {
|
|
1181
|
+
return operation;
|
|
1182
|
+
}
|
|
1183
|
+
if (operation.outcomeUnknown) {
|
|
1184
|
+
return { ...operation, state: "sending", error: void 0 };
|
|
1185
|
+
}
|
|
1186
|
+
const nextClientEventId = generateClientEventId();
|
|
1187
|
+
const retryExtras = resolveSendExtras(sendExtrasRef.current);
|
|
1188
|
+
const retryInput = composeSendInput(operation.text, nextClientEventId, retryExtras, {
|
|
1189
|
+
...options.effectiveControl?.controlEtag ? { controlEtag: options.effectiveControl.controlEtag } : {},
|
|
1190
|
+
resources: operation.resources,
|
|
1191
|
+
annotations: operation.annotations
|
|
1192
|
+
});
|
|
1193
|
+
return {
|
|
1194
|
+
...operation,
|
|
1195
|
+
clientEventId: nextClientEventId,
|
|
1196
|
+
input: retryInput,
|
|
1197
|
+
draftPayload: operation.draftPayload ? {
|
|
1198
|
+
...operation.draftPayload,
|
|
1199
|
+
model: retryExtras.model ?? operation.draftPayload.model,
|
|
1200
|
+
reasoningEffort: retryExtras.reasoningEffort ?? operation.draftPayload.reasoningEffort,
|
|
1201
|
+
latencyMode: retryExtras.latencyMode ?? operation.draftPayload.latencyMode
|
|
1202
|
+
} : null,
|
|
1203
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1204
|
+
state: "sending",
|
|
1205
|
+
error: void 0,
|
|
1206
|
+
outcomeUnknown: false,
|
|
1207
|
+
canRetry: true
|
|
1208
|
+
};
|
|
1209
|
+
})
|
|
1210
|
+
);
|
|
1211
|
+
queueMicrotask(processOptimisticSends);
|
|
1212
|
+
},
|
|
1213
|
+
[options.effectiveControl?.controlEtag, processOptimisticSends, replaceOptimisticSends]
|
|
1214
|
+
);
|
|
1215
|
+
const removeOptimisticMessage = useCallback(
|
|
1216
|
+
(clientEventId) => {
|
|
1217
|
+
replaceOptimisticSends(
|
|
1218
|
+
(current) => current.filter((operation) => operation.clientEventId !== clientEventId)
|
|
1219
|
+
);
|
|
1220
|
+
},
|
|
1221
|
+
[replaceOptimisticSends]
|
|
1222
|
+
);
|
|
771
1223
|
const hasReadyResources = restoredResources.length > 0 || (resolveSendExtras(sendExtrasRef.current).resources?.length ?? 0) > 0;
|
|
772
1224
|
const hasPendingOperation = pendingOperationRef.current !== null;
|
|
1225
|
+
const annotationsComplete = annotations.every((annotation) => annotation.note.trim().length > 0);
|
|
773
1226
|
const pause = useCallback(
|
|
774
1227
|
async (reason) => {
|
|
775
1228
|
const ownedTargetKey = targetKey;
|
|
@@ -780,10 +1233,18 @@ function useComposer(sessionId, options = {}) {
|
|
|
780
1233
|
setPausing(true);
|
|
781
1234
|
setError(null);
|
|
782
1235
|
try {
|
|
783
|
-
await client.pauseSession(workspaceId, sessionId, {
|
|
1236
|
+
const result = await client.pauseSession(workspaceId, sessionId, {
|
|
784
1237
|
...reason !== void 0 ? { reason } : {},
|
|
785
1238
|
...options.effectiveControl?.controlEtag ? { expectedControlEtag: options.effectiveControl.controlEtag } : {}
|
|
786
1239
|
});
|
|
1240
|
+
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
1241
|
+
setControlStopping(
|
|
1242
|
+
result.interruptionCount > 0 && result.effectiveControl.state === "paused" && result.effectiveControl.settlement !== null ? {
|
|
1243
|
+
controlVersion: result.effectiveControl.controlVersion,
|
|
1244
|
+
controlEtag: result.effectiveControl.controlEtag
|
|
1245
|
+
} : null
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
787
1248
|
} catch (cause) {
|
|
788
1249
|
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
789
1250
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
@@ -880,13 +1341,82 @@ function useComposer(sessionId, options = {}) {
|
|
|
880
1341
|
resources: mergeResources(
|
|
881
1342
|
restoredResourcesRef.current,
|
|
882
1343
|
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
883
|
-
)
|
|
1344
|
+
),
|
|
1345
|
+
annotations: annotationsRef.current
|
|
884
1346
|
}
|
|
885
1347
|
);
|
|
1348
|
+
setOptimisticDraftShadow({
|
|
1349
|
+
text: next,
|
|
1350
|
+
resources: mergeResources(
|
|
1351
|
+
restoredResourcesRef.current,
|
|
1352
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
1353
|
+
),
|
|
1354
|
+
annotations: annotationsRef.current
|
|
1355
|
+
});
|
|
886
1356
|
setValue(next);
|
|
887
1357
|
},
|
|
888
|
-
[pendingOperationKey, targetKey]
|
|
1358
|
+
[pendingOperationKey, setOptimisticDraftShadow, targetKey]
|
|
889
1359
|
);
|
|
1360
|
+
const updateAnnotations = useCallback(
|
|
1361
|
+
(next, reviewTargetId = null) => {
|
|
1362
|
+
if (targetKeyRef.current !== targetKey) return;
|
|
1363
|
+
localEditRevision.current += 1;
|
|
1364
|
+
annotationsRef.current = next;
|
|
1365
|
+
pendingOperationRef.current = updatePendingComposerShadow(
|
|
1366
|
+
pendingOperationKey,
|
|
1367
|
+
pendingOperationRef.current,
|
|
1368
|
+
{
|
|
1369
|
+
text: valueRef.current,
|
|
1370
|
+
resources: mergeResources(
|
|
1371
|
+
restoredResourcesRef.current,
|
|
1372
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
1373
|
+
),
|
|
1374
|
+
annotations: next
|
|
1375
|
+
}
|
|
1376
|
+
);
|
|
1377
|
+
setOptimisticDraftShadow({
|
|
1378
|
+
text: valueRef.current,
|
|
1379
|
+
resources: mergeResources(
|
|
1380
|
+
restoredResourcesRef.current,
|
|
1381
|
+
resolveSendExtras(sendExtrasRef.current).resources ?? []
|
|
1382
|
+
),
|
|
1383
|
+
annotations: next
|
|
1384
|
+
});
|
|
1385
|
+
setAnnotations(next);
|
|
1386
|
+
setAnnotationReviewTargetId(reviewTargetId);
|
|
1387
|
+
},
|
|
1388
|
+
[pendingOperationKey, setOptimisticDraftShadow, targetKey]
|
|
1389
|
+
);
|
|
1390
|
+
const addAnnotation = useCallback(
|
|
1391
|
+
(annotation) => {
|
|
1392
|
+
if (annotationsRef.current.length >= 12) {
|
|
1393
|
+
setError(new Error("A message can include at most 12 timeline annotations."));
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const next = [...annotationsRef.current, annotation];
|
|
1397
|
+
updateAnnotations(next, annotation.id);
|
|
1398
|
+
},
|
|
1399
|
+
[updateAnnotations]
|
|
1400
|
+
);
|
|
1401
|
+
const updateAnnotation = useCallback(
|
|
1402
|
+
(id, note) => {
|
|
1403
|
+
updateAnnotations(
|
|
1404
|
+
annotationsRef.current.map(
|
|
1405
|
+
(annotation) => annotation.id === id ? { ...annotation, note } : annotation
|
|
1406
|
+
)
|
|
1407
|
+
);
|
|
1408
|
+
},
|
|
1409
|
+
[updateAnnotations]
|
|
1410
|
+
);
|
|
1411
|
+
const removeAnnotation = useCallback(
|
|
1412
|
+
(id) => {
|
|
1413
|
+
updateAnnotations(annotationsRef.current.filter((annotation) => annotation.id !== id));
|
|
1414
|
+
},
|
|
1415
|
+
[updateAnnotations]
|
|
1416
|
+
);
|
|
1417
|
+
const clearAnnotationReviewTarget = useCallback(() => {
|
|
1418
|
+
setAnnotationReviewTargetId(null);
|
|
1419
|
+
}, []);
|
|
890
1420
|
const removeRestoredResource = useCallback(
|
|
891
1421
|
(index) => {
|
|
892
1422
|
if (targetKeyRef.current !== targetKey) return;
|
|
@@ -898,17 +1428,23 @@ function useComposer(sessionId, options = {}) {
|
|
|
898
1428
|
pendingOperationRef.current,
|
|
899
1429
|
{
|
|
900
1430
|
text: valueRef.current,
|
|
901
|
-
resources: mergeResources(next, resolveSendExtras(sendExtrasRef.current).resources ?? [])
|
|
1431
|
+
resources: mergeResources(next, resolveSendExtras(sendExtrasRef.current).resources ?? []),
|
|
1432
|
+
annotations: annotationsRef.current
|
|
902
1433
|
}
|
|
903
1434
|
);
|
|
1435
|
+
setOptimisticDraftShadow({
|
|
1436
|
+
text: valueRef.current,
|
|
1437
|
+
resources: mergeResources(next, resolveSendExtras(sendExtrasRef.current).resources ?? []),
|
|
1438
|
+
annotations: annotationsRef.current
|
|
1439
|
+
});
|
|
904
1440
|
setRestoredResources(next);
|
|
905
1441
|
},
|
|
906
|
-
[pendingOperationKey, targetKey]
|
|
1442
|
+
[pendingOperationKey, setOptimisticDraftShadow, targetKey]
|
|
907
1443
|
);
|
|
908
1444
|
const hasDraftContent = useCallback(() => {
|
|
909
1445
|
const current = draftRef.current;
|
|
910
1446
|
const extras = resolveSendExtras(sendExtrasRef.current);
|
|
911
|
-
return valueRef.current.length > 0 || restoredResourcesRef.current.length > 0 || (extras.resources?.length ?? 0) > 0 || current?.sourceTurnId !== null && current?.sourceTurnId !== void 0;
|
|
1447
|
+
return valueRef.current.length > 0 || annotationsRef.current.length > 0 || restoredResourcesRef.current.length > 0 || (extras.resources?.length ?? 0) > 0 || current?.sourceTurnId !== null && current?.sourceTurnId !== void 0;
|
|
912
1448
|
}, []);
|
|
913
1449
|
const resolveDraftConflict = useCallback(
|
|
914
1450
|
async (choice) => {
|
|
@@ -943,6 +1479,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
943
1479
|
]
|
|
944
1480
|
);
|
|
945
1481
|
const identityMatches = stateTargetKey === targetKey;
|
|
1482
|
+
const visibleSteering = identityMatches ? steering : null;
|
|
946
1483
|
const reloadDraft = useCallback(async () => await loadDraft(true), [loadDraft]);
|
|
947
1484
|
const clearError = useCallback(() => {
|
|
948
1485
|
if (targetKeyRef.current !== targetKey) return;
|
|
@@ -952,12 +1489,24 @@ function useComposer(sessionId, options = {}) {
|
|
|
952
1489
|
return {
|
|
953
1490
|
value: identityMatches ? value : "",
|
|
954
1491
|
setValue: updateValue,
|
|
1492
|
+
annotations: identityMatches ? annotations : [],
|
|
1493
|
+
addAnnotation,
|
|
1494
|
+
updateAnnotation,
|
|
1495
|
+
removeAnnotation,
|
|
1496
|
+
annotationReviewTargetId: identityMatches ? annotationReviewTargetId : null,
|
|
1497
|
+
clearAnnotationReviewTarget,
|
|
955
1498
|
hasDraftContent,
|
|
956
1499
|
send,
|
|
1500
|
+
optimisticMessages: identityMatches ? optimisticSends.map(
|
|
1501
|
+
({ input: _input, draftPayload: _payload, canRetry: _retry, ...item }) => item
|
|
1502
|
+
) : [],
|
|
1503
|
+
retryOptimisticMessage,
|
|
1504
|
+
removeOptimisticMessage,
|
|
957
1505
|
steer,
|
|
958
|
-
steering:
|
|
1506
|
+
steering: visibleSteering,
|
|
1507
|
+
stoppingAttempt: identityMatches ? controlStopping ? "current" : visibleSteering?.phase === "accepted" && visibleSteering.stoppingPreviousAttempt === true ? "previous" : null : null,
|
|
959
1508
|
sending: identityMatches ? sending : false,
|
|
960
|
-
canSend: identityMatches && Boolean(sessionId) && !sending && sendBlockedRef.current?.() !== true && (hasPendingOperation || value.trim().length > 0 || hasReadyResources),
|
|
1509
|
+
canSend: identityMatches && Boolean(sessionId) && !sending && sendBlockedRef.current?.() !== true && annotationsComplete && (hasPendingOperation || value.trim().length > 0 || hasReadyResources || annotations.length > 0),
|
|
961
1510
|
pause,
|
|
962
1511
|
pausing: identityMatches ? pausing : false,
|
|
963
1512
|
resume,
|
|
@@ -981,7 +1530,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
981
1530
|
function isComposerDraftEvent(event) {
|
|
982
1531
|
return event.type === "user.message" || event.type === "session.queue.changed";
|
|
983
1532
|
}
|
|
984
|
-
var FILE_ONLY_MESSAGE_TEXT = "(see attached
|
|
1533
|
+
var FILE_ONLY_MESSAGE_TEXT = "(see attached context)";
|
|
985
1534
|
function resolveSendExtras(extras) {
|
|
986
1535
|
return (typeof extras === "function" ? extras() : extras) ?? {};
|
|
987
1536
|
}
|
|
@@ -1034,16 +1583,18 @@ function draftPayload(draft) {
|
|
|
1034
1583
|
expectedRevision: draft.revision,
|
|
1035
1584
|
text: draft.text,
|
|
1036
1585
|
resources: draft.resources,
|
|
1586
|
+
annotations: draft.annotations ?? [],
|
|
1037
1587
|
model: draft.model,
|
|
1038
1588
|
reasoningEffort: draft.reasoningEffort,
|
|
1039
1589
|
latencyMode: draft.latencyMode ?? "standard"
|
|
1040
1590
|
};
|
|
1041
1591
|
}
|
|
1042
|
-
function composerDraftPayload(base, text, restoredResources, extras) {
|
|
1592
|
+
function composerDraftPayload(base, text, restoredResources, annotations, extras) {
|
|
1043
1593
|
return {
|
|
1044
1594
|
expectedRevision: base.revision,
|
|
1045
1595
|
text,
|
|
1046
1596
|
resources: mergeResources(restoredResources, extras.resources ?? []),
|
|
1597
|
+
annotations,
|
|
1047
1598
|
model: extras.model ?? base.model,
|
|
1048
1599
|
reasoningEffort: extras.reasoningEffort ?? base.reasoningEffort,
|
|
1049
1600
|
latencyMode: extras.latencyMode ?? base.latencyMode ?? "standard"
|
|
@@ -1070,4 +1621,4 @@ export {
|
|
|
1070
1621
|
shouldSubmitOnKey,
|
|
1071
1622
|
shouldSteerOnKey
|
|
1072
1623
|
};
|
|
1073
|
-
//# sourceMappingURL=chunk-
|
|
1624
|
+
//# sourceMappingURL=chunk-IGC6A2ZI.js.map
|