docs(eve): background tasks - define task and notification contract plan#1085
Draft
ruiconti wants to merge 6 commits into
Draft
docs(eve): background tasks - define task and notification contract plan#1085ruiconti wants to merge 6 commits into
ruiconti wants to merge 6 commits into
Conversation
Research plan for durable task records aligned with the MCP Tasks extension (io.modelcontextprotocol/tasks, SEP-2663): sync vs background becomes a property of the invocation, background elections terminalize the tool-call position with a CreateTaskResult-shaped placeholder, and status transitions reach subscribers as deliveries to registered notification endpoints - the generalization of the existing remote-agent callback, entering parked sessions through the driver's existing deliver-only wake vocabulary under the initiator auth principal. Phased additively: task contract (no users), notifications contract (registered endpoints on session state), task-to-notification wiring, defineTool() election via execution.taskSupport, and migration of the built-in agent tool and subagents. Issue: #1084 Signed-off-by: Rui Conti <ruiconti@gmail.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…oles Reshape the plan from five horizontal phases into three vertical slices (inert task mode; subagents on the task model; public authoring API), organized by the two roles eve plays in the task contract: caller (elect, park, wake, route) and callee (run, transition, notify), each with its own protocol section and sequence diagram. New commitments since the first draft: - Design goal: the agent tool must be authorable as a plain defineTool on public primitives - ctx.task handle, detachable execution (ctx.task.detach()), and endpoint registration - with the privileged internal path deleted as slice 3's exit criterion. - Notification envelope: explicit event kinds (task.created/progress/ status/terminal) carrying the full DetailedTask snapshot; default routing is wake-worthy kinds only, per-endpoint filters for passive observers. - Endpoints collapse to a single URL/POST delivery path targeting the session driver (loopback locally), generalizing the remote-agent callback instead of special-casing it. - Election combinators sync()/defer()/background() as sugar over 2025-11-25 taskSupport semantics; sync-by-default is explicitly transitional (new open question 6 on flipping to defer()). - Mid-turn notification arrival scenario: buffer-then-route via the existing buffered-delivery and turn-delivery-request machinery. - Sync-invocation normalization onto the record (mode discriminator, "who awaits") moved to deferred. Issue: #1084 Signed-off-by: Rui Conti <ruiconti@gmail.com>
Add a "What this unlocks" section after Motivation stating the today-vs-after delta per capability: fire-and-forget delegation, subagent HITL reaching the parent without blocking it (today's proxy exists only because the parent turn is parked on the child), child progress on the parent's surfaces at statusMessage granularity (today the parent stream has only subagent.called/completed), late answers executing via updateTask instead of degrading to text, and detached executors covering non-subagent integrations. Issue: #1084 Signed-off-by: Rui Conti <ruiconti@gmail.com>
…consumers 'Notification' names the emitted event itself (the TaskNotification envelope a transition produces), so using it for the receiving side too conflated the two halves of the return path. The registered receivers — previously 'notification endpoints', 'endpoints', 'subscribers', and 'observers', inconsistently — are now event consumers: EventConsumer, DEFAULT_CONSUMER_EVENTS, consumer registration, per-consumer guards. The task-resolution URL ctx.task.detach() returns keeps the name 'callback endpoint': it resolves the task; it does not consume events. Signed-off-by: Rui Conti <ruiconti@gmail.com>
The TaskService surface is not framework-internal: agents operate on their session's live tasks too — read, answer, abort, and await, including a join across all live tasks (fan out, then block until every task settles). Awaiting is an invocation posture over an unchanged record, so background and sync remain two postures over one task. Narrows open question 2 to the model-facing shape of those operations. Signed-off-by: Rui Conti <ruiconti@gmail.com>
Implementation of Slice 1 (#1190) showed session state cannot own the record: it is threaded through step results, while transitions must be writable from paths holding no threadable state — the routing step while parked, the callback route, a detached executor. Each record is now owned by a dedicated durable task run (single-writer actor, command hook in, snapshots out, cross-run tail reads); the caller keeps a live-task index on session state and the recorded election rides the pending action batch, keeping the extension capability contracts untouched. Also notes the inert-mode consumer-orphaning edge under continuation-token rotation. Signed-off-by: Rui Conti <ruiconti@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes-tracking #1084. Research plan only — no runtime changes, no changeset.
What
Adds
research/background-tasks.md: a plan for letting model-initiated work outlive the turn that started it.Today results can only wake an active turn — subagents hold the parent turn parked until every child resolves — and HITL requests either expire (questions cleared as
ignoredby the next message) or block (approvals defer all later input). A response that arrives after its request leftpendingInputsis downgraded to text and never executes the original action. There is no third state: work that neither keeps the turn active nor gates the conversation.The doc leads with the capability delta this buys: fire-and-forget delegation, subagent HITL that reaches the parent without blocking it, child progress on the parent's surfaces, late answers that execute via
updateTask, and detached executors that cover non-subagent integrations.The shape
io.modelcontextprotocol/tasks, SEP-2663): same object shape (ttlMs,pollIntervalMs,DetailedTaskvariants carryinginputRequests/result/errorinline) and same operation semantics (tasks/get/tasks/update/tasks/cancel). Shapes and semantics only — no MCP wire surface; that stays a future adapter.CreateTaskResult-shaped placeholder at the call position, register the session driver as wake endpoint, route the arriving notification) and callee (run the work, transition the record, POST a notification envelope per transition to registered endpoints). Subagents compose the two.callback: { token, url }contract, paying a loopback POST locally to avoid a local/remote branch. The driver's wake vocabulary staysdeliver-only andNextDriverActionstays closed; discrimination lives in step bodies, so the pinned driver is untouched.agenttool ends up authorable as a plaindefineToolon public primitives —ctx.taskhandle, detachable execution (ctx.task.detach()), endpoint registration — with the privileged internal path deleted as the exit criterion.Three vertical slices, each cutting through both roles end-to-end: inert task mode (no public electors) → subagents as the first electors, via
sync()/defer()/background()combinators ondefineAgent→ the public authoring API ondefineTool(), closing with theagenttool re-implemented on it. Normalizing sync invocations onto the record (modediscriminator, "who awaits") is deferred, as is token-budget accounting for detached work.For reviewers
The open questions section is the review surface: subagent default (
defer()vssync()), a built-incancel_tasktool, ttl-gating of lateinput_requiredanswers, observer registration for channels/external callers, whether to chase the extension draft's unsettled negotiation names, and what would make flipping the default todefer()safe for arbitrary tools.