@researai/deepscientist 1.5.9 → 1.5.11

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 (140) hide show
  1. package/README.md +107 -94
  2. package/assets/branding/connector-qq.png +0 -0
  3. package/assets/branding/connector-rokid.png +0 -0
  4. package/assets/branding/connector-weixin.png +0 -0
  5. package/assets/branding/projects.png +0 -0
  6. package/bin/ds.js +168 -9
  7. package/docs/assets/branding/projects.png +0 -0
  8. package/docs/en/00_QUICK_START.md +308 -70
  9. package/docs/en/01_SETTINGS_REFERENCE.md +3 -0
  10. package/docs/en/02_START_RESEARCH_GUIDE.md +112 -0
  11. package/docs/en/04_LINGZHU_CONNECTOR_GUIDE.md +62 -179
  12. package/docs/en/09_DOCTOR.md +41 -5
  13. package/docs/en/10_WEIXIN_CONNECTOR_GUIDE.md +137 -0
  14. package/docs/en/11_LICENSE_AND_RISK.md +256 -0
  15. package/docs/en/12_GUIDED_WORKFLOW_TOUR.md +427 -0
  16. package/docs/en/13_CORE_ARCHITECTURE_GUIDE.md +297 -0
  17. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +506 -0
  18. package/docs/en/99_ACKNOWLEDGEMENTS.md +4 -1
  19. package/docs/en/README.md +79 -0
  20. package/docs/images/lingzhu/rokid-agent-platform-create.png +0 -0
  21. package/docs/images/weixin/weixin-plugin-entry.png +0 -0
  22. package/docs/images/weixin/weixin-plugin-entry.svg +33 -0
  23. package/docs/images/weixin/weixin-qr-confirm.svg +30 -0
  24. package/docs/images/weixin/weixin-quest-media-flow.svg +44 -0
  25. package/docs/images/weixin/weixin-settings-bind.svg +57 -0
  26. package/docs/zh/00_QUICK_START.md +315 -74
  27. package/docs/zh/01_SETTINGS_REFERENCE.md +3 -0
  28. package/docs/zh/02_START_RESEARCH_GUIDE.md +112 -0
  29. package/docs/zh/04_LINGZHU_CONNECTOR_GUIDE.md +62 -193
  30. package/docs/zh/09_DOCTOR.md +41 -5
  31. package/docs/zh/10_WEIXIN_CONNECTOR_GUIDE.md +144 -0
  32. package/docs/zh/11_LICENSE_AND_RISK.md +256 -0
  33. package/docs/zh/12_GUIDED_WORKFLOW_TOUR.md +423 -0
  34. package/docs/zh/13_CORE_ARCHITECTURE_GUIDE.md +296 -0
  35. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +506 -0
  36. package/docs/zh/99_ACKNOWLEDGEMENTS.md +4 -1
  37. package/docs/zh/README.md +126 -0
  38. package/install.sh +0 -34
  39. package/package.json +2 -2
  40. package/pyproject.toml +1 -1
  41. package/src/deepscientist/__init__.py +1 -1
  42. package/src/deepscientist/annotations.py +343 -0
  43. package/src/deepscientist/artifact/arxiv.py +484 -37
  44. package/src/deepscientist/artifact/service.py +574 -108
  45. package/src/deepscientist/arxiv_library.py +275 -0
  46. package/src/deepscientist/bash_exec/service.py +9 -0
  47. package/src/deepscientist/bridges/builtins.py +2 -0
  48. package/src/deepscientist/bridges/connectors.py +447 -0
  49. package/src/deepscientist/channels/__init__.py +2 -0
  50. package/src/deepscientist/channels/builtins.py +3 -1
  51. package/src/deepscientist/channels/qq.py +1 -1
  52. package/src/deepscientist/channels/qq_gateway.py +1 -1
  53. package/src/deepscientist/channels/relay.py +7 -1
  54. package/src/deepscientist/channels/weixin.py +59 -0
  55. package/src/deepscientist/channels/weixin_ilink.py +317 -0
  56. package/src/deepscientist/config/models.py +22 -2
  57. package/src/deepscientist/config/service.py +431 -60
  58. package/src/deepscientist/connector/__init__.py +4 -0
  59. package/src/deepscientist/connector/connector_profiles.py +481 -0
  60. package/src/deepscientist/connector/lingzhu_support.py +668 -0
  61. package/src/deepscientist/connector/qq_profiles.py +206 -0
  62. package/src/deepscientist/connector/weixin_support.py +663 -0
  63. package/src/deepscientist/connector_profiles.py +1 -374
  64. package/src/deepscientist/connector_runtime.py +2 -0
  65. package/src/deepscientist/daemon/api/handlers.py +165 -5
  66. package/src/deepscientist/daemon/api/router.py +13 -1
  67. package/src/deepscientist/daemon/app.py +1130 -61
  68. package/src/deepscientist/doctor.py +5 -2
  69. package/src/deepscientist/gitops/diff.py +120 -29
  70. package/src/deepscientist/lingzhu_support.py +1 -182
  71. package/src/deepscientist/mcp/server.py +11 -4
  72. package/src/deepscientist/prompts/builder.py +15 -0
  73. package/src/deepscientist/qq_profiles.py +1 -196
  74. package/src/deepscientist/quest/node_traces.py +23 -0
  75. package/src/deepscientist/quest/service.py +112 -43
  76. package/src/deepscientist/quest/stage_views.py +71 -5
  77. package/src/deepscientist/runners/codex.py +55 -3
  78. package/src/deepscientist/weixin_support.py +1 -0
  79. package/src/prompts/connectors/lingzhu.md +3 -1
  80. package/src/prompts/connectors/weixin.md +230 -0
  81. package/src/prompts/system.md +2 -0
  82. package/src/tui/package.json +1 -1
  83. package/src/ui/dist/assets/{AiManusChatView-BKZ103sn.js → AiManusChatView-D0mTXG4-.js} +156 -48
  84. package/src/ui/dist/assets/{AnalysisPlugin-mTTzGAlK.js → AnalysisPlugin-Db0cTXxm.js} +1 -1
  85. package/src/ui/dist/assets/{CliPlugin-BH58n3GY.js → CliPlugin-DrV8je02.js} +164 -9
  86. package/src/ui/dist/assets/{CodeEditorPlugin-BKGRUH7e.js → CodeEditorPlugin-QXMSCH71.js} +8 -8
  87. package/src/ui/dist/assets/{CodeViewerPlugin-BMADwFWJ.js → CodeViewerPlugin-7hhtWj_E.js} +5 -5
  88. package/src/ui/dist/assets/{DocViewerPlugin-ZOnTIHLN.js → DocViewerPlugin-BWMSnRJe.js} +3 -3
  89. package/src/ui/dist/assets/{GitDiffViewerPlugin-CQ7h1Djm.js → GitDiffViewerPlugin-7J9h9Vy_.js} +20 -21
  90. package/src/ui/dist/assets/{ImageViewerPlugin-GVS5MsnC.js → ImageViewerPlugin-CHJl_0lr.js} +5 -5
  91. package/src/ui/dist/assets/{LabCopilotPanel-BZNv1JML.js → LabCopilotPanel-1qSow1es.js} +11 -11
  92. package/src/ui/dist/assets/{LabPlugin-TWcJsdQA.js → LabPlugin-eQpPPCEp.js} +2 -1
  93. package/src/ui/dist/assets/{LatexPlugin-DIjHiR2x.js → LatexPlugin-BwRfi89Z.js} +7 -7
  94. package/src/ui/dist/assets/{MarkdownViewerPlugin-D3ooGAH0.js → MarkdownViewerPlugin-836PVQWV.js} +4 -4
  95. package/src/ui/dist/assets/{MarketplacePlugin-DfVfE9hN.js → MarketplacePlugin-C2y_556i.js} +3 -3
  96. package/src/ui/dist/assets/{NotebookEditor-s8JhzuX1.js → NotebookEditor-BRzJbGsn.js} +12 -12
  97. package/src/ui/dist/assets/{NotebookEditor-DDl0_Mc0.js → NotebookEditor-DIX7Mlzu.js} +1 -1
  98. package/src/ui/dist/assets/{PdfLoader-C2Sf6SJM.js → PdfLoader-DzRaTAlq.js} +14 -7
  99. package/src/ui/dist/assets/{PdfMarkdownPlugin-CXFLoIsa.js → PdfMarkdownPlugin-DZUfIUnp.js} +73 -6
  100. package/src/ui/dist/assets/{PdfViewerPlugin-BYTmz2fK.js → PdfViewerPlugin-BwtICzue.js} +103 -34
  101. package/src/ui/dist/assets/PdfViewerPlugin-DQ11QcSf.css +3627 -0
  102. package/src/ui/dist/assets/{SearchPlugin-CjWBI1O9.js → SearchPlugin-DHeIAMsx.js} +1 -1
  103. package/src/ui/dist/assets/{TextViewerPlugin-DdOBU3-S.js → TextViewerPlugin-C3tCmFox.js} +5 -4
  104. package/src/ui/dist/assets/{VNCViewer-B8HGgLwQ.js → VNCViewer-CQsKVm3t.js} +10 -10
  105. package/src/ui/dist/assets/bot-BEA2vWuK.js +21 -0
  106. package/src/ui/dist/assets/branding/logo-rokid.png +0 -0
  107. package/src/ui/dist/assets/browser-BAcuE0Xj.js +2895 -0
  108. package/src/ui/dist/assets/{code-BWAY76JP.js → code-XfbSR8K2.js} +1 -1
  109. package/src/ui/dist/assets/{file-content-C1NwU5oQ.js → file-content-BjxNaIfy.js} +1 -1
  110. package/src/ui/dist/assets/{file-diff-panel-CywslwB9.js → file-diff-panel-D_lLVQk0.js} +1 -1
  111. package/src/ui/dist/assets/{file-socket-B4kzuOBQ.js → file-socket-D9x_5vlY.js} +1 -1
  112. package/src/ui/dist/assets/{image-D-NZM-6P.js → image-BhWT33W1.js} +1 -1
  113. package/src/ui/dist/assets/{index-DHZJ_0TI.js → index--c4iXtuy.js} +12 -12
  114. package/src/ui/dist/assets/{index-BdM1Gqfr.js → index-BDxipwrC.js} +2 -2
  115. package/src/ui/dist/assets/{index-7Chr1g9c.js → index-DZTZ8mWP.js} +14221 -9523
  116. package/src/ui/dist/assets/{index-DGIYDuTv.css → index-Dqj-Mjb4.css} +2 -13
  117. package/src/ui/dist/assets/index-PJbSbPTy.js +25 -0
  118. package/src/ui/dist/assets/{monaco-Cb2uKKe6.js → monaco-K8izTGgo.js} +1 -1
  119. package/src/ui/dist/assets/{pdf-effect-queue-DSw_D3RV.js → pdf-effect-queue-DfBors6y.js} +16 -1
  120. package/src/ui/dist/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
  121. package/src/ui/dist/assets/{popover-Bg72DGgT.js → popover-yFK1J4fL.js} +1 -1
  122. package/src/ui/dist/assets/{project-sync-Ce_0BglY.js → project-sync-PENr2zcz.js} +1 -74
  123. package/src/ui/dist/assets/select-CAbJDfYv.js +1690 -0
  124. package/src/ui/dist/assets/{sigma-DPaACDrh.js → sigma-DEuYJqTl.js} +1 -1
  125. package/src/ui/dist/assets/{index-CDxNdQdz.js → square-check-big-omoSUmcd.js} +2 -13
  126. package/src/ui/dist/assets/{trash-BvTgE5__.js → trash--F119N47.js} +1 -1
  127. package/src/ui/dist/assets/{useCliAccess-CgPeMOwP.js → useCliAccess-D31UR23I.js} +1 -1
  128. package/src/ui/dist/assets/{useFileDiffOverlay-xPhz7P5B.js → useFileDiffOverlay-BH6KcMzq.js} +1 -1
  129. package/src/ui/dist/assets/{wrap-text-C3Un3YQr.js → wrap-text-CZ613PM5.js} +1 -1
  130. package/src/ui/dist/assets/{zoom-out-BgxLa0Ri.js → zoom-out-BgDLAv3z.js} +1 -1
  131. package/src/ui/dist/index.html +2 -2
  132. package/src/ui/dist/assets/AutoFigurePlugin-BGxN8Umr.css +0 -3056
  133. package/src/ui/dist/assets/AutoFigurePlugin-C_wWw4AP.js +0 -8149
  134. package/src/ui/dist/assets/PdfViewerPlugin-BJXtIwj_.css +0 -260
  135. package/src/ui/dist/assets/Stepper-B0Dd8CxK.js +0 -158
  136. package/src/ui/dist/assets/bibtex-CKaefIN2.js +0 -189
  137. package/src/ui/dist/assets/file-utils-H2fjA46S.js +0 -109
  138. package/src/ui/dist/assets/message-square-BzjLiXir.js +0 -16
  139. package/src/ui/dist/assets/pdfjs-DU1YE8WO.js +0 -3
  140. package/src/ui/dist/assets/tooltip-C_mA6R0w.js +0 -108
