feat(tasks): wake an idle owner when a background task completes
Completion notices went through agent.inject(), which never reserves a driver, so a task settling after its turn closed left the notice parked until unrelated input woke the agent — while the same prompt told the model not to poll for it. An unreported completion now picks its lane from the owner's state: a busy owner is injected as before, an idle owner is woken with followup(). This adopts the delivery rule the subagent continuation manager already ships. maxConsecutiveWakes bounds the self-exciting chain and is reset by user-authored input; completionDelivery: quiet restores the old lane for deterministic transcripts. Teardown cancellation now claims the terminal report the way kill() already does, so an owner being destroyed is never woken, and settle() announces completion last so a reporter that opens a turn synchronously sees a committed record.
This commit is contained in:
@@ -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: 36ad950428f02f0147c465b633aff8e9ccd4c002
|
||||
config-catalog.zh.md: adfa319e4d753e03993e607d333df46745c20370
|
||||
config-catalog.md: cd92d41ce7dd4966fa54bcd27dd554674ef05cc5
|
||||
config-catalog.zh.md: 41db6f9c1310b7a83ba785f2f350fbca12fbc3ba
|
||||
|
||||
+18
-2
@@ -2379,16 +2379,32 @@ Source: [`packages/subagent/tool-subagent-report/src/index.ts:27`](../packages/s
|
||||
Requires: `tools` · `tasks` · `systemPrompt`
|
||||
|
||||
```ts config-catalog
|
||||
/** Configures bounded `task_output` waits. */
|
||||
/** Configures bounded `task_output` waits and completion-notice delivery. */
|
||||
export interface Config {
|
||||
/** Wait duration applied when `task_output` sets `wait` without `timeout_ms` (default 30s). */
|
||||
waitTimeoutMs?: number
|
||||
/** Hard cap on any single wait; a larger model-supplied `timeout_ms` is clamped down to it (default 10min). */
|
||||
maxWaitTimeoutMs?: number
|
||||
/** Whether a completion opens a turn on an idle owner (default `wakeup`). */
|
||||
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 task whose
|
||||
* completion wakes it again.
|
||||
*/
|
||||
maxConsecutiveWakes?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* How an unreported completion reaches an owner that is already idle: `wakeup`
|
||||
* opens a turn for it, `quiet` leaves it pending until something else wakes the
|
||||
* owner. A busy owner is injected either way.
|
||||
*/
|
||||
export type CompletionDelivery = 'quiet' | 'wakeup'
|
||||
```
|
||||
|
||||
Source: [`packages/tasks/tool-tasks/src/index.ts:23`](../packages/tasks/tool-tasks/src/index.ts)
|
||||
Source: [`packages/tasks/tool-tasks/src/index.ts:31`](../packages/tasks/tool-tasks/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-tool-todo`
|
||||
|
||||
|
||||
@@ -2380,16 +2380,32 @@ export interface Config {
|
||||
需要:`tools` · `tasks` · `systemPrompt`
|
||||
|
||||
```ts config-catalog
|
||||
/** Configures bounded `task_output` waits. */
|
||||
/** Configures bounded `task_output` waits and completion-notice delivery. */
|
||||
export interface Config {
|
||||
/** Wait duration applied when `task_output` sets `wait` without `timeout_ms` (default 30s). */
|
||||
waitTimeoutMs?: number
|
||||
/** Hard cap on any single wait; a larger model-supplied `timeout_ms` is clamped down to it (default 10min). */
|
||||
maxWaitTimeoutMs?: number
|
||||
/** Whether a completion opens a turn on an idle owner (default `wakeup`). */
|
||||
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 task whose
|
||||
* completion wakes it again.
|
||||
*/
|
||||
maxConsecutiveWakes?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* How an unreported completion reaches an owner that is already idle: `wakeup`
|
||||
* opens a turn for it, `quiet` leaves it pending until something else wakes the
|
||||
* owner. A busy owner is injected either way.
|
||||
*/
|
||||
export type CompletionDelivery = 'quiet' | 'wakeup'
|
||||
```
|
||||
|
||||
来源:[`packages/tasks/tool-tasks/src/index.ts:23`](../packages/tasks/tool-tasks/src/index.ts)
|
||||
来源:[`packages/tasks/tool-tasks/src/index.ts:31`](../packages/tasks/tool-tasks/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-tool-todo`
|
||||
|
||||
|
||||
@@ -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/event-producer-consumer.md
|
||||
event-producer-consumer.md: e14bad2b7a0f604f101281e271c4250a60b8c8eb
|
||||
event-producer-consumer.zh.md: 81ced8b25dc43a4f0d37b73ea954b1e4a997bdf9
|
||||
event-producer-consumer.md: 59d81478edca9ebc83368157ab76ff24fca20ec2
|
||||
event-producer-consumer.zh.md: 91e0bf3ff5b20ffd68c641992ed98f5e2c609697
|
||||
|
||||
@@ -11,7 +11,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/runtime-types.ts:159`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-presets`](../packages/preset/agent-presets), [`goal-session`](../packages/goal/goal-session) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:168`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `apiproxy`, [`goal-session`](../packages/goal/goal-session), [`session-telemetry`](../packages/session/session-telemetry) |
|
||||
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:197`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:197`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent), [`tool-tasks`](../packages/tasks/tool-tasks) |
|
||||
| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:205`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:186`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
|
||||
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:231`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/runtime-types.ts:159`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-presets`](../packages/preset/agent-presets), [`goal-session`](../packages/goal/goal-session) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:168`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `apiproxy`, [`goal-session`](../packages/goal/goal-session), [`session-telemetry`](../packages/session/session-telemetry) |
|
||||
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:197`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:197`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent), [`tool-tasks`](../packages/tasks/tool-tasks) |
|
||||
| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:205`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session), [`subagent`](../packages/subagent/subagent) |
|
||||
| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:186`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
|
||||
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:231`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
|
||||
|
||||
@@ -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/subsystems/tasks.md
|
||||
tasks.md: 2798eb2963e83d42321a343d3c0a6a2bb658be41
|
||||
tasks.zh.md: 8d4539514cb4bc654dd86031d51417c4f79f8982
|
||||
tasks.md: 37807bc446f607c3e1635a49432670e86760f2a4
|
||||
tasks.zh.md: 39014acb13f6051431a40ae084c1e03298ae8993
|
||||
|
||||
@@ -97,7 +97,7 @@ interface TaskOutcome {
|
||||
|
||||
## Consumer views
|
||||
|
||||
Snapshots are fresh read-only projections. `ownerSession` carries the shared `SessionId` used for authorization; completion listeners separately receive the exact owner object used for lifecycle cleanup. `reported` suppresses a completion notice after another reporter has delivered or committed to deliver the terminal state.
|
||||
Snapshots are fresh read-only projections. `ownerSession` carries the shared `SessionId` used for authorization; completion listeners separately receive the exact owner object used for lifecycle cleanup. `reported` suppresses a completion notice after another reporter has delivered or committed to deliver the terminal state, including the teardown cancel that drains an owner or the service.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -128,8 +128,11 @@ interface TaskSnapshot {
|
||||
/** Epoch ms when the task settled; absent while `running`/`stopping`. */
|
||||
finishedAt?: number
|
||||
/**
|
||||
* True when a kill, read, or wait has reported or committed to report the
|
||||
* terminal state. Completion reporters suppress redundant notices when set.
|
||||
* True when a kill, read, wait, or teardown cancel has reported or committed
|
||||
* to report the terminal state. Completion reporters suppress redundant
|
||||
* notices when set. Teardown claims it because the owner or service being
|
||||
* destroyed leaves no reader: a reporter that opens a turn on notice would
|
||||
* otherwise spend a model request per teardown layer.
|
||||
*/
|
||||
reported: boolean
|
||||
}
|
||||
@@ -169,9 +172,9 @@ Abstract background task registry. Subclass, implement the abstract methods, and
|
||||
|
||||
Implementations must honor these semantics:
|
||||
|
||||
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record.
|
||||
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record. Teardown cancellation also marks the record reported, because a record its owner is being destroyed for has no reader left.
|
||||
- Owned-task access is fenced by the owner's session id. Ids are predictable, so authorization — not secrecy — is the boundary.
|
||||
- Settlement is first-wins: one terminal record, one round of contained listener notification, and released waiters, even against a late producer outcome.
|
||||
- Settlement is first-wins: one terminal record, released waiters, and one round of contained listener notification, even against a late producer outcome. Completion is announced last, after the record is committed and every other observer of the settlement has seen it, because a reporter may open a model turn synchronously.
|
||||
- start refuses work while no attached task controller serves the spec's owner, so a producer cannot start work that owner cannot collect or stop. One registry serves every composition in the process, so this question — and completion-listener delivery — is owner-relative rather than process-wide: registrations made from an unscoped context serve every owner, and registrations made under an agent composition's scope serve exactly the agents composed under it.
|
||||
|
||||
```ts cordis-catalog
|
||||
@@ -282,5 +285,5 @@ abstract attachController(name: string): () => void
|
||||
|
||||
Types: [Agent](core.md)
|
||||
|
||||
Source: [`packages/tasks/tasks/src/index.ts:58`](../../packages/tasks/tasks/src/index.ts)
|
||||
Source: [`packages/tasks/tasks/src/index.ts:62`](../../packages/tasks/tasks/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -97,7 +97,7 @@ interface TaskOutcome {
|
||||
|
||||
## 消费方视图
|
||||
|
||||
快照是每次新建的只读投影。`ownerSession` 携带用于授权的共享 `SessionId`;完成监听器则会另行收到用于生命周期清理的确切拥有者对象。另一个接口已经交付终止状态或承诺交付时,`reported` 会抑制完成通知。
|
||||
快照是每次新建的只读投影。`ownerSession` 携带用于授权的共享 `SessionId`;完成监听器则会另行收到用于生命周期清理的确切拥有者对象。另一个接口已经交付终止状态或承诺交付时,`reported` 会抑制完成通知;排空 owner 或服务的 teardown 取消同样计入。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -128,8 +128,11 @@ interface TaskSnapshot {
|
||||
/** Epoch ms when the task settled; absent while `running`/`stopping`. */
|
||||
finishedAt?: number
|
||||
/**
|
||||
* True when a kill, read, or wait has reported or committed to report the
|
||||
* terminal state. Completion reporters suppress redundant notices when set.
|
||||
* True when a kill, read, wait, or teardown cancel has reported or committed
|
||||
* to report the terminal state. Completion reporters suppress redundant
|
||||
* notices when set. Teardown claims it because the owner or service being
|
||||
* destroyed leaves no reader: a reporter that opens a turn on notice would
|
||||
* otherwise spend a model request per teardown layer.
|
||||
*/
|
||||
reported: boolean
|
||||
}
|
||||
@@ -169,9 +172,9 @@ Abstract background task registry. Subclass, implement the abstract methods, and
|
||||
|
||||
Implementations must honor these semantics:
|
||||
|
||||
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record.
|
||||
- Registrations outlive producer and controller fibers. Owner and service disposal cancel live work and await compliant producers; a throwing teardown cancel force-fails only the record. Teardown cancellation also marks the record reported, because a record its owner is being destroyed for has no reader left.
|
||||
- Owned-task access is fenced by the owner's session id. Ids are predictable, so authorization — not secrecy — is the boundary.
|
||||
- Settlement is first-wins: one terminal record, one round of contained listener notification, and released waiters, even against a late producer outcome.
|
||||
- Settlement is first-wins: one terminal record, released waiters, and one round of contained listener notification, even against a late producer outcome. Completion is announced last, after the record is committed and every other observer of the settlement has seen it, because a reporter may open a model turn synchronously.
|
||||
- start refuses work while no attached task controller serves the spec's owner, so a producer cannot start work that owner cannot collect or stop. One registry serves every composition in the process, so this question — and completion-listener delivery — is owner-relative rather than process-wide: registrations made from an unscoped context serve every owner, and registrations made under an agent composition's scope serve exactly the agents composed under it.
|
||||
|
||||
```ts cordis-catalog
|
||||
@@ -282,5 +285,5 @@ abstract attachController(name: string): () => void
|
||||
|
||||
Types: [Agent](core.md)
|
||||
|
||||
Source: [`packages/tasks/tasks/src/index.ts:58`](../../packages/tasks/tasks/src/index.ts)
|
||||
Source: [`packages/tasks/tasks/src/index.ts:62`](../../packages/tasks/tasks/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
Reference in New Issue
Block a user