@ycodium-ai/plugin-api 0.2.2169 → 0.2.2488

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.
@@ -8,13 +8,17 @@
8
8
  * ## 视图与生命周期
9
9
  *
10
10
  * 会话插件在 manifest 里只声明 `ui.entry`(无 pages/panels),bundle 的
11
- * `register(bridge)` 返回 `views: { conversation }`。宿主经 `PluginViewHost`
12
- * 挂载:每台机器、每个插件版本各一份;插件停用/卸载后注册条目整个消失,宿主
13
- * 显示「视图不可用」。多个保活会话 pane 各挂各的实例,全局资源(快捷键、壳
11
+ * `register(bridge)` 返回 `views: { conversation }`,并在 `viewCapabilities` 里声明
12
+ * {@link CONVERSATION_VIEW_HOST_DATA_CAPABILITY}。宿主经 `PluginViewHost` 挂载:每台
13
+ * 机器、每个插件版本各一份;插件停用/卸载、或与宿主的数据接口版本不一致时,宿主
14
+ * 说明原因。多个保活会话 pane 各挂各的实例,全局资源(快捷键、壳
14
15
  * 内容发布、已读标记)由宿主按 `scope.active` 仲裁,插件不得自行注册全局监听。
15
16
  *
16
17
  * ## 数据边界
17
18
  *
19
+ * - 插件按 `(机器, 会话)` 经 {@link ConversationHostData} 自己订阅要用的数据、自己发
20
+ * 命令;只有那块数据变了,用到它的组件才重新渲染。数据来自宿主已有的连接与缓存,
21
+ * 插件不另建连接或缓存。尚未迁进数据接口的部分仍由宿主经 props 给出。
18
22
  * - `ConversationTimelineEntry` / `ConversationThreadActivity` 对插件**不透明**:
19
23
  * 插件只把它们原样传回宿主派生函数,不解读。派生(行模型、用量、页脚)留在
20
24
  * 宿主,因为它们依赖宿主共享状态;插件拿到的行模型是可渲染的窄形状。
@@ -26,8 +30,8 @@
26
30
  *
27
31
  * @module @ycodium-ai/plugin-api/conversation
28
32
  */
29
- import type { ConversationComposerSourceMetrics } from "./source-metrics.ts";
30
- export type { ConversationComposerSourceMetrics, QuotaPoolScopeKind, SourceMetric, SourceMetricKind, SourceMetricUnit, SourceMetricsCapability, SourceMetricsCredentialIdentity, SourceMetricsRefreshOutcome, SourceMetricsRefreshResult, SourceMetricsSelectionRef, SourceMetricsSnapshot, } from "./source-metrics.ts";
33
+ import type { ConversationComposerAccountUsage } from "./account-usage.ts";
34
+ export type { AccountUsageCapability, AccountUsageCredentialIdentity, AccountUsageMetric, AccountUsageMetricKind, AccountUsageRefreshOutcome, AccountUsageRefreshResult, AccountUsageScopeKind, AccountUsageSnapshot, AccountUsageUnit, ConversationComposerAccountUsage, } from "./account-usage.ts";
31
35
  /** 时间线派生输入。插件只透传,不解读。 */
32
36
  export type ConversationTimelineEntry = unknown;
33
37
  /** 会话活动流。插件只透传给宿主派生函数,不解读。 */
@@ -72,14 +76,10 @@ export interface ConversationModelBadge {
72
76
  readonly instanceId: string;
73
77
  /** 实例已被删掉又猜不出 driver 时为 null。 */
74
78
  readonly driverKind: string | null;
75
- readonly providerId: string | null;
76
79
  readonly providerDisplayName: string;
77
80
  readonly accentColor: string | undefined;
78
81
  readonly modelLabel: string;
79
82
  readonly effortLabel: string | null;
80
- /** 来源显示名。同模型不同来源必须能分开。 */
81
- readonly sourceLabel?: string | null | undefined;
82
- readonly sourceId?: string | null | undefined;
83
83
  /** 徽章身份键:同身份跨渲染保持行对象稳定。 */
84
84
  readonly identity: string;
85
85
  }
@@ -191,46 +191,7 @@ export interface ConversationModelSelection {
191
191
  instanceId: ConversationProviderInstanceId;
192
192
  model: string;
193
193
  options?: ReadonlyArray<ConversationProviderOptionSelection> | undefined;
194
- /** 仅展示/身份用,不代替 ExecutionSelection,也不拼进 instanceId。 */
195
- sourceId?: string | undefined;
196
- sourceLabel?: string | undefined;
197
194
  }
198
- /** 用户意图:runtime + source + profile,不是 instanceId 字符串。 */
199
- export interface ConversationExecutionSelection {
200
- environmentId: ConversationEnvironmentId;
201
- runtimeId: string;
202
- sourceId: string;
203
- profileId: string;
204
- endpointId?: string | undefined;
205
- optionOverrides: ReadonlyArray<ConversationProviderOptionSelection>;
206
- expectedRevisions: {
207
- runtime: number;
208
- source: number;
209
- profile: number;
210
- endpoint?: number | undefined;
211
- };
212
- }
213
- export type ConversationCompatibilityMatch = "native" | "via-adapter" | "unsupported" | "unknown";
214
- /** picker 一行:同模型两个来源是两行;ready 只在 native/via-adapter。 */
215
- export interface ConversationComposerChoice {
216
- readonly runtimeId: string;
217
- readonly runtimeLabel: string;
218
- readonly sourceId: string;
219
- readonly sourceLabel: string;
220
- readonly sourceKind: "api" | "native-subscription" | "runtime-managed";
221
- readonly profileId: string;
222
- readonly modelId: string;
223
- readonly modelLabel: string;
224
- readonly endpointId?: string | undefined;
225
- readonly endpointLabel: string | null;
226
- readonly match: ConversationCompatibilityMatch | null;
227
- readonly reasonCode: string | null;
228
- readonly dispatch?: "execution-selection" | "legacy-instance" | "none" | null;
229
- readonly ready: boolean;
230
- readonly legacyInstanceId: ConversationProviderInstanceId | null;
231
- readonly selection: ConversationExecutionSelection;
232
- }
233
- export type ConversationExecutionAppliesTo = "composer" | "running" | "next-message";
234
195
  export interface ConversationProviderOptionSelection {
235
196
  id: string;
236
197
  value: string | boolean;
@@ -266,7 +227,6 @@ export interface ConversationServerProviderModel {
266
227
  subProvider?: string | undefined;
267
228
  isDefault?: boolean | undefined;
268
229
  isCustom?: boolean | undefined;
269
- isLegacy?: boolean | undefined;
270
230
  aliases?: ReadonlyArray<string> | undefined;
271
231
  capabilities?: ConversationModelCapabilities | undefined;
272
232
  }
@@ -299,8 +259,6 @@ export interface ConversationServerProvider {
299
259
  enabled: boolean;
300
260
  installed: boolean;
301
261
  accentColor?: string | undefined;
302
- /** provider 目录里的那一行 id(ACP 厂商各不相同)。 */
303
- providerId?: string | undefined;
304
262
  continuation?: {
305
263
  groupKey?: string | undefined;
306
264
  } | undefined;
@@ -342,7 +300,6 @@ export interface ConversationServerProvider {
342
300
  export interface ConversationProviderInstanceEntry {
343
301
  instanceId: ConversationProviderInstanceId;
344
302
  driverKind: ConversationProviderDriverKind;
345
- providerId: string;
346
303
  displayName: string;
347
304
  /** 这一格坐的是哪双手(品牌名)。昵称在 `displayName`,两者可能不同。 */
348
305
  agentName?: string | undefined;
@@ -360,7 +317,6 @@ export interface ConversationAppModelOption {
360
317
  name: string;
361
318
  shortName?: string | undefined;
362
319
  subProvider?: string | undefined;
363
- isLegacy?: boolean | undefined;
364
320
  description?: string | undefined;
365
321
  isDefault?: boolean | undefined;
366
322
  aliases?: ReadonlyArray<string> | undefined;
@@ -369,10 +325,6 @@ export interface ConversationModelFavorite {
369
325
  environmentId: ConversationEnvironmentId;
370
326
  instanceId: ConversationProviderInstanceId;
371
327
  model: string;
372
- runtimeId?: string | undefined;
373
- /** 有来源时按 source+profile 收藏;缺席时仍按 instance+model。 */
374
- sourceId?: string | undefined;
375
- profileId?: string | undefined;
376
328
  }
377
329
  /** 宿主 `UnifiedSettings` 的结构子集(composer 子树读到的字段)。 */
378
330
  export interface ConversationUnifiedSettings {
@@ -647,6 +599,11 @@ export interface ConversationPromptStashEntry {
647
599
  unreadableImageNames?: ReadonlyArray<string> | undefined;
648
600
  pendingImageCount?: number | undefined;
649
601
  }
602
+ /**
603
+ * 上传失败的原因码,插件按码取自己的译文。`rejected` 时 `httpStatus` 带服务器回的状态码;
604
+ * 宿主以后可能加新码,不认识的码与没给码的旧宿主一样退回 `reason`(宿主的英文诊断串)。
605
+ */
606
+ export type ConversationAttachmentUploadFailureCode = "start-failed" | "not-connected" | "rejected" | "timed-out" | "cancelled" | "failed";
650
607
  export type ConversationAttachmentUploadState = {
651
608
  status: "ready";
652
609
  environmentId: string;
@@ -660,14 +617,32 @@ export type ConversationAttachmentUploadState = {
660
617
  status: "failed";
661
618
  environmentId: string;
662
619
  reason: string;
620
+ code?: ConversationAttachmentUploadFailureCode;
621
+ httpStatus?: number;
663
622
  attachmentId?: string;
664
623
  };
624
+ /** 宿主 `ComposerThreadDraftState` 的结构子集:一条会话输入框里的草稿。 */
625
+ export interface ConversationComposerThreadDraft {
626
+ prompt: string;
627
+ images: ConversationComposerImageAttachment[];
628
+ nonPersistedImageIds: string[];
629
+ persistedAttachments: ConversationPersistedComposerImageAttachment[];
630
+ terminalContexts: ConversationTerminalContextDraft[];
631
+ elementContexts: ConversationElementContextDraft[];
632
+ previewAnnotations: ConversationPreviewAnnotationPayload[];
633
+ reviewComments: ConversationReviewCommentContext[];
634
+ modelSelectionByProvider: Readonly<Record<string, ConversationModelSelection | undefined>>;
635
+ nativeModeByProvider?: Readonly<Record<string, string | null | undefined>> | undefined;
636
+ activeProvider: ConversationProviderInstanceId | null;
637
+ runtimeMode: ConversationRuntimeMode | null;
638
+ interactionMode: ConversationProviderInteractionMode | null;
639
+ selectedPluginActionKey: string | null;
640
+ }
665
641
  /**
666
- * 宿主 `ComposerThreadDraftState` 的结构子集 + store 的 action 面(zustand 的
667
- * state 与 action 同住一个对象;hook 经 api.draft.useStore 穿界,选择器读到
668
- * 的就是这份形状)。
642
+ * 草稿字段 + store 的 action 面(zustand 的 state 与 action 同住一个对象;hook 经
643
+ * api.draft.useStore 穿界,插件只用它取 action)。
669
644
  */
670
- export interface ConversationComposerDraftState {
645
+ export interface ConversationComposerDraftState extends ConversationComposerThreadDraft {
671
646
  setPrompt(target: ConversationComposerThreadTarget, prompt: string): void;
672
647
  setTerminalContexts(target: ConversationComposerThreadTarget, contexts: ConversationTerminalContextDraft[]): void;
673
648
  setProviderModelOptions(target: ConversationComposerThreadTarget, provider: ConversationProviderDriverKind, nextProviderOptions: ReadonlyArray<ConversationProviderOptionSelection> | null | undefined, options?: {
@@ -685,22 +660,6 @@ export interface ConversationComposerDraftState {
685
660
  removePreviewAnnotation(target: ConversationComposerThreadTarget, annotationId: string): void;
686
661
  removeReviewComment(target: ConversationComposerThreadTarget, commentId: string): void;
687
662
  clearComposerPromptAndImages(target: ConversationComposerThreadTarget): void;
688
- prompt: string;
689
- images: ConversationComposerImageAttachment[];
690
- nonPersistedImageIds: string[];
691
- persistedAttachments: ConversationPersistedComposerImageAttachment[];
692
- terminalContexts: ConversationTerminalContextDraft[];
693
- elementContexts: ConversationElementContextDraft[];
694
- previewAnnotations: ConversationPreviewAnnotationPayload[];
695
- reviewComments: ConversationReviewCommentContext[];
696
- modelSelectionByProvider: Readonly<Record<string, ConversationModelSelection | undefined>>;
697
- /** 未发送的执行选择。缺席=仍走遗留 instanceId+model。 */
698
- executionSelection?: ConversationExecutionSelection | null | undefined;
699
- nativeModeByProvider?: Readonly<Record<string, string | null | undefined>> | undefined;
700
- activeProvider: ConversationProviderInstanceId | null;
701
- runtimeMode: ConversationRuntimeMode | null;
702
- interactionMode: ConversationProviderInteractionMode | null;
703
- selectedPluginActionKey: string | null;
704
663
  }
705
664
  /** 宿主 `useComposerDraftStore` 同形:zustand store hook 原样穿界。 */
706
665
  export interface ConversationComposerDraftStoreHook {
@@ -723,7 +682,6 @@ export interface ConversationEffectiveComposerModelState {
723
682
  selectedModel: string;
724
683
  selectedModelStatus: ConversationComposerModelStatus;
725
684
  modelOptions: Readonly<Record<string, ReadonlyArray<ConversationProviderOptionSelection> | undefined>> | null;
726
- executionSelection?: ConversationExecutionSelection | null | undefined;
727
685
  }
728
686
  export interface ConversationPromptStashStoreState {
729
687
  entries: ReadonlyArray<ConversationPromptStashEntry>;
@@ -843,7 +801,11 @@ export interface ConversationComposerApi {
843
801
  /** 宿主 `composerDraftStore`。store hook 原样穿界,hook 规则照常适用。 */
844
802
  readonly draft: {
845
803
  useStore: ConversationComposerDraftStoreHook;
846
- useThreadDraft(target: ConversationComposerThreadTarget): ConversationComposerDraftState;
804
+ /**
805
+ * 按选择器订一条会话草稿的部分字段。结果按浅比较,没变就不重渲染:正文每改一个字
806
+ * 草稿就换新对象,只读附件或手的组件不该跟着醒。草稿不存在时选择器拿到空草稿。
807
+ */
808
+ useThreadDraftSelector<T>(target: ConversationComposerThreadTarget, selector: (draft: ConversationComposerThreadDraft) => T): T;
847
809
  useEffectiveModelState(input: {
848
810
  threadRef?: ConversationComposerThreadTarget;
849
811
  draftId?: ConversationDraftId;
@@ -1090,8 +1052,6 @@ export interface ConversationComposerHandle {
1090
1052
  selectedPromptEffort: string | null;
1091
1053
  selectedModelOptionsForDispatch: unknown;
1092
1054
  selectedModelSelection: ConversationModelSelection;
1093
- executionSelection?: ConversationExecutionSelection | null;
1094
- executionReady?: boolean;
1095
1055
  selectedNativeModeId?: string;
1096
1056
  providerAvailable: boolean;
1097
1057
  selectedProvider: ConversationProviderDriverKind;
@@ -1334,8 +1294,6 @@ export interface ConversationQueueEntry {
1334
1294
  readonly attachmentNames: ReadonlyArray<string>;
1335
1295
  /** 在队列里等待发送(拖拽排序只作用于这些行)。 */
1336
1296
  readonly waiting: boolean;
1337
- readonly executionSelection?: ConversationExecutionSelection | undefined;
1338
- readonly sourceLabel?: string | undefined;
1339
1297
  readonly management?: {
1340
1298
  readonly revision: string | number;
1341
1299
  readonly expectedTurnId: string | null;
@@ -1381,6 +1339,325 @@ export interface ConversationQueueActions {
1381
1339
  readonly onInvalid: () => void;
1382
1340
  }): () => void;
1383
1341
  }
1342
+ /**
1343
+ * 宿主数据接口的契约版本。宿主与插件各自编译进自己构建时的这个数字;两边不一致时
1344
+ * 会话区显示版本不一致的说明,而不是拿对方读不懂的数据硬画。改动或删除已有成员、
1345
+ * 删除 viewProps 字段、或把更多数据从 props 挪进这里时加一。
1346
+ */
1347
+ export declare const CONVERSATION_HOST_DATA_VERSION = 14;
1348
+ /**
1349
+ * 会话视图在 `viewCapabilities` 里声明自己读的是哪一版宿主数据接口。宿主只把会话区
1350
+ * 交给声明了同一版的视图。
1351
+ */
1352
+ export declare const CONVERSATION_VIEW_HOST_DATA_CAPABILITY: "ycodium.conversation.host-data.v14";
1353
+ /** 宿主数据接口读写的那条会话。`threadId` 在草稿路由上是预分配给它的会话 id。 */
1354
+ export interface ConversationThreadTarget {
1355
+ readonly environmentId: ConversationEnvironmentId;
1356
+ readonly threadId: ConversationThreadId;
1357
+ }
1358
+ /** 一条会话的排队读模型。服务器推新数据或连接状态变化时重新渲染调用它的组件。 */
1359
+ export interface ConversationQueueSnapshot {
1360
+ readonly entries: readonly ConversationQueueEntry[];
1361
+ readonly state: ConversationQueueState;
1362
+ /** 这台机器没有连上:排队的编辑、调序、立即发送都不可用。 */
1363
+ readonly unavailable: boolean;
1364
+ /** 本机刚放进排队区、服务器还没登记成排队意图的提交,排队区先画占位。 */
1365
+ readonly pendingSubmissions: readonly ConversationPendingQueueSubmission[];
1366
+ }
1367
+ /** 排队管理命令。失败时 reject,错误信息可直接给用户看。 */
1368
+ export interface ConversationQueueCommands {
1369
+ edit(target: ConversationThreadTarget, entry: ConversationQueueEntry, message: ConversationQueuedMessageEdit): Promise<void>;
1370
+ reorder(target: ConversationThreadTarget, entry: ConversationQueueEntry, beforeIntentId: string | null): Promise<void>;
1371
+ run(target: ConversationThreadTarget, entry: ConversationQueueEntry, mode: "steer" | "interrupt"): Promise<void>;
1372
+ cancel(target: ConversationThreadTarget, entry: ConversationQueueEntry): Promise<void>;
1373
+ resume(target: ConversationThreadTarget): Promise<void>;
1374
+ /** 同 {@link ConversationQueueActions.watchEditorAuthority}。 */
1375
+ watchEditorAuthority(input: {
1376
+ readonly threadKey: string;
1377
+ readonly intentId: string;
1378
+ readonly revision: string | number | null;
1379
+ readonly onInvalid: () => void;
1380
+ }): () => void;
1381
+ }
1382
+ /**
1383
+ * 一条会话的轨迹读模型与翻页。面板不可见时保留上一份行,不因后台流式活动反复重算;
1384
+ * 翻页与可见轮次上报是轨迹面板自己的一份,与聊天面板互不占用。
1385
+ */
1386
+ export interface ConversationTrajectorySnapshot {
1387
+ readonly rows: readonly ConversationTrajectoryRow[];
1388
+ /** 行 → 聊天锚消息 id;无可锚的行返回 null。 */
1389
+ resolveChatAnchorMessageId(row: ConversationTrajectoryRow): string | null;
1390
+ readonly loadEarlier: {
1391
+ readonly loading: boolean;
1392
+ onLoadEarlier(): void;
1393
+ } | null;
1394
+ readonly loadLater: {
1395
+ readonly loading: boolean;
1396
+ onLoadLater(): void;
1397
+ } | null;
1398
+ /** 原生选区或编辑中的行仍占有历史正文时,不能加载会驱逐它们的新页。 */
1399
+ readonly pagingBlockedByInteraction: boolean;
1400
+ /** 只有最新页已可用时调用 onReady;没有更新的页时为 null。 */
1401
+ readonly onReturnToLatest: ((onReady?: () => void) => void) | null;
1402
+ onVisibleTurnRangeChange(range: ConversationVisibleTurnRange | null): void;
1403
+ }
1404
+ /**
1405
+ * 聊天面板的数据窗口:翻页、回到最新页与可见轮次上报。与轨迹面板各持一份,互不占用;
1406
+ * 面板不可见时不上报可见轮次。
1407
+ */
1408
+ export interface ConversationChatWindow {
1409
+ readonly loadEarlier: {
1410
+ readonly loading: boolean;
1411
+ onLoadEarlier(): void;
1412
+ } | null;
1413
+ readonly loadLater: {
1414
+ readonly loading: boolean;
1415
+ onLoadLater(): void;
1416
+ } | null;
1417
+ /** 原生选区或编辑中的行仍占有历史正文时,不能加载会驱逐它们的新页。 */
1418
+ readonly pagingBlockedByInteraction: boolean;
1419
+ /**
1420
+ * 停在历史窗口或窗口正在重载时非空:把数据窗口换回最新页,到位后调用 onReady。
1421
+ * 为 null 时最新页已经在。
1422
+ */
1423
+ readonly onReturnToLatest: ((onReady: () => void) => void) | null;
1424
+ onVisibleTurnRangeChange(range: ConversationVisibleTurnRange | null): void;
1425
+ }
1426
+ /** 导出整段会话(轮次导航菜单里的复制与下载)。 */
1427
+ export interface ConversationThreadExport {
1428
+ markdown(): string;
1429
+ download(format: "md" | "json"): void;
1430
+ }
1431
+ /**
1432
+ * 聊天正文的数据:服务器正文并上本机刚发出还没确认的消息、工具与计划行、每轮用量、
1433
+ * 导出。停在历史窗口时不并本机消息。面板不可见时保留上一份,不因后台流式输出重算。
1434
+ */
1435
+ export interface ConversationChatTimeline {
1436
+ readonly activities: readonly ConversationThreadActivity[];
1437
+ readonly timelineEntries: readonly ConversationTimelineEntry[];
1438
+ readonly usageByTurnId: ReadonlyMap<string, ConversationTurnUsage>;
1439
+ readonly export: ConversationThreadExport;
1440
+ }
1441
+ /**
1442
+ * 输入卡只读的模式与模型上下文。字段没变时是同一个对象。
1443
+ * `projectId` 在这条会话还没有项目、或项目还没加载时为 null。
1444
+ */
1445
+ export interface ConversationComposerModelContext {
1446
+ readonly runtimeMode: ConversationRuntimeMode;
1447
+ readonly interactionMode: ConversationProviderInteractionMode;
1448
+ readonly lockedProvider: ConversationProviderDriverKind | null;
1449
+ readonly providerStatuses: ReadonlyArray<ConversationServerProvider>;
1450
+ readonly projectId: ConversationProjectId | null;
1451
+ readonly activeProjectDefaultModelSelection: ConversationModelSelection | null;
1452
+ readonly activeThreadModelSelection: ConversationModelSelection | null;
1453
+ }
1454
+ /** 输入卡上的上下文用量:小圈的读数与点开后的三段构成。 */
1455
+ export interface ConversationContextUsage {
1456
+ readonly window: ConversationContextWindowSnapshot | null;
1457
+ readonly breakdown: {
1458
+ readonly systemTokens: number;
1459
+ readonly toolsTokens: number;
1460
+ readonly messageTokens: number;
1461
+ };
1462
+ }
1463
+ /** 会话正文的同步状态:loading 还没有可显示的正文,syncing 正在对齐已缓存的正文。 */
1464
+ export type ConversationThreadSyncPhase = "loading" | "syncing";
1465
+ /**
1466
+ * 输入卡正在回答的那道 agent 提问走到哪了。没有待回答的提问时整份快照为 null。
1467
+ */
1468
+ export interface ConversationPendingUserInputProgress {
1469
+ readonly questionIndex: number;
1470
+ readonly isLastQuestion: boolean;
1471
+ readonly canAdvance: boolean;
1472
+ readonly customAnswer: string;
1473
+ readonly activeQuestion: {
1474
+ readonly id: string;
1475
+ readonly multiSelect?: boolean | undefined;
1476
+ readonly allowFreeform?: boolean | undefined;
1477
+ } | null;
1478
+ }
1479
+ /**
1480
+ * 输入卡的待处理项与回答它们的命令。快照在输入没变时保持同一引用。
1481
+ * 命令失败时 reject,错误信息可直接给用户看;中断(请求被更新的请求取代)不报错。
1482
+ */
1483
+ export interface ConversationComposerPending {
1484
+ readonly activePendingApproval: ConversationPendingApproval | null;
1485
+ readonly pendingUserInputs: readonly ConversationPendingUserInput[];
1486
+ readonly activePendingProgress: ConversationPendingUserInputProgress | null;
1487
+ readonly activePendingResolvedAnswers: Record<string, unknown> | null;
1488
+ readonly activePendingIsResponding: boolean;
1489
+ readonly activePendingDraftAnswers: Record<string, ConversationPendingUserInputDraftAnswer>;
1490
+ readonly activePendingQuestionIndex: number;
1491
+ readonly respondingRequestIds: readonly ConversationApprovalRequestId[];
1492
+ /** 谁占着输入区顶部,已按优先级排好;输入区一次只画第一个。 */
1493
+ readonly occupants: readonly ConversationComposerOccupant[];
1494
+ respondToApproval(requestId: ConversationApprovalRequestId, decision: ConversationProviderApprovalDecision, reason?: string): Promise<void>;
1495
+ /**
1496
+ * 回答决定卡。时间线那份是同一个回答——两处画的是同一张卡。
1497
+ * 失败时返回错误信息,调用方写进线程错误条;成功或被更新的请求取代时为 null。
1498
+ */
1499
+ respondToDecision(requestId: string, optionId: string): Promise<string | null>;
1500
+ selectUserInputOption(questionId: string, optionLabel: string): void;
1501
+ /**
1502
+ * 写入当前提问的自由作答。`nextCursor` 是收窄后的光标,`expandedCursor` 含被折叠的
1503
+ * 提及;`cursorAdjacentToMention` 为真时不重新探测触发器。
1504
+ */
1505
+ changeUserInputCustomAnswer(questionId: string, value: string, nextCursor: number, expandedCursor: number, cursorAdjacentToMention: boolean): void;
1506
+ /**
1507
+ * 下一题;最后一题且答案齐了就提交。提交失败时返回错误信息,调用方写进线程错误条;
1508
+ * 推进、答案不齐、成功或被更新的请求取代时为 null。
1509
+ */
1510
+ advanceUserInput(): Promise<string | null>;
1511
+ previousUserInputQuestion(): void;
1512
+ }
1513
+ /** 轨迹工具行展开详情的回取参数。 */
1514
+ export interface ConversationToolCallDetailRequest {
1515
+ readonly toolCallId: string;
1516
+ readonly turnId: string | null;
1517
+ readonly revision: string;
1518
+ }
1519
+ /**
1520
+ * 宿主借给会话插件的数据接口。整个对象身份恒定,不随会话或渲染变化;插件在组件
1521
+ * 顶层调用 `use*` 成员订阅自己要的那一块数据,只有这块数据变了才重新渲染。
1522
+ * 数据来自宿主已有的连接与缓存,插件不另建连接或缓存。
1523
+ */
1524
+ export interface ConversationHostData {
1525
+ readonly version: typeof CONVERSATION_HOST_DATA_VERSION;
1526
+ /** 排队区的读模型。`threadKey` 是 scope.threadKey,用来读发送流程写下的本机排队占位。 */
1527
+ useQueue(input: {
1528
+ readonly target: ConversationThreadTarget;
1529
+ readonly threadKey: string;
1530
+ }): ConversationQueueSnapshot;
1531
+ readonly queue: ConversationQueueCommands;
1532
+ /**
1533
+ * 轨迹面板的数据。`threadKey` 是 scope.threadKey,用来隔离每个 presenter 的翻页
1534
+ * 状态;`visible` 为 false 时不重算行、不上报可见轮次。
1535
+ */
1536
+ useTrajectory(input: {
1537
+ readonly target: ConversationThreadTarget;
1538
+ readonly threadKey: string;
1539
+ readonly visible: boolean;
1540
+ }): ConversationTrajectorySnapshot;
1541
+ /** 聊天正文;参数含义同 useTrajectory。只在聊天面板挂载时调用。 */
1542
+ useChatTimeline(input: {
1543
+ readonly target: ConversationThreadTarget;
1544
+ readonly threadKey: string;
1545
+ readonly visible: boolean;
1546
+ }): ConversationChatTimeline;
1547
+ /**
1548
+ * 输入卡的上下文用量。草稿会话(服务器上还没有这条会话)为 null。只在输入卡里调用,
1549
+ * 用量随每条回复变化,读它的组件会跟着重渲染。
1550
+ */
1551
+ useContextUsage(target: ConversationThreadTarget): ConversationContextUsage | null;
1552
+ /**
1553
+ * 输入卡只读的模式与模型上下文:四档、计划/构建、已锁的 driver、这台机器的实例表、
1554
+ * 项目默认与这条会话落库的模型选择。`projectId` 在这条会话还没有项目时为 null。
1555
+ * 只在输入卡里调用;字段没变时返回同一个对象。
1556
+ */
1557
+ useComposerModelContext(target: ConversationThreadTarget): ConversationComposerModelContext;
1558
+ /**
1559
+ * 这只手自己账号的额度观测。`active` 为 false(同屏里没聚焦的那一格)时不轮询,
1560
+ * 返回 undefined;宿主还没接线(机器或实例未知)时同样是 undefined。只在额度区里调用,
1561
+ * 轮询到的新快照只重渲染额度区。
1562
+ */
1563
+ useAccountUsage(input: {
1564
+ readonly target: ConversationThreadTarget;
1565
+ readonly active: boolean;
1566
+ }): ConversationComposerAccountUsage | undefined;
1567
+ /** 聊天面板的数据窗口;参数含义同 useTrajectory。 */
1568
+ useChatWindow(input: {
1569
+ readonly target: ConversationThreadTarget;
1570
+ readonly threadKey: string;
1571
+ readonly visible: boolean;
1572
+ }): ConversationChatWindow;
1573
+ /** 工具行展开时回取输入/输出;宿主负责去重、代际与实时输出尾部。 */
1574
+ useToolCallDetail(target: ConversationThreadTarget, request: ConversationToolCallDetailRequest): ConversationTrajectoryToolCallDetailData | null;
1575
+ /**
1576
+ * 会话正文的同步状态,未经延时。已是实时数据、草稿会话或机器未连上时为 null;
1577
+ * 机器未连上另有机器状态提示。
1578
+ */
1579
+ useThreadSyncPhase(target: ConversationThreadTarget): ConversationThreadSyncPhase | null;
1580
+ /**
1581
+ * 输入卡的待处理项:审批、agent 提问、决定卡与谁占着输入区。`threadKey` 是
1582
+ * scope.threadKey,草稿答案、问题序号与正在回答的请求按它在各 presenter 间共享。
1583
+ * 审批与提问随活动流变化,读它的组件会跟着重渲染。
1584
+ */
1585
+ useComposerPending(input: {
1586
+ readonly target: ConversationThreadTarget;
1587
+ readonly threadKey: string;
1588
+ }): ConversationComposerPending;
1589
+ /**
1590
+ * 输入卡上方的横幅:机器重连与版本、项目文件夹、会话生命周期、分支不一致。宿主按会话自己取
1591
+ * 横幅要的数据,读它的组件只随横幅相关的数据重渲染。`threadKey` 是 scope.threadKey。会话页
1592
+ * 外没有横幅(横幅上的动作要回到宿主的输入卡与错误表)。
1593
+ */
1594
+ useComposerBanners(input: {
1595
+ readonly target: ConversationThreadTarget;
1596
+ readonly threadKey: string;
1597
+ }): ConversationComposerBanners;
1598
+ /**
1599
+ * 输入卡的会话与发送状态:阶段、发送中、为什么发不了、机器是否可用、能否压缩。宿主按会话
1600
+ * 自己推导,字段都没变时返回同一个对象,读它的组件只随这些状态重渲染。`threadKey` 是
1601
+ * scope.threadKey,用来读发送流程写下的本机发送进度。
1602
+ */
1603
+ useComposerStatus(input: {
1604
+ readonly target: ConversationThreadTarget;
1605
+ readonly threadKey: string;
1606
+ }): ConversationComposerStatus;
1607
+ /**
1608
+ * 这一轮现在的状态:是否在干活、卡在哪、从何时起。`threadKey` 是 scope.threadKey,
1609
+ * 用来读发送流程写下的本机发送进度;`visible` 为 false 时保留上一份、卡住的计时也不走。
1610
+ * 只在聊天面板挂载时调用。
1611
+ */
1612
+ useTurnStatus(input: {
1613
+ readonly target: ConversationThreadTarget;
1614
+ readonly threadKey: string;
1615
+ readonly visible: boolean;
1616
+ }): ConversationTurnStatus;
1617
+ /**
1618
+ * 输入卡的计划:已提出的计划、进行中的任务进度与步骤。草稿会话(服务器上还没有这条
1619
+ * 会话)计划为 null、进度与步骤为 null。只在输入卡里调用。
1620
+ */
1621
+ useComposerPlan(target: ConversationThreadTarget): ConversationComposerPlan;
1622
+ }
1623
+ /** 这一轮现在的状态(宿主原先经时间线 props 传的那几样)。 */
1624
+ export interface ConversationTurnStatus {
1625
+ readonly latestTurn: ConversationLatestTurn | null;
1626
+ readonly latestAssistantMessageId: string | null;
1627
+ /** 正在跑的那一轮;没有为 null。 */
1628
+ readonly runningTurnId: string | null;
1629
+ /** 本轮在干活:会话在跑,或消息刚发出、服务器还没确认。 */
1630
+ readonly isWorking: boolean;
1631
+ /** 工作行上那句当前步骤;不是本轮自己的计划时为 null。 */
1632
+ readonly workingStepLabel: string | null;
1633
+ /** 非空表示这一轮超过门槛没动静;插件只读呈现。 */
1634
+ readonly stall: ConversationTurnStall | null;
1635
+ readonly retry: unknown;
1636
+ readonly activeTurnStartedAt: string | null;
1637
+ readonly totalTurnCount: number | null;
1638
+ readonly pendingModelSelection: ConversationModelSelection | null | undefined;
1639
+ readonly scheduledWakes: ReadonlyArray<unknown>;
1640
+ }
1641
+ /** 输入卡的计划(宿主原先经输入卡 props 传的那几样)。 */
1642
+ export interface ConversationComposerPlan {
1643
+ /** 最新一轮已落定后,最新一份还能实施的计划;进行中、已实施或没有为 null。 */
1644
+ readonly proposedPlan: {
1645
+ readonly id: string;
1646
+ readonly planMarkdown: string;
1647
+ } | null;
1648
+ /** 本轮未落定时的任务进度;落定或没有为 null。 */
1649
+ readonly tasksProgress: {
1650
+ readonly step: string;
1651
+ readonly completedSteps: number;
1652
+ readonly totalSteps: number;
1653
+ } | null;
1654
+ /** 与进度同一轮的步骤;对不上或没有进度为 null。 */
1655
+ readonly taskSteps: readonly {
1656
+ readonly durationMs?: number;
1657
+ readonly step: string;
1658
+ readonly status: "pending" | "inProgress" | "completed";
1659
+ }[] | null;
1660
+ }
1384
1661
  export interface ConversationTrajectoryRow {
1385
1662
  readonly id: string;
1386
1663
  readonly kind: string;
@@ -1465,32 +1742,18 @@ export interface ConversationShortcutLabels {
1465
1742
  readonly toggleTrajectory: string | null;
1466
1743
  readonly interrupt: string | null;
1467
1744
  }
1468
- /** 时间线当前的可滚动几何;宿主用它维持跟底、发送锚点与阅读位置。 */
1469
- export interface ConversationTimelineScrollMeasurement {
1470
- readonly scroll: number;
1471
- readonly scrollLength: number;
1472
- readonly contentLength: number;
1473
- readonly topOf: (rowId: string) => number | null;
1474
- readonly heightOf: (rowId: string) => number | null;
1475
- readonly rowAtScroll: () => {
1476
- readonly rowId: string;
1477
- readonly offsetPx: number;
1478
- } | null;
1479
- }
1480
1745
  /**
1481
- * 时间线插件公开给宿主的 imperative handle。滚动节点保持不透明,避免插件 API
1482
- * 引入 DOM 类型;宿主只在自己的客户端边界把它收窄成真实元素。
1746
+ * 宿主驱动聊天时间线的入口。跟随末尾、「回到底部」与手势停跟都归插件,宿主只在
1747
+ * 自己发出消息后请求回到末尾。
1483
1748
  */
1484
- export interface ConversationTimelineHandle {
1485
- readonly node: unknown;
1486
- readonly measure: () => ConversationTimelineScrollMeasurement | null;
1487
- readonly scrollToEnd: (options?: {
1749
+ export interface ConversationTimelineControl {
1750
+ /**
1751
+ * 解除展开阅读锁、恢复跟随并滚到末尾。聊天停在历史窗口时先经数据窗口的
1752
+ * `onReturnToLatest` 换回最新页;时间线不可见时等重新可见再滚。
1753
+ */
1754
+ followEnd(options?: {
1488
1755
  readonly animated?: boolean;
1489
- }) => void;
1490
- readonly scrollToRow: (rowId: string, options?: {
1491
- readonly viewOffset?: number;
1492
- }) => boolean;
1493
- readonly scrollToOffset: (offset: number) => void;
1756
+ }): void;
1494
1757
  }
1495
1758
  /** 可见和受交互保护的轮次;历史页保留归宿主,插件只报告当前呈现范围。 */
1496
1759
  export interface ConversationVisibleTurnRange {
@@ -1503,8 +1766,6 @@ export interface ConversationVisibleTurnRange {
1503
1766
  readonly protectedTurnIds: ReadonlyArray<ConversationTurnId>;
1504
1767
  }
1505
1768
  export interface ConversationTimelineProps {
1506
- readonly activities: readonly ConversationThreadActivity[];
1507
- readonly timelineEntries: readonly ConversationTimelineEntry[];
1508
1769
  /** 行派生(= ConversationViewProps.derive;时间线经 ctx 下发给行组件)。 */
1509
1770
  readonly derive: ConversationDeriveApi;
1510
1771
  /** 会话路由键:开合态、滚动落点、已读上报都按它隔离(= scope.threadKey)。 */
@@ -1513,19 +1774,8 @@ export interface ConversationTimelineProps {
1513
1774
  readonly threadRef: unknown;
1514
1775
  /** 会话所属机器(= scope.environmentId):计划卡保存走它。 */
1515
1776
  readonly environmentId: string;
1516
- readonly latestTurn: ConversationLatestTurn | null;
1517
- readonly latestAssistantMessageId: string | null;
1518
- readonly runningTurnId: string | null;
1519
- readonly isWorking: boolean;
1520
- readonly workingStepLabel: string | null;
1521
- /** 非空表示这一轮超过门槛没动静;插件只读呈现。 */
1522
- readonly stall: ConversationTurnStall | null;
1523
- readonly retry: unknown;
1524
- readonly activeTurnStartedAt: string | null;
1525
- readonly totalTurnCount: number | null;
1526
1777
  readonly threadTitle: string;
1527
1778
  readonly resolveModelBadge: ((selection: ConversationModelSelection | undefined) => ConversationModelBadge | null) | undefined;
1528
- readonly pendingModelSelection: ConversationModelSelection | null | undefined;
1529
1779
  readonly markdownCwd: string | undefined;
1530
1780
  readonly resolvedTheme: "light" | "dark";
1531
1781
  readonly timestampFormat: string;
@@ -1534,35 +1784,15 @@ export interface ConversationTimelineProps {
1534
1784
  readonly name: string;
1535
1785
  readonly displayName: string;
1536
1786
  }>;
1537
- readonly anchorMessageId: string | null;
1538
1787
  readonly contentInsetEndAdjustment: number;
1539
- readonly liveFollowEnabled: boolean;
1540
1788
  readonly awaitingUserDecision: boolean;
1541
1789
  readonly hideEmptyPlaceholder: boolean;
1542
1790
  readonly topFadeEnabled: boolean;
1543
- /** 宿主滚动状态机使用的句柄;插件挂载、换节点与卸载时都必须同步。 */
1544
- readonly scrollerRef: {
1545
- current: ConversationTimelineHandle | null;
1546
- } | ((handle: ConversationTimelineHandle | null) => void);
1547
- readonly loadEarlier: {
1548
- readonly loading: boolean;
1549
- onLoadEarlier(): void;
1550
- } | null;
1551
- readonly loadLater?: {
1552
- readonly loading: boolean;
1553
- onLoadLater(): void;
1554
- } | null;
1555
- /** 原生选区或编辑中的行仍占有历史正文时,不能加载会驱逐它们的新页。 */
1556
- readonly pagingBlockedByInteraction?: boolean;
1557
- readonly onReturnToLatest?: (() => void) | null;
1558
- readonly onVisibleTurnRangeChange?: ((range: ConversationVisibleTurnRange | null) => void) | null;
1559
- readonly revealMessageRequest: {
1560
- readonly rowId: string;
1561
- readonly nonce: number;
1562
- } | null;
1563
- readonly usageByTurnId: ReadonlyMap<string, ConversationTurnUsage>;
1791
+ /** 宿主拿到的时间线控制入口;插件在挂载、换会话与卸载时同步它。 */
1792
+ readonly controlRef: {
1793
+ current: ConversationTimelineControl | null;
1794
+ } | ((control: ConversationTimelineControl | null) => void);
1564
1795
  readonly agentPanelModel: unknown;
1565
- readonly scheduledWakes: ReadonlyArray<unknown>;
1566
1796
  /** 草稿/断开会话传 null:决定卡只显示状态、无按钮。 */
1567
1797
  readonly onRespondToDecision: ((requestId: string, optionId: string) => void) | null;
1568
1798
  readonly onInterrupt: (() => void) | null;
@@ -1571,15 +1801,6 @@ export interface ConversationTimelineProps {
1571
1801
  readonly onForkFromUserMessage: ((messageId: string) => void) | null;
1572
1802
  readonly onImageExpand: (preview: unknown) => void;
1573
1803
  readonly onOpenAgents: (() => void) | null;
1574
- onAnchorReady(messageId: string, anchor: {
1575
- readonly rowId: string;
1576
- readonly lastRowId: string | null;
1577
- }): void;
1578
- onRowsChange(lastRowId: string | null): void;
1579
- onIsAtEndChange(isAtEnd: boolean): void;
1580
- onManualNavigation(): void;
1581
- onDisclosureNavigation(): void;
1582
- onRevealMessageHandled(): void;
1583
1804
  /**
1584
1805
  * 工具呈现接缝:一组工具/过程行的展开体。宿主先用现有实现渲染,接
1585
1806
  * tool-display 插件后改指其视图;缺省时插件画极简回退(标题+摘要)。
@@ -1609,11 +1830,6 @@ export interface ConversationTimelineProps {
1609
1830
  };
1610
1831
  /** 已读上报:可见帧内最后一条完成消息变化时由宿主标记。 */
1611
1832
  useVisibleThreadCompletion(input: unknown): void;
1612
- /** 导出(md/json)由宿主构建:读的是完整视图模型,插件只触发。 */
1613
- readonly export: {
1614
- markdown(): string;
1615
- download(format: "md" | "json"): void;
1616
- };
1617
1833
  /**
1618
1834
  * 计划卡「保存到工作区」的宿主 RPC(写文件走宿主命令管线)。null 时不画
1619
1835
  * 该菜单项(草稿会话、环境断开)。
@@ -1642,46 +1858,12 @@ export interface ConversationTrajectoryToolCallDetailData {
1642
1858
  readonly input?: string | undefined;
1643
1859
  readonly output?: string | undefined;
1644
1860
  }
1861
+ /**
1862
+ * 轨迹面板里宿主仍要给的部分。行、翻页与工具详情由插件经 {@link ConversationHostData}
1863
+ * 自己读;在轨迹中选中一行后切回聊天定位由插件自己协调。
1864
+ */
1645
1865
  export interface ConversationTrajectoryProps {
1646
- readonly rows: readonly ConversationTrajectoryRow[];
1647
- readonly loadEarlier: {
1648
- readonly loading: boolean;
1649
- onLoadEarlier(): void;
1650
- } | null;
1651
- readonly loadLater?: {
1652
- readonly loading: boolean;
1653
- onLoadLater(): void;
1654
- } | null;
1655
- /** 原生选区或编辑中的行仍占有历史正文时,不能加载会驱逐它们的新页。 */
1656
- readonly pagingBlockedByInteraction?: boolean;
1657
- /** 只有最新页已可用时调用 onReady;被交互保护拒绝的导航不能切换跟随。 */
1658
- readonly onReturnToLatest?: ((onReady?: () => void) => void) | null;
1659
- readonly onVisibleTurnRangeChange?: ((range: ConversationVisibleTurnRange | null) => void) | null;
1660
1866
  readonly timestampFormat: string;
1661
- /** 保活隐藏时为 false:不量几何、不碰布局、不跟随。 */
1662
- readonly paneVisible: boolean;
1663
- /** 宿主 ScopedThreadRef(对插件不透明):工具详情懒加载按它寻址。 */
1664
- readonly threadRef: unknown;
1665
- /** 行 → 聊天锚消息 id;无可锚的行返回 null(不画出不去的按钮)。 */
1666
- resolveChatAnchorMessageId(row: ConversationTrajectoryRow): string | null;
1667
- onOpenInChat(rowId: string): void;
1668
- /**
1669
- * 工具行展开详情的懒加载 hook 接缝:宿主绑定现有 useToolCallDetailState
1670
- * (scoped ref / turnId / revision / enabled),只读结果供行组件使用。RPC、
1671
- * 代际、去重、实时输出尾部都归宿主机制;插件在组件顶层调用(hook 规则)。
1672
- */
1673
- readonly useToolCallDetail?: ((input: ConversationTrajectoryToolCallDetailRequest) => ConversationTrajectoryToolCallDetailData | null) | undefined;
1674
- }
1675
- export interface ConversationQueueProps {
1676
- readonly entries: readonly ConversationQueueEntry[];
1677
- /** 可选以兼容尚未提供乐观队列占位的宿主。 */
1678
- readonly pendingSubmissions?: readonly ConversationPendingQueueSubmission[] | undefined;
1679
- readonly state: ConversationQueueState;
1680
- readonly environmentId: string;
1681
- readonly unavailable: boolean;
1682
- readonly actions: ConversationQueueActions;
1683
- onCancel(entry: ConversationQueueEntry): Promise<void>;
1684
- onResume(): Promise<void>;
1685
1867
  }
1686
1868
  /** 输入卡上方的横幅堆条目(宿主算好顺序;icon/title 等是宿主创建的节点,插件原样渲染)。 */
1687
1869
  export interface ConversationComposerBannerItem {
@@ -1700,6 +1882,35 @@ export interface ConversationComposerBannerItem {
1700
1882
  readonly dismissLabel?: string | undefined;
1701
1883
  readonly onDismiss?: (() => void) | undefined;
1702
1884
  }
1885
+ /** 输入卡上方的横幅堆(宿主排好了顺序)与横幅带出的宿主浮层。 */
1886
+ export interface ConversationComposerBanners {
1887
+ readonly items: ReadonlyArray<ConversationComposerBannerItem>;
1888
+ /** 宿主 React 节点(例如切回分支前的确认框),插件原样挂在输入卡旁;没有时为 null。 */
1889
+ readonly overlay: unknown;
1890
+ }
1891
+ /** 输入卡读的会话与发送状态(`ConversationHostData.useComposerStatus`)。 */
1892
+ export interface ConversationComposerStatus {
1893
+ readonly activeThreadId: ConversationThreadId | null;
1894
+ /** 只读这些 meta 字段;宿主给低频 meta 视图而不是完整 detail。 */
1895
+ readonly activeThread: Pick<ConversationThreadMeta, "id" | "session" | "latestTurn" | "presetId" | "nativeModeId"> | undefined;
1896
+ readonly isLocalDraftThread: boolean;
1897
+ /** 草稿会话还没有项目,发不出去。 */
1898
+ readonly projectSelectionRequired: boolean;
1899
+ readonly phase: ConversationSessionPhase;
1900
+ /** 消息刚发出、服务器还没确认。 */
1901
+ readonly isSendBusy: boolean;
1902
+ /** 发送被挡下的原因(宿主解析好的文案);能发时为 null。 */
1903
+ readonly sendDisabledReason: string | null;
1904
+ /** 刚发出的消息在等新 worktree 建好。 */
1905
+ readonly isPreparingWorktree: boolean;
1906
+ /** 会话所在机器没连上时,它的名字与连接状态。 */
1907
+ readonly environmentUnavailable: {
1908
+ readonly label: string;
1909
+ readonly connection: ConversationEnvironmentConnectionPresentation;
1910
+ } | null;
1911
+ readonly compactDisabled: boolean;
1912
+ readonly compactDisabledReason: string | null;
1913
+ }
1703
1914
  export interface ConversationComposerProps {
1704
1915
  /** 宿主注入的 composer API(store/hook/纯函数面)。 */
1705
1916
  api: ConversationComposerApi;
@@ -1710,87 +1921,16 @@ export interface ConversationComposerProps {
1710
1921
  routeKind: "server" | "draft";
1711
1922
  routeThreadRef: ConversationScopedThreadRef;
1712
1923
  draftId: ConversationDraftId | null;
1713
- activeThreadId: ConversationThreadId | null;
1714
- activeThreadEnvironmentId: ConversationEnvironmentId | undefined;
1715
- /** 只读这些 meta 字段;宿主传低频 meta 视图而不是完整 detail。 */
1716
- activeThread: Pick<ConversationThreadMeta, "id" | "session" | "latestTurn" | "presetId" | "nativeModeId"> | undefined;
1717
- isServerThread: boolean;
1718
- isLocalDraftThread: boolean;
1719
1924
  forceExpandedOnMobile: boolean;
1720
- projectSelectionRequired: boolean;
1721
- phase: ConversationSessionPhase;
1722
- isConnecting: boolean;
1723
- isSendBusy: boolean;
1724
- sendDisabledReason: string | null;
1725
- isPreparingWorktree: boolean;
1726
- externalDrawerAttached: boolean;
1727
- environmentUnavailable: {
1728
- readonly label: string;
1729
- readonly connection: ConversationEnvironmentConnectionPresentation;
1730
- } | null;
1731
- activePendingApproval: ConversationPendingApproval | null;
1732
- /** 回答决定卡。时间线那份是同一个回调——两处画的是同一张卡。 */
1733
- onRespondToComposerDecision: (requestId: string, optionId: string) => void;
1734
- pendingUserInputs: ConversationPendingUserInput[];
1735
- activePendingProgress: {
1736
- questionIndex: number;
1737
- isLastQuestion: boolean;
1738
- canAdvance: boolean;
1739
- customAnswer: string;
1740
- activeQuestion: {
1741
- id: string;
1742
- multiSelect?: boolean | undefined;
1743
- allowFreeform?: boolean | undefined;
1744
- } | null;
1745
- } | null;
1746
- activePendingResolvedAnswers: Record<string, unknown> | null;
1747
- activePendingIsResponding: boolean;
1748
- activePendingDraftAnswers: Record<string, ConversationPendingUserInputDraftAnswer>;
1749
- activePendingQuestionIndex: number;
1750
- respondingRequestIds: ConversationApprovalRequestId[];
1751
- /** 谁占着输入区顶部,已按优先级排好;输入区一次只画第一个。 */
1752
- occupants: ReadonlyArray<ConversationComposerOccupant>;
1753
- showPlanFollowUpPrompt: boolean;
1754
- activeProposedPlan: ConversationThreadMeta["proposedPlans"][number] | null;
1755
- activeTasksProgress: unknown;
1756
- activeTaskSteps: readonly unknown[] | null;
1757
- runtimeMode: ConversationRuntimeMode;
1758
- interactionMode: ConversationProviderInteractionMode;
1759
- lockedProvider: ConversationProviderDriverKind | null;
1760
- providerStatuses: ConversationServerProvider[];
1761
- activeProjectDefaultModelSelection: ConversationModelSelection | null | undefined;
1762
- projectId?: ConversationProjectId | null;
1763
- activeThreadModelSelection: ConversationModelSelection | null | undefined;
1764
- selectedExecutionSelection?: ConversationExecutionSelection | null | undefined;
1765
- composerChoices?: ReadonlyArray<ConversationComposerChoice> | undefined;
1766
- selectedExecutionReady?: boolean | undefined;
1767
- selectedModelOptionDescriptors?: ReadonlyArray<ConversationProviderOptionDescriptor> | undefined;
1768
- onModelPickerOpenChange?: ((open: boolean) => void) | undefined;
1769
- onClearExecutionSelection?: (() => void) | undefined;
1770
- runningExecutionSelection?: ConversationExecutionSelection | null | undefined;
1771
- executionAppliesTo?: ConversationExecutionAppliesTo | undefined;
1772
- /**
1773
- * 所选来源的额度观测。宿主注入共享查询结果;缺席表示宿主尚未接线。
1774
- * 插件只渲染这份 props,不自行取数。
1775
- */
1776
- sourceMetrics?: ConversationComposerSourceMetrics | undefined;
1777
- activeContextWindow: ConversationContextWindowSnapshot | null;
1778
- /** 点开小圈的三段构成;缺席时只画已用/窗口。 */
1779
- activeContextBreakdown?: {
1780
- readonly systemTokens: number;
1781
- readonly toolsTokens: number;
1782
- readonly messageTokens: number;
1783
- } | null;
1784
1925
  /**
1785
1926
  * 输入区座位。骨架调用 `renderSlot`;整条输入区被占住时走 `renderSlotChain`。
1786
- * 缺席(测试)时骨架画内置默认控件。
1927
+ * 缺席(测试)时骨架画内置默认控件。`fallback` 是骨架的内置控件:座位无人占用、
1928
+ * 或占用者渲染时抛错,宿主都画它,一个插件的控件坏掉不会连带整块会话区。
1787
1929
  */
1788
1930
  slots?: {
1789
- readonly renderSlot: (name: string, owner?: unknown) => unknown;
1931
+ readonly renderSlot: (name: string, owner?: unknown, fallback?: unknown) => unknown;
1790
1932
  readonly renderSlotChain: (name: string, owner: unknown, fallback: unknown) => unknown;
1791
1933
  };
1792
- compactDisabled: boolean;
1793
- compactDisabledReason: string | null;
1794
1934
  resolvedTheme: "light" | "dark";
1795
1935
  settings: ConversationUnifiedSettings;
1796
1936
  keybindings: ConversationResolvedKeybindingsConfig;
@@ -1822,9 +1962,10 @@ export interface ConversationComposerProps {
1822
1962
  composerRef: {
1823
1963
  current: ConversationComposerHandle | null;
1824
1964
  } | ((handle: ConversationComposerHandle | null) => void);
1965
+ /** `steer`:agent 在干活时照常排队,再请当前这一轮接住它;没在干活时与普通发送相同。 */
1825
1966
  onSend: (e?: {
1826
1967
  preventDefault: () => void;
1827
- }, intent?: "foreground" | "background") => void;
1968
+ }, intent?: "foreground" | "background" | "steer") => void;
1828
1969
  onInterrupt: () => void;
1829
1970
  /** Esc 两段式停止已武装:停止按钮显示「再按一次」态。缺席按 false。 */
1830
1971
  interruptArmed?: boolean;
@@ -1839,18 +1980,12 @@ export interface ConversationComposerProps {
1839
1980
  /** 派发原生压缩命令。服务端按 adapter 能力位执行,不再伪造一条 `/compact` 消息。 */
1840
1981
  onCompactContext: () => void;
1841
1982
  onImplementPlanInNewThread: () => void;
1842
- onRespondToApproval: (requestId: ConversationApprovalRequestId, decision: ConversationProviderApprovalDecision, reason?: string) => Promise<unknown>;
1843
- onSelectActivePendingUserInputOption: (questionId: string, optionLabel: string) => void;
1844
- onAdvanceActivePendingUserInput: () => void;
1845
- onPreviousActivePendingUserInputQuestion: () => void;
1846
- onChangeActivePendingUserInputCustomAnswer: (questionId: string, value: string, nextCursor: number, expandedCursor: number, cursorAdjacentToMention: boolean) => void;
1847
1983
  /** 点了某个实例下的一行模型:`model` 归 `instanceId`。 */
1848
1984
  onProviderModelSelect: (instanceId: ConversationProviderInstanceId, model: string) => void;
1849
1985
  /** 选手格换手:`carried` 是换手前的选择,型号归不归新手由宿主判,选手格不猜。 */
1850
1986
  onProviderInstanceSelect: (instanceId: ConversationProviderInstanceId, carried: ConversationModelSelection) => void;
1851
1987
  /** 选择器(模型或档位)一改就把合成后的选择交出去,由宿主立刻落库。 */
1852
1988
  onModelSelectionChosen?: ((selection: ConversationModelSelection) => void) | undefined;
1853
- onExecutionSelectionChosen?: ((selection: ConversationExecutionSelection, choice: ConversationComposerChoice) => void) | undefined;
1854
1989
  /** 选中 / 清空团队。传 null 表示回到单模型;选模型时由宿主用它清掉团队。 */
1855
1990
  onSelectPluginAction: (teamId: string | null) => void;
1856
1991
  /** 跳到设置里的 Teams 位置。 */
@@ -1884,8 +2019,6 @@ export interface ConversationComposerProps {
1884
2019
  readonly terminalControl?: unknown;
1885
2020
  /** 输入卡下缘内容(分支工具条等宿主工作区控件),原样渲染。 */
1886
2021
  readonly belowSlot: unknown;
1887
- /** 排队列表上方的横幅堆(同步提示等):条目数据,插件用自己的栈渲染。 */
1888
- readonly bannerItems: ReadonlyArray<ConversationComposerBannerItem>;
1889
2022
  /** 草稿首条消息的居中大标题态。 */
1890
2023
  readonly draftHero: {
1891
2024
  readonly active: boolean;
@@ -1896,10 +2029,10 @@ export interface ConversationComposerProps {
1896
2029
  readonly composerTransitionName?: string | undefined;
1897
2030
  readonly headlineTransitionName?: string | undefined;
1898
2031
  };
1899
- readonly attached: boolean;
1900
2032
  readonly contextStrip: boolean;
1901
2033
  /**
1902
- * 浮层根元素上报:宿主量它的高度给时间线让位(contentInsetEndAdjustment)。
2034
+ * 浮层根元素上报:宿主量它的高度给时间线让位(contentInsetEndAdjustment),
2035
+ * 并在其中找 `[data-chat-composer-stack="true"]` 量输入卡占的列(浮动预览可停在两侧留白)。
1903
2036
  * 卸载时以 null 收尾。元素对契约不透明(本文件不引 DOM 类型)。
1904
2037
  */
1905
2038
  readonly onOverlayElementChange?: ((element: unknown) => void) | undefined;
@@ -1910,9 +2043,12 @@ export interface ConversationViewProps {
1910
2043
  readonly mode: ConversationModeProps;
1911
2044
  readonly shortcuts: ConversationShortcutLabels;
1912
2045
  readonly derive: ConversationDeriveApi;
2046
+ /**
2047
+ * 宿主数据接口。老版本宿主不给;插件发现缺席或版本不同时显示版本不一致的说明。
2048
+ */
2049
+ readonly data?: ConversationHostData | undefined;
1913
2050
  readonly timeline: ConversationTimelineProps;
1914
2051
  readonly trajectory: ConversationTrajectoryProps;
1915
- readonly queue: ConversationQueueProps;
1916
2052
  readonly composer: ConversationComposerProps;
1917
2053
  readonly attachments: ConversationAttachmentsApi;
1918
2054
  }