@@ -0,0 +1,230 @@
1
+ # Weixin Connector Contract
2
+
3
+ - connector_contract_id: weixin
4
+ - connector_contract_scope: loaded only when Weixin is the active or bound external connector for this quest
5
+ - connector_contract_goal: use `artifact.interact(...)` as the main durable user-visible thread while respecting the Weixin iLink `context_token` reply model
6
+ - weixin_runtime_ack_rule: the Weixin bridge itself emits the immediate transport-level receipt acknowledgement before the model turn starts
7
+ - weixin_no_duplicate_ack_rule: do not waste your first model response or first `artifact.interact(...)` call on a second bare acknowledgement such as "received", "已收到", or "processing" when the bridge already sent that
8
+ - weixin_reply_style_rule: keep Weixin replies concise, milestone-first, respectful, and easy to scan on a phone
9
+ - weixin_reply_length_rule: for ordinary Weixin progress replies, normally use only 2 to 4 short sentences, or 3 short bullets at most
10
+ - weixin_summary_first_rule: start with the user-facing conclusion, then what it means, then the next action
11
+ - weixin_progress_shape_rule: make the current task, the main difficulty or latest real progress, and the next concrete measure explicit whenever possible
12
+ - weixin_eta_rule: for important long-running phases such as baseline reproduction, main experiments, analysis, or paper packaging, include a rough ETA or next check-in window when you can
13
+ - weixin_tool_call_keepalive_rule: for ordinary active work, prefer one concise Weixin progress update after roughly 10 tool calls when there is already a human-meaningful delta, and do not let work drift beyond roughly 20 tool calls or about 15 minutes without a user-visible checkpoint
14
+ - weixin_internal_detail_rule: omit worker names, retry counters, pending/running/completed counts, low-level file listings, and monitor-window narration unless the user explicitly asked for them or they change the recommended action
15
+ - weixin_translation_rule: translate internal execution and file-management work into user value instead of narrating tool or filesystem churn
16
+ - weixin_preflight_rule: before sending a Weixin-facing progress update, rewrite it if it still reads like a monitor log, execution diary, or file inventory
17
+ - weixin_operator_surface_rule: treat Weixin as an operator surface for concise coordination and milestone delivery, not as a full artifact browser
18
+ - weixin_default_text_rule: plain text is the default and safest Weixin mode
19
+ - weixin_context_token_rule: ordinary downstream replies rely on the runtime-managed `context_token`; do not invent your own reply token fields
20
+ - weixin_media_rule: Weixin supports native image, video, and file delivery through structured attachments; request them through `artifact.interact(..., attachments=[...])` instead of inventing inline tag syntax
21
+ - weixin_media_path_rule: when sending native Weixin media, prefer absolute local paths; remote URLs are allowed only when the bridge can download them safely
22
+ - weixin_media_path_priority_rule: prefer quest-local files under `artifacts/`, `experiments/`, `paper/`, or `userfiles/` over arbitrary external URLs
23
+ - weixin_media_hint_rule: when you need native Weixin media typing, set `connector_delivery={'weixin': {'media_kind': ...}}` on the attachment instead of relying only on filename suffixes
24
+ - weixin_inbound_media_rule: inbound image, video, and file messages can now enter the quest as attachments, including media-only inbound turns
25
+ - weixin_inbound_materialization_rule: inbound media is copied into quest-local `userfiles/weixin/...`; if the user sent media, read those quest-local files before continuing
26
+ - weixin_audio_output_rule: there is no native Weixin voice-message output branch; audio files fall back to ordinary file delivery, not Weixin voice messages
27
+ - weixin_partial_delivery_rule: the runtime now preflights native attachments before send and prefers a single combined Weixin message for text plus media, so do not assume text was already delivered if attachment preparation failed
28
+ - weixin_failure_rule: if `artifact.interact(...)` returns `attachment_issues` or `delivery_results` errors, treat that as a real delivery failure and adapt before assuming the user received the media
29
+ - weixin_first_followup_rule: after a new inbound Weixin message, your first substantive follow-up should either answer directly or give the first meaningful checkpoint and next action, not a second bare acknowledgement
30
+
31
+ ## Weixin Runtime Capabilities
32
+
33
+ - always supported:
34
+ - concise plain-text Weixin replies through `artifact.interact(...)`
35
+ - ordinary threaded continuity through runtime-managed `context_token`
36
+ - automatic downstream reply-to-user behavior when a valid `context_token` has been seen for that user
37
+ - inbound text messages entering the quest as user turns
38
+ - inbound image, video, and file attachments being materialized into quest-local `userfiles/weixin/...`
39
+ - supported when you attach one structured attachment with explicit delivery hints:
40
+ - native Weixin image delivery
41
+ - native Weixin video delivery
42
+ - native Weixin file delivery
43
+ - do not assume:
44
+ - inline connector-specific tags in the message body
45
+ - arbitrary historical quote reconstruction beyond the active `context_token`
46
+ - device-side `surface_actions`
47
+ - native Weixin voice-message output
48
+
49
+ ## Structured Usage Rules
50
+
51
+ - request native Weixin image delivery by attaching one structured attachment with:
52
+ - `connector_delivery={'weixin': {'media_kind': 'image'}}`
53
+ - request native Weixin video delivery by attaching one structured attachment with:
54
+ - `connector_delivery={'weixin': {'media_kind': 'video'}}`
55
+ - request native Weixin file delivery by attaching one structured attachment with:
56
+ - `connector_delivery={'weixin': {'media_kind': 'file'}}`
57
+ - when you want native Weixin media delivery, make sure the attachment exposes at least one usable file reference such as:
58
+ - `path`
59
+ - `source_path`
60
+ - `output_path`
61
+ - `artifact_path`
62
+ - `url`
63
+ - if no native media delivery is needed, omit `connector_delivery`
64
+ - do not attach many files to Weixin by default; choose only the one highest-value image, video, or file for that milestone
65
+ - if native delivery fails, fall back to a concise text update unless the missing media is essential
66
+ - if the user sent media into Weixin, prefer the quest-local copied attachment path over connector cache or remote URL
67
+
68
+ ## Examples
69
+
70
+ ### 0. Bad vs good Weixin progress update
71
+
72
+ Bad:
73
+
74
+ ```text
75
+ 我刚看完新的一轮监控窗,现在还是 12 pending / 3 running / 1 completed。retry 计数已经到第 4 次,workspace 里又多了几个 png 和 json。我接下来继续盯日志和文件变动,之后再看看是不是还要再补一轮。
76
+ ```
77
+
78
+ Why bad:
79
+
80
+ - it forces the user to infer the real conclusion from internal telemetry
81
+ - it exposes retry counters, queue numbers, and file churn that usually do not help a phone-side operator
82
+ - it reads like a monitor log, not a concise collaborator update
83
+
84
+ Good:
85
+
86
+ ```text
87
+ 主实验还在继续推进,当前不需要您额外处理。最新进展是核心结果已经基本稳定,但还有一条对照线比较慢。接下来我会补完这条对照,预计 20 分钟左右给您下一次关键更新。
88
+ ```
89
+
90
+ Why good:
91
+
92
+ - it starts with the conclusion the user actually needs
93
+ - it keeps the meaningful risk but removes low-level runtime chatter
94
+ - it tells the user what happens next and when to expect the next checkpoint
95
+
96
+ ### 1. Plain-text Weixin progress update
97
+
98
+ ```python
99
+ artifact.interact(
100
+ kind="progress",
101
+ message="主实验第一轮已经跑完,当前结果基本稳定。接下来我会继续补关键对照,确认这个提升是不是稳得住。预计下一次关键更新在 20 分钟左右。",
102
+ reply_mode="threaded",
103
+ )
104
+ ```
105
+
106
+ ### 2. Continue the current Weixin thread normally
107
+
108
+ Use the normal `artifact.interact(...)` call. The runtime keeps continuity through the latest `context_token` for that Weixin user.
109
+
110
+ ```python
111
+ artifact.interact(
112
+ kind="progress",
113
+ message="我已经看完您刚才发来的材料,也确认了它和当前 baseline 的关键差异。接下来我会把真正影响路线判断的部分整理出来,再给您一个更完整的结论。",
114
+ reply_mode="threaded",
115
+ )
116
+ ```
117
+
118
+ ### 3. Send one native Weixin image
119
+
120
+ ```python
121
+ artifact.interact(
122
+ kind="milestone",
123
+ message="主实验已经完成。我发一张汇总图给您,方便直接在手机上看。",
124
+ reply_mode="threaded",
125
+ attachments=[
126
+ {
127
+ "kind": "path",
128
+ "path": "/absolute/path/to/main_summary.png",
129
+ "label": "main-summary",
130
+ "content_type": "image/png",
131
+ "connector_delivery": {"weixin": {"media_kind": "image"}},
132
+ }
133
+ ],
134
+ )
135
+ ```
136
+
137
+ ### 4. Send one native Weixin video
138
+
139
+ ```python
140
+ artifact.interact(
141
+ kind="milestone",
142
+ message="我把这段关键演示视频一起发给您。",
143
+ reply_mode="threaded",
144
+ attachments=[
145
+ {
146
+ "kind": "path",
147
+ "path": "/absolute/path/to/demo.mp4",
148
+ "label": "demo-video",
149
+ "content_type": "video/mp4",
150
+ "connector_delivery": {"weixin": {"media_kind": "video"}},
151
+ }
152
+ ],
153
+ )
154
+ ```
155
+
156
+ ### 5. Send one native Weixin file
157
+
158
+ ```python
159
+ artifact.interact(
160
+ kind="milestone",
161
+ message="论文初稿已经整理完成,我把 PDF 一并发给您。",
162
+ reply_mode="threaded",
163
+ attachments=[
164
+ {
165
+ "kind": "path",
166
+ "path": "/absolute/path/to/paper_draft.pdf",
167
+ "label": "paper-draft",
168
+ "content_type": "application/pdf",
169
+ "connector_delivery": {"weixin": {"media_kind": "file"}},
170
+ }
171
+ ],
172
+ )
173
+ ```
174
+
175
+ ### 6. Send a native Weixin image from an artifact-style path field
176
+
177
+ If the attachment is not using `path` but does expose a real quest-local file through `source_path`, `output_path`, or `artifact_path`, the runtime can still use it for native Weixin media delivery.
178
+
179
+ ```python
180
+ artifact.interact(
181
+ kind="milestone",
182
+ message="我把这张结果图直接发给您。",
183
+ reply_mode="threaded",
184
+ attachments=[
185
+ {
186
+ "kind": "runner_result",
187
+ "source_path": "/absolute/path/to/result.png",
188
+ "content_type": "image/png",
189
+ "connector_delivery": {"weixin": {"media_kind": "image"}},
190
+ }
191
+ ],
192
+ )
193
+ ```
194
+
195
+ ### 7. If the user sent Weixin media into the quest
196
+
197
+ - inspect the current turn attachments
198
+ - prefer the copied quest-local file under `userfiles/weixin/...`
199
+ - reason over that local file instead of asking the user to resend unless the attachment is broken
200
+
201
+ ### 8. If delivery fails
202
+
203
+ - inspect `attachment_issues`
204
+ - inspect `delivery_results`
205
+ - if native media failed, send a concise text-only fallback unless the missing media is essential
206
+
207
+ Example fallback shape:
208
+
209
+ ```python
210
+ result = artifact.interact(
211
+ kind="milestone",
212
+ message="我把汇总图发给您。",
213
+ reply_mode="threaded",
214
+ attachments=[
215
+ {
216
+ "kind": "path",
217
+ "path": "/absolute/path/to/main_summary.png",
218
+ "content_type": "image/png",
219
+ "connector_delivery": {"weixin": {"media_kind": "image"}},
220
+ }
221
+ ],
222
+ )
223
+
224
+ if result.get("attachment_issues") or any(not item.get("ok") for item in (result.get("delivery_results") or [])):
225
+ artifact.interact(
226
+ kind="progress",
227
+ message="图片这次没有成功送达。我先继续用文字给您同步结论,稍后再补发可用版本。",
228
+ reply_mode="threaded",
229
+ )
230
+ ```
@@ -1045,6 +1045,8 @@ Prefer these patterns:
1045
1045
  - use `artifact.checkpoint(...)` for meaningful code-state milestones
