-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Open
Description
Problem
The native task tool blocks synchronously, limiting multi-agent orchestration patterns. Community plugins like opencode-background-agents enable async delegation, but changes made by background agents cannot be reverted with /undo because:
PatchParts are only created during LLM stream processing atfinish-step- No SDK API exists to create patches programmatically
- No plugin hooks for snapshot/patch integration
This forces an impossible choice: either block the parent session (defeating async), or lose undo capability.
Proposed Solution
Expose two primitives that already exist internally, allowing plugins to integrate async operations with the native undo system.
API
// 1. Expose existing Snapshot.track()
// Captures current file state, returns git tree hash
client.snapshot.track(): Promise<string | undefined>
// 2. Expose Session.updatePart() pattern
// Adds a PatchPart to an existing message so /undo can find it
client.session.addPart(
sessionId: string,
messageId: string,
part: { type: 'patch', hash: string, files: string[] }
): Promise<void>Usage
// Before async delegation starts
const beforeHash = await client.snapshot.track()
if (!beforeHash) throw new Error("Failed to capture snapshot")
// Fire async delegation (non-blocking)
const delegation = await startDelegation(prompt, agent)
// ... parent continues working ...
// When delegation completes (via session.idle listener)
const changedFiles = getFilesChangedByDelegation(delegation)
if (changedFiles.length === 0) return // Nothing to register
// Register changes with parent's undo history
await client.session.addPart(parentSessionId, parentMessageId, {
type: 'patch',
hash: beforeHash,
files: changedFiles
})
// Now /undo will find this patch and revert the async agent's changes!Why This Design
- Minimal surface area - Just two methods, both exposing existing internals
- Matches existing patterns -
addPartmirrorsSession.updatePart(), validation follows "trust caller, validate structure" philosophy - No architectural changes - Uses existing
PatchPartstructure and undo scanning - Enables ecosystem innovation - Plugins can build async orchestration with native undo
Prior Art
| Tool/Framework | Approach |
|---|---|
| VS Code | Extensions push custom undo handlers via workspace.applyEdit() |
| Cline | Shadow Git repo for checkpoints, message-timestamp association |
| Aider | Session-scoped commit tracking, per-file git checkout for undo |
| Crystal | Git worktrees for isolation, commit-based undo |
Internal Consistency
This proposal exposes existing internals without modification:
Snapshot.track()returnsstring | undefined→ expose asclient.snapshot.track()Session.updatePart()pattern → expose asclient.session.addPart()PatchPart { type, hash, files }→ same structure- Undo scans for
type === 'patch'→ works automatically
References
- [feat] True Async/Background Sub-Agent Delegation #5887 - Async background sub-agent delegation (primary request)
- [Tracking] Edit tool reliability: "modified since last read" errors (Undo/Redo & Persistence) #5840 - Undo system architecture
- Task Tool Timeouts & Early Termination in Multi-Agent Conductor Pattern #6792 - Task tool blocks synchronously
- [FEATURE]: Expose the ability to modify the chat messages thread via a plugin #7034 - Chat message modification via plugin
- feat(cli): add snapshot management commands #7263 - Snapshot management commands
Implementation Notes
The implementation would:
- Add
track()method to SDK's snapshot namespace (wraps existingSnapshot.track()) - Add
addPart()method to SDK's session namespace (wraps existingSession.updatePart()pattern) - Use Zod for structure validation (matching existing patterns)
- Trust caller for sessionId/messageId (matching existing patterns)
Happy to contribute a PR if this direction is acceptable.
Metadata
Metadata
Assignees
Labels
No labels