@athenaintel/react 0.50.0 → 0.52.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 +20 -0
- package/dist/agent-runtime/src/desktop-handoff.d.ts +10 -0
- package/dist/agent-runtime/src/edit-command.cjs +21 -0
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/agent-runtime/src/edit-command.d.ts +8 -0
- package/dist/agent-runtime/src/edit-command.js +21 -0
- package/dist/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/agent-runtime/src/handoff-arrival.cjs +19 -0
- package/dist/agent-runtime/src/handoff-arrival.cjs.map +1 -1
- package/dist/agent-runtime/src/handoff-arrival.d.ts +18 -3
- package/dist/agent-runtime/src/handoff-arrival.js +20 -1
- package/dist/agent-runtime/src/handoff-arrival.js.map +1 -1
- package/dist/agent-runtime/src/handoff-cursor-id.cjs +6 -0
- package/dist/agent-runtime/src/handoff-cursor-id.cjs.map +1 -0
- package/dist/agent-runtime/src/handoff-cursor-id.d.ts +12 -0
- package/dist/agent-runtime/src/handoff-cursor-id.js +6 -0
- package/dist/agent-runtime/src/handoff-cursor-id.js.map +1 -0
- package/dist/agent-runtime/src/handoff-cursor.cjs +88 -0
- package/dist/agent-runtime/src/handoff-cursor.cjs.map +1 -0
- package/dist/agent-runtime/src/handoff-cursor.d.ts +120 -0
- package/dist/agent-runtime/src/handoff-cursor.js +88 -0
- package/dist/agent-runtime/src/handoff-cursor.js.map +1 -0
- package/dist/agent-runtime/src/index.d.ts +4 -3
- package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +6 -0
- package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +114 -4
- package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +8 -3
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +7 -0
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js +9 -4
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
- package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +5 -0
- package/dist/chat/AthenaChat.cjs +27 -12
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +13 -0
- package/dist/chat/AthenaChat.js +27 -12
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/AthenaPlanStrip.cjs +97 -0
- package/dist/chat/AthenaPlanStrip.cjs.map +1 -0
- package/dist/chat/AthenaPlanStrip.d.ts +15 -0
- package/dist/chat/AthenaPlanStrip.js +97 -0
- package/dist/chat/AthenaPlanStrip.js.map +1 -0
- package/dist/chat/super-grouping/SuperGroupingCard.cjs +4 -2
- package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingCard.js +4 -2
- package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +4 -2
- package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
- package/dist/chat/super-grouping/SuperGroupingTranscript.js +4 -2
- package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.cjs +6 -4
- package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
- package/dist/chat/super-grouping/activity-timeline.d.ts +9 -1
- package/dist/chat/super-grouping/activity-timeline.js +6 -4
- package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
- package/dist/chat/super-grouping/index.d.ts +1 -1
- package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs +17 -0
- package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs.map +1 -0
- package/dist/chat/super-grouping/use-earlier-steps-unloaded.d.ts +8 -0
- package/dist/chat/super-grouping/use-earlier-steps-unloaded.js +17 -0
- package/dist/chat/super-grouping/use-earlier-steps-unloaded.js.map +1 -0
- package/dist/chat-ui/index.cjs +8 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +2 -1
- package/dist/chat-ui/index.js +9 -1
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/thread/chat-thread-shell.cjs +2 -0
- package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
- package/dist/chat-ui/thread/chat-thread-shell.d.ts +4 -0
- package/dist/chat-ui/thread/chat-thread-shell.js +2 -0
- package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
- package/dist/chat-ui/thread/handoff-cursor.cjs +216 -0
- package/dist/chat-ui/thread/handoff-cursor.cjs.map +1 -0
- package/dist/chat-ui/thread/handoff-cursor.d.ts +57 -0
- package/dist/chat-ui/thread/handoff-cursor.js +216 -0
- package/dist/chat-ui/thread/handoff-cursor.js.map +1 -0
- package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -0
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.d.ts +2 -0
- package/dist/chat-ui/thread/statewire-history-edge.js +3 -1
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +20 -3
- package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -1
- package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +11 -2
- package/dist/keryx-http/src/unconfirmed-update-attributions.js +21 -4
- package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tools/write-todos-tool-ui.cjs +8 -0
- package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
- package/dist/tools/write-todos-tool-ui.d.ts +2 -0
- package/dist/tools/write-todos-tool-ui.js +8 -0
- package/dist/tools/write-todos-tool-ui.js.map +1 -1
- package/dist/workbench/session-workbench-panes.cjs +1 -8
- package/dist/workbench/session-workbench-panes.cjs.map +1 -1
- package/dist/workbench/session-workbench-panes.js +2 -9
- package/dist/workbench/session-workbench-panes.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff-cursor.cjs","sources":["../../../../agent-runtime/src/handoff-cursor.ts"],"sourcesContent":["/**\n * Where a handed-off conversation opens: at the message the source was\n * reading (\"the view at the last message the source showed\"), not just at the\n * bottom.\n *\n * The source reads its own transcript when the person issues the handover\n * (`readingCursorOf` on the web, the visible rows on the phone): `null` when\n * it was at the bottom, else the id of the last message fully in view or,\n * when one long message fills the view, the message at the reading line. The\n * id travels as the handover's `cursor` (an opaque id from the person's own\n * thread, never content). The destination records it with the arrival\n * (`recordHandoffArrival({ cursor })`) and its transcript reveals that message\n * once it has it, paging older history in when needed\n * (`handoffCursorRevealStep`), and highlights it briefly. A cursor it can't\n * find leaves the view at the bottom, as before: it is a hint, never a wait.\n */\nimport { getExternalStoreMessages, type ThreadMessage } from '@assistant-ui/core';\nimport { parseHandoffCursor } from './handoff-cursor-id';\n\n/** Whether older history is left to page in (`null` until the window is known). */\nexport { deepAgentHistoryHasOlder } from './edit-command';\n\n/** How far from the bottom still counts as \"at the bottom\": no cursor is sent. */\nexport const READING_CURSOR_BOTTOM_TOLERANCE_PX = 48;\n/** The reading line, as a fraction of the visible height from its top. */\nexport const READING_LINE_FRACTION = 1 / 3;\n\n/** One rendered message row, in the viewport's coordinates. */\nexport interface ReadingRow {\n id: string;\n top: number;\n bottom: number;\n}\n\n/**\n * The reading position among `rows` (in document order) visible between `top`\n * and `bottom`: the last row fully inside, else the row across the reading\n * line (a message taller than the view). `null` when neither is a message id.\n */\nexport function readingCursorFromRows({\n top,\n bottom,\n rows,\n}: {\n top: number;\n bottom: number;\n rows: readonly ReadingRow[];\n}): string | null {\n const line = top + (bottom - top) * READING_LINE_FRACTION;\n let lastFull: ReadingRow | null = null;\n let atLine: ReadingRow | null = null;\n for (const row of rows) {\n if (row.bottom <= row.top) continue;\n if (row.top >= top - 1 && row.bottom <= bottom + 1) lastFull = row;\n if (row.top <= line && row.bottom > line) atLine = row;\n }\n return parseHandoffCursor((lastFull ?? atLine)?.id);\n}\n\n/**\n * The reading position of a list that reports which rows are visible, in\n * order and partly visible ones included, but not where they sit (a native\n * list's viewability). Only the first and last visible rows can be cut off,\n * so with three or more the last fully visible one is the one before the\n * last; with fewer, the first (the one across the reading line, as far as the\n * list can tell). Callers check \"at the bottom\" themselves.\n */\nexport function readingCursorFromVisibleIds(ids: readonly string[]): string | null {\n return parseHandoffCursor(ids.length >= 3 ? ids[ids.length - 2] : ids[0]);\n}\n\ninterface BoxLike {\n getBoundingClientRect(): { top: number; bottom: number };\n}\n\ninterface RowLike extends BoxLike {\n getAttribute(name: string): string | null;\n}\n\n/** The scroll container of a web transcript (structural, so Core needs no DOM types). */\nexport interface ReadingViewportLike extends BoxLike {\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n querySelectorAll(selectors: string): ArrayLike<RowLike>;\n}\n\n/** assistant-ui stamps every message row with its id. */\nexport const MESSAGE_ROW_ID_ATTRIBUTE = 'data-message-id';\n\n/**\n * Where a web transcript is being read: `null` at the bottom, else the message\n * id `readingCursorFromRows` picks. `coveredBottomPx` is what a sticky footer\n * (the composer) hides at the bottom of the viewport.\n */\nexport function readingCursorOf({\n viewport,\n coveredBottomPx = 0,\n idAttribute = MESSAGE_ROW_ID_ATTRIBUTE,\n}: {\n viewport: ReadingViewportLike;\n coveredBottomPx?: number;\n idAttribute?: string;\n}): string | null {\n const fromBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n if (fromBottom <= READING_CURSOR_BOTTOM_TOLERANCE_PX) return null;\n const box = viewport.getBoundingClientRect();\n const rows: ReadingRow[] = Array.from(viewport.querySelectorAll(`[${idAttribute}]`), (row) => {\n const rect = row.getBoundingClientRect();\n return { id: row.getAttribute(idAttribute) ?? '', top: rect.top, bottom: rect.bottom };\n });\n return readingCursorFromRows({\n top: box.top,\n bottom: Math.max(box.top, box.bottom - coveredBottomPx),\n rows,\n });\n}\n\n/**\n * The id of the displayed message that holds `cursor`. A displayed message is\n * one agent turn whose id is its first wire message's, and that id changes\n * when an older page folds into it, so the cursor is matched against every\n * wire message behind each row as well.\n */\nexport function handoffCursorMessageId({\n messages,\n cursor,\n}: {\n messages: readonly ThreadMessage[];\n cursor: string;\n}): string | null {\n for (const message of messages) {\n if (message.id === cursor) return message.id;\n }\n for (const message of messages) {\n const sources: unknown = getExternalStoreMessages(message);\n if (!Array.isArray(sources)) continue;\n for (const source of sources) {\n if (source && typeof source === 'object' && (source as { id?: unknown }).id === cursor) {\n return message.id;\n }\n }\n }\n return null;\n}\n\n/** Pages of older history one landing may load looking for its message. */\nexport const HANDOFF_CURSOR_MAX_HISTORY_PAGES = 20;\n/**\n * How long a landing waits without progress before staying at the bottom: a\n * transcript still attaching, or a page of older history that never lands.\n * Each page that lands starts the wait again, so a long conversation on a slow\n * connection still reaches its message within the page budget.\n */\nexport const HANDOFF_CURSOR_WAIT_MS = 30_000;\n/** How long the revealed message stays highlighted. */\nexport const HANDOFF_CURSOR_HIGHLIGHT_MS = 2_400;\n/**\n * How long the revealed message is held in view while the transcript settles\n * (an attaching thread can still scroll itself to the bottom). Any scroll the\n * person makes ends it at once.\n */\nexport const HANDOFF_CURSOR_SETTLE_MS = 1_500;\n\nexport type HandoffCursorRevealStep = 'reveal' | 'wait' | 'page' | 'bottom';\n\n/**\n * What a destination does next while opening at a cursor: `reveal` the message\n * it found; `wait` while the transcript (or a page it asked for) is loading or\n * whether older history exists is not known yet (`hasOlder: null`: a cached\n * transcript shows before its history window resolves); `page` older history\n * in while some is left and the budget allows; else stay at the `bottom`, the\n * fallback for a cursor that can't be found.\n */\nexport function handoffCursorRevealStep({\n found,\n loading,\n hasOlder,\n pagesLoaded,\n elapsedMs,\n}: {\n found: boolean;\n loading: boolean;\n hasOlder: boolean | null;\n pagesLoaded: number;\n /** Since the landing last made progress: it started, or a page of older history landed. */\n elapsedMs: number;\n}): HandoffCursorRevealStep {\n if (found) return 'reveal';\n if (elapsedMs >= HANDOFF_CURSOR_WAIT_MS) return 'bottom';\n if (loading || hasOlder === null) return 'wait';\n if (hasOlder && pagesLoaded < HANDOFF_CURSOR_MAX_HISTORY_PAGES) return 'page';\n return 'bottom';\n}\n"],"names":["parseHandoffCursor","getExternalStoreMessages"],"mappings":";;;;AAuBO,MAAM,qCAAqC;AAE3C,MAAM,wBAAwB,IAAI;AAclC,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAIkB;;AAChB,QAAM,OAAO,OAAO,SAAS,OAAO;AACpC,MAAI,WAA8B;AAClC,MAAI,SAA4B;AAChC,aAAW,OAAO,MAAM;AACtB,QAAI,IAAI,UAAU,IAAI,IAAK;AAC3B,QAAI,IAAI,OAAO,MAAM,KAAK,IAAI,UAAU,SAAS,EAAG,YAAW;AAC/D,QAAI,IAAI,OAAO,QAAQ,IAAI,SAAS,KAAM,UAAS;AAAA,EACrD;AACA,SAAOA,oCAAoB,iBAAY,WAAZ,mBAAqB,EAAE;AACpD;AA+BO,MAAM,2BAA2B;AAOjC,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,kBAAkB;AAAA,EAClB,cAAc;AAChB,GAIkB;AAChB,QAAM,aAAa,SAAS,eAAe,SAAS,YAAY,SAAS;AACzE,MAAI,cAAc,mCAAoC,QAAO;AAC7D,QAAM,MAAM,SAAS,sBAAA;AACrB,QAAM,OAAqB,MAAM,KAAK,SAAS,iBAAiB,IAAI,WAAW,GAAG,GAAG,CAAC,QAAQ;AAC5F,UAAM,OAAO,IAAI,sBAAA;AACjB,WAAO,EAAE,IAAI,IAAI,aAAa,WAAW,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAA;AAAA,EAChF,CAAC;AACD,SAAO,sBAAsB;AAAA,IAC3B,KAAK,IAAI;AAAA,IACT,QAAQ,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,eAAe;AAAA,IACtD;AAAA,EAAA,CACD;AACH;AAQO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AACF,GAGkB;AAChB,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,OAAO,OAAQ,QAAO,QAAQ;AAAA,EAC5C;AACA,aAAW,WAAW,UAAU;AAC9B,UAAM,UAAmBC,KAAAA,yBAAyB,OAAO;AACzD,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG;AAC7B,eAAW,UAAU,SAAS;AAC5B,UAAI,UAAU,OAAO,WAAW,YAAa,OAA4B,OAAO,QAAQ;AACtF,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,MAAM,mCAAmC;AAOzC,MAAM,yBAAyB;AAE/B,MAAM,8BAA8B;AAMpC,MAAM,2BAA2B;AAYjC,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAO4B;AAC1B,MAAI,MAAO,QAAO;AAClB,MAAI,aAAa,uBAAwB,QAAO;AAChD,MAAI,WAAW,aAAa,KAAM,QAAO;AACzC,MAAI,YAAY,cAAc,iCAAkC,QAAO;AACvE,SAAO;AACT;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a handed-off conversation opens: at the message the source was
|
|
3
|
+
* reading ("the view at the last message the source showed"), not just at the
|
|
4
|
+
* bottom.
|
|
5
|
+
*
|
|
6
|
+
* The source reads its own transcript when the person issues the handover
|
|
7
|
+
* (`readingCursorOf` on the web, the visible rows on the phone): `null` when
|
|
8
|
+
* it was at the bottom, else the id of the last message fully in view or,
|
|
9
|
+
* when one long message fills the view, the message at the reading line. The
|
|
10
|
+
* id travels as the handover's `cursor` (an opaque id from the person's own
|
|
11
|
+
* thread, never content). The destination records it with the arrival
|
|
12
|
+
* (`recordHandoffArrival({ cursor })`) and its transcript reveals that message
|
|
13
|
+
* once it has it, paging older history in when needed
|
|
14
|
+
* (`handoffCursorRevealStep`), and highlights it briefly. A cursor it can't
|
|
15
|
+
* find leaves the view at the bottom, as before: it is a hint, never a wait.
|
|
16
|
+
*/
|
|
17
|
+
import { type ThreadMessage } from '@assistant-ui/core';
|
|
18
|
+
/** Whether older history is left to page in (`null` until the window is known). */
|
|
19
|
+
export { deepAgentHistoryHasOlder } from './edit-command';
|
|
20
|
+
/** How far from the bottom still counts as "at the bottom": no cursor is sent. */
|
|
21
|
+
export declare const READING_CURSOR_BOTTOM_TOLERANCE_PX = 48;
|
|
22
|
+
/** The reading line, as a fraction of the visible height from its top. */
|
|
23
|
+
export declare const READING_LINE_FRACTION: number;
|
|
24
|
+
/** One rendered message row, in the viewport's coordinates. */
|
|
25
|
+
export interface ReadingRow {
|
|
26
|
+
id: string;
|
|
27
|
+
top: number;
|
|
28
|
+
bottom: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The reading position among `rows` (in document order) visible between `top`
|
|
32
|
+
* and `bottom`: the last row fully inside, else the row across the reading
|
|
33
|
+
* line (a message taller than the view). `null` when neither is a message id.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readingCursorFromRows({ top, bottom, rows, }: {
|
|
36
|
+
top: number;
|
|
37
|
+
bottom: number;
|
|
38
|
+
rows: readonly ReadingRow[];
|
|
39
|
+
}): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* The reading position of a list that reports which rows are visible, in
|
|
42
|
+
* order and partly visible ones included, but not where they sit (a native
|
|
43
|
+
* list's viewability). Only the first and last visible rows can be cut off,
|
|
44
|
+
* so with three or more the last fully visible one is the one before the
|
|
45
|
+
* last; with fewer, the first (the one across the reading line, as far as the
|
|
46
|
+
* list can tell). Callers check "at the bottom" themselves.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readingCursorFromVisibleIds(ids: readonly string[]): string | null;
|
|
49
|
+
interface BoxLike {
|
|
50
|
+
getBoundingClientRect(): {
|
|
51
|
+
top: number;
|
|
52
|
+
bottom: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
interface RowLike extends BoxLike {
|
|
56
|
+
getAttribute(name: string): string | null;
|
|
57
|
+
}
|
|
58
|
+
/** The scroll container of a web transcript (structural, so Core needs no DOM types). */
|
|
59
|
+
export interface ReadingViewportLike extends BoxLike {
|
|
60
|
+
scrollTop: number;
|
|
61
|
+
scrollHeight: number;
|
|
62
|
+
clientHeight: number;
|
|
63
|
+
querySelectorAll(selectors: string): ArrayLike<RowLike>;
|
|
64
|
+
}
|
|
65
|
+
/** assistant-ui stamps every message row with its id. */
|
|
66
|
+
export declare const MESSAGE_ROW_ID_ATTRIBUTE = "data-message-id";
|
|
67
|
+
/**
|
|
68
|
+
* Where a web transcript is being read: `null` at the bottom, else the message
|
|
69
|
+
* id `readingCursorFromRows` picks. `coveredBottomPx` is what a sticky footer
|
|
70
|
+
* (the composer) hides at the bottom of the viewport.
|
|
71
|
+
*/
|
|
72
|
+
export declare function readingCursorOf({ viewport, coveredBottomPx, idAttribute, }: {
|
|
73
|
+
viewport: ReadingViewportLike;
|
|
74
|
+
coveredBottomPx?: number;
|
|
75
|
+
idAttribute?: string;
|
|
76
|
+
}): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* The id of the displayed message that holds `cursor`. A displayed message is
|
|
79
|
+
* one agent turn whose id is its first wire message's, and that id changes
|
|
80
|
+
* when an older page folds into it, so the cursor is matched against every
|
|
81
|
+
* wire message behind each row as well.
|
|
82
|
+
*/
|
|
83
|
+
export declare function handoffCursorMessageId({ messages, cursor, }: {
|
|
84
|
+
messages: readonly ThreadMessage[];
|
|
85
|
+
cursor: string;
|
|
86
|
+
}): string | null;
|
|
87
|
+
/** Pages of older history one landing may load looking for its message. */
|
|
88
|
+
export declare const HANDOFF_CURSOR_MAX_HISTORY_PAGES = 20;
|
|
89
|
+
/**
|
|
90
|
+
* How long a landing waits without progress before staying at the bottom: a
|
|
91
|
+
* transcript still attaching, or a page of older history that never lands.
|
|
92
|
+
* Each page that lands starts the wait again, so a long conversation on a slow
|
|
93
|
+
* connection still reaches its message within the page budget.
|
|
94
|
+
*/
|
|
95
|
+
export declare const HANDOFF_CURSOR_WAIT_MS = 30000;
|
|
96
|
+
/** How long the revealed message stays highlighted. */
|
|
97
|
+
export declare const HANDOFF_CURSOR_HIGHLIGHT_MS = 2400;
|
|
98
|
+
/**
|
|
99
|
+
* How long the revealed message is held in view while the transcript settles
|
|
100
|
+
* (an attaching thread can still scroll itself to the bottom). Any scroll the
|
|
101
|
+
* person makes ends it at once.
|
|
102
|
+
*/
|
|
103
|
+
export declare const HANDOFF_CURSOR_SETTLE_MS = 1500;
|
|
104
|
+
export type HandoffCursorRevealStep = 'reveal' | 'wait' | 'page' | 'bottom';
|
|
105
|
+
/**
|
|
106
|
+
* What a destination does next while opening at a cursor: `reveal` the message
|
|
107
|
+
* it found; `wait` while the transcript (or a page it asked for) is loading or
|
|
108
|
+
* whether older history exists is not known yet (`hasOlder: null`: a cached
|
|
109
|
+
* transcript shows before its history window resolves); `page` older history
|
|
110
|
+
* in while some is left and the budget allows; else stay at the `bottom`, the
|
|
111
|
+
* fallback for a cursor that can't be found.
|
|
112
|
+
*/
|
|
113
|
+
export declare function handoffCursorRevealStep({ found, loading, hasOlder, pagesLoaded, elapsedMs, }: {
|
|
114
|
+
found: boolean;
|
|
115
|
+
loading: boolean;
|
|
116
|
+
hasOlder: boolean | null;
|
|
117
|
+
pagesLoaded: number;
|
|
118
|
+
/** Since the landing last made progress: it started, or a page of older history landed. */
|
|
119
|
+
elapsedMs: number;
|
|
120
|
+
}): HandoffCursorRevealStep;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getExternalStoreMessages } from "@assistant-ui/core";
|
|
2
|
+
import { parseHandoffCursor } from "./handoff-cursor-id.js";
|
|
3
|
+
const READING_CURSOR_BOTTOM_TOLERANCE_PX = 48;
|
|
4
|
+
const READING_LINE_FRACTION = 1 / 3;
|
|
5
|
+
function readingCursorFromRows({
|
|
6
|
+
top,
|
|
7
|
+
bottom,
|
|
8
|
+
rows
|
|
9
|
+
}) {
|
|
10
|
+
var _a;
|
|
11
|
+
const line = top + (bottom - top) * READING_LINE_FRACTION;
|
|
12
|
+
let lastFull = null;
|
|
13
|
+
let atLine = null;
|
|
14
|
+
for (const row of rows) {
|
|
15
|
+
if (row.bottom <= row.top) continue;
|
|
16
|
+
if (row.top >= top - 1 && row.bottom <= bottom + 1) lastFull = row;
|
|
17
|
+
if (row.top <= line && row.bottom > line) atLine = row;
|
|
18
|
+
}
|
|
19
|
+
return parseHandoffCursor((_a = lastFull ?? atLine) == null ? void 0 : _a.id);
|
|
20
|
+
}
|
|
21
|
+
const MESSAGE_ROW_ID_ATTRIBUTE = "data-message-id";
|
|
22
|
+
function readingCursorOf({
|
|
23
|
+
viewport,
|
|
24
|
+
coveredBottomPx = 0,
|
|
25
|
+
idAttribute = MESSAGE_ROW_ID_ATTRIBUTE
|
|
26
|
+
}) {
|
|
27
|
+
const fromBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
|
|
28
|
+
if (fromBottom <= READING_CURSOR_BOTTOM_TOLERANCE_PX) return null;
|
|
29
|
+
const box = viewport.getBoundingClientRect();
|
|
30
|
+
const rows = Array.from(viewport.querySelectorAll(`[${idAttribute}]`), (row) => {
|
|
31
|
+
const rect = row.getBoundingClientRect();
|
|
32
|
+
return { id: row.getAttribute(idAttribute) ?? "", top: rect.top, bottom: rect.bottom };
|
|
33
|
+
});
|
|
34
|
+
return readingCursorFromRows({
|
|
35
|
+
top: box.top,
|
|
36
|
+
bottom: Math.max(box.top, box.bottom - coveredBottomPx),
|
|
37
|
+
rows
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function handoffCursorMessageId({
|
|
41
|
+
messages,
|
|
42
|
+
cursor
|
|
43
|
+
}) {
|
|
44
|
+
for (const message of messages) {
|
|
45
|
+
if (message.id === cursor) return message.id;
|
|
46
|
+
}
|
|
47
|
+
for (const message of messages) {
|
|
48
|
+
const sources = getExternalStoreMessages(message);
|
|
49
|
+
if (!Array.isArray(sources)) continue;
|
|
50
|
+
for (const source of sources) {
|
|
51
|
+
if (source && typeof source === "object" && source.id === cursor) {
|
|
52
|
+
return message.id;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const HANDOFF_CURSOR_MAX_HISTORY_PAGES = 20;
|
|
59
|
+
const HANDOFF_CURSOR_WAIT_MS = 3e4;
|
|
60
|
+
const HANDOFF_CURSOR_HIGHLIGHT_MS = 2400;
|
|
61
|
+
const HANDOFF_CURSOR_SETTLE_MS = 1500;
|
|
62
|
+
function handoffCursorRevealStep({
|
|
63
|
+
found,
|
|
64
|
+
loading,
|
|
65
|
+
hasOlder,
|
|
66
|
+
pagesLoaded,
|
|
67
|
+
elapsedMs
|
|
68
|
+
}) {
|
|
69
|
+
if (found) return "reveal";
|
|
70
|
+
if (elapsedMs >= HANDOFF_CURSOR_WAIT_MS) return "bottom";
|
|
71
|
+
if (loading || hasOlder === null) return "wait";
|
|
72
|
+
if (hasOlder && pagesLoaded < HANDOFF_CURSOR_MAX_HISTORY_PAGES) return "page";
|
|
73
|
+
return "bottom";
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
HANDOFF_CURSOR_HIGHLIGHT_MS,
|
|
77
|
+
HANDOFF_CURSOR_MAX_HISTORY_PAGES,
|
|
78
|
+
HANDOFF_CURSOR_SETTLE_MS,
|
|
79
|
+
HANDOFF_CURSOR_WAIT_MS,
|
|
80
|
+
MESSAGE_ROW_ID_ATTRIBUTE,
|
|
81
|
+
READING_CURSOR_BOTTOM_TOLERANCE_PX,
|
|
82
|
+
READING_LINE_FRACTION,
|
|
83
|
+
handoffCursorMessageId,
|
|
84
|
+
handoffCursorRevealStep,
|
|
85
|
+
readingCursorFromRows,
|
|
86
|
+
readingCursorOf
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=handoff-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff-cursor.js","sources":["../../../../agent-runtime/src/handoff-cursor.ts"],"sourcesContent":["/**\n * Where a handed-off conversation opens: at the message the source was\n * reading (\"the view at the last message the source showed\"), not just at the\n * bottom.\n *\n * The source reads its own transcript when the person issues the handover\n * (`readingCursorOf` on the web, the visible rows on the phone): `null` when\n * it was at the bottom, else the id of the last message fully in view or,\n * when one long message fills the view, the message at the reading line. The\n * id travels as the handover's `cursor` (an opaque id from the person's own\n * thread, never content). The destination records it with the arrival\n * (`recordHandoffArrival({ cursor })`) and its transcript reveals that message\n * once it has it, paging older history in when needed\n * (`handoffCursorRevealStep`), and highlights it briefly. A cursor it can't\n * find leaves the view at the bottom, as before: it is a hint, never a wait.\n */\nimport { getExternalStoreMessages, type ThreadMessage } from '@assistant-ui/core';\nimport { parseHandoffCursor } from './handoff-cursor-id';\n\n/** Whether older history is left to page in (`null` until the window is known). */\nexport { deepAgentHistoryHasOlder } from './edit-command';\n\n/** How far from the bottom still counts as \"at the bottom\": no cursor is sent. */\nexport const READING_CURSOR_BOTTOM_TOLERANCE_PX = 48;\n/** The reading line, as a fraction of the visible height from its top. */\nexport const READING_LINE_FRACTION = 1 / 3;\n\n/** One rendered message row, in the viewport's coordinates. */\nexport interface ReadingRow {\n id: string;\n top: number;\n bottom: number;\n}\n\n/**\n * The reading position among `rows` (in document order) visible between `top`\n * and `bottom`: the last row fully inside, else the row across the reading\n * line (a message taller than the view). `null` when neither is a message id.\n */\nexport function readingCursorFromRows({\n top,\n bottom,\n rows,\n}: {\n top: number;\n bottom: number;\n rows: readonly ReadingRow[];\n}): string | null {\n const line = top + (bottom - top) * READING_LINE_FRACTION;\n let lastFull: ReadingRow | null = null;\n let atLine: ReadingRow | null = null;\n for (const row of rows) {\n if (row.bottom <= row.top) continue;\n if (row.top >= top - 1 && row.bottom <= bottom + 1) lastFull = row;\n if (row.top <= line && row.bottom > line) atLine = row;\n }\n return parseHandoffCursor((lastFull ?? atLine)?.id);\n}\n\n/**\n * The reading position of a list that reports which rows are visible, in\n * order and partly visible ones included, but not where they sit (a native\n * list's viewability). Only the first and last visible rows can be cut off,\n * so with three or more the last fully visible one is the one before the\n * last; with fewer, the first (the one across the reading line, as far as the\n * list can tell). Callers check \"at the bottom\" themselves.\n */\nexport function readingCursorFromVisibleIds(ids: readonly string[]): string | null {\n return parseHandoffCursor(ids.length >= 3 ? ids[ids.length - 2] : ids[0]);\n}\n\ninterface BoxLike {\n getBoundingClientRect(): { top: number; bottom: number };\n}\n\ninterface RowLike extends BoxLike {\n getAttribute(name: string): string | null;\n}\n\n/** The scroll container of a web transcript (structural, so Core needs no DOM types). */\nexport interface ReadingViewportLike extends BoxLike {\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n querySelectorAll(selectors: string): ArrayLike<RowLike>;\n}\n\n/** assistant-ui stamps every message row with its id. */\nexport const MESSAGE_ROW_ID_ATTRIBUTE = 'data-message-id';\n\n/**\n * Where a web transcript is being read: `null` at the bottom, else the message\n * id `readingCursorFromRows` picks. `coveredBottomPx` is what a sticky footer\n * (the composer) hides at the bottom of the viewport.\n */\nexport function readingCursorOf({\n viewport,\n coveredBottomPx = 0,\n idAttribute = MESSAGE_ROW_ID_ATTRIBUTE,\n}: {\n viewport: ReadingViewportLike;\n coveredBottomPx?: number;\n idAttribute?: string;\n}): string | null {\n const fromBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n if (fromBottom <= READING_CURSOR_BOTTOM_TOLERANCE_PX) return null;\n const box = viewport.getBoundingClientRect();\n const rows: ReadingRow[] = Array.from(viewport.querySelectorAll(`[${idAttribute}]`), (row) => {\n const rect = row.getBoundingClientRect();\n return { id: row.getAttribute(idAttribute) ?? '', top: rect.top, bottom: rect.bottom };\n });\n return readingCursorFromRows({\n top: box.top,\n bottom: Math.max(box.top, box.bottom - coveredBottomPx),\n rows,\n });\n}\n\n/**\n * The id of the displayed message that holds `cursor`. A displayed message is\n * one agent turn whose id is its first wire message's, and that id changes\n * when an older page folds into it, so the cursor is matched against every\n * wire message behind each row as well.\n */\nexport function handoffCursorMessageId({\n messages,\n cursor,\n}: {\n messages: readonly ThreadMessage[];\n cursor: string;\n}): string | null {\n for (const message of messages) {\n if (message.id === cursor) return message.id;\n }\n for (const message of messages) {\n const sources: unknown = getExternalStoreMessages(message);\n if (!Array.isArray(sources)) continue;\n for (const source of sources) {\n if (source && typeof source === 'object' && (source as { id?: unknown }).id === cursor) {\n return message.id;\n }\n }\n }\n return null;\n}\n\n/** Pages of older history one landing may load looking for its message. */\nexport const HANDOFF_CURSOR_MAX_HISTORY_PAGES = 20;\n/**\n * How long a landing waits without progress before staying at the bottom: a\n * transcript still attaching, or a page of older history that never lands.\n * Each page that lands starts the wait again, so a long conversation on a slow\n * connection still reaches its message within the page budget.\n */\nexport const HANDOFF_CURSOR_WAIT_MS = 30_000;\n/** How long the revealed message stays highlighted. */\nexport const HANDOFF_CURSOR_HIGHLIGHT_MS = 2_400;\n/**\n * How long the revealed message is held in view while the transcript settles\n * (an attaching thread can still scroll itself to the bottom). Any scroll the\n * person makes ends it at once.\n */\nexport const HANDOFF_CURSOR_SETTLE_MS = 1_500;\n\nexport type HandoffCursorRevealStep = 'reveal' | 'wait' | 'page' | 'bottom';\n\n/**\n * What a destination does next while opening at a cursor: `reveal` the message\n * it found; `wait` while the transcript (or a page it asked for) is loading or\n * whether older history exists is not known yet (`hasOlder: null`: a cached\n * transcript shows before its history window resolves); `page` older history\n * in while some is left and the budget allows; else stay at the `bottom`, the\n * fallback for a cursor that can't be found.\n */\nexport function handoffCursorRevealStep({\n found,\n loading,\n hasOlder,\n pagesLoaded,\n elapsedMs,\n}: {\n found: boolean;\n loading: boolean;\n hasOlder: boolean | null;\n pagesLoaded: number;\n /** Since the landing last made progress: it started, or a page of older history landed. */\n elapsedMs: number;\n}): HandoffCursorRevealStep {\n if (found) return 'reveal';\n if (elapsedMs >= HANDOFF_CURSOR_WAIT_MS) return 'bottom';\n if (loading || hasOlder === null) return 'wait';\n if (hasOlder && pagesLoaded < HANDOFF_CURSOR_MAX_HISTORY_PAGES) return 'page';\n return 'bottom';\n}\n"],"names":[],"mappings":";;AAuBO,MAAM,qCAAqC;AAE3C,MAAM,wBAAwB,IAAI;AAclC,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAIkB;;AAChB,QAAM,OAAO,OAAO,SAAS,OAAO;AACpC,MAAI,WAA8B;AAClC,MAAI,SAA4B;AAChC,aAAW,OAAO,MAAM;AACtB,QAAI,IAAI,UAAU,IAAI,IAAK;AAC3B,QAAI,IAAI,OAAO,MAAM,KAAK,IAAI,UAAU,SAAS,EAAG,YAAW;AAC/D,QAAI,IAAI,OAAO,QAAQ,IAAI,SAAS,KAAM,UAAS;AAAA,EACrD;AACA,SAAO,oBAAoB,iBAAY,WAAZ,mBAAqB,EAAE;AACpD;AA+BO,MAAM,2BAA2B;AAOjC,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,kBAAkB;AAAA,EAClB,cAAc;AAChB,GAIkB;AAChB,QAAM,aAAa,SAAS,eAAe,SAAS,YAAY,SAAS;AACzE,MAAI,cAAc,mCAAoC,QAAO;AAC7D,QAAM,MAAM,SAAS,sBAAA;AACrB,QAAM,OAAqB,MAAM,KAAK,SAAS,iBAAiB,IAAI,WAAW,GAAG,GAAG,CAAC,QAAQ;AAC5F,UAAM,OAAO,IAAI,sBAAA;AACjB,WAAO,EAAE,IAAI,IAAI,aAAa,WAAW,KAAK,IAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAA;AAAA,EAChF,CAAC;AACD,SAAO,sBAAsB;AAAA,IAC3B,KAAK,IAAI;AAAA,IACT,QAAQ,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,eAAe;AAAA,IACtD;AAAA,EAAA,CACD;AACH;AAQO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AACF,GAGkB;AAChB,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,OAAO,OAAQ,QAAO,QAAQ;AAAA,EAC5C;AACA,aAAW,WAAW,UAAU;AAC9B,UAAM,UAAmB,yBAAyB,OAAO;AACzD,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG;AAC7B,eAAW,UAAU,SAAS;AAC5B,UAAI,UAAU,OAAO,WAAW,YAAa,OAA4B,OAAO,QAAQ;AACtF,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,MAAM,mCAAmC;AAOzC,MAAM,yBAAyB;AAE/B,MAAM,8BAA8B;AAMpC,MAAM,2BAA2B;AAYjC,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAO4B;AAC1B,MAAI,MAAO,QAAO;AAClB,MAAI,aAAa,uBAAwB,QAAO;AAChD,MAAI,WAAW,aAAa,KAAM,QAAO;AACzC,MAAI,YAAY,cAAc,iCAAkC,QAAO;AACvE,SAAO;AACT;"}
|
|
@@ -21,7 +21,7 @@ export { ATHENA_TRUNCATED_TOOL_OUTPUT_ARTIFACT_KEY, buildHistoryMessageCommand,
|
|
|
21
21
|
export { buildHistoryLaneCommand, type DeepAgentLaneHistoryState, type DeepAgentLaneTarget, deepAgentLaneHistoryQueryKey, type HistoryLaneCommand, type HistoryLanePage, isWholeCardRefusal, mergeLaneHistory, readHistoryLanePage, useDeepAgentLaneHistory, } from "./lane-history";
|
|
22
22
|
export { buildHistoryLaneImageCommand, type DeepAgentLaneImagesState, deepAgentLaneImageQueryKey, type HistoryLaneImageChunk, type HistoryLaneImageCommand, type InlineImage, type InlineImageChunk, type LaneImageRef, laneImageKey, omittedLaneImages, readChunkedInlineImage, readHistoryLaneImageChunk, readInlineImageChunk, readLaneImage, readLaneImageTile, showLaneImages, useDeepAgentLaneImages, } from "./lane-images";
|
|
23
23
|
export { createDeepAgentLaneFollowRegistry, type DeepAgentLaneFollowRegistry, type DeepAgentLaneFollowRequest, type DeepAgentLaneFollows, isLaneFollowRequest, laneFollowRequest, mergeFollowedLane, readDeepAgentFollowedWindow, readDeepAgentLaneFollows, useDeepAgentLaneFollow, useDeepAgentLaneFollowSync, type UseDeepAgentLaneFollowSyncOptions, } from "./lane-follow";
|
|
24
|
-
export { buildDeepAgentEditCommand, type DeepAgentPreparedMessageMetadata, type DeepAgentStatewireExtras, isSourceBelowLoadedWindow, loadOlderDeepAgentHistoryPage, MAX_EDIT_ANCHOR_HISTORY_LOADS, readDeepAgentStatewireExtras, resolveDeepAgentEditCommand, resolveWireAnchorMessageId, } from "./edit-command";
|
|
24
|
+
export { buildDeepAgentEditCommand, deepAgentHistoryHasOlder, type DeepAgentPreparedMessageMetadata, type DeepAgentStatewireExtras, isSourceBelowLoadedWindow, loadOlderDeepAgentHistoryPage, MAX_EDIT_ANCHOR_HISTORY_LOADS, readDeepAgentStatewireExtras, resolveDeepAgentEditCommand, resolveWireAnchorMessageId, } from "./edit-command";
|
|
25
25
|
export { DEEP_AGENT_MAX_CHAINED_HISTORY_LOADS, type DeepAgentHistoryLoadEvent, type DeepAgentWindowSyncMessage, type DeepAgentWindowSyncState, useDeepAgentMessageWindow, useDeepAgentWindowSync, type UseDeepAgentWindowSyncOptions, } from "./use-deep-agent-window-sync";
|
|
26
26
|
export { useDeepAgentMessageEditSubmit } from "./use-deep-agent-edit-submit";
|
|
27
27
|
export { createDeepAgentToolOutputStore, type DeepAgentToolOutputEntry, type DeepAgentToolOutputStore, type DeepAgentToolOutputStoreOptions, } from "./tool-outputs";
|
|
@@ -75,8 +75,9 @@ export { type DeepAgentAttachSource, type DeepAgentHeadersResolver, readSessionP
|
|
|
75
75
|
export { GITHUB_EVENT_SOURCE_KEY, type GithubWakeEvent, githubWakeEventLabel, githubWakeEventUrl, githubWakeMarkerText, githubWakeReference, isGithubWakeMessage, readGithubWakeEvent, } from "./github-wake-event";
|
|
76
76
|
export { githubSubscriptionHeadline, isSubscriptionWakeMessage, readSubscriptionWakeEvent, SUBSCRIPTION_EVENT_KEY, type SubscriptionWakeChange, type SubscriptionWakeEvent, type SubscriptionWakeGithub, subscriptionPlaceWords, subscriptionWakeDetail, subscriptionWakeGithub, subscriptionWakeHeadline, } from "./subscription-wake-event";
|
|
77
77
|
export { describeTerminationReason, readRuntimeMessageFromSourceMessages, readSoleRuntimeMessageKind, readTerminationReason, runtimeMessageMarkerText, TERMINATION_REASONS, type AthenaRuntimeMessage, type AthenaRuntimeMessageKind, type AthenaRuntimeMessageSeverity, type AthenaTerminationReason, type TerminationDescription, } from "./runtime-messages";
|
|
78
|
-
export { buildHandoffQrSvg, buildMobileHandoffSchemeUrl, classifyHandoffTransportError, type CreateThreadHandoffInput, type CreateThreadHandoffResponse, createThreadHandoffResponseSchema, describeHandoffTransportError, type DestinationReadiness, HANDOFF_DESTINATION_LABELS, HANDOFF_DESTINATIONS, HANDOFF_URL_PARAM, type HandoffDestination, type HandoffDestinationPresentation, type HandoffEnvironment, HandoffGoneError, type HandoffLaunch, type HandoffOutcome, HandoffPermanentError, type HandoffReadinessResponse, type HandoffReadinessState, type HandoffSource, type HandoffState, type HandoffTransportErrorKind, handoffHttpError, handoffReadinessResponseSchema, handoffStateSchema, isHandoffGoneError, isHandoffPermanentError, isRetryableHandoffHttpStatus, isOfficeHandoffDestination, isServerHandoffDestination, isTerminalHandoffState, type MobileHandoffLaunch, type MobileLaunchMode, mobileHandoffLaunchSchema, OFFICE_HANDOFF_DESTINATIONS, type OfficeHandoffDestination, type OfficeHandoffLaunch, officeHandoffLaunchSchema, type OfficeInvitationLaunch, officeInvitationLaunchSchema, type OfficeLaunchMode, officeLaunchModeSchema, threadHandoffCreateBody, parseHandoffIdParam, presentHandoffDestination, readinessByDestination, SERVER_HANDOFF_DESTINATIONS, type ServerHandoffDestination, selectMobileLaunchMode, supportsDesktopHandoffSource, type ThreadHandoffTransport, type ThreadHandoffView, threadHandoffViewSchema, waitForHandoffOutcome, type WaitForHandoffOutcomeOptions, type ClaimThreadHandoffResponse, claimThreadHandoffResponseSchema, clampHandoffDraft, handoffDraftToCarry, continuedFromLabel, pickUpFromLabel, pickUpOfferLabel, type DesktopThreadHandoffLaunch, desktopThreadHandoffLaunchSchema, driverSurfaceForChannel, drivingOnLabel, type HandoffLanding, handoffLandingFromView, isPickupSurface, MAX_HANDOFF_DRAFT_LENGTH, nextHandoffLaunchSchema, parseThreadHandoffCreateResponse, pendingThreadHandoffResponseSchema, pickedUpOnLabel, PICKUP_SURFACES, type PickupSurface, shouldOfferPendingHandoff, SPACES_HANDOFF_URL_PARAM, type SpacesHandoffLaunch, spacesHandoffLaunchSchema, SURFACE_HANDOFF_DESTINATIONS, type SurfaceHandoffDestination, supportsSurfaceHandoffDestinations, THREAD_HANDOFF_DESTINATION_LABELS, THREAD_HANDOFF_DESTINATIONS, threadHandoffClaimBody, threadHandoffCreateRequest, type ThreadHandoffDestination, type ThreadHandoffReceiverTransport, MAX_PENDING_EXCLUDE, pendingExcludeIds, pickupExcludeIds, pendingHandoffPath, type HandoffMode, handoffModeSchema, isVoiceHandoffDestination, livenessAgo, presentSurfaceLiveness, type SurfaceLiveness, type SurfaceLivenessBasis, surfaceLivenessBasisSchema, surfaceLivenessBySurface, type SurfaceLivenessPresentation, surfaceLivenessSchema, type SurfaceLivenessState, surfaceLivenessStateSchema, type SurfaceLivenessTone, supportsSurfaceLiveness, } from "./thread-handoff";
|
|
78
|
+
export { buildHandoffQrSvg, buildMobileHandoffSchemeUrl, classifyHandoffTransportError, type CreateThreadHandoffInput, type CreateThreadHandoffResponse, createThreadHandoffResponseSchema, describeHandoffTransportError, type DestinationReadiness, HANDOFF_DESTINATION_LABELS, HANDOFF_DESTINATIONS, HANDOFF_URL_PARAM, type HandoffDestination, type HandoffDestinationPresentation, type HandoffEnvironment, HandoffGoneError, type HandoffLaunch, type HandoffOutcome, HandoffPermanentError, type HandoffReadinessResponse, type HandoffReadinessState, type HandoffSource, type HandoffState, type HandoffTransportErrorKind, handoffHttpError, handoffReadinessResponseSchema, handoffStateSchema, isHandoffGoneError, isHandoffPermanentError, isRetryableHandoffHttpStatus, isOfficeHandoffDestination, isServerHandoffDestination, isTerminalHandoffState, type MobileHandoffLaunch, type MobileLaunchMode, mobileHandoffLaunchSchema, OFFICE_HANDOFF_DESTINATIONS, type OfficeHandoffDestination, type OfficeHandoffLaunch, officeHandoffLaunchSchema, type OfficeInvitationLaunch, officeInvitationLaunchSchema, type OfficeLaunchMode, officeLaunchModeSchema, threadHandoffCreateBody, parseHandoffIdParam, presentHandoffDestination, readinessByDestination, SERVER_HANDOFF_DESTINATIONS, type ServerHandoffDestination, selectMobileLaunchMode, supportsDesktopHandoffSource, type ThreadHandoffTransport, type ThreadHandoffView, threadHandoffViewSchema, waitForHandoffOutcome, type WaitForHandoffOutcomeOptions, type ClaimThreadHandoffResponse, claimThreadHandoffResponseSchema, clampHandoffDraft, handoffDraftToCarry, continuedFromLabel, pickUpFromLabel, pickUpOfferLabel, type DesktopThreadHandoffLaunch, desktopThreadHandoffLaunchSchema, driverSurfaceForChannel, drivingOnLabel, type HandoffLanding, handoffLandingFromView, isPickupSurface, MAX_HANDOFF_DRAFT_LENGTH, nextHandoffLaunchSchema, parseThreadHandoffCreateResponse, pendingThreadHandoffResponseSchema, pickedUpOnLabel, PICKUP_SURFACES, type PickupSurface, shouldOfferPendingHandoff, IMPLICIT_PICKUP_ACTIVATION_WINDOW_MS, IMPLICIT_PICKUP_DECLINE_HOLD_MS, type ImplicitPickupAnswer, type ImplicitPickupContext, implicitPickupAllowed, isImplicitHandoff, nextImplicitPickupAnswer, SPACES_HANDOFF_URL_PARAM, type SpacesHandoffLaunch, spacesHandoffLaunchSchema, SURFACE_HANDOFF_DESTINATIONS, type SurfaceHandoffDestination, supportsSurfaceHandoffDestinations, THREAD_HANDOFF_DESTINATION_LABELS, THREAD_HANDOFF_DESTINATIONS, threadHandoffClaimBody, threadHandoffCreateRequest, type ThreadHandoffDestination, type ThreadHandoffReceiverTransport, MAX_PENDING_EXCLUDE, handoffDeclineKey, pendingExcludeIds, pickupExcludeIds, pendingHandoffPath, type HandoffMode, handoffModeSchema, isVoiceHandoffDestination, MAX_HANDOFF_CURSOR_LENGTH, parseHandoffCursor, supportsHandoffCursor, livenessAgo, presentSurfaceLiveness, type SurfaceLiveness, type SurfaceLivenessBasis, surfaceLivenessBasisSchema, surfaceLivenessBySurface, type SurfaceLivenessPresentation, surfaceLivenessSchema, type SurfaceLivenessState, surfaceLivenessStateSchema, type SurfaceLivenessTone, supportsSurfaceLiveness, } from "./thread-handoff";
|
|
79
79
|
export { DEFAULT_MOBILE_HANDOFF_TIMEOUT_MS, DEFAULT_OFFICE_HANDOFF_TIMEOUT_MS, DEFAULT_PICKUP_HANDOFF_TIMEOUT_MS, type StartThreadHandoffInput, type ThreadHandoffFlowCallbacks, type ThreadHandoffFlowController, type ThreadHandoffFlowPhase, useThreadHandoffFlow, type UseThreadHandoffFlowOptions, } from "./use-thread-handoff-flow";
|
|
80
|
-
export { anchorHandoffArrival, clearHandoffArrival, type HandoffArrival, HandoffArrivalContext, MAX_HANDOFF_ARRIVALS, readHandoffArrival, recordHandoffArrival, resetHandoffArrivalsForTests, subscribeHandoffArrivals, useHandoffArrival, useHandoffArrivalAfter, } from "./handoff-arrival";
|
|
80
|
+
export { anchorHandoffArrival, clearHandoffArrival, type HandoffArrival, HandoffArrivalContext, MAX_HANDOFF_ARRIVALS, readHandoffArrival, recordHandoffArrival, resetHandoffArrivalsForTests, settleHandoffArrivalCursor, subscribeHandoffArrivals, useHandoffArrival, useHandoffArrivalAfter, useHandoffArrivalCursor, } from "./handoff-arrival";
|
|
81
|
+
export { HANDOFF_CURSOR_HIGHLIGHT_MS, HANDOFF_CURSOR_MAX_HISTORY_PAGES, HANDOFF_CURSOR_SETTLE_MS, HANDOFF_CURSOR_WAIT_MS, handoffCursorMessageId, handoffCursorRevealStep, type HandoffCursorRevealStep, MESSAGE_ROW_ID_ATTRIBUTE, READING_CURSOR_BOTTOM_TOLERANCE_PX, READING_LINE_FRACTION, type ReadingRow, type ReadingViewportLike, readingCursorFromRows, readingCursorFromVisibleIds, readingCursorOf, } from "./handoff-cursor";
|
|
81
82
|
export { connectVoiceMedia, VOICE_MICROPHONE_CAPTURE } from './voice-media';
|
|
82
83
|
export type { VoiceMediaDriver, VoiceMediaEvent, VoiceMediaEventType, VoiceMediaHandlers, VoiceMediaSession, } from './voice-media';
|
|
@@ -54,6 +54,9 @@ export declare const threadHandoffFlowMachine: import("xstate").StateMachine<Han
|
|
|
54
54
|
claimed_surface?: "chrome" | "desktop" | "mobile" | "spaces" | null | undefined;
|
|
55
55
|
mode?: "text" | "voice" | undefined;
|
|
56
56
|
draft?: string | null | undefined;
|
|
57
|
+
cursor?: string | null | undefined;
|
|
58
|
+
revision?: number | undefined;
|
|
59
|
+
implicit?: boolean | undefined;
|
|
57
60
|
};
|
|
58
61
|
launch: {
|
|
59
62
|
kind: "mobile";
|
|
@@ -114,6 +117,9 @@ export declare const threadHandoffFlowMachine: import("xstate").StateMachine<Han
|
|
|
114
117
|
claimed_surface?: "chrome" | "desktop" | "mobile" | "spaces" | null | undefined;
|
|
115
118
|
mode?: "text" | "voice" | undefined;
|
|
116
119
|
draft?: string | null | undefined;
|
|
120
|
+
cursor?: string | null | undefined;
|
|
121
|
+
revision?: number | undefined;
|
|
122
|
+
implicit?: boolean | undefined;
|
|
117
123
|
};
|
|
118
124
|
launch: {
|
|
119
125
|
kind: "mobile";
|
|
@@ -29,6 +29,7 @@ export declare const SURFACE_HANDOFF_DESTINATIONS: readonly ['spaces', 'mobile',
|
|
|
29
29
|
export type SurfaceHandoffDestination = (typeof SURFACE_HANDOFF_DESTINATIONS)[number];
|
|
30
30
|
/** The source composer's unsent text that travels prefilled, never sent. */
|
|
31
31
|
export declare const MAX_HANDOFF_DRAFT_LENGTH = 4000;
|
|
32
|
+
export { MAX_HANDOFF_CURSOR_LENGTH, parseHandoffCursor } from './handoff-cursor-id';
|
|
32
33
|
/**
|
|
33
34
|
* How the person chose to continue. `voice` is an offer the destination shows
|
|
34
35
|
* ("Continue by voice"); it never starts a microphone by itself. Older servers
|
|
@@ -170,6 +171,7 @@ export declare const handoffReadinessResponseSchema: z.ZodObject<{
|
|
|
170
171
|
supports_office_invitations: z.ZodOptional<z.ZodBoolean>;
|
|
171
172
|
supports_desktop_source: z.ZodOptional<z.ZodBoolean>;
|
|
172
173
|
supports_surface_destinations: z.ZodOptional<z.ZodBoolean>;
|
|
174
|
+
supports_handoff_cursor: z.ZodOptional<z.ZodBoolean>;
|
|
173
175
|
supports_surface_liveness: z.ZodOptional<z.ZodBoolean>;
|
|
174
176
|
surfaces: z.ZodCatch<z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodUnknown>, z.ZodTransform<{
|
|
175
177
|
surface: "chrome" | "desktop" | "mobile" | "spaces";
|
|
@@ -183,6 +185,8 @@ export type HandoffReadinessResponse = z.infer<typeof handoffReadinessResponseSc
|
|
|
183
185
|
export declare const supportsDesktopHandoffSource: (readiness: HandoffReadinessResponse | null | undefined) => boolean;
|
|
184
186
|
/** Whether the server can broker the four-surface destinations; absent means an older agora. */
|
|
185
187
|
export declare const supportsSurfaceHandoffDestinations: (readiness: HandoffReadinessResponse | null | undefined) => boolean;
|
|
188
|
+
/** Whether the server takes a reading position (`cursor`); absent means an older agora. */
|
|
189
|
+
export declare const supportsHandoffCursor: (readiness: HandoffReadinessResponse | null | undefined) => boolean;
|
|
186
190
|
/** Whether the server reports where the person's apps were last seen; absent means an older agora. */
|
|
187
191
|
export declare const supportsSurfaceLiveness: (readiness: HandoffReadinessResponse | null | undefined) => boolean;
|
|
188
192
|
/** Index liveness rows by surface; `null` where the server sent none (or can't report liveness). */
|
|
@@ -256,8 +260,17 @@ export declare const threadHandoffViewSchema: z.ZodObject<{
|
|
|
256
260
|
voice: "voice";
|
|
257
261
|
}>>;
|
|
258
262
|
draft: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
263
|
+
cursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
264
|
+
revision: z.ZodOptional<z.ZodNumber>;
|
|
265
|
+
implicit: z.ZodOptional<z.ZodBoolean>;
|
|
259
266
|
}, z.core.$strip>;
|
|
260
267
|
export type ThreadHandoffView = z.infer<typeof threadHandoffViewSchema>;
|
|
268
|
+
/**
|
|
269
|
+
* What a "Not now" remembers: the ticket at the revision it was offered at (`<id>@<revision>`).
|
|
270
|
+
* Sent back as `exclude`, it skips that ticket only until the same conversation is sent here
|
|
271
|
+
* again, which offers it again.
|
|
272
|
+
*/
|
|
273
|
+
export declare function handoffDeclineKey(view: Pick<ThreadHandoffView, 'handoff_id' | 'revision'>): string;
|
|
261
274
|
export declare const mobileHandoffLaunchSchema: z.ZodObject<{
|
|
262
275
|
kind: z.ZodLiteral<"mobile">;
|
|
263
276
|
push_attempted: z.ZodBoolean;
|
|
@@ -400,6 +413,9 @@ export declare const createThreadHandoffResponseSchema: z.ZodObject<{
|
|
|
400
413
|
voice: "voice";
|
|
401
414
|
}>>;
|
|
402
415
|
draft: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
416
|
+
cursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
417
|
+
revision: z.ZodOptional<z.ZodNumber>;
|
|
418
|
+
implicit: z.ZodOptional<z.ZodBoolean>;
|
|
403
419
|
}, z.core.$strip>;
|
|
404
420
|
launch: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
405
421
|
kind: z.ZodLiteral<"mobile">;
|
|
@@ -457,6 +473,13 @@ export interface CreateThreadHandoffInput {
|
|
|
457
473
|
draft?: string;
|
|
458
474
|
/** `voice` offers "Continue by voice" on the destination. Omitted means text. */
|
|
459
475
|
mode?: HandoffMode;
|
|
476
|
+
/**
|
|
477
|
+
* Where the source was reading: the id of the last message it showed, or `''`
|
|
478
|
+
* when it was at the bottom (which also clears a reused ticket's). Send it only
|
|
479
|
+
* to a host whose readiness says `supports_handoff_cursor`. A value that is
|
|
480
|
+
* not a message id goes as `''`.
|
|
481
|
+
*/
|
|
482
|
+
cursor?: string;
|
|
460
483
|
}
|
|
461
484
|
/**
|
|
462
485
|
* The draft a create carries, the one rule every source follows. An Office
|
|
@@ -475,7 +498,7 @@ export declare function handoffDraftToCarry({ destination, draft, }: {
|
|
|
475
498
|
/** A phone invites the computer's pane; other sources keep their existing launch. */
|
|
476
499
|
export declare function resolveOfficeHandoffLaunchMode({ source, destination, officeLaunchMode, }: CreateThreadHandoffInput): OfficeLaunchMode | undefined;
|
|
477
500
|
/** The same request body for every host's authenticated HTTP adapter. */
|
|
478
|
-
export declare function threadHandoffCreateBody({ threadId, destination, source, officeLaunchMode, expectedWorkspaceId, draft, mode: handoffMode, }: CreateThreadHandoffInput): {
|
|
501
|
+
export declare function threadHandoffCreateBody({ threadId, destination, source, officeLaunchMode, expectedWorkspaceId, draft, mode: handoffMode, cursor, }: CreateThreadHandoffInput): {
|
|
479
502
|
thread_id: string;
|
|
480
503
|
destination: "chrome" | "desktop" | "excel" | "mobile" | "next" | "powerpoint" | "spaces" | "word";
|
|
481
504
|
source: HandoffSource;
|
|
@@ -483,6 +506,7 @@ export declare function threadHandoffCreateBody({ threadId, destination, source,
|
|
|
483
506
|
office_launch_mode?: "invitation" | "new_document" | undefined;
|
|
484
507
|
draft?: string | undefined;
|
|
485
508
|
mode?: "voice" | undefined;
|
|
509
|
+
cursor?: string | undefined;
|
|
486
510
|
};
|
|
487
511
|
/** Keep a draft within the server's limit at a character boundary; never send more. */
|
|
488
512
|
export declare const clampHandoffDraft: (draft: string) => string;
|
|
@@ -528,10 +552,15 @@ export declare function pickupExcludeIds({ issuedHere, declined, }: {
|
|
|
528
552
|
issuedHere: Iterable<string>;
|
|
529
553
|
declined: Iterable<string>;
|
|
530
554
|
}): string[];
|
|
531
|
-
/**
|
|
532
|
-
|
|
555
|
+
/**
|
|
556
|
+
* `/pending?destination=…&exclude=…` relative to `/api/handoffs`. `includeImplicit` asks for an
|
|
557
|
+
* implicit ticket too ("pick up without sending"): only on a `next` peek, the one destination an
|
|
558
|
+
* implicit ticket names. A server that predates it ignores the flag.
|
|
559
|
+
*/
|
|
560
|
+
export declare function pendingHandoffPath({ destination, exclude, includeImplicit, }: {
|
|
533
561
|
destination: Exclude<ThreadHandoffDestination, 'desktop'> | 'desktop';
|
|
534
562
|
exclude?: Iterable<string>;
|
|
563
|
+
includeImplicit?: boolean;
|
|
535
564
|
}): string;
|
|
536
565
|
export declare const claimThreadHandoffResponseSchema: z.ZodObject<{
|
|
537
566
|
handoff: z.ZodObject<{
|
|
@@ -582,6 +611,9 @@ export declare const claimThreadHandoffResponseSchema: z.ZodObject<{
|
|
|
582
611
|
voice: "voice";
|
|
583
612
|
}>>;
|
|
584
613
|
draft: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
614
|
+
cursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
615
|
+
revision: z.ZodOptional<z.ZodNumber>;
|
|
616
|
+
implicit: z.ZodOptional<z.ZodBoolean>;
|
|
585
617
|
}, z.core.$strip>;
|
|
586
618
|
}, z.core.$strip>;
|
|
587
619
|
export type ClaimThreadHandoffResponse = z.infer<typeof claimThreadHandoffResponseSchema>;
|
|
@@ -635,6 +667,9 @@ export declare const pendingThreadHandoffResponseSchema: z.ZodNullable<z.ZodObje
|
|
|
635
667
|
voice: "voice";
|
|
636
668
|
}>>;
|
|
637
669
|
draft: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
670
|
+
cursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
671
|
+
revision: z.ZodOptional<z.ZodNumber>;
|
|
672
|
+
implicit: z.ZodOptional<z.ZodBoolean>;
|
|
638
673
|
}, z.core.$strip>;
|
|
639
674
|
}, z.core.$strip>>;
|
|
640
675
|
/**
|
|
@@ -650,6 +685,8 @@ export interface ThreadHandoffReceiverTransport {
|
|
|
650
685
|
destination: Exclude<ThreadHandoffDestination, 'desktop'>;
|
|
651
686
|
/** Tickets this receiver declined or issued itself; the newest other one is returned. */
|
|
652
687
|
exclude?: readonly string[];
|
|
688
|
+
/** Also an implicit ticket, on a `next` peek ({@link pendingHandoffPath}). */
|
|
689
|
+
includeImplicit?: boolean;
|
|
653
690
|
signal?: AbortSignal;
|
|
654
691
|
}) => Promise<ClaimThreadHandoffResponse | null>;
|
|
655
692
|
claim: (options: {
|
|
@@ -667,16 +704,84 @@ export interface ThreadHandoffReceiverTransport {
|
|
|
667
704
|
signal?: AbortSignal;
|
|
668
705
|
}) => Promise<ThreadHandoffView>;
|
|
669
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* "Pick up without sending": an app offers a conversation the person is driving on another of
|
|
709
|
+
* their apps only when it just became active (the Spaces tab became visible, the phone came to the
|
|
710
|
+
* foreground, Chrome's side panel opened, the desktop window gained focus), within this long.
|
|
711
|
+
*/
|
|
712
|
+
export declare const IMPLICIT_PICKUP_ACTIVATION_WINDOW_MS = 10000;
|
|
713
|
+
/**
|
|
714
|
+
* How long a "Not now" to an implicit offer is kept where declines expire (Chrome, the desktop
|
|
715
|
+
* app). The ticket keeps its id for as long as the same app drives, past its 10 minutes, so a
|
|
716
|
+
* decline kept only as long as the ticket's first expiry would let the same offer come back.
|
|
717
|
+
*/
|
|
718
|
+
export declare const IMPLICIT_PICKUP_DECLINE_HOLD_MS: number;
|
|
719
|
+
/** Whether a view is an implicit ticket: left by the app the person drives from, never sent. */
|
|
720
|
+
export declare const isImplicitHandoff: (view: Pick<ThreadHandoffView, 'implicit'> | null | undefined) => boolean;
|
|
721
|
+
/**
|
|
722
|
+
* What a receiving app knows about itself to judge an implicit ticket. `seenAt` comes from
|
|
723
|
+
* {@link nextImplicitPickupAnswer}: when this app first saw the ticket after `becameActiveAt`.
|
|
724
|
+
*/
|
|
725
|
+
export interface ImplicitPickupContext {
|
|
726
|
+
/** This app. */
|
|
727
|
+
here: PickupSurface;
|
|
728
|
+
/** The conversation this app shows now; `null` when none. */
|
|
729
|
+
showingThreadId: string | null;
|
|
730
|
+
/** When this app last became active; `null` until it has. */
|
|
731
|
+
becameActiveAt: number | null;
|
|
732
|
+
/** When this app first saw the ticket after becoming active; `null` when it has not. */
|
|
733
|
+
seenAt: number | null;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Whether an implicit ticket may be offered here: it is another app's, this app isn't already
|
|
737
|
+
* showing that conversation, and this app saw it within {@link IMPLICIT_PICKUP_ACTIVATION_WINDOW_MS}
|
|
738
|
+
* of becoming active. An idle poll's answer never qualifies on its own, and nothing here ever
|
|
739
|
+
* raises a notification: only the app's own banner offers one.
|
|
740
|
+
*/
|
|
741
|
+
export declare function implicitPickupAllowed({ pending, context, }: {
|
|
742
|
+
pending: Pick<ThreadHandoffView, 'source' | 'thread_id'>;
|
|
743
|
+
context: ImplicitPickupContext;
|
|
744
|
+
}): boolean;
|
|
745
|
+
/** An implicit-pickup peek's answer, with what judging it needs. */
|
|
746
|
+
export interface ImplicitPickupAnswer<View extends {
|
|
747
|
+
handoff_id: string;
|
|
748
|
+
} = ThreadHandoffView> {
|
|
749
|
+
view: View | null;
|
|
750
|
+
/** The activation this answer belongs to. */
|
|
751
|
+
becameActiveAt: number;
|
|
752
|
+
/** When this app first saw `view`'s ticket since that activation. */
|
|
753
|
+
seenAt: number;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Fold a new peek into the earlier ones: a ticket seen again since the same activation keeps the
|
|
757
|
+
* time it was first seen, so an offer made on becoming active stays up until it is answered or
|
|
758
|
+
* goes away, while a ticket that first shows up on a later poll is never offered. `previous` may
|
|
759
|
+
* be every answer of this activation (one per set of exclusions a host asked with), so declining
|
|
760
|
+
* another ticket never makes this one look new.
|
|
761
|
+
*/
|
|
762
|
+
export declare function nextImplicitPickupAnswer<View extends {
|
|
763
|
+
handoff_id: string;
|
|
764
|
+
} = ThreadHandoffView>({ previous, view, becameActiveAt, now, }: {
|
|
765
|
+
previous: ImplicitPickupAnswer<View> | readonly (ImplicitPickupAnswer<View> | null | undefined)[] | null | undefined;
|
|
766
|
+
view: View | null;
|
|
767
|
+
becameActiveAt: number;
|
|
768
|
+
now?: number;
|
|
769
|
+
}): ImplicitPickupAnswer<View>;
|
|
670
770
|
/**
|
|
671
771
|
* Whether a pending handoff should be offered on this surface: still open,
|
|
672
772
|
* not one this device issued itself, and not one it already declined.
|
|
673
773
|
* Offering never claims; the person's tap does.
|
|
774
|
+
*
|
|
775
|
+
* A ticket the person sent is judged exactly as it always was. An implicit
|
|
776
|
+
* one ("pick up without sending") also needs `implicit`, the receiver's own
|
|
777
|
+
* context ({@link implicitPickupAllowed}); without it, it is never offered.
|
|
674
778
|
*/
|
|
675
|
-
export declare function shouldOfferPendingHandoff({ pending, issuedHere, declined, now, }: {
|
|
779
|
+
export declare function shouldOfferPendingHandoff({ pending, issuedHere, declined, now, implicit, }: {
|
|
676
780
|
pending: ThreadHandoffView | null | undefined;
|
|
677
781
|
issuedHere: ReadonlySet<string>;
|
|
678
782
|
declined: ReadonlySet<string>;
|
|
679
783
|
now?: number;
|
|
784
|
+
implicit?: ImplicitPickupContext;
|
|
680
785
|
}): boolean;
|
|
681
786
|
/**
|
|
682
787
|
* Surface-specific HTTP adapter. Olympus binds it to `apiClient`; the
|
|
@@ -741,6 +846,11 @@ export interface HandoffLanding {
|
|
|
741
846
|
* "Continue by voice"; nothing starts the microphone on its own.
|
|
742
847
|
*/
|
|
743
848
|
continueByVoice: boolean;
|
|
849
|
+
/**
|
|
850
|
+
* The message the source was reading; the transcript opens there instead of
|
|
851
|
+
* at the bottom. `null` when the source was at the bottom (or sent none).
|
|
852
|
+
*/
|
|
853
|
+
cursor: string | null;
|
|
744
854
|
}
|
|
745
855
|
export declare const handoffLandingFromView: (view: ThreadHandoffView) => HandoffLanding;
|
|
746
856
|
export declare const isOfficeHandoffDestination: (destination: ThreadHandoffDestination) => destination is OfficeHandoffDestination;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Backward-compatible entrypoint. Use the protocol entrypoint without QR rendering. */
|
|
2
|
-
export { type CreateThreadHandoffInput, type CreateThreadHandoffResponse, type DestinationReadiness, HANDOFF_DESTINATIONS, HANDOFF_DESTINATION_LABELS, HANDOFF_URL_PARAM, type HandoffDestination, type HandoffDestinationPresentation, type HandoffEnvironment, HandoffGoneError, type HandoffLaunch, type HandoffOutcome, HandoffPermanentError, type HandoffReadinessResponse, type HandoffReadinessState, type HandoffSource, type HandoffState, type HandoffTransportErrorKind, MOBILE_APP_SCHEME, type MobileHandoffLaunch, type MobileLaunchMode, OFFICE_HANDOFF_DESTINATIONS, type OfficeHandoffDestination, type OfficeHandoffLaunch, type OfficeInvitationLaunch, type OfficeLaunchMode, SERVER_HANDOFF_DESTINATIONS, type ServerHandoffDestination, type ThreadHandoffTransport, type ThreadHandoffView, type WaitForHandoffOutcomeOptions, buildMobileHandoffSchemeUrl, chromeHandoffLaunchSchema, classifyHandoffTransportError, createThreadHandoffResponseSchema, describeHandoffTransportError, destinationReadinessSchema, handoffEnvironmentSchema, handoffHttpError, handoffLaunchSchema, handoffReadinessResponseSchema, handoffReadinessStateSchema, handoffStateSchema, isHandoffGoneError, isHandoffPermanentError, isOfficeHandoffDestination, isRetryableHandoffHttpStatus, isServerHandoffDestination, isTerminalHandoffState, mobileHandoffLaunchSchema, officeHandoffLaunchSchema, officeInvitationLaunchSchema, officeLaunchModeSchema, parseHandoffIdParam, presentHandoffDestination, readinessByDestination, resolveOfficeHandoffLaunchMode, selectMobileLaunchMode, supportsDesktopHandoffSource, threadHandoffCreateBody, threadHandoffViewSchema, waitForHandoffOutcome, type ClaimThreadHandoffResponse, claimThreadHandoffResponseSchema, clampHandoffDraft, handoffDraftToCarry, continuedFromLabel, pickUpFromLabel, pickUpOfferLabel, type DesktopThreadHandoffLaunch, desktopThreadHandoffLaunchSchema, driverSurfaceForChannel, drivingOnLabel, type HandoffLanding, handoffLandingFromView, isPickupSurface, MAX_HANDOFF_DRAFT_LENGTH, nextHandoffLaunchSchema, parseThreadHandoffCreateResponse, pendingThreadHandoffResponseSchema, pickedUpOnLabel, PICKUP_SURFACES, type PickupSurface, shouldOfferPendingHandoff, SPACES_HANDOFF_URL_PARAM, type SpacesHandoffLaunch, spacesHandoffLaunchSchema, SURFACE_HANDOFF_DESTINATIONS, type SurfaceHandoffDestination, supportsSurfaceHandoffDestinations, THREAD_HANDOFF_DESTINATION_LABELS, THREAD_HANDOFF_DESTINATIONS, threadHandoffClaimBody, threadHandoffCreateRequest, type ThreadHandoffDestination, type ThreadHandoffReceiverTransport, MAX_PENDING_EXCLUDE, pendingExcludeIds, pickupExcludeIds, pendingHandoffPath, type HandoffMode, handoffModeSchema, isVoiceHandoffDestination, livenessAgo, presentSurfaceLiveness, type SurfaceLiveness, type SurfaceLivenessBasis, surfaceLivenessBasisSchema, surfaceLivenessBySurface, type SurfaceLivenessPresentation, surfaceLivenessSchema, type SurfaceLivenessState, surfaceLivenessStateSchema, type SurfaceLivenessTone, supportsSurfaceLiveness, } from './thread-handoff-protocol';
|
|
2
|
+
export { type CreateThreadHandoffInput, type CreateThreadHandoffResponse, type DestinationReadiness, HANDOFF_DESTINATIONS, HANDOFF_DESTINATION_LABELS, HANDOFF_URL_PARAM, type HandoffDestination, type HandoffDestinationPresentation, type HandoffEnvironment, HandoffGoneError, type HandoffLaunch, type HandoffOutcome, HandoffPermanentError, type HandoffReadinessResponse, type HandoffReadinessState, type HandoffSource, type HandoffState, type HandoffTransportErrorKind, MOBILE_APP_SCHEME, type MobileHandoffLaunch, type MobileLaunchMode, OFFICE_HANDOFF_DESTINATIONS, type OfficeHandoffDestination, type OfficeHandoffLaunch, type OfficeInvitationLaunch, type OfficeLaunchMode, SERVER_HANDOFF_DESTINATIONS, type ServerHandoffDestination, type ThreadHandoffTransport, type ThreadHandoffView, type WaitForHandoffOutcomeOptions, buildMobileHandoffSchemeUrl, chromeHandoffLaunchSchema, classifyHandoffTransportError, createThreadHandoffResponseSchema, describeHandoffTransportError, destinationReadinessSchema, handoffEnvironmentSchema, handoffHttpError, handoffLaunchSchema, handoffReadinessResponseSchema, handoffReadinessStateSchema, handoffStateSchema, isHandoffGoneError, isHandoffPermanentError, isOfficeHandoffDestination, isRetryableHandoffHttpStatus, isServerHandoffDestination, isTerminalHandoffState, mobileHandoffLaunchSchema, officeHandoffLaunchSchema, officeInvitationLaunchSchema, officeLaunchModeSchema, parseHandoffIdParam, presentHandoffDestination, readinessByDestination, resolveOfficeHandoffLaunchMode, selectMobileLaunchMode, supportsDesktopHandoffSource, threadHandoffCreateBody, threadHandoffViewSchema, waitForHandoffOutcome, type ClaimThreadHandoffResponse, claimThreadHandoffResponseSchema, clampHandoffDraft, handoffDraftToCarry, continuedFromLabel, pickUpFromLabel, pickUpOfferLabel, type DesktopThreadHandoffLaunch, desktopThreadHandoffLaunchSchema, driverSurfaceForChannel, drivingOnLabel, type HandoffLanding, handoffLandingFromView, isPickupSurface, MAX_HANDOFF_DRAFT_LENGTH, nextHandoffLaunchSchema, parseThreadHandoffCreateResponse, pendingThreadHandoffResponseSchema, pickedUpOnLabel, PICKUP_SURFACES, type PickupSurface, shouldOfferPendingHandoff, IMPLICIT_PICKUP_ACTIVATION_WINDOW_MS, IMPLICIT_PICKUP_DECLINE_HOLD_MS, type ImplicitPickupAnswer, type ImplicitPickupContext, implicitPickupAllowed, isImplicitHandoff, nextImplicitPickupAnswer, SPACES_HANDOFF_URL_PARAM, type SpacesHandoffLaunch, spacesHandoffLaunchSchema, SURFACE_HANDOFF_DESTINATIONS, type SurfaceHandoffDestination, supportsSurfaceHandoffDestinations, THREAD_HANDOFF_DESTINATION_LABELS, THREAD_HANDOFF_DESTINATIONS, threadHandoffClaimBody, threadHandoffCreateRequest, type ThreadHandoffDestination, type ThreadHandoffReceiverTransport, MAX_PENDING_EXCLUDE, handoffDeclineKey, pendingExcludeIds, pickupExcludeIds, pendingHandoffPath, type HandoffMode, handoffModeSchema, isVoiceHandoffDestination, MAX_HANDOFF_CURSOR_LENGTH, parseHandoffCursor, supportsHandoffCursor, livenessAgo, presentSurfaceLiveness, type SurfaceLiveness, type SurfaceLivenessBasis, surfaceLivenessBasisSchema, surfaceLivenessBySurface, type SurfaceLivenessPresentation, surfaceLivenessSchema, type SurfaceLivenessState, surfaceLivenessStateSchema, type SurfaceLivenessTone, supportsSurfaceLiveness, } from './thread-handoff-protocol';
|
|
3
3
|
export { buildHandoffQrSvg } from './thread-handoff-qr';
|
|
@@ -33,6 +33,9 @@ function pageShowedEarlierMessage(from, messages) {
|
|
|
33
33
|
}
|
|
34
34
|
return messages.length > from.count;
|
|
35
35
|
}
|
|
36
|
+
function isReply(message) {
|
|
37
|
+
return (message == null ? void 0 : message.role) !== void 0 && message.role !== "user";
|
|
38
|
+
}
|
|
36
39
|
function opensMidTurn(messages) {
|
|
37
40
|
const first = messages[0];
|
|
38
41
|
if (first === void 0) return false;
|
|
@@ -130,15 +133,17 @@ function useDeepAgentWindowSync({
|
|
|
130
133
|
if (chainRef.current.loads >= maxChainedLoads) return;
|
|
131
134
|
chainRef.current.loads += 1;
|
|
132
135
|
}
|
|
136
|
+
const insideTurn = lastPageUnseen || isReply(messages[0]);
|
|
137
|
+
const stated = insideTurn && messageWindow.pageInterestRequests(window, runconfigContract.DEEP_AGENT_MAX_MESSAGE_WINDOW) || pageRequests;
|
|
133
138
|
pageFromRef.current = { count: messages.length, secondId: (_a = messages[1]) == null ? void 0 : _a.id };
|
|
134
139
|
const page = window.pages + 1;
|
|
135
|
-
const request =
|
|
140
|
+
const request = stated[stated.length - 1];
|
|
136
141
|
inflightRef.current = true;
|
|
137
142
|
setLoading(true);
|
|
138
143
|
const startedAt = nowRef.current();
|
|
139
|
-
invoke(runconfigContract.DEEP_AGENT_INTEREST_PROTOCOL, "set", [
|
|
144
|
+
invoke(runconfigContract.DEEP_AGENT_INTEREST_PROTOCOL, "set", [stated]).then(() => {
|
|
140
145
|
var _a2;
|
|
141
|
-
statedRef.current =
|
|
146
|
+
statedRef.current = stated;
|
|
142
147
|
(_a2 = onHistoryLoadRef.current) == null ? void 0 : _a2.call(onHistoryLoadRef, {
|
|
143
148
|
page,
|
|
144
149
|
request,
|