feat(bash): add an opaque owner token to the executor seam
Background-task ownership needs a stable home that survives a consumer HMR reload. Add an optional `owner?: string` to `BashExecRequest` and a required-but-nullable `owner: string | undefined` to the resolved `BashExecSpec` (mirroring how `workdir`/`timeoutMs` are required on the spec — a forgotten owner is a visible `undefined`, never a silently-absent property that yields an unowned, cross-session-readable task). `resolve()` carries it through. Expose the stored token via a new `BashExecutor.ownerOf(id): string | undefined` seam (ONE read path — not also on the public `BashTask`). The executor stores and returns the token verbatim and NEVER interprets it: the access POLICY lives in the consumer (`dsh-tool-bash`). `bash-local` stores `owner` on its `TrackedTask` and implements `ownerOf`; unknown-id and known-but-ownerless both read as `undefined`. Because ownership lives on the task in the executor (disposed with the `dsh-bash` fiber), it survives a `tool-bash` HMR reload. Updates the StubExecutor seam test and the bash/bash-local READMEs.
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
# RFC: Agent lifecycle and ownership seams
|
||||
|
||||
Status: proposed
|
||||
|
||||
## Problem
|
||||
|
||||
Several ACP and tool-bash limitations are symptoms of the same missing seam: plugins can create or resume agents through `ctx.agents`, but they cannot own and dispose one agent independently, and long-running bash tasks carry no stable owner in the executor itself. ACP currently aborts and awaits agents on disconnect, but cannot unregister just that session's agent; `session/cancel` cannot cancel queued-but-not-yet-started work; and `tool-bash` keeps task ownership in a plugin-local `Map`, so an HMR reload can make an old task look unowned.
|
||||
|
||||
## Proposal
|
||||
|
||||
Add explicit lifecycle ownership to the agent factory and explicit ownership metadata to background tasks.
|
||||
|
||||
1. `ctx.agents.create/resume` should return an `AgentHandle` (or add an adjacent method) that exposes the `Agent` plus an async disposer. The disposer unregisters the agent, aborts queued/running work, and resolves only when the driver loop reaches quiescence.
|
||||
2. Add a queue-aware cancel primitive to the `Agent` interface. It must clear queued work that has not started, abort the current step if one exists, and make `whenIdle()` wait for the post-cancel quiescent state. ACP `session/cancel` and bridge teardown then become honest cancellation, not best-effort pre-step cancellation.
|
||||
3. Move background task ownership into the bash seam. `BashExecSpec` or `BashTask` should carry a stable owner token, preferably the session id rather than the `Agent` object identity. `bash_output`/`bash_kill` then ask the executor for ownership rather than relying on a `tool-bash` instance-local map.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- ACP disconnect/session close leaves no registered agent for that session, even when `session/load` races teardown.
|
||||
- `session/cancel` before a queued prompt starts prevents that prompt from running and cannot batch the next prompt into the cancelled turn.
|
||||
- A `tool-bash` HMR reload does not make an existing background task readable or killable by a different session.
|
||||
- Existing non-ACP demos still work without managing handles explicitly; config-created agents remain owned by the `AgentLoop` plugin fiber.
|
||||
|
||||
## Risks
|
||||
|
||||
This touches public interfaces (`Agent`, `AgentFactory`, and the bash seam), so it should not be smuggled into a local ACP patch. The compatibility trap is preserving the simple synchronous `Agent.send()` ergonomics while adding a robust async lifecycle path for owners that need it.
|
||||
Reference in New Issue
Block a user