@co0ontty/wand 4.83.1 → 4.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +4 -0
  2. package/dist/agent-dispatch.js +5 -1
  3. package/dist/ai-team-availability.d.ts +1 -1
  4. package/dist/ai-team-prompts.js +13 -4
  5. package/dist/ai-team-runner.d.ts +80 -5
  6. package/dist/ai-team-runner.js +332 -55
  7. package/dist/ai-team-types.d.ts +92 -5
  8. package/dist/ai-team-types.js +9 -2
  9. package/dist/build-info.json +3 -3
  10. package/dist/cli-api.d.ts +1 -0
  11. package/dist/cli-api.js +3 -0
  12. package/dist/cli.js +18 -1
  13. package/dist/commit-task-archive.d.ts +3 -1
  14. package/dist/commit-task-archive.js +21 -10
  15. package/dist/config.d.ts +1 -1
  16. package/dist/config.js +73 -0
  17. package/dist/conversation-mentions.d.ts +16 -0
  18. package/dist/conversation-mentions.js +32 -0
  19. package/dist/conversation-service.d.ts +90 -0
  20. package/dist/conversation-service.js +759 -0
  21. package/dist/conversation-session-preview.d.ts +9 -0
  22. package/dist/conversation-session-preview.js +47 -0
  23. package/dist/conversation-task-preview.d.ts +5 -0
  24. package/dist/conversation-task-preview.js +8 -0
  25. package/dist/conversation-types.d.ts +80 -0
  26. package/dist/conversation-types.js +7 -0
  27. package/dist/core-extension-host.d.ts +27 -0
  28. package/dist/core-extension-host.js +138 -0
  29. package/dist/core-runner.d.ts +86 -0
  30. package/dist/core-runner.js +757 -0
  31. package/dist/core-status-cli.d.ts +9 -0
  32. package/dist/core-status-cli.js +280 -0
  33. package/dist/core-turn-tracker.d.ts +12 -0
  34. package/dist/core-turn-tracker.js +36 -0
  35. package/dist/decision-runner.d.ts +6 -0
  36. package/dist/decision-runner.js +3 -1
  37. package/dist/decision-tool.d.ts +2 -0
  38. package/dist/decision-tool.js +49 -22
  39. package/dist/env-utils.d.ts +7 -3
  40. package/dist/env-utils.js +30 -5
  41. package/dist/git-quick-commit.d.ts +5 -0
  42. package/dist/git-quick-commit.js +9 -3
  43. package/dist/git-utils.js +2 -1
  44. package/dist/harness-engine.d.ts +110 -0
  45. package/dist/harness-engine.js +331 -0
  46. package/dist/mission-diff.js +2 -1
  47. package/dist/model-auto-assign.d.ts +50 -0
  48. package/dist/model-auto-assign.js +163 -0
  49. package/dist/model-group-runner.d.ts +4 -0
  50. package/dist/model-group-runner.js +72 -0
  51. package/dist/model-groups.d.ts +38 -0
  52. package/dist/model-groups.js +138 -0
  53. package/dist/models.d.ts +12 -0
  54. package/dist/models.js +102 -8
  55. package/dist/npm-update-utils.js +2 -1
  56. package/dist/openrouter-free-cli.d.ts +25 -0
  57. package/dist/openrouter-free-cli.js +109 -0
  58. package/dist/openrouter-free-models.d.ts +85 -0
  59. package/dist/openrouter-free-models.js +458 -0
  60. package/dist/openrouter-free-selection.d.ts +5 -0
  61. package/dist/openrouter-free-selection.js +7 -0
  62. package/dist/path-repair.d.ts +10 -0
  63. package/dist/path-repair.js +50 -5
  64. package/dist/pi-auto-resources.d.ts +12 -0
  65. package/dist/pi-auto-resources.js +151 -0
  66. package/dist/pi-execution-types.d.ts +48 -0
  67. package/dist/pi-execution-types.js +1 -0
  68. package/dist/pi-execution.d.ts +9 -0
  69. package/dist/pi-execution.js +187 -0
  70. package/dist/pi-extension-resources.d.ts +13 -0
  71. package/dist/pi-extension-resources.js +71 -0
  72. package/dist/pi-openrouter-free-bridge.d.ts +2 -0
  73. package/dist/pi-openrouter-free-bridge.js +80 -0
  74. package/dist/pi-resource-bridge.d.ts +2 -0
  75. package/dist/pi-resource-bridge.js +73 -0
  76. package/dist/pi-resource-catalog.d.ts +18 -0
  77. package/dist/pi-resource-catalog.js +82 -0
  78. package/dist/pi-resource-recommendation.d.ts +29 -0
  79. package/dist/pi-resource-recommendation.js +169 -0
  80. package/dist/pi-resource-run.d.ts +8 -0
  81. package/dist/pi-resource-run.js +28 -0
  82. package/dist/pi-session-settings.d.ts +157 -0
  83. package/dist/pi-session-settings.js +166 -0
  84. package/dist/platform-action-catalog.d.ts +24 -0
  85. package/dist/platform-action-catalog.js +141 -0
  86. package/dist/platform-action-service.d.ts +37 -0
  87. package/dist/platform-action-service.js +138 -0
  88. package/dist/platform-action-types.d.ts +125 -0
  89. package/dist/platform-action-types.js +7 -0
  90. package/dist/process-manager.d.ts +21 -1
  91. package/dist/process-manager.js +168 -15
  92. package/dist/provider-catalog.js +1 -1
  93. package/dist/provider-cli-updater.js +29 -8
  94. package/dist/provider-history-scanner.d.ts +9 -0
  95. package/dist/provider-history-scanner.js +107 -1
  96. package/dist/retention.d.ts +19 -7
  97. package/dist/retention.js +171 -28
  98. package/dist/server-ai-team-routes.js +6 -2
  99. package/dist/server-conversation-routes.d.ts +5 -0
  100. package/dist/server-conversation-routes.js +91 -0
  101. package/dist/server-employee-routes.d.ts +2 -0
  102. package/dist/server-employee-routes.js +85 -11
  103. package/dist/server-pi-recommendation-routes.d.ts +13 -0
  104. package/dist/server-pi-recommendation-routes.js +67 -0
  105. package/dist/server-resume-routes.js +1 -1
  106. package/dist/server-session-routes.d.ts +4 -3
  107. package/dist/server-session-routes.js +182 -37
  108. package/dist/server-settings-routes.d.ts +5 -0
  109. package/dist/server-settings-routes.js +46 -0
  110. package/dist/server-task-routes.js +49 -4
  111. package/dist/server-team-dispatch-routes.d.ts +19 -0
  112. package/dist/server-team-dispatch-routes.js +71 -0
  113. package/dist/server-update-routes.d.ts +1 -1
  114. package/dist/server-update-routes.js +10 -5
  115. package/dist/server-workspace-routes.js +44 -24
  116. package/dist/server.d.ts +1 -0
  117. package/dist/server.js +148 -30
  118. package/dist/session-ai-context.d.ts +5 -1
  119. package/dist/session-ai-context.js +9 -4
  120. package/dist/session-registry.d.ts +4 -0
  121. package/dist/session-registry.js +47 -13
  122. package/dist/session-transport.js +8 -1
  123. package/dist/settings-web-access.d.ts +15 -0
  124. package/dist/settings-web-access.js +37 -0
  125. package/dist/silicon-employee-defaults.d.ts +29 -0
  126. package/dist/silicon-employee-defaults.js +41 -0
  127. package/dist/silicon-employee-dispatch.d.ts +5 -1
  128. package/dist/silicon-employee-dispatch.js +10 -4
  129. package/dist/silicon-employee-draft.d.ts +21 -4
  130. package/dist/silicon-employee-draft.js +103 -16
  131. package/dist/storage.d.ts +73 -2
  132. package/dist/storage.js +362 -26
  133. package/dist/structured-claude-protocol.js +1 -1
  134. package/dist/structured-client-protocol.js +11 -33
  135. package/dist/structured-content.d.ts +23 -0
  136. package/dist/structured-content.js +72 -0
  137. package/dist/structured-failure.d.ts +16 -0
  138. package/dist/structured-failure.js +36 -0
  139. package/dist/structured-pi-adapter.d.ts +8 -3
  140. package/dist/structured-pi-adapter.js +131 -30
  141. package/dist/structured-provider-common.d.ts +6 -0
  142. package/dist/structured-provider-common.js +23 -1
  143. package/dist/structured-runner.d.ts +45 -0
  144. package/dist/structured-session-manager.d.ts +78 -4
  145. package/dist/structured-session-manager.js +513 -129
  146. package/dist/structured-thinking.d.ts +15 -0
  147. package/dist/structured-thinking.js +46 -0
  148. package/dist/subagent-dispatch.d.ts +56 -0
  149. package/dist/subagent-dispatch.js +102 -0
  150. package/dist/system-employee.d.ts +4 -3
  151. package/dist/system-employee.js +7 -4
  152. package/dist/task-retention.d.ts +23 -0
  153. package/dist/task-retention.js +69 -0
  154. package/dist/task-types.d.ts +4 -0
  155. package/dist/task-types.js +3 -0
  156. package/dist/team-dispatch.d.ts +101 -0
  157. package/dist/team-dispatch.js +269 -0
  158. package/dist/tool-preview.js +7 -1
  159. package/dist/tui/commands.js +50 -1
  160. package/dist/tui/ipc-protocol.d.ts +1 -1
  161. package/dist/tui/ipc-server.d.ts +4 -0
  162. package/dist/tui/ipc-server.js +23 -0
  163. package/dist/turn-heartbeat.d.ts +35 -0
  164. package/dist/turn-heartbeat.js +64 -0
  165. package/dist/types.d.ts +147 -1
  166. package/dist/user-profile.d.ts +33 -0
  167. package/dist/user-profile.js +70 -0
  168. package/dist/web-ui/chat-message-meta.d.ts +11 -0
  169. package/dist/web-ui/chat-message-meta.js +47 -0
  170. package/dist/web-ui/content/ai-teams.js +13 -13
  171. package/dist/web-ui/content/scripts.js +795 -95
  172. package/dist/web-ui/content/styles.css +1 -1
  173. package/dist/web-ui/content/tailwind.css +1 -6808
  174. package/dist/web-ui/content/vendor/qrcode/qrcode.bundle.js +1 -1
  175. package/dist/web-ui/embedded-assets.d.ts +1 -1
  176. package/dist/web-ui/embedded-assets.js +5 -5
  177. package/dist/web-ui/index.d.ts +1 -1
  178. package/dist/web-ui/index.js +3 -3
  179. package/dist/web-ui/markdown.d.ts +2 -0
  180. package/dist/web-ui/markdown.js +111 -0
  181. package/dist/web-ui/page.d.ts +4 -0
  182. package/dist/web-ui/page.js +8 -0
  183. package/dist/web-ui/react/local-preview/controller.d.ts +30 -0
  184. package/dist/web-ui/react/local-preview/controller.js +159 -0
  185. package/dist/web-ui/running-activity.d.ts +63 -0
  186. package/dist/web-ui/running-activity.js +134 -0
  187. package/dist/web-ui/styles.js +2 -11
  188. package/package.json +7 -2
