@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SessionTurnEndError, SessionTurnStatus } from '../rest/projects-client/sessions.js';
|
|
2
|
+
export declare const TURN_END_WATCH_FIRST_READ_MS = 1000;
|
|
3
|
+
export declare const TURN_END_WATCH_POLL_MS = 5000;
|
|
4
|
+
export declare const TURN_END_WATCH_LONG_TURN_MS: number;
|
|
5
|
+
export declare const TURN_END_WATCH_SLOW_POLL_MS = 15000;
|
|
6
|
+
export declare const TURN_END_WATCH_START_MS = 60000;
|
|
7
|
+
export declare const TURN_END_WATCH_MAX_MS: number;
|
|
8
|
+
export interface TurnEndHandoff {
|
|
9
|
+
projectId: string;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
openTurnToken: string | null;
|
|
12
|
+
lastEndedToken: string | null;
|
|
13
|
+
title?: () => string | undefined;
|
|
14
|
+
}
|
|
15
|
+
export type TurnEndOutcome = {
|
|
16
|
+
kind: 'completed';
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'failed';
|
|
19
|
+
error: SessionTurnEndError | null;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'stopped';
|
|
22
|
+
};
|
|
23
|
+
export type TurnEndVerdict = {
|
|
24
|
+
state: 'running';
|
|
25
|
+
} | {
|
|
26
|
+
state: 'waiting';
|
|
27
|
+
} | {
|
|
28
|
+
state: 'ended';
|
|
29
|
+
outcome: TurnEndOutcome;
|
|
30
|
+
};
|
|
31
|
+
export declare function readTurnEnd(handoff: TurnEndHandoff, read: SessionTurnStatus, sawOpen: boolean): TurnEndVerdict;
|
|
32
|
+
export interface TurnEndWatchDeps {
|
|
33
|
+
readTurn(projectId: string, sessionId: string): Promise<SessionTurnStatus>;
|
|
34
|
+
subscribe?(projectId: string, sessionId: string, onRead: (read: SessionTurnStatus) => void): () => void;
|
|
35
|
+
pushLive?(projectId: string, sessionId: string): boolean;
|
|
36
|
+
onEnded(handoff: TurnEndHandoff, outcome: TurnEndOutcome, watchedSinceMs: number): void;
|
|
37
|
+
now?: () => number;
|
|
38
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
39
|
+
clearTimer?: (handle: unknown) => void;
|
|
40
|
+
}
|
|
41
|
+
export interface TurnEndWatch {
|
|
42
|
+
watch(handoff: TurnEndHandoff): void;
|
|
43
|
+
release(projectId: string, sessionId: string): void;
|
|
44
|
+
releaseAll(): void;
|
|
45
|
+
watching(): string[];
|
|
46
|
+
}
|
|
47
|
+
export declare function createTurnEndWatch(deps: TurnEndWatchDeps): TurnEndWatch;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { isAbortError } from '../http/abort-error.js';
|
|
2
|
+
export const TURN_END_WATCH_FIRST_READ_MS = 1_000;
|
|
3
|
+
export const TURN_END_WATCH_POLL_MS = 5_000;
|
|
4
|
+
export const TURN_END_WATCH_LONG_TURN_MS = 5 * 60_000;
|
|
5
|
+
export const TURN_END_WATCH_SLOW_POLL_MS = 15_000;
|
|
6
|
+
export const TURN_END_WATCH_START_MS = 60_000;
|
|
7
|
+
export const TURN_END_WATCH_MAX_MS = 12 * 60 * 60_000;
|
|
8
|
+
function outcomeOf(read) {
|
|
9
|
+
const ended = read.last_ended;
|
|
10
|
+
if (!ended)
|
|
11
|
+
return { kind: 'completed' };
|
|
12
|
+
const reason = ended.end_reason;
|
|
13
|
+
if (reason === 'completed' || reason === 'unknown' || reason === null)
|
|
14
|
+
return { kind: 'completed' };
|
|
15
|
+
const error = ended.error;
|
|
16
|
+
if (error) {
|
|
17
|
+
if (isAbortError({ name: error.name ?? undefined, message: error.message ?? undefined })) {
|
|
18
|
+
return { kind: 'stopped' };
|
|
19
|
+
}
|
|
20
|
+
return { kind: 'failed', error };
|
|
21
|
+
}
|
|
22
|
+
if (reason === 'failed') {
|
|
23
|
+
const listed = read.recent_failures?.some((failure) => !!ended.message_id && failure.message_id === ended.message_id);
|
|
24
|
+
if (!listed)
|
|
25
|
+
return { kind: 'stopped' };
|
|
26
|
+
}
|
|
27
|
+
return { kind: 'failed', error: null };
|
|
28
|
+
}
|
|
29
|
+
export function readTurnEnd(handoff, read, sawOpen) {
|
|
30
|
+
if (read.turns.length > 0)
|
|
31
|
+
return { state: 'running' };
|
|
32
|
+
const lastEndedToken = read.last_ended?.turn_token ?? null;
|
|
33
|
+
const endedSinceHandoff = handoff.openTurnToken !== null ||
|
|
34
|
+
sawOpen ||
|
|
35
|
+
(lastEndedToken !== null && lastEndedToken !== handoff.lastEndedToken);
|
|
36
|
+
if (!endedSinceHandoff)
|
|
37
|
+
return { state: 'waiting' };
|
|
38
|
+
return { state: 'ended', outcome: outcomeOf(read) };
|
|
39
|
+
}
|
|
40
|
+
function isFinalReadError(error) {
|
|
41
|
+
const status = error?.status;
|
|
42
|
+
return status === 401 || status === 403 || status === 404;
|
|
43
|
+
}
|
|
44
|
+
export function createTurnEndWatch(deps) {
|
|
45
|
+
const now = deps.now ?? Date.now;
|
|
46
|
+
const setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
47
|
+
const clearTimer = deps.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
48
|
+
const entries = new Map();
|
|
49
|
+
const keyOf = (projectId, sessionId) => `${projectId}/${sessionId}`;
|
|
50
|
+
function drop(key, entry) {
|
|
51
|
+
if (entries.get(key) !== entry)
|
|
52
|
+
return;
|
|
53
|
+
entry.dropped = true;
|
|
54
|
+
clearTimer(entry.timer);
|
|
55
|
+
entry.unsubscribe?.();
|
|
56
|
+
entries.delete(key);
|
|
57
|
+
}
|
|
58
|
+
function schedule(key, entry, ms) {
|
|
59
|
+
entry.timer = setTimer(() => void tick(key, entry), ms);
|
|
60
|
+
}
|
|
61
|
+
function evaluate(key, entry, read) {
|
|
62
|
+
if (entries.get(key) !== entry)
|
|
63
|
+
return;
|
|
64
|
+
const verdict = readTurnEnd(entry.handoff, read, entry.sawOpen);
|
|
65
|
+
if (verdict.state === 'ended') {
|
|
66
|
+
drop(key, entry);
|
|
67
|
+
deps.onEnded(entry.handoff, verdict.outcome, entry.sinceMs);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (verdict.state === 'running')
|
|
71
|
+
entry.sawOpen = true;
|
|
72
|
+
entry.lastState = verdict.state;
|
|
73
|
+
}
|
|
74
|
+
async function tick(key, entry) {
|
|
75
|
+
if (entries.get(key) !== entry)
|
|
76
|
+
return;
|
|
77
|
+
const { projectId, sessionId } = entry.handoff;
|
|
78
|
+
if (!deps.pushLive?.(projectId, sessionId)) {
|
|
79
|
+
try {
|
|
80
|
+
const read = await deps.readTurn(projectId, sessionId);
|
|
81
|
+
evaluate(key, entry, read);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (entries.get(key) !== entry)
|
|
85
|
+
return;
|
|
86
|
+
if (isFinalReadError(error)) {
|
|
87
|
+
drop(key, entry);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (entries.get(key) !== entry)
|
|
93
|
+
return;
|
|
94
|
+
const age = now() - entry.sinceMs;
|
|
95
|
+
const waitingTooLong = entry.lastState === 'waiting' && !entry.sawOpen && age >= TURN_END_WATCH_START_MS;
|
|
96
|
+
if (waitingTooLong || age >= TURN_END_WATCH_MAX_MS) {
|
|
97
|
+
drop(key, entry);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
schedule(key, entry, age >= TURN_END_WATCH_LONG_TURN_MS ? TURN_END_WATCH_SLOW_POLL_MS : TURN_END_WATCH_POLL_MS);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
watch(handoff) {
|
|
104
|
+
if (!handoff.projectId || !handoff.sessionId)
|
|
105
|
+
return;
|
|
106
|
+
const key = keyOf(handoff.projectId, handoff.sessionId);
|
|
107
|
+
const previous = entries.get(key);
|
|
108
|
+
if (previous)
|
|
109
|
+
drop(key, previous);
|
|
110
|
+
const entry = {
|
|
111
|
+
handoff,
|
|
112
|
+
sinceMs: now(),
|
|
113
|
+
sawOpen: false,
|
|
114
|
+
lastState: null,
|
|
115
|
+
timer: null,
|
|
116
|
+
unsubscribe: null,
|
|
117
|
+
dropped: false,
|
|
118
|
+
};
|
|
119
|
+
entries.set(key, entry);
|
|
120
|
+
schedule(key, entry, TURN_END_WATCH_FIRST_READ_MS);
|
|
121
|
+
const unsubscribe = deps.subscribe?.(handoff.projectId, handoff.sessionId, (read) => evaluate(key, entry, read));
|
|
122
|
+
if (entry.dropped)
|
|
123
|
+
unsubscribe?.();
|
|
124
|
+
else
|
|
125
|
+
entry.unsubscribe = unsubscribe ?? null;
|
|
126
|
+
},
|
|
127
|
+
release(projectId, sessionId) {
|
|
128
|
+
const key = keyOf(projectId, sessionId);
|
|
129
|
+
const entry = entries.get(key);
|
|
130
|
+
if (entry)
|
|
131
|
+
drop(key, entry);
|
|
132
|
+
},
|
|
133
|
+
releaseAll() {
|
|
134
|
+
for (const [key, entry] of [...entries])
|
|
135
|
+
drop(key, entry);
|
|
136
|
+
},
|
|
137
|
+
watching() {
|
|
138
|
+
return [...entries.keys()];
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { KNOWN_TOOL_KINDS, SESSION_LOG_MINOR, SESSION_LOG_SCHEMA, type AttachmentBlock, type CompactionBlock, type CompactionLayoutEntry, type GradeCounts, type HarnessBlock, type KnownToolKind, type ModelRef, type ReasoningBlock, type RestoreGrade, type SessionLog, type SessionLogBlock, type SessionLogMessage, type SessionLogThread, type StepBlock, type SubtaskBlock, type TextBlock, type ThreadInteraction, type ToolCallBlock, type ToolResultContent, } from './types.js';
|
|
2
2
|
export type { Todo as SessionLogTodo, ToolKind as SessionLogToolKind } from './types.js';
|
|
3
|
+
export type { SessionLogPage, SessionLogPageQuery } from './source.js';
|
|
3
4
|
export { toolCallBlock, toolCallMetadata } from './tool-call.js';
|
|
5
|
+
export { toolCallError, toolCallRawOutput, toolCallStatus } from './tool-call.js';
|
|
6
|
+
export { sessionLogOfV1Rows } from './v1-view.js';
|
|
7
|
+
export * from './turns/index.js';
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { KNOWN_TOOL_KINDS, SESSION_LOG_MINOR, SESSION_LOG_SCHEMA, } from './types.js';
|
|
2
2
|
export { toolCallBlock, toolCallMetadata } from './tool-call.js';
|
|
3
|
+
export { toolCallError, toolCallRawOutput, toolCallStatus } from './tool-call.js';
|
|
4
|
+
export { sessionLogOfV1Rows } from './v1-view.js';
|
|
5
|
+
export * from './turns/index.js';
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { ToolPart } from '../runtime/runtime-types.js';
|
|
2
|
-
import { type ToolCallBlock } from './types.js';
|
|
1
|
+
import type { Part, ToolPart } from '../runtime/runtime-types.js';
|
|
2
|
+
import { type SessionLogBlock, type ToolCallBlock } from './types.js';
|
|
3
3
|
export declare function toolCallBlock(part: ToolPart): ToolCallBlock;
|
|
4
|
+
export declare function blockPartOf(block: SessionLogBlock): Part | undefined;
|
|
4
5
|
export declare function toolPartOf(call: ToolCallBlock): ToolPart;
|
|
5
6
|
export declare const isToolCallBlock: (value: unknown) => value is ToolCallBlock;
|
|
7
|
+
export declare const asToolPart: (value: ToolPart | ToolCallBlock) => ToolPart;
|
|
6
8
|
export declare function toolCallMetadata(call: ToolCallBlock): Record<string, unknown>;
|
|
9
|
+
export declare function toolCallStatus(call: ToolCallBlock): 'pending' | 'running' | 'completed' | 'error';
|
|
10
|
+
export declare function toolCallRawOutput(call: ToolCallBlock): string;
|
|
11
|
+
export declare function toolCallError(call: ToolCallBlock): string | undefined;
|
|
@@ -16,17 +16,16 @@ export function toolCallBlock(part) {
|
|
|
16
16
|
return block;
|
|
17
17
|
}
|
|
18
18
|
const parts = new WeakMap();
|
|
19
|
-
export function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return cached;
|
|
19
|
+
export function blockPartOf(block) {
|
|
20
|
+
if (parts.has(block))
|
|
21
|
+
return parts.get(block);
|
|
23
22
|
const thread = {
|
|
24
23
|
...V, thread_id: '', parent_thread_id: null, spawned_by: null, agent: null, title: null, created_at: '',
|
|
25
24
|
ext: { opencode: { version: BEEUNI_TRANSCRIPT_SCHEMA } },
|
|
26
25
|
messages: [{
|
|
27
26
|
...V, message_id: '', thread_id: '', seq: 1, role: 'assistant', kind: 'turn', status: 'complete', in_context: true,
|
|
28
27
|
model: null, usage: null, finish: null, error: null, created_at: '', completed_at: null,
|
|
29
|
-
producer: { harness: '', harness_version: '', adapter_version: '' }, blocks: [
|
|
28
|
+
producer: { harness: '', harness_version: '', adapter_version: '' }, blocks: [block],
|
|
30
29
|
}],
|
|
31
30
|
};
|
|
32
31
|
const { rows } = toV1({
|
|
@@ -34,13 +33,33 @@ export function toolPartOf(call) {
|
|
|
34
33
|
selection: { agent: null, model: null }, todos: [], pending: { questions: [], permissions: [] }, threads: [thread],
|
|
35
34
|
});
|
|
36
35
|
const part = rows[0].parts[0];
|
|
37
|
-
parts.set(
|
|
36
|
+
parts.set(block, part);
|
|
38
37
|
return part;
|
|
39
38
|
}
|
|
39
|
+
export function toolPartOf(call) {
|
|
40
|
+
return blockPartOf(call);
|
|
41
|
+
}
|
|
40
42
|
export const isToolCallBlock = (value) => typeof value === 'object' && value !== null && value.type === 'tool_call';
|
|
43
|
+
export const asToolPart = (value) => (isToolCallBlock(value) ? toolPartOf(value) : value);
|
|
41
44
|
export function toolCallMetadata(call) {
|
|
42
45
|
const metadata = toolPartOf(call).state.metadata;
|
|
43
46
|
return typeof metadata === 'object' && metadata !== null && !Array.isArray(metadata) && Object.keys(metadata).length
|
|
44
47
|
? metadata
|
|
45
48
|
: EMPTY;
|
|
46
49
|
}
|
|
50
|
+
const textOf = (call) => (call.result?.content ?? []).flatMap((c) => (c.type === 'text' ? [c.text] : [])).join('');
|
|
51
|
+
export function toolCallStatus(call) {
|
|
52
|
+
return call.status === 'complete' ? 'completed' : call.status;
|
|
53
|
+
}
|
|
54
|
+
const rawOutputs = new WeakMap();
|
|
55
|
+
export function toolCallRawOutput(call) {
|
|
56
|
+
if (call.status !== 'complete')
|
|
57
|
+
return '';
|
|
58
|
+
let text = rawOutputs.get(call);
|
|
59
|
+
if (text === undefined)
|
|
60
|
+
rawOutputs.set(call, (text = textOf(call)));
|
|
61
|
+
return text;
|
|
62
|
+
}
|
|
63
|
+
export function toolCallError(call) {
|
|
64
|
+
return call.status === 'error' ? call.result?.error?.message || textOf(call) : undefined;
|
|
65
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type ClassifiedTurn } from '../../turns/classify.js';
|
|
2
|
+
import { type BurstSummary } from '../../turns/segments/burst-summary.js';
|
|
3
|
+
import type { StepFamily } from '../../turns/segments/narration.js';
|
|
4
|
+
import { type SegmentTurnOptions } from '../../turns/segments/segment-turn.js';
|
|
5
|
+
import type { SessionLogBlock, SessionLogMessage, SessionLogThread, TextBlock, ToolCallBlock } from '../types.js';
|
|
6
|
+
import { type SessionLogTurn } from './turn.js';
|
|
7
|
+
export type SessionLogSegment = {
|
|
8
|
+
kind: 'burst';
|
|
9
|
+
blocks: SessionLogBlock[];
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'text';
|
|
12
|
+
block: TextBlock;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'standalone';
|
|
15
|
+
block: ToolCallBlock;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'show-group';
|
|
18
|
+
blocks: ToolCallBlock[];
|
|
19
|
+
};
|
|
20
|
+
export interface SessionLogTurnWorkSplit {
|
|
21
|
+
work: SessionLogSegment[];
|
|
22
|
+
hoisted: SessionLogSegment[];
|
|
23
|
+
final: SessionLogSegment[];
|
|
24
|
+
}
|
|
25
|
+
export type SessionLogBurstStep = {
|
|
26
|
+
kind: 'thought';
|
|
27
|
+
key: string;
|
|
28
|
+
texts: string[];
|
|
29
|
+
running: boolean;
|
|
30
|
+
durationMs?: number;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'block';
|
|
33
|
+
key: string;
|
|
34
|
+
block: SessionLogBlock;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'group';
|
|
37
|
+
key: string;
|
|
38
|
+
blocks: ToolCallBlock[];
|
|
39
|
+
family: StepFamily;
|
|
40
|
+
label: string;
|
|
41
|
+
status: 'running' | 'error' | 'done';
|
|
42
|
+
durationMs?: number;
|
|
43
|
+
};
|
|
44
|
+
export declare function sessionLogTurnSegments(blocks: ReadonlyArray<SessionLogBlock>, options?: SegmentTurnOptions): SessionLogSegment[];
|
|
45
|
+
export declare function splitSessionLogTurnWork(segments: ReadonlyArray<SessionLogSegment>, options: {
|
|
46
|
+
working: boolean;
|
|
47
|
+
}): SessionLogTurnWorkSplit | null;
|
|
48
|
+
export declare function sessionLogBurstSteps(blocks: ReadonlyArray<SessionLogBlock>): SessionLogBurstStep[];
|
|
49
|
+
export declare function sessionLogActivityText(turn: Pick<SessionLogTurn, 'blocks'>, childMessages?: readonly SessionLogMessage[]): string;
|
|
50
|
+
export declare function sessionLogBurstSummary(blocks: ReadonlyArray<SessionLogBlock>): BurstSummary;
|
|
51
|
+
export declare function sessionLogBurstWorkLabel(blocks: ReadonlyArray<SessionLogBlock>): string;
|
|
52
|
+
export declare function sessionLogBurstRunning(blocks: ReadonlyArray<SessionLogBlock>, options: {
|
|
53
|
+
working: boolean;
|
|
54
|
+
trailing?: boolean;
|
|
55
|
+
}): boolean;
|
|
56
|
+
export declare function sessionLogOpenCall(call: ToolCallBlock): ToolCallBlock;
|
|
57
|
+
export declare function sessionLogShowGroupCall(calls: ReadonlyArray<ToolCallBlock>): ToolCallBlock;
|
|
58
|
+
export declare function sessionLogAnsweredQuestionCall(call: ToolCallBlock, answers: string[][], input?: Record<string, unknown>): ToolCallBlock;
|
|
59
|
+
export declare function classifySessionLogMessage(message: SessionLogMessage): ClassifiedTurn;
|
|
60
|
+
export declare function subAgentCalls(thread: Pick<SessionLogThread, 'messages'> | null | undefined): readonly ToolCallBlock[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { classifyTurn, toToolView } from '../../turns/classify.js';
|
|
2
|
+
import { isReasoningPart, isToolPart, shouldShowToolPart } from '../../turns/parts.js';
|
|
3
|
+
import { burstSummary, burstWorkLabel } from '../../turns/segments/burst-summary.js';
|
|
4
|
+
import { mergeBurstSteps, reasoningIsRunning } from '../../turns/segments/merge-steps.js';
|
|
5
|
+
import { segmentTurn } from '../../turns/segments/segment-turn.js';
|
|
6
|
+
import { isQuestionTool } from '../../turns/segments/session-activity-groups.js';
|
|
7
|
+
import { groupShowSegments, showGroupItems } from '../../turns/segments/show-group.js';
|
|
8
|
+
import { stepLabel } from '../../turns/segments/step-label.js';
|
|
9
|
+
import { splitTurnWork } from '../../turns/segments/work-split.js';
|
|
10
|
+
import { getTurnStatus } from '../../turns/state.js';
|
|
11
|
+
import { parseQuestionAnswersFromOutput } from '../../turns/tools/question-helpers.js';
|
|
12
|
+
import { toolCallBlock, toolPartOf } from '../tool-call.js';
|
|
13
|
+
import { isClosedByRule } from './turn.js';
|
|
14
|
+
import { blockOf, messageOfBlock, partOf, partsOf, v1RowsOf } from './v1-parts.js';
|
|
15
|
+
const call = (p) => blockOf(p);
|
|
16
|
+
export function sessionLogTurnSegments(blocks, options = {}) {
|
|
17
|
+
return groupShowSegments(segmentTurn(partsOf(blocks), options), options).map((s) => s.kind === 'burst' ? { kind: s.kind, blocks: s.parts.map(blockOf) }
|
|
18
|
+
: s.kind === 'show-group' ? { kind: s.kind, blocks: s.parts.map(call) }
|
|
19
|
+
: s.kind === 'text' ? { kind: s.kind, block: blockOf(s.part) }
|
|
20
|
+
: { kind: s.kind, block: call(s.part) });
|
|
21
|
+
}
|
|
22
|
+
const v1Segment = (s) => s.kind === 'burst' || s.kind === 'show-group' ? { kind: s.kind, parts: partsOf(s.blocks) } : { kind: s.kind, part: partOf(s.block) };
|
|
23
|
+
export function splitSessionLogTurnWork(segments, options) {
|
|
24
|
+
const by = new Map(segments.map((s) => [v1Segment(s), s]));
|
|
25
|
+
const split = splitTurnWork([...by.keys()], options);
|
|
26
|
+
if (!split)
|
|
27
|
+
return null;
|
|
28
|
+
const back = (list) => list.map((s) => by.get(s));
|
|
29
|
+
return { work: back(split.work), hoisted: back(split.hoisted), final: back(split.final) };
|
|
30
|
+
}
|
|
31
|
+
function isAnswered(part) {
|
|
32
|
+
if (!isToolPart(part) || !isQuestionTool(part.tool))
|
|
33
|
+
return false;
|
|
34
|
+
const questions = part.state.input?.questions;
|
|
35
|
+
if (!Array.isArray(questions) || !questions.length)
|
|
36
|
+
return false;
|
|
37
|
+
const view = toToolView(part);
|
|
38
|
+
return (view.answers ?? parseQuestionAnswersFromOutput(view.outputText ?? '', questions.length) ?? []).length > 0;
|
|
39
|
+
}
|
|
40
|
+
export function sessionLogBurstSteps(blocks) {
|
|
41
|
+
const steps = mergeBurstSteps(partsOf(blocks), (p) => stepLabel(p).tier).flatMap((step) => step.kind === 'group' && step.step.parts.some(isAnswered) ? step.step.parts.map((part) => ({ kind: 'part', key: part.id, part })) : [step]);
|
|
42
|
+
return steps.map((s) => {
|
|
43
|
+
if (s.kind === 'part')
|
|
44
|
+
return { kind: 'block', key: s.key, block: blockOf(s.part) };
|
|
45
|
+
if (s.kind === 'group')
|
|
46
|
+
return { kind: 'group', key: s.key, blocks: s.step.parts.map(call), family: s.step.family, label: s.step.label, status: s.step.status, durationMs: s.step.durationMs };
|
|
47
|
+
const first = blocks.find((b) => b.id === s.key.slice('thought-'.length));
|
|
48
|
+
const message = first && messageOfBlock.get(first);
|
|
49
|
+
return { kind: 'thought', key: s.key, texts: s.texts, running: s.running && (!message || message.status === 'streaming'), durationMs: s.durationMs };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function sessionLogActivityText(turn, childMessages) {
|
|
53
|
+
return getTurnStatus(partsOf(turn.blocks).map((part) => ({ part })), childMessages?.flatMap(v1RowsOf));
|
|
54
|
+
}
|
|
55
|
+
export function sessionLogBurstSummary(blocks) {
|
|
56
|
+
return burstSummary(partsOf(blocks));
|
|
57
|
+
}
|
|
58
|
+
export function sessionLogBurstWorkLabel(blocks) {
|
|
59
|
+
return burstWorkLabel(partsOf(blocks));
|
|
60
|
+
}
|
|
61
|
+
export function sessionLogBurstRunning(blocks, options) {
|
|
62
|
+
if (!options.working)
|
|
63
|
+
return false;
|
|
64
|
+
if (options.trailing)
|
|
65
|
+
return true;
|
|
66
|
+
return blocks.some((block) => {
|
|
67
|
+
if (block.type === 'tool_call')
|
|
68
|
+
return block.status === 'pending' || block.status === 'running';
|
|
69
|
+
if (block.type !== 'reasoning')
|
|
70
|
+
return false;
|
|
71
|
+
const part = partOf(block);
|
|
72
|
+
const message = messageOfBlock.get(block);
|
|
73
|
+
return !!part && isReasoningPart(part) && reasoningIsRunning(part) && (!message || message.status === 'streaming');
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const reopened = new WeakMap();
|
|
77
|
+
export function sessionLogOpenCall(call) {
|
|
78
|
+
if (!isClosedByRule(call))
|
|
79
|
+
return call;
|
|
80
|
+
let open = reopened.get(call);
|
|
81
|
+
if (!open) {
|
|
82
|
+
const { result: _result, ...rest } = call;
|
|
83
|
+
open = { ...rest, status: call.started_at ? 'running' : 'pending' };
|
|
84
|
+
reopened.set(call, open);
|
|
85
|
+
}
|
|
86
|
+
return open;
|
|
87
|
+
}
|
|
88
|
+
function carouselItem(item) {
|
|
89
|
+
const { callID: _callID, error, ...rest } = item;
|
|
90
|
+
return item.status === 'error' ? { ...rest, type: 'error', content: error || 'This output failed to load.' } : rest;
|
|
91
|
+
}
|
|
92
|
+
export function sessionLogShowGroupCall(calls) {
|
|
93
|
+
const parts = calls.map(toolPartOf);
|
|
94
|
+
const first = parts[0];
|
|
95
|
+
const running = parts.some((p) => p.state.status === 'running' || p.state.status === 'pending');
|
|
96
|
+
const items = showGroupItems(parts).map(carouselItem);
|
|
97
|
+
return toolCallBlock({ ...first, state: { ...first.state, status: running ? 'running' : 'completed', input: { items } } });
|
|
98
|
+
}
|
|
99
|
+
export function sessionLogAnsweredQuestionCall(call, answers, input) {
|
|
100
|
+
const part = toolPartOf(call);
|
|
101
|
+
const state = part.state;
|
|
102
|
+
return toolCallBlock({
|
|
103
|
+
...part,
|
|
104
|
+
state: { ...state, status: 'completed', ...(input !== undefined && { input }), metadata: { ...(state.metadata ?? {}), answers } },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
export function classifySessionLogMessage(message) {
|
|
108
|
+
const rows = v1RowsOf(message).map((row) => classifyTurn(row));
|
|
109
|
+
const error = rows.find((row) => row.error)?.error;
|
|
110
|
+
return { parts: rows.flatMap((row) => row.parts), error, isEmpty: rows.every((row) => row.isEmpty) && !error };
|
|
111
|
+
}
|
|
112
|
+
const NO_CALLS = Object.freeze([]);
|
|
113
|
+
const threadCalls = new WeakMap();
|
|
114
|
+
export function subAgentCalls(thread) {
|
|
115
|
+
if (!thread)
|
|
116
|
+
return NO_CALLS;
|
|
117
|
+
let calls = threadCalls.get(thread);
|
|
118
|
+
if (!calls) {
|
|
119
|
+
const found = thread.messages.flatMap((m) => m.role === 'assistant' ? m.blocks.filter((b) => b.type === 'tool_call' && shouldShowToolPart(b)) : []);
|
|
120
|
+
calls = found.length ? found : NO_CALLS;
|
|
121
|
+
threadCalls.set(thread, calls);
|
|
122
|
+
}
|
|
123
|
+
return calls;
|
|
124
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SessionLog, SessionLogMessage, SessionLogThread } from '../types.js';
|
|
2
|
+
import type { SessionLogTurn } from './turn.js';
|
|
3
|
+
export declare function isSessionLogRestoreNote(message: Pick<SessionLogMessage, 'role' | 'blocks'>): boolean;
|
|
4
|
+
export interface SessionLogHarnessSwitch {
|
|
5
|
+
harness: string;
|
|
6
|
+
previous: string;
|
|
7
|
+
version: string;
|
|
8
|
+
at: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function sessionLogHarnessSwitches(history: SessionLog['harness']['history'] | undefined, turns: ReadonlyArray<Pick<SessionLogTurn, 'id'> & {
|
|
11
|
+
user: Pick<SessionLogMessage, 'seq'>;
|
|
12
|
+
}>): Map<string, SessionLogHarnessSwitch>;
|
|
13
|
+
export interface SessionLogRestoreNotice {
|
|
14
|
+
generation: number;
|
|
15
|
+
missing: Array<{
|
|
16
|
+
kind: 'message' | 'attachment' | 'text' | 'tool_input' | 'tool_result' | 'cut_point' | 'other';
|
|
17
|
+
count: number;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export declare function sessionLogRestoreNotice(session: Partial<Pick<SessionLog, 'restore_grade' | 'grade_counts' | 'ext'>> | null | undefined): SessionLogRestoreNotice | null;
|
|
21
|
+
export interface SessionLogReferencedText {
|
|
22
|
+
ref: string;
|
|
23
|
+
bytes: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function sessionLogReferencedTexts(messages: readonly SessionLogMessage[]): SessionLogReferencedText[];
|
|
26
|
+
export declare const SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES: number;
|
|
27
|
+
export declare function sessionLogFullTextAction(refs: readonly SessionLogReferencedText[]): 'inline' | 'download';
|
|
28
|
+
export declare function sessionLogFullTextPlan(refs: readonly SessionLogReferencedText[]): {
|
|
29
|
+
inline: SessionLogReferencedText[];
|
|
30
|
+
download: SessionLogReferencedText[];
|
|
31
|
+
};
|
|
32
|
+
export declare function sessionLogWithFullTexts(thread: SessionLogThread, texts: ReadonlyMap<string, string>): SessionLogThread;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const RESTORE_NOTE = 'prt_beeuni_restore_g';
|
|
2
|
+
export function isSessionLogRestoreNote(message) {
|
|
3
|
+
return message.role === 'user' && message.blocks.some((b) => b.type === 'text' && b.id.startsWith(RESTORE_NOTE));
|
|
4
|
+
}
|
|
5
|
+
export function sessionLogHarnessSwitches(history, turns) {
|
|
6
|
+
const out = new Map();
|
|
7
|
+
const entries = [...(history ?? [])].sort((a, b) => a.from_seq - b.from_seq);
|
|
8
|
+
for (let i = 1; i < entries.length; i++) {
|
|
9
|
+
const entry = entries[i];
|
|
10
|
+
const previous = entries[i - 1].harness;
|
|
11
|
+
if (entry.harness === previous)
|
|
12
|
+
continue;
|
|
13
|
+
const at = turns.findIndex((t) => t.user.seq >= entry.from_seq);
|
|
14
|
+
if (at < 0 || (at === 0 && turns[0].user.seq !== entry.from_seq))
|
|
15
|
+
continue;
|
|
16
|
+
out.set(turns[at].id, { harness: entry.harness, previous, version: entry.version, at: entry.at });
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
21
|
+
const count = (v) => (typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : 0);
|
|
22
|
+
const COUNTED = new Set(['message', 'attachment', 'tool_input', 'cut_point', 'layout', 'text', 'tool_result']);
|
|
23
|
+
export function sessionLogRestoreNotice(session) {
|
|
24
|
+
if (session?.restore_grade !== 'partial')
|
|
25
|
+
return null;
|
|
26
|
+
const data = session.ext?.beeuni?.data;
|
|
27
|
+
const restore = isObj(data) ? data.restore : undefined;
|
|
28
|
+
if (!isObj(restore))
|
|
29
|
+
return null;
|
|
30
|
+
const dropped = isObj(restore.dropped) ? restore.dropped : {};
|
|
31
|
+
const grade = session.grade_counts;
|
|
32
|
+
const missing = [
|
|
33
|
+
{ kind: 'message', count: count(dropped.message) },
|
|
34
|
+
{ kind: 'attachment', count: count(grade?.attachment) },
|
|
35
|
+
{ kind: 'text', count: count(dropped.text) + count(restore.truncated) },
|
|
36
|
+
{ kind: 'tool_input', count: count(grade?.tool_input) },
|
|
37
|
+
{ kind: 'tool_result', count: count(dropped.tool_result) },
|
|
38
|
+
{ kind: 'cut_point', count: count(grade?.cut_point) },
|
|
39
|
+
{ kind: 'other', count: Object.entries(dropped).reduce((n, [k, v]) => (COUNTED.has(k) ? n : n + count(v)), 0) },
|
|
40
|
+
];
|
|
41
|
+
return { generation: count(restore.generation), missing: missing.filter((m) => m.count > 0) };
|
|
42
|
+
}
|
|
43
|
+
const textsOf = (b) => b.type === 'text'
|
|
44
|
+
? [b]
|
|
45
|
+
: b.type === 'tool_call'
|
|
46
|
+
? (b.result?.content ?? []).filter((c) => c.type === 'text')
|
|
47
|
+
: [];
|
|
48
|
+
export function sessionLogReferencedTexts(messages) {
|
|
49
|
+
const out = new Map();
|
|
50
|
+
for (const m of messages)
|
|
51
|
+
for (const b of m.blocks)
|
|
52
|
+
for (const t of textsOf(b))
|
|
53
|
+
if (t.ref && t.bytes !== undefined && !out.has(t.ref))
|
|
54
|
+
out.set(t.ref, t.bytes);
|
|
55
|
+
return [...out].map(([ref, bytes]) => ({ ref, bytes }));
|
|
56
|
+
}
|
|
57
|
+
const swap = (item, texts) => {
|
|
58
|
+
const text = item.ref === undefined ? undefined : texts.get(item.ref);
|
|
59
|
+
if (text === undefined)
|
|
60
|
+
return item;
|
|
61
|
+
const { ref: _ref, bytes: _bytes, ...rest } = item;
|
|
62
|
+
return { ...rest, text };
|
|
63
|
+
};
|
|
64
|
+
const swapAll = (items, texts) => items.map((c) => (c.type === 'text' ? swap(c, texts) : c));
|
|
65
|
+
const filled = new WeakMap();
|
|
66
|
+
function withTexts(m, texts) {
|
|
67
|
+
const cached = filled.get(m);
|
|
68
|
+
if (cached?.texts === texts)
|
|
69
|
+
return cached.out;
|
|
70
|
+
const out = !m.blocks.some((b) => textsOf(b).some((t) => t.ref !== undefined && texts.has(t.ref)))
|
|
71
|
+
? m
|
|
72
|
+
: {
|
|
73
|
+
...m,
|
|
74
|
+
blocks: m.blocks.map((b) => b.type === 'text' ? swap(b, texts) : b.type === 'tool_call' && b.result ? { ...b, result: { ...b.result, content: swapAll(b.result.content, texts) } } : b),
|
|
75
|
+
};
|
|
76
|
+
filled.set(m, { texts, out });
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
export const SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES = 1024 * 1024;
|
|
80
|
+
export function sessionLogFullTextAction(refs) {
|
|
81
|
+
return refs.reduce((n, r) => n + r.bytes, 0) > SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES ? 'download' : 'inline';
|
|
82
|
+
}
|
|
83
|
+
export function sessionLogFullTextPlan(refs) {
|
|
84
|
+
const fits = new Set();
|
|
85
|
+
let total = 0;
|
|
86
|
+
for (const r of [...refs].sort((a, b) => a.bytes - b.bytes)) {
|
|
87
|
+
if (total + r.bytes > SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES)
|
|
88
|
+
break;
|
|
89
|
+
total += r.bytes;
|
|
90
|
+
fits.add(r);
|
|
91
|
+
}
|
|
92
|
+
return { inline: refs.filter((r) => fits.has(r)), download: refs.filter((r) => !fits.has(r)) };
|
|
93
|
+
}
|
|
94
|
+
export function sessionLogWithFullTexts(thread, texts) {
|
|
95
|
+
if (texts.size === 0)
|
|
96
|
+
return thread;
|
|
97
|
+
const messages = thread.messages.map((m) => withTexts(m, texts));
|
|
98
|
+
return messages.every((m, i) => m === thread.messages[i]) ? thread : { ...thread, messages };
|
|
99
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { classifySessionLogMessage, sessionLogActivityText, sessionLogAnsweredQuestionCall, sessionLogBurstRunning, sessionLogBurstSteps, sessionLogBurstSummary, sessionLogBurstWorkLabel, sessionLogOpenCall, sessionLogShowGroupCall, sessionLogTurnSegments, splitSessionLogTurnWork, subAgentCalls, type SessionLogBurstStep, type SessionLogSegment, type SessionLogTurnWorkSplit, } from './blocks.js';
|
|
2
|
+
export { sessionLogTurns, sessionLogCallStatus, sessionLogPromptMessage, sessionLogRetrying, type SessionLogTurnsOptions, type SessionLogCallStatus, type SessionLogCompaction, type SessionLogContinuation, type SessionLogTurn, type SessionLogTurnError, type SessionLogTurnStatus, } from './turn.js';
|
|
3
|
+
export { sessionLogThreadMetrics, type SessionLogThreadMetrics } from './metrics.js';
|
|
4
|
+
export { SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES, isSessionLogRestoreNote, sessionLogFullTextAction, sessionLogFullTextPlan, sessionLogHarnessSwitches, sessionLogReferencedTexts, sessionLogRestoreNotice, sessionLogWithFullTexts, type SessionLogHarnessSwitch, type SessionLogReferencedText, type SessionLogRestoreNotice, } from './history.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { classifySessionLogMessage, sessionLogActivityText, sessionLogAnsweredQuestionCall, sessionLogBurstRunning, sessionLogBurstSteps, sessionLogBurstSummary, sessionLogBurstWorkLabel, sessionLogOpenCall, sessionLogShowGroupCall, sessionLogTurnSegments, splitSessionLogTurnWork, subAgentCalls, } from './blocks.js';
|
|
2
|
+
export { sessionLogTurns, sessionLogCallStatus, sessionLogPromptMessage, sessionLogRetrying, } from './turn.js';
|
|
3
|
+
export { sessionLogThreadMetrics } from './metrics.js';
|
|
4
|
+
export { SESSION_LOG_FULL_TEXT_INLINE_MAX_BYTES, isSessionLogRestoreNote, sessionLogFullTextAction, sessionLogFullTextPlan, sessionLogHarnessSwitches, sessionLogReferencedTexts, sessionLogRestoreNotice, sessionLogWithFullTexts, } from './history.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ModelPricingLookup } from '../../turns/types.js';
|
|
2
|
+
import type { SessionLogMessage, SessionLogThread } from '../types.js';
|
|
3
|
+
export interface SessionLogThreadMetrics {
|
|
4
|
+
cost: null;
|
|
5
|
+
messages: {
|
|
6
|
+
all: number;
|
|
7
|
+
user: number;
|
|
8
|
+
assistant: number;
|
|
9
|
+
};
|
|
10
|
+
tokens: {
|
|
11
|
+
input: number;
|
|
12
|
+
output: number;
|
|
13
|
+
reasoning: number;
|
|
14
|
+
cacheRead: number;
|
|
15
|
+
cacheWrite: number;
|
|
16
|
+
};
|
|
17
|
+
context: SessionLogMessage | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function sessionLogThreadMetrics(thread: SessionLogThread, options?: {
|
|
20
|
+
pricing?: ModelPricingLookup;
|
|
21
|
+
}): SessionLogThreadMetrics;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sessionLogTurns, usageCostOf } from './turn.js';
|
|
2
|
+
export function sessionLogThreadMetrics(thread, options = {}) {
|
|
3
|
+
const m = {
|
|
4
|
+
cost: null,
|
|
5
|
+
messages: { all: 0, user: 0, assistant: 0 },
|
|
6
|
+
tokens: { input: 0, output: 0, reasoning: 0, cacheRead: 0, cacheWrite: 0 },
|
|
7
|
+
context: null,
|
|
8
|
+
};
|
|
9
|
+
for (const turn of sessionLogTurns(thread)) {
|
|
10
|
+
const cost = usageCostOf([turn.user, ...turn.assistant], options.pricing);
|
|
11
|
+
if (!cost)
|
|
12
|
+
continue;
|
|
13
|
+
for (const k of Object.keys(m.tokens))
|
|
14
|
+
m.tokens[k] += cost.tokens[k];
|
|
15
|
+
}
|
|
16
|
+
for (const msg of thread.messages) {
|
|
17
|
+
if (msg.role === 'user')
|
|
18
|
+
m.messages.user++;
|
|
19
|
+
else if (msg.role === 'assistant')
|
|
20
|
+
m.messages.assistant++;
|
|
21
|
+
if (msg.kind === 'compaction' && msg.status !== 'streaming')
|
|
22
|
+
m.messages.assistant++;
|
|
23
|
+
const u = msg.usage;
|
|
24
|
+
if ((msg.role === 'assistant' || msg.kind === 'compaction') && u && u.input + u.output + (u.reasoning ?? 0) + u.cache_read + u.cache_write > 0)
|
|
25
|
+
m.context = msg;
|
|
26
|
+
}
|
|
27
|
+
m.messages.all = m.messages.user + m.messages.assistant;
|
|
28
|
+
return m;
|
|
29
|
+
}
|