Skip to content

feat(cli): wire /tasks + /background on a session-scoped TaskManager#172

Merged
oratis merged 1 commit into
mainfrom
claude/determined-torvalds-9fc11b
Jun 8, 2026
Merged

feat(cli): wire /tasks + /background on a session-scoped TaskManager#172
oratis merged 1 commit into
mainfrom
claude/determined-torvalds-9fc11b

Conversation

@oratis

@oratis oratis commented Jun 8, 2026

Copy link
Copy Markdown
Owner

What

Wires the /tasks and /background slash commands (both were 🔄 in docs/BEHAVIOR_PARITY.md) on top of the existing background-task infrastructure (TaskManager + TASK_TOOLS).

The gap: the TaskManager was constructed per runAgent call (agent.ts), so background tasks evaporated after each turn and nothing outside the agent loop (the REPL, slash commands) could see them. This re-scopes the manager to the REPL session so tasks persist across turns and are visible to both the agent and the user.

This is slice 1 (visibility) — the foundation. Slice 2 (move the in-flight turn to the background, Ctrl+B parity) needs REPL concurrency and is scoped separately.

How

@deepcode/core

  • TaskManager.setRunner() — lets a host own a long-lived (session-scoped) manager while the agent loop attaches its run-local sub-agent runner each turn. The rich runner resolves named sub-agents + fires SubagentStop. Tasks already started are unaffected (their handle is captured at create() time).
  • RunAgentOptions.taskManager — when provided, runAgent calls setRunner(...) on it and exposes it as toolCtx.tasks instead of creating a per-run manager. When absent, the original per-run fallback is kept verbatim, so headless / desktop / library callers are unchanged.

deepcode-cli

  • The REPL constructs one TaskManager for the whole session and threads it into every runAgent call (opts.taskManager) and onto the slash-command SessionContext (ctx.tasks). Its baseline runner runs a prompt as a depth-1 sub-agent (clean context, no nested tasks; reads ctx.model/ctx.mode live) — this only matters for /background started before the first turn, after which the agent loop's richer runner takes over.
  • /tasks — lists this session's background tasks (id · status · description). /tasks <id> shows a single task's status + output.
  • /background <prompt> (alias /bg) — creates a background task that runs <prompt> as a sub-agent via the manager.

docs — flip /background + /tasks to ✅ in BEHAVIOR_PARITY.md; /batch stays 🔄 (batch-of-prompts not wired here — use /background per prompt).

Notes on behavior

  • One shared manager means tasks the agent starts (via TaskCreate) and tasks the user starts (via /background) show up in the same /tasks list.
  • Background sub-agents run without an approval/askUser callback (matching the existing sub-agent path), so an ask-verdict tool is returned to the sub-agent as a blocked tool result rather than blocking on the REPL prompt — no deadlock / interleaved prompts.
  • Each task carries its own AbortController, so it isn't tied to a turn's lifecycle and keeps running while the user is at the prompt (await rl.question yields to the event loop).

Testing

  • core: setRunner re-targets the runner for subsequent create() calls.
  • cli: new background-commands.test.ts (10 cases) drives /tasks + /background against a stub-backed TaskManager — create / list / <id> output / aliases / usage / unavailable / runner-failure.
  • Full workspace green: typecheck (6 pkgs) · lint (0 errors) · format:check · core 643 · cli 155 · desktop 54 · lsp 8.
  • Manual end-to-end smoke through the real CommandRegistry confirms the rendered output for empty list, create, /bg, list-with-statuses, /tasks <id>, and usage.

🤖 Generated with Claude Code

The background-task infrastructure (TaskManager + TASK_TOOLS) existed but the
manager was created per runAgent call (agent.ts), so tasks vanished after each
turn and slash commands couldn't see them. Re-scope it to the REPL session so
tasks persist and are visible to both the agent and the user.

- core: TaskManager.setRunner() lets a host own a long-lived manager while the
  agent loop attaches its run-local sub-agent runner each turn (resolves named
  sub-agents + fires SubagentStop). Already-started tasks are unaffected.
- core: RunAgentOptions.taskManager — when set, runAgent attaches its runner and
  exposes it on the tool context instead of making a per-run manager (the
  fallback, unchanged, keeps M1/headless/desktop behavior).
- cli: REPL creates one session-scoped TaskManager (baseline runner handles
  /background started before the first turn) and threads it into every runAgent
  call + onto the slash-command SessionContext.
- cli: /tasks lists this session's background tasks (id/status/description);
  /tasks <id> shows one's status + output. /background <prompt> (alias /bg) runs
  the prompt as a depth-1 background sub-agent via the manager.
- docs: flip /background + /tasks to ✅ in BEHAVIOR_PARITY; /batch stays 🔄.

Slice 1 (visibility) only. Moving the in-flight turn to the background (Ctrl+B
parity) needs REPL concurrency and is scoped separately.

Tests: setRunner re-targeting (core); /tasks + /background against a stub-backed
TaskManager (cli, 10 cases). typecheck + lint + format:check + full suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@oratis oratis merged commit dd3b7bf into main Jun 8, 2026
3 checks passed
@oratis oratis deleted the claude/determined-torvalds-9fc11b branch June 8, 2026 05:32
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