@qwen-code/web-shell 0.24.4 → 0.24.5-nightly.20260925.c3a4058a0c

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.
Files changed (85) hide show
  1. package/README.md +152 -1
  2. package/dist/code-highlighter.js +4 -0
  3. package/dist/codeHighlighter-pfciSYNN.js +89 -0
  4. package/dist/daemon-react-sdk.js +1 -1
  5. package/dist/index.js +49184 -44478
  6. package/dist/sw.js +1 -1
  7. package/dist/{toolNames-Cnaa5VCB.js → toolNames-C6S4xtsA.js} +7018 -6371
  8. package/dist/transcript.js +11334 -10857
  9. package/dist/types/App.d.ts +18 -3
  10. package/dist/types/App.typecheck.d.ts +6 -0
  11. package/dist/types/ManagedAgentWebShell.d.ts +17 -0
  12. package/dist/types/adapters/toolClassification.d.ts +1 -0
  13. package/dist/types/adapters/transcriptToMessages.d.ts +3 -2
  14. package/dist/types/code-highlighter.d.ts +6 -0
  15. package/dist/types/components/BranchPickerPopover.d.ts +3 -1
  16. package/dist/types/components/ChatEditor.d.ts +2 -0
  17. package/dist/types/components/ChatPane.d.ts +5 -3
  18. package/dist/types/components/ConversationSearch.d.ts +18 -0
  19. package/dist/types/components/GlobalTurnNavigation.d.ts +3 -1
  20. package/dist/types/components/MessageList.d.ts +10 -2
  21. package/dist/types/components/MessageTimestamp.d.ts +4 -1
  22. package/dist/types/components/SplitView.d.ts +3 -1
  23. package/dist/types/components/WorkspaceSessionProvider.d.ts +2 -0
  24. package/dist/types/components/artifacts/ArtifactPanel.d.ts +18 -1
  25. package/dist/types/components/artifacts/SideTaskPanel.d.ts +5 -1
  26. package/dist/types/components/artifacts/TrajectoryOverview.d.ts +71 -0
  27. package/dist/types/components/artifacts/TurnCallPromptSelect.d.ts +9 -0
  28. package/dist/types/components/artifacts/TurnCallsPanel.d.ts +38 -0
  29. package/dist/types/components/artifacts/TurnOutputs.d.ts +6 -1
  30. package/dist/types/components/artifacts/loadTurnCalls.d.ts +4 -0
  31. package/dist/types/components/channels/channel-editor-state.d.ts +9 -3
  32. package/dist/types/components/conversation-search-i18n.d.ts +6 -0
  33. package/dist/types/components/dialogs/DeleteSessionDialog.d.ts +9 -1
  34. package/dist/types/components/dialogs/GitDialog.d.ts +6 -2
  35. package/dist/types/components/dialogs/GitWorktreesDialog.d.ts +11 -0
  36. package/dist/types/components/managed/ManagedSessionProgress.d.ts +6 -0
  37. package/dist/types/components/managed/ManagedSessionsPage.d.ts +7 -0
  38. package/dist/types/components/managed/java-managed-agent-client.d.ts +140 -0
  39. package/dist/types/components/managed/java-managed-agent-event-projector.d.ts +5 -0
  40. package/dist/types/components/managed/java-managed-agent-provider.d.ts +8 -0
  41. package/dist/types/components/managed/managed-agent-provider.d.ts +77 -0
  42. package/dist/types/components/managed/managed-session-messages.d.ts +4 -0
  43. package/dist/types/components/managed/managed-session-storage.d.ts +7 -0
  44. package/dist/types/components/managed/use-managed-session.d.ts +12 -0
  45. package/dist/types/components/messages/AuthMessage.d.ts +2 -1
  46. package/dist/types/components/messages/LinkifiedText.d.ts +1 -1
  47. package/dist/types/components/messages/ModelManagementSection.d.ts +3 -2
  48. package/dist/types/components/messages/PlanExecutionView.d.ts +14 -66
  49. package/dist/types/components/messages/SettingsMessage.d.ts +2 -2
  50. package/dist/types/components/messages/ToolFilePreviewButton.d.ts +2 -1
  51. package/dist/types/components/messages/ToolGroup.d.ts +3 -0
  52. package/dist/types/components/messages/codeHighlighter.d.ts +7 -0
  53. package/dist/types/components/messages/taskExecutionIndex.d.ts +80 -0
  54. package/dist/types/components/messages/toolFormatting.d.ts +3 -0
  55. package/dist/types/components/panels/EnvironmentPanel.d.ts +2 -1
  56. package/dist/types/components/sidebar/WebShellSidebar.d.ts +12 -3
  57. package/dist/types/components/sidebar/WorkspaceSection.d.ts +8 -1
  58. package/dist/types/components/workflow/SessionWorkflowCockpit.d.ts +8 -1
  59. package/dist/types/components/workflow/SessionWorkflowInspector.d.ts +8 -1
  60. package/dist/types/components/workflow/session-workflow-model.d.ts +9 -1
  61. package/dist/types/customization.d.ts +9 -1
  62. package/dist/types/daemon/session/actions.d.ts +1 -1
  63. package/dist/types/daemon/session/transcript-page-table.d.ts +1 -1
  64. package/dist/types/daemon/session/turn-navigation-store.d.ts +32 -0
  65. package/dist/types/daemon/session/types.d.ts +3 -1
  66. package/dist/types/hooks/useMessageNavigation.d.ts +12 -0
  67. package/dist/types/hooks/useQueuedPrompts.d.ts +4 -1
  68. package/dist/types/hooks/useTranscriptViewport.d.ts +4 -1
  69. package/dist/types/index.d.ts +17 -1
  70. package/dist/types/live/live-presence.d.ts +14 -0
  71. package/dist/types/live/screen-feed-sampler.d.ts +12 -0
  72. package/dist/types/live/useLiveBrowserHost.d.ts +7 -0
  73. package/dist/types/live/useLiveVoice.d.ts +2 -0
  74. package/dist/types/modelManagement.d.ts +16 -0
  75. package/dist/types/navigation.d.ts +37 -0
  76. package/dist/types/trajectory/buildTimeline.d.ts +67 -0
  77. package/dist/types/trajectory/timelineRange.d.ts +27 -0
  78. package/dist/types/trajectory/transcriptPageLoader.d.ts +4 -2
  79. package/dist/types/trajectory/types.d.ts +3 -3
  80. package/dist/types/trajectory/useTrajectoryWindow.d.ts +55 -17
  81. package/dist/types/turnCallsContext.d.ts +12 -0
  82. package/dist/types/utils/navigationUrl.d.ts +21 -0
  83. package/dist/types/utils/popoverRoom.d.ts +22 -0
  84. package/dist/types/utils/sessionPath.d.ts +1 -0
  85. package/package.json +5 -1
