@qwen-code/web-shell 0.24.3 → 0.24.4-nightly.20260923.d0cd622a68
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/LICENSE +203 -0
- package/README.md +85 -1
- package/dist/daemon-react-sdk.js +1 -1
- package/dist/index.js +49318 -47393
- package/dist/sw.js +1 -1
- package/dist/{toolNames-DQad2xSu.js → toolNames-CaonquFc.js} +5677 -5279
- package/dist/transcript.js +10484 -10284
- package/dist/types/App.d.ts +9 -1
- package/dist/types/adapters/types.d.ts +4 -1
- package/dist/types/components/ChatPane.d.ts +3 -1
- package/dist/types/components/ConversationSearch.d.ts +18 -0
- package/dist/types/components/GlobalTurnNavigation.d.ts +3 -1
- package/dist/types/components/MessageList.d.ts +10 -2
- package/dist/types/components/SplitView.d.ts +3 -1
- package/dist/types/components/StandaloneAuth.d.ts +2 -1
- package/dist/types/components/WorkspaceSessionProvider.d.ts +2 -0
- package/dist/types/components/artifacts/ArtifactPanel.d.ts +7 -1
- package/dist/types/components/artifacts/SideTaskPanel.d.ts +5 -1
- package/dist/types/components/artifacts/TrajectoryOverview.d.ts +39 -0
- package/dist/types/components/channels/channel-editor-state.d.ts +9 -3
- package/dist/types/components/conversation-search-i18n.d.ts +6 -0
- package/dist/types/components/local-control-api.d.ts +3 -1
- package/dist/types/components/messages/AuthMessage.d.ts +2 -1
- package/dist/types/components/messages/ModelManagementSection.d.ts +3 -2
- package/dist/types/components/messages/PlanExecutionView.d.ts +14 -66
- package/dist/types/components/messages/SettingsMessage.d.ts +2 -2
- package/dist/types/components/messages/ToolGroup.d.ts +0 -1
- package/dist/types/components/messages/taskExecutionIndex.d.ts +80 -0
- package/dist/types/components/workflow/SessionWorkflowCockpit.d.ts +8 -1
- package/dist/types/components/workflow/SessionWorkflowInspector.d.ts +8 -1
- package/dist/types/components/workflow/session-workflow-model.d.ts +9 -1
- package/dist/types/config/pairing.d.ts +9 -0
- package/dist/types/config/remote-workspace-add.d.ts +25 -0
- package/dist/types/customization.d.ts +6 -0
- package/dist/types/daemon/session/session-context.d.ts +11 -0
- package/dist/types/daemon/session/transcript-page-table.d.ts +1 -1
- package/dist/types/daemon/session/turn-navigation-store.d.ts +32 -0
- package/dist/types/hooks/useMessageNavigation.d.ts +12 -0
- package/dist/types/hooks/useQueuedPrompts.d.ts +4 -1
- package/dist/types/hooks/useTranscriptViewport.d.ts +3 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/types/modelManagement.d.ts +16 -0
- package/dist/types/navigation.d.ts +37 -0
- package/dist/types/trajectory/buildTimeline.d.ts +48 -0
- package/dist/types/trajectory/timelineRange.d.ts +26 -0
- package/dist/types/trajectory/transcriptPageLoader.d.ts +4 -2
- package/dist/types/trajectory/types.d.ts +3 -3
- package/dist/types/trajectory/useTrajectoryWindow.d.ts +55 -17
- package/dist/types/utils/navigationUrl.d.ts +21 -0
- package/dist/types/utils/popoverRoom.d.ts +22 -0
- package/dist/types/utils/sessionPath.d.ts +1 -0
- package/dist/types/utils/unifiedDiff.d.ts +1 -0
- package/dist/types/utils/workspace.d.ts +3 -2
- package/package.json +19 -20
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { DaemonSessionArtifact, DaemonSessionTaskStatus } from '@qwen-code/sdk/daemon';
|
|
2
2
|
import type { ACPToolCall, TodoItem } from '../../adapters/types';
|
|
3
|
+
import { type SessionWorkflowProjection } from './session-workflow-model';
|
|
3
4
|
export interface SessionWorkflowInspectorProps {
|
|
4
5
|
todos: readonly TodoItem[];
|
|
5
6
|
tools: readonly ACPToolCall[];
|
|
6
7
|
tasks: readonly DaemonSessionTaskStatus[];
|
|
8
|
+
/**
|
|
9
|
+
* The projection shared by every workflow surface for this render. The app
|
|
10
|
+
* derives it once and hands the same object to the cockpit, the embedded
|
|
11
|
+
* graph and this inspector; when absent it is derived from the raw props.
|
|
12
|
+
*/
|
|
13
|
+
projection?: SessionWorkflowProjection;
|
|
7
14
|
artifacts: readonly DaemonSessionArtifact[];
|
|
8
15
|
selectedTodoId?: string;
|
|
9
16
|
onSelectedTodoIdChange: (todoId: string | undefined) => void;
|
|
@@ -12,4 +19,4 @@ export interface SessionWorkflowInspectorProps {
|
|
|
12
19
|
onOpenArtifact?: (artifactId: string) => void;
|
|
13
20
|
canvasMode?: boolean;
|
|
14
21
|
}
|
|
15
|
-
export declare function SessionWorkflowInspector({ todos, tools, tasks, artifacts, selectedTodoId, onSelectedTodoIdChange, onExpandGraph, onOpenSubagent, onOpenArtifact, canvasMode, }: SessionWorkflowInspectorProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function SessionWorkflowInspector({ todos, tools, tasks, projection: sharedProjection, artifacts, selectedTodoId, onSelectedTodoIdChange, onExpandGraph, onOpenSubagent, onOpenArtifact, canvasMode, }: SessionWorkflowInspectorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { DaemonSessionAgentTaskStatus, DaemonSessionTaskStatus } from '@qwen-code/sdk/daemon';
|
|
2
2
|
import type { ACPToolCall, TodoItem } from '../../adapters/types';
|
|
3
|
-
import { type PlanNodeStatus } from '../messages/
|
|
3
|
+
import { type PlanNodeStatus, type TaskExecutionIndex } from '../messages/taskExecutionIndex';
|
|
4
4
|
export interface SessionWorkflowProjection {
|
|
5
|
+
/**
|
|
6
|
+
* The task execution index this projection was built from, so a surface
|
|
7
|
+
* rendering per-tool live status (the graph's executions) reuses the one
|
|
8
|
+
* build instead of raising its own.
|
|
9
|
+
*/
|
|
10
|
+
taskIndex: TaskExecutionIndex;
|
|
5
11
|
todosById: ReadonlyMap<string, TodoItem>;
|
|
6
12
|
toolsByTaskId: ReadonlyMap<string, ACPToolCall>;
|
|
7
13
|
toolsByTodo: ReadonlyMap<string, readonly ACPToolCall[]>;
|
|
14
|
+
/** Tool calls whose `todo_id` is missing or points outside the plan. */
|
|
15
|
+
unassignedTools: readonly ACPToolCall[];
|
|
8
16
|
agentToolsByTodo: ReadonlyMap<string, readonly ACPToolCall[]>;
|
|
9
17
|
tasksByTool: ReadonlyMap<ACPToolCall, DaemonSessionAgentTaskStatus>;
|
|
10
18
|
states: ReadonlyMap<string, {
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
export interface RemotePathSuggestions {
|
|
2
|
+
dir: string;
|
|
3
|
+
sep: string;
|
|
4
|
+
suggestions: {
|
|
5
|
+
name: string;
|
|
6
|
+
path: string;
|
|
7
|
+
}[];
|
|
8
|
+
truncated: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Fetches directory suggestions from an arbitrary daemon origin without
|
|
12
|
+
* navigating the page. Used by the Add-workspace dialog's location switcher
|
|
13
|
+
* so the user can browse a remote daemon's folders in place.
|
|
14
|
+
*
|
|
15
|
+
* Goes through the current daemon's proxy route to avoid CSP issues.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fetchRemotePathSuggestions(daemonOrigin: string, prefix: string): Promise<RemotePathSuggestions>;
|
|
18
|
+
/**
|
|
19
|
+
* Registers a workspace on an arbitrary daemon origin without navigating the
|
|
20
|
+
* page. Used by the Add-workspace dialog when the user browsed a remote
|
|
21
|
+
* daemon's folders in place and then confirms the add.
|
|
22
|
+
*
|
|
23
|
+
* Goes through the current daemon's proxy route to avoid CSP issues.
|
|
24
|
+
*/
|
|
25
|
+
export declare function addWorkspaceToDaemon(daemonOrigin: string, cwd: string, persist: boolean, displayName?: string): Promise<void>;
|
|
1
26
|
export declare function isRemoteWorkspaceAddActive(): boolean;
|
|
2
27
|
export declare function clearRemoteWorkspaceAddStep(): void;
|
|
3
28
|
export declare function startRemoteWorkspaceAdd(daemonOrigin: string, token?: string): boolean;
|
|
@@ -10,6 +10,12 @@ import type { WebShellTheme } from './themeContext';
|
|
|
10
10
|
export type MarkdownContentSource = 'assistant' | 'thinking';
|
|
11
11
|
export interface MarkdownRenderContext {
|
|
12
12
|
source: MarkdownContentSource;
|
|
13
|
+
/**
|
|
14
|
+
* 当前消息的生成态;历史或静态内容为 false。所属 MessageList 空闲
|
|
15
|
+
* (isResponding=false)时,恢复 replay 中残留的 streaming 标记也会被
|
|
16
|
+
* 收口为 false,且已收口的行在会话重新响应时、内容不变的前提下保持收口。
|
|
17
|
+
*/
|
|
18
|
+
isStreaming: boolean;
|
|
13
19
|
}
|
|
14
20
|
export interface WebShellCodeBlockRenderInfo {
|
|
15
21
|
/**
|
|
@@ -7,6 +7,17 @@ import type { DaemonCapabilities, DaemonSession } from '@qwen-code/sdk/daemon';
|
|
|
7
7
|
import type { DaemonProductSessionContext, DaemonStandaloneConnectionState } from './types.js';
|
|
8
8
|
export declare function resolveProviderSessionContext(sessionContext: DaemonProductSessionContext | undefined, workspaceCwd: string | undefined, inheritedWorkspaceCwd: string | undefined): DaemonProductSessionContext | undefined;
|
|
9
9
|
export declare function resolveActionSessionContext(sessionContext: DaemonProductSessionContext | undefined, workspaceCwd: string | undefined, fallback: DaemonProductSessionContext | undefined): DaemonProductSessionContext | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* The Live workspace is the daemon's own, and it announces it by tagging that
|
|
12
|
+
* entry `kind: 'live'` — a tag no daemon old enough to lack Live session
|
|
13
|
+
* routing has ever sent. That tag is therefore the whole precondition.
|
|
14
|
+
*
|
|
15
|
+
* It deliberately does not ask for `multi_workspace_sessions`. That feature
|
|
16
|
+
* counts *user* workspaces (`listEntries()` drops the Live entry as internal),
|
|
17
|
+
* so a daemon serving a single project withholds it while still advertising
|
|
18
|
+
* the Live workspace in the very same payload — and a Live session could not
|
|
19
|
+
* be opened at all. The checks below are what actually keep this safe.
|
|
20
|
+
*/
|
|
10
21
|
export declare function resolveLiveSessionWorkspaceCwd(capabilities: DaemonCapabilities): string;
|
|
11
22
|
export declare function sessionContextKey(context: DaemonProductSessionContext | undefined): string;
|
|
12
23
|
export declare function restoreSessionContextMatches(requested: DaemonProductSessionContext | undefined, active: DaemonProductSessionContext | undefined): boolean;
|
|
@@ -118,7 +118,7 @@ export declare class HistoricalTranscriptPageTable {
|
|
|
118
118
|
clearSelection(): void;
|
|
119
119
|
setViewportAnchor(viewportId: string, pageId?: string): void;
|
|
120
120
|
admitBefore(beforeRecordId: string, snapshot: string, response: DaemonSessionTranscriptPage): AdmittedHistoricalTarget | undefined;
|
|
121
|
-
admitAnchor(ordinal: number, turnId: string, snapshot: string, response: DaemonSessionTranscriptPage): AdmittedHistoricalTarget;
|
|
121
|
+
admitAnchor(ordinal: number, turnId: string, snapshot: string, response: DaemonSessionTranscriptPage, targetRecordId?: string): AdmittedHistoricalTarget;
|
|
122
122
|
private admitAnchorPage;
|
|
123
123
|
beginBoundaryLoad(rangeId: string, direction: BoundaryDirection): FrozenTranscriptBoundaryRequest | undefined;
|
|
124
124
|
reopenLiveBoundary(rangeId: string, beforeRecordId: string, snapshot: string): void;
|
|
@@ -5,6 +5,35 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { DaemonEvent, DaemonSessionTranscriptPage, DaemonSessionTranscriptPageOptions, DaemonSessionTurnIndexEntry, DaemonSessionTurnIndexPage, DaemonSessionTurnIndexPageOptions, DaemonTranscriptBlock } from '@qwen-code/sdk/daemon';
|
|
7
7
|
import { type HistoricalTranscriptPage, type HistoricalTranscriptRange, type HistoricalViewportRange, type MaterializedTranscriptPage } from './transcript-page-table.js';
|
|
8
|
+
export interface ConversationSearchHit {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
snapshot: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
recordId: string;
|
|
13
|
+
liveBlockId?: string;
|
|
14
|
+
turnId: string;
|
|
15
|
+
turnOrdinal: number;
|
|
16
|
+
role: 'user' | 'assistant';
|
|
17
|
+
snippet: string;
|
|
18
|
+
matchStart: number;
|
|
19
|
+
matchEnd: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ConversationSearchResult {
|
|
22
|
+
hits: ConversationSearchHit[];
|
|
23
|
+
messageCount: number;
|
|
24
|
+
matchCount: number;
|
|
25
|
+
complete: boolean;
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface ConversationSearchOptions extends HistoryViewportRequest {
|
|
29
|
+
onProgress?: (result: ConversationSearchResult) => void;
|
|
30
|
+
stopAfterMessages?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function createConversationSearchSnippet(text: string, query: string): {
|
|
33
|
+
snippet: string;
|
|
34
|
+
matchStart: number;
|
|
35
|
+
matchEnd: number;
|
|
36
|
+
} | undefined;
|
|
8
37
|
export interface DaemonTurnIndexPage {
|
|
9
38
|
start: number;
|
|
10
39
|
end: number;
|
|
@@ -94,6 +123,9 @@ export interface HistoryViewportRequest {
|
|
|
94
123
|
isCurrent(): boolean;
|
|
95
124
|
}
|
|
96
125
|
export interface DaemonHistoryNavigationStore extends DaemonTurnNavigationStore {
|
|
126
|
+
scanConversation(query: string, options: ConversationSearchOptions): Promise<ConversationSearchResult>;
|
|
127
|
+
resolveMessageRecord(recordId: string, request: HistoryViewportRequest): Promise<ConversationSearchHit | undefined>;
|
|
128
|
+
locateViewportSearchHit(hit: ConversationSearchHit, request: HistoryViewportRequest, releaseAnchor: () => void): Promise<DaemonTurnLocation>;
|
|
97
129
|
locateViewportOrdinal(ordinal: number, request: HistoryViewportRequest, releaseAnchor: () => void): Promise<DaemonTurnLocation>;
|
|
98
130
|
getViewportSnapshot(): DaemonHistoryViewportSnapshot;
|
|
99
131
|
captureLiveBoundary(): LiveHistoryBoundary;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { MessageListHandle } from '../components/MessageList';
|
|
3
|
+
export interface WebShellMessageNavigationRequest {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
/** JSONL 持久化记录 ID,不是渲染块 ID、摘要或时间戳。 */
|
|
6
|
+
recordId: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export type WebShellMessageNavigationResult = {
|
|
10
|
+
status: 'located' | 'not_found' | 'not_ready' | 'session_mismatch' | 'unsupported' | 'cancelled' | 'error';
|
|
11
|
+
};
|
|
12
|
+
export declare function useMessageNavigation(messageListRef: RefObject<MessageListHandle | null>, active?: boolean): ({ sessionId, recordId, signal, }: WebShellMessageNavigationRequest) => Promise<WebShellMessageNavigationResult>;
|
|
@@ -13,6 +13,9 @@ interface RefBox<T> {
|
|
|
13
13
|
current: T;
|
|
14
14
|
}
|
|
15
15
|
interface UseQueuedPromptsArgs {
|
|
16
|
+
/** Synchronous caller policy, checked again immediately before SDK dispatch.
|
|
17
|
+
* Return a message to reject locally; undefined permits dispatch. */
|
|
18
|
+
getPromptDispatchError?: (text: string) => string | undefined;
|
|
16
19
|
connected: boolean;
|
|
17
20
|
writeBlocked?: boolean;
|
|
18
21
|
runtimeStopped?: boolean;
|
|
@@ -71,5 +74,5 @@ export interface UseQueuedPromptsResult {
|
|
|
71
74
|
editLastQueuedPrompt: () => boolean;
|
|
72
75
|
clearQueuedPrompts: () => boolean;
|
|
73
76
|
}
|
|
74
|
-
export declare function useQueuedPrompts({ connected, writeBlocked, runtimeStopped, sessionId, workspaceCwd, clientId, canMutateMidTurn, canQueryMidTurn, canInjectMidTurnMedia, workspaceFileActions, streamingState, sessionHasActivePrompt, holdQueuedPromptsLocally, sessionActions, store, editorRef, reportError, t, }: UseQueuedPromptsArgs): UseQueuedPromptsResult;
|
|
77
|
+
export declare function useQueuedPrompts({ getPromptDispatchError, connected, writeBlocked, runtimeStopped, sessionId, workspaceCwd, clientId, canMutateMidTurn, canQueryMidTurn, canInjectMidTurnMedia, workspaceFileActions, streamingState, sessionHasActivePrompt, holdQueuedPromptsLocally, sessionActions: unguardedSessionActions, store, editorRef, reportError, t, }: UseQueuedPromptsArgs): UseQueuedPromptsResult;
|
|
75
78
|
export {};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type Translator } from '../adapters/localizedMessages';
|
|
7
7
|
import type { Message } from '../adapters/types';
|
|
8
|
+
import type { ConversationSearchHit } from '../daemon/session/turn-navigation-store';
|
|
8
9
|
export declare function useTranscriptViewport(liveMessages: Message[], t: Translator): {
|
|
9
10
|
navigation: import("..").DaemonTurnNavigationSnapshot;
|
|
10
11
|
store: import("../daemon/session/turn-navigation-store").DaemonHistoryNavigationStore;
|
|
@@ -12,7 +13,8 @@ export declare function useTranscriptViewport(liveMessages: Message[], t: Transl
|
|
|
12
13
|
blockId: string;
|
|
13
14
|
token: number;
|
|
14
15
|
} | undefined;
|
|
15
|
-
selectOrdinal: (ordinal: number) => Promise<
|
|
16
|
+
selectOrdinal: (ordinal: number, hit?: ConversationSearchHit, isCurrent?: () => boolean) => Promise<boolean | "cancelled">;
|
|
17
|
+
selectSearchHit: (hit: ConversationSearchHit, isCurrent?: () => boolean) => Promise<boolean | "cancelled">;
|
|
16
18
|
cancelSelection: () => void;
|
|
17
19
|
continueLive: (blockId?: string) => void;
|
|
18
20
|
messages: import("../adapters/messageTypes").DaemonMessage[];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WebShellUrlNavigationOptions } from './navigation';
|
|
2
|
+
export type { WebShellUrlNavigationOptions, WebShellPage } from './navigation';
|
|
1
3
|
import { type DaemonProductSessionContext } from '@qwen-code/web-shell/daemon-react-sdk';
|
|
2
4
|
import { type WebShellProps } from './App';
|
|
3
5
|
import { type WebShellBrowserNotificationsOptions } from './browser-turn-notifications';
|
|
@@ -6,6 +8,12 @@ export type { WebShellTranscriptProps } from './components/WebShellTranscript';
|
|
|
6
8
|
export * from './daemon-react-sdk';
|
|
7
9
|
export type { WebShellBrowserNotificationsOptions } from './browser-turn-notifications';
|
|
8
10
|
export interface WebShellWithProvidersProps extends WebShellProps {
|
|
11
|
+
/**
|
|
12
|
+
* Opt in to URL routing. Explicit initial session target props override the
|
|
13
|
+
* URL; later target prop changes replace it. Stop host history writes when
|
|
14
|
+
* enabled. Omit to keep host-owned navigation. basePath defaults to root.
|
|
15
|
+
*/
|
|
16
|
+
urlNavigation?: WebShellUrlNavigationOptions;
|
|
9
17
|
/** Connect browser notifications with optional branding and an initial preference (off by default). */
|
|
10
18
|
browserNotifications?: WebShellBrowserNotificationsOptions;
|
|
11
19
|
/** Daemon API base URL. Defaults to the browser origin when omitted. */
|
|
@@ -67,3 +75,5 @@ export { ECHARTS_FULLDATA_LANGUAGE, EchartsFullDataBlock, createMarkdownChartReg
|
|
|
67
75
|
export type { DatasetCell, EchartsFullDataBlockProps, EchartsFullDataOption, EchartsFullDataRefMeta, EchartsFullDataRefResolver, EchartsFullDataResolvedDataset, EchartsFullDataRendererOptions, EchartsInstance, EchartsRuntime, EchartsRuntimeLoader, } from './components/messages/MarkdownChartRenderer';
|
|
68
76
|
export { WEB_SHELL_SETTING_ITEM_IDS } from './settings';
|
|
69
77
|
export type { WebShellSettingItemId, WebShellSettingsOptions, } from './settings';
|
|
78
|
+
export type { WebShellModelManagementOptions } from './modelManagement';
|
|
79
|
+
export type { WebShellMessageNavigationRequest, WebShellMessageNavigationResult, } from './hooks/useMessageNavigation';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** WebShell interaction controls only; daemon APIs and external provisioning remain available. */
|
|
2
|
+
export interface WebShellModelManagementOptions {
|
|
3
|
+
/** Allow provider/model setup, including /auth. Defaults to true. */
|
|
4
|
+
allowAdd?: boolean;
|
|
5
|
+
/** Allow model deletion. Defaults to true. */
|
|
6
|
+
allowDelete?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveModelManagement(options?: WebShellModelManagementOptions): Required<WebShellModelManagementOptions>;
|
|
9
|
+
/** The command identity a session's command snapshot carries per entry. */
|
|
10
|
+
export interface ModelSetupCommandInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
source?: string;
|
|
13
|
+
altNames?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function isModelCommandSnapshotReady(commands?: readonly ModelSetupCommandInfo[]): boolean;
|
|
16
|
+
export declare function isModelSetupCommand(input: string, commands?: readonly ModelSetupCommandInfo[]): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactNode } from 'react';
|
|
7
|
+
import type { DaemonProductSessionContext } from '@qwen-code/web-shell/daemon-react-sdk';
|
|
8
|
+
import type { WebShellProps } from './App';
|
|
9
|
+
import { type WebShellPage, type WebShellRoute } from './utils/navigationUrl';
|
|
10
|
+
export type { WebShellPage } from './utils/navigationUrl';
|
|
11
|
+
export interface WebShellUrlNavigationOptions {
|
|
12
|
+
/** URL pathname containing the shell, e.g. /agentic-code. Defaults to root. */
|
|
13
|
+
basePath?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface NavigationSessionProps {
|
|
16
|
+
sessionId?: string;
|
|
17
|
+
workspaceId?: string;
|
|
18
|
+
workspaceCwd?: string;
|
|
19
|
+
sessionContext?: DaemonProductSessionContext;
|
|
20
|
+
}
|
|
21
|
+
interface NavigationController {
|
|
22
|
+
route: WebShellRoute;
|
|
23
|
+
revision: number;
|
|
24
|
+
openPage: (page: WebShellPage) => void;
|
|
25
|
+
returnToChat: () => void;
|
|
26
|
+
reconcilePage: (page: WebShellPage | undefined) => void;
|
|
27
|
+
beginSessionNavigation: (sessionId?: string, workspaceId?: string, sessionContext?: DaemonProductSessionContext) => void;
|
|
28
|
+
cancelSessionNavigation: () => void;
|
|
29
|
+
finishNewChat: (context?: DaemonProductSessionContext) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare const useWebShellNavigation: () => NavigationController | undefined;
|
|
32
|
+
export declare function WebShellNavigationBoundary({ options, externalTarget, onSessionIdChange, children, }: {
|
|
33
|
+
options?: WebShellUrlNavigationOptions;
|
|
34
|
+
externalTarget: NavigationSessionProps;
|
|
35
|
+
onSessionIdChange?: WebShellProps['onSessionIdChange'];
|
|
36
|
+
children: (target: NavigationSessionProps, onChange: NonNullable<WebShellProps['onSessionIdChange']>) => ReactNode;
|
|
37
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { Trajectory, TrajectoryRow } from './types';
|
|
7
|
+
/** 0 main-session requests · 1 tool calls · 2 subagent requests. */
|
|
8
|
+
export type TimelineLane = 0 | 1 | 2;
|
|
9
|
+
export interface TimelineSpan {
|
|
10
|
+
/** The table row this span draws; selecting a span selects this row. */
|
|
11
|
+
rowKey: string;
|
|
12
|
+
row: TrajectoryRow;
|
|
13
|
+
lane: TimelineLane;
|
|
14
|
+
/** Offset into the compressed domain, ms. */
|
|
15
|
+
start: number;
|
|
16
|
+
end: number;
|
|
17
|
+
/** Requests with a recorded TTFT only: where the first token landed. */
|
|
18
|
+
ttftEnd?: number;
|
|
19
|
+
error: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface TimelineTurnMark {
|
|
22
|
+
turnIndex: number;
|
|
23
|
+
at: number;
|
|
24
|
+
}
|
|
25
|
+
export interface TimelineModel {
|
|
26
|
+
/** Ascending by start, then lane, then row key. */
|
|
27
|
+
spans: TimelineSpan[];
|
|
28
|
+
turnMarks: TimelineTurnMark[];
|
|
29
|
+
/**
|
|
30
|
+
* Length of the domain. Idle gaps are cut out, so this is the time during
|
|
31
|
+
* which at least one request or tool was running — not wall-clock time.
|
|
32
|
+
*/
|
|
33
|
+
total: number;
|
|
34
|
+
/**
|
|
35
|
+
* Rows with a recorded duration but no recorded start — tool calls from a
|
|
36
|
+
* session written before tools recorded one. Not drawn, never estimated.
|
|
37
|
+
*/
|
|
38
|
+
droppedRows: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Project the rows that recorded both a start and a duration onto one time
|
|
42
|
+
* axis, with the idle stretches between them cut out.
|
|
43
|
+
*
|
|
44
|
+
* Only measured values are drawn. A row without a start is left off rather
|
|
45
|
+
* than placed next to its neighbours: a bar in the wrong place reads as a fact.
|
|
46
|
+
* Returns `undefined` when nothing in the window can be drawn.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildTimeline(trajectory: Trajectory): TimelineModel | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { TimelineModel } from './buildTimeline';
|
|
7
|
+
/**
|
|
8
|
+
* A stretch of the overview's domain, in the same compressed milliseconds as
|
|
9
|
+
* the spans. Always ordered: `start <= end`.
|
|
10
|
+
*/
|
|
11
|
+
export interface TimelineRange {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Rows whose span was running at any point in the range, ends included.
|
|
17
|
+
*
|
|
18
|
+
* With idle time cut out of the domain, spans cover it end to end, so any
|
|
19
|
+
* range finds at least one row. That stops holding once a mode draws real
|
|
20
|
+
* clock time with its gaps left in.
|
|
21
|
+
*
|
|
22
|
+
* Closed on both sides so a zero-length span sitting on an edge — a tool that
|
|
23
|
+
* reported 0 ms, or a range made by a drag that stopped exactly on a bar's
|
|
24
|
+
* end — still counts as inside rather than vanishing from both sides.
|
|
25
|
+
*/
|
|
26
|
+
export declare function rowKeysInRange(model: TimelineModel, range: TimelineRange): Set<string>;
|
|
@@ -12,6 +12,7 @@ export interface TrajectoryTranscriptClient {
|
|
|
12
12
|
getSessionTranscriptPage(sessionId: string, opts: {
|
|
13
13
|
direction?: 'backward';
|
|
14
14
|
limit?: number;
|
|
15
|
+
cursor?: string;
|
|
15
16
|
}): Promise<TrajectoryPageResult>;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -20,8 +21,9 @@ export interface TrajectoryTranscriptClient {
|
|
|
20
21
|
* Identity matters: the loader is the window hook's effect dependency, so a
|
|
21
22
|
* fresh function on every render would refetch the newest page on every render.
|
|
22
23
|
*
|
|
23
|
-
* Reads
|
|
24
|
-
* turn is the one they just watched run
|
|
24
|
+
* Reads walk backward from the newest page, which is the one the reader wants —
|
|
25
|
+
* the newest turn is the one they just watched run — following the cursor each
|
|
26
|
+
* page hands back. It deliberately goes through the raw
|
|
25
27
|
* client rather than
|
|
26
28
|
* `DaemonSessionClient.getTranscriptPage`, which hydrates attachments and would
|
|
27
29
|
* fetch image bytes this table never shows, and it never asks for the
|
|
@@ -42,9 +42,9 @@ export type TrajectoryEntry = {
|
|
|
42
42
|
export interface TrajectoryTiming {
|
|
43
43
|
durationMs: number;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Epoch ms. Requests have one whenever their end was recorded; tool calls
|
|
46
|
+
* only when the session recorded the call's start, which older sessions did
|
|
47
|
+
* not.
|
|
48
48
|
*/
|
|
49
49
|
startedAt?: number;
|
|
50
50
|
/** Requests only: dispatch to first user-visible content. */
|
|
@@ -11,6 +11,18 @@ import type { Trajectory } from './types';
|
|
|
11
11
|
* shorter than this — `truncated` is what says so, never the count.
|
|
12
12
|
*/
|
|
13
13
|
export declare const TRAJECTORY_PAGE_SIZE = 250;
|
|
14
|
+
/**
|
|
15
|
+
* Pages read, newest first, before the window is drawn. The window is
|
|
16
|
+
* re-projected whole on every change and each page is a full replay of its
|
|
17
|
+
* records, so this is what bounds the retained bytes and the per-change work —
|
|
18
|
+
* and holding four pages where the table held one raises both by four, to 1000
|
|
19
|
+
* records nominally. The true worst case is nearer 3000: a backward page can
|
|
20
|
+
* exceed the limit it was asked for, because the daemon extends it to keep
|
|
21
|
+
* turns and tool pairs whole, up to `3 * limit` records and its own 4 MB
|
|
22
|
+
* ceiling. Four is where that ceiling stays defensible while still reaching a
|
|
23
|
+
* useful way back.
|
|
24
|
+
*/
|
|
25
|
+
export declare const TRAJECTORY_MAX_PAGES = 4;
|
|
14
26
|
/**
|
|
15
27
|
* The fields of a transcript page this view reads. Narrower than the daemon's
|
|
16
28
|
* own page type on purpose: the client's page satisfies it structurally, and a
|
|
@@ -19,11 +31,14 @@ export declare const TRAJECTORY_PAGE_SIZE = 250;
|
|
|
19
31
|
export interface TrajectoryPageResult {
|
|
20
32
|
events: readonly DaemonEvent[];
|
|
21
33
|
hasMore: boolean;
|
|
34
|
+
/** Where the next older page starts; absent when there is none to ask for. */
|
|
35
|
+
nextCursor?: string;
|
|
22
36
|
partial?: true;
|
|
23
37
|
replayError?: string;
|
|
24
38
|
}
|
|
25
39
|
/**
|
|
26
|
-
* Fetches
|
|
40
|
+
* Fetches one page of the session's transcript: the newest without a cursor,
|
|
41
|
+
* the one older than a previous page with that page's `nextCursor`.
|
|
27
42
|
*
|
|
28
43
|
* Supplied by the host rather than called here so the panel never reaches for
|
|
29
44
|
* a daemon client of its own. There is no cancellation: the daemon client
|
|
@@ -32,11 +47,12 @@ export interface TrajectoryPageResult {
|
|
|
32
47
|
*/
|
|
33
48
|
export type TrajectoryPageLoader = (opts: {
|
|
34
49
|
limit: number;
|
|
50
|
+
cursor?: string;
|
|
35
51
|
}) => Promise<TrajectoryPageResult>;
|
|
36
52
|
/**
|
|
37
|
-
* Why
|
|
38
|
-
*
|
|
39
|
-
*
|
|
53
|
+
* Why a read failed. `partial` is not a message — the daemon reports it as a
|
|
54
|
+
* flag — so it is carried as a kind for the view to name, rather than as a
|
|
55
|
+
* word that would end up quoted at the reader.
|
|
40
56
|
*/
|
|
41
57
|
export type TrajectoryWindowFailure = {
|
|
42
58
|
kind: 'partial';
|
|
@@ -47,29 +63,51 @@ export type TrajectoryWindowFailure = {
|
|
|
47
63
|
export interface TrajectoryWindow {
|
|
48
64
|
trajectory: Trajectory | undefined;
|
|
49
65
|
status: 'idle' | 'loading' | 'ready' | 'error';
|
|
66
|
+
/**
|
|
67
|
+
* The newest page could not be read. The window already on screen, if any,
|
|
68
|
+
* is kept.
|
|
69
|
+
*/
|
|
50
70
|
error?: TrajectoryWindowFailure;
|
|
51
71
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
72
|
+
* Pages read so far by the load under way, or held by the window once it
|
|
73
|
+
* is ready. What the placeholder counts while the walk back is running.
|
|
74
|
+
*/
|
|
75
|
+
loadedPages: number;
|
|
76
|
+
/**
|
|
77
|
+
* The session has history older than the window: the walk stopped at the
|
|
78
|
+
* page cap, at a page the daemon gave no cursor for, or at an older page it
|
|
79
|
+
* could not read.
|
|
54
80
|
*/
|
|
55
81
|
truncated: boolean;
|
|
56
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* An older page could not be read, so the window stops just after it. The
|
|
84
|
+
* newer pages are drawn; `refresh` is the retry.
|
|
85
|
+
*/
|
|
86
|
+
olderFailure?: TrajectoryWindowFailure;
|
|
87
|
+
/** Read the window again from the newest page. Also every retry. */
|
|
57
88
|
refresh: () => void;
|
|
58
89
|
}
|
|
59
90
|
/**
|
|
60
|
-
* Hold
|
|
61
|
-
*
|
|
91
|
+
* Hold a window of one session's transcript and fold it into a trajectory.
|
|
92
|
+
*
|
|
93
|
+
* The pages are this view's own: paged replay is the only path that emits
|
|
94
|
+
* timing frames, and the chat store is fed by the live stream and by bulk
|
|
95
|
+
* replay, neither of which carries them. Reading here also keeps the window
|
|
96
|
+
* contiguous by construction, which is what lets the projection pair a frame
|
|
97
|
+
* with what it measured.
|
|
62
98
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
99
|
+
* A load walks back from the newest page by cursor, up to the page cap, and
|
|
100
|
+
* the window lands once, whole, when the walk ends. Nothing is ever put in
|
|
101
|
+
* front of rows already drawn: a list that grew upwards under the reader would
|
|
102
|
+
* have to move its scroll offset, its keys, its selection and its focus in
|
|
103
|
+
* step, and no part of that is observable outside a real browser.
|
|
68
104
|
*
|
|
69
|
-
* `refresh`
|
|
70
|
-
* are chosen per request, so a fresh page and the held
|
|
71
|
-
* unknown amount and cannot be joined without dropping or
|
|
105
|
+
* `refresh` rebuilds the window from the newest page rather than splicing into
|
|
106
|
+
* it: page boundaries are chosen per request, so a fresh page and the held
|
|
107
|
+
* ones overlap by an unknown amount and cannot be joined without dropping or
|
|
108
|
+
* repeating records.
|
|
72
109
|
*/
|
|
73
110
|
export declare function useTrajectoryWindow(loadPage: TrajectoryPageLoader | undefined, options?: {
|
|
74
111
|
pageSize?: number;
|
|
112
|
+
maxPages?: number;
|
|
75
113
|
}): TrajectoryWindow;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
export declare const WEB_SHELL_PAGES: readonly ["plugins", "channels", "scheduled-tasks", "goals", "settings"];
|
|
7
|
+
export type WebShellPage = (typeof WEB_SHELL_PAGES)[number];
|
|
8
|
+
export interface SessionUrlTarget {
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
workspaceId?: string;
|
|
11
|
+
context?: 'standalone' | 'live';
|
|
12
|
+
}
|
|
13
|
+
export type WebShellRoute = {
|
|
14
|
+
page: WebShellPage;
|
|
15
|
+
} | ({
|
|
16
|
+
page: 'chat';
|
|
17
|
+
} & SessionUrlTarget);
|
|
18
|
+
export declare function normalizeNavigationBasePath(basePath?: string): string;
|
|
19
|
+
export declare function isWebShellPage(value: string): value is WebShellPage;
|
|
20
|
+
export declare function readNavigationUrl(url: URL, basePath?: string): WebShellRoute | undefined;
|
|
21
|
+
export declare function buildNavigationUrl(current: URL, route: WebShellRoute, basePath?: string): URL;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The top clearance a host declares for upward-opening popovers through
|
|
3
|
+
* `--web-shell-popover-safe-top`, or `undefined` when it declares none. A
|
|
4
|
+
* declared `0px` stays 0 instead of falling back to a default.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readPopoverSafeTop(element: Element): number | undefined;
|
|
7
|
+
export declare const POPOVER_MIN_HEIGHT = 96;
|
|
8
|
+
/**
|
|
9
|
+
* The highest viewport y at which a popover positioned inside `anchor` is still
|
|
10
|
+
* painted: below the top edge of every ancestor that clips its overflow, and
|
|
11
|
+
* below the host's declared safe top.
|
|
12
|
+
*
|
|
13
|
+
* With no declared safe top the bound starts at 0, because an in-flow popover
|
|
14
|
+
* is already bounded by the clipping ancestors this walk measures. A popover
|
|
15
|
+
* portaled out of that chain has no clipping ancestor to measure and needs a
|
|
16
|
+
* numeric allowance instead — see AtMentionPanel's
|
|
17
|
+
* `readPopoverSafeTop(anchor) ?? 48`. Keep the two defaults distinct: folding
|
|
18
|
+
* the 48 in here would steal room from in-flow popovers, and folding the 0
|
|
19
|
+
* into AtMentionPanel would let it rise under a host header that declares
|
|
20
|
+
* nothing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function popoverTopEdge(anchor: Element): number;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2026 Qwen Team
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
export declare function inferStandaloneBasePath(pathname: string): string;
|
|
6
7
|
/**
|
|
7
8
|
* Build the pathname for a standalone session URL while preserving any base
|
|
8
9
|
* path the app is deployed under (e.g. `/app/session/<id>` stays under
|
|
@@ -10,8 +10,9 @@ import type { DaemonCapabilities, DaemonSessionSummary, DaemonWorkspaceCapabilit
|
|
|
10
10
|
* it has no segments.
|
|
11
11
|
*/
|
|
12
12
|
export declare function workspaceBasename(cwd: string): string;
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function
|
|
13
|
+
export declare function sshWorkspaceLabel(ssh: NonNullable<DaemonWorkspaceCapability['ssh']>): string;
|
|
14
|
+
export declare function workspaceLabel(workspace: Pick<DaemonWorkspaceCapability, 'cwd' | 'displayName' | 'ssh'>): string;
|
|
15
|
+
export declare function workspaceLabelForCwd(cwd: string, workspaces: readonly Pick<DaemonWorkspaceCapability, 'cwd' | 'displayName' | 'ssh'>[] | undefined): string;
|
|
15
16
|
/**
|
|
16
17
|
* True when the daemon advertises more than one registered workspace — i.e. the
|
|
17
18
|
* multi-workspace session surfaces (per-workspace labels/tags) should show.
|