dsh-lark-bot 0.10.0 → 0.10.2
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/README.md +25 -17
- package/cordis.patch.yml +5 -4
- package/dist/cli.js +85 -27
- package/dist/cli.js.map +1 -1
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.js +62 -17
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ DSH_LARK_APP_ID=cli_xxx DSH_LARK_APP_SECRET=<secret> DSH_LARK_TENANT=feishu \
|
|
|
127
127
|
> 卸载:`dsh plugin --profile dsh-lark remove dsh-lark-bot`。
|
|
128
128
|
> Uninstall: `dsh plugin --profile dsh-lark remove dsh-lark-bot`.
|
|
129
129
|
|
|
130
|
-
###
|
|
130
|
+
### 3. 基本使用 | Basic usage
|
|
131
131
|
|
|
132
132
|
在飞书里向 bot 发送普通消息即可开始工作,常用命令:
|
|
133
133
|
|
|
@@ -231,8 +231,9 @@ you answer — no extra command needed. The run-timeout watchdog pauses while a
|
|
|
231
231
|
- `/safemode stop`:终止当前正在运行的安全模式任务(也可点击任务卡片上的 ⏹ 按钮);
|
|
232
232
|
- `/safemode exit`:退出安全模式,守护重启完整 profile 并把飞书通道交还给正常形态;
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
234
|
+
安全模式任务有**空闲超时**(`DSH_LARK_GUARDIAN_SAFE_TIMEOUT_MS`,默认 10 分钟:任务持续无
|
|
235
|
+
活动事件才被终止,活跃的流式任务不会被误杀),超时或失败都会在卡片上给出明确终态,不会无声
|
|
236
|
+
挂起。全程不需要命令行;dsh 恢复后守护自动断开并回归静默。安装:
|
|
236
237
|
|
|
237
238
|
```bash
|
|
238
239
|
# 随 setup 默认安装(无需额外参数);已安装后也可单独安装 / 重装:
|
|
@@ -258,8 +259,9 @@ takes over the Feishu channel so you can self-heal without touching the command
|
|
|
258
259
|
- `/safemode exit`: leave safe mode — the guardian relaunches the full profile and hands the
|
|
259
260
|
Feishu channel back;
|
|
260
261
|
|
|
261
|
-
Safe-mode tasks are bounded by
|
|
262
|
-
(`DSH_LARK_GUARDIAN_SAFE_TIMEOUT_MS`, default 10 minutes
|
|
262
|
+
Safe-mode tasks are bounded by an **idle timeout**
|
|
263
|
+
(`DSH_LARK_GUARDIAN_SAFE_TIMEOUT_MS`, default 10 minutes: a task is stopped only after it has
|
|
264
|
+
been silent for the whole window, so active streaming work is never cut short); timeouts and failures always surface a
|
|
263
265
|
clear terminal state on the card instead of hanging silently. No command line is needed for the
|
|
264
266
|
whole rescue flow; once dsh is back, the guardian releases the channel automatically. Install:
|
|
265
267
|
|
|
@@ -494,7 +496,7 @@ Core environment variables:
|
|
|
494
496
|
| `DSH_LARK_MAX_TOKENS` | 未设置 | SDK agent 每请求输出 token 上限<br>Per-request output token cap for SDK agents |
|
|
495
497
|
| `DSH_LARK_ACCESS_DEFAULT_DENY` | `false` | 无白名单时拒绝私聊<br>Reject private chats when no allowlist is configured |
|
|
496
498
|
| `DSH_LARK_EVENT_FRESHNESS_MS` | `600000` | 过期消息拒绝窗口(0 关闭)<br>Stale-message rejection window (0 disables) |
|
|
497
|
-
| `DSH_LARK_RUN_TIMEOUT_MS` | `300000` |
|
|
499
|
+
| `DSH_LARK_RUN_TIMEOUT_MS` | `300000` | 单次运行空闲超时:持续无活动事件才终止(活跃任务不会被误杀)<br>Idle timeout for a single run: stops only after the run has been silent for this long |
|
|
498
500
|
| `DSH_LARK_STOP_GRACE_MS` | `5000` | SIGTERM 后等待优雅退出再 SIGKILL 的宽限期<br>Grace period after SIGTERM before SIGKILL |
|
|
499
501
|
| `DSH_LARK_SCOPE_CONCURRENCY` | `2` | 每个 scope 的并行任务数(1=严格串行)<br>Concurrent runs per scope (1 = strictly serial) |
|
|
500
502
|
| `DSH_LARK_RETENTION_MSGS` | `40` | 每个 scope 保留的消息条数(0=全部保留)<br>Messages kept per scope (0 keeps everything) |
|
|
@@ -508,7 +510,7 @@ Core environment variables:
|
|
|
508
510
|
| `DSH_LARK_GUARDIAN_STALE_MS` | `15000` | 心跳超时阈值,超过且无 dsh 进程则接管飞书通道<br>Heartbeat staleness threshold before channel takeover |
|
|
509
511
|
| `DSH_LARK_GUARDIAN_ENGINE_DEAD_MS` | `120000` | dsh 进程存活但心跳持续超时该时长,判定桥接引擎已死并接管<br>Live dsh process with heartbeat stale this long is treated as engine-dead (takeover) |
|
|
510
512
|
| `DSH_LARK_GUARDIAN_SAFE_ADAPTER` | `auto` | 安全模式引擎:`auto` 优先 SDK 流式、失败回退 headless;`sdk` 强制 SDK;`headless` 跳过预置<br>Safe-mode engine: `auto` tries the SDK streaming runtime then falls back to headless; `sdk` requires it; `headless` skips provisioning |
|
|
511
|
-
| `DSH_LARK_GUARDIAN_SAFE_TIMEOUT_MS` | `600000` |
|
|
513
|
+
| `DSH_LARK_GUARDIAN_SAFE_TIMEOUT_MS` | `600000` | 安全模式单任务空闲超时(持续无活动事件才停止并出超时卡)<br>Safe-mode per-task idle timeout (stops the run after it has been silent this long and renders a timeout card) |
|
|
512
514
|
| `DSH_LARK_GUARDIAN_CARD_DENSITY` | `detailed` | 安全模式任务卡片密度(compact / standard / detailed)<br>Card density for safe-mode run cards |
|
|
513
515
|
|
|
514
516
|
启动时会自动查找本机常见的 `@deepseek-ai/dsh` 安装位置。只有自动发现失败或需要指定特殊 profile 时,才需要设置这两个变量。
|
|
@@ -574,14 +576,14 @@ availability probe for the current adapter (`sdk` / `acp` / `headless` runtime h
|
|
|
574
576
|
Common issues:
|
|
575
577
|
|
|
576
578
|
- **bot 静默 / 长连接失败**:查看 stderr 上的 JSONL 日志,关注 `channel` 与 `channel-command` 类别;SDK 会自动重连。
|
|
577
|
-
- **agent 无响应**:发送 `/status` 查看当前 scope、cwd 和 active run;发送 `/stop`
|
|
579
|
+
- **agent 无响应**:发送 `/status` 查看当前 scope、cwd 和 active run;发送 `/stop` 终止当前任务;持续无响应超过 `DSH_LARK_RUN_TIMEOUT_MS` 时看门狗会自动终止(空闲超时,活跃任务不会被误杀)。
|
|
578
580
|
- **首次扫码失败**:确认本机时间准确、网络可访问飞书开放平台;已拿到 App ID/Secret 时可用 `--app-id` / `--app-secret` 跳过扫码。
|
|
579
581
|
|
|
580
582
|
- **Silent bot / long-connection failure**: check the JSONL logs on stderr, focusing on the
|
|
581
583
|
`channel` and `channel-command` categories; the SDK reconnects automatically.
|
|
582
584
|
- **Unresponsive agent**: send `/status` to view the scope, cwd and active run; send `/stop` to
|
|
583
|
-
terminate the current task; the watchdog terminates it automatically after
|
|
584
|
-
`DSH_LARK_RUN_TIMEOUT_MS
|
|
585
|
+
terminate the current task; the idle watchdog terminates it automatically after it has been
|
|
586
|
+
silent for `DSH_LARK_RUN_TIMEOUT_MS` (active streaming work is never cut short).
|
|
585
587
|
- **First QR binding fails**: make sure the local clock is accurate and the Feishu open platform
|
|
586
588
|
is reachable; with an existing App ID/Secret you can skip scanning via `--app-id` /
|
|
587
589
|
`--app-secret`.
|
|
@@ -759,36 +761,42 @@ and relaunches the full profile on `/safemode exit`.
|
|
|
759
761
|
|
|
760
762
|
## 社区收录情况 | Community Listings
|
|
761
763
|
|
|
762
|
-
> 本项目的社区收录 / 推荐状态,随提交的更新请求持续维护。截至 v0.
|
|
763
|
-
> Community listing & recommendation status, kept current as update requests land. As of v0.
|
|
764
|
+
> 本项目的社区收录 / 推荐状态,随提交的更新请求持续维护。截至 v0.10.2:
|
|
765
|
+
> Community listing & recommendation status, kept current as update requests land. As of v0.10.2:
|
|
764
766
|
|
|
765
767
|
| 平台 Platform | 状态 Status | 说明 Notes |
|
|
766
768
|
| :--- | :--- | :--- |
|
|
767
|
-
| [awesome-dsh-plugins](https://github.com/AdamPlatin123/awesome-dsh-plugins) | ✅ 已收录 · 运行级可用<br>Listed · runtime-verified | 社区榜单标注 `✅ 运行级可用`,2026-08-14 agent
|
|
768
|
-
| [dshfind](https://dshfind.com/zh/plugins/PlutoKeating/dsh-lark-bot) | ✅ 已收录 · 详情页已上线<br>Listed · detail page live | 中英日韩四语详情页已上线(含安装命令与亮点),条目名称正常([issue #2](https://github.com/hikariming/dshfind/issues/2)
|
|
769
|
-
| [omdsh-dev/community](https://github.com/orgs/omdsh-dev/discussions/12) | ✅ 已提交收录申请<br>Submission submitted | `[Plugin]` 收录申请(Discussion #12)已通过;v0.8.0
|
|
769
|
+
| [awesome-dsh-plugins](https://github.com/AdamPlatin123/awesome-dsh-plugins) | ✅ 已收录 · 运行级可用<br>Listed · runtime-verified | 社区榜单标注 `✅ 运行级可用`,2026-08-14 agent 实测通过;收录条目更新至 v0.8.0([PR #127](https://github.com/AdamPlatin123/awesome-dsh-plugins/pull/127) 已合并),v0.10.1 同步已跟进([#139](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139) 最新评论)<br>Shown as `✅ 运行级可用` in the community leaderboard; agent-tested on 2026-08-14; v0.8.0 entry merged via [PR #127](https://github.com/AdamPlatin123/awesome-dsh-plugins/pull/127); v0.10.1 sync requested in [#139](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139) |
|
|
770
|
+
| [dshfind](https://dshfind.com/zh/plugins/PlutoKeating/dsh-lark-bot) | ✅ 已收录 · 详情页已上线<br>Listed · detail page live | 中英日韩四语详情页已上线(含安装命令与亮点),条目名称正常([issue #2](https://github.com/hikariming/dshfind/issues/2) 已关闭);v0.10.1 数据刷新已提交([issue #6](https://github.com/hikariming/dshfind/issues/6));顶部徽章 / 展示卡来自 dshfind<br>Four-language detail page is live (install command & highlights), entry name fixed ([issue #2](https://github.com/hikariming/dshfind/issues/2) closed); v0.10.1 data refresh requested in [issue #6](https://github.com/hikariming/dshfind/issues/6); the header badge / card comes from dshfind |
|
|
771
|
+
| [omdsh-dev/community](https://github.com/orgs/omdsh-dev/discussions/12) | ✅ 已提交收录申请<br>Submission submitted | `[Plugin]` 收录申请(Discussion #12)已通过;v0.8.0 更新、六项独家亮点与 v0.10.1 稳定性修复说明均已发布在该讨论<br>`[Plugin]` submission (Discussion #12) accepted; v0.8.0 update, the six-exclusive-highlights summary and the v0.10.1 stability-fix notes are all posted there |
|
|
770
772
|
|
|
771
773
|
**更新请求进度 / Update request status**:
|
|
772
774
|
|
|
773
775
|
- awesome-dsh-plugins 收录条目更新:[#127](https://github.com/AdamPlatin123/awesome-dsh-plugins/pull/127) — ✅ 已合并
|
|
774
776
|
- dshfind 数据刷新请求(含条目名称异常修正):[#2](https://github.com/hikariming/dshfind/issues/2) — ✅ 已关闭,详情页已更新
|
|
775
777
|
- omdsh-dev/community 收录讨论更新:[Discussion #12 更新评论](https://github.com/orgs/omdsh-dev/discussions/12#discussioncomment-18021998) — ✅ 已发布
|
|
778
|
+
- awesome-dsh-plugins 榜单行同步至 v0.10.1:[#139 最新评论](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139#issuecomment-5304291100) — 📨 已提交
|
|
779
|
+
- dshfind 数据刷新至 v0.10.1:[#6](https://github.com/hikariming/dshfind/issues/6) — 📨 已提交
|
|
780
|
+
- omdsh-dev/community v0.10.1 更新说明:[Discussion #12 评论](https://github.com/omdsh-dev/community/discussions/12#discussioncomment-18034706) — ✅ 已发布
|
|
776
781
|
|
|
777
782
|
**Update requests**:
|
|
778
783
|
|
|
779
784
|
- awesome-dsh-plugins entry refresh: [#127](https://github.com/AdamPlatin123/awesome-dsh-plugins/pull/127) — ✅ merged
|
|
780
785
|
- dshfind data-refresh request (incl. fixing the entry name): [#2](https://github.com/hikariming/dshfind/issues/2) — ✅ closed, detail page updated
|
|
781
786
|
- omdsh-dev/community listing update: [Discussion #12 update comment](https://github.com/orgs/omdsh-dev/discussions/12#discussioncomment-18021998) — ✅ posted
|
|
787
|
+
- awesome-dsh-plugins leaderboard sync to v0.10.1: [#139 comment](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139#issuecomment-5304291100) — 📨 submitted
|
|
788
|
+
- dshfind data refresh to v0.10.1: [#6](https://github.com/hikariming/dshfind/issues/6) — 📨 submitted
|
|
789
|
+
- omdsh-dev/community v0.10.1 update: [Discussion #12 comment](https://github.com/omdsh-dev/community/discussions/12#discussioncomment-18034706) — ✅ posted
|
|
782
790
|
|
|
783
791
|
**亮点跟进 / Highlights follow-ups**(六项独家能力与 issue #6 设计实现):
|
|
784
792
|
|
|
785
|
-
- awesome-dsh-plugins 榜单行同步(仓库描述 →
|
|
793
|
+
- awesome-dsh-plugins 榜单行同步(仓库描述 → 最新)与 agent-test 报告名称异常:[#139](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139) — 📨 已提交(维护方已确认,等待渲染周期同步)
|
|
786
794
|
- dshfind 详情页补「对话内管理模型和密钥」亮点:[#2 跟进评论](https://github.com/hikariming/dshfind/issues/2#issuecomment-5301019067) — 📨 已提交
|
|
787
795
|
- omdsh 六项独家亮点补充(含 Guardian 设计实现):[Discussion #12 亮点评论](https://github.com/orgs/omdsh-dev/discussions/12#discussioncomment-18026370) — 📨 已提交
|
|
788
796
|
|
|
789
797
|
**Highlights follow-ups** (six exclusive capabilities & the issue #6 design):
|
|
790
798
|
|
|
791
|
-
- awesome-dsh-plugins leaderboard row sync (repo description →
|
|
799
|
+
- awesome-dsh-plugins leaderboard row sync (repo description → latest) & agent-test name anomaly: [#139](https://github.com/AdamPlatin123/awesome-dsh-plugins/issues/139) — 📨 submitted (maintainer confirmed; awaiting the snapshot/render cycle)
|
|
792
800
|
- dshfind detail page: add the in-chat model/key management highlight: [#2 follow-up](https://github.com/hikariming/dshfind/issues/2#issuecomment-5301019067) — 📨 submitted
|
|
793
801
|
- omdsh six-exclusive-highlights summary (incl. the Guardian design): [Discussion #12 highlights comment](https://github.com/orgs/omdsh-dev/discussions/12#discussioncomment-18026370) — 📨 submitted
|
|
794
802
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# dsh-lark-bot as a profile bundle.
|
|
2
|
-
|
|
3
|
-
# Installed with `dsh plugin --profile <name> add dsh-lark-bot` (or the
|
|
4
|
-
#
|
|
2
|
+
#
|
|
3
|
+
# Installed with `dsh plugin --profile <name> add dsh-lark-bot` (or the single
|
|
4
|
+
# `dsh-lark-bot setup` command), dsh appends this package to the profile's
|
|
5
5
|
# `dsh.profile.bundles` and applies this patch on boot:
|
|
6
6
|
# - `dsh-lark-bot/plugin` starts the full bridge engine IN-PROCESS (Feishu
|
|
7
7
|
# channel, workspace/session layers, notify server, nested dsh SDK
|
|
8
8
|
# runtime) and exposes `ctx.larkBridge` (status / stop).
|
|
9
|
-
# - `dsh-lark-bot/notify` mounts the `lark_notify` tool for the host agent
|
|
9
|
+
# - `dsh-lark-bot/notify` mounts the `lark_notify` tool for the host agent
|
|
10
|
+
# (mentions + cross-session messages).
|
|
10
11
|
# First boot without credentials prints a QR code for one-time binding.
|
|
11
12
|
# Set DSH_LARK_DISABLED=1 to keep the engine stopped.
|
|
12
13
|
|
package/dist/cli.js
CHANGED
|
@@ -1146,6 +1146,7 @@ function waitWithTimeout(promise, timeoutMs) {
|
|
|
1146
1146
|
var SdkDshAdapter = class {
|
|
1147
1147
|
id = "dsh-sdk";
|
|
1148
1148
|
displayName = "DeepSeek Harness (SDK)";
|
|
1149
|
+
resumeCapable = true;
|
|
1149
1150
|
launch;
|
|
1150
1151
|
provider;
|
|
1151
1152
|
model;
|
|
@@ -2901,7 +2902,41 @@ async function runAgentBatch(input) {
|
|
|
2901
2902
|
const workspace = input.workspaceManager ? await input.workspaceManager.ensure(input.scope, requestedCwd) : { cwd: requestedCwd };
|
|
2902
2903
|
const cwd = workspace.cwd;
|
|
2903
2904
|
const sessionId = activeBefore === 0 ? input.sessions.resumeFor(input.scope, cwd) : void 0;
|
|
2904
|
-
const
|
|
2905
|
+
const resuming = sessionId !== void 0 && input.adapter.resumeCapable === true;
|
|
2906
|
+
try {
|
|
2907
|
+
await runAttempt(input, cwd, sessionId, resuming, replyOptions);
|
|
2908
|
+
} catch (error) {
|
|
2909
|
+
if (resuming) {
|
|
2910
|
+
log.warn("run-flow", "resume-fallback", { scope: input.scope, sessionId });
|
|
2911
|
+
input.sessions.clearSession(input.scope);
|
|
2912
|
+
try {
|
|
2913
|
+
await runAttempt(input, cwd, void 0, false, replyOptions);
|
|
2914
|
+
return;
|
|
2915
|
+
} catch (retryError) {
|
|
2916
|
+
log.fail("run-flow", retryError, {
|
|
2917
|
+
scope: input.scope,
|
|
2918
|
+
step: "resume-fallback"
|
|
2919
|
+
});
|
|
2920
|
+
await reportRunFailure(input, retryError, replyOptions);
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
await reportRunFailure(input, error, replyOptions);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
async function reportRunFailure(input, error, replyOptions) {
|
|
2928
|
+
log.fail("run-flow", error, { scope: input.scope });
|
|
2929
|
+
try {
|
|
2930
|
+
await input.channel.sendMarkdown(
|
|
2931
|
+
input.chatId,
|
|
2932
|
+
`\u26A0\uFE0F agent \u8FD0\u884C\u5931\u8D25\uFF1A${errorMessage(error)}`,
|
|
2933
|
+
replyOptions
|
|
2934
|
+
);
|
|
2935
|
+
} catch {
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
async function runAttempt(input, cwd, sessionId, resuming, replyOptions) {
|
|
2939
|
+
const history = resuming ? [] : input.sessions.historyFor(input.scope, cwd);
|
|
2905
2940
|
const prompt = buildPrompt(history, input.messages, input.role);
|
|
2906
2941
|
const runId = randomUUID4();
|
|
2907
2942
|
const run = input.adapter.run({
|
|
@@ -2932,12 +2967,15 @@ async function runAgentBatch(input) {
|
|
|
2932
2967
|
const timeoutMs = input.runPolicies?.get(input.scope) ?? input.runTimeoutMs ?? 0;
|
|
2933
2968
|
let timedOut = false;
|
|
2934
2969
|
let assistantOutput = "";
|
|
2970
|
+
let sawActivity = false;
|
|
2935
2971
|
const density = input.densityStore?.get(input.scope) ?? "standard";
|
|
2936
2972
|
try {
|
|
2937
2973
|
await input.channel.streamCard(
|
|
2938
2974
|
input.chatId,
|
|
2939
2975
|
renderCard(state, density),
|
|
2940
2976
|
async (controller) => {
|
|
2977
|
+
let timeoutTimer;
|
|
2978
|
+
let armTimeout;
|
|
2941
2979
|
const ticker = setInterval(() => {
|
|
2942
2980
|
void controller.update(renderCard(state, density, Date.now())).catch(() => {
|
|
2943
2981
|
});
|
|
@@ -2956,13 +2994,16 @@ async function runAgentBatch(input) {
|
|
|
2956
2994
|
if (event.type === "system" && event.sessionId) {
|
|
2957
2995
|
input.sessions.set(input.scope, event.sessionId, event.cwd ?? cwd);
|
|
2958
2996
|
}
|
|
2997
|
+
if (event.type !== "system" && event.type !== "error") {
|
|
2998
|
+
sawActivity = true;
|
|
2999
|
+
}
|
|
3000
|
+
armTimeout?.();
|
|
2959
3001
|
await controller.update(renderCard(state, density));
|
|
2960
3002
|
}
|
|
2961
3003
|
};
|
|
2962
|
-
let timeoutTimer;
|
|
2963
|
-
let armTimeout;
|
|
2964
3004
|
const timeoutPromise = timeoutMs > 0 ? new Promise((resolve5) => {
|
|
2965
3005
|
armTimeout = () => {
|
|
3006
|
+
if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
|
|
2966
3007
|
timeoutTimer = setTimeout(() => {
|
|
2967
3008
|
if (timedOut) return;
|
|
2968
3009
|
if (input.questions?.pendingCount(input.scope)) {
|
|
@@ -2997,6 +3038,9 @@ async function runAgentBatch(input) {
|
|
|
2997
3038
|
},
|
|
2998
3039
|
replyOptions
|
|
2999
3040
|
);
|
|
3041
|
+
if (resuming && state.terminal === "error" && !sawActivity) {
|
|
3042
|
+
throw new Error(state.errorMsg ?? "native session resume failed");
|
|
3043
|
+
}
|
|
3000
3044
|
input.sessions.recordExchange(input.scope, cwd, input.messages, assistantOutput, {
|
|
3001
3045
|
...input.retention === void 0 ? {} : { retention: input.retention },
|
|
3002
3046
|
...input.archiver ? {
|
|
@@ -3008,15 +3052,6 @@ async function runAgentBatch(input) {
|
|
|
3008
3052
|
}).then(() => pruneArchives(input))
|
|
3009
3053
|
} : {}
|
|
3010
3054
|
});
|
|
3011
|
-
} catch (error) {
|
|
3012
|
-
log.fail("run-flow", error, { scope: input.scope, runId });
|
|
3013
|
-
state = markInterrupted(state);
|
|
3014
|
-
try {
|
|
3015
|
-
await input.channel.sendMarkdown(input.chatId, `\u26A0\uFE0F agent \u8FD0\u884C\u5931\u8D25\uFF1A${errorMessage(error)}`, {
|
|
3016
|
-
...replyOptions
|
|
3017
|
-
});
|
|
3018
|
-
} catch {
|
|
3019
|
-
}
|
|
3020
3055
|
} finally {
|
|
3021
3056
|
input.activeRuns.delete(input.scope, runId);
|
|
3022
3057
|
if (input.approvals) {
|
|
@@ -4159,7 +4194,7 @@ var HELP = [
|
|
|
4159
4194
|
"- `/status` \u2014 \u67E5\u770B\u5F53\u524D\u72B6\u6001",
|
|
4160
4195
|
"- `/resume` \u2014 \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u6700\u8FD1\u4E0A\u4E0B\u6587",
|
|
4161
4196
|
"- `/stop` \u2014 \u7EC8\u6B62\u5F53\u524D\u4EFB\u52A1",
|
|
4162
|
-
"- `/timeout [N|off|default]` \u2014 \u67E5\u770B\u6216\u8BBE\u7F6E\u5F53\u524D\u4F1A\u8BDD\
|
|
4197
|
+
"- `/timeout [N|off|default]` \u2014 \u67E5\u770B\u6216\u8BBE\u7F6E\u5F53\u524D\u4F1A\u8BDD\u7A7A\u95F2\u8D85\u65F6\uFF08\u6301\u7EED\u65E0\u6D3B\u52A8\u4E8B\u4EF6 N \u5206\u949F\u624D\u7EC8\u6B62\uFF09",
|
|
4163
4198
|
"- `/concurrency [N|default]` \u2014 \u67E5\u770B\u6216\u8BBE\u7F6E\u5F53\u524D scope \u7684\u5E76\u884C\u4EFB\u52A1\u6570",
|
|
4164
4199
|
"- `/role list|show <id>|set <id>|clear` \u2014 \u67E5\u770B / \u7ED1\u5B9A\u89D2\u8272",
|
|
4165
4200
|
"- `/role save <id> <name> [--persona \u6587\u6848] [--model <id>] [--tools <csv>] [--rules \u6587\u6848]` \u2014 \u521B\u5EFA/\u66F4\u65B0\u89D2\u8272\uFF08\u7BA1\u7406\u5458\uFF09",
|
|
@@ -4314,18 +4349,18 @@ async function handleTimeout(args, ctx) {
|
|
|
4314
4349
|
const minutes2 = effectiveMs > 0 ? Math.round(effectiveMs / 6e4) : 0;
|
|
4315
4350
|
await reply5(
|
|
4316
4351
|
ctx,
|
|
4317
|
-
minutes2 > 0 ? `\u5F53\u524D\u4F1A\u8BDD\
|
|
4352
|
+
minutes2 > 0 ? `\u5F53\u524D\u4F1A\u8BDD\u7A7A\u95F2\u8D85\u65F6\uFF1A\u6301\u7EED\u65E0\u6D3B\u52A8\u4E8B\u4EF6 ${minutes2} \u5206\u949F\u624D\u7EC8\u6B62\u3002\u53EF\u7528 \`/timeout <N|off|default>\` \u8C03\u6574\u3002` : "\u5F53\u524D\u4F1A\u8BDD\u7A7A\u95F2\u8D85\u65F6\uFF1A\u5173\u95ED\u3002"
|
|
4318
4353
|
);
|
|
4319
4354
|
return;
|
|
4320
4355
|
}
|
|
4321
4356
|
if (input === "off") {
|
|
4322
4357
|
ctx.runPolicies.set(ctx.scope, 0);
|
|
4323
|
-
await reply5(ctx, "\u5DF2\u5173\u95ED\u5F53\u524D\u4F1A\u8BDD\
|
|
4358
|
+
await reply5(ctx, "\u5DF2\u5173\u95ED\u5F53\u524D\u4F1A\u8BDD\u7A7A\u95F2\u8D85\u65F6\u3002");
|
|
4324
4359
|
return;
|
|
4325
4360
|
}
|
|
4326
4361
|
if (input === "default") {
|
|
4327
4362
|
ctx.runPolicies.clear(ctx.scope);
|
|
4328
|
-
await reply5(ctx, "\u5DF2\u6062\u590D\u9ED8\u8BA4\
|
|
4363
|
+
await reply5(ctx, "\u5DF2\u6062\u590D\u9ED8\u8BA4\u7A7A\u95F2\u8D85\u65F6\u3002");
|
|
4329
4364
|
return;
|
|
4330
4365
|
}
|
|
4331
4366
|
const minutes = Number(input);
|
|
@@ -4334,7 +4369,7 @@ async function handleTimeout(args, ctx) {
|
|
|
4334
4369
|
return;
|
|
4335
4370
|
}
|
|
4336
4371
|
ctx.runPolicies.set(ctx.scope, minutes * 6e4);
|
|
4337
|
-
await reply5(ctx, `\u5DF2\u8BBE\u7F6E\u5F53\u524D\u4F1A\u8BDD\
|
|
4372
|
+
await reply5(ctx, `\u5DF2\u8BBE\u7F6E\u5F53\u524D\u4F1A\u8BDD\u7A7A\u95F2\u8D85\u65F6\uFF1A\u6301\u7EED\u65E0\u6D3B\u52A8\u4E8B\u4EF6 ${minutes} \u5206\u949F\u624D\u7EC8\u6B62\u3002`);
|
|
4338
4373
|
}
|
|
4339
4374
|
async function handleConcurrency(args, ctx) {
|
|
4340
4375
|
const input = args.trim();
|
|
@@ -5180,6 +5215,16 @@ var SessionStore = class {
|
|
|
5180
5215
|
const record = this.data.chats[scopeId];
|
|
5181
5216
|
return record && record.cwd === cwd ? record.sessionId : void 0;
|
|
5182
5217
|
}
|
|
5218
|
+
/**
|
|
5219
|
+
* Drop the native dsh session binding for a scope. The transcript is kept so
|
|
5220
|
+
* the next run can start a fresh session and replay history from the store.
|
|
5221
|
+
*/
|
|
5222
|
+
clearSession(scopeId) {
|
|
5223
|
+
const existing = this.data.chats[scopeId];
|
|
5224
|
+
if (!existing) return;
|
|
5225
|
+
this.data.chats[scopeId] = { ...existing, sessionId: void 0 };
|
|
5226
|
+
this.schedulePersist();
|
|
5227
|
+
}
|
|
5183
5228
|
async flush() {
|
|
5184
5229
|
await this.pendingArchive;
|
|
5185
5230
|
await this.saving;
|
|
@@ -7158,6 +7203,7 @@ var GuardianService = class {
|
|
|
7158
7203
|
let errorText;
|
|
7159
7204
|
let timedOut = false;
|
|
7160
7205
|
let events = 0;
|
|
7206
|
+
let sawActivity = false;
|
|
7161
7207
|
const run = engine.adapter.run({
|
|
7162
7208
|
runId,
|
|
7163
7209
|
prompt,
|
|
@@ -7185,6 +7231,7 @@ var GuardianService = class {
|
|
|
7185
7231
|
}, SAFE_CARD_TICK_MS);
|
|
7186
7232
|
ticker.unref?.();
|
|
7187
7233
|
let timeoutTimer;
|
|
7234
|
+
let armTimeout;
|
|
7188
7235
|
try {
|
|
7189
7236
|
const consume = async () => {
|
|
7190
7237
|
for await (const event of run.events) {
|
|
@@ -7201,20 +7248,28 @@ var GuardianService = class {
|
|
|
7201
7248
|
this.sessionIds.set(scope, event.sessionId);
|
|
7202
7249
|
}
|
|
7203
7250
|
if (event.type === "error") errorText = event.message;
|
|
7251
|
+
if (event.type !== "system" && event.type !== "error") {
|
|
7252
|
+
sawActivity = true;
|
|
7253
|
+
}
|
|
7254
|
+
armTimeout?.();
|
|
7204
7255
|
await controller.update(renderCard(state, density, Date.now()));
|
|
7205
7256
|
}
|
|
7206
7257
|
};
|
|
7207
7258
|
const timeoutPromise = timeoutMs > 0 ? new Promise((resolve5) => {
|
|
7208
|
-
|
|
7209
|
-
if (
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7259
|
+
armTimeout = () => {
|
|
7260
|
+
if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
|
|
7261
|
+
timeoutTimer = setTimeout(() => {
|
|
7262
|
+
if (timedOut) return;
|
|
7263
|
+
timedOut = true;
|
|
7264
|
+
state = markIdleTimeout(
|
|
7265
|
+
state,
|
|
7266
|
+
Math.round(timeoutMs / 6e4)
|
|
7267
|
+
);
|
|
7268
|
+
void run.stop();
|
|
7269
|
+
resolve5();
|
|
7270
|
+
}, timeoutMs);
|
|
7271
|
+
};
|
|
7272
|
+
armTimeout();
|
|
7218
7273
|
}) : void 0;
|
|
7219
7274
|
if (timeoutPromise) {
|
|
7220
7275
|
await Promise.race([consume(), timeoutPromise]);
|
|
@@ -7259,6 +7314,9 @@ var GuardianService = class {
|
|
|
7259
7314
|
...errorText === void 0 ? {} : { errorText },
|
|
7260
7315
|
outputLength: (finalText ?? assistantOutput).trim().length
|
|
7261
7316
|
});
|
|
7317
|
+
if (terminal === "error" && !sawActivity) {
|
|
7318
|
+
this.sessionIds.delete(scope);
|
|
7319
|
+
}
|
|
7262
7320
|
if (timedOut || terminal === "interrupted" || terminal === "error" || terminal === "idle_timeout") {
|
|
7263
7321
|
return;
|
|
7264
7322
|
}
|