Skip to content

fix: background subagent API returns nil handle and docs claim subprocess isolation that doesn't exist #86

Description

@ezynda3

Bug Description

The extension-facing background/async subagent API does not match its documentation or its own type definitions:

  1. extbridge.SpawnSubagent (internal/extbridge/extbridge.go) always calls k.Subagent(ctx, sdkCfg) synchronously and returns handle == nil, regardless of Blocking: false. OnComplete fires synchronously before SpawnSubagent even returns.
  2. SubagentHandle (internal/extensions/subagent.go) has dead fields (proc *os.Process, done chan, etc.) suggesting a subprocess design that was never wired up in the in-process bridge.
  3. The extension API docs (internal/extensions/api.go:707) claim the subagent "runs as a separate subprocess with full tool access but isolated session and extensions (--no-session --no-extensions)". In reality it is an in-process child Kit with session persistence enabled by default and no extension isolation.
  4. The shipped example examples/extensions/subagent-test.go (/subbg command) expects a usable handle — it receives nil and would panic or silently misbehave if exercised.

Expected: either non-blocking spawning works and returns a populated handle, or the API/docs/example clearly state that spawning is synchronous and in-process.

Steps to Reproduce

  1. Load examples/extensions/subagent-test.go (output/kit -e examples/extensions/subagent-test.go --no-session)
  2. Run the /subbg slash command, which calls ctx.SpawnSubagent(ext.SubagentConfig{Blocking: false, ...})
  3. Observe the call blocks until the subagent finishes and the returned handle is nil
  4. Compare against the documented behavior in internal/extensions/api.go:706-739

Relevant Code / Configuration

// internal/extbridge/extbridge.go — always synchronous, always nil handle:
result, err := k.Subagent(ctx, sdkCfg)
// ... handle is never constructed or returned

// internal/extensions/subagent.go — dead fields:
type SubagentHandle struct {
    proc *os.Process // never populated by the in-process bridge
    done chan struct{}
    // ...
}

// internal/extensions/api.go:707 — inaccurate doc:
// "runs as a separate subprocess ... isolated session and extensions (--no-session --no-extensions)"

Affected Component

extensions, subagent bridge (extbridge), SDK

Kit Version

main (ed51910)

Additional Context

Two ways to resolve:

Option A (preferred): implement true non-blocking spawn — run k.Subagent in a goroutine, return a populated SubagentHandle (done channel, cancel func, status), invoke OnComplete/OnEvent callbacks asynchronously. Result injection back into the parent session could follow opencode's background task pattern.

Option B (minimal): remove the dead SubagentHandle subprocess fields, correct the doc comment in api.go to describe in-process synchronous execution, and fix examples/extensions/subagent-test.go to not rely on a handle.

Either way, docs, types, bridge behavior, and the shipped example must agree — right now the API is a trap for extension authors.

Related: #84 (named agents) builds on the same subagent machinery.

Checklist

  • I've searched existing issues and this hasn't been reported yet
  • I've tested with the latest version of Kit

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions