@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,55 @@
|
|
|
1
|
+
import type { SessionPrompt } from '../../rest/projects-client/sessions.js';
|
|
2
|
+
import { type GatewayErrorDetails } from '../../turns/errors.js';
|
|
3
|
+
import type { ModelPricingLookup, TurnCostInfo } from '../../turns/types.js';
|
|
4
|
+
import { type AttachmentBlock, type CompactionBlock, type SessionLogBlock, type SessionLogMessage, type SessionLogThread, type TextBlock, type ToolCallBlock } from '../types.js';
|
|
5
|
+
export type SessionLogTurnStatus = 'pending' | SessionLogMessage['status'];
|
|
6
|
+
export type SessionLogCallStatus = ToolCallBlock['status'] | 'interrupted';
|
|
7
|
+
export interface SessionLogTurnError {
|
|
8
|
+
code: string;
|
|
9
|
+
message: string;
|
|
10
|
+
details?: GatewayErrorDetails;
|
|
11
|
+
raw?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface SessionLogCompaction {
|
|
14
|
+
block: CompactionBlock | null;
|
|
15
|
+
hasContent: boolean;
|
|
16
|
+
inFlight: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface SessionLogContinuation {
|
|
19
|
+
marker: string;
|
|
20
|
+
prompt: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface SessionLogTurn {
|
|
23
|
+
id: string;
|
|
24
|
+
user: SessionLogMessage;
|
|
25
|
+
assistant: SessionLogMessage[];
|
|
26
|
+
partial: boolean;
|
|
27
|
+
continues: SessionLogContinuation | null;
|
|
28
|
+
blocks: SessionLogBlock[];
|
|
29
|
+
status: SessionLogTurnStatus;
|
|
30
|
+
error: SessionLogTurnError | null;
|
|
31
|
+
cost: TurnCostInfo | null;
|
|
32
|
+
compaction: SessionLogCompaction | null;
|
|
33
|
+
threads: string[];
|
|
34
|
+
plan: boolean;
|
|
35
|
+
shell: ToolCallBlock | null;
|
|
36
|
+
response: TextBlock | null;
|
|
37
|
+
attachments: AttachmentBlock[];
|
|
38
|
+
live: boolean;
|
|
39
|
+
queued: boolean;
|
|
40
|
+
retrying: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface SessionLogTurnsOptions {
|
|
43
|
+
busy?: boolean;
|
|
44
|
+
liveMessageId?: string | null;
|
|
45
|
+
held?: readonly string[];
|
|
46
|
+
threads?: ReadonlyArray<Pick<SessionLogThread, 'thread_id' | 'spawned_by'>>;
|
|
47
|
+
pricing?: ModelPricingLookup;
|
|
48
|
+
hideOutOfContext?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export declare const isClosedByRule: (b: ToolCallBlock) => boolean;
|
|
51
|
+
export declare function sessionLogCallStatus(call: ToolCallBlock, live: boolean): SessionLogCallStatus;
|
|
52
|
+
export declare function sessionLogPromptMessage(prompt: SessionPrompt, threadId: string): SessionLogMessage;
|
|
53
|
+
export declare function usageCostOf(replies: SessionLogMessage[], lookup?: ModelPricingLookup): TurnCostInfo | null;
|
|
54
|
+
export declare function sessionLogRetrying(thread: Pick<SessionLogThread, 'messages'>): boolean;
|
|
55
|
+
export declare function sessionLogTurns(source: SessionLogThread, options?: SessionLogTurnsOptions): SessionLogTurn[];
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { extractGatewayErrorDetails, rawErrorText, unwrapError } from '../../turns/errors.js';
|
|
2
|
+
import { hasRetryingAssistantTurn } from '../../turns/open-turn.js';
|
|
3
|
+
import { getChildSessionId, shouldShowToolPart } from '../../turns/parts.js';
|
|
4
|
+
import { resolveWorkingTurn } from '../../turns/segments/working-turn.js';
|
|
5
|
+
import { getTurnTokens } from '../../turns/state.js';
|
|
6
|
+
import { toolKind } from '../../turns/tool-kind.js';
|
|
7
|
+
import { SESSION_LOG_MINOR, SESSION_LOG_SCHEMA, } from '../types.js';
|
|
8
|
+
import { isV1Thread } from '../v1-view.js';
|
|
9
|
+
import { messageOfBlock, v1RowsOf } from './v1-parts.js';
|
|
10
|
+
const V1_IMPORT = 'beeuni-v1-import';
|
|
11
|
+
function getTurnCost(parts, _lookup) {
|
|
12
|
+
const tokens = getTurnTokens(parts);
|
|
13
|
+
return tokens ? { cost: null, tokens } : undefined;
|
|
14
|
+
}
|
|
15
|
+
const V = { schema: SESSION_LOG_SCHEMA, v: SESSION_LOG_MINOR };
|
|
16
|
+
function last(list, test) {
|
|
17
|
+
for (let i = list.length - 1; i >= 0; i--)
|
|
18
|
+
if (test(list[i]))
|
|
19
|
+
return list[i];
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const isCall = (b) => b.type === 'tool_call';
|
|
23
|
+
const isOpen = (b) => b.status === 'pending' || b.status === 'running';
|
|
24
|
+
export const isClosedByRule = (b) => b.status === 'error' && (b.result?.synthetic === true || b.result?.error?.code === 'interrupted');
|
|
25
|
+
export function sessionLogCallStatus(call, live) {
|
|
26
|
+
const closed = isClosedByRule(call);
|
|
27
|
+
if (!isOpen(call) && !closed)
|
|
28
|
+
return call.status;
|
|
29
|
+
if (!live)
|
|
30
|
+
return 'interrupted';
|
|
31
|
+
return call.status === 'running' || (closed && call.started_at) ? 'running' : 'pending';
|
|
32
|
+
}
|
|
33
|
+
const prompts = new WeakMap();
|
|
34
|
+
export function sessionLogPromptMessage(prompt, threadId) {
|
|
35
|
+
const cached = prompts.get(prompt);
|
|
36
|
+
if (cached)
|
|
37
|
+
return cached;
|
|
38
|
+
const ms = typeof prompt.client_sent_at_ms === 'number' ? prompt.client_sent_at_ms : Date.parse(prompt.created_at);
|
|
39
|
+
const message = {
|
|
40
|
+
...V, message_id: prompt.message_id || `queued-${prompt.prompt_id}`, thread_id: threadId, seq: Number.MAX_SAFE_INTEGER, role: 'user', kind: 'turn',
|
|
41
|
+
status: 'complete', in_context: true, origin: 'prompt', model: null, usage: null, finish: null, error: null,
|
|
42
|
+
created_at: Number.isFinite(ms) ? new Date(ms).toISOString() : '', completed_at: null,
|
|
43
|
+
producer: { harness: 'beeuni-inbox', harness_version: '', adapter_version: '1' },
|
|
44
|
+
blocks: [{ type: 'text', id: `syn-${prompt.prompt_id}`, text: prompt.full_text ?? prompt.text }],
|
|
45
|
+
};
|
|
46
|
+
prompts.set(prompt, message);
|
|
47
|
+
return message;
|
|
48
|
+
}
|
|
49
|
+
const standIns = new WeakMap();
|
|
50
|
+
function standInFor(reply) {
|
|
51
|
+
let m = standIns.get(reply);
|
|
52
|
+
if (!m) {
|
|
53
|
+
m = {
|
|
54
|
+
...V, message_id: reply.reply_to, thread_id: reply.thread_id, seq: reply.seq, role: 'user', kind: 'turn', status: 'complete', in_context: true,
|
|
55
|
+
model: null, usage: null, finish: null, error: null, created_at: reply.created_at, completed_at: null, producer: reply.producer, blocks: [],
|
|
56
|
+
};
|
|
57
|
+
standIns.set(reply, m);
|
|
58
|
+
}
|
|
59
|
+
return m;
|
|
60
|
+
}
|
|
61
|
+
const continuations = new WeakMap();
|
|
62
|
+
function continuationOf(marker) {
|
|
63
|
+
let m = continuations.get(marker);
|
|
64
|
+
if (!m) {
|
|
65
|
+
m = {
|
|
66
|
+
...V, message_id: `${marker.message_id}:continued`, thread_id: marker.thread_id, seq: marker.seq, role: 'user', kind: 'turn', status: 'complete', in_context: true,
|
|
67
|
+
model: null, usage: null, finish: null, error: null, created_at: marker.created_at, completed_at: null, producer: marker.producer, blocks: [],
|
|
68
|
+
};
|
|
69
|
+
continuations.set(marker, m);
|
|
70
|
+
}
|
|
71
|
+
return m;
|
|
72
|
+
}
|
|
73
|
+
const isEmptyShell = (m) => !m.error && m.blocks.length === 0 && !(m.usage && (m.usage.input || m.usage.output || m.usage.reasoning));
|
|
74
|
+
function drafts(messages, held) {
|
|
75
|
+
const turns = [];
|
|
76
|
+
const byId = new Map();
|
|
77
|
+
for (const m of messages) {
|
|
78
|
+
if (m.role !== 'user' || m.kind === 'context' || byId.has(m.message_id))
|
|
79
|
+
continue;
|
|
80
|
+
const d = { user: m, assistant: [], partial: false };
|
|
81
|
+
byId.set(m.message_id, d);
|
|
82
|
+
turns.push(d);
|
|
83
|
+
}
|
|
84
|
+
const partials = [];
|
|
85
|
+
const leading = [];
|
|
86
|
+
const order = new Map();
|
|
87
|
+
const continued = new Map();
|
|
88
|
+
let last = null;
|
|
89
|
+
let compaction = null;
|
|
90
|
+
for (const m of messages) {
|
|
91
|
+
if (m.role === 'user') {
|
|
92
|
+
const d = byId.get(m.message_id);
|
|
93
|
+
if (d && !held.has(m.message_id)) {
|
|
94
|
+
last = d;
|
|
95
|
+
if (!order.has(d))
|
|
96
|
+
order.set(d, order.size);
|
|
97
|
+
if (d.user.kind === 'compaction')
|
|
98
|
+
compaction = d;
|
|
99
|
+
}
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (m.role !== 'assistant' || isEmptyShell(m))
|
|
103
|
+
continue;
|
|
104
|
+
const named = m.reply_to ? byId.get(m.reply_to) : undefined;
|
|
105
|
+
let home = named ?? last;
|
|
106
|
+
if (compaction && home && (home === compaction || (order.get(home) ?? Infinity) < order.get(compaction))) {
|
|
107
|
+
let next = continued.get(compaction);
|
|
108
|
+
if (!next) {
|
|
109
|
+
next = { user: continuationOf(compaction.user), assistant: [], partial: true, continues: { marker: compaction.user.message_id, prompt: home === compaction ? null : home.user.message_id } };
|
|
110
|
+
continued.set(compaction, next);
|
|
111
|
+
}
|
|
112
|
+
home = next;
|
|
113
|
+
}
|
|
114
|
+
if (home) {
|
|
115
|
+
home.assistant.push(m);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (!m.reply_to) {
|
|
119
|
+
leading.push(m);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const current = partials[partials.length - 1];
|
|
123
|
+
if (current && current.user.message_id === m.reply_to)
|
|
124
|
+
current.assistant.push(m);
|
|
125
|
+
else
|
|
126
|
+
partials.push({ user: standInFor(m), assistant: [m], partial: true });
|
|
127
|
+
}
|
|
128
|
+
if (leading.length) {
|
|
129
|
+
const first = turns[0] ?? partials[0];
|
|
130
|
+
if (first)
|
|
131
|
+
first.assistant.unshift(...leading);
|
|
132
|
+
else
|
|
133
|
+
turns.push({ user: leading[0], assistant: leading.slice(1), partial: false });
|
|
134
|
+
}
|
|
135
|
+
const shown = turns.filter((t) => !held.has(t.user.message_id)).flatMap((t) => {
|
|
136
|
+
const next = continued.get(t);
|
|
137
|
+
return next ? [t, next] : [t];
|
|
138
|
+
});
|
|
139
|
+
return [...partials, ...shown, ...[...held].flatMap((id) => byId.get(id) ?? [])];
|
|
140
|
+
}
|
|
141
|
+
function continuedHint(list, hint) {
|
|
142
|
+
if (!hint)
|
|
143
|
+
return hint;
|
|
144
|
+
let out = hint;
|
|
145
|
+
for (const d of list) {
|
|
146
|
+
const c = d.continues;
|
|
147
|
+
if (c && (out === c.marker || out === c.prompt || hint === c.prompt))
|
|
148
|
+
out = d.user.message_id;
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
function errorOf(d) {
|
|
153
|
+
const sources = d.user.kind === 'compaction' ? [d.user, ...d.assistant] : d.assistant;
|
|
154
|
+
const m = sources.find((x) => x.error);
|
|
155
|
+
if (!m?.error)
|
|
156
|
+
return null;
|
|
157
|
+
const error = v1RowsOf(m)[0]?.info?.error ?? m.error.message;
|
|
158
|
+
const details = extractGatewayErrorDetails(error);
|
|
159
|
+
const raw = rawErrorText(error);
|
|
160
|
+
return { code: m.error.code, message: details?.message || unwrapError(error), ...(details && { details }), ...(raw !== undefined && { raw }) };
|
|
161
|
+
}
|
|
162
|
+
function costOf(replies, exact, lookup) {
|
|
163
|
+
if (replies.length && replies.every(exact)) {
|
|
164
|
+
return getTurnCost(replies.flatMap((m) => v1RowsOf(m).flatMap((row) => row.parts.map((part) => ({ part, message: row })))), lookup) ?? null;
|
|
165
|
+
}
|
|
166
|
+
return usageCostOf(replies, lookup);
|
|
167
|
+
}
|
|
168
|
+
export function usageCostOf(replies, lookup) {
|
|
169
|
+
const parts = replies.flatMap((m) => {
|
|
170
|
+
const u = m.usage;
|
|
171
|
+
if (!u)
|
|
172
|
+
return [];
|
|
173
|
+
const part = { type: 'step-finish', cost: u.cost ?? 0, tokens: { input: u.input, output: u.output, reasoning: u.reasoning ?? 0, cache: { read: u.cache_read, write: u.cache_write } } };
|
|
174
|
+
return [{ part, message: { info: { id: m.message_id, role: 'assistant', providerID: m.model?.provider, modelID: m.model?.model }, parts: [part] } }];
|
|
175
|
+
});
|
|
176
|
+
return getTurnCost(parts, lookup) ?? null;
|
|
177
|
+
}
|
|
178
|
+
function compactionOf(user) {
|
|
179
|
+
if (user.kind !== 'compaction')
|
|
180
|
+
return null;
|
|
181
|
+
const block = user.blocks.find((b) => b.type === 'compaction') ?? null;
|
|
182
|
+
return { block, hasContent: !!block?.summary?.trim(), inFlight: user.status === 'streaming' };
|
|
183
|
+
}
|
|
184
|
+
function shellOf(d) {
|
|
185
|
+
const prompt = d.user.blocks;
|
|
186
|
+
if (!prompt.length || !prompt.every((b) => b.type === 'text' && b.synthetic))
|
|
187
|
+
return null;
|
|
188
|
+
const only = d.assistant.length === 1 && d.assistant[0].blocks.length === 1 ? d.assistant[0].blocks[0] : null;
|
|
189
|
+
return only && isCall(only) && only.name === 'bash' ? only : null;
|
|
190
|
+
}
|
|
191
|
+
function threadsOf(d, blocks, heads, known) {
|
|
192
|
+
if (!heads.length)
|
|
193
|
+
return [];
|
|
194
|
+
const replies = new Set(d.assistant.map((m) => m.message_id));
|
|
195
|
+
const out = new Set();
|
|
196
|
+
for (const h of heads)
|
|
197
|
+
if (h.spawned_by && replies.has(h.spawned_by.message_id))
|
|
198
|
+
out.add(h.thread_id);
|
|
199
|
+
for (const b of blocks) {
|
|
200
|
+
const id = b.type === 'subtask' ? b.thread_id : isCall(b) ? getChildSessionId(b) : undefined;
|
|
201
|
+
if (id && known.has(id))
|
|
202
|
+
out.add(id);
|
|
203
|
+
}
|
|
204
|
+
return [...out];
|
|
205
|
+
}
|
|
206
|
+
const writesPlan = (d) => d.assistant.some((m) => m.blocks.some((b) => isCall(b) && toolKind(b.name) === 'todowrite' && shouldShowToolPart(b)));
|
|
207
|
+
const cache = new WeakMap();
|
|
208
|
+
const NONE = [];
|
|
209
|
+
export function sessionLogRetrying(thread) {
|
|
210
|
+
const reply = last(thread.messages, (m) => m.role === 'assistant');
|
|
211
|
+
return !!reply && hasRetryingAssistantTurn(v1RowsOf(reply));
|
|
212
|
+
}
|
|
213
|
+
const inContext = new WeakMap();
|
|
214
|
+
function inContextOnly(thread) {
|
|
215
|
+
let out = inContext.get(thread);
|
|
216
|
+
if (!out) {
|
|
217
|
+
const messages = thread.messages.filter((m) => m.in_context || m.status === 'streaming');
|
|
218
|
+
out = messages.length === thread.messages.length ? thread : { ...thread, messages };
|
|
219
|
+
inContext.set(thread, out);
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
export function sessionLogTurns(source, options = {}) {
|
|
224
|
+
const thread = options.hideOutOfContext ? inContextOnly(source) : source;
|
|
225
|
+
const held = new Set(options.held ?? NONE);
|
|
226
|
+
const list = drafts(thread.messages, held);
|
|
227
|
+
const lastReply = last(thread.messages, (m) => m.role === 'assistant');
|
|
228
|
+
const retrying = sessionLogRetrying(thread);
|
|
229
|
+
const working = resolveWorkingTurn({
|
|
230
|
+
turns: list.map((d) => ({
|
|
231
|
+
userMessage: { info: { id: d.user.message_id } },
|
|
232
|
+
assistantMessages: d.assistant.map((m) => ({ info: { time: m.status === 'streaming' ? {} : { completed: 1 }, error: m.error ?? undefined } })),
|
|
233
|
+
})),
|
|
234
|
+
hintMessageId: continuedHint(list, options.liveMessageId),
|
|
235
|
+
unrunTurnIds: held,
|
|
236
|
+
});
|
|
237
|
+
const anchor = last(list, writesPlan) ?? list[list.length - 1];
|
|
238
|
+
const v1Thread = isV1Thread(thread);
|
|
239
|
+
const exact = (m) => v1Thread || m.producer.harness === V1_IMPORT;
|
|
240
|
+
const heads = options.threads ?? [];
|
|
241
|
+
const known = new Set(heads.map((h) => h.thread_id));
|
|
242
|
+
return list.map((d) => {
|
|
243
|
+
const live = options.busy === true && working.workingTurnId === d.user.message_id;
|
|
244
|
+
const queued = working.pendingTurnIds.includes(d.user.message_id);
|
|
245
|
+
const retries = retrying && d.assistant.includes(lastReply);
|
|
246
|
+
const key = `${d.partial}|${live}|${queued}|${d === anchor}|${retries}`;
|
|
247
|
+
const prev = cache.get(d.user);
|
|
248
|
+
if (prev && prev.key === key && prev.pricing === options.pricing && prev.heads === options.threads && prev.assistant.length === d.assistant.length && prev.assistant.every((m, i) => m === d.assistant[i]))
|
|
249
|
+
return prev.turn;
|
|
250
|
+
const blocks = d.assistant.flatMap((m) => m.blocks);
|
|
251
|
+
for (const m of d.assistant)
|
|
252
|
+
for (const b of m.blocks)
|
|
253
|
+
messageOfBlock.set(b, m);
|
|
254
|
+
const threads = threadsOf(d, blocks, heads, known);
|
|
255
|
+
const reply = d.assistant[d.assistant.length - 1];
|
|
256
|
+
const turn = {
|
|
257
|
+
id: d.user.message_id,
|
|
258
|
+
user: d.user,
|
|
259
|
+
assistant: d.assistant,
|
|
260
|
+
partial: d.partial,
|
|
261
|
+
continues: d.continues ?? null,
|
|
262
|
+
blocks,
|
|
263
|
+
status: retries ? 'streaming' : reply ? reply.status : d.user.kind === 'compaction' ? d.user.status : 'pending',
|
|
264
|
+
error: errorOf(d),
|
|
265
|
+
cost: costOf([d.user, ...d.assistant], exact, options.pricing),
|
|
266
|
+
compaction: compactionOf(d.user),
|
|
267
|
+
threads,
|
|
268
|
+
plan: d === anchor,
|
|
269
|
+
shell: shellOf(d),
|
|
270
|
+
response: last(blocks, (b) => b.type === 'text' && !!b.text.trim()) ?? null,
|
|
271
|
+
attachments: d.user.blocks.filter((b) => b.type === 'attachment' && (b.mime.startsWith('image/') || b.mime === 'application/pdf')),
|
|
272
|
+
live,
|
|
273
|
+
queued,
|
|
274
|
+
retrying: retries,
|
|
275
|
+
};
|
|
276
|
+
cache.set(d.user, { assistant: d.assistant, key, pricing: options.pricing, heads: options.threads, turn });
|
|
277
|
+
return turn;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Part } from '../../runtime/runtime-types.js';
|
|
2
|
+
import { type SessionLogBlock, type SessionLogMessage } from '../types.js';
|
|
3
|
+
import { type V1MessageRow } from '../v1-view.js';
|
|
4
|
+
export declare const messageOfBlock: WeakMap<SessionLogBlock, SessionLogMessage>;
|
|
5
|
+
export declare function partOf(block: SessionLogBlock): Part | undefined;
|
|
6
|
+
export declare const blockOf: (part: object) => SessionLogBlock;
|
|
7
|
+
export declare const partsOf: (list: ReadonlyArray<SessionLogBlock>) => Part[];
|
|
8
|
+
export declare function v1RowsOf(m: SessionLogMessage): V1MessageRow[];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { blockPartOf } from '../tool-call.js';
|
|
2
|
+
import { SESSION_LOG_MINOR, SESSION_LOG_SCHEMA } from '../types.js';
|
|
3
|
+
import { rowsOf } from '../v1-view.js';
|
|
4
|
+
const blocks = new WeakMap();
|
|
5
|
+
export const messageOfBlock = new WeakMap();
|
|
6
|
+
const fallbacks = new WeakMap();
|
|
7
|
+
export function partOf(block) {
|
|
8
|
+
let part;
|
|
9
|
+
const message = messageOfBlock.get(block);
|
|
10
|
+
if (message)
|
|
11
|
+
part = partsById(message).get(block.id);
|
|
12
|
+
if (part)
|
|
13
|
+
return blocks.set(part, block), part;
|
|
14
|
+
if (block.type !== 'harness')
|
|
15
|
+
part = blockPartOf(block);
|
|
16
|
+
else if (typeof block.data?.type === 'string')
|
|
17
|
+
part = block.data;
|
|
18
|
+
else if (block.model_visible && block.fallback_text !== undefined) {
|
|
19
|
+
part = fallbacks.get(block) ?? { id: block.id, type: 'text', text: block.fallback_text, synthetic: true };
|
|
20
|
+
fallbacks.set(block, part);
|
|
21
|
+
}
|
|
22
|
+
if (part)
|
|
23
|
+
blocks.set(part, block);
|
|
24
|
+
return part;
|
|
25
|
+
}
|
|
26
|
+
export const blockOf = (part) => blocks.get(part);
|
|
27
|
+
export const partsOf = (list) => list.flatMap((b) => partOf(b) ?? []);
|
|
28
|
+
const indexes = new WeakMap();
|
|
29
|
+
function partsById(m) {
|
|
30
|
+
let index = indexes.get(m);
|
|
31
|
+
if (!index)
|
|
32
|
+
indexes.set(m, (index = new Map(v1RowsOf(m)[0]?.parts.map((p) => [p.id, p]))));
|
|
33
|
+
return index;
|
|
34
|
+
}
|
|
35
|
+
const heads = new Map();
|
|
36
|
+
export function v1RowsOf(m) {
|
|
37
|
+
let head = heads.get(m.thread_id);
|
|
38
|
+
if (!head) {
|
|
39
|
+
head = { schema: SESSION_LOG_SCHEMA, v: SESSION_LOG_MINOR, thread_id: m.thread_id, parent_thread_id: null, spawned_by: null, agent: null, title: null, created_at: '' };
|
|
40
|
+
heads.set(m.thread_id, head);
|
|
41
|
+
}
|
|
42
|
+
return rowsOf(head, m);
|
|
43
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Message, Part } from '../runtime/runtime-types.js';
|
|
2
2
|
import type { SessionLogState, SessionLogThreadHead } from './store.js';
|
|
3
|
-
import { type SessionLog } from './types.js';
|
|
3
|
+
import { type SessionLog, type SessionLogMessage, type SessionLogThread } from './types.js';
|
|
4
4
|
export interface V1MessageRow {
|
|
5
5
|
info: Message;
|
|
6
6
|
parts: Part[];
|
|
7
7
|
}
|
|
8
8
|
export declare const isV1Thread: (head: SessionLogThreadHead | undefined) => boolean;
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function rowsOf(head: SessionLogThreadHead, message: SessionLogMessage): V1MessageRow[];
|
|
10
|
+
export type OptimisticEchoOf = (id: string) => string | undefined;
|
|
11
|
+
export declare function sessionLogRowsFor<R extends V1MessageRow>(logs: Record<string, SessionLogState>, runtimeSessionId: string, v1Rows: R[], echoOf?: OptimisticEchoOf): R[];
|
|
12
|
+
export declare function sessionLogThreadWithV1Rows(state: SessionLogState, thread: SessionLogThread, v1Rows: readonly V1MessageRow[], echoOf?: OptimisticEchoOf): SessionLogThread;
|
|
10
13
|
export declare function sessionLogOfV1Rows(sessionId: string, runtimeSessionId: string, rows: readonly V1MessageRow[]): SessionLog;
|
|
@@ -21,7 +21,7 @@ const nativeOf = (head) => {
|
|
|
21
21
|
return e ? (e.native_id ?? null) : head.thread_id;
|
|
22
22
|
};
|
|
23
23
|
const messageRows = new WeakMap();
|
|
24
|
-
function rowsOf(head, message) {
|
|
24
|
+
export function rowsOf(head, message) {
|
|
25
25
|
const native = nativeOf(head);
|
|
26
26
|
const cached = messageRows.get(message);
|
|
27
27
|
if (cached && cached.native === native)
|
|
@@ -37,7 +37,7 @@ function indexOf(state) {
|
|
|
37
37
|
if (!index) {
|
|
38
38
|
index = new Map();
|
|
39
39
|
for (const head of Object.values(state.threads)) {
|
|
40
|
-
const native =
|
|
40
|
+
const native = nativeOf(head);
|
|
41
41
|
if (native !== null && !index.has(native))
|
|
42
42
|
index.set(native, head.thread_id);
|
|
43
43
|
}
|
|
@@ -64,25 +64,64 @@ function insertAt(rows, row) {
|
|
|
64
64
|
});
|
|
65
65
|
return at < 0 ? rows.length : at;
|
|
66
66
|
}
|
|
67
|
+
function messageInsertAt(messages, message) {
|
|
68
|
+
const created = Date.parse(message.created_at);
|
|
69
|
+
if (!Number.isFinite(created))
|
|
70
|
+
return messages.length;
|
|
71
|
+
const at = messages.findIndex((other) => {
|
|
72
|
+
const t = Date.parse(other.created_at);
|
|
73
|
+
return Number.isFinite(t) && (t > created || (t === created && other.message_id > message.message_id));
|
|
74
|
+
});
|
|
75
|
+
return at < 0 ? messages.length : at;
|
|
76
|
+
}
|
|
67
77
|
const v1Order = (a, b) => (createdOf(a) ?? -Infinity) - (createdOf(b) ?? -Infinity) || (a.info.id < b.info.id ? -1 : a.info.id > b.info.id ? 1 : 0);
|
|
68
78
|
const merged = new WeakMap();
|
|
69
|
-
|
|
79
|
+
function heldByLog(row, held, revs, echoOf) {
|
|
80
|
+
const id = row.info.id;
|
|
81
|
+
if (held.has(id) || revs[id] !== undefined)
|
|
82
|
+
return true;
|
|
83
|
+
const echo = echoOf?.(id);
|
|
84
|
+
return echo !== undefined && (held.has(echo) || revs[echo] !== undefined);
|
|
85
|
+
}
|
|
86
|
+
export function sessionLogRowsFor(logs, runtimeSessionId, v1Rows, echoOf) {
|
|
70
87
|
const found = findThread(logs, runtimeSessionId);
|
|
71
88
|
if (!found)
|
|
72
89
|
return v1Rows;
|
|
73
90
|
const { state, head, list } = found;
|
|
74
91
|
const cached = merged.get(list);
|
|
75
92
|
const native = nativeOf(head);
|
|
76
|
-
|
|
93
|
+
const own = cached && cached.v1Rows === v1Rows && cached.native === native && cached.revs === state.revs ? cached.own : list.flatMap((m) => rowsOf(head, m)).sort(v1Order);
|
|
94
|
+
const shown = new Set(own.map((r) => r.info.id));
|
|
95
|
+
const extra = v1Rows.filter((row) => !heldByLog(row, shown, state.revs, echoOf));
|
|
96
|
+
if (cached && cached.own === own && cached.extra.length === extra.length && cached.extra.every((r, i) => r === extra[i]))
|
|
77
97
|
return cached.result;
|
|
78
|
-
const result =
|
|
79
|
-
const
|
|
80
|
-
for (const row of v1Rows) {
|
|
81
|
-
if (shown.has(row.info.id) || state.revs[row.info.id] !== undefined)
|
|
82
|
-
continue;
|
|
98
|
+
const result = [...own];
|
|
99
|
+
for (const row of extra)
|
|
83
100
|
result.splice(insertAt(result, row), 0, row);
|
|
101
|
+
merged.set(list, { v1Rows, native, revs: state.revs, own, extra, result });
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
const extended = new WeakMap();
|
|
105
|
+
export function sessionLogThreadWithV1Rows(state, thread, v1Rows, echoOf) {
|
|
106
|
+
const cached = extended.get(thread);
|
|
107
|
+
const held = cached?.held ?? new Set(thread.messages.flatMap((m) => rowsOf(thread, m).map((r) => r.info.id)));
|
|
108
|
+
const extra = v1Rows.filter((r) => !heldByLog(r, held, state.revs, echoOf));
|
|
109
|
+
if (cached && cached.extra.length === extra.length && cached.extra.every((r, i) => r === extra[i]))
|
|
110
|
+
return cached.result;
|
|
111
|
+
let result = thread;
|
|
112
|
+
if (extra.length) {
|
|
113
|
+
let seq = thread.messages[thread.messages.length - 1]?.seq ?? 0;
|
|
114
|
+
const { paired, used } = pairCompactions(extra);
|
|
115
|
+
const messages = [...thread.messages];
|
|
116
|
+
for (const r of extra) {
|
|
117
|
+
if (used.has(r))
|
|
118
|
+
continue;
|
|
119
|
+
const message = convert(state.session.session_id ?? '', thread.thread_id, r, paired.get(r), ++seq).message;
|
|
120
|
+
messages.splice(messageInsertAt(messages, message), 0, message);
|
|
121
|
+
}
|
|
122
|
+
result = { ...thread, messages };
|
|
84
123
|
}
|
|
85
|
-
|
|
124
|
+
extended.set(thread, { held, extra, result });
|
|
86
125
|
return result;
|
|
87
126
|
}
|
|
88
127
|
const converted = new WeakMap();
|
|
@@ -94,13 +133,48 @@ const summaryOf = (row, marker) => {
|
|
|
94
133
|
const info = row.info;
|
|
95
134
|
return info.role === 'assistant' && info.summary === true && info.parentID === marker.info.id;
|
|
96
135
|
};
|
|
136
|
+
const partBlocks = new WeakMap();
|
|
137
|
+
function reuseBlocks(native, row, message) {
|
|
138
|
+
const info = row.info;
|
|
139
|
+
const at = `${native}|${String(info.id)}|${String(info.time?.created)}`;
|
|
140
|
+
let reused = false;
|
|
141
|
+
const blocks = message.blocks.map((block, i) => {
|
|
142
|
+
const part = row.parts[i];
|
|
143
|
+
if (!isObj(part) || block.type === 'compaction')
|
|
144
|
+
return block;
|
|
145
|
+
const key = `${at}|${i}|${block.id}`;
|
|
146
|
+
const prev = partBlocks.get(part);
|
|
147
|
+
if (prev && prev.key === key) {
|
|
148
|
+
reused = true;
|
|
149
|
+
return prev.block;
|
|
150
|
+
}
|
|
151
|
+
partBlocks.set(part, { key, block });
|
|
152
|
+
return block;
|
|
153
|
+
});
|
|
154
|
+
return reused ? { ...message, blocks } : message;
|
|
155
|
+
}
|
|
156
|
+
function pairCompactions(rows) {
|
|
157
|
+
const paired = new Map();
|
|
158
|
+
const used = new Set();
|
|
159
|
+
for (const row of rows) {
|
|
160
|
+
if (!isMarker(row))
|
|
161
|
+
continue;
|
|
162
|
+
const sum = rows.find((r) => !used.has(r) && summaryOf(r, row));
|
|
163
|
+
if (!sum)
|
|
164
|
+
continue;
|
|
165
|
+
paired.set(row, sum);
|
|
166
|
+
used.add(sum);
|
|
167
|
+
}
|
|
168
|
+
return { paired, used };
|
|
169
|
+
}
|
|
97
170
|
function convert(sessionId, native, row, sum, seq) {
|
|
98
171
|
const cached = converted.get(row);
|
|
99
172
|
if (cached && cached.sessionId === sessionId && cached.native === native && cached.sum === sum && cached.seq === seq)
|
|
100
173
|
return cached;
|
|
101
174
|
const unit = sum ? [row, sum] : [row];
|
|
102
175
|
const log = fromV1({ session_id: sessionId, root_id: native, rows: unit.map((r) => ({ runtime_session_id: native, info: r.info, parts: r.parts })) }, { live: true });
|
|
103
|
-
const
|
|
176
|
+
const message = reuseBlocks(native, row, log.threads[0].messages[0]);
|
|
177
|
+
const entry = { sessionId, native, sum, seq, message: { ...message, seq }, lost: log.grade_counts };
|
|
104
178
|
converted.set(row, entry);
|
|
105
179
|
return entry;
|
|
106
180
|
}
|
|
@@ -109,17 +183,7 @@ export function sessionLogOfV1Rows(sessionId, runtimeSessionId, rows) {
|
|
|
109
183
|
if (cached && cached.sessionId === sessionId && cached.native === runtimeSessionId)
|
|
110
184
|
return cached.log;
|
|
111
185
|
const sorted = [...rows].sort(v1Order);
|
|
112
|
-
const paired =
|
|
113
|
-
const used = new Set();
|
|
114
|
-
for (const row of sorted) {
|
|
115
|
-
if (!isMarker(row))
|
|
116
|
-
continue;
|
|
117
|
-
const sum = sorted.find((r) => !used.has(r) && summaryOf(r, row));
|
|
118
|
-
if (!sum)
|
|
119
|
-
continue;
|
|
120
|
-
paired.set(row, sum);
|
|
121
|
-
used.add(sum);
|
|
122
|
-
}
|
|
186
|
+
const { paired, used } = pairCompactions(sorted);
|
|
123
187
|
const counts = { tool_input: 0, cut_point: 0, attachment: 0 };
|
|
124
188
|
const messages = [];
|
|
125
189
|
for (const row of sorted) {
|
|
@@ -71,6 +71,12 @@ const extOf = (h, entry) => ({ [h]: { version: BEEUNI_TRANSCRIPT_SCHEMA, ...entr
|
|
|
71
71
|
const urlOf = (a, c) => a.url !== undefined ? { url: a.url } : a.ref !== undefined && !c.fromV1 ? { url: a.ref } : {};
|
|
72
72
|
const textOf = (content) => (content ?? []).flatMap((c) => (c.type === 'text' ? [c.text] : [])).join('');
|
|
73
73
|
const head = (b, c) => ({ id: b.id, ...(c.sessionID !== null && { sessionID: c.sessionID }), messageID: c.messageID });
|
|
74
|
+
const detailsOf = (b) => {
|
|
75
|
+
if (isObj(b.result?.details))
|
|
76
|
+
return b.result.details;
|
|
77
|
+
const own = Object.values(b.ext ?? {}).find((e) => e.version !== BEEUNI_TRANSCRIPT_SCHEMA && isObj(e.data?.details));
|
|
78
|
+
return own ? own.data.details : {};
|
|
79
|
+
};
|
|
74
80
|
function toolState(b, c, base) {
|
|
75
81
|
const r = b.result;
|
|
76
82
|
const input = b.input === null || b.input === undefined ? {} : { input: b.input };
|
|
@@ -86,7 +92,7 @@ function toolState(b, c, base) {
|
|
|
86
92
|
const atts = (r?.content ?? []).flatMap((x, i) => x.type === 'attachment' ? [{ ...base, id: `${b.id}-a${i}`, type: 'file', mime: x.mime, ...urlOf(x, c) }] : []);
|
|
87
93
|
const compacted = msOf(r?.cleared_at);
|
|
88
94
|
return {
|
|
89
|
-
status: 'completed', ...input, output: textOf(r?.content), title: b.title ?? '', metadata: meta,
|
|
95
|
+
status: 'completed', ...input, output: textOf(r?.content), title: b.title ?? '', metadata: { ...detailsOf(b), ...meta },
|
|
90
96
|
time: { start, end, ...(compacted !== undefined && { compacted }) },
|
|
91
97
|
...(atts.length && { attachments: atts }),
|
|
92
98
|
};
|
|
@@ -310,14 +316,15 @@ function toMessage(row, sum, threadId, seq, superseded, c) {
|
|
|
310
316
|
const aborted = err?.name === 'MessageAbortedError';
|
|
311
317
|
const completedAt = src && isObj(src.time) ? iso(src.time.completed) : null;
|
|
312
318
|
const open = c.live ? 'streaming' : 'interrupted';
|
|
313
|
-
const
|
|
319
|
+
const settled = !src ? (marker ? open : 'complete') : err ? (aborted ? 'aborted' : 'error') : completedAt ? 'complete' : open;
|
|
314
320
|
const createdAt = iso(isObj(info.time) ? info.time.created : undefined) ?? '';
|
|
315
321
|
const summary = sum ? (Array.isArray(sum.parts) ? sum.parts : []).flatMap((p) => (isObj(p) && p.type === 'text' && typeof p.text === 'string' ? [p.text] : [])).join('') : null;
|
|
316
322
|
const lost = { tool_input: 0, cut_point: 0, attachment: 0 };
|
|
317
323
|
const ids = blockIds(parts);
|
|
318
324
|
const blocks = parts.map((p, i) => blockOf(p, ids[i], marker, summary, c, lost));
|
|
319
325
|
const visible = blocks.some((b) => b.type === 'text' || b.type === 'attachment' || b.type === 'tool_call' || (b.type === 'harness' && b.model_visible));
|
|
320
|
-
const inContext = !superseded && role !== 'system' && (marker ?
|
|
326
|
+
const inContext = !superseded && role !== 'system' && (marker ? settled === 'complete' : !err || (aborted && visible));
|
|
327
|
+
const status = c.live && inContext && settled !== 'streaming' && blocks.some((b) => b.type === 'tool_call' && (b.status === 'pending' || b.status === 'running')) ? 'streaming' : settled;
|
|
321
328
|
const hidden = superseded ? 'superseded' : inContext || !err ? undefined : aborted ? 'aborted' : 'failed_attempt';
|
|
322
329
|
if (marker && isObj(marker) && str(marker.tail_start_id) === undefined)
|
|
323
330
|
lost.cut_point++;
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SessionTranscriptSyncEnvelope } from '../rest/projects-client/sessions.js';
|
|
2
|
+
export declare const SAVED_HISTORY_SNAPSHOT_WAIT_MS = 2000;
|
|
3
|
+
export declare function loadSavedTranscript(projectId: string, sessionId: string, options?: {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
snapshotWaitMs?: number;
|
|
6
|
+
}): Promise<SessionTranscriptSyncEnvelope>;
|
|
7
|
+
export interface SavedSessionCopy {
|
|
8
|
+
envelope: SessionTranscriptSyncEnvelope | null;
|
|
9
|
+
empty: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function readSavedSessionCopy(input: {
|
|
12
|
+
projectId: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
runtimeSessionId: string;
|
|
15
|
+
}): Promise<SavedSessionCopy>;
|
|
2
16
|
export type SavedTranscript = 'loading' | 'shown' | 'none';
|
|
3
17
|
export interface SavedTranscriptInput {
|
|
4
18
|
enabled: boolean;
|