package/dist/types.d.ts CHANGED
@@ -79,6 +79,31 @@ export interface RenderConfig {
79
79
  export interface StructuredProcessConfig {
80
80
  processHost: "legacy" | "rust";
81
81
  }
82
+ /**
83
+ * 结构化会话的 harness 引擎。`core` 在 Wand 进程内跑 agent loop(首期只支持 pi),
84
+ * `cli` 保留既有 CLI runner,`auto` 在 Pi 侧认证可用时选 core、否则回退 CLI。
85
+ * 与 `render.engine` 一样只切换执行所有权,存储与客户端契约不变。
86
+ */
87
+ export type HarnessEngine = "auto" | "core" | "cli";
88
+ /** 进程内 core 引擎的上下文预算;省略时用上游内置默认。 */
89
+ export interface HarnessCompactionConfig {
90
+ /** 是否开启自动压缩(默认开启)。关闭后超长会话会直接报上下文超限。 */
91
+ enabled?: boolean;
92
+ /** 为模型回复预留的 token(默认 16384)。 */
93
+ reserveTokens?: number;
94
+ /** 压缩后保留的近期 token(默认 20000)。 */
95
+ keepRecentTokens?: number;
96
+ }
97
+ export interface HarnessConfig {
98
+ engine: HarnessEngine;
99
+ /**
100
+ * Pi 的 agent 目录(默认 `~/.pi/agent`):core 引擎从这里读认证与模型目录。
101
+ * 与 Pi CLI 共用同一份登录态;隔离实例可指向别处。
102
+ */
103
+ agentDir?: string;
104
+ /** 新建 core 会话的默认上下文预算;单会话可在会话选项里覆盖。 */
105
+ compaction?: HarnessCompactionConfig;
106
+ }
82
107
  export interface MacosDmgConfig {
83
108
  enabled?: boolean;
84
109
  dmgDir?: string;
@@ -128,6 +153,8 @@ export interface WandConfig {
128
153
  allowedCommandPrefixes: string[];
129
154
  commandPresets: CommandPreset[];
130
155
  structuredChatPersona?: StructuredChatPersonaConfig;
156
+ /** 用户自己的显示名与头像;会话里「我」这条发言的署名。DB 权威源 `pref:userProfile`。 */
157
+ userProfile?: import("./user-profile.js").UserProfileConfig;
131
158
  /** Max total size (bytes) for shortcut interaction logs per session (default: 10 MB). Set 0 to disable logging. */
132
159
  shortcutLogMaxBytes?: number;
133
160
  /** Preferred response language for Claude (e.g. "中文", "English"). Empty string means no override. */
@@ -139,12 +166,16 @@ export interface WandConfig {
139
166
  ios?: IosIpaConfig;
140
167
  /** Render(常驻 PTY 持有者)引擎选择。默认 auto。 */
141
168
  render?: RenderConfig;
169
+ /** 结构化会话 harness 引擎选择。默认 auto(Pi 侧认证可用时用进程内 core)。 */
170
+ harness?: HarnessConfig;
142
171
  /** Structured CLI process owner for new runs; default legacy. SDK stays in Node. */
143
172
  structured?: StructuredProcessConfig;
144
173
  /** Optional offline, inference-only local decision worker. Never a chat provider. */
145
174
  localDecision?: import("./decision-types.js").LocalDecisionConfig;
146
175
  /** Default expand/collapse state for card types in structured chat view */
147
176
  cardDefaults?: CardExpandDefaults;
177
+ /** 看板任务自动归档 / 自动删除。会话保留期不跟这里变。 */
178
+ taskRetention?: import("./task-retention.js").TaskRetentionSettings;
148
179
  /** 新建会话时默认使用的 Claude 模型(别名或完整 ID)。留空则不传 --model,由 claude 自行决定。 */
149
180
  defaultModel?: string;
150
181
  /** 新建 Codex 会话时默认使用的模型。留空则不传 --model,由 codex 自行决定。 */
@@ -159,6 +190,8 @@ export interface WandConfig {
159
190
  defaultPiModel?: string;
160
191
  /** 新建 Gemini 会话时默认使用的模型。留空则不传 --model,由 gemini 自行决定。 */
161
192
  defaultGeminiModel?: string;
193
+ /** 每工具的命名模型优先链。SQLite 偏好,稳定分组 ID 可用于会话/员工/默认值。 */
194
+ modelGroups?: import("./model-groups.js").ModelGroup[];
162
195
  /** 快捷提交生成 commit message / tag 时使用的 CLI。 */
163
196
  commitCli?: SessionProvider;
164
197
  /** 快捷提交专用模型。留空则跟随所选 CLI 的默认模型。 */
@@ -204,6 +237,8 @@ export interface ClaudeModelInfo {
204
237
  id: string;
205
238
  /** UI 显示的友好标签 */
206
239
  label: string;
240
+ /** 模型选择器的显示分组。 */
241
+ group?: string;
207
242
  /** 可选备注:例如 "当前默认"、"最新" */
208
243
  note?: string;
209
244
  /** 是否为别名(opus/sonnet 等);完整 ID 为 false */
@@ -469,6 +504,9 @@ interface TextBlock {
469
504
  interface ThinkingBlock {
470
505
  type: "thinking";
471
506
  thinking: string;
507
+ /** Server observation times; absent for untimed historical reasoning. */
508
+ occurredAt?: string;
509
+ lastActivityAt?: string;
472
510
  __subagent?: SubagentMeta;
473
511
  }
474
512
  interface StructuredQuestionOption {
@@ -519,6 +557,13 @@ type ToolUseSemantic = {
519
557
  summary?: DecisionCardSummary;
520
558
  };
521
559
  export interface ToolUseBlock {
560
+ /** Bounded facts supplied by the Pi executor; partial updates are not final results. */
561
+ execution?: import("./pi-execution-types.js").PiExecutionSnapshot;
562
+ /** Host-only reference to a run created by this invocation. */
563
+ piExecutionRef?: {
564
+ runId: string;
565
+ directory: string;
566
+ };
522
567
  type: "tool_use";
523
568
  id: string;
524
569
  name: string;
@@ -566,7 +611,7 @@ export interface ConversationAuthor {
566
611
  /** 团队成员 id;用户发言为 "user"。 */
567
612
  id: string;
568
613
  name: string;
569
- /** 成员头像:上传的 data URL、"cat:<n>" 指定毛色,或空(按 id 哈希毛色)。 */
614
+ /** 成员头像:上传的 data URL、"cat:<n>" 指定毛色,或空(按身份生成一张脸)。 */
570
615
  avatar?: string;
571
616
  leader?: boolean;
572
617
  provider?: SessionProvider;
@@ -592,11 +637,43 @@ export interface TeamReportFile {
592
637
  excerpt: string;
593
638
  };
594
639
  }
640
+ export interface ConversationSessionPreview {
641
+ status: "starting" | "running" | "waiting_user" | "done" | "failed" | "stopped" | "unavailable";
642
+ text: string;
643
+ }
595
644
  export interface ConversationTurn {
645
+ /** Stable instance/target association. Legacy turns remain unchanged. */
646
+ messageId?: string;
647
+ requestId?: string;
648
+ conversationId?: string;
649
+ conversationTarget?: {
650
+ taskId: string;
651
+ runId: string;
652
+ } | null;
653
+ /** Legacy/explicit group task links remain readable; ordinary DMs link directly to a session. */
654
+ conversationLink?: {
655
+ conversationId: string;
656
+ taskId: string;
657
+ title: string;
658
+ };
659
+ sessionLink?: {
660
+ sessionId: string;
661
+ title: string;
662
+ };
663
+ /** Read-only projection, not a second transcript. */
664
+ sessionPreview?: ConversationSessionPreview;
665
+ /** Bounded read-only task output, projected at read time; never stored as a second execution history. */
666
+ taskPreview?: {
667
+ runId: string | null;
668
+ status: "starting" | "running" | "awaiting_approval" | "waiting_user" | "done" | "failed" | "stopped" | "unavailable";
669
+ text: string;
670
+ };
596
671
  role: "user" | "assistant";
597
672
  content: ContentBlock[];
598
673
  author?: ConversationAuthor;
599
674
  reportFile?: TeamReportFile;
675
+ /** Per-round Pi resource choice; retained with history, never injected into model input. */
676
+ resourceSelection?: import("./pi-session-settings.js").PiResourceSelectionNotice;
600
677
  /** 群聊里的进度提示(派工、开始、出错):渲染成居中的一行,不是气泡。 */
601
678
  notice?: boolean;
602
679
  /** ISO time when this turn was first recorded (user send / assistant start). */
@@ -623,8 +700,33 @@ export interface StructuredSessionState {
623
700
  lastError: string | null;
624
701
  inFlight: boolean;
625
702
  activeRequestId: string | null;
703
+ /**
704
+ * 本轮(一次用户输入触发的 assistant 回合)开始时刻。客户端据此算「这一轮已经跑了
705
+ * 多久」,刷新页面后依旧可读(随 structured_state 落库,不依赖本地挂载时刻)。
706
+ * 与 inFlight 同生命周期:回合开始 / daemon 领养时写入(领养保留真实起点),
707
+ * inFlight 清 false 的一切收敛路径(结束、失败、停止、中断、重启降级)置 null。
708
+ */
709
+ turnStartedAt?: string | null;
710
+ /**
711
+ * 本轮最近一次观测到活动(回合开始、输出 chunk、状态变化)的时刻,由服务端权威给出。
712
+ * 客户端用「服务端当前时刻 − lastActivityAt」显示「已 N 分钟无新消息」;静默期服务端
713
+ * 会按 TURN_QUIET_HEARTBEAT_MS 用 status 事件重发带该锚点的快照。同样只在 inFlight
714
+ * 期间非空,回合收敛时与 turnStartedAt 一起回 null,不残留「还在跑」的旧读数。
715
+ */
716
+ lastActivityAt?: string | null;
626
717
  /** Pi 已结束前台回复、但 CLI 仍在等待异步子任务收尾。 */
627
718
  phase?: "responding" | "background";
719
+ /**
720
+ * 本轮实际使用的执行引擎:`core` = Wand 进程内 harness(目前仅 pi),
721
+ * `cli` = 外部 CLI runner。决定重启后能否恢复:进程内回合不会比服务活得更久。
722
+ */
723
+ engine?: "core" | "cli";
724
+ /** 选择该引擎的原因;`auto` 降级回 CLI 时写在这里(可直接展示给用户)。 */
725
+ engineReason?: string;
726
+ /** 最近一次回合结束后的上下文占用(core 引擎能给才有)。 */
727
+ contextUsage?: import("./structured-runner.js").StructuredContextUsage;
728
+ /** 本会话累计发生的上下文压缩次数(core 引擎)。 */
729
+ compactions?: number;
628
730
  }
629
731
  export interface SessionSnapshot extends SessionCompletionState {
630
732
  id: string;
@@ -663,6 +765,15 @@ export interface SessionSnapshot extends SessionCompletionState {
663
765
  * 其余 provider 不产生该信号(undefined),客户端应回落到旧行为。
664
766
  */
665
767
  ptyBusy?: boolean;
768
+ /**
769
+ * PTY 本轮的时间锚点,语义与 structuredState.turnStartedAt / lastActivityAt 完全一致,
770
+ * 是 ptyBusy 的配套读数:turn 开始(或静默窗口被输出续期)时写入,ptyBusy 清 false
771
+ * 的一切路径(回复结束、CLI 退回提示符、进程退出、stop)一起置 null。
772
+ * 同样是运行时信号,不持久化(存储里没有对应列),服务重启后视为 null。
773
+ * 目前仅 Claude PTY(bridge)与非 Claude 前台采样能给出;其余 provider 为 undefined。
774
+ */
775
+ ptyTurnStartedAt?: string | null;
776
+ ptyLastActivityAt?: string | null;
666
777
  runner?: SessionRunner;
667
778
  command: string;
668
779
  cwd: string;
@@ -737,6 +848,39 @@ export interface SessionSnapshot extends SessionCompletionState {
737
848
  ptyOutputSeq?: number;
738
849
  /** Internal shell-wrapper marker needed to keep parsing a daemon-owned PTY after reattach. */
739
850
  ptyLaunchMarkerToken?: string | null;
851
+ /**
852
+ * core 引擎的上下文压缩状态(摘要 + 切点)。
853
+ * 只在服务端使用:会话历史不删,模型上下文按切点 + 摘要重建;不投影给客户端。
854
+ */
855
+ harnessContext?: HarnessSessionContext;
856
+ /** Native Pi harness settings, session-scoped; changes apply on the next turn. */
857
+ piSettings?: import("./pi-session-settings.js").PiSessionSettings;
858
+ /** SDK extension metadata only. Private, no separate copy of ordinary conversation history. */
859
+ harnessExtensionState?: HarnessExtensionState;
860
+ }
861
+ export interface HarnessExtensionState {
862
+ entries: Array<{
863
+ type: "custom";
864
+ customType: string;
865
+ data?: unknown;
866
+ } | {
867
+ type: "custom_message";
868
+ customType: string;
869
+ content: string;
870
+ display: boolean;
871
+ details?: unknown;
872
+ }>;
873
+ }
874
+ /** core 引擎每会话的上下文压缩状态;旧会话没有该字段时按未压缩处理。 */
875
+ export interface HarnessSessionContext {
876
+ /** 被压缩历史的结构化摘要。 */
877
+ summary: string;
878
+ /** 从第几条会话消息开始未被压缩(摘要覆盖它之前的全部内容)。 */
879
+ fromTurnIndex: number;
880
+ /** 上次压缩前的上下文 token 估算。 */
881
+ tokensBefore: number;
882
+ /** 累计压缩次数。 */
883
+ compactions: number;
740
884
  }
741
885
  /** 工作空间默认 IDE;新建 Agent 标签时缺省回落到该 provider。 */
742
886
  export type WorkspaceDefaultProvider = SessionProvider;
@@ -817,6 +961,8 @@ export interface WorkspaceTask {
817
961
  /** 该任务的工作窗口 Tabs;每个窗口内部可含一棵分屏树。 */
818
962
  layout: TaskWindowLayout | null;
819
963
  status: WorkspaceTaskStatus;
964
+ /** Canonical board archive flag; a done task is not necessarily archived. */
965
+ archived?: boolean;
820
966
  createdAt: string;
821
967
  lastOpenedAt: string | null;
822
968
  layoutRevision?: number;
@@ -0,0 +1,33 @@
1
+ import type { ConversationAuthor, WandConfig } from "./types.js";
2
+ /**
3
+ * 用户自己的资料:会话里显示的名字与头像。
4
+ *
5
+ * 只放展示信息,不进模型输入的身份、权限或凭据。真源是 DB 里的 `pref:userProfile`
6
+ * (见 `PREFERENCE_KEYS`),改完热生效,不需要重启。
7
+ */
8
+ export interface UserProfileConfig {
9
+ /** 会话里的署名。留空回落 `DEFAULT_USER_DISPLAY_NAME`,不做多套默认。 */
10
+ name?: string;
11
+ /** `""`(按名字生成)、`"cat:<n>"`(像素猫毛色)或 `data:image/*;base64,…`(上传图)。 */
12
+ avatar?: string;
13
+ }
14
+ /** 未设置资料时的唯一默认署名。历史数据、客户端兜底与文案都用这一个值。 */
15
+ export declare const DEFAULT_USER_DISPLAY_NAME = "\u6211";
16
+ /** 与硅基员工头像同一条白名单与上限,避免两套大小口径。 */
17
+ export declare const USER_AVATAR_MAX_CHARS = 60000;
18
+ export declare const USER_PROFILE_NAME_MAX = 24;
19
+ /** 读端归一:坏值退回未设置,绝不因为一份脏数据换出另一张脸。 */
20
+ export declare function normalizeUserProfile(input: unknown): UserProfileConfig | undefined;
21
+ /** 写端校验:名字超长或头像格式不对都明确报错,由路由回 400,不静默吞掉。 */
22
+ export declare function parseUserProfile(input: unknown): UserProfileConfig | undefined;
23
+ /** 会话署名:只用于展示,不改变任何执行身份或权限。 */
24
+ export declare function userDisplayName(profile: UserProfileConfig | null | undefined): string;
25
+ /**
26
+ * 会话里「我自己」这条发言的作者投影。
27
+ *
28
+ * 稳定 id 固定是 `user`;显示信息跟随当前设置解析,历史回合不批量改写
29
+ * (员工身份与展示信息的分层与 AGENTS.md 的约定一致)。
30
+ */
31
+ export declare function userAuthor(profile: UserProfileConfig | null | undefined): ConversationAuthor;
32
+ /** 从运行时配置取当前用户资料,供服务端拼预览与群内上下文时复用同一口径。 */
33
+ export declare function configUserProfile(config: Pick<WandConfig, "userProfile"> | null | undefined): UserProfileConfig | undefined;
@@ -0,0 +1,70 @@
1
+ /** 未设置资料时的唯一默认署名。历史数据、客户端兜底与文案都用这一个值。 */
2
+ export const DEFAULT_USER_DISPLAY_NAME = "我";
3
+ /** 与硅基员工头像同一条白名单与上限,避免两套大小口径。 */
4
+ export const USER_AVATAR_MAX_CHARS = 60_000;
5
+ const AVATAR_DATA_URL = /^data:image\/(png|jpeg|webp);base64,[A-Za-z0-9+/=]+$/;
6
+ const AVATAR_CAT = /^cat:(\d{1,2})$/;
7
+ export const USER_PROFILE_NAME_MAX = 24;
8
+ function clean(value) {
9
+ return typeof value === "string" ? value.trim() : "";
10
+ }
11
+ /** 只接受像素猫毛色与白名单 data URL;其他写法当没配,不猜也不静默改写。 */
12
+ function normalizeAvatar(value) {
13
+ const avatar = clean(value);
14
+ if (!avatar || AVATAR_CAT.test(avatar) || (avatar.length <= USER_AVATAR_MAX_CHARS && AVATAR_DATA_URL.test(avatar))) {
15
+ return avatar;
16
+ }
17
+ return "";
18
+ }
19
+ /** 读端归一:坏值退回未设置,绝不因为一份脏数据换出另一张脸。 */
20
+ export function normalizeUserProfile(input) {
21
+ if (!input || typeof input !== "object")
22
+ return undefined;
23
+ const raw = input;
24
+ const name = clean(raw.name).slice(0, USER_PROFILE_NAME_MAX);
25
+ const avatar = normalizeAvatar(raw.avatar);
26
+ if (!name && !avatar)
27
+ return undefined;
28
+ return { ...(name ? { name } : {}), ...(avatar ? { avatar } : {}) };
29
+ }
30
+ /** 写端校验:名字超长或头像格式不对都明确报错,由路由回 400,不静默吞掉。 */
31
+ export function parseUserProfile(input) {
32
+ if (!input || typeof input !== "object")
33
+ return undefined;
34
+ const raw = input;
35
+ const name = clean(raw.name);
36
+ if (name.length > USER_PROFILE_NAME_MAX) {
37
+ throw new Error(`名字不能超过 ${USER_PROFILE_NAME_MAX} 个字符。`);
38
+ }
39
+ if (name.includes("\n"))
40
+ throw new Error("名字不能包含换行。");
41
+ const avatarInput = raw.avatar;
42
+ const avatar = clean(avatarInput);
43
+ if (avatar) {
44
+ if (!AVATAR_CAT.test(avatar) && !AVATAR_DATA_URL.test(avatar)) {
45
+ throw new Error("头像格式无效,请重新选择或上传图片。");
46
+ }
47
+ if (avatar.length > USER_AVATAR_MAX_CHARS)
48
+ throw new Error("头像太大,请换一张小一点的图片。");
49
+ }
50
+ if (!name && !avatar)
51
+ return undefined;
52
+ return { ...(name ? { name } : {}), ...(avatar ? { avatar } : {}) };
53
+ }
54
+ /** 会话署名:只用于展示,不改变任何执行身份或权限。 */
55
+ export function userDisplayName(profile) {
56
+ return clean(profile?.name) || DEFAULT_USER_DISPLAY_NAME;
57
+ }
58
+ /**
59
+ * 会话里「我自己」这条发言的作者投影。
60
+ *
61
+ * 稳定 id 固定是 `user`;显示信息跟随当前设置解析,历史回合不批量改写
62
+ * (员工身份与展示信息的分层与 AGENTS.md 的约定一致)。
63
+ */
64
+ export function userAuthor(profile) {
65
+ return { id: "user", name: userDisplayName(profile) };
66
+ }
67
+ /** 从运行时配置取当前用户资料,供服务端拼预览与群内上下文时复用同一口径。 */
68
+ export function configUserProfile(config) {
69
+ return normalizeUserProfile(config?.userProfile);
70
+ }
@@ -0,0 +1,11 @@
1
+ import type { ConversationTurn } from "../types.js";
2
+ export interface ChatUsageMetric {
3
+ key: string;
4
+ label: string;
5
+ value: string;
6
+ title: string;
7
+ }
8
+ /** Display only reported counts. Round aggregation remains owned by the chat renderer. */
9
+ export declare function chatUsageMetrics(usage: ConversationTurn["usage"]): ChatUsageMetric[];
10
+ /** Never infer a finished duration from wall time, a live turn, or invalid/reversed anchors. */
11
+ export declare function chatReplyDuration(turn: Pick<ConversationTurn, "role" | "createdAt" | "completedAt">): string | null;
@@ -0,0 +1,47 @@
1
+ import { formatMinutes } from "./running-activity.js";
2
+ function compactNumber(value) {
3
+ if (value >= 1_000_000)
4
+ return `${(value / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
5
+ if (value >= 1_000)
6
+ return `${(value / 1_000).toFixed(1).replace(/\.0$/, "")}k`;
7
+ return String(Math.round(value));
8
+ }
9
+ /** Display only reported counts. Round aggregation remains owned by the chat renderer. */
10
+ export function chatUsageMetrics(usage) {
11
+ if (!usage)
12
+ return [];
13
+ const metrics = [];
14
+ const fields = [
15
+ ["inputTokens", "输入", false],
16
+ ["cacheReadInputTokens", "缓存命中", false],
17
+ ["cacheCreationInputTokens", "缓存写入", false],
18
+ ["outputTokens", "输出", true],
19
+ ["reasoningOutputTokens", "推理", true],
20
+ ];
21
+ for (const [key, label, estimatedOutput] of fields) {
22
+ const value = usage[key];
23
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
24
+ continue;
25
+ const approximate = usage.estimated === true && estimatedOutput ? "≈" : "";
26
+ metrics.push({ key, label, value: approximate + compactNumber(value),
27
+ title: `${label} ${approximate}${Math.round(value)} Token${approximate ? "(估算)" : ""}` });
28
+ }
29
+ const cost = usage.totalCostUsd;
30
+ if (typeof cost === "number" && Number.isFinite(cost) && cost > 0) {
31
+ metrics.push({ key: "totalCostUsd", label: "费用", value: `$${cost.toFixed(4).replace(/0+$/, "").replace(/\.$/, "")}`,
32
+ title: `费用 $${cost}` });
33
+ }
34
+ if (!metrics.length && usage.estimated === true) {
35
+ metrics.push({ key: "pending", label: "用量", value: "统计中…", title: "正在统计用量,尚无可显示的计数" });
36
+ }
37
+ return metrics;
38
+ }
39
+ /** Never infer a finished duration from wall time, a live turn, or invalid/reversed anchors. */
40
+ export function chatReplyDuration(turn) {
41
+ if (turn.role !== "assistant" || !turn.createdAt || !turn.completedAt)
42
+ return null;
43
+ const duration = Date.parse(turn.completedAt) - Date.parse(turn.createdAt);
44
+ if (!Number.isFinite(duration) || duration <= 0)
45
+ return null;
46
+ return duration < 1_000 ? "不足 1 秒" : formatMinutes(duration);
47
+ }