35 Commits
Author SHA1 Message Date
root 36e95c2631 feat(goal): TG /goal 指令进入目标模式
- 复用宿主命令运行时(commands.execute,与 Web 端 /goal 同通道),不依赖
  GUI 连接:command-goal 在宿主侧操作 session-log 支撑的 goals 域
- 语法与 Web 完全一致:/goal <目标>、/goal(查看)、clear/edit/pause/resume
- 目标状态变更(blocked/complete)实时推送 TG 通知,手机可感知目标模式进展
- 配置侧启用 command-goal(plugin_toggle,已 live 生效)
2026-09-17 12:05:26 +08:00
root 85a42f28a6 feat(approval): 审批改用 TG 原生 inline keyboard 按钮
- 审批请求以「✅ 允许一次 / ❌ 拒绝」双按钮消息呈现,点按钮即裁决并 toast 确认
- 按钮发送失败自动降级为原文本提示(回复 A/B),审批绝不丢失
- 文本作答通道保留(handleTgApproval 的 A/B 解析不变),/cancel 语义不变
2026-09-17 11:53:45 +08:00
root f49ac5d3c0 fix(approval): prototype 拦截器替代 approval/request waterfall hook
与 ask_user 同一根因:'approval/request' waterfall 分发在 per-fiber events
注册表,本插件 ctx.on 监听收不到。改为包装 ApprovalService.prototype.
request(类跨实例/fiber 共享),TG 审批答案与原请求竞速,保留 dual-path
语义;stop 时恢复原型。已验证运行态拦截器标记就位。
2026-09-17 11:47:48 +08:00
RRRosmontis 566932e0ae Revert "feat(tg): 思考块改为可折叠引用块(与工具调用区分)"
This reverts commit 6d00225cc4.
2026-09-17 11:39:18 +08:00
root 6d00225cc4 feat(tg): 思考块改为可折叠引用块(与工具调用区分)
TG 不支持按消息自定义引用块颜色(竖线颜色跟随客户端主题),按用户
选择采用官方可折叠引用块:默认收起为「💭 思考过程 ▾」,点击展开,
与工具调用的普通蓝色竖线引用块在交互上明确区分。

- rich 模式:Telegram 原生 markdown 可折叠引用语法(**> 开头 + 行尾 ||)
- HTML 模式:markdownToHtml 转换为 <blockquote expandable>
- 思考文本内的 || 全角化,避免被解析成 spoiler 实体
2026-09-17 11:36:15 +08:00
root 0b946ffaa4 Merge remote-tracking branch 'gitea/master'
# Conflicts:
#	lib/bridge.js
#	src/bridge.ts
2026-09-17 11:24:47 +08:00
root 2c50a35795 feat(ask): 键盘常态提供 ✏️ 输入自定义答案 按钮
- 每个带选项的提问键盘固定追加第三行 ✏️ 输入自定义答案
- 点击后该题进入待文本状态(textWanted),丢弃该题已点选的按钮项
- 下一条文本消息自动填充最先待文本的题;带 1:/2: 前缀的作答同样可清除待文本状态
- 已实测:✏️ → 发送 111 → custom 答案正确回流
2026-09-17 11:21:30 +08:00
rootandShirobana Agent Bot <tg-bridge> da06485d64 fix(ask): prototype 拦截器替代 waterfall hook + TG 原生按钮作答
根因:dsh 0.1.5-rc.1 把 'user-questions/request' waterfall 分发到
per-fiber events 注册表,其他 fiber 的 ctx.on 监听(含本插件)永远收不到
分发(实测 root/global/agent 作用域监听全部落空)。

修复:包装 UserQuestionService.prototype.ask(类跨实例共享),无论哪个
fiber/实例发起的 ask 都能拦截到 agent+questions;TG 答案与原 ask 竞速,
保留 dual-path 语义;stop 时恢复原型,热重载不叠加。

功能(应用户要求):
- 提问前显式发送预告文本消息
- 选项改用 TG 原生 inline keyboard 按钮(单选即点即答,多选带提交键)
- 文本作答保留并与按钮作答合并,逐题累计、齐题自动提交
- 附带修复:Telegram API 调用加 15s 客户端超时(代理挂死会卡死通知队列)

Co-authored-by: Shirobana Agent Bot <tg-bridge>
2026-09-17 11:15:16 +08:00
dsh-agent 4039dec03c feat(config): 渲染默认 rich(用户要求 2026-09-14)
Config.rendering 默认 'html'→'rich';显式 'html'/'compat' 仍可强制兼容模式。
profile cordis.patch.yml 的 rendering 同步改为 rich(patch 会覆盖插件默认值,
两处都改才生效)。旧客户端显示'不支持'时用 /rich off 按聊天切回。
2026-09-16 10:42:45 +08:00
owo e748132541 fix: make optional services non-blocking 2026-09-15 21:37:16 +08:00
dsh-agent 5fa2827ed6 feat(tg): 去掉 '> Thinking…' 思考提示流水
用户要求:不再发送思考指示(assistant/chunk 的 reasoning 分支整段移除,
连带 thinkingSessions 状态与相关清理)。tool/call、tool/result、todo/write、
turn/end 的流水保持不变。
2026-09-14 19:16:28 +08:00
dsh-agent 9f03c6c748 fix(ask_user): dsh 0.1.5 走 user-questions/request waterfall
0.1.5 移除了 userQuestions.provider,改为 waterfall 事件注册(与 approval/request 同形);
同时把 2026-09-12 只在编译产物上做过的修复回植到 src,避免源码/产物漂移。
2026-09-14 19:03:08 +08:00
RRRosmontis f3f6a3ebaf feat: admit photos and image documents into the bound session
Telegram 图片消息此前一律回复「暂只支持文本消息」。本改动:

- photo 取最大尺寸(TG 压成 jpeg),document 仅接受 image/png|jpeg|webp|gif
- caption 作为伴随文本块进入同一条消息(不解析为命令)
- 相册(同一 media_group_id)去抖 1.2s 合并为一条多图消息,避免 N 连回合
- 派发走宿主 sessions.prompt RPC(Web 端同通道):模型图像能力校验 +
  附件服务持久化 + mode queue 排队,与纯文本路径的 agent.followup 语义一致
- 错误映射:MODEL_DOES_NOT_SUPPORT_IMAGES → 提示换多模态模型;其余回退
  图片发送失败 + [code] 详情;下载失败即时提示
- 客户端新增 getFile/downloadFile(GET file 端点,复用代理感知 fetch)

tests +7(58/58):photo+caption、png document、pdf 拒绝、相册合并、
模型不支持、下载失败、未绑定。无 emoji 文案。
2026-09-05 17:51:21 +08:00
RRRosmontis 8600835436 fix: /compact 改走宿主命令通道(commands.execute),绕开隔离作用域
根因:compaction-basic 挂载在隔离作用域,插件 ctx 的 ctx.get('compaction')
解析不到(实测 undefined;agent.ctx 亦不可达)。但宿主命令运行时的按 agent
作用层已注册 /compact(command-compact,Web 端同款通道),
commands.find(agent,'compact') 可命中。

因此 /compact 改为 ctx.commands.execute(agent,'/compact',[],signal) 派发:
- 与 Web 端 /compact 完全同语义(busy/错误映射、command/run 生命周期日志)
- 结果文本按 command-compact 固定文案翻译为中文回复;无法识别时回退原文详情
- 忙会话提前提示(busySessions/phase 预检);异步执行不阻塞轮询;同会话去重
- 测试改 mock commands.find/execute(51 全绿)
2026-09-05 17:34:37 +08:00
RRRosmontis d712fb3494 feat: /status 通用状态显示 + /compact 手动压缩;HTML 兼容渲染默认化
- /status:绑定会话/会话 ID/工作区/当前模型/思考强度/上下文长度/
  首 token 平均/输出速率/输入输出 tokens —— 与 Web 底部统计条同源
  同算法(sessionProjections: sessionStats/tokenUsage/contextPressure),
  投影插件缺失时逐组优雅降级;冷会话不强制 resume
- /compact:复用 ctx.compaction.compactNow 手动压缩绑定会话历史,
  异步执行不阻塞轮询,完成/失败均中文通知,同会话去重,
  桥停止/热重载自动中止进行中的压缩
- 全部用户可见文案不使用 emoji
- rendering 默认改 html(兼容优先)+ userQuestions/approval 双通道等
  既有修改一并合入(此前已上线确认)
- tests:+6 覆盖 /status 全字段与 /compact 各分支(51 全绿)
2026-09-05 17:24:41 +08:00
RRRosmontis 20898d5d21 fix: guard markdownToHtml in the HTML fallback path
A throw from the converter used to escape the try/catch and drop the
message; degrade to plain text for that chunk instead.
2026-09-02 16:10:48 +08:00
RRRosmontis cecb655a4b fix: register /cancel as a proper command
Previously /cancel was intercepted inside the 'unknown' branch, and the
comparison used the command WITHOUT the leading slash while parseCommand
returns it WITH one ('/cancel') — the check never matched, so /cancel
always fell through to the unknown-command reply, even with a pending
ask/approval.

Now a first-class command (menu included): closes the TG-side
answer/approval channel when one is pending, otherwise reports that
nothing is awaiting a reply.
2026-09-02 16:07:53 +08:00
RRRosmontis cde6979f2d feat: resilient delivery, live progress visibility, TG-side answering, /stop /mission /new
Delivery & visibility:
- per-chunk retry with capped backoff on rich/HTML sends; rich failure now
  falls back to HTML instead of dropping the reply
- typing heartbeat: refresh sendChatAction every 4s for the whole turn
  (a single action expires in ~5s, long turns looked dead)
- turn/end error / abort / crash-recovery notices reach Telegram
  (API failures were previously invisible)
- tool/call progress summaries, tool/result failure notices, todo progress
- thinking indicator (one notice per reasoning phase, status only)
- notices prefixed '> ' render as Telegram blockquotes; plain-text fallback
- non-text messages get a hint instead of being silently dropped
- busy feedback: a followup queued mid-turn gets an explicit ack

TG-side answering (dual-path race; UI path untouched, first answer wins):
- ask_user_question questions render with letter options on TG; reply A /
  [A] / A,B (multi) / free text / '1: A' per-line for multi-question
- approval requests (approval/request waterfall) answerable from TG:
  [A] allow once / [B] reject; /cancel closes only the TG side

Commands: /stop (cancel current turn), /mission (todo snapshot + busy state),
/new (create a session in the current workspace and attach)

Persistence: chat→session bindings stored at $DSH_HOME/
telegram-channel-bindings.json (override: DSH_TELEGRAM_BINDINGS_FILE) and
restored on start — hot reloads and restarts no longer drop attachments.

Host seams without public surface yet are reached with documented structural
casts (userQuestions provider wrap, approval/request waterfall).

Tests: 41/41 pass. Two pre-existing environment failures in bridge.test.ts
(/last history mirror, assistant mirror) now pass: the former was fixed by
the rich→HTML fallback, the latter needed an event-loop flush after the
(void) listener call since resilient delivery lengthens the chain.
2026-09-01 16:06:00 +08:00
93f3bec975 feat: use Telegram native Rich Message for assistant replies
Co-authored-by: MuziIsabel <MuziIsabel@users.noreply.github.com>
2026-08-18 18:27:32 +08:00
owoandCursor 819010de1c fix: stabilize /model reasoning-effort selection (v0.3.5)
Keep pending model outside the picker map, omit undefined reasoningEffort in selectModel payload, auto-apply single-effort models, and surface RPC errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 22:41:43 +08:00
owoandCursor fb5a334edb feat: /last and bind button for previous Q/A context (v0.3.4)
After attaching a session, show a button and /last command that load the latest human/assistant turn via session.history so phone users can continue with context.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:25:07 +08:00
owoandCursor 3566df945b fix: warn when /sessions falls back to live-only list (v0.3.3)
Phone showed 2 sessions because apiProxy catalog failed (pre-0.3.2 inject bug) and live-agent fallback hid cold Web sessions. Surface that mode explicitly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:22:20 +08:00
owoandCursor 5dbe833205 fix: resolve apiProxy via ctx.get for /model (v0.3.2)
Cordis throws on ctx.apiProxy without inject; use ctx.get and declare apiProxy inject so catalog and model APIs work under dsh web.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:20:55 +08:00
owoandCursor ef0e7f34e3 feat: workspace→session picker and /model switch (v0.3.0)
Align Telegram session list with Web via apiProxy (exclude archived), resume cold sessions on bind, and allow model selection from the phone.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 18:46:56 +08:00
owoandCursor c6125807f7 fix: show session title and workspace in /sessions picker
Read cwd from session.header (not only meta) and title from sessionTitle service or session/title events; list details in the message body and richer inline buttons.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 18:10:43 +08:00
owoandCursor a83af2f33c Simplify install with one-click scripts; honor HTTP(S)_PROXY for Telegram.
Add install.ps1/install.sh, shrink README to script-first flow, register bot commands, and route API calls through undici EnvHttpProxyAgent when a proxy is set (v0.2.2).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 17:46:01 +08:00
owoandCursor c2c0db7597 feat: remote-control live DSH Web sessions from Telegram
Attach via /sessions picker instead of creating parallel agents; mirror the same session trajectory to phone and Web (Codex-style remote).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 17:11:13 +08:00
owoandCursor 0a4847d955 fix: wire pollingTimeoutSec from config to TelegramClient
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:33:08 +08:00
owoandCursor 27f02b7460 feat: wire Cordis apply lifecycle with soft-fail token check
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:30:48 +08:00
owoandCursor cdfe81cd9e fix: harden bridge session matching and error handling
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:26:06 +08:00
owoandCursor 2b800da381 feat: bridge Telegram updates to harness agents
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:22:46 +08:00
owoandCursor 4a9a41484e feat: add Telegram Bot API fetch client with token redaction
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:08:12 +08:00
owoandCursor db09bc6327 feat: add auth gate and Chinese command parsing
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:04:38 +08:00
owoandCursor a070a77e6b feat: add Telegram HTML format and split helpers
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:00:59 +08:00
owoandCursor f9d8ae7c6f chore: scaffold dsh-telegram-channel package
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 15:57:41 +08:00