1046
1046
  - use `artifact.render_git_graph(...)` when the quest needs a refreshed Git history view
1047
1047
  - use `artifact.arxiv(paper_id=..., full_text=False)` to read an already identified arXiv paper
1048
+ - `artifact.arxiv(mode='read', paper_id=..., full_text=False)` is the preferred explicit form; it is local-first and will auto-persist the paper into the quest arXiv library when missing
1049
+ - use `artifact.arxiv(mode='list')` when you need to inspect the arXiv papers already saved for the current quest
1048
1050
  - keep paper discovery in web search; switch to `artifact.arxiv(..., full_text=True)` only when the full paper body is actually needed
1049
1051
  - use stage-significant artifact writes for progress, milestone, report, run, and decision updates
1050
1052
  - if the runtime exposes `artifact.interact(...)`, use it for structured progress updates, decision requests, and approval responses
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepscientist-tui",
3
- "version": "1.5.9",
3
+ "version": "1.5.11",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,14 +1,14 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-BdM1Gqfr.js","assets/index-7Chr1g9c.js","assets/index-DGIYDuTv.css","assets/useCliAccess-CgPeMOwP.js","assets/VNCViewer-B8HGgLwQ.js","assets/file-content-C1NwU5oQ.js","assets/file-utils-H2fjA46S.js","assets/file-jump-queue-r5XKgJEV.js","assets/pdf-effect-queue-DSw_D3RV.js","assets/file-diff-panel-CywslwB9.js","assets/message-square-BzjLiXir.js","assets/NotebookEditor-DDl0_Mc0.js","assets/NotebookEditor-C3VQ7ylN.css","assets/tooltip-C_mA6R0w.js","assets/trash-BvTgE5__.js"])))=>i.map(i=>d[i]);
2
- import { z as createLucideIcon, r as reactExports, cl as useIsomorphicLayoutEffect, cn as frame_1, da as LayoutGroupContext, db as nodeGroup_1, j as jsxRuntimeExports, cq as useConstant, s as apiClient, bH as isQuestRuntimeSurface, dc as shouldUseQuestProject, bI as getApiBaseUrl, dd as deriveMcpIdentity, de as supportsProductApis, cX as axios, df as getCachedValue, dg as setCachedValue, dh as recordRequestEvent, di as redactSensitive, dj as sanitizeUrl, dk as refreshAccessToken, aS as useChatSessionStore, cH as getMyToken, bJ as redirectToLanding, I as create$1, ay as useLabCopilotStore, bK as useCliStore, dl as EXPLORER_REFRESH_EVENT, o as useTabsStore, cZ as buildCliFileId, dm as getCliFileName, c_ as toCliResourcePath, aK as getPluginIdFromExtension, t as BUILTIN_PLUGINS, b as cn, dn as GripVertical, f as useFileTreeStore, N as toFilesResourcePath, aJ as getPluginIdFromMimeType, T as TriangleAlert, dp as resolveMcpIdentity, dq as getToolArgsRecord, dr as getToolResultRecord, ds as getToolResultValue, dt as asString$2, du as asRecord$4, dv as asStringArray, dw as extractPathEntries, dx as BookOpenText, dy as Clock3, dz as truncateText, dA as ArrowUpRight, l as Search, dB as Database, dC as ArrowRightLeft, D as Sparkles, k as FileText, bA as GitBranch, dD as asBoolean, bB as Bot, dE as asNumber, dF as BASH_CARRIAGE_RETURN_PREFIX, aL as useQuery, bt as CircleHelp, dG as Activity, bv as Clock, dH as truncateText$1, dI as listLabPendingQuestions, dJ as listLabQuestionHistory, aa as BookOpen, ae as ExternalLink, u as useI18n, dK as normalizeWebSearchPayload, dL as WebSearchQueryPills, dM as WebSearchResults, H as EnhancedTerminal, h as dynamic, _ as __vitePreload, dN as BashToolView, a7 as Terminal, p as useToast, dO as getMimeTypeFromExtension, dP as getFileTextPreview, dQ as createFileObjectUrl, c as copyToClipboard, c6 as ChevronLeft, cw as Folder, m as ChevronUp, n as ChevronDown, X as X$1, L as LoaderCircle, aH as FileIcon, dR as formatFileSize, E as Eye, e as Copy, bc as Dialog, bd as DialogContent, c9 as ChevronRight, dS as ConfirmModal, dT as RotatingText, d as Check, aP as useReducedMotion, dU as useTokenStream, dV as McpBashExecView, dW as PngIcon, dX as Brain, dY as BRAND_LOGO_SMALL_SRC, dZ as BRAND_LOGO_SMALL_SRC_INVERTED, d_ as CircleX, aZ as motion, bf as DialogTitle, ca as DialogDescription, bS as DropdownMenu, bT as DropdownMenuTrigger, b5 as Ellipsis, bU as DropdownMenuContent, bW as DropdownMenuItem, cD as DropdownMenuSeparator, cv as GlareHover, cC as SpotlightCard, d6 as Noise, b6 as Plus, ag as Info, W as Play, d$ as getWorkspaceContentTone, aY as AnimatePresence, a6 as useOpenFile, a as useWorkspaceSurfaceStore, x as useAuthStore, e0 as useSearchParams, e1 as useAgentRegistryStore, e2 as PanelLeft, e3 as parseCliFileId, bZ as getFile, bO as getProject, cK as refreshCliServerStatus, e4 as COPILOT_FILES_ENABLED, U as listLatexBuilds, a0 as getLatexBuildLogText, Q as compileLatex, e5 as ThinkingIndicator, b7 as Select, b8 as SelectTrigger, ba as SelectContent, bb as SelectItem, e6 as assetUrl, e7 as VariableSizeList, bu as OrbitLogoStatus, aO as reactDomExports, e8 as ChatScrollProvider, be as DialogHeader, bg as DialogFooter } from './index-7Chr1g9c.js';
3
- import { u as useFileContentStore } from './file-content-C1NwU5oQ.js';
4
- import { n as normalizePath$1, j as joinPath, s as splitPath, S as Server, L as Layers } from './file-utils-H2fjA46S.js';
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-BDxipwrC.js","assets/index-DZTZ8mWP.js","assets/index-Dqj-Mjb4.css","assets/useCliAccess-D31UR23I.js","assets/VNCViewer-CQsKVm3t.js","assets/file-content-BjxNaIfy.js","assets/select-CAbJDfYv.js","assets/index-PJbSbPTy.js","assets/file-jump-queue-r5XKgJEV.js","assets/pdf-effect-queue-DfBors6y.js","assets/file-diff-panel-D_lLVQk0.js","assets/bot-BEA2vWuK.js","assets/NotebookEditor-DIX7Mlzu.js","assets/NotebookEditor-C3VQ7ylN.css","assets/trash--F119N47.js"])))=>i.map(i=>d[i]);
2
+ import { w as createLucideIcon, r as reactExports, ce as useIsomorphicLayoutEffect, cg as frame_1, d8 as LayoutGroupContext, d9 as nodeGroup_1, j as jsxRuntimeExports, cj as useConstant, t as apiClient, bE as isQuestRuntimeSurface, da as shouldUseQuestProject, bF as getApiBaseUrl, db as deriveMcpIdentity, dc as supportsProductApis, cV as axios, dd as getCachedValue, de as setCachedValue, df as recordRequestEvent, dg as redactSensitive, dh as sanitizeUrl, di as refreshAccessToken, aV as useChatSessionStore, cF as getMyToken, bG as redirectToLanding, Q as create$1, aA as useLabCopilotStore, bH as useCliStore, dj as EXPLORER_REFRESH_EVENT, n as useTabsStore, cX as buildCliFileId, dk as getCliFileName, cY as toCliResourcePath, aM as getPluginIdFromExtension, v as BUILTIN_PLUGINS, b as cn, dl as GripVertical, f as useFileTreeStore, W as toFilesResourcePath, aL as getPluginIdFromMimeType, T as TriangleAlert, dm as resolveMcpIdentity, dn as getToolArgsRecord, dp as getToolResultRecord, dq as getToolResultValue, dr as asString$2, ds as asRecord$4, dt as asStringArray, du as extractPathEntries, dv as BookOpenText, dw as Clock3, dx as truncateText, dy as ArrowUpRight, l as Search, dz as Database, dA as ArrowRightLeft, N as Sparkles, k as FileText, by as GitBranch, dB as asBoolean, dC as asNumber, dD as BASH_CARRIAGE_RETURN_PREFIX, aN as useQuery, br as CircleHelp, dE as Activity, bt as Clock, dF as truncateText$1, dG as listLabPendingQuestions, dH as listLabQuestionHistory, af as BookOpen, dI as ExternalLink, u as useI18n, dJ as GraduationCap, dK as normalizeWebSearchPayload, dL as WebSearchQueryPills, dM as WebSearchResults, P as EnhancedTerminal, h as dynamic, _ as __vitePreload, dN as BashToolView, ac as Terminal, o as useToast, dO as getMimeTypeFromExtension, dP as getFileTextPreview, c2 as createFileObjectUrl, c as copyToClipboard, cp as ChevronLeft, cs as Folder, m as ChevronDown, X as X$1, L as LoaderCircle, aJ as FileIcon, dQ as formatFileSize, E as Eye, e as Copy, ba as Dialog, bb as DialogContent, co as ChevronRight, dR as ConfirmModal, dS as RotatingText, d as Check, aS as useReducedMotion, dT as useTokenStream, dU as McpBashExecView, dV as PngIcon, dW as Brain, dX as BRAND_LOGO_SMALL_SRC, dY as BRAND_LOGO_SMALL_SRC_INVERTED, dZ as CircleX, b0 as motion, bd as DialogTitle, ct as DialogDescription, bT as DropdownMenu, bU as DropdownMenuTrigger, b8 as Ellipsis, bV as DropdownMenuContent, bX as DropdownMenuItem, cB as DropdownMenuSeparator, cq as GlareHover, cA as SpotlightCard, d4 as Noise, b9 as Plus, ai as Info, a2 as Play, d_ as getWorkspaceContentTone, a$ as AnimatePresence, ab as useOpenFile, a as useWorkspaceSurfaceStore, z as useAuthStore, d$ as useSearchParams, e0 as useAgentRegistryStore, e1 as PanelLeft, e2 as parseCliFileId, c0 as getFile, bL as getProject, cI as refreshCliServerStatus, e3 as COPILOT_FILES_ENABLED, a0 as listLatexBuilds, a6 as getLatexBuildLogText, $ as compileLatex, e4 as ThinkingIndicator, e5 as assetUrl, e6 as VariableSizeList, bs as OrbitLogoStatus, aR as reactDomExports, e7 as ChatScrollProvider, bc as DialogHeader, be as DialogFooter } from './index-DZTZ8mWP.js';
3
+ import { u as useFileContentStore } from './file-content-BjxNaIfy.js';
4
+ import { n as normalizePath$1, j as joinPath, s as splitPath, e as Server, L as Layers, S as Select, a as SelectTrigger, c as SelectContent, d as SelectItem } from './select-CAbJDfYv.js';
5
5
  import { q as queueFileJumpEffect } from './file-jump-queue-r5XKgJEV.js';
