From b6775f6d4f55eaa0bcac36318cd7dfc98d70195c Mon Sep 17 00:00:00 2001 From: turtle1999 Date: Tue, 22 Sep 2026 15:29:23 +0800 Subject: [PATCH 1/2] fix(tool-jobs): wake an idle owner for every completion by default `maxConsecutiveWakes` no longer defaults to 3. Unset, every background job completion wakes its idle owner; the cap is opt-in. With the default cap, a session chaining background commands and one-shot subagents exhausted the budget after three wakes. The fourth notice was injected into the next-step inbox without waking the owner, the client showed nothing, and the session stalled until the user typed something (dsh-external/issues#641). Closes dsh-external/issues#641 --- ...b-completion-wakes-an-idle-owner.i18n.yaml | 4 +-- ...ound-job-completion-wakes-an-idle-owner.md | 2 ++ ...d-job-completion-wakes-an-idle-owner.zh.md | 2 ++ docs/config-catalog.i18n.yaml | 4 +-- docs/config-catalog.md | 8 +++-- docs/config-catalog.zh.md | 8 +++-- packages/jobs/tool-jobs/README.i18n.yaml | 4 +-- packages/jobs/tool-jobs/README.md | 12 ++++---- packages/jobs/tool-jobs/README.zh.md | 12 ++++---- packages/jobs/tool-jobs/src/index.ts | 29 ++++++++++--------- .../jobs/tool-jobs/tests/tool-jobs.spec.ts | 19 ++++++++++-- 11 files changed, 64 insertions(+), 40 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.i18n.yaml b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.i18n.yaml index 0c51d4b42e..a38790ad6e 100644 --- a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.md -2026-08-11-background-job-completion-wakes-an-idle-owner.md: b8a10a2150ead02036cb97506a25c3a51202adcf -2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md: adc0770cd00adeb3b13c95dcc386464493dcd943 +2026-08-11-background-job-completion-wakes-an-idle-owner.md: 0844948eaa8e58e9d22667eb0962865a17969c07 +2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md: d09454d17b8f1b579305cc1e98540d5ecb2f2d4f diff --git a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.md b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.md index b8a10a2150..0844948eaa 100644 --- a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.md +++ b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.md @@ -4,6 +4,8 @@ Status: implemented Update: the `reported` bit and `onJobDone` this note relies on left the registry with the [jobs seam consolidation](../architecture/2026-09-03-jobs-seam-consolidation.md): `dsh-tool-jobs` now keeps the delivery ledger (a wait or an accepted `job_kill` claims the task) and skips teardown settlements by their `settled` cause, so the wake-or-inject decision below stands while the mechanism it names is the tool's ledger, not a registry flag. +Update: `maxConsecutiveWakes` no longer defaults to 3; unset, every idle completion wakes its owner. The accepted risk below — a spent budget is restored only by user input — was reported as a bug ([dsh-external/issues#641](https://github.com/dsh-external/issues/issues/641)): a session mixing background commands and one-shot subagents exhausted the budget after three wakes, the fourth notice parked silently in the next-step inbox with nothing visible in the client, and the model's "you are notified when a job finishes" promise turned false exactly when the user was away. An invisible stall costs more than an unbounded chain of model requests the user can watch and stop, so the cap is opt-in. The self-exciting rationale for the field stands for deployments that set it. + English | [中文](2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md) ## Problem diff --git a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md index adc0770cd0..d09454d17b 100644 --- a/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md +++ b/.agents/notes/implemented/feature/2026-08-11-background-job-completion-wakes-an-idle-owner.zh.md @@ -4,6 +4,8 @@ Status: implemented Update:本文依赖的 `reported` 位与 `onJobDone` 已随 [jobs seam 收敛](../architecture/2026-09-03-jobs-seam-consolidation.zh.md)离开注册表:`dsh-tool-jobs` 现在持有投递台账(等待或被接受的 `job_kill` 认领任务),并按 `settled` 事件的 cause 跳过 teardown 结算,因此下文唤醒或注入的决策仍然成立,只是它提到的机制是工具的台账而非注册表标志。 +Update:`maxConsecutiveWakes` 不再默认为 3;未设置时每次空闲完成都会唤醒所有者。下文"已花掉的预算只由用户输入恢复"这条接受的风险被报告为缺陷([dsh-external/issues#641](https://github.com/dsh-external/issues/issues/641)):一个会话混用后台命令与一次性子代理,三次唤醒后预算耗尽,第四条通知静默停在 next-step inbox 里,客户端毫无显示,而模型得到的"任务完成会通知你"承诺恰好在用户离开时失效。不可见的停摆比一条用户能看到并能停止的、无上限的模型请求链代价更高,因此上限改为可选。自激链的理由对设置了该字段的部署依然成立。 + [English](2026-08-11-background-job-completion-wakes-an-idle-owner.md) | 中文 ## 问题 diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index 22a29bf6ac..5b3de07b40 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: f86f02fbec949c0212c3c038f47d1fdf50d239fe -config-catalog.zh.md: d56d5bdef41e76ce7a41e3d582a1a8afde18766b +config-catalog.md: a9616098f2e5adfb7061bec0dc8d66c7600f91e3 +config-catalog.zh.md: d53d3a93923c45e04820c10204b8380feb2d2da2 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index f86f02fbec..a9616098f2 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -3405,9 +3405,11 @@ export interface Config { completionDelivery?: CompletionDelivery /** * Turns one owner may have opened by completion wakes before the next - * notice degrades to injection, reset by any user-authored input (default 3). - * Bounds the self-exciting chain where a woken turn starts the job whose - * completion wakes it again. + * notice degrades to injection, reset by any user-authored input. Absent by + * default: every idle completion wakes its owner. Set it to bound the + * self-exciting chain where a woken turn starts the job whose completion + * wakes it again, at the cost of notices past the cap waiting silently for + * the next user input. */ maxConsecutiveWakes?: number } diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index d56d5bdef4..d53d3a9392 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -3407,9 +3407,11 @@ export interface Config { completionDelivery?: CompletionDelivery /** * Turns one owner may have opened by completion wakes before the next - * notice degrades to injection, reset by any user-authored input (default 3). - * Bounds the self-exciting chain where a woken turn starts the job whose - * completion wakes it again. + * notice degrades to injection, reset by any user-authored input. Absent by + * default: every idle completion wakes its owner. Set it to bound the + * self-exciting chain where a woken turn starts the job whose completion + * wakes it again, at the cost of notices past the cap waiting silently for + * the next user input. */ maxConsecutiveWakes?: number } diff --git a/packages/jobs/tool-jobs/README.i18n.yaml b/packages/jobs/tool-jobs/README.i18n.yaml index f6b8dc12ed..d76b088fdc 100644 --- a/packages/jobs/tool-jobs/README.i18n.yaml +++ b/packages/jobs/tool-jobs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/jobs/tool-jobs/README.md -README.md: 38cf89961d3bbf13d803bef206cbe843726f31ab -README.zh.md: 9a6c1c01103f03c5e9915094714bcecadf473042 +README.md: 2836f10e160ae3ecfe9a2df7f6b82a88b4ddf33f +README.zh.md: b5b702ab5f8ca4c72545f3e46899a3f01624eb42 diff --git a/packages/jobs/tool-jobs/README.md b/packages/jobs/tool-jobs/README.md index 38cf89961d..2836f10e16 100644 --- a/packages/jobs/tool-jobs/README.md +++ b/packages/jobs/tool-jobs/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Use `dsh-tool-jobs` to inspect and control background commands, PTY work, and subagents through `job_output`, `job_list`, and `job_kill`. Reads can wait within a configured timeout, list results identify each job's kind and status, and cancellation settles only after the work stops. When owned work finishes, the agent receives an in-session notice: busy agents receive it in their next step, while idle agents may be woken by a bounded follow-up turn. Configuration controls wait limits, completion delivery, and consecutive wakeups. Stream output is consumed by one reader, and pending notices do not survive owner disposal. +Use `dsh-tool-jobs` to inspect and control background commands, PTY work, and subagents through `job_output`, `job_list`, and `job_kill`. Reads can wait within a configured timeout, list results identify each job's kind and status, and cancellation settles only after the work stops. When owned work finishes, the agent receives an in-session notice: busy agents receive it in their next step, while idle agents are woken by a follow-up turn. Configuration controls wait limits, completion delivery, and an optional cap on consecutive wakeups. Stream output is consumed by one reader, and pending notices do not survive owner disposal. ## Table of Contents @@ -39,7 +39,7 @@ The three tools return `{ text, job }`, `PublicJobSnapshot[]`, and `{ outcome: ' When a job finishes, the owning agent receives `background job (: