Skip to content

feat: depth-free delegation, recoverable subagents, and resilient ChatGPT transport - #253

Merged
danielkov merged 3 commits into
mainfrom
feat/subagent-delegation-and-resilience
Oct 1, 2026
Merged

danielkov merged 3 commits into
mainfrom
feat/subagent-delegation-and-resilience

Conversation

@danielkov

Copy link
Copy Markdown
Contributor

Summary

Makes delegation independent of nesting depth and cheaper to steer, keeps subagents recoverable when a turn fails, makes provider failures and retries diagnosable, and moves ChatGPT subscription inference to the hardened WebSocket transport in agentkit-provider-openai 0.10.12.

Delegation

  • No depth limit. The system prompt and tool set are the same at every depth. Live subagents are bounded to 120 per parent and across the whole delegation tree, using slot files shared with child processes through KIT_SUBAGENT_TREE_SLOTS.
  • Fork the current conversation. fork without subagent copies the caller's conversation. The session id is never put in the model's context. Continuation metadata in the copied transcript is re-bound to the new session rather than stripped.
  • Guidance in tool descriptions. It describes when delegation pays off: parallel work, or a step whose intermediate context the caller doesn't need. It also covers what forks and fresh subagents cost, and that a fork carries the whole conversation into every request. Kit children get a short framing message saying what they inherited.
  • One prompt tool. steer is merged into prompt, which accepts a subagent id or value.
    • An idle subagent starts a new turn.
    • A working subagent gets the message within its current turn when its harness supports steering, otherwise as the next turn.
    • The call returns once the turn that handled the message ends. The TUI still steers directly.
  • Recoverable failures. A failed turn, including the first turn of subagent or fork, leaves a live child idle with its conversation and edits intact. Only a dead child process is retired. The error names the subagent and the cause from its fatal log, and says how to continue it.
  • Cache lineage. Forks share their parent's prompt-cache key and session-id, so their first request reuses the parent's cached prefix. Fresh subagents use their own.

Observability

  • Fatal error records include the typed provider failure: reason, last attempt reason, upstream classification and attempt count.
  • Provider retry observers are forwarded through Kit's session wrappers. Before, they were dropped silently, so retries never reached loop observers or ~/.kit/errors/<session>/retries.jsonl.
  • Compose spill markers name the omitted byte range and the artifact offset to read it from.
  • The voice-state monitor takes its baseline from session history, so sessions without voice no longer get a spurious status message.

ChatGPT transport

  • Uses agentkit-provider-openai 0.10.12 and agentkit-tool-compose 0.10.12 with runlet 0.6.1, all from crates.io. The temporary provider git pin and its patch section are removed.
  • Stalls are detected by response progress, with a 60s limit for the first event and 180s between later ones. Lost sockets are resent in full on a fresh socket, including after visible output when the client accepts superseded attempts.
  • The context window is discovered in the background with retries instead of blocking session start. Usage reporting picks it up once it is known.
  • Logging in again to the same account keeps a session working; switching accounts is still rejected. session_binding_rejects_generation_change is replaced by credential_binding_accepts_reauthentication_but_rejects_account_changes.

Known test issue

Locally, a few ACP tests that list sessions can time out. Listing reads every legacy transcript stored flat in the real ~/.kit/sessions to find its workspace, and on a machine with many of them that takes longer than the tests allow. This is pre-existing and not changed here; CI starts from a clean HOME.

Comment thread docs/user/subagents-and-acp-harnesses.md Outdated

@kit-code-agent kit-code-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent prompt delivery can reject valid messages, return another generation’s result, or remain suspended after lifecycle transitions. Current-conversation forks fail for transient runtime sessions. Background catalog discovery can extend foreground startup waits, and retained subagents can attribute later failures to stale fatal records.

Comment thread src/tools/subagent.rs Outdated
Comment thread src/tools/subagent.rs Outdated
Comment thread src/tools/subagent.rs
Comment thread src/session.rs
Comment thread src/provider/chatgpt.rs
Comment thread src/tools/subagent.rs Outdated
…tGPT transport

Delegation:
- Remove the subagent depth limit and depth-dependent prompts; bound live
  subagents per delegation tree with shared slot files instead.
- `fork` without `subagent` copies the current conversation; framing messages
  tell children what they inherited. Delegation guidance lives in the tool
  descriptions, including what forks and fresh subagents cost.
- Merge `steer` into `prompt`: it accepts an ID or value and delivers into a
  running turn when supported, otherwise as the next turn, and returns once
  the turn that handled the message ends.
- A failed turn, including the first, leaves a live subagent idle and
  promptable; the error names the subagent, its recorded cause, and how to
  continue it.
- Forks share their parent's prompt-cache key; fresh subagents use their own.

Observability:
- Fatal error records carry the typed provider failure.
- Forward provider retry observers through Kit's session wrappers, so
  retries reach loop observers and `~/.kit/errors/<session>/retries.jsonl`.
- Compose spill markers name the omitted byte range and the artifact offset
  to read it from.

ChatGPT transport:
- Use agentkit-provider-openai 0.10.12 (progress-based stall detection,
  fresh-socket resends including after visible output with supersession,
  cache-key routing) and agentkit-tool-compose 0.10.12 with runlet 0.6.1.
- Discover the context window in the background with retries instead of
  blocking session start; accept re-authentication to the same account.
@danielkov
danielkov force-pushed the feat/subagent-delegation-and-resilience branch from a27d0c5 to 036bf3a Compare October 1, 2026 08:48

@kit-code-agent kit-code-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new findings in the scoped update. Six prior findings remain active: idle delivery can lose admission races; injected callers can receive another generation’s output or miss their generation’s failure; retirement and fork release can leave delivery waiters suspended; current-conversation forks cannot inherit transient run-N parents; foreground catalog requests can exceed their deadline while awaiting background initialization; and failed turns can report an earlier turn’s fatal cause.

…ostics

- Admit idle deliveries atomically: a session another caller is using,
  starting, forking or has advanced hands the prompt back unsent, and
  `prompt` re-checks state instead of failing.
- Record each finished generation's outcome and output so callers whose
  message was injected receive that generation's result, not a later one.
- Wake delivery waiters on child-exit retirement and fork-reservation
  release.
- Fork the current conversation from in-memory `run-N` sessions: transient
  runs record their transcript, including compaction rewrites.
- Bound the foreground model-catalog lookup as a whole, so it does not wait
  out background context discovery.
- Report a fatal cause only when it was recorded during the failing turn.
- Accept `{ id }` for `prompt`'s subagent, matching `close`.

@kit-code-agent kit-code-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fatal-cause attribution remains incomplete for delayed injected callers: a failed generation can be diagnosed using a later generation's fatal record because lookup enforces only the failing turn's start time.

@kit-code-agent kit-code-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. The changes look good to merge.

@danielkov
danielkov merged commit 49cc529 into main Oct 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant