lark-remote 0.5.12 → 0.5.14
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.
- package/CHANGELOG.md +28 -2
- package/README.en.md +1 -14
- package/README.md +1 -14
- package/dist/agent-kinds.d.ts +27 -0
- package/dist/agent-kinds.js +38 -0
- package/dist/bridge/approval-actions.d.ts +95 -0
- package/dist/bridge/approval-actions.js +104 -0
- package/dist/bridge/approval-coordinator.d.ts +1 -0
- package/dist/bridge/approval-coordinator.js +12 -24
- package/dist/bridge/final-usage.d.ts +8 -17
- package/dist/bridge/final-usage.js +1 -18
- package/dist/bridge/index.d.ts +52 -38
- package/dist/bridge/index.js +255 -433
- package/dist/bridge/queue-manager.d.ts +25 -1
- package/dist/bridge/queue-manager.js +179 -160
- package/dist/bridge/run-approval.d.ts +16 -0
- package/dist/bridge/run-approval.js +41 -0
- package/dist/bridge/run-usage.d.ts +32 -0
- package/dist/bridge/run-usage.js +87 -0
- package/dist/bridge/session-writeback.d.ts +19 -0
- package/dist/bridge/session-writeback.js +36 -0
- package/dist/card/approval-render.d.ts +3 -2
- package/dist/card/approval-render.js +3 -2
- package/dist/card/bash-card-session.js +2 -5
- package/dist/{router → card}/card-helpers.d.ts +7 -7
- package/dist/{router → card}/card-helpers.js +8 -8
- package/dist/card/card-session.d.ts +6 -2
- package/dist/card/card-session.js +28 -28
- package/dist/card/card-shared.d.ts +4 -4
- package/dist/card/card-shared.js +4 -10
- package/dist/card/collapsible.js +3 -2
- package/dist/card/final-answer-overflow.d.ts +1 -0
- package/dist/card/final-answer-overflow.js +46 -0
- package/dist/card/run-card-session.d.ts +2 -2
- package/dist/card/run-card-session.js +4 -9
- package/dist/card/run-renderer.d.ts +14 -0
- package/dist/card/run-renderer.js +126 -23
- package/dist/card/run-state.d.ts +40 -51
- package/dist/card/run-state.js +96 -34
- package/dist/card/usage-format.d.ts +30 -0
- package/dist/{router → card}/usage-format.js +3 -10
- package/dist/clone.js +6 -1
- package/dist/config/codex-config.js +4 -4
- package/dist/config/index.d.ts +18 -20
- package/dist/config/index.js +6 -20
- package/dist/config/pi-config.js +6 -4
- package/dist/connector/index.d.ts +2 -4
- package/dist/connector/index.js +7 -11
- package/dist/{runner/dsh → dsh}/auth.js +3 -4
- package/dist/dsh/home.d.ts +2 -0
- package/dist/dsh/home.js +8 -0
- package/dist/dsh/protocol.d.ts +9 -0
- package/dist/dsh/protocol.js +61 -0
- package/dist/inbound/turn-assembler.js +6 -0
- package/dist/index.js +25 -414
- package/dist/message-handlers.d.ts +26 -0
- package/dist/message-handlers.js +275 -0
- package/dist/model-catalog.d.ts +14 -0
- package/dist/model-catalog.js +25 -0
- package/dist/platform/path.d.ts +9 -0
- package/dist/platform/path.js +18 -0
- package/dist/router/active-run-usage.d.ts +4 -0
- package/dist/router/active-run-usage.js +12 -0
- package/dist/router/card-action-payload.d.ts +1 -1
- package/dist/router/card-action-payload.js +1 -1
- package/dist/router/commands/ls.d.ts +9 -0
- package/dist/router/commands/ls.js +243 -0
- package/dist/router/commands/order.d.ts +12 -0
- package/dist/router/commands/order.js +178 -0
- package/dist/router/commands/resume.d.ts +48 -0
- package/dist/router/commands/resume.js +246 -0
- package/dist/router/commands/ws.d.ts +15 -0
- package/dist/router/commands/ws.js +189 -0
- package/dist/router/config/claude.js +5 -4
- package/dist/router/config/codex.js +2 -6
- package/dist/router/config/common/render.d.ts +5 -1
- package/dist/router/config/common/render.js +6 -13
- package/dist/router/config/common/turn-idle.d.ts +2 -0
- package/dist/router/config/common/turn-idle.js +9 -0
- package/dist/router/config/dsh.d.ts +1 -1
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/kimi.js +2 -6
- package/dist/router/config/opencode.js +2 -6
- package/dist/router/config/types.d.ts +11 -3
- package/dist/router/index.d.ts +55 -33
- package/dist/router/index.js +233 -1376
- package/dist/router/paging.d.ts +13 -0
- package/dist/router/paging.js +20 -0
- package/dist/router/path-validation.d.ts +27 -0
- package/dist/router/path-validation.js +45 -0
- package/dist/runner/agent-choices-common.d.ts +1 -1
- package/dist/runner/claude/runner.d.ts +23 -6
- package/dist/runner/claude/runner.js +6 -20
- package/dist/runner/claude/session.js +1 -1
- package/dist/runner/codex/app-server/runner.d.ts +3 -1
- package/dist/runner/codex/app-server/translator.d.ts +13 -0
- package/dist/runner/codex/app-server/translator.js +114 -151
- package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
- package/dist/runner/common/acp/base-translator.d.ts +3 -8
- package/dist/runner/common/acp/base-translator.js +16 -7
- package/dist/runner/common/connection-based-runner.d.ts +3 -1
- package/dist/runner/common/jsonrpc/transport.js +17 -7
- package/dist/runner/common/spawning-runner.js +10 -4
- package/dist/runner/common/turn-started.d.ts +2 -0
- package/dist/runner/common/turn-started.js +10 -0
- package/dist/runner/dsh/client.d.ts +2 -10
- package/dist/runner/dsh/client.js +13 -220
- package/dist/runner/dsh/daemon.d.ts +0 -2
- package/dist/runner/dsh/daemon.js +9 -7
- package/dist/runner/dsh/errors.d.ts +5 -0
- package/dist/runner/dsh/errors.js +11 -0
- package/dist/runner/dsh/exit-cancel.d.ts +0 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +0 -2
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.d.ts +26 -0
- package/dist/runner/dsh/mux-stream.js +176 -0
- package/dist/runner/dsh/runner.d.ts +21 -1
- package/dist/runner/dsh/runner.js +76 -73
- package/dist/runner/dsh/translator.d.ts +1 -11
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +22 -1
- package/dist/runner/kimi/acp/runner.js +83 -60
- package/dist/runner/kimi/acp/translator.js +1 -12
- package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
- package/dist/runner/opencode/acp/error-monitor.js +31 -2
- package/dist/runner/opencode/acp/runner.d.ts +3 -1
- package/dist/runner/opencode/acp/translator.js +1 -12
- package/dist/runner/pi/rpc/runner.d.ts +2 -1
- package/dist/runner/pi/rpc/translator.d.ts +2 -8
- package/dist/runner/pi/rpc/translator.js +2 -7
- package/dist/runner/probe.d.ts +1 -1
- package/dist/runner/probe.js +10 -9
- package/dist/runner/registry.d.ts +2 -20
- package/dist/runner/registry.js +0 -32
- package/dist/runner/types.d.ts +37 -155
- package/dist/runner/types.js +4 -24
- package/dist/session/claude/session-index.d.ts +11 -4
- package/dist/session/claude/session-index.js +34 -10
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/sessions.d.ts +2 -2
- package/dist/session/claude/sessions.js +41 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -1
- package/dist/session/codex/rollout-reader.js +14 -13
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +4 -2
- package/dist/session/common/jsonl.js +7 -1
- package/dist/session/common/pagination.d.ts +2 -0
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +6 -0
- package/dist/session/common/recency.js +7 -5
- package/dist/session/common/two-pass.d.ts +1 -1
- package/dist/session/common/usage-accumulator.d.ts +1 -1
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +37 -12
- package/dist/session/kimi/sessions.d.ts +1 -1
- package/dist/session/kimi/sessions.js +37 -10
- package/dist/session/opencode/sessions.d.ts +1 -1
- package/dist/session/opencode/sessions.js +53 -48
- package/dist/session/pi/sessions.d.ts +1 -1
- package/dist/session/pi/sessions.js +23 -9
- package/dist/session/registry.d.ts +2 -1
- package/dist/session/session-store.d.ts +1 -2
- package/dist/session/session-store.js +14 -7
- package/dist/session-contract.d.ts +178 -0
- package/dist/session-contract.js +9 -0
- package/package.json +1 -1
- package/dist/router/usage-format.d.ts +0 -60
- /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.14] - 2026-10-28
|
|
9
|
+
|
|
10
|
+
### 修复
|
|
11
|
+
|
|
12
|
+
- **卡片停止刷新不再查不到原因**:飞书拒收一次卡片更新后,这张卡会被永久剔除、此后不再原地刷新(用户侧表现为「卡片卡在最后一步」)。这条失败此前只记 debug 级日志,在默认 `info` 级别下看不到;现在按 warn 记录,带上下原因
|
|
13
|
+
- **自重启的前实例信息读取失败不再被误归因**:`/restart` 读旧实例的 startup-contact 时,只有「从未写过」(首次运行)算正常;解析失败或权限问题现在会留痕,否则排查时被当成「旧 owner 没有条目」而走错方向
|
|
14
|
+
|
|
15
|
+
### 维护
|
|
16
|
+
|
|
17
|
+
- **agent 名单、会话读取契约、用量字段与模型别名各自单源**:新增 `src/agent-kinds.ts`、`src/session-contract.ts`、`src/model-catalog.ts`。用量字段清单原先手抄了 5 份、加一个字段要同时改三处,现在统一从一处派生。无行为变化
|
|
18
|
+
- **模块归位,依赖方向只剩单向**:`card-helpers` / `usage-format` 从 router 移入 card,DSH 的协议、类型与 home 解析从 runner 提到 `src/dsh/`;地基层(配置、会话、卡片)不再反向 import 上层,并加了依赖方向守卫用例守住这条边。无行为变化
|
|
19
|
+
- **`/config` 卡片删掉从未被任何字段使用的开关控件形态**,同时加「控件 ↔ 处理端」一致性守卫:渲染得出的按钮必定有对应处理分支,也不会漏进即时动作名单——「按钮点下去没人处理」这类状态从此被测出来
|
|
20
|
+
- **审批动作与排队管理从 bridge 拆成独立模块**,入口装配线(进程级异常处理器、ConfigDir 派生路径、agent 工厂闭包)收拢为单文件静态守卫;顺带清掉死代码、重复实现与静默吞错的分支。无行为变化
|
|
21
|
+
- **单元测试纳入类型检查**:`tsconfig.test-infra.json` 不再排除 `src/**/*.test.ts`,`bun run typecheck` 现在会检查测试文件的类型,测试夹具里的陈旧键当场可见
|
|
22
|
+
|
|
23
|
+
## [0.5.13] - 2026-10-26
|
|
24
|
+
|
|
25
|
+
### 修复
|
|
26
|
+
|
|
27
|
+
- **长回合的最终回答不再被进度内容挤掉**:工具输出、思考、通知此前与最终回答共用同一份卡片预算,回合越长越容易把回答截断甚至整段挤没。现在最终回答独立保存,不受进度文本窗口和工具块上限淘汰;终态渲染先排状态、审批与操作控件,再放完整回答,剩余空间才从新到旧回填进度内容。协议带 `final_answer` 标记的(Codex app-server)直接采用,没有标记的 agent 在收尾时把最后一条非空 assistant 文本提升为最终回答,Claude、opencode、Kimi、Pi、DSH 共用同一套保护
|
|
28
|
+
- **回答放不下时随后补发完整文本**:单张卡片连「最终回答 + 必要控件」都装不下时,原卡提示「最终回答较长,完整内容已随后发送」,完整回答按 UTF-8 字节边界拆成多条纯文本消息补发(优先落在换行处,单条上限 20000 字节),拼接后与原文逐字一致,不再静默截断;补发失败会在原卡给出「完整回答补发失败」的可见回执
|
|
29
|
+
|
|
30
|
+
### 维护
|
|
31
|
+
|
|
32
|
+
- **入口与路由/桥接层拆分为独立模块**:消息入站装配、命令实现(`ls` / `order` / `resume` / `ws`)、分页与路径校验、审批转发、用量聚合、会话回写、DSH 多路复用流各自成模块,`src/index.ts`、`src/bridge/index.ts`、`src/router/index.ts` 只保留编排职责;同时为拆分中暴露的状态竞争(中断与自然结束并发写同一张卡的终态、审批重投递、bash 卡片收尾)补上回归用例。无行为变化
|
|
33
|
+
|
|
8
34
|
## [0.5.12] - 2026-10-24
|
|
9
35
|
|
|
10
36
|
### 修复
|
|
@@ -562,8 +588,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
562
588
|
### 修复
|
|
563
589
|
|
|
564
590
|
- `displayTitle`/`summary` 跳过 task-notification 注入
|
|
565
|
-
- 修复 `scanClaudeSessions`
|
|
566
|
-
- 修复 probe
|
|
591
|
+
- 修复 `scanClaudeSessions` 对迁移到其他目录的会话的遗漏
|
|
592
|
+
- 修复 probe 生命周期、工厂回退、卡片回调等问题
|
|
567
593
|
|
|
568
594
|
### 变更
|
|
569
595
|
|
package/README.en.md
CHANGED
|
@@ -14,19 +14,6 @@ Designed for a single-user, peer-to-peer private chat. The agent's system prompt
|
|
|
14
14
|
|
|
15
15
|
> **New here? Start with [docs/en/getting-started.md](docs/en/getting-started.md).**
|
|
16
16
|
|
|
17
|
-
## ⚠️ Security Warning (read first)
|
|
18
|
-
|
|
19
|
-
This tool turns Feishu messages directly into local agent executions. **Out of the box, agents run without approval prompts**: Claude defaults to `bypassPermissions` (no approval card), but that permission mode is **configurable** — set it to anything other than `bypassPermissions` (`default` / `acceptEdits` / `auto` / `dontAsk` / `plan`) to enable interactive approval, where high-risk actions wait for your confirmation on the Feishu card. Codex uses the **app-server approval mode** (default `approvalPolicy=on-request` — command execution requires your confirmation on the Feishu card, sandbox defaults to `workspace-write`); Kimi defaults to `manual` (per-action approval; note that since kimi 0.40, `yolo` means Ask-When-Needed rather than allow-everything — use `auto` for fully unattended, no-approval execution); other agents run with their CLI defaults. **In any mode**, agents can read and write the local directories you specify. You MUST:
|
|
20
|
-
|
|
21
|
-
- **Use it only in your own private (p2p) chat.** Never add the bot to any group chat; never let anyone else talk to it.
|
|
22
|
-
- Restrict the app's visibility to **yourself only** in the Feishu Open Platform.
|
|
23
|
-
- Don't run it on a machine holding data you can't afford to lose; prefer a dedicated user / machine / container.
|
|
24
|
-
- Keep credentials such as `appSecret` in your local `config.yaml` only — **never commit them anywhere**.
|
|
25
|
-
|
|
26
|
-
In approval mode (Claude, Codex, Kimi), an approval card arrives before a command runs — approve or reject it right in Feishu:
|
|
27
|
-
|
|
28
|
-

|
|
29
|
-
|
|
30
17
|
## Prerequisites
|
|
31
18
|
|
|
32
19
|
- OS: macOS / Linux / Windows 10+ (native Windows support is verified on real hardware, with behavior matching macOS/Linux)
|
|
@@ -70,7 +57,7 @@ defaultAgent: claude
|
|
|
70
57
|
claude:
|
|
71
58
|
model: claude-opus-4-8
|
|
72
59
|
effort: medium # low | medium | high | xhigh | max
|
|
73
|
-
permissionMode:
|
|
60
|
+
permissionMode: auto # Claude official --permission-mode: default | acceptEdits | auto | bypassPermissions | manual | dontAsk | plan (switchable via /config card; auto is the default: low-risk actions run, high-risk actions ask)
|
|
74
61
|
stopGraceMs: 5000
|
|
75
62
|
|
|
76
63
|
logging:
|
package/README.md
CHANGED
|
@@ -14,19 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
> **第一次接触本项目?先看 [docs/zh/getting-started.md](docs/zh/getting-started.md)。**
|
|
16
16
|
|
|
17
|
-
## ⚠️ 安全警告(必读)
|
|
18
|
-
|
|
19
|
-
本工具会把飞书消息直接转为本地 agent 执行。**开箱默认是不弹审批的**:Claude 默认以 `bypassPermissions` 运行(无审批卡),但该权限模式**可配置**——把它改成 `bypassPermissions` 以外的值(`default` / `acceptEdits` / `auto` / `dontAsk` / `plan`)即启用交互式审批,高风险操作会在飞书卡片上等你确认;Codex 采用 **app-server 审批模式**(默认 `approvalPolicy=on-request`,执行命令需在飞书卡片上确认,沙箱默认 `workspace-write`);Kimi 默认 `manual`(逐条审批;注意 kimi 0.40+ 起 `yolo` 已改为「必要时询问」而非全部放行,要完全不弹审批需选 `auto`);其他 agent 以各自 CLI 的默认权限运行。**无论哪种模式**,agent 都能读写你指定的本地目录,请务必:
|
|
20
|
-
|
|
21
|
-
- **仅限你自己的私聊(p2p)使用**。不要把机器人拉进任何群聊,不要让任何其他人能与它对话。
|
|
22
|
-
- 在飞书开放平台把应用可见范围限制到**只有你自己**。
|
|
23
|
-
- 运行本工具的机器上不要存放与损失承受力不符的数据;建议在独立用户/机器/容器里运行。
|
|
24
|
-
- `appSecret` 等凭据只在本地 `config.yaml`,**不要提交到任何仓库**。
|
|
25
|
-
|
|
26
|
-
审批模式下(Claude、Codex、Kimi),执行命令前会收到审批卡片,可直接在飞书里允许或拒绝:
|
|
27
|
-
|
|
28
|
-

|
|
29
|
-
|
|
30
17
|
## 前置条件
|
|
31
18
|
|
|
32
19
|
- 操作系统:macOS / Linux / Windows 10+(原生 Windows 支持已完成真机验证,行为与 macOS/Linux 对齐)
|
|
@@ -70,7 +57,7 @@ defaultAgent: claude
|
|
|
70
57
|
claude:
|
|
71
58
|
model: claude-opus-4-8
|
|
72
59
|
effort: medium # low | medium | high | xhigh | max
|
|
73
|
-
permissionMode:
|
|
60
|
+
permissionMode: auto # Claude 官方 --permission-mode:default | acceptEdits | auto | bypassPermissions | manual | dontAsk | plan(`/config` 卡片可切换;auto 为默认:低风险放行、高风险审批)
|
|
74
61
|
stopGraceMs: 5000
|
|
75
62
|
|
|
76
63
|
logging:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent 名单单源(运行时数组 + 类型同源,二者不可能漂移)。
|
|
3
|
+
*
|
|
4
|
+
* 中立层:config(校验 defaultAgent)、session(按 agent 存 sessionId)、card
|
|
5
|
+
* (显示名)、router(解析用户输入)、runner(实现各家 runner)都要用这个名字,
|
|
6
|
+
* 谁都不该为了拿它去 import 别层的模块 —— 名单原先住在 runner/types.ts 时,
|
|
7
|
+
* 地基层 config 就得反向 import 上层 runner。
|
|
8
|
+
*
|
|
9
|
+
* 历史上这份名单被手抄成多份(router 的 VALID_AGENTS、bridge 的 resume.compact
|
|
10
|
+
* 校验、各 agent 的 config builder…),漂移过:dsh 加入后 bridge 的
|
|
11
|
+
* resume.compact 仍只有 5 个,带 dsh 的 resume 卡片会静默退回 defaultAgent。
|
|
12
|
+
* 新增 agent 只改这里。
|
|
13
|
+
*/
|
|
14
|
+
export declare const AGENT_KINDS: readonly ["claude", "codex", "opencode", "pi", "kimi", "dsh"];
|
|
15
|
+
export type AgentKind = (typeof AGENT_KINDS)[number];
|
|
16
|
+
/** 用户可见 agent 显示名单源:启动注册与无 registry 的回退路径共用。 */
|
|
17
|
+
export declare const AGENT_DISPLAY_NAMES: Record<AgentKind, string>;
|
|
18
|
+
/** 任意输入(卡片 payload / 命令行参数)是否是一个已知 agent 名。 */
|
|
19
|
+
export declare function isAgentKind(value: unknown): value is AgentKind;
|
|
20
|
+
/**
|
|
21
|
+
* 解析用户给出的 agent 名,未知/缺省时回退 `fallback`。
|
|
22
|
+
*
|
|
23
|
+
* 判定此前抄了三份(router 的两处 resume 分支 + commands/resume.ts);名单与
|
|
24
|
+
* 判定同源才有意义——`resume.compact` 曾因手抄名单漏掉 dsh 而静默退回
|
|
25
|
+
* defaultAgent(见 `AGENT_KINDS` 头注)。
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveAgentKind(value: unknown, fallback: AgentKind): AgentKind;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent 名单单源(运行时数组 + 类型同源,二者不可能漂移)。
|
|
3
|
+
*
|
|
4
|
+
* 中立层:config(校验 defaultAgent)、session(按 agent 存 sessionId)、card
|
|
5
|
+
* (显示名)、router(解析用户输入)、runner(实现各家 runner)都要用这个名字,
|
|
6
|
+
* 谁都不该为了拿它去 import 别层的模块 —— 名单原先住在 runner/types.ts 时,
|
|
7
|
+
* 地基层 config 就得反向 import 上层 runner。
|
|
8
|
+
*
|
|
9
|
+
* 历史上这份名单被手抄成多份(router 的 VALID_AGENTS、bridge 的 resume.compact
|
|
10
|
+
* 校验、各 agent 的 config builder…),漂移过:dsh 加入后 bridge 的
|
|
11
|
+
* resume.compact 仍只有 5 个,带 dsh 的 resume 卡片会静默退回 defaultAgent。
|
|
12
|
+
* 新增 agent 只改这里。
|
|
13
|
+
*/
|
|
14
|
+
export const AGENT_KINDS = ['claude', 'codex', 'opencode', 'pi', 'kimi', 'dsh'];
|
|
15
|
+
/** 用户可见 agent 显示名单源:启动注册与无 registry 的回退路径共用。 */
|
|
16
|
+
export const AGENT_DISPLAY_NAMES = {
|
|
17
|
+
claude: 'Claude',
|
|
18
|
+
codex: 'Codex',
|
|
19
|
+
opencode: 'Opencode',
|
|
20
|
+
pi: 'Pi',
|
|
21
|
+
kimi: 'Kimi',
|
|
22
|
+
dsh: 'DSH',
|
|
23
|
+
};
|
|
24
|
+
/** 任意输入(卡片 payload / 命令行参数)是否是一个已知 agent 名。 */
|
|
25
|
+
export function isAgentKind(value) {
|
|
26
|
+
return AGENT_KINDS.includes(value);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 解析用户给出的 agent 名,未知/缺省时回退 `fallback`。
|
|
30
|
+
*
|
|
31
|
+
* 判定此前抄了三份(router 的两处 resume 分支 + commands/resume.ts);名单与
|
|
32
|
+
* 判定同源才有意义——`resume.compact` 曾因手抄名单漏掉 dsh 而静默退回
|
|
33
|
+
* defaultAgent(见 `AGENT_KINDS` 头注)。
|
|
34
|
+
*/
|
|
35
|
+
export function resolveAgentKind(value, fallback) {
|
|
36
|
+
return isAgentKind(value) ? value : fallback;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=agent-kinds.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 审批卡动作分发(Bridge 的终态卡动作侧,审计 #32 的一刀)。
|
|
3
|
+
*
|
|
4
|
+
* 每个方法只做一件事:按 runId 找到那次 run 的 `ApprovalCoordinator`,把卡片
|
|
5
|
+
* payload 翻成一次 coordinator 调用。原先这 7 个 handler 连同「找不到
|
|
6
|
+
* coordinator 怎么回答」的公共前缀平铺在 Bridge 里,占了 160 行却不碰任何
|
|
7
|
+
* Bridge 状态——只碰 `approvalCoordinators` 这一张表。
|
|
8
|
+
*
|
|
9
|
+
* 语义红线(保留原位,别在调用方重新实现):
|
|
10
|
+
* - run 已结束(coordinator 已释放)时点审批按钮必须给用户明确反馈,不能静默;
|
|
11
|
+
* router 把这个异常转成 error toast。
|
|
12
|
+
* - `nonce` 原样透传给 coordinator:飞书会重投递同一个按钮回调,去重靠它
|
|
13
|
+
* (见 approval-coordinator.ts 的状态机注释)。
|
|
14
|
+
*/
|
|
15
|
+
import type { ApprovalCoordinator } from './approval-coordinator.js';
|
|
16
|
+
export interface ApprovalActionDeps {
|
|
17
|
+
/** 按 runId 取该 run 的 coordinator;已结束返回 undefined。 */
|
|
18
|
+
coordinatorFor(runId: string): ApprovalCoordinator | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** 所有 handler 共用的入参形状。 */
|
|
21
|
+
interface RunScopedOpts {
|
|
22
|
+
runId: string;
|
|
23
|
+
requestId: number | string;
|
|
24
|
+
}
|
|
25
|
+
export declare class ApprovalActions {
|
|
26
|
+
private deps;
|
|
27
|
+
constructor(deps: ApprovalActionDeps);
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the ApprovalCoordinator for a run, or throw the standard
|
|
30
|
+
* "已结束" error. Shared preamble of all approval card-action handlers.
|
|
31
|
+
*/
|
|
32
|
+
private withCoordinator;
|
|
33
|
+
private dispatchQuestionAction;
|
|
34
|
+
/**
|
|
35
|
+
* Handle an approval response from a card button click.
|
|
36
|
+
* Routes to the ApprovalCoordinator for the given run.
|
|
37
|
+
*/
|
|
38
|
+
handleApprovalRespond(opts: RunScopedOpts & {
|
|
39
|
+
decision: string;
|
|
40
|
+
nonce: string;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* 计划审批修改意见(ExitPlanMode 输入框):记录到 coordinator 并回流卡片。
|
|
44
|
+
* 「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
45
|
+
*/
|
|
46
|
+
handleApprovalPlanFeedback(opts: RunScopedOpts & {
|
|
47
|
+
text: string;
|
|
48
|
+
nonce: string;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Handle a permission toggle from a card button click.
|
|
52
|
+
* Routes to the ApprovalCoordinator for the given run.
|
|
53
|
+
*/
|
|
54
|
+
handleApprovalToggle(opts: RunScopedOpts & {
|
|
55
|
+
permId: string;
|
|
56
|
+
selected?: boolean;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Claude AskUserQuestion 选项点击(单选即时提交 / 多选切换勾选)。
|
|
60
|
+
* 路由到 ApprovalCoordinator.toggleAnswer。
|
|
61
|
+
*/
|
|
62
|
+
handleApprovalAnswer(opts: RunScopedOpts & {
|
|
63
|
+
questionIndex: number;
|
|
64
|
+
option: string;
|
|
65
|
+
nonce: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Claude AskUserQuestion 多选问题的「提交答案」按钮。
|
|
69
|
+
* 路由到 ApprovalCoordinator.submitAnswers。
|
|
70
|
+
*/
|
|
71
|
+
handleApprovalAnswerSubmit(opts: RunScopedOpts & {
|
|
72
|
+
questionIndex: number;
|
|
73
|
+
nonce: string;
|
|
74
|
+
}): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Claude AskUserQuestion 自定义答案(Other):自由文本直接
|
|
77
|
+
* 作为该单选问题的答案提交。路由到 ApprovalCoordinator.answerCustom。
|
|
78
|
+
*/
|
|
79
|
+
handleApprovalAnswerCustom(opts: RunScopedOpts & {
|
|
80
|
+
questionIndex: number;
|
|
81
|
+
text: string;
|
|
82
|
+
nonce: string;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Codex AskUserQuestion 补充说明(user_note,2026-08-18 live 验证模型理解
|
|
86
|
+
* "user_note: <text>" 条目):记录到 coordinator,随答案一起提交。
|
|
87
|
+
* 路由到 ApprovalCoordinator.answerNote。
|
|
88
|
+
*/
|
|
89
|
+
handleApprovalAnswerNote(opts: RunScopedOpts & {
|
|
90
|
+
questionIndex: number;
|
|
91
|
+
text: string;
|
|
92
|
+
nonce: string;
|
|
93
|
+
}): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 审批卡动作分发(Bridge 的终态卡动作侧,审计 #32 的一刀)。
|
|
3
|
+
*
|
|
4
|
+
* 每个方法只做一件事:按 runId 找到那次 run 的 `ApprovalCoordinator`,把卡片
|
|
5
|
+
* payload 翻成一次 coordinator 调用。原先这 7 个 handler 连同「找不到
|
|
6
|
+
* coordinator 怎么回答」的公共前缀平铺在 Bridge 里,占了 160 行却不碰任何
|
|
7
|
+
* Bridge 状态——只碰 `approvalCoordinators` 这一张表。
|
|
8
|
+
*
|
|
9
|
+
* 语义红线(保留原位,别在调用方重新实现):
|
|
10
|
+
* - run 已结束(coordinator 已释放)时点审批按钮必须给用户明确反馈,不能静默;
|
|
11
|
+
* router 把这个异常转成 error toast。
|
|
12
|
+
* - `nonce` 原样透传给 coordinator:飞书会重投递同一个按钮回调,去重靠它
|
|
13
|
+
* (见 approval-coordinator.ts 的状态机注释)。
|
|
14
|
+
*/
|
|
15
|
+
import { getLogger } from '../logger/index.js';
|
|
16
|
+
import { decisionToApprovalAction } from './approval-coordinator.js';
|
|
17
|
+
export class ApprovalActions {
|
|
18
|
+
deps;
|
|
19
|
+
constructor(deps) {
|
|
20
|
+
this.deps = deps;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the ApprovalCoordinator for a run, or throw the standard
|
|
24
|
+
* "已结束" error. Shared preamble of all approval card-action handlers.
|
|
25
|
+
*/
|
|
26
|
+
withCoordinator(runId, fn) {
|
|
27
|
+
const coordinator = this.deps.coordinatorFor(runId);
|
|
28
|
+
if (!coordinator) {
|
|
29
|
+
getLogger().warn(`[lark-remote] no approval coordinator for runId=${runId.slice(0, 8)}`);
|
|
30
|
+
throw new Error('任务已结束,审批无法响应');
|
|
31
|
+
}
|
|
32
|
+
return fn(coordinator);
|
|
33
|
+
}
|
|
34
|
+
dispatchQuestionAction(opts, action, invoke) {
|
|
35
|
+
getLogger().info(`[lark-remote] ${action} runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} question=${opts.questionIndex}`);
|
|
36
|
+
return this.withCoordinator(opts.runId, invoke);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Handle an approval response from a card button click.
|
|
40
|
+
* Routes to the ApprovalCoordinator for the given run.
|
|
41
|
+
*/
|
|
42
|
+
async handleApprovalRespond(opts) {
|
|
43
|
+
getLogger().info(`[lark-remote] handleApprovalRespond runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} decision=${opts.decision}`);
|
|
44
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
45
|
+
// Build ApprovalAction from the decision string
|
|
46
|
+
const action = decisionToApprovalAction(opts.decision);
|
|
47
|
+
await coordinator.submit(action, { requestId: opts.requestId, nonce: opts.nonce });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 计划审批修改意见(ExitPlanMode 输入框):记录到 coordinator 并回流卡片。
|
|
52
|
+
* 「拒绝并附意见」/「批准并采纳修改」按钮复用该文本。
|
|
53
|
+
*/
|
|
54
|
+
async handleApprovalPlanFeedback(opts) {
|
|
55
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
56
|
+
await coordinator.planFeedback({ text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Handle a permission toggle from a card button click.
|
|
61
|
+
* Routes to the ApprovalCoordinator for the given run.
|
|
62
|
+
*/
|
|
63
|
+
async handleApprovalToggle(opts) {
|
|
64
|
+
getLogger().info(`[lark-remote] handleApprovalToggle runId=${opts.runId.slice(0, 8)}... requestId=${opts.requestId} permId=${opts.permId}`);
|
|
65
|
+
return this.withCoordinator(opts.runId, async (coordinator) => {
|
|
66
|
+
// selected 由卡片根据当前渲染状态传回(取反),不再硬编码只授不撤。
|
|
67
|
+
const toggleAction = {
|
|
68
|
+
permId: opts.permId,
|
|
69
|
+
selected: opts.selected ?? true,
|
|
70
|
+
};
|
|
71
|
+
await coordinator.togglePerm(toggleAction, { requestId: opts.requestId });
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Claude AskUserQuestion 选项点击(单选即时提交 / 多选切换勾选)。
|
|
76
|
+
* 路由到 ApprovalCoordinator.toggleAnswer。
|
|
77
|
+
*/
|
|
78
|
+
async handleApprovalAnswer(opts) {
|
|
79
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswer', (coordinator) => coordinator.toggleAnswer({ questionIndex: opts.questionIndex, option: opts.option }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Claude AskUserQuestion 多选问题的「提交答案」按钮。
|
|
83
|
+
* 路由到 ApprovalCoordinator.submitAnswers。
|
|
84
|
+
*/
|
|
85
|
+
async handleApprovalAnswerSubmit(opts) {
|
|
86
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerSubmit', (coordinator) => coordinator.submitAnswers({ questionIndex: opts.questionIndex }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Claude AskUserQuestion 自定义答案(Other):自由文本直接
|
|
90
|
+
* 作为该单选问题的答案提交。路由到 ApprovalCoordinator.answerCustom。
|
|
91
|
+
*/
|
|
92
|
+
async handleApprovalAnswerCustom(opts) {
|
|
93
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerCustom', (coordinator) => coordinator.answerCustom({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Codex AskUserQuestion 补充说明(user_note,2026-08-18 live 验证模型理解
|
|
97
|
+
* "user_note: <text>" 条目):记录到 coordinator,随答案一起提交。
|
|
98
|
+
* 路由到 ApprovalCoordinator.answerNote。
|
|
99
|
+
*/
|
|
100
|
+
async handleApprovalAnswerNote(opts) {
|
|
101
|
+
return this.dispatchQuestionAction(opts, 'handleApprovalAnswerNote', (coordinator) => coordinator.answerNote({ questionIndex: opts.questionIndex, text: opts.text }, { requestId: opts.requestId, nonce: opts.nonce }));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=approval-actions.js.map
|
|
@@ -179,6 +179,7 @@ export declare class ApprovalCoordinator {
|
|
|
179
179
|
/** 记录问题答案到 tracked.answers(懒初始化 Map)。 */
|
|
180
180
|
private recordAnswer;
|
|
181
181
|
private getPendingQuestion;
|
|
182
|
+
private getPendingQuestionAt;
|
|
182
183
|
private allQuestionsAnswered;
|
|
183
184
|
/** 推送审批卡视图更新(approval_view_updated 重渲染;失败仅记日志)。 */
|
|
184
185
|
private pushApprovalViewUpdate;
|
|
@@ -178,12 +178,7 @@ export class ApprovalCoordinator {
|
|
|
178
178
|
* submitAnswers() 显式提交。
|
|
179
179
|
*/
|
|
180
180
|
async toggleAnswer(action, ctx) {
|
|
181
|
-
const tracked = this.
|
|
182
|
-
const questions = tracked.view.questions;
|
|
183
|
-
const q = questions[action.questionIndex];
|
|
184
|
-
if (!q) {
|
|
185
|
-
throw new Error(`Question ${action.questionIndex} not found in request ${ctx.requestId}`);
|
|
186
|
-
}
|
|
181
|
+
const { tracked, question: q } = this.getPendingQuestionAt(ctx.requestId, action.questionIndex);
|
|
187
182
|
const option = q.options.find((o) => o.label === action.option);
|
|
188
183
|
if (!option) {
|
|
189
184
|
throw new Error(`Option "${action.option}" not found in question ${action.questionIndex}`);
|
|
@@ -217,12 +212,7 @@ export class ApprovalCoordinator {
|
|
|
217
212
|
* 提交该问题当前勾选的选项;全部问题答完时通过 responder 提交。
|
|
218
213
|
*/
|
|
219
214
|
async submitAnswers(action, ctx) {
|
|
220
|
-
const tracked = this.
|
|
221
|
-
const questions = tracked.view.questions;
|
|
222
|
-
const q = questions[action.questionIndex];
|
|
223
|
-
if (!q) {
|
|
224
|
-
throw new Error(`Question ${action.questionIndex} not found in request ${ctx.requestId}`);
|
|
225
|
-
}
|
|
215
|
+
const { tracked, question: q } = this.getPendingQuestionAt(ctx.requestId, action.questionIndex);
|
|
226
216
|
if (!q.multiSelect) {
|
|
227
217
|
throw new Error('该问题为单选,请直接点击选项');
|
|
228
218
|
}
|
|
@@ -240,12 +230,7 @@ export class ApprovalCoordinator {
|
|
|
240
230
|
* 单选问题的答案。与单选 toggle 同语义:全部问题答完自动提交。
|
|
241
231
|
*/
|
|
242
232
|
async answerCustom(action, ctx) {
|
|
243
|
-
const tracked = this.
|
|
244
|
-
const questions = tracked.view.questions;
|
|
245
|
-
const q = questions[action.questionIndex];
|
|
246
|
-
if (!q) {
|
|
247
|
-
throw new Error(`Question ${action.questionIndex} not found in request ${ctx.requestId}`);
|
|
248
|
-
}
|
|
233
|
+
const { tracked, question: q } = this.getPendingQuestionAt(ctx.requestId, action.questionIndex);
|
|
249
234
|
if (q.multiSelect) {
|
|
250
235
|
throw new Error('自定义答案仅支持单选');
|
|
251
236
|
}
|
|
@@ -266,12 +251,7 @@ export class ApprovalCoordinator {
|
|
|
266
251
|
* 后回流视图(卡片回显),同一请求可多次修改(新 nonce 来自新渲染)。
|
|
267
252
|
*/
|
|
268
253
|
async answerNote(action, ctx) {
|
|
269
|
-
const tracked = this.
|
|
270
|
-
const questions = tracked.view.questions;
|
|
271
|
-
const q = questions[action.questionIndex];
|
|
272
|
-
if (!q) {
|
|
273
|
-
throw new Error(`Question ${action.questionIndex} not found in request ${ctx.requestId}`);
|
|
274
|
-
}
|
|
254
|
+
const { tracked, question: q } = this.getPendingQuestionAt(ctx.requestId, action.questionIndex);
|
|
275
255
|
const text = action.text.trim();
|
|
276
256
|
if (!text) {
|
|
277
257
|
throw new Error('请输入补充说明');
|
|
@@ -443,6 +423,14 @@ export class ApprovalCoordinator {
|
|
|
443
423
|
}
|
|
444
424
|
return tracked;
|
|
445
425
|
}
|
|
426
|
+
getPendingQuestionAt(requestId, questionIndex) {
|
|
427
|
+
const tracked = this.getPendingQuestion(requestId);
|
|
428
|
+
const question = tracked.view.questions?.[questionIndex];
|
|
429
|
+
if (!question) {
|
|
430
|
+
throw new Error(`Question ${questionIndex} not found in request ${requestId}`);
|
|
431
|
+
}
|
|
432
|
+
return { tracked, question };
|
|
433
|
+
}
|
|
446
434
|
allQuestionsAnswered(tracked) {
|
|
447
435
|
const questions = tracked.view.questions ?? [];
|
|
448
436
|
if (questions.length === 0)
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import type { SessionReaderRegistry } from '../session/index.js';
|
|
2
|
-
import type { AgentKind } from '../
|
|
2
|
+
import type { AgentKind } from '../agent-kinds.js';
|
|
3
|
+
import type { AgentSessionUsage } from '../session-contract.js';
|
|
3
4
|
export declare function errorMessage(error: unknown): string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
cacheCreationTokens?: number;
|
|
11
|
-
totalTokens?: number;
|
|
12
|
-
inputTokens?: number;
|
|
13
|
-
outputTokens?: number;
|
|
14
|
-
cumulativeTotalTokens?: number;
|
|
15
|
-
cumulativeInputTokens?: number;
|
|
16
|
-
cumulativeOutputTokens?: number;
|
|
17
|
-
cumulativeCacheReadTokens?: number;
|
|
18
|
-
cumulativeCacheCreationTokens?: number;
|
|
19
|
-
};
|
|
5
|
+
/**
|
|
6
|
+
* jsonl 侧的最终用量快照。就是 `AgentSessionUsage` 的可选形态:reader 保证
|
|
7
|
+
* input/output/contextLength 有值,这里允许「读不到」,字段清单不再手抄第二份
|
|
8
|
+
* (§9.21 的口径一致要求靠这个单源维持)。
|
|
9
|
+
*/
|
|
10
|
+
export type FinalUsageSnapshot = Partial<AgentSessionUsage>;
|
|
20
11
|
/**
|
|
21
12
|
* Read final usage (contextLength + compactCount + cache tokens) from the
|
|
22
13
|
* session jsonl. Called after a run finishes.
|
|
@@ -28,24 +28,7 @@ export function resolveFinalUsage(registry, defaultAgent, sessionId, cwd, agentK
|
|
|
28
28
|
// fallback to per-run live usage is visible in logs, not silent.
|
|
29
29
|
getLogger().warn(`[lark-remote] resolveFinalUsage: no usage from jsonl sessionId=${sessionId} cwd=${cwd} agent=${agentKind}, card falls back to per-run live usage`);
|
|
30
30
|
}
|
|
31
|
-
return content.usage
|
|
32
|
-
? {
|
|
33
|
-
contextLength: content.usage.contextLength,
|
|
34
|
-
contextLimit: content.usage.contextLimit,
|
|
35
|
-
compactCount: content.usage.compactCount,
|
|
36
|
-
compactPreContextLength: content.usage.compactPreContextLength,
|
|
37
|
-
cacheReadTokens: content.usage.cacheReadTokens,
|
|
38
|
-
cacheCreationTokens: content.usage.cacheCreationTokens,
|
|
39
|
-
totalTokens: content.usage.totalTokens,
|
|
40
|
-
inputTokens: content.usage.inputTokens,
|
|
41
|
-
outputTokens: content.usage.outputTokens,
|
|
42
|
-
cumulativeTotalTokens: content.usage.cumulativeTotalTokens,
|
|
43
|
-
cumulativeInputTokens: content.usage.cumulativeInputTokens,
|
|
44
|
-
cumulativeOutputTokens: content.usage.cumulativeOutputTokens,
|
|
45
|
-
cumulativeCacheReadTokens: content.usage.cumulativeCacheReadTokens,
|
|
46
|
-
cumulativeCacheCreationTokens: content.usage.cumulativeCacheCreationTokens,
|
|
47
|
-
}
|
|
48
|
-
: undefined;
|
|
31
|
+
return content.usage ? { ...content.usage } : undefined;
|
|
49
32
|
}
|
|
50
33
|
catch (err) {
|
|
51
34
|
getLogger().warn(`[lark-remote] resolveFinalUsage failed sessionId=${sessionId}: ${errorMessage(err)}`);
|