6
- import { q as queuePdfEffect } from './pdf-effect-queue-DSw_D3RV.js';
7
- import { F as FileDiffPanel } from './file-diff-panel-CywslwB9.js';
8
- import { M as MessageSquare } from './message-square-BzjLiXir.js';
9
- import { v as ve$2, d as defaultExtensions, g as getEditorMarkdown, s as setEditorMarkdown, U as U$2, I as I$1 } from './NotebookEditor-DDl0_Mc0.js';
10
- import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from './tooltip-C_mA6R0w.js';
11
- import { T as Trash, A as ArrowDown } from './trash-BvTgE5__.js';
6
+ import { q as queuePdfEffect, M as MessageSquare } from './pdf-effect-queue-DfBors6y.js';
7
+ import { F as FileDiffPanel } from './file-diff-panel-D_lLVQk0.js';
8
+ import { B as Bot } from './bot-BEA2vWuK.js';
9
+ import { C as ChevronUp } from './index-PJbSbPTy.js';
10
+ import { v as ve$2, d as defaultExtensions, g as getEditorMarkdown, s as setEditorMarkdown, U as U$2, I as I$1 } from './NotebookEditor-DIX7Mlzu.js';
11
+ import { T as Trash, A as ArrowDown } from './trash--F119N47.js';
12
12
 
