@kortix/ui 0.14.6 → 0.14.8
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/dist/_vendor/kortix-shared.js +490 -382
- package/dist/_vendor/shared/runtime-versions.d.ts +4 -0
- package/dist/access/access/access-dialog-share.js +4 -7
- package/dist/access/access/access-dialog.js +2 -1
- package/dist/access/access/principal-picker.js +6 -5
- package/dist/customize/agents/agent-authority-card.js +1 -2
- package/dist/customize/agents/agent-cover-card.d.ts +17 -0
- package/dist/customize/agents/agent-cover-card.js +13 -0
- package/dist/customize/agents/agent-detail-aside.js +15 -14
- package/dist/customize/agents/agent-drives-section.js +5 -6
- package/dist/customize/agents/agent-grant-pages.js +68 -70
- package/dist/customize/agents/agent-page.js +27 -19
- package/dist/customize/agents/agent-people-section.js +18 -17
- package/dist/customize/agents/agent-triggers-section.js +6 -5
- package/dist/customize/agents/agents-page.js +43 -49
- package/dist/customize/connectors/catalog/catalog-entry.d.ts +14 -0
- package/dist/customize/connectors/catalog/catalog-entry.js +18 -1
- package/dist/customize/connectors/catalog/connector-browse.d.ts +28 -0
- package/dist/customize/connectors/catalog/connector-browse.js +18 -5
- package/dist/customize/connectors/detail/app-page.js +19 -5
- package/dist/customize/connectors/detail/connector-modal.d.ts +10 -13
- package/dist/customize/connectors/detail/connector-modal.js +18 -101
- package/dist/customize/connectors/detail/connector-notice.js +6 -2
- package/dist/customize/connectors/detail/connector-page.d.ts +30 -0
- package/dist/customize/connectors/detail/connector-page.js +26 -13
- package/dist/customize/migrate-to-v2/migration-prompt.d.ts +1 -0
- package/dist/customize/migrate-to-v2/migration-prompt.js +1 -0
- package/dist/customize/sections/llm-provider/custom-provider-form.js +24 -7
- package/dist/customize/sections/llm-provider/custom-provider-panel.js +44 -8
- package/dist/customize/sections/llm-provider/types.d.ts +3 -0
- package/dist/customize/sections/llm-provider/utils.js +2 -2
- package/dist/customize/sections/view/agent-editor-access-fields.d.ts +9 -16
- package/dist/customize/sections/view/agent-editor-access-fields.js +30 -30
- package/dist/customize/sections/view/agent-editor-basics-fields.js +3 -10
- package/dist/customize/sections/view/agent-editor-primitives.d.ts +28 -10
- package/dist/customize/sections/view/agent-editor-primitives.js +32 -19
- package/dist/customize/sections/view/agent-editor.js +3 -3
- package/dist/customize/sections/view/channels-view.js +2 -6
- package/dist/customize/sections/view/gateway/gateway-routing.js +3 -3
- package/dist/customize/sections/view/grant-mode-field.d.ts +2 -5
- package/dist/customize/sections/view/grant-mode-field.js +10 -18
- package/dist/customize/sections/view/permission-editor.js +11 -8
- package/dist/customize/sections/view/secrets-view.js +2 -1
- package/dist/customize/settings/tabs/general-tab.d.ts +9 -2
- package/dist/customize/settings/tabs/general-tab.js +33 -11
- package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
- package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
- package/dist/customize/shared/new-entity-menu.js +4 -1
- package/dist/customize/skills/skills-page.js +4 -5
- package/dist/customize/triggers/schedule/then-fields.js +1 -5
- package/dist/customize/triggers/schedule-view.js +4 -2
- package/dist/files/project/api/runtime-files.js +3 -2
- package/dist/files/project/file-source.js +18 -1
- package/dist/files/project/hooks/use-file-content.js +2 -0
- package/dist/files/viewer/file-header.js +1 -1
- package/dist/files/viewer/file-preview-modal.d.ts +1 -1
- package/dist/files/viewer/file-preview-modal.js +12 -2
- package/dist/files/viewer/file-source.d.ts +3 -1
- package/dist/files/viewer/use-file-content-state.js +26 -7
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/i18n/en.d.ts +393 -12
- package/dist/i18n/en.js +400 -19
- package/dist/internal/components/agent-mark.d.ts +38 -0
- package/dist/internal/components/agent-mark.js +38 -0
- package/dist/internal/files/types.d.ts +2 -0
- package/dist/internal/goals/goal-meta.d.ts +30 -0
- package/dist/internal/goals/goal-meta.js +55 -0
- package/dist/internal/lib/pixel-mark.d.ts +33 -0
- package/dist/internal/lib/pixel-mark.js +152 -0
- package/dist/internal/lib/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- package/dist/internal/lib/use-project-feature-flags.js +3 -3
- package/dist/internal/share/session-shares-modal.js +3 -7
- package/dist/internal/stores/kortix-computer-store.js +14 -2
- package/dist/internal/stores/session-hover-store.d.ts +32 -0
- package/dist/internal/stores/session-hover-store.js +38 -0
- package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
- package/dist/internal/stores/sidebar-nav-store.js +3 -1
- package/dist/lib/routes.d.ts +2 -0
- package/dist/lib/routes.js +1 -0
- package/dist/markdown/genui/components/inline.d.ts +1 -1
- package/dist/markdown/setup-links/app-credentials-form.d.ts +12 -0
- package/dist/markdown/setup-links/app-credentials-form.js +115 -0
- package/dist/markdown/setup-links/secret-intake-form.d.ts +10 -0
- package/dist/markdown/setup-links/secret-intake-form.js +1 -1
- package/dist/primitives/entity-avatar.d.ts +4 -1
- package/dist/primitives/entity-avatar.js +4 -2
- package/dist/primitives/settings-section-header.d.ts +1 -1
- package/dist/primitives/user-avatar.d.ts +3 -1
- package/dist/primitives/user-avatar.js +4 -2
- package/dist/providers/ui-provider.d.ts +1 -1
- package/dist/session/composer/agent-selector.d.ts +7 -1
- package/dist/session/composer/agent-selector.js +20 -10
- package/dist/session/composer/attachment-tile.d.ts +0 -2
- package/dist/session/composer/attachment-tile.js +0 -8
- package/dist/session/composer/clipboard-files.d.ts +2 -1
- package/dist/session/composer/composer-draft-recovery.d.ts +1 -2
- package/dist/session/composer/composer-draft-recovery.js +0 -13
- package/dist/session/composer/context-ring.d.ts +1 -1
- package/dist/session/composer/context-ring.js +2 -2
- package/dist/session/composer/context-usage.d.ts +8 -6
- package/dist/session/composer/context-usage.js +9 -7
- package/dist/session/composer/inbox-row-claims.d.ts +2 -2
- package/dist/session/composer/inbox-row-claims.js +1 -1
- package/dist/session/composer/queue-projection.d.ts +3 -2
- package/dist/session/composer/queued-prompt-edit.d.ts +1 -1
- package/dist/session/composer/reasoning-effort-selector.d.ts +2 -1
- package/dist/session/composer/session-composer-readiness.d.ts +7 -22
- package/dist/session/composer/session-composer-readiness.js +12 -40
- package/dist/session/composer/token-progress.d.ts +1 -1
- package/dist/session/composer/token-progress.js +6 -6
- package/dist/session/header/export-transcript-modal.d.ts +3 -2
- package/dist/session/header/export-transcript-modal.js +6 -3
- package/dist/session/header/session-goal-indicator.d.ts +1 -0
- package/dist/session/header/session-goal-indicator.js +41 -0
- package/dist/session/header/session-site-header.js +2 -1
- package/dist/session/models/model-selector.d.ts +3 -1
- package/dist/session/models/model-selector.js +3 -2
- package/dist/session/panel/easy/plan-card.d.ts +2 -0
- package/dist/session/panel/easy/plan-card.js +2 -0
- package/dist/session/panel/session-action-panel-column.js +11 -1
- package/dist/session/panel/session-audit-shared.d.ts +0 -4
- package/dist/session/panel/session-audit-shared.js +1 -1
- package/dist/session/panel/session-changes-shared.d.ts +2 -1
- package/dist/session/prompts/session-permission-prompt.js +6 -3
- package/dist/session/status/provisioning-failure.d.ts +2 -1
- package/dist/session/status/session-error-banner.js +8 -6
- package/dist/session/status/session-starting-loader.d.ts +24 -31
- package/dist/session/status/session-starting-loader.js +32 -81
- package/dist/session/thread/chat-minimap-items.d.ts +4 -4
- package/dist/session/thread/first-prompt-handover.d.ts +3 -2
- package/dist/session/thread/message-parsing.d.ts +3 -3
- package/dist/session/thread/plan-surface.d.ts +1 -0
- package/dist/session/thread/plan-surface.js +1 -0
- package/dist/session/thread/session-chat.js +5 -12
- package/dist/session/thread/session-context-metrics.d.ts +15 -0
- package/dist/session/thread/session-context-metrics.js +10 -1
- package/dist/session/thread/session-context-modal.d.ts +4 -2
- package/dist/session/thread/session-context-modal.js +16 -9
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-thread.js +1 -1
- package/dist/session/thread/transcript.d.ts +4 -0
- package/dist/session/thread/transcript.js +18 -5
- package/dist/session/thread/turn-share.d.ts +16 -0
- package/dist/session/thread/turn-share.js +162 -0
- package/dist/session/tool/tools/apply-patch-tool.d.ts +1 -0
- package/dist/session/tool/tools/apply-patch-tool.js +1 -0
- package/dist/session/tool/tools/pty-kill-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-kill-tool.js +1 -0
- package/dist/session/tool/tools/pty-read-tool.d.ts +2 -0
- package/dist/session/tool/tools/pty-read-tool.js +2 -0
- package/dist/session/tool/tools/pty-spawn-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-spawn-tool.js +1 -0
- package/dist/session/tool/tools/pty-write-tool.d.ts +1 -0
- package/dist/session/tool/tools/pty-write-tool.js +1 -0
- package/dist/session/tool/tools/todo-write-tool.d.ts +1 -0
- package/dist/session/tool/tools/todo-write-tool.js +1 -0
- package/dist/session/turn/message-author-label.js +6 -2
- package/dist/session/turn/plan-anchor.d.ts +3 -0
- package/dist/session/turn/plan-anchor.js +3 -0
- package/dist/session/turn/plan-card.d.ts +7 -0
- package/dist/session/turn/plan-card.js +7 -0
- package/dist/styles.css +1 -1
- package/dist/terminal/pty-connection.d.ts +5 -4
- package/dist/terminal/session-terminal-connect-bar.js +2 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/saved-session-skeleton-shape.d.ts +4 -3
- package/dist/workspace/saved-session-skeleton.d.ts +5 -1
- package/dist/workspace/saved-session-skeleton.js +5 -4
- package/dist/workspace/session-workspace.js +4 -5
- package/dist/workspace/sidebar/footer/project-goals-nav.d.ts +6 -0
- package/dist/workspace/sidebar/footer/project-goals-nav.js +26 -0
- package/dist/workspace/sidebar/modal/session-embed-section.d.ts +25 -0
- package/dist/workspace/sidebar/modal/session-embed-section.js +71 -0
- package/dist/workspace/sidebar/modal/share-session-modal.js +2 -2
- package/dist/workspace/sidebar/nav-row-meta.js +14 -2
- package/dist/workspace/sidebar/project-nav-items.js +3 -0
- package/dist/workspace/sidebar/project-session-list.js +12 -3
- package/dist/workspace/sidebar/session-brief-hover-card.js +33 -9
- package/package.json +12 -4
- package/skills/kortix-ui/references/modules.md +2 -0
- package/dist/session/status/use-readiness-settling.d.ts +0 -18
- package/dist/session/status/use-readiness-settling.js +0 -43
- package/dist/workspace/sidebar/modal/session-public-link-section.d.ts +0 -13
- package/dist/workspace/sidebar/modal/session-public-link-section.js +0 -66
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { type SessionStartResult
|
|
1
|
+
import { type SessionStartResult } from '@kortix/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* What a session shows while its Kortix Computer comes up: nothing, unless the
|
|
4
|
+
* wake needs attention. Opening a session wakes its computer in the background,
|
|
5
|
+
* and a send goes out as soon as it is up, so a routine wake has nothing to
|
|
6
|
+
* say. Two states speak: an escalation or retry note from the server, and a
|
|
7
|
+
* wake stuck past `STUCK_AFTER_MS` that the caller may restart.
|
|
8
|
+
*
|
|
9
|
+
* All the heavy lifting (provision / wake / runtime readiness) is server-side
|
|
10
|
+
* behind POST /sessions/:id/start.
|
|
11
|
+
*/
|
|
2
12
|
/**
|
|
3
13
|
* After this long, offer a manual restart. Sandboxes occasionally wedge (e.g. a
|
|
4
14
|
* stuck provider-side proxy) with no server-side signal that anything is wrong —
|
|
5
15
|
* a stop/start of the sandbox is the known fix, so surface it as a fallback
|
|
6
|
-
* instead of leaving the user
|
|
16
|
+
* instead of leaving the user waiting indefinitely.
|
|
7
17
|
*/
|
|
8
18
|
export declare const STUCK_AFTER_MS = 45000;
|
|
9
|
-
interface Step {
|
|
10
|
-
/** User-visible label for the active startup stage. */
|
|
11
|
-
label: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Accepted for input compatibility. Both values intentionally render the same
|
|
15
|
-
* loader.
|
|
16
|
-
*/
|
|
17
|
-
type BootStepVariant = 'stepper' | 'compact';
|
|
18
19
|
type StartFailure = SessionStartResult['failure'];
|
|
19
20
|
/**
|
|
20
21
|
* Keep the server's retry visible while `/start` waits for its retry clock.
|
|
@@ -26,14 +27,6 @@ export declare function sessionWakeStatusNote(input: {
|
|
|
26
27
|
note?: string | null;
|
|
27
28
|
now: number;
|
|
28
29
|
}): string | null;
|
|
29
|
-
/** Copy is deliberately parallel, so stage changes read as one continuous task. */
|
|
30
|
-
export declare const STEPS: Step[];
|
|
31
|
-
/**
|
|
32
|
-
* Resolve which step is CURRENTLY active from the backend stage plus how long
|
|
33
|
-
* we've been in it. The index is the floor we KNOW we're at — earlier steps are
|
|
34
|
-
* genuinely complete, later ones haven't started.
|
|
35
|
-
*/
|
|
36
|
-
export declare function activeStep(stage: SessionStartStage, msInStage: number): number;
|
|
37
30
|
/**
|
|
38
31
|
* Whether the stuck-boot restart offer may render. Restart is the session
|
|
39
32
|
* owner's or a project manager's; the server answers anyone else 403
|
|
@@ -53,31 +46,31 @@ export declare function RestartFallback({ show, pending, onRestart, className, b
|
|
|
53
46
|
className?: string;
|
|
54
47
|
buttonClassName?: string;
|
|
55
48
|
}): import("react").JSX.Element | null;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The wake's status where nothing else can paint: the side panel, a chat with
|
|
51
|
+
* no runtime yet, a resume with nothing saved. A routine wake renders nothing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function SessionStartingLoader({ projectId, sessionId,
|
|
59
54
|
/** Honest one-liner from the SDK wake escalation ladder. */
|
|
60
55
|
note, reason, failure, }: {
|
|
61
|
-
stage?: SessionStartStage;
|
|
62
|
-
delayMs?: number;
|
|
63
56
|
projectId?: string;
|
|
64
57
|
sessionId?: string;
|
|
65
|
-
variant?: BootStepVariant;
|
|
66
58
|
note?: string | null;
|
|
67
59
|
reason?: string | null;
|
|
68
60
|
failure?: StartFailure;
|
|
69
|
-
}): import("react").JSX.Element;
|
|
61
|
+
}): import("react").JSX.Element | null;
|
|
70
62
|
/**
|
|
71
|
-
* Compact status above a readable conversation while its runtime wakes
|
|
72
|
-
*
|
|
63
|
+
* Compact status above a readable conversation while its runtime wakes, shown
|
|
64
|
+
* only when the wake needs attention: an escalation or retry note, or a stuck
|
|
65
|
+
* wake with a Restart. A normal wake renders nothing. The transcript remains
|
|
66
|
+
* scrollable because only the restart control accepts input.
|
|
73
67
|
*/
|
|
74
|
-
export declare function SessionConnectingBanner({
|
|
75
|
-
stage?: SessionStartStage;
|
|
68
|
+
export declare function SessionConnectingBanner({ projectId, sessionId, className, note, reason, failure, }: {
|
|
76
69
|
projectId?: string;
|
|
77
70
|
sessionId?: string;
|
|
78
71
|
className?: string;
|
|
79
72
|
note?: string | null;
|
|
80
73
|
reason?: string | null;
|
|
81
74
|
failure?: StartFailure;
|
|
82
|
-
}): import("react").JSX.Element;
|
|
75
|
+
}): import("react").JSX.Element | null;
|
|
83
76
|
export {};
|
|
@@ -3,37 +3,26 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Button } from '../../primitives/button.js';
|
|
4
4
|
import Loading from '../../primitives/loading.js';
|
|
5
5
|
import { useRestartProjectSession } from '../../internal/hooks/projects/use-restart-project-session.js';
|
|
6
|
-
import { useLocalizedUiCatalog } from '../../i18n/ui-catalog/use-localized-ui-catalog.js';
|
|
7
6
|
import { useTranslations } from '../../i18n/index.js';
|
|
8
7
|
import { cn } from '../../lib/utils.js';
|
|
9
8
|
import { useProjectSession } from '@kortix/sdk/react';
|
|
10
9
|
import { ArrowCounterClockwiseIcon as RotateCcw } from '@phosphor-icons/react';
|
|
11
10
|
import { useEffect, useRef, useState } from 'react';
|
|
12
11
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* What a session shows while its Kortix Computer comes up: nothing, unless the
|
|
13
|
+
* wake needs attention. Opening a session wakes its computer in the background,
|
|
14
|
+
* and a send goes out as soon as it is up, so a routine wake has nothing to
|
|
15
|
+
* say. Two states speak: an escalation or retry note from the server, and a
|
|
16
|
+
* wake stuck past `STUCK_AFTER_MS` that the caller may restart.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* only when the backend stage advances, so it never claims progress we do not
|
|
21
|
-
* have.
|
|
18
|
+
* All the heavy lifting (provision / wake / runtime readiness) is server-side
|
|
19
|
+
* behind POST /sessions/:id/start.
|
|
22
20
|
*/
|
|
23
|
-
const LOADER_DELAY_MS = 100;
|
|
24
|
-
/**
|
|
25
|
-
* How long we sit in the backend `starting` stage before softly advancing from
|
|
26
|
-
* "Loading your workspace" to "Waking the agent". Both happen within that one
|
|
27
|
-
* backend stage (clone → OpenCode boot), so the advance reflects real order.
|
|
28
|
-
*/
|
|
29
|
-
const STARTING_SUBSTEP_MS = 5_000;
|
|
30
|
-
/** After this long, show copy that sets expectations for a cold start. */
|
|
31
|
-
const SLOW_AFTER_MS = 15_000;
|
|
32
21
|
/**
|
|
33
22
|
* After this long, offer a manual restart. Sandboxes occasionally wedge (e.g. a
|
|
34
23
|
* stuck provider-side proxy) with no server-side signal that anything is wrong —
|
|
35
24
|
* a stop/start of the sandbox is the known fix, so surface it as a fallback
|
|
36
|
-
* instead of leaving the user
|
|
25
|
+
* instead of leaving the user waiting indefinitely.
|
|
37
26
|
*/
|
|
38
27
|
export const STUCK_AFTER_MS = 45_000;
|
|
39
28
|
/**
|
|
@@ -64,42 +53,14 @@ export function sessionWakeStatusNote(input) {
|
|
|
64
53
|
const duration = minutes > 0 ? `${minutes}m\u00a0${remainder}s` : `${remainder}s`;
|
|
65
54
|
return `Still starting your computer. Trying again in ${duration} (attempt ${nextAttempt}).`;
|
|
66
55
|
}
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
{ label: 'Reserving your computer' },
|
|
70
|
-
{ label: 'Loading your workspace' },
|
|
71
|
-
{ label: 'Waking the agent' },
|
|
72
|
-
{ label: 'Connecting' },
|
|
73
|
-
];
|
|
74
|
-
/**
|
|
75
|
-
* Resolve which step is CURRENTLY active from the backend stage plus how long
|
|
76
|
-
* we've been in it. The index is the floor we KNOW we're at — earlier steps are
|
|
77
|
-
* genuinely complete, later ones haven't started.
|
|
78
|
-
*/
|
|
79
|
-
export function activeStep(stage, msInStage) {
|
|
80
|
-
if (stage === 'provisioning')
|
|
81
|
-
return 0;
|
|
82
|
-
if (stage === 'starting')
|
|
83
|
-
return msInStage >= STARTING_SUBSTEP_MS ? 2 : 1;
|
|
84
|
-
return 3;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* The shared boot clock: a 1s tick that resolves the current active step from
|
|
88
|
-
* the backend stage plus time-in-stage, and exposes `now` for elapsed-time UI.
|
|
89
|
-
*/
|
|
90
|
-
function useBootProgress(stage) {
|
|
56
|
+
/** A 1 s tick: the retry countdown and the stuck clock read it. */
|
|
57
|
+
function useNow() {
|
|
91
58
|
const [now, setNow] = useState(() => Date.now());
|
|
92
59
|
useEffect(() => {
|
|
93
60
|
const id = setInterval(() => setNow(Date.now()), 1_000);
|
|
94
61
|
return () => clearInterval(id);
|
|
95
62
|
}, []);
|
|
96
|
-
|
|
97
|
-
const [prevStage, setPrevStage] = useState(stage);
|
|
98
|
-
if (prevStage !== stage) {
|
|
99
|
-
setPrevStage(stage);
|
|
100
|
-
setStageEnteredAt(now);
|
|
101
|
-
}
|
|
102
|
-
return { active: activeStep(stage, now - stageEnteredAt), now };
|
|
63
|
+
return now;
|
|
103
64
|
}
|
|
104
65
|
/**
|
|
105
66
|
* The boot clock a restart carries. The shared restart hook owns the whole
|
|
@@ -139,52 +100,42 @@ export function RestartFallback({ show, pending, onRestart, className, buttonCla
|
|
|
139
100
|
return (_jsxs("div", { className: cn('mt-5 w-full', className), children: [_jsx("p", { className: "text-muted-foreground mb-2 text-xs text-pretty", children: tI18nComplete.raw('textbeda94e911d3') }), _jsxs(Button, { type: "button", variant: "secondary", size: "sm", className: cn('w-full active:scale-[0.96]', buttonClassName), disabled: pending, onClick: onRestart, children: [pending ? (_jsx(Loading, { className: "size-3.5 shrink-0 text-current motion-reduce:animate-none" })) : (_jsx(RotateCcw, { className: "size-3.5 shrink-0" })), pending ? tI18nComplete.raw('text75d0f1469d16') : tI18nComplete.raw('textcb886371afc6')] })] }));
|
|
140
101
|
}
|
|
141
102
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
103
|
+
* The wake's status where nothing else can paint: the side panel, a chat with
|
|
104
|
+
* no runtime yet, a resume with nothing saved. A routine wake renders nothing.
|
|
144
105
|
*/
|
|
145
|
-
function
|
|
146
|
-
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
147
|
-
const steps = useLocalizedUiCatalog(STEPS);
|
|
148
|
-
const step = steps[Math.min(active, steps.length - 1)];
|
|
149
|
-
return (_jsx("div", { className: "flex h-full min-h-0 w-full flex-1 items-center justify-center px-4 sm:px-8", children: show ? (_jsxs("div", { role: "status", "aria-live": "polite", className: "flex w-full max-w-xs items-start gap-3", children: [_jsx(Loading, { variant: "spokes", className: "text-muted-foreground mt-0.5 size-4 shrink-0 motion-reduce:animate-none" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: "text-foreground text-sm font-medium text-balance", children: tI18nComplete.raw('textac4e05b19878') }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs text-pretty", children: note ?? step.label }), _jsx("div", { role: "progressbar", "aria-label": tI18nComplete.raw('text4cbf9c0d5628'), "aria-valuemin": 1, "aria-valuemax": steps.length, "aria-valuenow": active + 1, "aria-valuetext": `Step ${active + 1} of ${steps.length}: ${step.label}`, className: "mt-3 grid grid-cols-4 gap-1.5", children: steps.map((item, index) => (_jsx("span", { "aria-hidden": true, className: cn('h-1 rounded-full', index <= active ? 'bg-kortix-green' : 'bg-border/70') }, item.label))) }), slow ? (_jsx("p", { className: "text-muted-foreground mt-3 text-xs text-pretty", children: tI18nComplete.raw('textbb2ab0401c3c') })) : null, _jsx(RestartFallback, { show: stuck && canRestart, pending: pending, onRestart: onRestart })] })] })) : null }));
|
|
150
|
-
}
|
|
151
|
-
export function SessionStartingLoader({ stage = 'provisioning',
|
|
152
|
-
/** Delay before the content appears. Warm opens avoid flashing the loader. */
|
|
153
|
-
delayMs = LOADER_DELAY_MS, projectId, sessionId,
|
|
106
|
+
export function SessionStartingLoader({ projectId, sessionId,
|
|
154
107
|
/** Honest one-liner from the SDK wake escalation ladder. */
|
|
155
108
|
note, reason, failure, }) {
|
|
156
|
-
const [delayElapsed, setDelayElapsed] = useState(false);
|
|
157
|
-
const show = delayMs <= 0 || delayElapsed;
|
|
158
|
-
useEffect(() => {
|
|
159
|
-
if (delayMs <= 0)
|
|
160
|
-
return;
|
|
161
|
-
const timeout = setTimeout(() => setDelayElapsed(true), delayMs);
|
|
162
|
-
return () => clearTimeout(timeout);
|
|
163
|
-
}, [delayMs]);
|
|
164
109
|
const { data: session } = useProjectSession(projectId, sessionId);
|
|
165
110
|
const restart = useRestartProjectSession(projectId ?? '', sessionId ?? '');
|
|
166
|
-
const
|
|
111
|
+
const now = useNow();
|
|
167
112
|
const statusNote = sessionWakeStatusNote({ reason, failure, note, now });
|
|
168
113
|
const clockStart = useRestartedBootClock(restart);
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return (_jsx(
|
|
114
|
+
const restartable = now - clockStart >= STUCK_AFTER_MS && canOfferBootRestart(session, projectId, sessionId);
|
|
115
|
+
if (!statusNote && !restartable)
|
|
116
|
+
return null;
|
|
117
|
+
return (_jsx("div", { className: "flex h-full min-h-0 w-full flex-1 items-center justify-center px-4 sm:px-8", children: _jsxs("div", { role: "status", "aria-live": "polite", className: "flex w-full max-w-xs items-start gap-3", children: [_jsx(Loading, { variant: "spokes", className: "text-muted-foreground mt-0.5 size-4 shrink-0 motion-reduce:animate-none" }), _jsxs("div", { className: "min-w-0 flex-1", children: [statusNote ? _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: statusNote }) : null, _jsx(RestartFallback, { show: restartable, pending: restart.isPending, onRestart: restart.restart, className: statusNote ? 'mt-3' : 'mt-0' })] })] }) }));
|
|
173
118
|
}
|
|
174
119
|
/**
|
|
175
|
-
* Compact status above a readable conversation while its runtime wakes
|
|
176
|
-
*
|
|
120
|
+
* Compact status above a readable conversation while its runtime wakes, shown
|
|
121
|
+
* only when the wake needs attention: an escalation or retry note, or a stuck
|
|
122
|
+
* wake with a Restart. A normal wake renders nothing. The transcript remains
|
|
123
|
+
* scrollable because only the restart control accepts input.
|
|
177
124
|
*/
|
|
178
|
-
export function SessionConnectingBanner({
|
|
125
|
+
export function SessionConnectingBanner({ projectId, sessionId, className, note, reason, failure, }) {
|
|
179
126
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
180
127
|
const { data: session } = useProjectSession(projectId, sessionId);
|
|
181
128
|
const restart = useRestartProjectSession(projectId ?? '', sessionId ?? '');
|
|
182
|
-
const
|
|
129
|
+
const now = useNow();
|
|
183
130
|
const statusNote = sessionWakeStatusNote({ reason, failure, note, now });
|
|
184
131
|
const clockStart = useRestartedBootClock(restart);
|
|
185
132
|
const stuck = now - clockStart >= STUCK_AFTER_MS;
|
|
186
133
|
const canRestart = canOfferBootRestart(session, projectId, sessionId);
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
134
|
+
// A normal wake is silent: opening the session wakes its computer in the
|
|
135
|
+
// background, and a send goes out as soon as it is up. The pill speaks only
|
|
136
|
+
// when something is wrong: the wake escalated or waits on a retry
|
|
137
|
+
// (`statusNote`), or it is stuck and can be restarted.
|
|
138
|
+
if (!statusNote && !(stuck && canRestart))
|
|
139
|
+
return null;
|
|
140
|
+
return (_jsx("div", { role: "status", "aria-live": "polite", "data-session-connecting-banner": "", className: cn('pointer-events-none absolute inset-x-0 top-0 z-30 flex justify-center px-3 pt-3', className), children: _jsxs("div", { className: "bg-background/85 text-muted-foreground flex max-w-full items-center gap-2 rounded-full border px-3 py-1.5 text-xs shadow-xs backdrop-blur-sm", children: [_jsx(Loading, { variant: "spokes", className: "size-3.5 shrink-0 text-current motion-reduce:animate-none" }), _jsx("span", { className: "min-w-0 text-pretty", children: statusNote ?? tI18nComplete.raw('textbeda94e911d3') }), stuck && canRestart ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "pointer-events-auto -mr-2 h-6 px-2 text-xs", disabled: restart.isPending, onClick: restart.restart, children: restart.isPending ? tI18nComplete.raw('text75d0f1469d16') : 'Restart' })) : null] }) }));
|
|
190
141
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { SessionLogTurn } from '@kortix/sdk';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
type MinimapMentionKind = 'agent' | 'file';
|
|
3
|
+
interface MinimapSegment {
|
|
4
4
|
/** For a mention this is the bare name — the `@` is drawn as an icon. */
|
|
5
5
|
text: string;
|
|
6
6
|
mention?: MinimapMentionKind;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
interface MinimapAttachment {
|
|
9
9
|
key: string;
|
|
10
10
|
name: string;
|
|
11
11
|
}
|
|
@@ -17,7 +17,7 @@ export interface MinimapItem {
|
|
|
17
17
|
segments: MinimapSegment[];
|
|
18
18
|
attachments: MinimapAttachment[];
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
interface MinimapDash {
|
|
21
21
|
item: MinimapItem;
|
|
22
22
|
index: number;
|
|
23
23
|
}
|
|
@@ -15,7 +15,7 @@ export declare function transcriptCarriesFirstPrompt(turns: readonly Pick<Sessio
|
|
|
15
15
|
* stand-in knew — text and file names — so it keeps drawing the bubble and the
|
|
16
16
|
* pending tiles through any frame where its own parts are still streaming.
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
interface FirstPromptHandoverInput {
|
|
19
19
|
/** The producer's copy exists (the boot preview store has this session). */
|
|
20
20
|
hasPreview: boolean;
|
|
21
21
|
/** The transcript's first user message carries its text right now. */
|
|
@@ -43,7 +43,7 @@ export interface FirstPromptHandoverInput {
|
|
|
43
43
|
*/
|
|
44
44
|
transcriptEmpty?: boolean;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
interface FirstPromptHandover {
|
|
47
47
|
/** Draw the stand-in bubble. */
|
|
48
48
|
showStandIn: boolean;
|
|
49
49
|
/** Hand the preview's text + file names to the real first turn. */
|
|
@@ -52,3 +52,4 @@ export interface FirstPromptHandover {
|
|
|
52
52
|
released: boolean;
|
|
53
53
|
}
|
|
54
54
|
export declare function resolveFirstPromptHandover(input: FirstPromptHandoverInput): FirstPromptHandover;
|
|
55
|
+
export {};
|
|
@@ -18,7 +18,7 @@ export declare function parseSessionReferences(text: string): {
|
|
|
18
18
|
cleanText: string;
|
|
19
19
|
sessions: ParsedSessionRef[];
|
|
20
20
|
};
|
|
21
|
-
|
|
21
|
+
interface ParsedProjectRef {
|
|
22
22
|
id?: string;
|
|
23
23
|
name: string;
|
|
24
24
|
path?: string;
|
|
@@ -28,11 +28,11 @@ export declare function parseProjectReferences(text: string): {
|
|
|
28
28
|
cleanText: string;
|
|
29
29
|
projects: ParsedProjectRef[];
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
interface ParsedFileMentionRef {
|
|
32
32
|
path: string;
|
|
33
33
|
name: string;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
interface ParsedAgentMentionRef {
|
|
36
36
|
name: string;
|
|
37
37
|
}
|
|
38
38
|
export declare function parseFileMentionReferences(text: string): {
|
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
* `isActionPanelOpen` / `isSidePanelOpen` / `panelMode`: those made the plan
|
|
6
6
|
* hop surfaces on every panel toggle, and they also re-rendered the whole
|
|
7
7
|
* transcript each time one of them changed.
|
|
8
|
+
* @deprecated Renders an OpenCode-only tool or todo list in an OpenCode session (pi has none). OpenCode is sunset (2026-10-23): no new code; delete with OpenCode.
|
|
8
9
|
*/
|
|
9
10
|
export declare function usePlanInChat(): boolean;
|
|
@@ -23,6 +23,7 @@ import { planBelongsToChat } from '../turn/plan-anchor.js';
|
|
|
23
23
|
* `isActionPanelOpen` / `isSidePanelOpen` / `panelMode`: those made the plan
|
|
24
24
|
* hop surfaces on every panel toggle, and they also re-rendered the whole
|
|
25
25
|
* transcript each time one of them changed.
|
|
26
|
+
* @deprecated Renders an OpenCode-only tool or todo list in an OpenCode session (pi has none). OpenCode is sunset (2026-10-23): no new code; delete with OpenCode.
|
|
26
27
|
*/
|
|
27
28
|
export function usePlanInChat() {
|
|
28
29
|
return planBelongsToChat({ isMobile: useIsMobile() });
|
|
@@ -95,7 +95,6 @@ import { resolveSessionContentState } from '../status/session-load-state.js';
|
|
|
95
95
|
import { nextOlderAutoloadArm, olderAutoloadExhausted, shouldLoadOlderHistory, } from './session-older-autoload.js';
|
|
96
96
|
import { OLDER_LOADING_MIN_MS, useHeldOlderLoading } from './session-older-loading.js';
|
|
97
97
|
import { HELD_COMMAND_ROW_PREFIX, canReleaseHeldCommand, heldCommandRows, } from '../composer/held-commands.js';
|
|
98
|
-
import { useReadinessSettling } from '../status/use-readiness-settling.js';
|
|
99
98
|
import { useSessionHistoryViewStore } from '../../internal/stores/session-history-view-store.js';
|
|
100
99
|
import { downloadBlob } from '../../internal/lib/utils/download.js';
|
|
101
100
|
import { RestoreNotice, hasOutOfContext } from './session-history.js';
|
|
@@ -3273,15 +3272,10 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
|
|
|
3273
3272
|
// its cause attached instead of a shrug. An actionable cause reports to
|
|
3274
3273
|
// Sentry; a routine browser tab discard only leaves a breadcrumb.
|
|
3275
3274
|
useReloadForensics(projectSessionId);
|
|
3276
|
-
// Nothing has answered yet and the mount is young: the difference between
|
|
3277
|
-
// "this session is asleep" and "we have not looked yet". Without it, every
|
|
3278
|
-
// page load painted the waking notice for a beat over a session that was
|
|
3279
|
-
// never asleep — which reads as a disconnect. See `settling`.
|
|
3280
|
-
const composerSettling = useReadinessSettling(runtimePhase === 'connecting');
|
|
3281
3275
|
// ONE answer for every surface that draws this session's runtime, and the
|
|
3282
3276
|
// reason the composer no longer guesses: `unknown` and `connecting` are
|
|
3283
|
-
// waits, and a wait is not a fault.
|
|
3284
|
-
//
|
|
3277
|
+
// waits, and a wait is not a fault. A routine wake says nothing either; only
|
|
3278
|
+
// a parked box with no wake under way earns the asleep notice.
|
|
3285
3279
|
const sessionConnection = projectSessionConnection({
|
|
3286
3280
|
sandbox: projectSessionRow?.status ?? null,
|
|
3287
3281
|
runtimeReady,
|
|
@@ -3294,7 +3288,6 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
|
|
|
3294
3288
|
pendingPrompt: allowSendBeforeReady && working.state === 'working',
|
|
3295
3289
|
pendingDelivery: working.pendingDelivery,
|
|
3296
3290
|
connection: sessionConnection,
|
|
3297
|
-
settling: composerSettling,
|
|
3298
3291
|
// Only an OPEN TURN the control plane is holding counts here. This tab's
|
|
3299
3292
|
// optimistic receipt and a stream frame both survive a box that died
|
|
3300
3293
|
// mid-turn, and a durable inbox row (which the projection also sources to
|
|
@@ -3425,7 +3418,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
|
|
|
3425
3418
|
if (transcriptFreshness === 'error' && !hasMessages) {
|
|
3426
3419
|
return (_jsx("div", { className: "bg-background relative flex h-full flex-col", "data-testid": "session-chat", children: _jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 p-6", "data-testid": "session-transcript-error", children: [_jsx("p", { className: "text-muted-foreground text-sm", children: tHardcodedUi.raw('i18nComplete.text8d0cef2d3405') }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => retryTranscript(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') })] }) }));
|
|
3427
3420
|
}
|
|
3428
|
-
return (_jsx("div", { className: "bg-background relative flex h-full flex-col", "data-testid": "session-chat", children: _jsx(SessionStartingLoader, {
|
|
3421
|
+
return (_jsx("div", { className: "bg-background relative flex h-full flex-col", "data-testid": "session-chat", children: _jsx(SessionStartingLoader, { projectId: projectId, sessionId: projectSessionId }) }));
|
|
3429
3422
|
}
|
|
3430
3423
|
return (
|
|
3431
3424
|
// Outcomes' cache coherence and the shareable `?cr=` link both hinge on
|
|
@@ -3442,7 +3435,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
|
|
|
3442
3435
|
: welcomeWallpaper, !hideHeader && (_jsx(SessionSiteHeader, { sessionId: sessionId, sessionTitle: session?.title || 'Untitled', leadingAction: headerLeadingAction, parent: parentCrumb })), _jsx(SessionContextModal, { open: contextModalOpen, onOpenChange: setContextModalOpen,
|
|
3443
3436
|
// A chat with no `useSession` source: the raw explorer lists the
|
|
3444
3437
|
// rows `useSessionSync` read for it.
|
|
3445
|
-
detachedRows: sessionState ? undefined : hookMessages, source: sessionState ?? DETACHED_SESSION_MESSAGES, session: session, providers: providers, allSessions: allSessions, servedModel: sessionServedModel, billedCost: sessionBilledCost(modelUsage) }), _jsx(CompactModal, { sessionId: sessionId, open: compactModalOpen, onOpenChange: setCompactModalOpen }), _jsx(ChangeRequestDetailDialog, { crId: openCrId, onClose: () => setOpenCrId(null), onOpenChangeRequest: setOpenCrId }), _jsxs(SessionBodyRow, { actionPanel: !hideHeader && !readOnly, children: [isNotFound ? (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm", children: tHardcodedUi.raw('componentsSessionSessionChat.line5821JsxTextThisSessionIsNotAccessibleRightNow') }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: "/", prefetch: true, onClick: () => {
|
|
3438
|
+
detachedRows: sessionState ? undefined : hookMessages, source: sessionState ?? DETACHED_SESSION_MESSAGES, session: session, sessionRow: projectSessionRow, providers: providers, allSessions: allSessions, servedModel: sessionServedModel, billedCost: sessionBilledCost(modelUsage) }), _jsx(CompactModal, { sessionId: sessionId, open: compactModalOpen, onOpenChange: setCompactModalOpen }), _jsx(ChangeRequestDetailDialog, { crId: openCrId, onClose: () => setOpenCrId(null), onOpenChangeRequest: setOpenCrId }), _jsxs(SessionBodyRow, { actionPanel: !hideHeader && !readOnly, children: [isNotFound ? (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm", children: tHardcodedUi.raw('componentsSessionSessionChat.line5821JsxTextThisSessionIsNotAccessibleRightNow') }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: "/", prefetch: true, onClick: () => {
|
|
3446
3439
|
try {
|
|
3447
3440
|
if (sessionId)
|
|
3448
3441
|
useTabStore.getState().closeTab?.(sessionId);
|
|
@@ -3553,7 +3546,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
|
|
|
3553
3546
|
pendingTurnIds.has(turn.id) &&
|
|
3554
3547
|
pendingTurnIds.has(turns[turnIndex - 1].id)
|
|
3555
3548
|
? 'mt-3'
|
|
3556
|
-
: 'mt-12', turn: turn, author: turnAuthor, showAuthor: showAuthorName(turnAuthor, groupChat, viewerId), stoppedBy: stoppedByName(messageAuthors, turn.id, viewerId), servedModel: servedModelOfTurn(modelUsage, turn.id, turns[turnIndex - 1]?.id), turnOutcome: turnOutcome, isLast: turn.id === lastUserMessageId, ownsPlan: turn.id === planAnchorId, sessionId: sessionId, sessionStatus: sessionStatus, permissions: pendingPermissions, questions: pendingQuestions, agentNames: agentNames, isFirstTurn: turnIndex === 0,
|
|
3549
|
+
: 'mt-12', turn: turn, author: turnAuthor, showAuthor: showAuthorName(turnAuthor, groupChat, viewerId), stoppedBy: stoppedByName(messageAuthors, turn.id, viewerId), servedModel: servedModelOfTurn(modelUsage, turn.id, turns[turnIndex - 1]?.id), turnOutcome: turnOutcome, isLast: turn.id === lastUserMessageId, ownsPlan: turn.id === planAnchorId, sessionId: sessionId, projectId: projectId, projectSessionId: projectSessionId, sessionStatus: sessionStatus, permissions: pendingPermissions, questions: pendingQuestions, agentNames: agentNames, isFirstTurn: turnIndex === 0,
|
|
3557
3550
|
// Handed over only once the stand-in has stepped
|
|
3558
3551
|
// aside — while it is up it draws these itself.
|
|
3559
3552
|
pendingText: turnIndex === 0 ? firstTurnHandover?.text : undefined, pendingAttachments: sentAttachmentsForTurn({
|
|
@@ -21,4 +21,19 @@ interface Metrics {
|
|
|
21
21
|
}
|
|
22
22
|
/** Pure token math for the modal — exported for characterization tests. */
|
|
23
23
|
export declare function getSessionContextMetrics(thread: SessionLogThread | null, providers: ProviderListResponse | undefined, pricingLookup: ModelPricingLookup): Metrics;
|
|
24
|
+
/**
|
|
25
|
+
* When the session started. The session row's creation, when the runtime is the row's root (or
|
|
26
|
+
* not up): a pi box stamps its runtime session at boot, so a woken session's runtime time is after
|
|
27
|
+
* its last reply. A sub-agent's runtime (not the row's root) keeps its own time.
|
|
28
|
+
*/
|
|
29
|
+
export declare function sessionStartedAt(runtime: {
|
|
30
|
+
id: string;
|
|
31
|
+
time: {
|
|
32
|
+
created: number;
|
|
33
|
+
};
|
|
34
|
+
} | undefined, row: {
|
|
35
|
+
created_at: string;
|
|
36
|
+
runtime_session_id?: string | null;
|
|
37
|
+
opencode_session_id?: string | null;
|
|
38
|
+
} | null | undefined): number | undefined;
|
|
24
39
|
export {};
|
|
@@ -16,7 +16,7 @@ export function getSessionContextMetrics(thread, providers, pricingLookup) {
|
|
|
16
16
|
? model.provider
|
|
17
17
|
: undefined;
|
|
18
18
|
const limit = model?.limit?.context;
|
|
19
|
-
const total = u.input + u.output +
|
|
19
|
+
const total = u.input + u.output + u.cache_read + u.cache_write; // reasoning is a subset of output
|
|
20
20
|
// The gateway registers every model under the single synthetic `kortix`
|
|
21
21
|
// opencode provider, so `provider.name` is always "Kortix" — even for a
|
|
22
22
|
// BYOK Anthropic/Bedrock/OpenAI model. The gateway separately serves the
|
|
@@ -42,3 +42,12 @@ export function getSessionContextMetrics(thread, providers, pricingLookup) {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* When the session started. The session row's creation, when the runtime is the row's root (or
|
|
47
|
+
* not up): a pi box stamps its runtime session at boot, so a woken session's runtime time is after
|
|
48
|
+
* its last reply. A sub-agent's runtime (not the row's root) keeps its own time.
|
|
49
|
+
*/
|
|
50
|
+
export function sessionStartedAt(runtime, row) {
|
|
51
|
+
const own = row && (!runtime || (row.runtime_session_id ?? row.opencode_session_id) === runtime.id);
|
|
52
|
+
return own ? Date.parse(row.created_at) : runtime?.time.created;
|
|
53
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Session, type SessionLogMessage } from '@kortix/sdk';
|
|
1
|
+
import { type ProjectSession, type Session, type SessionLogMessage } from '@kortix/sdk';
|
|
2
2
|
import { type ProviderListResponse, type SessionMessagesSource } from '@kortix/sdk/react';
|
|
3
3
|
import { type RawV1Row } from './session-context-message-explorer.js';
|
|
4
4
|
import type { ServedModelNotice } from '../turn/served-model.js';
|
|
@@ -23,6 +23,8 @@ interface SessionContextModalProps {
|
|
|
23
23
|
/** The session whose log the metrics read (`useSessionLogThread`). */
|
|
24
24
|
source: SessionMessagesSource;
|
|
25
25
|
session: Session | undefined;
|
|
26
|
+
/** The session's row: when it started (`sessionStartedAt`). */
|
|
27
|
+
sessionRow?: ProjectSession | null;
|
|
26
28
|
providers: ProviderListResponse | undefined;
|
|
27
29
|
allSessions?: Session[];
|
|
28
30
|
/** The model that answered the newest request, when a fallback model did. */
|
|
@@ -30,5 +32,5 @@ interface SessionContextModalProps {
|
|
|
30
32
|
/** What Kortix billed the session, when a fallback model answered a turn. */
|
|
31
33
|
billedCost?: number | null;
|
|
32
34
|
}
|
|
33
|
-
export declare function SessionContextModal({ open, onOpenChange, detachedRows, source, session, providers, allSessions, servedModel, billedCost, }: SessionContextModalProps): import("react").JSX.Element;
|
|
35
|
+
export declare function SessionContextModal({ open, onOpenChange, detachedRows, source, session, sessionRow, providers, allSessions, servedModel, billedCost, }: SessionContextModalProps): import("react").JSX.Element;
|
|
34
36
|
export {};
|
|
@@ -7,10 +7,10 @@ import { Close } from '../../icons/close.js';
|
|
|
7
7
|
import { useModelPricingLookup } from '../../internal/lib/model-pricing.js';
|
|
8
8
|
import { cn } from '../../lib/utils.js';
|
|
9
9
|
import { formatUsdAsCredits } from '../../_vendor/kortix-shared.js';
|
|
10
|
-
import { useSessionLog, useSessionLogThread, useSessionMessages, } from '@kortix/sdk/react';
|
|
11
|
-
import { useMemo } from 'react';
|
|
10
|
+
import { useSessionLog, useSessionLogOlder, useSessionLogThread, useSessionMessages, } from '@kortix/sdk/react';
|
|
11
|
+
import { useEffect, useMemo } from 'react';
|
|
12
12
|
import { CopyAllButton, rawExplorerData, SessionContextMessageExplorer, } from './session-context-message-explorer.js';
|
|
13
|
-
import { getSessionContextMetrics } from './session-context-metrics.js';
|
|
13
|
+
import { getSessionContextMetrics, sessionStartedAt } from './session-context-metrics.js';
|
|
14
14
|
import { SubSessionSection } from './session-context-sub-sessions.js';
|
|
15
15
|
const BREAKDOWN_ORDER = ['system', 'user', 'assistant', 'tool', 'other'];
|
|
16
16
|
const BREAKDOWN_SEGMENT_CLASS = {
|
|
@@ -140,14 +140,14 @@ function createFormatter(locale = 'en-US') {
|
|
|
140
140
|
// Stat primitives
|
|
141
141
|
// ============================================================================
|
|
142
142
|
function OverviewStat({ label, value, meta, valueClassName, }) {
|
|
143
|
-
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsx("div", { className: "text-muted-foreground text-xs", children: label }), _jsx("div", { className: cn('text-foreground truncate text-base font-semibold tabular-nums', valueClassName), children: value }), meta ? (_jsx("div", { className: "text-muted-foreground
|
|
143
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsx("div", { className: "text-muted-foreground text-xs", children: label }), _jsx("div", { className: cn('text-foreground truncate text-base font-semibold tabular-nums', valueClassName), children: value }), meta ? (_jsx("div", { className: "text-muted-foreground truncate text-xs tabular-nums", children: meta })) : null] }));
|
|
144
144
|
}
|
|
145
145
|
// ============================================================================
|
|
146
146
|
// Modal body — mounted only while the modal is open, so the store
|
|
147
147
|
// subscriptions and metric computations cost nothing during streaming.
|
|
148
148
|
// ============================================================================
|
|
149
149
|
const NO_MESSAGES = [];
|
|
150
|
-
function SessionContextModalBody({ detachedRows, source, session, providers, allSessions, servedModel, billedCost, }) {
|
|
150
|
+
function SessionContextModalBody({ detachedRows, source, session, sessionRow, providers, allSessions, servedModel, billedCost, }) {
|
|
151
151
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
152
152
|
const t = useTranslations('hardcodedUi.componentsSessionSessionContextModal');
|
|
153
153
|
const tCredits = useTranslations('settings.credits');
|
|
@@ -157,6 +157,13 @@ function SessionContextModalBody({ detachedRows, source, session, providers, all
|
|
|
157
157
|
// JSON read the active source: the records on v2, else the v1 rows.
|
|
158
158
|
const thread = useSessionLogThread(source);
|
|
159
159
|
const log = thread?.messages ?? NO_MESSAGES;
|
|
160
|
+
// The counts are of the whole thread: an open modal reads every older page of the log
|
|
161
|
+
// (the chat holds only the newest window). A failed page stops the walk.
|
|
162
|
+
const { hasOlder, isLoadingOlder, loadOlder } = useSessionLogOlder(source);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (hasOlder && !isLoadingOlder)
|
|
165
|
+
void loadOlder();
|
|
166
|
+
}, [hasOlder, isLoadingOlder, loadOlder]);
|
|
160
167
|
const logSource = useSessionLog(source)?.source;
|
|
161
168
|
// Only the v1 explorer reads the v1 rows: on v2 (or with detached rows) a
|
|
162
169
|
// null runtime id reads none, so a streamed v1 row re-renders nothing here.
|
|
@@ -206,7 +213,7 @@ function SessionContextModalBody({ detachedRows, source, session, providers, all
|
|
|
206
213
|
formatted: formatUsdAsCredits(billedCost ?? metrics.totalCost),
|
|
207
214
|
}) }), _jsx(OverviewStat, { label: t.raw('statMessages'), value: counts.all.toLocaleString() })] }), _jsxs("div", { className: "grid gap-x-12 gap-y-8 lg:grid-cols-2", children: [_jsxs("section", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-4", children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: t.raw('statContextUsed') }), _jsx("span", { className: cn('text-foreground text-sm font-semibold tabular-nums', usageTone), children: fmt.percent(ctx?.usage) })] }), (usageFraction != null || barSegments.length > 0) && (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-muted flex h-2 w-full overflow-hidden rounded-full", children: barSegments.length > 0 ? (barSegments.map((segment) => (_jsx("div", { className: cn('h-full', BREAKDOWN_SEGMENT_CLASS[segment.key]), style: { width: `${segment.width}%` } }, segment.key)))) : (_jsx("div", { className: "bg-foreground h-full", style: {
|
|
208
215
|
width: `${(usageFraction ?? 0) > 0 ? Math.max((usageFraction ?? 0) * 100, 1.5) : 0}%`,
|
|
209
|
-
} })) }), usageFraction != null && (_jsxs("div", { className: "text-muted-foreground text-right text-xs tabular-nums", children: [fmt.number(ctx?.total), " / ", fmt.number(ctx?.limit)] }))] })), breakdown.length > 0 && (_jsx("ul", { className: "space-y-2 pt-1", children: breakdown.map((segment) => (_jsxs("li", { className: "flex items-center gap-2 text-xs", children: [_jsx("span", { className: cn('size-2 shrink-0 rounded-
|
|
216
|
+
} })) }), usageFraction != null && (_jsxs("div", { className: "text-muted-foreground text-right text-xs tabular-nums", children: [fmt.number(ctx?.total), " / ", fmt.number(ctx?.limit)] }))] })), breakdown.length > 0 && (_jsx("ul", { className: "space-y-2 pt-1", children: breakdown.map((segment) => (_jsxs("li", { className: "flex items-center gap-2 text-xs", children: [_jsx("span", { className: cn('size-2 shrink-0 rounded-sm', BREAKDOWN_SEGMENT_CLASS[segment.key]) }), _jsx("span", { className: "text-foreground", children: t.raw(BREAKDOWN_LABEL_KEY[segment.key]) }), _jsxs("span", { className: "text-muted-foreground ml-auto tabular-nums", children: [fmt.number(segment.tokens), " \u00B7 ", segment.percent, "%"] })] }, segment.key))) }))] }), _jsxs("section", { className: "space-y-3", children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: t.raw('detailsLabel') }), _jsx("dl", { className: "divide-border/60 divide-y", children: [
|
|
210
217
|
{ label: t.raw('detailInput'), value: fmt.number(ctx?.input) },
|
|
211
218
|
{ label: t.raw('detailOutput'), value: fmt.number(ctx?.output) },
|
|
212
219
|
{ label: t.raw('detailReasoning'), value: fmt.number(ctx?.reasoning) },
|
|
@@ -219,10 +226,10 @@ function SessionContextModalBody({ detachedRows, source, session, providers, all
|
|
|
219
226
|
label: t.raw('detailAssistantMessages'),
|
|
220
227
|
value: counts.assistant.toLocaleString(),
|
|
221
228
|
},
|
|
222
|
-
{ label: t.raw('detailStarted'), value: fmt.time(session
|
|
229
|
+
{ label: t.raw('detailStarted'), value: fmt.time(sessionStartedAt(session, sessionRow)) },
|
|
223
230
|
{ label: t.raw('detailLastReply'), value: fmt.time(ctx ? Date.parse(ctx.message.created_at) : undefined) },
|
|
224
231
|
].map((row) => (_jsxs("div", { className: "flex items-baseline justify-between gap-4 py-1.5 first:pt-0 last:pb-0", children: [_jsx("dt", { className: "text-muted-foreground text-xs", children: row.label }), _jsx("dd", { className: "text-foreground text-xs font-medium tabular-nums", children: row.value })] }, row.label))) })] })] }), _jsx(SubSessionSection, { source: source, session: session, allSessions: allSessions, pricingLookup: pricingLookup, fmt: fmt }), _jsx(SessionContextMessageExplorer, { rows: raw.rows, formatTime: fmt.time, count: counts.all })] })] }));
|
|
225
232
|
}
|
|
226
|
-
export function SessionContextModal({ open, onOpenChange, detachedRows, source, session, providers, allSessions, servedModel, billedCost, }) {
|
|
227
|
-
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsx(ModalContent, { className: "max-h-[85vh] lg:max-w-3xl", showCloseButton: false, children: _jsx(SessionContextModalBody, { detachedRows: detachedRows, source: source, session: session, providers: providers, allSessions: allSessions, servedModel: servedModel, billedCost: billedCost }) }) }));
|
|
233
|
+
export function SessionContextModal({ open, onOpenChange, detachedRows, source, session, sessionRow, providers, allSessions, servedModel, billedCost, }) {
|
|
234
|
+
return (_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsx(ModalContent, { className: "max-h-[85vh] lg:max-w-3xl", showCloseButton: false, children: _jsx(SessionContextModalBody, { detachedRows: detachedRows, source: source, session: session, sessionRow: sessionRow, providers: providers, allSessions: allSessions, servedModel: servedModel, billedCost: billedCost }) }) }));
|
|
228
235
|
}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* runs after layout and before paint. No frame is painted with the jump,
|
|
37
37
|
* whenever the content lands.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
interface TurnScrollAnchor {
|
|
40
40
|
element: HTMLElement;
|
|
41
41
|
/** The anchor's position in content space at capture time. */
|
|
42
42
|
contentTop: number;
|
|
@@ -53,3 +53,4 @@ export declare function captureTurnScrollAnchor(container: HTMLElement): TurnScr
|
|
|
53
53
|
* idempotent. An anchor that left the DOM is never written.
|
|
54
54
|
*/
|
|
55
55
|
export declare function holdTurnScrollAnchor(container: HTMLElement, content: HTMLElement, anchor: TurnScrollAnchor | null): () => void;
|
|
56
|
+
export {};
|
|
@@ -9,5 +9,5 @@ import { SessionStartingLoader } from '../status/session-starting-loader.js';
|
|
|
9
9
|
import '../tool/tools/register.js';
|
|
10
10
|
export function SessionThread({ session, hideHeader = false, readOnly = false, quote, className }) {
|
|
11
11
|
const { projectId, sessionId, runtimeSessionId } = session;
|
|
12
|
-
return (_jsx(ProjectScope, { projectId: projectId, sessionId: sessionId, children: _jsx(ProviderPoolDraftBoundary, { identity: `${projectId}/${sessionId}`, children: _jsxs("div", { "data-slot": "session-thread", className: cn('relative flex h-full min-h-0 flex-col', className), children: [runtimeSessionId ? (_jsx(SessionChat, { sessionId: runtimeSessionId, projectSessionId: sessionId, projectId: projectId, sessionState: session, hideHeader: hideHeader, readOnly: readOnly, hostQuote: quote }, runtimeSessionId)) : (_jsx(SessionStartingLoader, {
|
|
12
|
+
return (_jsx(ProjectScope, { projectId: projectId, sessionId: sessionId, children: _jsx(ProviderPoolDraftBoundary, { identity: `${projectId}/${sessionId}`, children: _jsxs("div", { "data-slot": "session-thread", className: cn('relative flex h-full min-h-0 flex-col', className), children: [runtimeSessionId ? (_jsx(SessionChat, { sessionId: runtimeSessionId, projectSessionId: sessionId, projectId: projectId, sessionState: session, hideHeader: hideHeader, readOnly: readOnly, hostQuote: quote }, runtimeSessionId)) : (_jsx(SessionStartingLoader, { projectId: projectId, sessionId: sessionId })), _jsx(SessionPanelFallback, { projectId: projectId, projectSessionId: sessionId, runtimeSessionId: runtimeSessionId })] }) }) }));
|
|
13
13
|
}
|
|
@@ -83,6 +83,10 @@ interface SessionTurnProps {
|
|
|
83
83
|
isLast: boolean;
|
|
84
84
|
ownsPlan: boolean;
|
|
85
85
|
sessionId: string;
|
|
86
|
+
/** The Kortix project and project-session ids. With both, a settled turn
|
|
87
|
+
* offers Share (`TurnShareButton`). Absent (a child session): no Share. */
|
|
88
|
+
projectId?: string;
|
|
89
|
+
projectSessionId?: string;
|
|
86
90
|
sessionStatus: import('../../internal/session-types/index.js').SessionStatus | undefined;
|
|
87
91
|
permissions: PermissionRequest[];
|
|
88
92
|
questions: QuestionRequest[];
|