@@ -15,7 +15,7 @@ export interface WebShellSidebarLockedWorkspace {
15
15
  expanded: boolean;
16
16
  }) => ReactNode;
17
17
  }
18
- export type WebShellSidebarPrimaryNavItem = 'newTask' | 'plugins' | 'channels' | 'scheduledTasks' | 'workflows' | 'goals';
18
+ export type WebShellSidebarPrimaryNavItem = 'newTask' | 'plugins' | 'channels' | 'scheduledTasks' | 'workflows' | 'goals' | 'managed';
19
19
  export interface WebShellSidebarPrimaryNavOptions {
20
20
  /** Built-in primary nav entries to show. Defaults to all. */
21
21
  items?: readonly WebShellSidebarPrimaryNavItem[];
@@ -59,6 +59,7 @@ interface WebShellSidebarProps {
59
59
  onOpenSettings: () => void;
60
60
  onOpenPlugins: () => void;
61
61
  onOpenChannels: () => void;
62
+ onOpenManagedSessions?: () => void;
62
63
  onOpenDaemonStatus: () => void;
63
64
  onOpenScheduledTasks: () => void;
64
65
  onOpenWorkflows: () => void;
@@ -79,7 +80,15 @@ interface WebShellSidebarProps {
79
80
  onLoadSession: (sessionId: string, workspaceCwd?: string) => Promise<void> | void;
80
81
  onSelectCurrentSession?: () => void;
81
82
  onSessionRenameConfirmed?: (workspaceCwd: string | undefined, sessionId: string, displayName: string) => void;
82
- onSessionsDeleted?: (sessionIds: string[]) => void;
83
+ /**
84
+ * `attachedSessionId` is the session this client was attached to when the
85
+ * delete was confirmed. The daemon publishes the terminal `session_closed`
86
+ * frame before the delete request resolves, so the attachment is no longer
87
+ * readable off the connection by the time this callback runs (#12619).
88
+ */
89
+ onSessionsDeleted?: (sessionIds: string[], meta?: {
90
+ attachedSessionId?: string;
91
+ }) => void;
83
92
  onError: (error: unknown, fallback: string) => void;
84
93
  theme: WebShellTheme;
85
94
  onThemeChange: (theme: WebShellTheme) => void;
@@ -136,4 +145,4 @@ interface WebShellSidebarProps {
136
145
  onLoadStandaloneSession?: (sessionId: string) => Promise<void> | void;
137
146
  onStandaloneNotice?: (message: string) => void;
138
147
  }
139
- export declare function WebShellSidebar({ collapsed, onCollapsedChange, onOpenSettings, onOpenPlugins, onOpenChannels, onOpenDaemonStatus, onOpenScheduledTasks, onOpenWorkflows, onOpenGoals, onOpenSessions, canOpenSessionsOverview, onOpenWorkspacesOverview, onOpenSplitView, canOpenSplitView, onNewSession, onNewStandaloneSession, onLoadSession, onSelectCurrentSession, onSessionRenameConfirmed, onSessionsDeleted, onError, theme, onThemeChange, mobileOpen, onMobileClose, selectedWorkspaceCwd, onSelectWorkspace, onOpenGitDiff, onOpenCommit, onOpenAddWorkspace, workspaces: providedWorkspaces, lockedWorkspaceCwd, lockedWorkspace: lockedWorkspaceOptions, branding, primaryNav: primaryNavOptions, showSessionSourceSwitch, showLive, hideProjectHeader, sessionActions: sessionActionsOptions, footer, workspaceOverview, onOpenWorkspaceManagement, onNewWorktreeSession, projectFeaturesEnabled, onLoadStandaloneSession, onStandaloneNotice, }: WebShellSidebarProps): import("react/jsx-runtime").JSX.Element;
148
+ export declare function WebShellSidebar({ collapsed, onCollapsedChange, onOpenSettings, onOpenPlugins, onOpenChannels, onOpenManagedSessions, onOpenDaemonStatus, onOpenScheduledTasks, onOpenWorkflows, onOpenGoals, onOpenSessions, canOpenSessionsOverview, onOpenWorkspacesOverview, onOpenSplitView, canOpenSplitView, onNewSession, onNewStandaloneSession, onLoadSession, onSelectCurrentSession, onSessionRenameConfirmed, onSessionsDeleted, onError, theme, onThemeChange, mobileOpen, onMobileClose, selectedWorkspaceCwd, onSelectWorkspace, onOpenGitDiff, onOpenCommit, onOpenAddWorkspace, workspaces: providedWorkspaces, lockedWorkspaceCwd, lockedWorkspace: lockedWorkspaceOptions, branding, primaryNav: primaryNavOptions, showSessionSourceSwitch, showLive, hideProjectHeader, sessionActions: sessionActionsOptions, footer, workspaceOverview, onOpenWorkspaceManagement, onNewWorktreeSession, projectFeaturesEnabled, onLoadStandaloneSession, onStandaloneNotice, }: WebShellSidebarProps): import("react/jsx-runtime").JSX.Element;
@@ -29,6 +29,7 @@ interface WorkspaceSectionProps {
29
29
  searchQuery?: string;
30
30
  expanded?: boolean;
31
31
  autoExpandKey?: string;
32
+ forceAutoExpand?: boolean;
32
33
  onExpandedChange?: (expanded: boolean) => void;
33
34
  renderSessions?: boolean;
34
35
  /**
@@ -41,6 +42,12 @@ interface WorkspaceSectionProps {
41
42
  renderSession: (session: DaemonSessionSummary, options?: {
42
43
  searchSnippet?: string | undefined;
43
44
  }) => ReactNode;
45
+ pendingSession?: {
46
+ key: string;
47
+ sessionId?: string;
48
+ sourceId?: string;
49
+ node: ReactNode;
50
+ };
44
51
  mapSession?: (session: DaemonSessionSummary) => DaemonSessionSummary;
45
52
  showSessionDetails?: boolean;
46
53
  /**
@@ -109,5 +116,5 @@ interface WorkspaceSectionProps {
109
116
  /** Commit entry for the same picker; the row still opens without it. */
110
117
  onOpenCommit?: (workspaceCwd: string) => void;
111
118
  }
112
- export declare function WorkspaceSection({ workspace, remote, renderHeader, client, reloadToken, untrustedLabel, readOnlyLabel, trustToOpenLabel, noSessionsLabel, loadErrorLabel, organizationEnabled, sessionCatalogRequestsEnabled, sessionGroupCatalog, sessionLiveStateEnabled, sourceType, channelGroupingEnabled, ungroupedLabel, searchQuery, expanded: controlledExpanded, autoExpandKey, onExpandedChange, renderSessions, renderSession, mapSession, showSessionDetails, headerActions, overviewEnabled, overviewMenuOpen, overviewItems, gitBranchWanted, sessionStats, onRenameGroup, onDeleteGroup, renameGroupLabel, deleteGroupLabel, groupActionsDisabled, excludePinned, limitSessions, isPinnedSectionMember, onOpenPathLocally, onOpenTerminalLocally, onOpenGitDiff, onOpenCommit, }: WorkspaceSectionProps): import("react/jsx-runtime").JSX.Element;
119
+ export declare function WorkspaceSection({ workspace, remote, renderHeader, client, reloadToken, untrustedLabel, readOnlyLabel, trustToOpenLabel, noSessionsLabel, loadErrorLabel, organizationEnabled, sessionCatalogRequestsEnabled, sessionGroupCatalog, sessionLiveStateEnabled, sourceType, channelGroupingEnabled, ungroupedLabel, searchQuery, expanded: controlledExpanded, autoExpandKey, forceAutoExpand, onExpandedChange, renderSessions, renderSession, pendingSession, mapSession, showSessionDetails, headerActions, overviewEnabled, overviewMenuOpen, overviewItems, gitBranchWanted, sessionStats, onRenameGroup, onDeleteGroup, renameGroupLabel, deleteGroupLabel, groupActionsDisabled, excludePinned, limitSessions, isPinnedSectionMember, onOpenPathLocally, onOpenTerminalLocally, onOpenGitDiff, onOpenCommit, }: WorkspaceSectionProps): import("react/jsx-runtime").JSX.Element;
113
120
  export {};
@@ -1,5 +1,6 @@
1
1
  import type { 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
  interface SessionWorkflowCockpitProps {
4
5
  sessionId: string;
5
6
  connected: boolean;
@@ -8,10 +9,16 @@ interface SessionWorkflowCockpitProps {
8
9
  todos: readonly TodoItem[];
9
10
  tools: readonly ACPToolCall[];
10
11
  tasks: readonly DaemonSessionTaskStatus[];
12
+ /**
13
+ * The projection shared by every workflow surface for this render. The app
14
+ * derives it once (it also feeds the inspector beside this cockpit); when
15
+ * absent — a standalone mount, a test — it is derived from the raw props.
16
+ */
17
+ projection?: SessionWorkflowProjection;
11
18
  selectedTodoId?: string;
12
19
  onSelectedTodoIdChange: (todoId: string | undefined) => void;
13
20
  onBackToChat: () => void;
14
21
  onOpenSubagent: (tool: ACPToolCall) => void;
15
22
  }
16
- export declare function SessionWorkflowCockpit({ sessionId, connected, sessionName, workspaceCwd, todos, tools, tasks, selectedTodoId, onSelectedTodoIdChange, onBackToChat, onOpenSubagent, }: SessionWorkflowCockpitProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function SessionWorkflowCockpit({ sessionId, connected, sessionName, workspaceCwd, todos, tools, tasks, projection: sharedProjection, selectedTodoId, onSelectedTodoIdChange, onBackToChat, onOpenSubagent, }: SessionWorkflowCockpitProps): import("react/jsx-runtime").JSX.Element;
17
24
  export {};
@@ -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/PlanExecutionView';
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,4 @@
1
+ import type { ArtifactFilter } from './components/artifacts/TurnOutputs';
1
2
  import { type CSSProperties, type ComponentType, type ReactNode } from 'react';
2
3
  import type { Components, Options } from 'react-markdown';
3
4
  import type { ChartRendererRegistry, MarkdownChartLabelOverrides } from '@datafe-open/markdown-chart';
@@ -10,7 +11,11 @@ import type { WebShellTheme } from './themeContext';
10
11
  export type MarkdownContentSource = 'assistant' | 'thinking';
11
12
  export interface MarkdownRenderContext {
12
13
  source: MarkdownContentSource;
13
- /** 当前消息的生成态;历史或静态内容为 false,不取会话全局忙态。 */
14
+ /**
15
+ * 当前消息的生成态;历史或静态内容为 false。所属 MessageList 空闲
16
+ * (isResponding=false)时,恢复 replay 中残留的 streaming 标记也会被
17
+ * 收口为 false,且已收口的行在会话重新响应时、内容不变的前提下保持收口。
18
+ */
14
19
  isStreaming: boolean;
15
20
  }
16
21
  export interface WebShellCodeBlockRenderInfo {
@@ -126,6 +131,8 @@ export type WelcomeHeaderRenderer = (props: WelcomeHeaderProps) => ReactNode;
126
131
  export type WelcomeFooterRenderer = (props: WelcomeHeaderProps) => ReactNode;
127
132
  export type WebShellChatHeaderItem = 'title' | 'environment' | 'rightPanel' | 'tokenUsage' | 'contextUsage';
128
133
  export interface WebShellChatHeaderOptions {
134
+ /** Show the mobile-access QR entry in chat headers. Defaults to false. */
135
+ showMobileAccess?: boolean;
129
136
  /** Built-in header actions to show. Token and context usage are opt-in. */
130
137
  items?: readonly WebShellChatHeaderItem[];
131
138
  }
@@ -494,6 +501,7 @@ export type FooterRenderer = ComponentType<WebShellFooterRenderInfo>;
494
501
  */
495
502
  export type LoadingPhrasesResolver = (language: string) => readonly string[] | undefined | null;
496
503
  export interface WebShellCustomization {
504
+ filterArtifact?: ArtifactFilter;
497
505
  artifact?: WebShellArtifactCustomization;
498
506
  /** Host-specific label for the Ask User Question free-text choice. */
499
507
  askUserFreeTextLabel?: string;
@@ -78,7 +78,7 @@ export interface CreateDaemonSessionActionsArgs {
78
78
  onPromptRemoved?: (owner: DaemonSessionClient, promptId: string, sessionId?: string) => void;
79
79
  }
80
80
  export declare function getWorkspaceModelsAfterSessionClear(current: DaemonConnectionState): DaemonConnectionState['models'];
81
- export declare function getConnectionAfterSessionClear(current: DaemonConnectionState, clearedSessionId: string | undefined, preserveWorkspaceMetadata?: boolean): DaemonConnectionState;
81
+ export declare function getConnectionAfterSessionClear(current: DaemonConnectionState, clearedSessionId: string | undefined, preserveWorkspaceMetadata?: boolean, dropSessionContext?: boolean): DaemonConnectionState;
82
82
  export declare function advanceSessionRecoveryGeneration(generations: WeakMap<DaemonSessionClient, number>, session: DaemonSessionClient): number;
83
83
  export declare function createDaemonSessionActions({ store, sessionRef, activePromptsRef, settledPromptsRef, pendingSessionLoadRef, pendingSessionLoadIdRef, sessionConfigGeneration, sessionRecoveryGeneration, heartbeatSupportedRef, manualSessionClearRef, skipNextCleanupDetachSessionRef, passiveAssistantDoneTimerRef, daemonActivePromptRef, settleRestoredActivePrompt, flushTranscript, getCreateSessionRequest, createDetachedSession, createDetachedStandaloneSession, getDefaultSessionContext, getConnection, getEventDetailMode, hasSessionActivePrompt, resetCurrentSessionActivePrompt, restartEventStream, addNotice, setConnection, setPromptStatus, setRestoreSessionId, setRestoreSessionContext, setRestoreMode, setRestoreSessionNonce, setAttachSessionNonce, setNewSessionNonce, clearLiveJournalRepair, onPromptAdmitted, onContinuationAdmitted, onPromptRemoved, }: CreateDaemonSessionActionsArgs): DaemonSessionActions;
84
84
  /**
@@ -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;
@@ -419,7 +419,9 @@ export interface DaemonSessionActions {
419
419
  };
420
420
  }): Promise<DaemonSession>;
421
421
  attachSession(): Promise<void>;
422
- clearSession(): Promise<void>;
422
+ clearSession(options?: {
423
+ dropSessionContext?: boolean;
424
+ }): Promise<void>;
423
425
  newSession(): Promise<void>;
424
426
  releaseSession(sessionId: string): Promise<void>;
425
427
  closeSession(): Promise<void>;
@@ -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,10 +13,12 @@ export declare function useTranscriptViewport(liveMessages: Message[], t: Transl
12
13
  blockId: string;
13
14
  token: number;
14
15
  } | undefined;
15
- selectOrdinal: (ordinal: number) => Promise<void>;
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[];
21
+ blocks: import("@qwen-code/sdk/daemon").DaemonTranscriptBlock[] | undefined;
19
22
  toolSources: Map<string, string>;
20
23
  historical: boolean;
21
24
  viewKey: string;
@@ -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';
@@ -5,7 +7,17 @@ export { WebShellTranscript } from './components/WebShellTranscript';
5
7
  export type { WebShellTranscriptProps } from './components/WebShellTranscript';
6
8
  export * from './daemon-react-sdk';
7
9
  export type { WebShellBrowserNotificationsOptions } from './browser-turn-notifications';
10
+ export { JavaManagedAgentClient, JavaManagedAgentHttpError, type JavaAgentCommandAdmission, type JavaAgentEnvironment, type JavaAgentEvent, type JavaAgentSession, type JavaAgentTranscript, type JavaAgentTurn, type JavaManagedAgentClientOptions, } from './components/managed/java-managed-agent-client';
11
+ export { createJavaManagedAgentProvider, type JavaManagedAgentProviderOptions, } from './components/managed/java-managed-agent-provider';
12
+ export { ManagedAgentWebShell, type ManagedAgentWebShellProps, } from './ManagedAgentWebShell';
13
+ export { type ManagedAgentCommandOptions, type ManagedAgentProvider, type ManagedAgentRequestOptions, type ManagedAgentRuntimeState, type ManagedAgentSessionEvent, type ManagedAgentSessionEventType, type ManagedAgentSessionPhase, type ManagedAgentSessionSummary, type ManagedAgentSessionTranscript, type ManagedAgentTurnAdmission, } from './components/managed/managed-agent-provider';
8
14
  export interface WebShellWithProvidersProps extends WebShellProps {
15
+ /**
16
+ * Opt in to URL routing. Explicit initial session target props override the
17
+ * URL; later target prop changes replace it. Stop host history writes when
18
+ * enabled. Omit to keep host-owned navigation. basePath defaults to root.
19
+ */
20
+ urlNavigation?: WebShellUrlNavigationOptions;
9
21
  /** Connect browser notifications with optional branding and an initial preference (off by default). */
10
22
  browserNotifications?: WebShellBrowserNotificationsOptions;
11
23
  /** Daemon API base URL. Defaults to the browser origin when omitted. */
@@ -62,8 +74,12 @@ export type { ComposerToolbarAction } from './components/ChatEditor';
62
74
  export type { CodeBlockRenderer, MarkdownContentSource, MarkdownTableMode, MarkdownRenderContext, ToolHeaderExtraRenderer, ToolHeaderExtraRenderInfo, ToolHeaderKind, ComposerTagClickHandler, ComposerTagRenderer, AssistantTurnFooterRenderer, UserMessageContentRenderer, UserMessageContentRenderInfo, UserMessageContentParser, ComposerHeaderRenderer, ComposerFooterRenderer, ComposerToolbarStartRenderer, ComposerToolbarRightRenderer, WebShellAtItemRenderInfo, WebShellAtItemRenderer, WebShellComposerApi, WebShellBuiltinComposerTagKind, WebShellBuiltinAtProviderId, WebShellBuiltinAtProvidersConfig, WebShellComposerInput, WebShellComposerTag, WebShellComposerTagIconMap, WebShellComposerTagKind, WebShellComposerTagOptions, WebShellComposerTagPlacement, WebShellComposerToolbarRenderInfo, WebShellComposerToolbarStartRenderInfo, WebShellComposerToolbarRightRenderInfo, WebShellComposerTextOptions, WelcomeFooterRenderer, WelcomeHeaderRenderer, ChatHeaderRenderer, ChatHeaderRenderInfo, WebShellChatHeaderItem, WebShellChatHeaderOptions, WebShellRightPanelItem, WebShellRightPanelOptions, WebShellEnvironmentPanelItem, WebShellEnvironmentPanelOptions, WebShellFooterRenderInfo, FooterRenderer, LoadingPhrasesResolver, WebShellAtProviderTab, WebShellAtItem, WebShellAtProvider, WebShellBottomStatusItem, WebShellCodeBlockRenderInfo, WebShellMarkdownChartCustomization, WebShellMarkdownCustomization, WebShellFootnote, WebShellSource, WebShellSourceReference, WebShellSourceIconResolver, WebShellFootnoteIconResolver, WebShellFootnotePreviewInfo, WebShellFootnotePreviewHandle, WebShellFootnotePreviewMount, WebShellAssistantMessageInfo, WebShellAssistantTurnOutcome, WebShellAssistantTurnSettledEvent, WebShellAssistantTurnFooterRenderInfo, ArtifactImageRenderer, WebShellArtifactCustomization, WebShellIconSource, WebShellTaskInfo, WebShellUserMessagePart, WebShellAgentTask, WebShellShellTask, WebShellMonitorTask, WebShellWorkflowTask, WebShellPreparedSubmit, WebShellSubmitSnapshot, WebShellSessionArtifactsChange, WebShellSessionArtifactsChangeReason, WebShellModelInfo, WebShellSkillInfo, WebShellAssistantFeedbackInfo, WebShellAssistantFeedbackOptions, WebShellAssistantFeedbackRating, WebShellAssistantFeedbackUserMessage, AssistantFeedbackHandler, } from './customization';
63
75
  export type { WelcomeHeaderProps } from './components/WelcomeHeader';
64
76
  export type { PaneHeaderActionsInfo, PaneHeaderActionsRenderer, } from './components/ChatPane';
65
- export type { TurnOutputKind, TurnOutputOpenRequest, } from './components/artifacts/TurnOutputs';
77
+ export type { ArtifactFilter, TurnOutputKind, TurnOutputOpenRequest, } from './components/artifacts/TurnOutputs';
66
78
  export { ECHARTS_FULLDATA_LANGUAGE, EchartsFullDataBlock, createMarkdownChartRegistry, createEchartsFullDataRenderer, } from './components/messages/MarkdownChartRenderer';
67
79
  export type { DatasetCell, EchartsFullDataBlockProps, EchartsFullDataOption, EchartsFullDataRefMeta, EchartsFullDataRefResolver, EchartsFullDataResolvedDataset, EchartsFullDataRendererOptions, EchartsInstance, EchartsRuntime, EchartsRuntimeLoader, } from './components/messages/MarkdownChartRenderer';
68
80
  export { WEB_SHELL_SETTING_ITEM_IDS } from './settings';
69
81
  export type { WebShellSettingItemId, WebShellSettingsOptions, } from './settings';
82
+ export type { WebShellModelManagementOptions } from './modelManagement';
83
+ export type { WebShellMessageNavigationRequest, WebShellMessageNavigationResult, } from './hooks/useMessageNavigation';
84
+ export { highlightCode } from './components/messages/codeHighlighter';
85
+ export type { CodeHighlightRequest } from './components/messages/codeHighlighter';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Qwen
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { DaemonClient, DaemonLiveStatus } from '@qwen-code/sdk/daemon';
7
+ export interface LivePresence {
8
+ callId?: string;
9
+ state: 'connecting' | DaemonLiveStatus['state'];
10
+ coordinator?: DaemonLiveStatus['coordinator'];
11
+ }
12
+ export declare function setLivePresence(client: DaemonClient, presence: LivePresence | undefined): void;
13
+ export declare function getLivePresence(client: DaemonClient): LivePresence | undefined;
14
+ export declare function useLivePresence(client: DaemonClient): LivePresence | undefined;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Qwen Team
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { LiveScreenFrame } from './screen-share';
7
+ export declare function sampleScreen(options: {
8
+ grab: () => Promise<LiveScreenFrame>;
9
+ canSend: () => boolean;
10
+ send: (image: string) => void;
11
+ onError: (error: unknown) => void;
12
+ }): () => void;
@@ -76,6 +76,13 @@ export interface UseLiveBrowserHostResult {
76
76
  /** Must run inside a user gesture: it asks which screen to share. */
77
77
  startSharingScreen: () => Promise<void>;
78
78
  stopSharingScreen: () => void;
79
+ screenFeed: LiveScreenFeedState;
80
+ }
81
+ export interface LiveScreenFeedState {
82
+ supported: boolean;
83
+ feedId?: string;
84
+ phase: 'idle' | 'starting' | 'streaming' | 'stopped' | 'error';
85
+ message?: string;
79
86
  }
80
87
  export interface LiveScreenShareState {
81
88
  /** False where `getDisplayMedia` is unavailable, so the share is never offered. */
@@ -16,6 +16,8 @@ export interface UseLiveVoiceResult {
16
16
  loading: boolean;
17
17
  mutating: boolean;
18
18
  refresh: () => Promise<void>;
19
+ begin: () => void;
20
+ cancelPending: () => void;
19
21
  start: (mode?: 'resume' | 'new') => Promise<void>;
20
22
  stop: () => Promise<void>;
21
23
  setMute: (update: DaemonLiveMuteUpdate) => Promise<void>;
@@ -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,67 @@
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
+ /**
22
+ * How the axis treats time in which nothing ran. `active` cuts it out, so the
23
+ * strip answers where the running time went; `clock` keeps it, so the strip
24
+ * answers when things ran and how long the run waited between them.
25
+ */
26
+ export type TimelineMode = 'active' | 'clock';
27
+ export interface TimelineTurnMark {
28
+ turnIndex: number;
29
+ at: number;
30
+ }
31
+ export interface TimelineModel {
32
+ /** Ascending by start, then lane, then row key. */
33
+ spans: TimelineSpan[];
34
+ turnMarks: TimelineTurnMark[];
35
+ mode: TimelineMode;
36
+ /**
37
+ * Length of the domain. In `active` mode idle gaps are cut out, so this is
38
+ * the time during which at least one request or tool was running. In
39
+ * `clock` mode it runs from the first drawn span's start to the last one's
40
+ * end — which is the loaded window's span, not the session's age.
41
+ */
42
+ total: number;
43
+ /**
44
+ * Time during which at least one drawn span was running. Equal to `total` in
45
+ * `active` mode.
46
+ */
47
+ activeMs: number;
48
+ /** Epoch ms of domain 0: the earliest recorded start among drawn spans. */
49
+ originMs: number;
50
+ /**
51
+ * Rows with a recorded duration but no recorded start — tool calls from a
52
+ * session written before tools recorded one. Not drawn, never estimated.
53
+ */
54
+ droppedRows: number;
55
+ }
56
+ /**
57
+ * Project the rows that recorded both a start and a duration onto one time
58
+ * axis — with the idle stretches between them cut out (`active`, the default)
59
+ * or left in (`clock`).
60
+ *
61
+ * Only measured values are drawn. A row without a start is left off rather
62
+ * than placed next to its neighbours: a bar in the wrong place reads as a fact.
63
+ * Returns `undefined` when nothing in the window can be drawn.
64
+ */
65
+ export declare function buildTimeline(trajectory: Trajectory, options?: {
66
+ mode?: TimelineMode;
67
+ }): TimelineModel | undefined;
@@ -0,0 +1,27 @@
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. In `clock` mode the gaps stay on the axis, and
20
+ * a range that falls inside one finds nothing — an ordinary answer the panel
21
+ * says in words rather than an edge case.
22
+ *
23
+ * Closed on both sides so a zero-length span sitting on an edge — a tool that
24
+ * reported 0 ms, or a range made by a drag that stopped exactly on a bar's
25
+ * end — still counts as inside rather than vanishing from both sides.
26
+ */
27
+ 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 ask for the newest page, which is the one the reader wants — the newest
24
- * turn is the one they just watched run. It deliberately goes through the raw
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
- * Requests only. Tool calls are logged in one loop after their whole batch
46
- * settles, so the recorded timestamp is the batch's end for every tool in it
47
- * and no honest per-tool start can be derived.
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. */