@athenaintel/react 0.48.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 +68 -10
- package/dist/agent-runtime/src/call-recap.cjs +319 -0
- package/dist/agent-runtime/src/call-recap.cjs.map +1 -0
- package/dist/agent-runtime/src/call-recap.d.ts +142 -0
- package/dist/agent-runtime/src/call-recap.js +319 -0
- package/dist/agent-runtime/src/call-recap.js.map +1 -0
- package/dist/agent-runtime/src/chat-outbox-store.cjs +1 -0
- package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -1
- package/dist/agent-runtime/src/chat-outbox-store.d.ts +7 -0
- package/dist/agent-runtime/src/chat-outbox-store.js +1 -0
- package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -1
- package/dist/agent-runtime/src/chat-outbox.cjs +46 -5
- package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -1
- package/dist/agent-runtime/src/chat-outbox.d.ts +29 -0
- package/dist/agent-runtime/src/chat-outbox.js +46 -5
- package/dist/agent-runtime/src/chat-outbox.js.map +1 -1
- package/dist/agent-runtime/src/conversation-index.cjs.map +1 -1
- package/dist/agent-runtime/src/conversation-index.d.ts +7 -0
- package/dist/agent-runtime/src/conversation-index.js.map +1 -1
- package/dist/agent-runtime/src/conversation-list.cjs +2 -1
- package/dist/agent-runtime/src/conversation-list.cjs.map +1 -1
- package/dist/agent-runtime/src/conversation-list.d.ts +19 -3
- package/dist/agent-runtime/src/conversation-list.js +2 -1
- package/dist/agent-runtime/src/conversation-list.js.map +1 -1
- 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 +9 -7
- package/dist/agent-runtime/src/interrupts.cjs +28 -1
- package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/agent-runtime/src/interrupts.d.ts +21 -1
- package/dist/agent-runtime/src/interrupts.js +28 -1
- package/dist/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/agent-runtime/src/model-context.cjs +3 -2
- package/dist/agent-runtime/src/model-context.cjs.map +1 -1
- package/dist/agent-runtime/src/model-context.d.ts +6 -1
- package/dist/agent-runtime/src/model-context.js +3 -2
- package/dist/agent-runtime/src/model-context.js.map +1 -1
- package/dist/agent-runtime/src/runtime.d.ts +3 -2
- package/dist/agent-runtime/src/send-recovery.cjs +37 -3
- package/dist/agent-runtime/src/send-recovery.cjs.map +1 -1
- package/dist/agent-runtime/src/send-recovery.d.ts +19 -0
- package/dist/agent-runtime/src/send-recovery.js +37 -3
- package/dist/agent-runtime/src/send-recovery.js.map +1 -1
- package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +14 -8
- package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +227 -5
- package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
- package/dist/agent-runtime/src/use-conversation-list.cjs +3 -2
- package/dist/agent-runtime/src/use-conversation-list.cjs.map +1 -1
- package/dist/agent-runtime/src/use-conversation-list.d.ts +6 -0
- package/dist/agent-runtime/src/use-conversation-list.js +3 -2
- package/dist/agent-runtime/src/use-conversation-list.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +21 -1
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +8 -2
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +21 -1
- package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +24 -6
- package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +17 -0
- package/dist/agent-runtime/src/use-deep-agent-runtime.js +24 -6
- package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs +44 -9
- package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +34 -1
- package/dist/agent-runtime/src/use-deep-agent-voice.js +44 -9
- package/dist/agent-runtime/src/use-deep-agent-voice.js.map +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 +13 -2
- package/dist/agent-runtime/src/voice-earcon.cjs +139 -0
- package/dist/agent-runtime/src/voice-earcon.cjs.map +1 -0
- package/dist/agent-runtime/src/voice-earcon.d.ts +38 -0
- package/dist/agent-runtime/src/voice-earcon.js +139 -0
- package/dist/agent-runtime/src/voice-earcon.js.map +1 -0
- package/dist/agent-runtime/src/voice-media.cjs.map +1 -1
- package/dist/agent-runtime/src/voice-media.d.ts +14 -0
- package/dist/agent-runtime/src/voice-media.js.map +1 -1
- package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
- package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
- package/dist/agent-runtime/src/voice-presentation.js +2 -0
- package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
- package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
- package/dist/assets/AthenaAssetEmbed.d.ts +11 -5
- package/dist/assets/AthenaAssetEmbed.js.map +1 -1
- package/dist/assets/use-asset-embed.cjs.map +1 -1
- package/dist/assets/use-asset-embed.d.ts +12 -5
- package/dist/assets/use-asset-embed.js.map +1 -1
- 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/StatewireApprovalCard.cjs +41 -6
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +18 -1
- package/dist/chat/StatewireApprovalCard.js +42 -7
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/StatewireQueuedMessages.cjs +56 -6
- package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
- package/dist/chat/StatewireQueuedMessages.d.ts +14 -0
- package/dist/chat/StatewireQueuedMessages.js +58 -8
- package/dist/chat/StatewireQueuedMessages.js.map +1 -1
- package/dist/chat/WaitingOnYouBanner.cjs +6 -2
- package/dist/chat/WaitingOnYouBanner.cjs.map +1 -1
- package/dist/chat/WaitingOnYouBanner.js +7 -3
- package/dist/chat/WaitingOnYouBanner.js.map +1 -1
- package/dist/chat/chat-voice-controls.cjs +3 -1
- package/dist/chat/chat-voice-controls.cjs.map +1 -1
- package/dist/chat/chat-voice-controls.d.ts +6 -1
- package/dist/chat/chat-voice-controls.js +3 -1
- package/dist/chat/chat-voice-controls.js.map +1 -1
- 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 +11 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +3 -1
- package/dist/chat-ui/index.js +12 -1
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/messages/from-the-call-card.cjs +124 -0
- package/dist/chat-ui/messages/from-the-call-card.cjs.map +1 -0
- package/dist/chat-ui/messages/from-the-call-card.d.ts +17 -0
- package/dist/chat-ui/messages/from-the-call-card.js +124 -0
- package/dist/chat-ui/messages/from-the-call-card.js.map +1 -0
- package/dist/chat-ui/rich-text/chat-link.cjs +1 -0
- package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-link.d.ts +4 -0
- package/dist/chat-ui/rich-text/chat-link.js +4 -3
- package/dist/chat-ui/rich-text/chat-link.js.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.cjs +1 -0
- package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
- package/dist/chat-ui/rich-text/chat-message-body.d.ts +12 -0
- package/dist/chat-ui/rich-text/chat-message-body.js +2 -1
- package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
- package/dist/chat-ui/thread/chat-thread-shell.cjs +4 -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 +4 -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/collab/client.cjs +15 -3
- package/dist/collab/client.cjs.map +1 -1
- package/dist/collab/client.d.ts +4 -2
- package/dist/collab/client.js +16 -4
- package/dist/collab/client.js.map +1 -1
- package/dist/composer/index.d.ts +1 -1
- package/dist/composer/useReadComposerDraft.cjs +26 -7
- package/dist/composer/useReadComposerDraft.cjs.map +1 -1
- package/dist/composer/useReadComposerDraft.d.ts +23 -1
- package/dist/composer/useReadComposerDraft.js +27 -8
- package/dist/composer/useReadComposerDraft.js.map +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +207 -0
- package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -0
- package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +141 -0
- package/dist/keryx-http/src/unconfirmed-update-attributions.js +207 -0
- package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -0
- package/dist/keryx-http/src/update-attributions.cjs +34 -4
- package/dist/keryx-http/src/update-attributions.cjs.map +1 -1
- package/dist/keryx-http/src/update-attributions.d.ts +36 -4
- package/dist/keryx-http/src/update-attributions.js +35 -5
- package/dist/keryx-http/src/update-attributions.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +4 -0
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +11 -1
- package/dist/provider/AthenaProvider.js +4 -0
- package/dist/provider/AthenaProvider.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/threads/api.cjs.map +1 -1
- package/dist/threads/api.d.ts +15 -0
- package/dist/threads/api.js.map +1 -1
- package/dist/threads/conversation-list-fetcher.cjs +13 -5
- package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
- package/dist/threads/conversation-list-fetcher.d.ts +4 -1
- package/dist/threads/conversation-list-fetcher.js +13 -5
- package/dist/threads/conversation-list-fetcher.js.map +1 -1
- package/dist/threads/statewire-thread-list.cjs +28 -6
- package/dist/threads/statewire-thread-list.cjs.map +1 -1
- package/dist/threads/statewire-thread-list.d.ts +20 -8
- package/dist/threads/statewire-thread-list.js +28 -6
- package/dist/threads/statewire-thread-list.js.map +1 -1
- package/dist/threads/use-athena-sessions.cjs +27 -5
- package/dist/threads/use-athena-sessions.cjs.map +1 -1
- package/dist/threads/use-athena-sessions.d.ts +10 -5
- package/dist/threads/use-athena-sessions.js +28 -6
- package/dist/threads/use-athena-sessions.js.map +1 -1
- package/dist/threads/use-rest-conversation-history.d.ts +1 -0
- 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
|
@@ -1,6 +1,28 @@
|
|
|
1
|
+
import { type ComposerHandoffStore } from './composer-handoff';
|
|
2
|
+
/**
|
|
3
|
+
* The composer's text, or `null` when there is no draft to read. Inside `AthenaProvider` (a
|
|
4
|
+
* `store`), the runtime's text is the person's draft only while the editor is mounted (it
|
|
5
|
+
* registers `focusEnd`) and no write made before it mounted is still waiting: until then the
|
|
6
|
+
* runtime holds an empty text that is not what the person sees. A read that throws (no composer
|
|
7
|
+
* in scope) is `null` too.
|
|
8
|
+
*/
|
|
9
|
+
export declare function tryReadComposerText({ read, store, }: {
|
|
10
|
+
read: () => {
|
|
11
|
+
text?: string | null;
|
|
12
|
+
};
|
|
13
|
+
store: Pick<ComposerHandoffStore, 'focusEnd' | 'pending'> | null;
|
|
14
|
+
}): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Reads the composer's draft (its text) when called, or `null` when there is no draft to read:
|
|
17
|
+
* the editor has not mounted, a write is still waiting for it, or the read fails. For a host that
|
|
18
|
+
* must tell a blank composer (`''`) from one it could not read, such as "Continue on…", where a
|
|
19
|
+
* blank composer clears the draft of a reused handover ticket and an unread one must leave it as
|
|
20
|
+
* it was.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useTryReadComposerDraft(): () => string | null;
|
|
1
23
|
/**
|
|
2
24
|
* Reads the composer's draft (its text) when called, for a host that hands the chat elsewhere and
|
|
3
25
|
* carries what was not sent yet along (a launcher window's "open in the app"). An empty string
|
|
4
|
-
* before the composer exists.
|
|
26
|
+
* before the composer exists; {@link useTryReadComposerDraft} tells that case apart.
|
|
5
27
|
*/
|
|
6
28
|
export declare function useReadComposerDraft(): () => string;
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { useAui } from "@assistant-ui/react";
|
|
3
|
+
import { useComposerHandoffStore } from "./composer-handoff.js";
|
|
4
|
+
function tryReadComposerText({
|
|
5
|
+
read,
|
|
6
|
+
store
|
|
7
|
+
}) {
|
|
8
|
+
if (store !== null && (store.focusEnd === null || store.pending.length > 0)) return null;
|
|
9
|
+
try {
|
|
10
|
+
return read().text ?? null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function useTryReadComposerDraft() {
|
|
16
|
+
const aui = useAui();
|
|
17
|
+
const store = useComposerHandoffStore();
|
|
18
|
+
return useCallback(
|
|
19
|
+
() => tryReadComposerText({ read: () => aui.composer.getState(), store }),
|
|
20
|
+
[aui, store]
|
|
21
|
+
);
|
|
22
|
+
}
|
|
3
23
|
function useReadComposerDraft() {
|
|
4
24
|
const aui = useAui();
|
|
5
|
-
return useCallback(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return "";
|
|
10
|
-
}
|
|
11
|
-
}, [aui]);
|
|
25
|
+
return useCallback(
|
|
26
|
+
() => tryReadComposerText({ read: () => aui.composer.getState(), store: null }) ?? "",
|
|
27
|
+
[aui]
|
|
28
|
+
);
|
|
12
29
|
}
|
|
13
30
|
export {
|
|
14
|
-
|
|
31
|
+
tryReadComposerText,
|
|
32
|
+
useReadComposerDraft,
|
|
33
|
+
useTryReadComposerDraft
|
|
15
34
|
};
|
|
16
35
|
//# sourceMappingURL=useReadComposerDraft.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReadComposerDraft.js","sources":["../../src/composer/useReadComposerDraft.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useAui } from '@assistant-ui/react';\n\n/**\n * Reads the composer's draft (its text) when called, for a host that hands the chat elsewhere and\n * carries what was not sent yet along (a launcher window's \"open in the app\"). An empty string\n * before the composer exists.\n */\nexport function useReadComposerDraft(): () => string {\n const aui = useAui();\n return useCallback(() => {
|
|
1
|
+
{"version":3,"file":"useReadComposerDraft.js","sources":["../../src/composer/useReadComposerDraft.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useAui } from '@assistant-ui/react';\nimport { type ComposerHandoffStore, useComposerHandoffStore } from './composer-handoff';\n\n/**\n * The composer's text, or `null` when there is no draft to read. Inside `AthenaProvider` (a\n * `store`), the runtime's text is the person's draft only while the editor is mounted (it\n * registers `focusEnd`) and no write made before it mounted is still waiting: until then the\n * runtime holds an empty text that is not what the person sees. A read that throws (no composer\n * in scope) is `null` too.\n */\nexport function tryReadComposerText({\n read,\n store,\n}: {\n read: () => { text?: string | null };\n store: Pick<ComposerHandoffStore, 'focusEnd' | 'pending'> | null;\n}): string | null {\n if (store !== null && (store.focusEnd === null || store.pending.length > 0)) return null;\n try {\n return read().text ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Reads the composer's draft (its text) when called, or `null` when there is no draft to read:\n * the editor has not mounted, a write is still waiting for it, or the read fails. For a host that\n * must tell a blank composer (`''`) from one it could not read, such as \"Continue on…\", where a\n * blank composer clears the draft of a reused handover ticket and an unread one must leave it as\n * it was.\n */\nexport function useTryReadComposerDraft(): () => string | null {\n const aui = useAui();\n const store = useComposerHandoffStore();\n return useCallback(\n () => tryReadComposerText({ read: () => aui.composer.getState(), store }),\n [aui, store]\n );\n}\n\n/**\n * Reads the composer's draft (its text) when called, for a host that hands the chat elsewhere and\n * carries what was not sent yet along (a launcher window's \"open in the app\"). An empty string\n * before the composer exists; {@link useTryReadComposerDraft} tells that case apart.\n */\nexport function useReadComposerDraft(): () => string {\n const aui = useAui();\n return useCallback(\n () => tryReadComposerText({ read: () => aui.composer.getState(), store: null }) ?? '',\n [aui]\n );\n}\n"],"names":[],"mappings":";;;AAWO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGkB;AAChB,MAAI,UAAU,SAAS,MAAM,aAAa,QAAQ,MAAM,QAAQ,SAAS,GAAI,QAAO;AACpF,MAAI;AACF,WAAO,KAAA,EAAO,QAAQ;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASO,SAAS,0BAA+C;AAC7D,QAAM,MAAM,OAAA;AACZ,QAAM,QAAQ,wBAAA;AACd,SAAO;AAAA,IACL,MAAM,oBAAoB,EAAE,MAAM,MAAM,IAAI,SAAS,SAAA,GAAY,OAAO;AAAA,IACxE,CAAC,KAAK,KAAK;AAAA,EAAA;AAEf;AAOO,SAAS,uBAAqC;AACnD,QAAM,MAAM,OAAA;AACZ,SAAO;AAAA,IACL,MAAM,oBAAoB,EAAE,MAAM,MAAM,IAAI,SAAS,SAAA,GAAY,OAAO,KAAA,CAAM,KAAK;AAAA,IACnF,CAAC,GAAG;AAAA,EAAA;AAER;"}
|
package/dist/index.cjs
CHANGED
|
@@ -57,6 +57,7 @@ const miscToolUis = require("./tools/misc-tool-uis.cjs");
|
|
|
57
57
|
const toolIcons = require("./tools/tool-icons.cjs");
|
|
58
58
|
const writeTodosToolUi = require("./tools/write-todos-tool-ui.cjs");
|
|
59
59
|
const todoPlanStore = require("./tools/todo-plan-store.cjs");
|
|
60
|
+
const AthenaPlanStrip = require("./chat/AthenaPlanStrip.cjs");
|
|
60
61
|
const fileToolUis = require("./tools/file-tool-uis.cjs");
|
|
61
62
|
const toolSummary = require("./tools/tool-summary.cjs");
|
|
62
63
|
const toolFallback = require("./tools/tool-fallback.cjs");
|
|
@@ -109,6 +110,7 @@ const dictationEngine = require("./dictation/dictation-engine.cjs");
|
|
|
109
110
|
const recordedDictationEngine = require("./dictation/recorded-dictation-engine.cjs");
|
|
110
111
|
const useAthenaDictationEngine = require("./dictation/use-athena-dictation-engine.cjs");
|
|
111
112
|
const dictationState = require("./dictation/dictation-state.cjs");
|
|
113
|
+
const useChatVoiceEnabled = require("./voice/use-chat-voice-enabled.cjs");
|
|
112
114
|
const useAppendToComposer = require("./composer/useAppendToComposer.cjs");
|
|
113
115
|
const composerHandoff = require("./composer/composer-handoff.cjs");
|
|
114
116
|
const useReadComposerDraft = require("./composer/useReadComposerDraft.cjs");
|
|
@@ -296,6 +298,7 @@ exports.PlanStepMarker = writeTodosToolUi.PlanStepMarker;
|
|
|
296
298
|
exports.WriteTodosToolUI = writeTodosToolUi.WriteTodosToolUI;
|
|
297
299
|
exports.parseTodoPlan = writeTodosToolUi.parseTodoPlan;
|
|
298
300
|
exports.useLatestTodoPlan = todoPlanStore.useLatestTodoPlan;
|
|
301
|
+
exports.AthenaPlanStrip = AthenaPlanStrip.AthenaPlanStrip;
|
|
299
302
|
exports.EditFileToolUI = fileToolUis.EditFileToolUI;
|
|
300
303
|
exports.ListFilesToolUI = fileToolUis.ListFilesToolUI;
|
|
301
304
|
exports.ReadFileToolUI = fileToolUis.ReadFileToolUI;
|
|
@@ -477,10 +480,12 @@ exports.createRecordedDictationEngine = recordedDictationEngine.createRecordedDi
|
|
|
477
480
|
exports.useAthenaDictationEngine = useAthenaDictationEngine.useAthenaDictationEngine;
|
|
478
481
|
exports.DICTATION_STATUSES = dictationState.DICTATION_STATUSES;
|
|
479
482
|
exports.composeDictationDraft = dictationState.composeDictationDraft;
|
|
483
|
+
exports.useChatVoiceEnabled = useChatVoiceEnabled.useChatVoiceEnabled;
|
|
480
484
|
exports.useAppendToComposer = useAppendToComposer.useAppendToComposer;
|
|
481
485
|
exports.useComposerReady = composerHandoff.useComposerReady;
|
|
482
486
|
exports.useFocusComposer = composerHandoff.useFocusComposer;
|
|
483
487
|
exports.useReadComposerDraft = useReadComposerDraft.useReadComposerDraft;
|
|
488
|
+
exports.useTryReadComposerDraft = useReadComposerDraft.useTryReadComposerDraft;
|
|
484
489
|
exports.useSendMessage = useSendMessage.useSendMessage;
|
|
485
490
|
exports.useComposerAttachment = useComposerAttachment.useComposerAttachment;
|
|
486
491
|
exports.ChatAgentPicker = chatPickers.ChatAgentPicker;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { AthenaAssistantActionBar, AthenaAssistantMessage, AthenaChat, AthenaRea
|
|
|
20
20
|
export type { AthenaAssistantActionBarProps, AthenaAssistantMessageProps, AthenaChatComponents, AthenaChatMessagePartComponents, AthenaChatProps, AthenaChatVoiceOptions, AthenaReasoningPartProps, AthenaUserMessageProps, } from './chat/AthenaChat';
|
|
21
21
|
export type { WelcomeSuggestion } from './chat/athena-chat-welcome';
|
|
22
22
|
export { AthenaChatErrorBoundary } from './chat/AthenaChatErrorBoundary';
|
|
23
|
-
export { type ActivityItem, type ActivityPhase, type ActivityTimelineOptions, type ActivityToolState, buildActivityTimeline, CollapsibleGroup, type CollapsibleGroupProps, countActivityDelegations, countActivityTools, createToolBasedGroupingFunction, DEFAULT_TOOLKIT_THEME, defaultGetToolGroupKey, excludeFinalTextFromGroups, excludePartsFromGroups, type FinalTextPart, type FinalTextSegment, formatWorkDuration, getActivityItemLabel, getActivitySummary, getActivityToolState, getFinalText, getFinalTextParts, getReasoningLabel, type GetToolGroupKey, type GroupablePart, GroupedMessageParts, type GroupedMessagePartsProps, type GroupingFunction, type MessagePartGroup, readTurnTiming, resetRememberedExpansionsForTests, resolveActivityPhase, splitTurnSegments, SuperGroupingCard, type SuperGroupingCardProps, SuperGroupingFinalText, SuperGroupingProvider, SuperGroupingTranscript, type SuperGroupingTranscriptProps, TOOLKIT_THEMES, type ToolGroupKeyOptions, type ToolkitTheme, type ToolPartPlacement, ToolPartPlacementProvider, type TurnSegment, type TurnTiming, useActivityExpansion, useToolPartPlacement, workedDurationMs, } from './chat/super-grouping';
|
|
23
|
+
export { type ActivityItem, type ActivityPhase, type ActivityTimelineOptions, type ActivityToolState, buildActivityTimeline, CollapsibleGroup, type CollapsibleGroupProps, countActivityDelegations, countActivityTools, createToolBasedGroupingFunction, DEFAULT_TOOLKIT_THEME, defaultGetToolGroupKey, excludeFinalTextFromGroups, excludePartsFromGroups, type FinalTextPart, type FinalTextSegment, formatWorkDuration, type ActivitySummaryOptions, getActivityItemLabel, getActivitySummary, getActivityToolState, getFinalText, getFinalTextParts, getReasoningLabel, type GetToolGroupKey, type GroupablePart, GroupedMessageParts, type GroupedMessagePartsProps, type GroupingFunction, type MessagePartGroup, readTurnTiming, resetRememberedExpansionsForTests, resolveActivityPhase, splitTurnSegments, SuperGroupingCard, type SuperGroupingCardProps, SuperGroupingFinalText, SuperGroupingProvider, SuperGroupingTranscript, type SuperGroupingTranscriptProps, TOOLKIT_THEMES, type ToolGroupKeyOptions, type ToolkitTheme, type ToolPartPlacement, ToolPartPlacementProvider, type TurnSegment, type TurnTiming, useActivityExpansion, useToolPartPlacement, workedDurationMs, } from './chat/super-grouping';
|
|
24
24
|
export { StatewireApprovalCard } from './chat/StatewireApprovalCard';
|
|
25
25
|
export type { StatewireApprovalCardProps, StatewireApprovalEditorContext, StatewireApprovalRequestContext, StatewireDecisionAnswer, StatewireDecisionConfirmer, } from './chat/StatewireApprovalCard';
|
|
26
26
|
export { useStatewireApprovalRequest } from './chat/use-statewire-approval-request';
|
|
@@ -85,6 +85,7 @@ export { classifyToolResult, describeToolArgs, getToolDisplayMeta, isToolResultS
|
|
|
85
85
|
export type { ToolDisplayMeta, ToolIconKey, ToolResultClassification, } from './agent-runtime/src/index';
|
|
86
86
|
export { HiddenWriteTodosToolUI, PlanStepMarker, WriteTodosToolUI, parseTodoPlan, } from './tools/write-todos-tool-ui';
|
|
87
87
|
export { useLatestTodoPlan } from './tools/todo-plan-store';
|
|
88
|
+
export { AthenaPlanStrip, type AthenaPlanStripProps } from './chat/AthenaPlanStrip';
|
|
88
89
|
export type { LatestTodoPlan } from './tools/todo-plan-store';
|
|
89
90
|
export { EditFileToolUI, friendlyWorkspacePath, ListFilesToolUI, ReadFileToolUI, SearchFilesToolUI, WriteFileToolUI, } from './tools/file-tool-uis';
|
|
90
91
|
export { collectAssetTitles, humanizeToolName, summarizeToolCall, type SummarizeToolCallInput, type ToolCallSummary, type ToolSummaryState, } from './tools/tool-summary';
|
|
@@ -189,10 +190,12 @@ export { useAthenaDictationEngine } from './dictation/use-athena-dictation-engin
|
|
|
189
190
|
export { composeDictationDraft, DICTATION_STATUSES } from './dictation/dictation-state';
|
|
190
191
|
export type { DictationStatus, DictationTranscript } from './dictation/dictation-state';
|
|
191
192
|
export type { AthenaChatComposerSlots, AthenaChatContinueByVoice, AthenaChatDictationOptions, } from './chat/AthenaChat';
|
|
193
|
+
/** Whether the chat's voice control shows for this sign-in: the gate `AthenaChat`'s mic uses. */
|
|
194
|
+
export { useChatVoiceEnabled } from './voice/use-chat-voice-enabled';
|
|
192
195
|
export { useAppendToComposer } from './composer/useAppendToComposer';
|
|
193
196
|
export type { AppendToComposerOptions } from './composer/useAppendToComposer';
|
|
194
197
|
export { useComposerReady, useFocusComposer } from './composer/composer-handoff';
|
|
195
|
-
export { useReadComposerDraft } from './composer/useReadComposerDraft';
|
|
198
|
+
export { useReadComposerDraft, useTryReadComposerDraft } from './composer/useReadComposerDraft';
|
|
196
199
|
export { useSendMessage } from './composer/useSendMessage';
|
|
197
200
|
export type { SendMessageOptions } from './composer/useSendMessage';
|
|
198
201
|
export { useComposerAttachment } from './composer/useComposerAttachment';
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ import { InstallPackageToolUI } from "./tools/misc-tool-uis.js";
|
|
|
55
55
|
import { TOOL_ICONS, toolIcon } from "./tools/tool-icons.js";
|
|
56
56
|
import { HiddenWriteTodosToolUI, PlanStepMarker, WriteTodosToolUI, parseTodoPlan } from "./tools/write-todos-tool-ui.js";
|
|
57
57
|
import { useLatestTodoPlan } from "./tools/todo-plan-store.js";
|
|
58
|
+
import { AthenaPlanStrip } from "./chat/AthenaPlanStrip.js";
|
|
58
59
|
import { EditFileToolUI, ListFilesToolUI, ReadFileToolUI, SearchFilesToolUI, WriteFileToolUI } from "./tools/file-tool-uis.js";
|
|
59
60
|
import { collectAssetTitles, friendlyWorkspacePath, humanizeToolName, summarizeToolCall } from "./tools/tool-summary.js";
|
|
60
61
|
import { ToolFallback, ToolFallbackAppearanceProvider, ToolFallbackArgs, ToolFallbackContent, ToolFallbackError, ToolFallbackNotice, ToolFallbackResult, ToolFallbackRoot, ToolFallbackTrigger, clearAutoOpenedAssets } from "./tools/tool-fallback.js";
|
|
@@ -107,9 +108,10 @@ import { DICTATION_ENGINE_ERROR_KINDS, createWebSpeechDictationEngine } from "./
|
|
|
107
108
|
import { createRecordedDictationEngine } from "./dictation/recorded-dictation-engine.js";
|
|
108
109
|
import { useAthenaDictationEngine } from "./dictation/use-athena-dictation-engine.js";
|
|
109
110
|
import { DICTATION_STATUSES, composeDictationDraft } from "./dictation/dictation-state.js";
|
|
111
|
+
import { useChatVoiceEnabled } from "./voice/use-chat-voice-enabled.js";
|
|
110
112
|
import { useAppendToComposer } from "./composer/useAppendToComposer.js";
|
|
111
113
|
import { useComposerReady, useFocusComposer } from "./composer/composer-handoff.js";
|
|
112
|
-
import { useReadComposerDraft } from "./composer/useReadComposerDraft.js";
|
|
114
|
+
import { useReadComposerDraft, useTryReadComposerDraft } from "./composer/useReadComposerDraft.js";
|
|
113
115
|
import { useSendMessage } from "./composer/useSendMessage.js";
|
|
114
116
|
import { useComposerAttachment } from "./composer/useComposerAttachment.js";
|
|
115
117
|
import { ChatAgentPicker, ChatModePicker, ChatModelPicker } from "./catalog/chat-pickers.js";
|
|
@@ -157,6 +159,7 @@ export {
|
|
|
157
159
|
AthenaChat,
|
|
158
160
|
AthenaChatErrorBoundary,
|
|
159
161
|
AthenaLayout,
|
|
162
|
+
AthenaPlanStrip,
|
|
160
163
|
AthenaProvider,
|
|
161
164
|
AthenaReasoningPart,
|
|
162
165
|
AthenaSdkError,
|
|
@@ -538,6 +541,7 @@ export {
|
|
|
538
541
|
useAthenaVoiceLive,
|
|
539
542
|
useAttachments,
|
|
540
543
|
useBrowserDecisionConfirmer,
|
|
544
|
+
useChatVoiceEnabled,
|
|
541
545
|
useComposerAttachment,
|
|
542
546
|
useComposerReady,
|
|
543
547
|
useDeviceOperationReader,
|
|
@@ -566,6 +570,7 @@ export {
|
|
|
566
570
|
useStatewireToolOutputs,
|
|
567
571
|
useToolAppearance,
|
|
568
572
|
useToolPartPlacement,
|
|
573
|
+
useTryReadComposerDraft,
|
|
569
574
|
useTurnToolCallsAwaitingApproval,
|
|
570
575
|
windowInFrontSignal,
|
|
571
576
|
withCaptureGateBypass,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
6
|
+
const Y = require("@y/y");
|
|
7
|
+
const updateAttributions = require("./update-attributions.cjs");
|
|
8
|
+
const MESSAGE_SYNC = 0;
|
|
9
|
+
const SYNC_STEP_1 = 0;
|
|
10
|
+
const SYNC_STEP_2 = 1;
|
|
11
|
+
const SYNC_UPDATE = 2;
|
|
12
|
+
const MAX_ATTRIBUTED_RUNS = 64;
|
|
13
|
+
function noIds(ids) {
|
|
14
|
+
return ids.inserts.isEmpty() && ids.deletes.isEmpty();
|
|
15
|
+
}
|
|
16
|
+
function sameAttributions(a, b) {
|
|
17
|
+
return a.length === b.length && a.every((pair, index) => {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
return pair.k === ((_a = b[index]) == null ? void 0 : _a.k) && pair.v === ((_b = b[index]) == null ? void 0 : _b.v);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function readSyncFrame(frame) {
|
|
23
|
+
const type = updateAttributions.readVarUint({ bytes: frame, start: 0 });
|
|
24
|
+
if ((type == null ? void 0 : type.value) !== MESSAGE_SYNC) return null;
|
|
25
|
+
const syncType = updateAttributions.readVarUint({ bytes: frame, start: type.next });
|
|
26
|
+
if (syncType === null) return null;
|
|
27
|
+
const length = updateAttributions.readVarUint({ bytes: frame, start: syncType.next });
|
|
28
|
+
if (length === null || length.next + length.value !== frame.length) return null;
|
|
29
|
+
return { syncType: syncType.value, payload: frame.subarray(length.next) };
|
|
30
|
+
}
|
|
31
|
+
function syncFrame({ syncType, payload }) {
|
|
32
|
+
const head = [];
|
|
33
|
+
updateAttributions.writeVarUint({ out: head, value: MESSAGE_SYNC });
|
|
34
|
+
updateAttributions.writeVarUint({ out: head, value: syncType });
|
|
35
|
+
updateAttributions.writeVarUint({ out: head, value: payload.length });
|
|
36
|
+
const frame = new Uint8Array(head.length + payload.length);
|
|
37
|
+
frame.set(head, 0);
|
|
38
|
+
frame.set(payload, head.length);
|
|
39
|
+
return frame;
|
|
40
|
+
}
|
|
41
|
+
function namesNoClient(stateVector) {
|
|
42
|
+
try {
|
|
43
|
+
return Y.decodeStateVector(stateVector).size === 0;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function idsOf(update) {
|
|
49
|
+
try {
|
|
50
|
+
return Y.createContentIdsFromUpdate(update);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function copyOf({ ids, attributions }) {
|
|
56
|
+
return { ids, attributions: attributions.map(({ k, v }) => ({ k, v })) };
|
|
57
|
+
}
|
|
58
|
+
function bounded(runs) {
|
|
59
|
+
return runs.length > MAX_ATTRIBUTED_RUNS ? runs.slice(runs.length - MAX_ATTRIBUTED_RUNS) : runs;
|
|
60
|
+
}
|
|
61
|
+
const ROOMS = Symbol.for("@athenaintel/keryx-http/unconfirmed-update-attributions");
|
|
62
|
+
function rooms() {
|
|
63
|
+
const host = globalThis;
|
|
64
|
+
return host[ROOMS] ?? (host[ROOMS] = { live: /* @__PURE__ */ new WeakMap(), left: /* @__PURE__ */ new WeakMap() });
|
|
65
|
+
}
|
|
66
|
+
class UnconfirmedUpdateAttributions {
|
|
67
|
+
constructor({
|
|
68
|
+
doc,
|
|
69
|
+
attributionsFor,
|
|
70
|
+
isRemoteOrigin
|
|
71
|
+
}) {
|
|
72
|
+
__publicField(this, "runs");
|
|
73
|
+
__publicField(this, "doc");
|
|
74
|
+
__publicField(this, "attributionsFor");
|
|
75
|
+
__publicField(this, "isRemoteOrigin");
|
|
76
|
+
__publicField(this, "handleUpdate", (update, origin) => {
|
|
77
|
+
if (this.isRemoteOrigin(origin)) return;
|
|
78
|
+
const attributions = this.attributionsFor();
|
|
79
|
+
if (!attributions || attributions.length === 0) return;
|
|
80
|
+
const ids = idsOf(update);
|
|
81
|
+
if (!ids || noIds(ids)) return;
|
|
82
|
+
const last = this.runs.at(-1);
|
|
83
|
+
if (last && sameAttributions(last.attributions, attributions)) {
|
|
84
|
+
last.ids = Y.mergeContentIds([last.ids, ids]);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.runs = bounded([...this.runs, copyOf({ ids, attributions })]);
|
|
88
|
+
});
|
|
89
|
+
this.doc = doc;
|
|
90
|
+
this.attributionsFor = attributionsFor;
|
|
91
|
+
this.isRemoteOrigin = isRemoteOrigin;
|
|
92
|
+
const { live, left } = rooms();
|
|
93
|
+
this.runs = left.get(doc) ?? [];
|
|
94
|
+
left.delete(doc);
|
|
95
|
+
live.set(doc, this);
|
|
96
|
+
doc.on("update", this.handleUpdate);
|
|
97
|
+
}
|
|
98
|
+
/** The attributed writes Keryx has not acknowledged, oldest first. */
|
|
99
|
+
get pending() {
|
|
100
|
+
return this.runs.map(copyOf);
|
|
101
|
+
}
|
|
102
|
+
/** Writes an earlier session kept, older than any written here: they go out first. */
|
|
103
|
+
restore(writes) {
|
|
104
|
+
this.runs = bounded([...writes.map(copyOf), ...this.runs]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* One socket of the room. While the room keeps writes, the socket's sync step 2 (its answer to
|
|
108
|
+
* Keryx's sync step 1) waits for the next frame Keryx sends, its whole document, which drops what
|
|
109
|
+
* Keryx already holds before the answer is split (see the module comment); every frame sent
|
|
110
|
+
* meanwhile waits behind it, in order. When Keryx's sync step 1 names no client, the room is new
|
|
111
|
+
* to Keryx: its answer goes out whole, born as one write, and what the room kept waits for its
|
|
112
|
+
* echo. Nothing kept, or a socket that cannot hear Keryx: every frame goes out as it is sent.
|
|
113
|
+
*/
|
|
114
|
+
connect({
|
|
115
|
+
send,
|
|
116
|
+
hears
|
|
117
|
+
}) {
|
|
118
|
+
let held = null;
|
|
119
|
+
let roomIsNew = false;
|
|
120
|
+
const sendNow = (frame) => {
|
|
121
|
+
for (const out of this.framesFor(frame)) send(out);
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
received: (frame) => {
|
|
125
|
+
const message = readSyncFrame(frame);
|
|
126
|
+
if ((message == null ? void 0 : message.syncType) === SYNC_STEP_1) roomIsNew = namesNoClient(message.payload);
|
|
127
|
+
this.received(frame);
|
|
128
|
+
if (held === null) return;
|
|
129
|
+
const waiting = held;
|
|
130
|
+
held = null;
|
|
131
|
+
for (const next of waiting) sendNow(next);
|
|
132
|
+
},
|
|
133
|
+
send: (frame) => {
|
|
134
|
+
var _a;
|
|
135
|
+
if (held !== null) {
|
|
136
|
+
held.push(frame);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const syncType = (_a = readSyncFrame(frame)) == null ? void 0 : _a.syncType;
|
|
140
|
+
if (roomIsNew && syncType === SYNC_STEP_2) {
|
|
141
|
+
send(frame);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (hears && this.runs.length > 0 && syncType === SYNC_STEP_2) {
|
|
145
|
+
held = [frame];
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
sendNow(frame);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** Keryx sent `frame`: the ids it carries are Keryx's, kept no longer. */
|
|
153
|
+
received(frame) {
|
|
154
|
+
if (this.runs.length === 0) return;
|
|
155
|
+
const message = readSyncFrame(frame);
|
|
156
|
+
if (message === null) return;
|
|
157
|
+
if (message.syncType !== SYNC_STEP_2 && message.syncType !== SYNC_UPDATE) return;
|
|
158
|
+
const held = idsOf(message.payload);
|
|
159
|
+
if (!held) return;
|
|
160
|
+
this.runs = this.runs.map(({ ids, attributions }) => ({ ids: Y.excludeContentIds(ids, held), attributions })).filter((run) => !noIds(run.ids));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* What goes out in place of `frame`: `[frame]` itself, except a sync step 2 carrying kept writes,
|
|
164
|
+
* which goes out behind each of them as its own marked sync update.
|
|
165
|
+
*/
|
|
166
|
+
framesFor(frame) {
|
|
167
|
+
if (this.runs.length === 0) return [frame];
|
|
168
|
+
const message = readSyncFrame(frame);
|
|
169
|
+
if (message === null || message.syncType !== SYNC_STEP_2) return [frame];
|
|
170
|
+
const answer = message.payload;
|
|
171
|
+
const all = idsOf(answer);
|
|
172
|
+
if (!all) return [frame];
|
|
173
|
+
const frames = [];
|
|
174
|
+
const marked = [];
|
|
175
|
+
for (const run of this.runs) {
|
|
176
|
+
const part = Y.intersectContentIds(run.ids, all);
|
|
177
|
+
if (noIds(part)) continue;
|
|
178
|
+
frames.push(
|
|
179
|
+
updateAttributions.withUpdateAttributionsTrailer({
|
|
180
|
+
message: syncFrame({
|
|
181
|
+
syncType: SYNC_UPDATE,
|
|
182
|
+
payload: Y.intersectUpdateWithContentIds(answer, part)
|
|
183
|
+
}),
|
|
184
|
+
attributions: run.attributions
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
marked.push(part);
|
|
188
|
+
}
|
|
189
|
+
if (marked.length === 0) return [frame];
|
|
190
|
+
const rest = Y.excludeContentIds(all, Y.mergeContentIds(marked));
|
|
191
|
+
frames.push(
|
|
192
|
+
syncFrame({ syncType: SYNC_STEP_2, payload: Y.intersectUpdateWithContentIds(answer, rest) })
|
|
193
|
+
);
|
|
194
|
+
return frames;
|
|
195
|
+
}
|
|
196
|
+
/** Stops keeping; what is still kept waits for the next tracker made for the document. */
|
|
197
|
+
destroy() {
|
|
198
|
+
this.doc.off("update", this.handleUpdate);
|
|
199
|
+
const { live, left } = rooms();
|
|
200
|
+
if (live.get(this.doc) === this) live.delete(this.doc);
|
|
201
|
+
if (this.runs.length > 0) left.set(this.doc, this.runs);
|
|
202
|
+
this.runs = [];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.MAX_ATTRIBUTED_RUNS = MAX_ATTRIBUTED_RUNS;
|
|
206
|
+
exports.UnconfirmedUpdateAttributions = UnconfirmedUpdateAttributions;
|
|
207
|
+
//# sourceMappingURL=unconfirmed-update-attributions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unconfirmed-update-attributions.cjs","sources":["../../../../keryx-http/src/unconfirmed-update-attributions.ts"],"sourcesContent":["/**\n * The attributed writes a room has not seen Keryx acknowledge, kept so that a write made while the\n * room's socket is closed still reaches Keryx with its attributions.\n *\n * A write made inside `runWithDocUpdateAttributions` leaves as its own sync update followed by the\n * attributions trailer (`attributingWebSocket`), but only if the room's socket is open as it is\n * made. One made while it is closed (offline, reconnecting, before the first connection) reaches\n * Keryx inside the reconnect's sync step 2: the client's answer to Keryx's sync step 1, everything\n * it holds that Keryx lacks. Keryx reads no trailer after a sync step 2, so it would count the\n * write as the person's edit.\n *\n * So the room keeps the content ids of every write made in a scope, with the scope's attributions,\n * until Keryx sends those ids back: the echo of the update that carried them, or the whole document\n * Keryx sends when a socket opens (`received`; `@y/websocket` confirms its own unconfirmed updates\n * the same way). When the socket sends a sync step 2, each kept write's part of it goes out first,\n * oldest first, as its own sync update with its trailer, and the sync step 2 follows without those\n * ids (`framesFor`). The parts are cut from the sync step 2 itself, which already holds only what\n * Keryx lacks past its state vector. One socket keeps the order, so Keryx appends each marked update\n * before the person's sync step 2.\n *\n * A room Keryx holds nothing of (its sync step 1 names no client: a document created on the device,\n * its first push) is born whole: assets are born as one write of their real initial state, signed by\n * their creator, and housekeeping inside a new room is part of that state, not system work on\n * anyone's content. Its sync step 2 goes out whole, as the creator's write. What the room kept stays\n * kept until Keryx echoes the push, like any write: a push lost with the socket goes out whole again\n * if Keryx still holds nothing when the room reconnects, and is split like any reconnect's if\n * someone wrote to the room meanwhile.\n *\n * That answer waits for Keryx's document (`connect`). Keryx sends its sync step 1 and, in the same\n * cork, its whole document, but `@y/websocket` answers the sync step 1 as it arrives. A kept write\n * Keryx already appended (its echo lost with the socket) is still kept then, and the sync step 2\n * holds the client's whole delete set, so its deletes would go out again as a marked update, which\n * Keryx appends: it drops what it holds from the first sync step 2 only. Held until the next frame,\n * the answer is split once Keryx's document has dropped what it holds.\n *\n * What Keryx records (`patches/@y%2Fhub@0.10.1.patch`):\n * - An update's content map attributes every id it carries, inserts and deletes, read from its\n * bytes: an update that cannot integrate yet (an item next to one the person typed offline, the\n * next clocks of the person's own client) is attributed like any other. Peers hold it until the\n * sync step 2 behind it brings what it needs.\n * - An id is attributed by the first stream entry that carries it (`getDoc`'s `excludeContentMap`),\n * so a later copy changes no merged history. The live change notices read each entry's own\n * content map, though (keryx/src/doc-watcher.ts), and the first sync step 2's strip of what Keryx\n * holds reads the known ids when the frame arrives, possibly before the update ahead of it is in\n * the stream (one whose content map the compute pool builds). A copy inside the sync step 2\n * would then be announced as the person's: the sync step 2 never carries a kept write's ids.\n *\n * Bounded: consecutive writes of one scope merge into one run, and past\n * {@link MAX_ATTRIBUTED_RUNS} runs the oldest is let go (its content then reaches Keryx in a sync\n * step 2, as the person's). A room's local copy keeps them across sessions\n * ({@link unconfirmedAttributedWritesOf}, {@link restoreUnconfirmedAttributedWrites}); without one,\n * a write the room kept is forgotten with the page.\n */\nimport {\n type ContentIds,\n createContentIdsFromUpdate,\n decodeStateVector,\n type Doc,\n excludeContentIds,\n intersectContentIds,\n intersectUpdateWithContentIds,\n mergeContentIds,\n} from '@y/y';\nimport {\n type AttributedWritesSocket,\n readVarUint,\n type UnconfirmedAttributedWrites,\n type UpdateAttribution,\n withUpdateAttributionsTrailer,\n writeVarUint,\n} from './update-attributions.js';\n\n/** y-protocols: a sync message, the sync step 1 (a state vector) and the sub-types that carry content. */\nconst MESSAGE_SYNC = 0;\nconst SYNC_STEP_1 = 0;\nconst SYNC_STEP_2 = 1;\nconst SYNC_UPDATE = 2;\n\n/** Writes made in one attribution scope, by the content ids they wrote (inserts and deletes). */\nexport interface AttributedWrites {\n ids: ContentIds;\n attributions: readonly UpdateAttribution[];\n}\n\n/**\n * The most runs a room keeps; a run is one scope's writes since a write of another scope. Housekeeping\n * all shares one scope, so runs pile up only as Quick Edit steps interleave with it.\n */\nexport const MAX_ATTRIBUTED_RUNS = 64;\n\nfunction noIds(ids: ContentIds): boolean {\n return ids.inserts.isEmpty() && ids.deletes.isEmpty();\n}\n\nfunction sameAttributions(\n a: readonly UpdateAttribution[],\n b: readonly UpdateAttribution[]\n): boolean {\n return (\n a.length === b.length &&\n a.every((pair, index) => pair.k === b[index]?.k && pair.v === b[index]?.v)\n );\n}\n\n/** The sync message `frame` is, when it is exactly one (nothing after its payload). */\nfunction readSyncFrame(frame: Uint8Array): { syncType: number; payload: Uint8Array } | null {\n const type = readVarUint({ bytes: frame, start: 0 });\n if (type?.value !== MESSAGE_SYNC) return null;\n const syncType = readVarUint({ bytes: frame, start: type.next });\n if (syncType === null) return null;\n const length = readVarUint({ bytes: frame, start: syncType.next });\n if (length === null || length.next + length.value !== frame.length) return null;\n return { syncType: syncType.value, payload: frame.subarray(length.next) };\n}\n\n/** A sync message of `syncType` carrying `payload`, as y-protocols writes one. */\nfunction syncFrame({ syncType, payload }: { syncType: number; payload: Uint8Array }): Uint8Array {\n const head: number[] = [];\n writeVarUint({ out: head, value: MESSAGE_SYNC });\n writeVarUint({ out: head, value: syncType });\n writeVarUint({ out: head, value: payload.length });\n const frame = new Uint8Array(head.length + payload.length);\n frame.set(head, 0);\n frame.set(payload, head.length);\n return frame;\n}\n\n/** Whether `stateVector` (a sync step 1's) names no client: Keryx holds nothing of the room. */\nfunction namesNoClient(stateVector: Uint8Array): boolean {\n try {\n return decodeStateVector(stateVector).size === 0;\n } catch {\n return false;\n }\n}\n\n/** The content ids an update carries, or `null` for bytes that are not one. */\nfunction idsOf(update: Uint8Array): ContentIds | null {\n try {\n return createContentIdsFromUpdate(update);\n } catch {\n return null;\n }\n}\n\nfunction copyOf({ ids, attributions }: AttributedWrites): AttributedWrites {\n return { ids, attributions: attributions.map(({ k, v }) => ({ k, v })) };\n}\n\n/** `runs` without the oldest past {@link MAX_ATTRIBUTED_RUNS}. */\nfunction bounded(runs: AttributedWrites[]): AttributedWrites[] {\n return runs.length > MAX_ATTRIBUTED_RUNS ? runs.slice(runs.length - MAX_ATTRIBUTED_RUNS) : runs;\n}\n\n/** What a page reads of a room's tracker, whichever copy of this module made it. */\ninterface KeptWrites {\n readonly pending: readonly AttributedWrites[];\n restore(writes: readonly AttributedWrites[]): void;\n}\n\n/**\n * Each document's tracker in the page, and what a destroyed one still kept for the next tracker made\n * for the same document (a transport rebuilt over a retained document; a local copy read into a\n * document before its room's transport exists). On `globalThis`, not in module scope, for the reason\n * the scopes are (`update-attributions.ts`): the SDK's dist inlines its own copy of this module, and\n * a local copy reading a room through another copy must find the same tracker. Weak: each entry goes\n * with its document.\n */\nconst ROOMS = Symbol.for('@athenaintel/keryx-http/unconfirmed-update-attributions');\n\ninterface Rooms {\n live: WeakMap<object, KeptWrites>;\n left: WeakMap<object, AttributedWrites[]>;\n}\n\ntype RoomsHost = typeof globalThis & {\n [ROOMS]?: Rooms;\n};\n\n// process-local: browser-only, each entry keyed by the document whose writes it holds\nfunction rooms(): Rooms {\n const host = globalThis as RoomsHost;\n return (host[ROOMS] ??= { live: new WeakMap(), left: new WeakMap() });\n}\n\n/**\n * The attributed writes Keryx has not acknowledged for `doc`, oldest first: its room's, or what a\n * destroyed room left for the next. What a room's local copy keeps of them.\n */\nexport function unconfirmedAttributedWritesOf(doc: object): readonly AttributedWrites[] {\n const { live, left } = rooms();\n return live.get(doc)?.pending ?? left.get(doc)?.map(copyOf) ?? [];\n}\n\n/**\n * Hands `doc`'s room attributed writes an earlier session kept (the room's local copy), or keeps\n * them for the next tracker made for it. They are older than anything written since, so they go\n * out first.\n */\nexport function restoreUnconfirmedAttributedWrites({\n doc,\n writes,\n}: {\n doc: object;\n writes: readonly AttributedWrites[];\n}): void {\n if (writes.length === 0) return;\n const { live, left } = rooms();\n const room = live.get(doc);\n if (room) {\n room.restore(writes);\n return;\n }\n left.set(doc, bounded([...writes.map(copyOf), ...(left.get(doc) ?? [])]));\n}\n\n/**\n * The two above, as a page hands them to its local copies (`@athenaintel/local-copy`'s\n * `openLocalCopyStore({ attributedWrites })`, `createLocalCopiesPage({ attributedWrites })`): the\n * copy records a room's kept writes with its edits and restores an ended session's to its room.\n */\nexport const roomAttributedWrites = {\n of: unconfirmedAttributedWritesOf,\n restore: restoreUnconfirmedAttributedWrites,\n};\n\n/**\n * One room's attributed writes Keryx has not acknowledged (see the module comment). Made once per\n * room by its transport, which hands it to every socket it opens (`attributingWebSocket`'s\n * `unconfirmed`) and destroys it with the room; a tracker made for the same document later takes\n * over what it still kept, and what its local copy kept from an earlier session\n * ({@link restoreUnconfirmedAttributedWrites}). Not for a room that only listens: its socket sends\n * nothing to split.\n */\nexport class UnconfirmedUpdateAttributions implements UnconfirmedAttributedWrites, KeptWrites {\n private runs: AttributedWrites[];\n private readonly doc: Doc;\n private readonly attributionsFor: () => readonly UpdateAttribution[] | undefined;\n private readonly isRemoteOrigin: (origin: unknown) => boolean;\n\n constructor({\n doc,\n attributionsFor,\n isRemoteOrigin,\n }: {\n doc: Doc;\n /** The attributions of the update being written now: what the socket's trailer reads. */\n attributionsFor: () => readonly UpdateAttribution[] | undefined;\n /** True for a transaction the transport applied (Keryx's state, a peer's edit). */\n isRemoteOrigin: (origin: unknown) => boolean;\n }) {\n this.doc = doc;\n this.attributionsFor = attributionsFor;\n this.isRemoteOrigin = isRemoteOrigin;\n const { live, left } = rooms();\n this.runs = left.get(doc) ?? [];\n left.delete(doc);\n live.set(doc, this);\n doc.on('update', this.handleUpdate);\n }\n\n private readonly handleUpdate = (update: Uint8Array, origin: unknown): void => {\n if (this.isRemoteOrigin(origin)) return;\n const attributions = this.attributionsFor();\n if (!attributions || attributions.length === 0) return;\n const ids = idsOf(update);\n if (!ids || noIds(ids)) return;\n const last = this.runs.at(-1);\n if (last && sameAttributions(last.attributions, attributions)) {\n last.ids = mergeContentIds([last.ids, ids]);\n return;\n }\n this.runs = bounded([...this.runs, copyOf({ ids, attributions })]);\n };\n\n /** The attributed writes Keryx has not acknowledged, oldest first. */\n get pending(): readonly AttributedWrites[] {\n return this.runs.map(copyOf);\n }\n\n /** Writes an earlier session kept, older than any written here: they go out first. */\n restore(writes: readonly AttributedWrites[]): void {\n this.runs = bounded([...writes.map(copyOf), ...this.runs]);\n }\n\n /**\n * One socket of the room. While the room keeps writes, the socket's sync step 2 (its answer to\n * Keryx's sync step 1) waits for the next frame Keryx sends, its whole document, which drops what\n * Keryx already holds before the answer is split (see the module comment); every frame sent\n * meanwhile waits behind it, in order. When Keryx's sync step 1 names no client, the room is new\n * to Keryx: its answer goes out whole, born as one write, and what the room kept waits for its\n * echo. Nothing kept, or a socket that cannot hear Keryx: every frame goes out as it is sent.\n */\n connect({\n send,\n hears,\n }: {\n send: (frame: Uint8Array) => void;\n hears: boolean;\n }): AttributedWritesSocket {\n let held: Uint8Array[] | null = null;\n /** Keryx's sync step 1 on this socket named no client: it holds nothing of the room. */\n let roomIsNew = false;\n const sendNow = (frame: Uint8Array) => {\n for (const out of this.framesFor(frame)) send(out);\n };\n return {\n received: (frame) => {\n const message = readSyncFrame(frame);\n if (message?.syncType === SYNC_STEP_1) roomIsNew = namesNoClient(message.payload);\n this.received(frame);\n if (held === null) return;\n const waiting = held;\n held = null;\n for (const next of waiting) sendNow(next);\n },\n send: (frame) => {\n if (held !== null) {\n held.push(frame);\n return;\n }\n const syncType = readSyncFrame(frame)?.syncType;\n if (roomIsNew && syncType === SYNC_STEP_2) {\n send(frame);\n return;\n }\n if (hears && this.runs.length > 0 && syncType === SYNC_STEP_2) {\n held = [frame];\n return;\n }\n sendNow(frame);\n },\n };\n }\n\n /** Keryx sent `frame`: the ids it carries are Keryx's, kept no longer. */\n received(frame: Uint8Array): void {\n if (this.runs.length === 0) return;\n const message = readSyncFrame(frame);\n if (message === null) return;\n if (message.syncType !== SYNC_STEP_2 && message.syncType !== SYNC_UPDATE) return;\n const held = idsOf(message.payload);\n if (!held) return;\n this.runs = this.runs\n .map(({ ids, attributions }) => ({ ids: excludeContentIds(ids, held), attributions }))\n .filter((run) => !noIds(run.ids));\n }\n\n /**\n * What goes out in place of `frame`: `[frame]` itself, except a sync step 2 carrying kept writes,\n * which goes out behind each of them as its own marked sync update.\n */\n framesFor(frame: Uint8Array): readonly Uint8Array[] {\n if (this.runs.length === 0) return [frame];\n const message = readSyncFrame(frame);\n if (message === null || message.syncType !== SYNC_STEP_2) return [frame];\n const answer = message.payload;\n const all = idsOf(answer);\n if (!all) return [frame];\n const frames: Uint8Array[] = [];\n const marked: ContentIds[] = [];\n for (const run of this.runs) {\n const part = intersectContentIds(run.ids, all);\n if (noIds(part)) continue;\n frames.push(\n withUpdateAttributionsTrailer({\n message: syncFrame({\n syncType: SYNC_UPDATE,\n payload: intersectUpdateWithContentIds(answer, part),\n }),\n attributions: run.attributions,\n })\n );\n marked.push(part);\n }\n if (marked.length === 0) return [frame];\n const rest = excludeContentIds(all, mergeContentIds(marked));\n frames.push(\n syncFrame({ syncType: SYNC_STEP_2, payload: intersectUpdateWithContentIds(answer, rest) })\n );\n return frames;\n }\n\n /** Stops keeping; what is still kept waits for the next tracker made for the document. */\n destroy(): void {\n this.doc.off('update', this.handleUpdate);\n const { live, left } = rooms();\n if (live.get(this.doc) === this) live.delete(this.doc);\n if (this.runs.length > 0) left.set(this.doc, this.runs);\n this.runs = [];\n }\n}\n"],"names":["readVarUint","writeVarUint","decodeStateVector","createContentIdsFromUpdate","mergeContentIds","excludeContentIds","intersectContentIds","withUpdateAttributionsTrailer","intersectUpdateWithContentIds"],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attributed writes a room has not seen Keryx acknowledge, kept so that a write made while the
|
|
3
|
+
* room's socket is closed still reaches Keryx with its attributions.
|
|
4
|
+
*
|
|
5
|
+
* A write made inside `runWithDocUpdateAttributions` leaves as its own sync update followed by the
|
|
6
|
+
* attributions trailer (`attributingWebSocket`), but only if the room's socket is open as it is
|
|
7
|
+
* made. One made while it is closed (offline, reconnecting, before the first connection) reaches
|
|
8
|
+
* Keryx inside the reconnect's sync step 2: the client's answer to Keryx's sync step 1, everything
|
|
9
|
+
* it holds that Keryx lacks. Keryx reads no trailer after a sync step 2, so it would count the
|
|
10
|
+
* write as the person's edit.
|
|
11
|
+
*
|
|
12
|
+
* So the room keeps the content ids of every write made in a scope, with the scope's attributions,
|
|
13
|
+
* until Keryx sends those ids back: the echo of the update that carried them, or the whole document
|
|
14
|
+
* Keryx sends when a socket opens (`received`; `@y/websocket` confirms its own unconfirmed updates
|
|
15
|
+
* the same way). When the socket sends a sync step 2, each kept write's part of it goes out first,
|
|
16
|
+
* oldest first, as its own sync update with its trailer, and the sync step 2 follows without those
|
|
17
|
+
* ids (`framesFor`). The parts are cut from the sync step 2 itself, which already holds only what
|
|
18
|
+
* Keryx lacks past its state vector. One socket keeps the order, so Keryx appends each marked update
|
|
19
|
+
* before the person's sync step 2.
|
|
20
|
+
*
|
|
21
|
+
* A room Keryx holds nothing of (its sync step 1 names no client: a document created on the device,
|
|
22
|
+
* its first push) is born whole: assets are born as one write of their real initial state, signed by
|
|
23
|
+
* their creator, and housekeeping inside a new room is part of that state, not system work on
|
|
24
|
+
* anyone's content. Its sync step 2 goes out whole, as the creator's write. What the room kept stays
|
|
25
|
+
* kept until Keryx echoes the push, like any write: a push lost with the socket goes out whole again
|
|
26
|
+
* if Keryx still holds nothing when the room reconnects, and is split like any reconnect's if
|
|
27
|
+
* someone wrote to the room meanwhile.
|
|
28
|
+
*
|
|
29
|
+
* That answer waits for Keryx's document (`connect`). Keryx sends its sync step 1 and, in the same
|
|
30
|
+
* cork, its whole document, but `@y/websocket` answers the sync step 1 as it arrives. A kept write
|
|
31
|
+
* Keryx already appended (its echo lost with the socket) is still kept then, and the sync step 2
|
|
32
|
+
* holds the client's whole delete set, so its deletes would go out again as a marked update, which
|
|
33
|
+
* Keryx appends: it drops what it holds from the first sync step 2 only. Held until the next frame,
|
|
34
|
+
* the answer is split once Keryx's document has dropped what it holds.
|
|
35
|
+
*
|
|
36
|
+
* What Keryx records (`patches/@y%2Fhub@0.10.1.patch`):
|
|
37
|
+
* - An update's content map attributes every id it carries, inserts and deletes, read from its
|
|
38
|
+
* bytes: an update that cannot integrate yet (an item next to one the person typed offline, the
|
|
39
|
+
* next clocks of the person's own client) is attributed like any other. Peers hold it until the
|
|
40
|
+
* sync step 2 behind it brings what it needs.
|
|
41
|
+
* - An id is attributed by the first stream entry that carries it (`getDoc`'s `excludeContentMap`),
|
|
42
|
+
* so a later copy changes no merged history. The live change notices read each entry's own
|
|
43
|
+
* content map, though (keryx/src/doc-watcher.ts), and the first sync step 2's strip of what Keryx
|
|
44
|
+
* holds reads the known ids when the frame arrives, possibly before the update ahead of it is in
|
|
45
|
+
* the stream (one whose content map the compute pool builds). A copy inside the sync step 2
|
|
46
|
+
* would then be announced as the person's: the sync step 2 never carries a kept write's ids.
|
|
47
|
+
*
|
|
48
|
+
* Bounded: consecutive writes of one scope merge into one run, and past
|
|
49
|
+
* {@link MAX_ATTRIBUTED_RUNS} runs the oldest is let go (its content then reaches Keryx in a sync
|
|
50
|
+
* step 2, as the person's). A room's local copy keeps them across sessions
|
|
51
|
+
* ({@link unconfirmedAttributedWritesOf}, {@link restoreUnconfirmedAttributedWrites}); without one,
|
|
52
|
+
* a write the room kept is forgotten with the page.
|
|
53
|
+
*/
|
|
54
|
+
import { type ContentIds, type Doc } from '@y/y';
|
|
55
|
+
import { type AttributedWritesSocket, type UnconfirmedAttributedWrites, type UpdateAttribution } from './update-attributions.js';
|
|
56
|
+
/** Writes made in one attribution scope, by the content ids they wrote (inserts and deletes). */
|
|
57
|
+
export interface AttributedWrites {
|
|
58
|
+
ids: ContentIds;
|
|
59
|
+
attributions: readonly UpdateAttribution[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The most runs a room keeps; a run is one scope's writes since a write of another scope. Housekeeping
|
|
63
|
+
* all shares one scope, so runs pile up only as Quick Edit steps interleave with it.
|
|
64
|
+
*/
|
|
65
|
+
export declare const MAX_ATTRIBUTED_RUNS = 64;
|
|
66
|
+
/** What a page reads of a room's tracker, whichever copy of this module made it. */
|
|
67
|
+
interface KeptWrites {
|
|
68
|
+
readonly pending: readonly AttributedWrites[];
|
|
69
|
+
restore(writes: readonly AttributedWrites[]): void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The attributed writes Keryx has not acknowledged for `doc`, oldest first: its room's, or what a
|
|
73
|
+
* destroyed room left for the next. What a room's local copy keeps of them.
|
|
74
|
+
*/
|
|
75
|
+
export declare function unconfirmedAttributedWritesOf(doc: object): readonly AttributedWrites[];
|
|
76
|
+
/**
|
|
77
|
+
* Hands `doc`'s room attributed writes an earlier session kept (the room's local copy), or keeps
|
|
78
|
+
* them for the next tracker made for it. They are older than anything written since, so they go
|
|
79
|
+
* out first.
|
|
80
|
+
*/
|
|
81
|
+
export declare function restoreUnconfirmedAttributedWrites({ doc, writes, }: {
|
|
82
|
+
doc: object;
|
|
83
|
+
writes: readonly AttributedWrites[];
|
|
84
|
+
}): void;
|
|
85
|
+
/**
|
|
86
|
+
* The two above, as a page hands them to its local copies (`@athenaintel/local-copy`'s
|
|
87
|
+
* `openLocalCopyStore({ attributedWrites })`, `createLocalCopiesPage({ attributedWrites })`): the
|
|
88
|
+
* copy records a room's kept writes with its edits and restores an ended session's to its room.
|
|
89
|
+
*/
|
|
90
|
+
export declare const roomAttributedWrites: {
|
|
91
|
+
of: typeof unconfirmedAttributedWritesOf;
|
|
92
|
+
restore: typeof restoreUnconfirmedAttributedWrites;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* One room's attributed writes Keryx has not acknowledged (see the module comment). Made once per
|
|
96
|
+
* room by its transport, which hands it to every socket it opens (`attributingWebSocket`'s
|
|
97
|
+
* `unconfirmed`) and destroys it with the room; a tracker made for the same document later takes
|
|
98
|
+
* over what it still kept, and what its local copy kept from an earlier session
|
|
99
|
+
* ({@link restoreUnconfirmedAttributedWrites}). Not for a room that only listens: its socket sends
|
|
100
|
+
* nothing to split.
|
|
101
|
+
*/
|
|
102
|
+
export declare class UnconfirmedUpdateAttributions implements UnconfirmedAttributedWrites, KeptWrites {
|
|
103
|
+
private runs;
|
|
104
|
+
private readonly doc;
|
|
105
|
+
private readonly attributionsFor;
|
|
106
|
+
private readonly isRemoteOrigin;
|
|
107
|
+
constructor({ doc, attributionsFor, isRemoteOrigin, }: {
|
|
108
|
+
doc: Doc;
|
|
109
|
+
/** The attributions of the update being written now: what the socket's trailer reads. */
|
|
110
|
+
attributionsFor: () => readonly UpdateAttribution[] | undefined;
|
|
111
|
+
/** True for a transaction the transport applied (Keryx's state, a peer's edit). */
|
|
112
|
+
isRemoteOrigin: (origin: unknown) => boolean;
|
|
113
|
+
});
|
|
114
|
+
private readonly handleUpdate;
|
|
115
|
+
/** The attributed writes Keryx has not acknowledged, oldest first. */
|
|
116
|
+
get pending(): readonly AttributedWrites[];
|
|
117
|
+
/** Writes an earlier session kept, older than any written here: they go out first. */
|
|
118
|
+
restore(writes: readonly AttributedWrites[]): void;
|
|
119
|
+
/**
|
|
120
|
+
* One socket of the room. While the room keeps writes, the socket's sync step 2 (its answer to
|
|
121
|
+
* Keryx's sync step 1) waits for the next frame Keryx sends, its whole document, which drops what
|
|
122
|
+
* Keryx already holds before the answer is split (see the module comment); every frame sent
|
|
123
|
+
* meanwhile waits behind it, in order. When Keryx's sync step 1 names no client, the room is new
|
|
124
|
+
* to Keryx: its answer goes out whole, born as one write, and what the room kept waits for its
|
|
125
|
+
* echo. Nothing kept, or a socket that cannot hear Keryx: every frame goes out as it is sent.
|
|
126
|
+
*/
|
|
127
|
+
connect({ send, hears, }: {
|
|
128
|
+
send: (frame: Uint8Array) => void;
|
|
129
|
+
hears: boolean;
|
|
130
|
+
}): AttributedWritesSocket;
|
|
131
|
+
/** Keryx sent `frame`: the ids it carries are Keryx's, kept no longer. */
|
|
132
|
+
received(frame: Uint8Array): void;
|
|
133
|
+
/**
|
|
134
|
+
* What goes out in place of `frame`: `[frame]` itself, except a sync step 2 carrying kept writes,
|
|
135
|
+
* which goes out behind each of them as its own marked sync update.
|
|
136
|
+
*/
|
|
137
|
+
framesFor(frame: Uint8Array): readonly Uint8Array[];
|
|
138
|
+
/** Stops keeping; what is still kept waits for the next tracker made for the document. */
|
|
139
|
+
destroy(): void;
|
|
140
|
+
}
|
|
141
|
+
export {};
|