@beeuni/sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -10
- package/dist/beeuni.esm.min.js +37 -35
- package/dist/beeuni.global.js +37 -35
- package/dist/browser/cache/idb-sync-cache.js +15 -5
- package/dist/browser/session-sync/server-transcript-mirror.d.ts +7 -1
- package/dist/browser/session-sync/server-transcript-mirror.js +30 -24
- package/dist/browser/session-sync/session-sync-registry.d.ts +3 -1
- package/dist/browser/session-sync/session-sync-registry.js +48 -3
- package/dist/browser/stores/sync-store/keep-unchanged.d.ts +4 -0
- package/dist/browser/stores/sync-store/keep-unchanged.js +34 -0
- package/dist/browser/stores/sync-store.d.ts +1 -0
- package/dist/browser/stores/sync-store.js +72 -16
- package/dist/core/attachments/prompt-attachments.d.ts +4 -0
- package/dist/core/attachments/prompt-attachments.js +34 -0
- package/dist/core/capture/rules.d.ts +17 -0
- package/dist/core/capture/rules.js +27 -0
- package/dist/core/client/beeuni.d.ts +92 -5
- package/dist/core/client/beeuni.js +2 -0
- package/dist/core/client/capture.d.ts +105 -0
- package/dist/core/client/capture.js +48 -0
- package/dist/core/client/facade-identity.d.ts +1 -0
- package/dist/core/client/facade-identity.js +1 -0
- package/dist/core/client/project-access-resources.d.ts +3 -1
- package/dist/core/client/project-access-resources.js +1 -1
- package/dist/core/client/project-operations-admin.d.ts +4 -4
- package/dist/core/client/project-operations-review.d.ts +2 -3
- package/dist/core/client/project-operations-review.js +1 -0
- package/dist/core/client/project-operations-workspace.d.ts +4 -4
- package/dist/core/client/project-operations-workspace.js +1 -1
- package/dist/core/client/project-operations.d.ts +5 -4
- package/dist/core/client/session-context.d.ts +2 -0
- package/dist/core/client/session-handle.d.ts +13 -0
- package/dist/core/client/session-lifecycle-resources.d.ts +11 -0
- package/dist/core/client/session-lifecycle-resources.js +4 -0
- package/dist/core/client/session-lifecycle-security.d.ts +2 -0
- package/dist/core/client/session-lifecycle-security.js +1 -0
- package/dist/core/client/session-readiness.d.ts +2 -0
- package/dist/core/client/session-readiness.js +20 -1
- package/dist/core/http/api-client.d.ts +1 -0
- package/dist/core/http/api-client.js +1 -0
- package/dist/core/http/config.d.ts +2 -0
- package/dist/core/http/response-error.js +26 -11
- package/dist/core/http/transport.js +6 -1
- package/dist/core/models/composer-model.d.ts +14 -0
- package/dist/core/models/composer-model.js +21 -1
- package/dist/core/rest/platform-client/capture.d.ts +363 -0
- package/dist/core/rest/platform-client/capture.js +116 -0
- package/dist/core/rest/platform-client/index.d.ts +1 -0
- package/dist/core/rest/platform-client/index.js +1 -0
- package/dist/core/rest/platform-client/lifecycle.js +18 -12
- package/dist/core/rest/platform-client/shared.d.ts +5 -0
- package/dist/core/rest/platform-client/shared.js +18 -0
- package/dist/core/rest/projects-client/account-secret-resources.d.ts +2 -1
- package/dist/core/rest/projects-client/accounts.d.ts +34 -0
- package/dist/core/rest/projects-client/accounts.js +10 -0
- package/dist/core/rest/projects-client/admin-providers.d.ts +0 -61
- package/dist/core/rest/projects-client/admin-providers.js +0 -6
- package/dist/core/rest/projects-client/agent-config.d.ts +3 -0
- package/dist/core/rest/projects-client/apps-fleet.d.ts +5 -1
- package/dist/core/rest/projects-client/apps-fleet.js +2 -2
- package/dist/core/rest/projects-client/apps.d.ts +3 -0
- package/dist/core/rest/projects-client/change-requests.d.ts +2 -0
- package/dist/core/rest/projects-client/change-requests.js +2 -0
- package/dist/core/rest/projects-client/connectors.d.ts +7 -0
- package/dist/core/rest/projects-client/connectors.js +3 -0
- package/dist/core/rest/projects-client/files.d.ts +3 -0
- package/dist/core/rest/projects-client/files.js +4 -0
- package/dist/core/rest/projects-client/gateway.d.ts +5 -0
- package/dist/core/rest/projects-client/iam-actions.d.ts +76 -0
- package/dist/core/rest/projects-client/iam-actions.js +101 -0
- package/dist/core/rest/projects-client/iam.d.ts +1 -0
- package/dist/core/rest/projects-client/iam.js +1 -0
- package/dist/core/rest/projects-client/notifications.d.ts +1 -1
- package/dist/core/rest/projects-client/notifications.js +4 -0
- package/dist/core/rest/projects-client/projects.d.ts +3 -2
- package/dist/core/rest/projects-client/projects.js +2 -3
- package/dist/core/rest/projects-client/review-items.d.ts +2 -0
- package/dist/core/rest/projects-client/review-items.js +4 -0
- package/dist/core/rest/projects-client/review.d.ts +17 -3
- package/dist/core/rest/projects-client/review.js +11 -0
- package/dist/core/rest/projects-client/secrets.d.ts +3 -0
- package/dist/core/rest/projects-client/session-attachments.js +14 -1
- package/dist/core/rest/projects-client/sessions.d.ts +29 -2
- package/dist/core/rest/projects-client/sessions.js +34 -4
- package/dist/core/runtime/runtime-rest-client.js +1 -1
- package/dist/core/runtime/runtime-types.d.ts +1 -0
- package/dist/core/session/open-bundle.d.ts +4 -4
- package/dist/core/session/open-bundle.js +29 -46
- package/dist/core/session/turn-end-watch.d.ts +47 -0
- package/dist/core/session/turn-end-watch.js +141 -0
- package/dist/core/session-log/index.d.ts +4 -0
- package/dist/core/session-log/index.js +3 -0
- package/dist/core/session-log/tool-call.d.ts +7 -2
- package/dist/core/session-log/tool-call.js +25 -6
- package/dist/core/session-log/turns/blocks.d.ts +60 -0
- package/dist/core/session-log/turns/blocks.js +124 -0
- package/dist/core/session-log/turns/history.d.ts +32 -0
- package/dist/core/session-log/turns/history.js +99 -0
- package/dist/core/session-log/turns/index.d.ts +4 -0
- package/dist/core/session-log/turns/index.js +4 -0
- package/dist/core/session-log/turns/metrics.d.ts +21 -0
- package/dist/core/session-log/turns/metrics.js +29 -0
- package/dist/core/session-log/turns/turn.d.ts +55 -0
- package/dist/core/session-log/turns/turn.js +279 -0
- package/dist/core/session-log/turns/v1-parts.d.ts +8 -0
- package/dist/core/session-log/turns/v1-parts.js +43 -0
- package/dist/core/session-log/v1-view.d.ts +5 -2
- package/dist/core/session-log/v1-view.js +86 -22
- package/dist/core/session-log/v1.js +10 -3
- package/dist/core/session-sync/saved-transcript.d.ts +15 -1
- package/dist/core/session-sync/saved-transcript.js +55 -0
- package/dist/core/session-sync/session-sync-controller.js +3 -0
- package/dist/core/stream/event-stream.js +3 -3
- package/dist/core/turns/classify.js +4 -2
- package/dist/core/turns/edit-blocks.d.ts +1 -0
- package/dist/core/turns/edit-blocks.js +15 -0
- package/dist/core/turns/segments/burst-summary.d.ts +1 -0
- package/dist/core/turns/segments/burst-summary.js +24 -0
- package/dist/core/turns/segments/group-steps.d.ts +4 -3
- package/dist/core/turns/segments/group-steps.js +9 -6
- package/dist/core/turns/segments/narration.d.ts +5 -4
- package/dist/core/turns/segments/narration.js +12 -3
- package/dist/core/turns/segments/session-activity-groups.d.ts +2 -1
- package/dist/core/turns/segments/session-activity-groups.js +3 -1
- package/dist/core/turns/segments/step-label.d.ts +2 -1
- package/dist/core/turns/segments/step-label.js +3 -1
- package/dist/core/turns/segments/working-turn.d.ts +4 -2
- package/dist/core/turns/segments/working-turn.js +9 -8
- package/dist/core/turns/tool-kind.d.ts +1 -1
- package/dist/core/turns/tool-kind.js +3 -2
- package/dist/core/turns/tool-registry.js +1 -0
- package/dist/core/turns/tools/tool-meta.d.ts +1 -1
- package/dist/core/turns/tools/tool-meta.js +3 -2
- package/dist/core/turns/view-model.js +1 -13
- package/dist/genui/catalog.d.ts +28 -0
- package/dist/genui/catalog.js +396 -0
- package/dist/genui/expansion.d.ts +3 -0
- package/dist/genui/expansion.js +295 -0
- package/dist/genui/fence-entry.d.ts +2 -0
- package/dist/genui/fence-entry.js +1 -0
- package/dist/genui/fence.d.ts +21 -0
- package/dist/genui/fence.js +138 -0
- package/dist/genui/index.d.ts +6 -0
- package/dist/genui/index.js +6 -0
- package/dist/genui/markdown.d.ts +12 -0
- package/dist/genui/markdown.js +104 -0
- package/dist/genui/parse.d.ts +6 -0
- package/dist/genui/parse.js +75 -0
- package/dist/genui/prompt.d.ts +2 -0
- package/dist/genui/prompt.js +206 -0
- package/dist/genui/react/default-components.d.ts +2 -0
- package/dist/genui/react/default-components.js +72 -0
- package/dist/genui/react/default-components.test.d.ts +1 -0
- package/dist/genui/react/default-components.test.js +94 -0
- package/dist/genui/react/genui-block-disabled.test.d.ts +1 -0
- package/dist/genui/react/genui-block-disabled.test.js +50 -0
- package/dist/genui/react/genui-block.d.ts +28 -0
- package/dist/genui/react/genui-block.js +91 -0
- package/dist/genui/react/genui-block.test.d.ts +1 -0
- package/dist/genui/react/genui-block.test.js +205 -0
- package/dist/genui/react/index.d.ts +2 -0
- package/dist/genui/react/index.js +2 -0
- package/dist/genui/share.d.ts +5 -0
- package/dist/genui/share.js +30 -0
- package/dist/genui/test-fixtures.d.ts +2 -0
- package/dist/genui/test-fixtures.js +16 -0
- package/dist/genui/types.d.ts +29 -0
- package/dist/genui/types.js +1 -0
- package/dist/genui/urls.d.ts +1 -0
- package/dist/genui/urls.js +18 -0
- package/dist/genui/validate.d.ts +9 -0
- package/dist/genui/validate.js +116 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/internal/connector-catalog.d.ts +1 -0
- package/dist/platform/ui.d.ts +8 -2
- package/dist/platform/ui.js +19 -4
- package/dist/react/chat/use-chat-turns.d.ts +1 -1
- package/dist/react/human-presence.d.ts +9 -0
- package/dist/react/human-presence.js +12 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +4 -0
- package/dist/react/opencode.d.ts +1 -1
- package/dist/react/opencode.js +1 -1
- package/dist/react/query-contracts.d.ts +6 -0
- package/dist/react/query-contracts.js +6 -0
- package/dist/react/query-keys.d.ts +17 -1
- package/dist/react/query-keys.js +24 -1
- package/dist/react/reset-identity-state.js +2 -0
- package/dist/react/session-cache-write.d.ts +1 -0
- package/dist/react/session-cache-write.js +20 -0
- package/dist/react/session-transcript-subscription.js +1 -1
- package/dist/react/sign-in-with-beeuni.d.ts +5 -5
- package/dist/react/turn-end-handoff.d.ts +13 -0
- package/dist/react/turn-end-handoff.js +101 -0
- package/dist/react/use-admin-providers.d.ts +0 -3
- package/dist/react/use-admin-providers.js +1 -11
- package/dist/react/use-capture.d.ts +47 -0
- package/dist/react/use-capture.js +173 -0
- package/dist/react/use-feature-flag.js +1 -0
- package/dist/react/use-opencode-events/handle-interaction-event.js +8 -3
- package/dist/react/use-opencode-events/handle-session-event.js +13 -8
- package/dist/react/use-opencode-events/handler-context.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.d.ts +1 -0
- package/dist/react/use-opencode-events/helpers.js +11 -1
- package/dist/react/use-opencode-local.d.ts +5 -1
- package/dist/react/use-opencode-local.js +15 -2
- package/dist/react/use-opencode-sessions/commands.d.ts +11 -1
- package/dist/react/use-opencode-sessions/commands.js +21 -1
- package/dist/react/use-opencode-sessions/permissions.js +28 -1
- package/dist/react/use-project-sessions.d.ts +1 -1
- package/dist/react/use-project-sessions.js +6 -5
- package/dist/react/use-prompt-attachments.js +1 -0
- package/dist/react/use-review-summary.d.ts +13 -0
- package/dist/react/use-review-summary.js +32 -0
- package/dist/react/use-runtime-reconnect.js +2 -2
- package/dist/react/use-session-list-state.d.ts +6 -0
- package/dist/react/use-session-list-state.js +33 -0
- package/dist/react/use-session-log.d.ts +3 -2
- package/dist/react/use-session-log.js +26 -17
- package/dist/react/use-session-messages.d.ts +2 -1
- package/dist/react/use-session-messages.js +15 -14
- package/dist/react/use-session-stream.js +12 -1
- package/dist/react/use-session-sync.d.ts +2 -2
- package/dist/react/use-session-sync.js +10 -8
- package/dist/react/use-session-transcript-history.js +2 -12
- package/dist/react/use-session-working.d.ts +2 -0
- package/dist/react/use-session-working.js +10 -0
- package/dist/react/use-session.d.ts +6 -0
- package/dist/react/use-session.js +87 -7
- package/package.json +49 -3
|
@@ -23,11 +23,21 @@ function openDB() {
|
|
|
23
23
|
const pending = new Promise((resolve, reject) => {
|
|
24
24
|
const request = idb.open(DB_NAME, DB_VERSION);
|
|
25
25
|
request.onupgradeneeded = () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
db.
|
|
29
|
-
|
|
30
|
-
db.
|
|
26
|
+
try {
|
|
27
|
+
const db = request.result;
|
|
28
|
+
if (db.objectStoreNames.contains(RETIRED_STORE_NAME))
|
|
29
|
+
db.deleteObjectStore(RETIRED_STORE_NAME);
|
|
30
|
+
if (!db.objectStoreNames.contains(STORE_NAME))
|
|
31
|
+
db.createObjectStore(STORE_NAME);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
reject(error);
|
|
35
|
+
try {
|
|
36
|
+
request.transaction?.abort();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
};
|
|
32
42
|
request.onsuccess = () => {
|
|
33
43
|
const db = request.result;
|
|
@@ -18,7 +18,6 @@ export declare function parseBeeuniSessionScope(scope: string | undefined): {
|
|
|
18
18
|
} | null;
|
|
19
19
|
export declare function loadSessionTranscriptMirror(input: {
|
|
20
20
|
beeuniSessionScope: string | undefined;
|
|
21
|
-
limit?: number;
|
|
22
21
|
signal?: AbortSignal;
|
|
23
22
|
child?: string;
|
|
24
23
|
}): Promise<SessionTranscriptSyncEnvelope | null>;
|
|
@@ -29,5 +28,12 @@ export declare function loadOlderSessionTranscriptMirror(input: {
|
|
|
29
28
|
signal?: AbortSignal;
|
|
30
29
|
child?: string;
|
|
31
30
|
}): Promise<SessionTranscriptSyncEnvelope | null>;
|
|
31
|
+
export declare function loadSavedSessionTranscriptMessages(scope: {
|
|
32
|
+
projectId: string;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
}, signal?: AbortSignal): Promise<Array<{
|
|
35
|
+
info: Message;
|
|
36
|
+
parts: Part[];
|
|
37
|
+
}>>;
|
|
32
38
|
export declare const SAVED_COPY_REFRESH_DELAY_MS = 3000;
|
|
33
39
|
export declare function refreshSavedCopy(store: SavedCopyStore, projectId: string, sessionId: string): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSessionTranscriptSync, } from "../../core/rest/projects-client/sessions.js";
|
|
2
|
-
import {
|
|
2
|
+
import { loadSavedTranscript } from "../../core/session-sync/saved-transcript.js";
|
|
3
3
|
export const MIRROR_HYDRATE_LIMIT = 40;
|
|
4
4
|
export function shouldHydrateFromMirror(input) {
|
|
5
5
|
const { envelope, runtimeSessionId, hasMessages } = input;
|
|
@@ -52,31 +52,14 @@ export async function loadSessionTranscriptMirror(input) {
|
|
|
52
52
|
const scope = parseBeeuniSessionScope(input.beeuniSessionScope);
|
|
53
53
|
if (!scope)
|
|
54
54
|
return null;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
limit:
|
|
55
|
+
try {
|
|
56
|
+
return input.child
|
|
57
|
+
? await getSessionTranscriptSync(scope.projectId, scope.sessionId, {
|
|
58
|
+
limit: MIRROR_HYDRATE_LIMIT,
|
|
59
59
|
signal: input.signal,
|
|
60
60
|
child: input.child,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const claimed = claimOpenBundle(scope.projectId, scope.sessionId);
|
|
68
|
-
if (claimed)
|
|
69
|
-
await claimed;
|
|
70
|
-
const stashed = takeOpenBundleTranscript(scope.projectId, scope.sessionId);
|
|
71
|
-
if (stashed)
|
|
72
|
-
return stashed;
|
|
73
|
-
if (takeOpenBundleTranscriptAbsence(scope.projectId, scope.sessionId))
|
|
74
|
-
return null;
|
|
75
|
-
try {
|
|
76
|
-
return await getSessionTranscriptSync(scope.projectId, scope.sessionId, {
|
|
77
|
-
limit: input.limit ?? MIRROR_HYDRATE_LIMIT,
|
|
78
|
-
signal: input.signal,
|
|
79
|
-
});
|
|
61
|
+
})
|
|
62
|
+
: await loadSavedTranscript(scope.projectId, scope.sessionId, { signal: input.signal });
|
|
80
63
|
}
|
|
81
64
|
catch {
|
|
82
65
|
return null;
|
|
@@ -98,6 +81,29 @@ export async function loadOlderSessionTranscriptMirror(input) {
|
|
|
98
81
|
return null;
|
|
99
82
|
}
|
|
100
83
|
}
|
|
84
|
+
const SAVED_EXPORT_WINDOW = 500;
|
|
85
|
+
export async function loadSavedSessionTranscriptMessages(scope, signal) {
|
|
86
|
+
const windows = [];
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
let before = null;
|
|
89
|
+
do {
|
|
90
|
+
const envelope = await getSessionTranscriptSync(scope.projectId, scope.sessionId, {
|
|
91
|
+
limit: SAVED_EXPORT_WINDOW,
|
|
92
|
+
history: true,
|
|
93
|
+
before,
|
|
94
|
+
signal,
|
|
95
|
+
});
|
|
96
|
+
if (!envelope.available)
|
|
97
|
+
break;
|
|
98
|
+
windows.unshift(mirrorMessagesForHydrate(envelope));
|
|
99
|
+
before = envelope.next_cursor ?? null;
|
|
100
|
+
if (before !== null && seen.has(before))
|
|
101
|
+
break;
|
|
102
|
+
if (before !== null)
|
|
103
|
+
seen.add(before);
|
|
104
|
+
} while (before !== null);
|
|
105
|
+
return windows.flat();
|
|
106
|
+
}
|
|
101
107
|
export const SAVED_COPY_REFRESH_DELAY_MS = 3_000;
|
|
102
108
|
export async function refreshSavedCopy(store, projectId, sessionId) {
|
|
103
109
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Message, Part, SessionStatus } from '../../core/runtime/runtime-types.js';
|
|
2
|
-
import { SessionSyncController, type SessionSyncPage, type SessionSyncReason } from '../../core/session-sync/session-sync-controller.js';
|
|
2
|
+
import { SessionSyncController, type SessionSyncPage, type SessionSyncReason, type SessionSyncTelemetryEvent } from '../../core/session-sync/session-sync-controller.js';
|
|
3
3
|
interface MessagesResponse {
|
|
4
4
|
data?: Array<{
|
|
5
5
|
info: Message;
|
|
@@ -28,6 +28,7 @@ export declare function readSessionMessagePage(client: SessionMessageClient, ses
|
|
|
28
28
|
limit: number;
|
|
29
29
|
before?: string;
|
|
30
30
|
}, signal?: AbortSignal): Promise<SessionSyncPage>;
|
|
31
|
+
export declare function reportTelemetry(sessionId: string, event: SessionSyncTelemetryEvent): void;
|
|
31
32
|
export declare function getSessionSyncController(sessionId: string, client?: SessionMessageClient, runtimeScope?: string): SessionSyncController;
|
|
32
33
|
export declare function prefetchSessionSyncOnce(sessionId: string, source: SessionPrefetchSource, client?: SessionMessageClient): Promise<boolean>;
|
|
33
34
|
export declare function clearActiveSessionPrefetches(): void;
|
|
@@ -35,6 +36,7 @@ export declare function retainSessionSyncController(sessionId: string, runtimeSc
|
|
|
35
36
|
export declare function reconcileSessionTail(sessionId: string, reason: SessionSyncReason, runtimeScope?: string): Promise<void>;
|
|
36
37
|
export declare function loadSessionRuntimeStatus(sessionId: string, client?: SessionMessageClient): Promise<SessionStatus | null>;
|
|
37
38
|
export declare function loadSessionTranscriptMessages(sessionId: string): Promise<SessionSyncPage['messages']>;
|
|
39
|
+
export declare function loadSessionExportMessages(sessionId: string, beeuniSessionScope?: string): Promise<SessionSyncPage['messages']>;
|
|
38
40
|
export declare function noteSessionSyncEvent(event: {
|
|
39
41
|
type?: string;
|
|
40
42
|
properties?: unknown;
|
|
@@ -4,8 +4,10 @@ import { ApiError } from '../../core/http/api/errors.js';
|
|
|
4
4
|
import { SessionSyncController, loadCompleteSessionHistory, } from '../../core/session-sync/session-sync-controller.js';
|
|
5
5
|
import { getCurrentRuntimeSandboxId, getCurrentRuntimeUrl } from '../../core/session/current-runtime.js';
|
|
6
6
|
import { useSyncStore } from '../stores/sync-store.js';
|
|
7
|
+
import { loadSavedSessionTranscriptMessages, parseBeeuniSessionScope } from './server-transcript-mirror.js';
|
|
7
8
|
export const ACTIVE_SESSION_PREFETCH_SOURCE = Symbol('active-session-prefetch');
|
|
8
9
|
const MAX_CONTROLLERS = 20;
|
|
10
|
+
const IDLE_BEFORE_EVICT_MS = 30_000;
|
|
9
11
|
const controllers = new Map();
|
|
10
12
|
function runtimeScopeKey(runtimeScope) {
|
|
11
13
|
return runtimeScope ?? getCurrentRuntimeSandboxId() ?? 'none';
|
|
@@ -83,7 +85,14 @@ export async function readSessionMessagePage(client, sessionId, request, signal)
|
|
|
83
85
|
nextCursor: result.response?.headers.get('x-next-cursor') || undefined,
|
|
84
86
|
};
|
|
85
87
|
}
|
|
86
|
-
function reportTelemetry(sessionId, event) {
|
|
88
|
+
export function reportTelemetry(sessionId, event) {
|
|
89
|
+
try {
|
|
90
|
+
if (!globalThis.localStorage?.getItem('beeuni:debug'))
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
87
96
|
console.debug('[session-sync]', { sessionId, ...event });
|
|
88
97
|
}
|
|
89
98
|
function resolveClient(key) {
|
|
@@ -118,8 +127,11 @@ function createController(sessionId, key) {
|
|
|
118
127
|
function evictInactiveControllers(protectedKey) {
|
|
119
128
|
if (controllers.size <= MAX_CONTROLLERS)
|
|
120
129
|
return;
|
|
130
|
+
const now = Date.now();
|
|
121
131
|
const inactive = [...controllers.entries()]
|
|
122
|
-
.filter(([key, entry]) => entry.consumers === 0 &&
|
|
132
|
+
.filter(([key, entry]) => entry.consumers === 0 &&
|
|
133
|
+
key !== protectedKey &&
|
|
134
|
+
now - entry.lastUsedAt > IDLE_BEFORE_EVICT_MS)
|
|
123
135
|
.sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt);
|
|
124
136
|
for (const [key, entry] of inactive) {
|
|
125
137
|
entry.controller.destroy();
|
|
@@ -128,6 +140,21 @@ function evictInactiveControllers(protectedKey) {
|
|
|
128
140
|
return;
|
|
129
141
|
}
|
|
130
142
|
}
|
|
143
|
+
let evictionTimer;
|
|
144
|
+
function scheduleEviction(delayMs = IDLE_BEFORE_EVICT_MS + 1) {
|
|
145
|
+
if (evictionTimer !== undefined)
|
|
146
|
+
return;
|
|
147
|
+
evictionTimer = setTimeout(() => {
|
|
148
|
+
evictionTimer = undefined;
|
|
149
|
+
evictInactiveControllers();
|
|
150
|
+
if (controllers.size <= MAX_CONTROLLERS)
|
|
151
|
+
return;
|
|
152
|
+
const idleSince = Math.min(...[...controllers.values()].filter((entry) => entry.consumers === 0).map((entry) => entry.lastUsedAt));
|
|
153
|
+
if (Number.isFinite(idleSince))
|
|
154
|
+
scheduleEviction(Math.max(1, idleSince + IDLE_BEFORE_EVICT_MS + 1 - Date.now()));
|
|
155
|
+
}, delayMs);
|
|
156
|
+
evictionTimer.unref?.();
|
|
157
|
+
}
|
|
131
158
|
function getOrCreateRegistryEntry(sessionId, client, initialConsumers = 0, runtimeScope) {
|
|
132
159
|
const key = controllerKey(sessionId, runtimeScope);
|
|
133
160
|
const existing = controllers.get(key);
|
|
@@ -197,8 +224,10 @@ export function retainSessionSyncController(sessionId, runtimeScope) {
|
|
|
197
224
|
return;
|
|
198
225
|
current.consumers = Math.max(0, current.consumers - 1);
|
|
199
226
|
current.lastUsedAt = Date.now();
|
|
200
|
-
if (current.consumers === 0)
|
|
227
|
+
if (current.consumers === 0) {
|
|
201
228
|
controller.setBusy(false);
|
|
229
|
+
scheduleEviction();
|
|
230
|
+
}
|
|
202
231
|
evictInactiveControllers();
|
|
203
232
|
};
|
|
204
233
|
}
|
|
@@ -217,6 +246,20 @@ export async function loadSessionRuntimeStatus(sessionId, client = getClient())
|
|
|
217
246
|
export function loadSessionTranscriptMessages(sessionId) {
|
|
218
247
|
return loadCompleteSessionHistory((request) => readSessionMessagePage(getClient(), sessionId, request));
|
|
219
248
|
}
|
|
249
|
+
export async function loadSessionExportMessages(sessionId, beeuniSessionScope) {
|
|
250
|
+
const saved = parseBeeuniSessionScope(beeuniSessionScope);
|
|
251
|
+
let live = [];
|
|
252
|
+
try {
|
|
253
|
+
live = await loadSessionTranscriptMessages(sessionId);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (!saved)
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
if (live.length > 0 || !saved)
|
|
260
|
+
return live;
|
|
261
|
+
return loadSavedSessionTranscriptMessages(saved);
|
|
262
|
+
}
|
|
220
263
|
const TRANSCRIPT_EVENT_TYPES = new Set([
|
|
221
264
|
'message.updated',
|
|
222
265
|
'message.part.updated',
|
|
@@ -242,6 +285,8 @@ export function resetSessionSyncControllers() {
|
|
|
242
285
|
for (const entry of controllers.values())
|
|
243
286
|
entry.controller.destroy();
|
|
244
287
|
controllers.clear();
|
|
288
|
+
clearTimeout(evictionTimer);
|
|
289
|
+
evictionTimer = undefined;
|
|
245
290
|
}
|
|
246
291
|
export function resetSessionSyncControllersForSession(sessionId, keepRuntimeScope) {
|
|
247
292
|
for (const [key, entry] of controllers) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function sameValue(a: unknown, b: unknown): boolean;
|
|
2
|
+
export declare function keepUnchanged<M extends {
|
|
3
|
+
id: string;
|
|
4
|
+
}, P>(previous: readonly M[] | undefined, next: M[], previousParts: Record<string, P[] | undefined>, nextParts: Record<string, P[] | undefined>): M[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function sameValue(a, b) {
|
|
2
|
+
if (a === b)
|
|
3
|
+
return true;
|
|
4
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
|
|
5
|
+
return false;
|
|
6
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
7
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
8
|
+
return false;
|
|
9
|
+
return a.every((value, index) => sameValue(value, b[index]));
|
|
10
|
+
}
|
|
11
|
+
const left = a;
|
|
12
|
+
const right = b;
|
|
13
|
+
const keys = Object.keys(left);
|
|
14
|
+
if (keys.length !== Object.keys(right).length)
|
|
15
|
+
return false;
|
|
16
|
+
return keys.every((key) => Object.hasOwn(right, key) && sameValue(left[key], right[key]));
|
|
17
|
+
}
|
|
18
|
+
export function keepUnchanged(previous, next, previousParts, nextParts) {
|
|
19
|
+
const byId = new Map((previous ?? []).map((message) => [message.id, message]));
|
|
20
|
+
const shared = next.map((message) => {
|
|
21
|
+
const old = byId.get(message.id);
|
|
22
|
+
return old && sameValue(old, message) ? old : message;
|
|
23
|
+
});
|
|
24
|
+
for (const message of shared) {
|
|
25
|
+
const before = previousParts[message.id];
|
|
26
|
+
const after = nextParts[message.id];
|
|
27
|
+
if (before && after && before !== after && sameValue(before, after))
|
|
28
|
+
nextParts[message.id] = before;
|
|
29
|
+
}
|
|
30
|
+
const unchanged = previous !== undefined &&
|
|
31
|
+
previous.length === shared.length &&
|
|
32
|
+
shared.every((message, index) => message === previous[index]);
|
|
33
|
+
return unchanged ? previous : shared;
|
|
34
|
+
}
|
|
@@ -68,4 +68,5 @@ interface SyncState {
|
|
|
68
68
|
statuses: Record<string, SessionStatus>;
|
|
69
69
|
}
|
|
70
70
|
export declare function hasOnlyCacheSourcedMessages(sessionID: string): boolean;
|
|
71
|
+
export declare function hasRuntimeConfirmedMessage(sessionID: string, messageID: string): boolean;
|
|
71
72
|
export declare const useSyncStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SyncState>>;
|
|
@@ -7,6 +7,7 @@ import { ascendingId } from "./sync-store/ascending-id.js";
|
|
|
7
7
|
import { Binary } from "./sync-store/binary.js";
|
|
8
8
|
import { DELTA_EVENT_TAIL_LIMIT } from "./sync-store/delta-event-window.js";
|
|
9
9
|
import { reconcileHydratedParts } from "./sync-store/reconcile-parts.js";
|
|
10
|
+
import { keepUnchanged } from "./sync-store/keep-unchanged.js";
|
|
10
11
|
export { ascendingId, Binary };
|
|
11
12
|
export function sameSessionStatus(a, b) {
|
|
12
13
|
if (a === b)
|
|
@@ -82,6 +83,16 @@ export function hasOnlyCacheSourcedMessages(sessionID) {
|
|
|
82
83
|
return false;
|
|
83
84
|
return messages.every((message) => cached.has(message.id));
|
|
84
85
|
}
|
|
86
|
+
export function hasRuntimeConfirmedMessage(sessionID, messageID) {
|
|
87
|
+
const messages = useSyncStore.getState().messages[sessionID];
|
|
88
|
+
if (!messages)
|
|
89
|
+
return false;
|
|
90
|
+
let ids = messageIdSets.get(messages);
|
|
91
|
+
if (!ids)
|
|
92
|
+
messageIdSets.set(messages, (ids = new Set(messages.map((message) => message.id))));
|
|
93
|
+
return ids.has(messageID) && !cacheSourcedIds.get(sessionID)?.has(messageID);
|
|
94
|
+
}
|
|
95
|
+
const messageIdSets = new WeakMap();
|
|
85
96
|
function recordOptimisticEcho(sessionID, optimisticID, echoID) {
|
|
86
97
|
if (optimisticID === echoID)
|
|
87
98
|
return;
|
|
@@ -139,6 +150,7 @@ function forgetSessionIds(sessionID) {
|
|
|
139
150
|
deltaActiveParts.delete(sessionID);
|
|
140
151
|
bridgedPartIds.delete(sessionID);
|
|
141
152
|
deltaEventTails.delete(sessionID);
|
|
153
|
+
deltaOffsets.delete(sessionID);
|
|
142
154
|
stubAssistantIds.delete(sessionID);
|
|
143
155
|
}
|
|
144
156
|
const isOptimistic = (sessionID, messageID) => hasTrackedId(optimisticIds, sessionID, messageID);
|
|
@@ -161,6 +173,15 @@ const awaitsUserEcho = (sessionID, messageID) => {
|
|
|
161
173
|
const bridgedPartIds = new Map();
|
|
162
174
|
const deltaActiveParts = new Map();
|
|
163
175
|
const deltaEventTails = new Map();
|
|
176
|
+
const deltaOffsets = new Map();
|
|
177
|
+
function setDeltaOffset(sessionID, key, offset) {
|
|
178
|
+
let bucket = deltaOffsets.get(sessionID);
|
|
179
|
+
if (!bucket) {
|
|
180
|
+
bucket = new Map();
|
|
181
|
+
deltaOffsets.set(sessionID, bucket);
|
|
182
|
+
}
|
|
183
|
+
bucket.set(key, offset);
|
|
184
|
+
}
|
|
164
185
|
const stubAssistantIds = new Map();
|
|
165
186
|
function trackStub(sessionID, stubId, parentId) {
|
|
166
187
|
const bucket = stubAssistantIds.get(sessionID);
|
|
@@ -224,6 +245,7 @@ const DETACHED_SESSION_LIMIT = 8;
|
|
|
224
245
|
const MESSAGE_ROWS_LIMIT = 20;
|
|
225
246
|
const sessionMessageRows = new Map();
|
|
226
247
|
const EMPTY_MESSAGE_ROWS = [];
|
|
248
|
+
const rowsByMessages = new WeakMap();
|
|
227
249
|
function touchSessionMessageRows(sessionID, entry) {
|
|
228
250
|
sessionMessageRows.delete(sessionID);
|
|
229
251
|
sessionMessageRows.set(sessionID, entry);
|
|
@@ -326,19 +348,38 @@ function appendPartDeltas(sessionID, messageID, partID, field, pieces) {
|
|
|
326
348
|
const result = Binary.search(list, partID, (p) => p.id);
|
|
327
349
|
if (!result.found)
|
|
328
350
|
return s;
|
|
329
|
-
|
|
351
|
+
const messages = s.messages[sessionID] ?? [];
|
|
352
|
+
let message;
|
|
353
|
+
for (let i = messages.length - 1; i >= 0 && !message; i--)
|
|
354
|
+
if (messages[i].id === messageID)
|
|
355
|
+
message = messages[i];
|
|
356
|
+
if (message?.role === "assistant" && message.time?.completed)
|
|
357
|
+
return s;
|
|
358
|
+
const existing = list[result.index][field] ?? "";
|
|
359
|
+
const offsetKey = `${messageID}:${partID}:${field}`;
|
|
360
|
+
let offset = deltaOffsets.get(sessionID)?.get(offsetKey) ?? existing.length;
|
|
361
|
+
let text = existing;
|
|
330
362
|
for (const piece of pieces) {
|
|
331
|
-
if (piece.id && !takeDeltaEventId(sessionID,
|
|
363
|
+
if (piece.id && !takeDeltaEventId(sessionID, offsetKey, piece.id)) {
|
|
364
|
+
if (offset < text.length && text.startsWith(piece.delta, offset))
|
|
365
|
+
offset += piece.delta.length;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (offset < text.length && text.startsWith(piece.delta, offset)) {
|
|
369
|
+
offset += piece.delta.length;
|
|
332
370
|
continue;
|
|
333
|
-
|
|
371
|
+
}
|
|
372
|
+
const held = offset < text.length && piece.delta.startsWith(text.slice(offset)) ? text.length - offset : 0;
|
|
373
|
+
text += piece.delta.slice(held);
|
|
374
|
+
offset = text.length;
|
|
334
375
|
applied = true;
|
|
335
376
|
}
|
|
377
|
+
setDeltaOffset(sessionID, offsetKey, offset);
|
|
336
378
|
if (!applied)
|
|
337
379
|
return s;
|
|
338
380
|
const next = [...list];
|
|
339
381
|
const part = { ...next[result.index] };
|
|
340
|
-
|
|
341
|
-
part[field] = (existing ?? "") + delta;
|
|
382
|
+
part[field] = text;
|
|
342
383
|
next[result.index] = part;
|
|
343
384
|
return { parts: { ...s.parts, [messageID]: next } };
|
|
344
385
|
});
|
|
@@ -364,15 +405,15 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
364
405
|
const list = s.messages[sessionID] ?? [];
|
|
365
406
|
const result = Binary.search(list, message.id, (m) => m.id);
|
|
366
407
|
const bsValid = result.found && list[result.index]?.id === message.id;
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
408
|
+
const index = bsValid ? result.index : list.findIndex((m) => m.id === message.id);
|
|
409
|
+
if (index !== -1) {
|
|
410
|
+
const previous = list[index];
|
|
411
|
+
const completed = previous.role === "assistant" ? previous.time?.completed : undefined;
|
|
412
|
+
const kept = completed && message.role === "assistant" && message.time && !message.time.completed
|
|
413
|
+
? { ...message, time: { ...message.time, completed } }
|
|
414
|
+
: message;
|
|
374
415
|
const next = [...list];
|
|
375
|
-
next[
|
|
416
|
+
next[index] = kept;
|
|
376
417
|
return { messages: { ...s.messages, [sessionID]: next } };
|
|
377
418
|
}
|
|
378
419
|
const next = [...list];
|
|
@@ -423,6 +464,9 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
423
464
|
prevText.length > 0) {
|
|
424
465
|
const isPrefixGrowth = incomingText.startsWith(prevText);
|
|
425
466
|
if (!isPrefixGrowth) {
|
|
467
|
+
if (partSessionID && prevText.startsWith(incomingText)) {
|
|
468
|
+
setDeltaOffset(partSessionID, `${messageID}:${part.id}:text`, incomingText.length);
|
|
469
|
+
}
|
|
426
470
|
if (!bridgeCleared)
|
|
427
471
|
return s;
|
|
428
472
|
const next = [...list];
|
|
@@ -431,9 +475,15 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
431
475
|
}
|
|
432
476
|
}
|
|
433
477
|
}
|
|
478
|
+
const followStream = () => {
|
|
479
|
+
if (partSessionID && isTextLikePart(part)) {
|
|
480
|
+
setDeltaOffset(partSessionID, `${messageID}:${part.id}:text`, part.text.length);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
434
483
|
const next = [...list];
|
|
435
484
|
if (result.found) {
|
|
436
485
|
next[result.index] = part;
|
|
486
|
+
followStream();
|
|
437
487
|
}
|
|
438
488
|
else {
|
|
439
489
|
if (hasTrackedId(deltaActiveParts, partSessionID, part.id) && isTextLikePart(part)) {
|
|
@@ -445,6 +495,7 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
445
495
|
}
|
|
446
496
|
}
|
|
447
497
|
next.splice(result.index, 0, part);
|
|
498
|
+
followStream();
|
|
448
499
|
}
|
|
449
500
|
return { parts: { ...s.parts, [messageID]: next } };
|
|
450
501
|
}),
|
|
@@ -747,7 +798,7 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
747
798
|
buildSessionMessages: (sessionID, msgs, parts) => {
|
|
748
799
|
if (!msgs || msgs.length === 0)
|
|
749
800
|
return EMPTY_MESSAGE_ROWS;
|
|
750
|
-
const cached = sessionMessageRows.get(sessionID);
|
|
801
|
+
const cached = rowsByMessages.get(msgs) ?? sessionMessageRows.get(sessionID);
|
|
751
802
|
if (cached && cached.msgs === msgs) {
|
|
752
803
|
let same = cached.partRefs.length === msgs.length;
|
|
753
804
|
if (same) {
|
|
@@ -793,7 +844,9 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
793
844
|
}
|
|
794
845
|
result.push(reuse ?? { info, parts: messageParts ?? [] });
|
|
795
846
|
}
|
|
796
|
-
|
|
847
|
+
const entry = { msgs, partRefs, result };
|
|
848
|
+
touchSessionMessageRows(sessionID, entry);
|
|
849
|
+
rowsByMessages.set(msgs, entry);
|
|
797
850
|
return result;
|
|
798
851
|
},
|
|
799
852
|
hydrate: (sessionID, msgs, opts) => {
|
|
@@ -1026,8 +1079,9 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
1026
1079
|
clearBridge: (id) => untrackId(bridgedPartIds, sessionID, id),
|
|
1027
1080
|
isDeltaActive: (id) => hasTrackedId(deltaActiveParts, sessionID, id),
|
|
1028
1081
|
});
|
|
1082
|
+
const kept = keepUnchanged(s.messages[sessionID], merged, s.parts, newParts);
|
|
1029
1083
|
return {
|
|
1030
|
-
messages: { ...s.messages, [sessionID]:
|
|
1084
|
+
messages: kept === s.messages[sessionID] ? s.messages : { ...s.messages, [sessionID]: kept },
|
|
1031
1085
|
parts: newParts,
|
|
1032
1086
|
};
|
|
1033
1087
|
});
|
|
@@ -1043,6 +1097,7 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
1043
1097
|
bridgedPartIds.clear();
|
|
1044
1098
|
deltaActiveParts.clear();
|
|
1045
1099
|
deltaEventTails.clear();
|
|
1100
|
+
deltaOffsets.clear();
|
|
1046
1101
|
stubAssistantIds.clear();
|
|
1047
1102
|
sessionConsumers.clear();
|
|
1048
1103
|
detachedSessions.clear();
|
|
@@ -1283,6 +1338,7 @@ export const useSyncStore = create()((set, get) => ({
|
|
|
1283
1338
|
}
|
|
1284
1339
|
deltaActiveParts.delete(sid);
|
|
1285
1340
|
deltaEventTails.delete(sid);
|
|
1341
|
+
deltaOffsets.delete(sid);
|
|
1286
1342
|
set((s) => {
|
|
1287
1343
|
const msgs = s.messages[sid] ?? [];
|
|
1288
1344
|
let userIdx = -1;
|
|
@@ -21,6 +21,10 @@ export interface PromptAttachmentControllerOptions {
|
|
|
21
21
|
export interface PromptAttachmentController {
|
|
22
22
|
add(file: File): string;
|
|
23
23
|
addMany(files: readonly File[]): string[];
|
|
24
|
+
restore(uploads: readonly {
|
|
25
|
+
attachment: PromptAttachment;
|
|
26
|
+
file?: File;
|
|
27
|
+
}[]): (string | undefined)[];
|
|
24
28
|
retry(id: string): void;
|
|
25
29
|
remove(id: string): Promise<void>;
|
|
26
30
|
abort(id: string): void;
|
|
@@ -216,6 +216,39 @@ export function createPromptAttachmentController(projectId, options = {}) {
|
|
|
216
216
|
pump();
|
|
217
217
|
return ids;
|
|
218
218
|
}
|
|
219
|
+
function restore(uploads) {
|
|
220
|
+
const valid = uploads.map(({ attachment, file }) => {
|
|
221
|
+
try {
|
|
222
|
+
return { attachment: completedMetadata(attachment), file };
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
validateSizes(valid.flatMap((upload) => (upload ? [upload.attachment.size] : [])));
|
|
229
|
+
const ids = valid.map((upload) => {
|
|
230
|
+
if (!upload)
|
|
231
|
+
return undefined;
|
|
232
|
+
const id = `attachment-${++lastAttachmentId}`;
|
|
233
|
+
const { attachment, file } = upload;
|
|
234
|
+
entries.set(id, {
|
|
235
|
+
generation: 0,
|
|
236
|
+
item: {
|
|
237
|
+
id,
|
|
238
|
+
...(file ? { file } : {}),
|
|
239
|
+
filename: attachment.filename,
|
|
240
|
+
mime: attachment.mime,
|
|
241
|
+
size: attachment.size,
|
|
242
|
+
status: 'ready',
|
|
243
|
+
receivedBytes: attachment.size,
|
|
244
|
+
attachment,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
return id;
|
|
248
|
+
});
|
|
249
|
+
emit();
|
|
250
|
+
return ids;
|
|
251
|
+
}
|
|
219
252
|
function abort(id) {
|
|
220
253
|
const entry = entries.get(id);
|
|
221
254
|
if (!entry || entry.item.status === 'ready')
|
|
@@ -284,6 +317,7 @@ export function createPromptAttachmentController(projectId, options = {}) {
|
|
|
284
317
|
return {
|
|
285
318
|
add: (file) => addMany([file])[0],
|
|
286
319
|
addMany,
|
|
320
|
+
restore,
|
|
287
321
|
retry(id) {
|
|
288
322
|
const entry = entries.get(id);
|
|
289
323
|
if (!entry?.handedOff)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const CAPTURE_LAYERS: readonly ["screen", "actions", "audio"];
|
|
2
|
+
export type CaptureLayer = (typeof CAPTURE_LAYERS)[number];
|
|
3
|
+
export declare const CAPTURE_EXCLUSION_GROUPS: readonly ["password_managers", "messaging", "banking"];
|
|
4
|
+
export type CaptureExclusionGroup = (typeof CAPTURE_EXCLUSION_GROUPS)[number];
|
|
5
|
+
export declare const CAPTURE_RETENTION_LIMITS: {
|
|
6
|
+
readonly localHours: number;
|
|
7
|
+
readonly remoteDays: 3650;
|
|
8
|
+
};
|
|
9
|
+
export declare const CAPTURE_FRAME_INTERVALS_MS: readonly [500, 1000, 2000, 5000, 10000];
|
|
10
|
+
export declare const CAPTURE_MAX_HEIGHTS: readonly [720, 1080, 1440, 2160];
|
|
11
|
+
export type CaptureMaxHeight = (typeof CAPTURE_MAX_HEIGHTS)[number];
|
|
12
|
+
export declare const CAPTURE_DEVICE_DEFAULTS: {
|
|
13
|
+
readonly intervalMs: 2000;
|
|
14
|
+
readonly maxHeight: 1440;
|
|
15
|
+
};
|
|
16
|
+
export declare function normalizeCaptureDomain(input: string): string | null;
|
|
17
|
+
export declare function normalizeCaptureAppId(input: string): string | null;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const CAPTURE_LAYERS = ['screen', 'actions', 'audio'];
|
|
2
|
+
export const CAPTURE_EXCLUSION_GROUPS = ['password_managers', 'messaging', 'banking'];
|
|
3
|
+
export const CAPTURE_RETENTION_LIMITS = { localHours: 24 * 365, remoteDays: 3650 };
|
|
4
|
+
export const CAPTURE_FRAME_INTERVALS_MS = [500, 1000, 2000, 5000, 10000];
|
|
5
|
+
export const CAPTURE_MAX_HEIGHTS = [720, 1080, 1440, 2160];
|
|
6
|
+
export const CAPTURE_DEVICE_DEFAULTS = { intervalMs: 2000, maxHeight: 1440 };
|
|
7
|
+
const HOST = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/;
|
|
8
|
+
export function normalizeCaptureDomain(input) {
|
|
9
|
+
const raw = input.trim().toLowerCase();
|
|
10
|
+
if (!raw)
|
|
11
|
+
return null;
|
|
12
|
+
let host;
|
|
13
|
+
try {
|
|
14
|
+
host = new URL(raw.includes('://') ? raw : `https://${raw}`).hostname;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
host = host.replace(/^www\./, '').replace(/\.$/, '');
|
|
20
|
+
return HOST.test(host) ? host : null;
|
|
21
|
+
}
|
|
22
|
+
export function normalizeCaptureAppId(input) {
|
|
23
|
+
const id = input.trim();
|
|
24
|
+
if (!id || id.length > 255 || id.startsWith('-') || /[\u0000-\u001f\u007f]/.test(id))
|
|
25
|
+
return null;
|
|
26
|
+
return /\s/.test(id) && !id.toLowerCase().endsWith('.exe') ? null : id;
|
|
27
|
+
}
|