13
13
  /**
14
14
  * @license lucide-react v0.511.0 - ISC
@@ -18,12 +18,12 @@ import { T as Trash, A as ArrowDown } from './trash-BvTgE5__.js';
18
18
  */
19
19
 
20
20
 
21
- const __iconNode$k = [
21
+ const __iconNode$j = [
22
22
  ["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
23
23
  ["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
24
24
  ["path", { d: "M10 12h4", key: "a56b0p" }]
25
25
  ];
26
- const Archive = createLucideIcon("archive", __iconNode$k);
26
+ const Archive = createLucideIcon("archive", __iconNode$j);
27
27
 
28
28
  /**
29
29
  * @license lucide-react v0.511.0 - ISC
@@ -33,11 +33,11 @@ const Archive = createLucideIcon("archive", __iconNode$k);
33
33
  */
34
34
 
35
35
 
36
- const __iconNode$j = [
36
+ const __iconNode$i = [
37
37
  ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
38
38
  ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
39
39
  ];
40
- const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$j);
40
+ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$i);
41
41
 
42
42
  /**
43
43
  * @license lucide-react v0.511.0 - ISC
@@ -47,12 +47,12 @@ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$j);
47
47
  */
48
48
 
49
49
 
50
- const __iconNode$i = [
50
+ const __iconNode$h = [
51
51
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
52
52
  ["line", { x1: "10", x2: "10", y1: "15", y2: "9", key: "c1nkhi" }],
53
53
  ["line", { x1: "14", x2: "14", y1: "15", y2: "9", key: "h65svq" }]
54
54
  ];
55
- const CirclePause = createLucideIcon("circle-pause", __iconNode$i);
55
+ const CirclePause = createLucideIcon("circle-pause", __iconNode$h);
56
56
 
57
57
  /**
58
58
  * @license lucide-react v0.511.0 - ISC
@@ -62,13 +62,13 @@ const CirclePause = createLucideIcon("circle-pause", __iconNode$i);
62
62
  */
63
63
 
64
64
 
65
- const __iconNode$h = [
65
+ const __iconNode$g = [
66
66
  [
67
67
  "path",
68
68
  { d: "M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3", key: "11bfej" }
69
69
  ]
70
70
  ];
71
- const Command = createLucideIcon("command", __iconNode$h);
71
+ const Command = createLucideIcon("command", __iconNode$g);
72
72
 
73
73
  /**
74
74
  * @license lucide-react v0.511.0 - ISC
@@ -78,7 +78,7 @@ const Command = createLucideIcon("command", __iconNode$h);
78
78
  */
79
79
 
80
80
 
81
- const __iconNode$g = [
81
+ const __iconNode$f = [
82
82
  ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
83
83
  [
84
84
  "path",
@@ -87,7 +87,7 @@ const __iconNode$g = [
87
87
  ["path", { d: "m9 18-1.5-1.5", key: "1j6qii" }],
88
88
  ["circle", { cx: "5", cy: "14", r: "3", key: "ufru5t" }]
89
89
  ];
90
- const FileSearch = createLucideIcon("file-search", __iconNode$g);
90
+ const FileSearch = createLucideIcon("file-search", __iconNode$f);
91
91
 
92
92
  /**
93
93
  * @license lucide-react v0.511.0 - ISC
@@ -97,12 +97,12 @@ const FileSearch = createLucideIcon("file-search", __iconNode$g);
97
97
  */
98
98
 
99
99
 
100
- const __iconNode$f = [
100
+ const __iconNode$e = [
101
101
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }],
102
102
  ["line", { x1: "3", x2: "9", y1: "12", y2: "12", key: "1dyftd" }],
103
103
  ["line", { x1: "15", x2: "21", y1: "12", y2: "12", key: "oup4p8" }]
104
104
  ];
105
- const GitCommitHorizontal = createLucideIcon("git-commit-horizontal", __iconNode$f);
105
+ const GitCommitHorizontal = createLucideIcon("git-commit-horizontal", __iconNode$e);
106
106
 
107
107
  /**
108
108
  * @license lucide-react v0.511.0 - ISC
@@ -112,33 +112,12 @@ const GitCommitHorizontal = createLucideIcon("git-commit-horizontal", __iconNode
112
112
  */
113
113
 
114
114
 
115
- const __iconNode$e = [
115
+ const __iconNode$d = [
116
116
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
117
117
  ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
118
118
  ["path", { d: "M2 12h20", key: "9i4pu4" }]
119
119
  ];
120
- const Globe = createLucideIcon("globe", __iconNode$e);
121
-
122
- /**
123
- * @license lucide-react v0.511.0 - ISC
124
- *
125
- * This source code is licensed under the ISC license.
126
- * See the LICENSE file in the root directory of this source tree.
127
- */
128
-
129
-
130
- const __iconNode$d = [
131
- [
132
- "path",
133
- {
134
- d: "M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z",
135
- key: "j76jl0"
136
- }
137
- ],
138
- ["path", { d: "M22 10v6", key: "1lu8f3" }],
139
- ["path", { d: "M6 12.5V16a6 3 0 0 0 12 0v-3.5", key: "1r8lef" }]
140
- ];
141
- const GraduationCap = createLucideIcon("graduation-cap", __iconNode$d);
120
+ const Globe = createLucideIcon("globe", __iconNode$d);
142
121
 
143
122
  /**
144
123
  * @license lucide-react v0.511.0 - ISC
@@ -9557,6 +9536,29 @@ function renderAttachBaseline(resultRecord) {
9557
9536
  ] }) });
9558
9537
  }
9559
9538
  function renderArxiv(resultRecord, args) {
9539
+ const mode = asString$2(resultRecord?.mode) || asString$2(args.mode) || "read";
9540
+ if (mode === "list") {
9541
+ const items = Array.isArray(resultRecord?.items) ? resultRecord.items.map((entry) => asRecord$4(entry)).filter((entry) => Boolean(entry)) : [];
9542
+ const count = typeof resultRecord?.count === "number" ? resultRecord.count : items.length;
9543
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DsToolSection, { title: "Saved arXiv papers", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 text-[12px] leading-6 text-[var(--text-secondary)]", children: [
9544
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
9545
+ "Count: ",
9546
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-[var(--text-primary)]", children: count })
9547
+ ] }),
9548
+ items.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "No saved arXiv papers yet." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: items.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
9549
+ "div",
9550
+ {
9551
+ className: "rounded-[12px] border border-[var(--border-light)] bg-[rgba(255,255,255,0.76)] px-3 py-3",
9552
+ children: [
9553
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[12px] font-medium text-[var(--text-primary)]", children: asString$2(item.title) || asString$2(item.arxiv_id) || `Paper ${index + 1}` }),
9554
+ asString$2(item.arxiv_id) ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 text-[11px] font-mono text-[var(--text-tertiary)]", children: asString$2(item.arxiv_id) }) : null,
9555
+ asString$2(item.abstract) ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 text-[12px] leading-6 text-[var(--text-secondary)]", children: truncateText(asString$2(item.abstract), 320) }) : null
9556
+ ]
9557
+ },
9558
+ `${asString$2(item.arxiv_id) || index}`
9559
+ )) })
9560
+ ] }) }) });
9561
+ }
9560
9562
  const paperId = asString$2(resultRecord?.paper_id) || asString$2(args.paper_id);
9561
9563
  const title = asString$2(resultRecord?.title) || paperId || "arXiv paper";
9562
9564
  const source = asString$2(resultRecord?.source);
@@ -9729,6 +9731,7 @@ function McpArtifactToolView({ toolContent }) {
9729
9731
  const resultValue = getToolResultValue(toolContent);
9730
9732
  const error = asString$2(toolContent.content?.error) || asString$2(resultRecord?.error);
9731
9733
  const active = toolContent.status === "calling";
9734
+ const arxivMode = asString$2(resultRecord?.mode) || asString$2(args.mode) || "read";
9732
9735
  const titleMap = {
9733
9736
  record: active ? "DeepScientist is recording artifact..." : "DeepScientist recorded artifact.",
9734
9737
  checkpoint: active ? "DeepScientist is creating checkpoint..." : "DeepScientist created checkpoint.",
@@ -9761,7 +9764,7 @@ function McpArtifactToolView({ toolContent }) {
9761
9764
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
9762
9765
  DsToolFrame,
9763
9766
  {
9764
- title: titleMap[toolLabel] || (active ? "DeepScientist is updating artifact..." : "DeepScientist updated artifact."),
9767
+ title: toolLabel === "arxiv" && arxivMode === "list" ? active ? "DeepScientist is listing saved arXiv papers..." : "DeepScientist listed the saved arXiv papers." : titleMap[toolLabel] || (active ? "DeepScientist is updating artifact..." : "DeepScientist updated artifact."),
9765
9768
  subtitle: recordSummary || subtitleMap[toolLabel] || "Artifact tools persist branch, report, baseline, and interaction state.",
9766
9769
  accent,
9767
9770
  meta: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
@@ -11535,7 +11538,7 @@ function SearchToolView({ toolContent, panelMode }) {
11535
11538
  }
11536
11539
 
11537
11540
  const CliToolTerminal = dynamic(
11538
- () => __vitePreload(() => import('./index-BdM1Gqfr.js'),true?__vite__mapDeps([0,1,2,3]):void 0).then((mod) => mod.CliToolTerminal),
11541
+ () => __vitePreload(() => import('./index-BDxipwrC.js'),true?__vite__mapDeps([0,1,2,3]):void 0).then((mod) => mod.CliToolTerminal),
11539
11542
  {
11540
11543
  loading: () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full items-center justify-center text-xs text-[var(--text-tertiary)]", children: "Loading terminal..." })
11541
11544
  }
@@ -16875,6 +16878,111 @@ const ChatMessage = reactExports.memo(ChatMessageBase, (prev, next) => {
16875
16878
  });
16876
16879
  ChatMessage.displayName = "ChatMessage";
16877
16880
 
16881
+ const TooltipContext = reactExports.createContext(null);
16882
+ const TooltipProvider = ({
16883
+ children,
16884
+ // These props are accepted for compatibility but not used in this simple implementation
16885
+ delayDuration: _delayDuration,
16886
+ skipDelayDuration: _skipDelayDuration
16887
+ }) => {
16888
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children });
16889
+ };
16890
+ const Tooltip = ({
16891
+ children,
16892
+ delayDuration = 200
16893
+ }) => {
16894
+ const [open, setOpen] = reactExports.useState(false);
16895
+ const timeoutRef = reactExports.useRef();
16896
+ const handleOpen = reactExports.useCallback(() => {
16897
+ timeoutRef.current = setTimeout(() => {
16898
+ setOpen(true);
16899
+ }, delayDuration);
16900
+ }, [delayDuration]);
16901
+ const handleClose = reactExports.useCallback(() => {
16902
+ if (timeoutRef.current) {
16903
+ clearTimeout(timeoutRef.current);
16904
+ }
16905
+ setOpen(false);
16906
+ }, []);
16907
+ reactExports.useEffect(() => {
16908
+ return () => {
16909
+ if (timeoutRef.current) {
16910
+ clearTimeout(timeoutRef.current);
16911
+ }
16912
+ };
16913
+ }, []);
16914
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
16915
+ TooltipContext.Provider,
16916
+ {
16917
+ value: {
16918
+ open,
16919
+ setOpen: (value) => {
16920
+ if (value) {
16921
+ handleOpen();
16922
+ } else {
16923
+ handleClose();
16924
+ }
16925
+ }
16926
+ },
16927
+ children
16928
+ }
16929
+ );
16930
+ };
16931
+ const TooltipTrigger = reactExports.forwardRef(
16932
+ ({ className, children, asChild, ...props }, ref) => {
16933
+ const context = reactExports.useContext(TooltipContext);
16934
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
16935
+ "div",
16936
+ {
16937
+ ref,
16938
+ className: cn("inline-flex", className),
16939
+ onMouseEnter: () => context?.setOpen(true),
16940
+ onMouseLeave: () => context?.setOpen(false),
16941
+ onFocus: () => context?.setOpen(true),
16942
+ onBlur: () => context?.setOpen(false),
16943
+ ...props,
16944
+ children
16945
+ }
16946
+ );
16947
+ }
16948
+ );
16949
+ TooltipTrigger.displayName = "TooltipTrigger";
16950
+ const TooltipContent = reactExports.forwardRef(
16951
+ ({ className, side = "top", sideOffset = 4, children, ...props }, ref) => {
16952
+ const context = reactExports.useContext(TooltipContext);
16953
+ if (!context?.open) {
16954
+ return null;
16955
+ }
16956
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
16957
+ "div",
16958
+ {
16959
+ ref,
16960
+ className: cn(
16961
+ "absolute z-[10002] overflow-hidden rounded-soft-sm px-3 py-1.5",
16962
+ "bg-soft-bg-elevated text-soft-text-primary text-sm",
16963
+ "shadow-soft-sm border border-soft-border",
16964
+ "animate-in fade-in-0 zoom-in-95",
16965
+ // Position based on side
16966
+ side === "top" && "bottom-full mb-2",
16967
+ side === "bottom" && "top-full mt-2",
16968
+ side === "left" && "right-full mr-2",
16969
+ side === "right" && "left-full ml-2",
16970
+ className
16971
+ ),
16972
+ style: {
16973
+ marginTop: side === "bottom" ? sideOffset : void 0,
16974
+ marginBottom: side === "top" ? sideOffset : void 0,
16975
+ marginLeft: side === "right" ? sideOffset : void 0,
16976
+ marginRight: side === "left" ? sideOffset : void 0
16977
+ },
16978
+ ...props,
16979
+ children
16980
+ }
16981
+ );
16982
+ }
16983
+ );
16984
+ TooltipContent.displayName = "TooltipContent";
16985
+
16878
16986
  function PlanPanel({
16879
16987
  plan,
16880
16988
  sessionId,
@@ -18393,7 +18501,7 @@ function pickGreetingTemplate() {
18393
18501
  return GREETING_TEMPLATES[index];
18394
18502
  }
18395
18503
  const VNCViewer = dynamic(
18396
- () => __vitePreload(() => import('./VNCViewer-B8HGgLwQ.js'),true?__vite__mapDeps([4,1,2,5,6,7,8,9,10,11,12,13,14]):void 0).then((mod) => mod.VNCViewer),
18504
+ () => __vitePreload(() => import('./VNCViewer-CQsKVm3t.js'),true?__vite__mapDeps([4,1,2,5,6,7,8,9,10,11,12,13,14]):void 0).then((mod) => mod.VNCViewer),
18397
18505
  {
18398
18506
  loading: () => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full w-full items-center justify-center text-sm text-[var(--text-tertiary)]", children: "Loading sandbox view..." })
18399
18507
  }
@@ -1,4 +1,4 @@
1
- import { r as reactExports, c as copyToClipboard, j as jsxRuntimeExports, k as FileText, l as Search, A as ChartColumn, D as Sparkles, d as Check, e as Copy, b as cn } from './index-7Chr1g9c.js';
1
+ import { r as reactExports, c as copyToClipboard, j as jsxRuntimeExports, k as FileText, l as Search, K as ChartColumn, N as Sparkles, d as Check, e as Copy, b as cn } from './index-DZTZ8mWP.js';
2
2
 
3
3
  const actions = [
4
4
  {