Skip to content

feat: keep Claude sessions warm and finish runtime wiring - #188

Open
Waishnav wants to merge 10 commits into
codex/subagent-runtime-pifrom
codex/subagent-runtime-overhaul
Open

feat: keep Claude sessions warm and finish runtime wiring#188
Waishnav wants to merge 10 commits into
codex/subagent-runtime-pifrom
codex/subagent-runtime-overhaul

Conversation

@Waishnav

@Waishnav Waishnav commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Claude's streaming query API can accept multiple user messages, but DevSpace previously created a fresh query for every prompt. This final stacked change keeps one query warm per logical Claude agent, resumes cold sessions from the persisted provider id, and completes the native driver registry, provider availability checks, package cleanup, and lifecycle documentation for the full runtime stack.

Summary by CodeRabbit

  • New Features
    • Added support for running local Claude coding sessions alongside existing providers.
    • Added optional thinking-level controls to agent run and continuation commands.
    • Improved provider availability detection and session resumption.
  • Bug Fixes
    • Improved workspace-specific agent listing and access validation.
    • Corrected workspace handling across agent sessions and provider runtimes.
  • Documentation
    • Expanded guidance for agent lifecycle, authentication, provider behavior, concurrency, restarts, and session continuation.
    • Corrected the documented continuation command.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a Claude local-agent runtime, centralizes provider drivers, renames workspace context fields to workspaceRoot, strengthens workspace scoping, updates provider availability checks, and documents daemon-managed runtime lifecycle and CLI behavior.

Changes

Local agent runtime architecture

Layer / File(s) Summary
Workspace-root runtime scoping
src/local-agent-runtime.ts, src/local-agent-manager.ts, src/local-agent-acp.ts, src/local-agent-codex.ts, src/local-agent-opencode.ts, src/local-agent-pi.ts, src/*test.ts
Runtime contexts and inputs now use workspaceRoot. Manager listing and agent access require workspace scope. ACP, Codex, OpenCode, and Pi use the renamed field for sessions, processes, and commands.
Claude driver and runtime
src/local-agent-claude.ts, src/local-agent-claude.test.ts
Adds Claude session creation, resumption, streaming input, model and thinking configuration, sandbox authority, environment cleanup, executable discovery, error handling, and lifecycle controls.
Provider driver registry and availability
src/local-agent-adapters.ts, src/local-agent-adapters.test.ts, src/local-agent-availability.ts, src/local-agent-availability.test.ts
The adapter registry now constructs dedicated drivers with injectable options. Provider availability uses package checks and environment-aware command resolution.
Daemon, CLI, and validation documentation
docs/agent-profile-schema.md, src/cli.ts, skills/subagent-delegation/SKILL.md, package.json
Documents daemon-owned runtimes, provider mappings, lifecycle behavior, thinking controls, continuation syntax, and expanded test coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentClient
  participant ClaudeLocalAgentDriver
  participant ClaudeQueryRuntime
  participant ClaudeAgentSDK
  AgentClient->>ClaudeLocalAgentDriver: run prompt with workspaceRoot
  ClaudeLocalAgentDriver->>ClaudeQueryRuntime: create or reuse runtime
  ClaudeQueryRuntime->>ClaudeAgentSDK: start or resume query
  ClaudeQueryRuntime->>ClaudeAgentSDK: stream prompt input
  ClaudeAgentSDK-->>ClaudeQueryRuntime: return streamed messages
  ClaudeQueryRuntime-->>ClaudeLocalAgentDriver: return response and session ID
  ClaudeLocalAgentDriver-->>AgentClient: return run result
Loading

Poem

A rabbit reviewed the runtime stream,
Where Claude sessions hop through a queueing dream.
Workspace roots keep each burrow clear,
Drivers guide providers far and near.
“Continue,” said the rabbit, “not run again!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: keeping Claude sessions warm and completing runtime integration.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/subagent-runtime-overhaul

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR completes the local-agent runtime wiring, introduces persistent warm Claude query sessions, and aligns workspace scoping and provider availability with the native drivers.

  • Adds a per-agent Claude runtime supporting warm continuation and cold session resumption.
  • Splits native provider implementations into dedicated drivers and completes the driver registry.
  • Propagates workspace roots and optional thinking overrides through the CLI and runtime stack.
  • Removes the bundled Codex SDK in favor of a host-installed Codex app-server.
  • Expands runtime lifecycle documentation and provider-specific tests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
src/local-agent-claude.ts Adds the per-agent streaming Claude runtime, input queue, session resumption, per-turn configuration, and lifecycle cleanup.
src/local-agent-runtime.ts Renames the runtime workspace field to workspaceRoot and carries workspace identity consistently through runtime interfaces.
src/local-agent-manager.ts Persists workspace IDs on creation and applies workspace scope checks when listing, reading, or continuing agents.
src/local-agent-adapters.ts Replaces embedded provider implementations with a compact registry of dedicated native drivers.
src/local-agent-availability.ts Aligns provider discovery with package-based and host-command-based native runtime requirements.
src/local-agent-codex.ts Updates Codex runtime inputs to use the explicit workspaceRoot field.
src/local-agent-acp.ts Propagates workspaceRoot through ACP session creation, resumption, process launch, and runtime keying.
src/local-agent-pi.ts Propagates workspaceRoot into Pi session construction and runtime identity.
src/local-agent-opencode.ts Updates OpenCode runtime construction to consume the renamed workspaceRoot input.
package.json Removes the bundled Codex SDK and adds dedicated native-driver tests to the test suite.

Sequence Diagram

sequenceDiagram
    participant CLI as DevSpace CLI
    participant D as devspace-agentd
    participant M as Agent Manager
    participant P as Runtime Pool
    participant C as Claude Query
    CLI->>D: agents run / continue
    D->>M: start or continue(agentId, prompt)
    M->>P: "run(runtime key = claude:agentId)"
    alt Warm runtime exists
        P->>C: push next user message
    else Cold or restarted runtime
        P->>C: "create query(resume = providerSessionId)"
        P->>C: push user message
    end
    C-->>P: streamed result and session ID
    P-->>M: final response
    M-->>D: persist status, response, session ID
    D-->>CLI: agent record
Loading

Reviews (2): Last reviewed commit: "refactor: keep provider availability sid..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc246a9047

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/local-agent-claude.ts

async run(input: LocalAgentRunInput): Promise<LocalAgentRunResult> {
if (!this.isAlive()) throw new Error("Claude runtime is not running.");
if (input.model && this.query.setModel) await this.query.setModel(input.model);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply thinking overrides to warm Claude sessions

When a Claude agent is continued with --thinking while its runtime is still warm, the pool reuses the same claude:${agentId} query, but this run path only applies dynamic model changes. The thinking/effort option is only passed when claudeQueryOptions() creates the query, so the per-run thinking override documented in skills/subagent-delegation/SKILL.md is silently ignored until the runtime idles out or the server restarts; recreate/key the runtime on thinking changes or apply the provider's dynamic control if available.

Useful? React with 👍 / 👎.

@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from fc246a9 to 30fbfe5 Compare August 11, 2026 18:28
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 30fbfe5 to bd3ad46 Compare August 11, 2026 19:16
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from bd3ad46 to 52108ed Compare August 11, 2026 19:20
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 52108ed to e31591f Compare August 12, 2026 04:14
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from e31591f to c492264 Compare August 12, 2026 04:20
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch 2 times, most recently from 89bdc95 to 0391357 Compare August 12, 2026 04:25
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 0391357 to 5afa49b Compare August 12, 2026 04:28
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch 2 times, most recently from 7e66ea4 to d3a3cfe Compare August 12, 2026 04:44
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from d3a3cfe to 6eea7af Compare August 12, 2026 04:57
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 6eea7af to 0f171a9 Compare August 12, 2026 04:58
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch 2 times, most recently from 29c8469 to a53c34e Compare August 12, 2026 05:19
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 00dc770 to 6053c80 Compare August 12, 2026 05:25
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch 2 times, most recently from ccad60d to 6cf3d56 Compare August 12, 2026 05:27
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from 6cf3d56 to 19e5f90 Compare August 12, 2026 05:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/local-agent-manager.ts (1)

220-229: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Revalidate the persisted workspace root before loading profiles or running the provider.

runTurn passes record.workspaceRoot directly to loadProfiles, buildRunInput, and LocalAgentRuntimeContext. Call authorizeWorkspace first and use the returned path throughout. Otherwise, a changed allowedRoots configuration or modified stored record can execute against an unauthorized workspace.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-manager.ts` around lines 220 - 229, Update runTurn to call
authorizeWorkspace with record.workspaceRoot before loading profiles or invoking
the provider, then use the returned authorized path for loadProfiles,
buildRunInput, and LocalAgentRuntimeContext.workspaceRoot. Ensure all subsequent
workspace operations use this revalidated value rather than the persisted record
field.

Source: Learnings

🧹 Nitpick comments (7)
package.json (1)

32-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add a packaged CLI smoke path.

The test script executes source files directly with tsx. It does not exercise npm pack/npx, daemon auto-start, or continuation after restart. Source tests can pass while the packaged user path is broken. Add a package-level smoke test in CI, or state that this command provides source-level coverage only.

As per coding guidelines: “Verify the actual user-consumption path, including packaged npm/npx usage, and clearly state when only a narrower proxy was verified.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 32, Add a package-level smoke test to the test/CI flow
that runs the built package through npm pack and npx, covering daemon auto-start
and continuation after restart; alternatively, explicitly label the existing
test command as source-level coverage only and add the packaged-path
verification separately. Anchor the change near the package.json test script and
reuse the project’s existing CLI and daemon test entry points.

Source: Coding guidelines

src/local-agent-acp.ts (1)

163-182: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Resolve workspaceRoot before you send it as cwd.

Every other site in this file passes resolve(context.workspaceRoot) (lines 248, 257, 389, 403). The session/resume and session/new requests pass input.workspaceRoot unchanged. If a caller supplies a relative root, the agent process receives a path that is resolved against its own working directory. Normalize the value here for consistent workspace scoping.

♻️ Proposed change
       const response = await this.connection.agent.request("session/resume", {
         sessionId: input.providerSessionId,
-        cwd: input.workspaceRoot,
+        cwd: resolve(input.workspaceRoot),
         mcpServers: [],
         ...this.additionalDirectoryParams(),
       });
     const response = await this.connection.agent.request("session/new", {
-      cwd: input.workspaceRoot,
+      cwd: resolve(input.workspaceRoot),
       mcpServers: [],
       ...this.additionalDirectoryParams(),
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-acp.ts` around lines 163 - 182, Update the session/resume and
session/new request payloads in the surrounding session creation flow to pass
the resolved input.workspaceRoot as cwd, matching the resolve-based handling
used elsewhere in the file. Ensure both requests normalize relative workspace
roots before sending them to the agent.
src/local-agent-adapters.test.ts (1)

24-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the full driver contract, not only runtimeKey.

LocalAgentDriver in src/local-agent-runtime.ts declares provider, runtimeKey, and createRuntime. This loop checks provider and runtimeKey only. A driver that omits createRuntime would still pass. Add the missing assertion so the registry contract stays covered.

♻️ Proposed change
 for (const provider of providers) {
   const adapter = createLocalAgentAdapter(provider);
   assert.equal(adapter.provider, provider);
   assert.equal(typeof adapter.runtimeKey, "function");
+  assert.equal(typeof adapter.createRuntime, "function");
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-adapters.test.ts` around lines 24 - 28, Extend the provider
loop in the local agent adapter test to assert that the created adapter’s
createRuntime property is a function, alongside the existing provider and
runtimeKey checks, covering the complete LocalAgentDriver contract.
src/local-agent-availability.test.ts (1)

23-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep a negative case for the package-backed check.

The previous test simulated a missing pi executable and asserted available: false with a reason. The new assertion only states that Pi is available. No test now proves that a package-backed provider reports available: false with a reason when the module does not resolve. That path is the changed behavior in src/local-agent-availability.ts.

Also note that this assertion depends on @earendil-works/pi-coding-agent being installed in the test environment. It verifies the local dev install, not a packaged npm/npx install with omitted optional dependencies.

Add a case that exercises the unresolvable-package branch, for example by exporting packageAvailability for tests or by asserting the reason shape for a provider whose package is absent.

As per coding guidelines: "Verify the actual user-consumption path, including packaged npm/npx usage ...; clearly state when only a narrower proxy was verified."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-availability.test.ts` around lines 23 - 25, Add a negative
test alongside the existing checkLocalAgentProviderAvailability("pi") assertion
that exercises the unresolvable package-backed provider path and verifies
available is false with a reason. Expose or reuse packageAvailability so the
test can deterministically simulate a missing module rather than relying on the
installed Pi package; keep the existing availability assertion, but ensure the
test distinguishes local development installation from packaged npm/npx behavior
with omitted optional dependencies.

Source: Coding guidelines

src/local-agent-claude.ts (2)

36-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused reject callback from the waiter record.

No code path calls waiter.reject. close() resolves pending waiters with { done: true }, and push() throws synchronously. Drop reject to keep the queue contract explicit.

Also applies to: 59-64

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-claude.ts` around lines 36 - 39, Remove the unused reject
callback from the waiter record declared in the waiters field, and update all
related waiter creation and handling in close() and push() to use only resolve.
Preserve close() resolving pending waiters with done: true and push() throwing
synchronously.

279-291: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Normalize the sandbox filesystem paths like the permission rules.

Line 259 normalizes backslashes for the permission rules, but sandbox.filesystem receives the raw workspaceRoot and the raw protectedPaths. On Windows the two lists then use different path separators for the same locations. Use one normalized form for both.

♻️ Proposed fix
   const sandbox = {
     enabled: true,
     failIfUnavailable: true,
     autoAllowBashIfSandboxed: true,
     allowUnsandboxedCommands: false,
     filesystem: {
-      allowWrite: allowed ? [workspaceRoot] : [],
-      denyWrite: allowed ? [] : [workspaceRoot],
-      denyRead: protectedPaths,
-      allowRead: [workspaceRoot],
+      allowWrite: allowed ? [resolvedWorkspace] : [],
+      denyWrite: allowed ? [] : [resolvedWorkspace],
+      denyRead: protectedPaths.map((path) => path.replaceAll("\\", "/")),
+      allowRead: [resolvedWorkspace],
     },
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-claude.ts` around lines 279 - 291, Normalize workspaceRoot
and protectedPaths before constructing the sandbox object, matching the
normalization already used by the permission rules near the surrounding setup.
Use these normalized values for sandbox.filesystem.allowWrite, denyWrite,
denyRead, and allowRead, while preserving the existing allowed branching and
permission behavior.
src/local-agent-claude.test.ts (1)

96-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting cwd and the scrubbed environment.

The test already captures lastOptions. Add assertions for cwd === "/tmp/project" and for the removal of CLAUDECODE and the other Claude entrypoint variables by claudeCommandEnvironment. Both are workspace-scoping and isolation guarantees of the new driver.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-claude.test.ts` around lines 96 - 102, Add assertions in the
test covering the captured lastOptions from the Claude query: verify cwd is
"/tmp/project" and confirm the environment has removed CLAUDECODE and the other
Claude entrypoint variables via claudeCommandEnvironment. Keep the existing
session reuse and permission assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/subagent-delegation/SKILL.md`:
- Around line 37-38: Update the continuation section describing `continue <id>
"<prompt>"` to include examples demonstrating both supported overrides,
`--model` and `--thinking`, while preserving the existing guidance not to use
`run <id>` for continuation.

In `@src/local-agent-availability.ts`:
- Around line 105-115: Update resolveCommand so the Windows extensions list
always probes the bare command name before PATHEXT variants by including an
empty extension first. Preserve the existing PATHEXT fallback and path iteration
behavior, ensuring commands already ending in extensions such as .exe or .cmd
are checked directly.
- Around line 82-97: Update codexAvailability and the related CLI/server
availability summaries to distinguish executable-only detection from full Codex
availability, explicitly stating that app-server support is required. Keep
commandAvailability side-effect-free and preserve its executable resolution
check, but ensure user-facing output does not report Codex as fully available
when app-server support is absent.

In `@src/local-agent-claude.ts`:
- Around line 166-168: Update LocalAgentClaude.runtimeKey to include
context.writeMode alongside context.agentId, ensuring runtime pool entries
remain distinct for each write mode while preserving the existing Claude key
prefix.

In `@src/local-agent-manager.ts`:
- Around line 318-320: Update the get and continue flows in the agent manager to
require both a workspace scope and its workspaceId, and ensure direct callers
invoke assertAgentWorkspace before accessing records. In the workspace
validation around record.workspaceId and scope, distinguish mismatched workspace
IDs from mismatched workspace roots with separate errors, while preserving
validation for matching scopes.

---

Outside diff comments:
In `@src/local-agent-manager.ts`:
- Around line 220-229: Update runTurn to call authorizeWorkspace with
record.workspaceRoot before loading profiles or invoking the provider, then use
the returned authorized path for loadProfiles, buildRunInput, and
LocalAgentRuntimeContext.workspaceRoot. Ensure all subsequent workspace
operations use this revalidated value rather than the persisted record field.

---

Nitpick comments:
In `@package.json`:
- Line 32: Add a package-level smoke test to the test/CI flow that runs the
built package through npm pack and npx, covering daemon auto-start and
continuation after restart; alternatively, explicitly label the existing test
command as source-level coverage only and add the packaged-path verification
separately. Anchor the change near the package.json test script and reuse the
project’s existing CLI and daemon test entry points.

In `@src/local-agent-acp.ts`:
- Around line 163-182: Update the session/resume and session/new request
payloads in the surrounding session creation flow to pass the resolved
input.workspaceRoot as cwd, matching the resolve-based handling used elsewhere
in the file. Ensure both requests normalize relative workspace roots before
sending them to the agent.

In `@src/local-agent-adapters.test.ts`:
- Around line 24-28: Extend the provider loop in the local agent adapter test to
assert that the created adapter’s createRuntime property is a function,
alongside the existing provider and runtimeKey checks, covering the complete
LocalAgentDriver contract.

In `@src/local-agent-availability.test.ts`:
- Around line 23-25: Add a negative test alongside the existing
checkLocalAgentProviderAvailability("pi") assertion that exercises the
unresolvable package-backed provider path and verifies available is false with a
reason. Expose or reuse packageAvailability so the test can deterministically
simulate a missing module rather than relying on the installed Pi package; keep
the existing availability assertion, but ensure the test distinguishes local
development installation from packaged npm/npx behavior with omitted optional
dependencies.

In `@src/local-agent-claude.test.ts`:
- Around line 96-102: Add assertions in the test covering the captured
lastOptions from the Claude query: verify cwd is "/tmp/project" and confirm the
environment has removed CLAUDECODE and the other Claude entrypoint variables via
claudeCommandEnvironment. Keep the existing session reuse and permission
assertions unchanged.

In `@src/local-agent-claude.ts`:
- Around line 36-39: Remove the unused reject callback from the waiter record
declared in the waiters field, and update all related waiter creation and
handling in close() and push() to use only resolve. Preserve close() resolving
pending waiters with done: true and push() throwing synchronously.
- Around line 279-291: Normalize workspaceRoot and protectedPaths before
constructing the sandbox object, matching the normalization already used by the
permission rules near the surrounding setup. Use these normalized values for
sandbox.filesystem.allowWrite, denyWrite, denyRead, and allowRead, while
preserving the existing allowed branching and permission behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 39fd5c05-ab2b-4ab4-8b6d-ee84c65e16d1

📥 Commits

Reviewing files that changed from the base of the PR and between 1a7fd9a and da68ab3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • docs/agent-profile-schema.md
  • package.json
  • skills/subagent-delegation/SKILL.md
  • src/cli.ts
  • src/local-agent-acp.test.ts
  • src/local-agent-acp.ts
  • src/local-agent-adapters.test.ts
  • src/local-agent-adapters.ts
  • src/local-agent-availability.test.ts
  • src/local-agent-availability.ts
  • src/local-agent-claude.test.ts
  • src/local-agent-claude.ts
  • src/local-agent-codex.test.ts
  • src/local-agent-codex.ts
  • src/local-agent-manager.test.ts
  • src/local-agent-manager.ts
  • src/local-agent-opencode.test.ts
  • src/local-agent-opencode.ts
  • src/local-agent-pi.test.ts
  • src/local-agent-pi.ts
  • src/local-agent-runtime.test.ts
  • src/local-agent-runtime.ts

Comment on lines +37 to +38
`continue <id> "<prompt>"` sends a follow-up to an existing agent. Do not use
`run <id>` for continuation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files 'skills/subagent-delegation/SKILL.md' 'src/cli.ts' 'package.json' 'README.md'
printf '%s\n' '--- skill structure ---'
ast-grep outline skills/subagent-delegation/SKILL.md --lang markdown 2>/dev/null || true
printf '%s\n' '--- relevant skill content ---'
cat -n skills/subagent-delegation/SKILL.md | sed -n '1,120p'
printf '%s\n' '--- CLI references ---'
rg -n -C 8 'agents (run|continue)|continue|thinking|model' src/cli.ts
printf '%s\n' '--- package and host-facing entry points ---'
rg -n -C 4 '"(bin|scripts|packageManager|engines)"|agents (run|continue)|subagent-delegation|SKILL' package.json README.md .github 2>/dev/null || true

Repository: Waishnav/devspace

Length of output: 10063


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
git ls-files 'skills/subagent-delegation/SKILL.md' 'src/cli.ts' 'package.json' 'README.md'
printf '%s\n' '--- skill content ---'
cat -n skills/subagent-delegation/SKILL.md | sed -n '1,120p'
printf '%s\n' '--- CLI references ---'
rg -n -C 8 'agents (run|continue)|continue|thinking|model' src/cli.ts
printf '%s\n' '--- package and host-facing entry points ---'
rg -n -C 4 '"(bin|scripts|packageManager|engines)"|agents (run|continue)|subagent-delegation|SKILL' package.json README.md .github 2>/dev/null || true

Repository: Waishnav/devspace

Length of output: 9980


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- CLI imports and parser definitions ---'
cat -n src/cli.ts | sed -n '1,90p'
rg -n -C 12 'function parseLocalAgent(Run|Continue)Args|parseLocalAgent(Run|Continue)Args|type .*Agent.*Args|interface .*Agent.*Args|--model|--thinking' src
printf '%s\n' '--- agent client continuation call graph ---'
rg -n -C 10 'continue\(|model\??:|thinking\??:|LocalAgent' src | head -n 260
printf '%s\n' '--- tests and docs for continuation overrides ---'
rg -n -C 8 'agents continue|parseLocalAgentContinueArgs|--model|--thinking' --glob '!dist/**' --glob '!node_modules/**' .

Repository: Waishnav/devspace

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- continuation implementation ---'
cat -n src/local-agent-client.ts | sed -n '1,180p'
rg -n -C 14 'async continue|continue\(agentId|RunOverrides|overrides.*model|overrides.*thinking' src/local-agent-manager.ts src/local-agent-daemon.ts src/local-agent-daemon-protocol.ts src/local-agent-client.ts
printf '%s\n' '--- packaged skill discovery and installation path ---'
rg -n -C 8 'skills|SKILL.md|DEVSPACE_SKILLS|ensureDevspaceDefaultSkills|files' src scripts package.json docs README.md | head -n 260
printf '%s\n' '--- focused continuation parser test presence ---'
cat -n src/local-agent-targets.test.ts | sed -n '1,115p'
printf '%s\n' '--- read-only behavioral verifier for the documented continuation forms ---'
python3 - <<'PY'
from dataclasses import dataclass

`@dataclass`
class Parsed:
    agentId: str
    prompt: str
    model: str | None = None
    thinking: str | None = None

def parse_continue(args):
    target, *rest = args
    if not target:
        raise ValueError("usage")
    model = thinking = None
    prompt_parts = []
    i = 0
    while i < len(rest):
        part = rest[i]
        if part == "--model":
            if i + 1 >= len(rest) or not rest[i+1].strip():
                raise ValueError("Missing value for --model.")
            model = rest[i+1].strip()
            i += 2
            continue
        if part.startswith("--model="):
            value = part[len("--model="):].strip()
            if not value:
                raise ValueError("Missing value for --model.")
            model = value
            i += 1
            continue
        if part == "--thinking":
            if i + 1 >= len(rest) or not rest[i+1].strip():
                raise ValueError("Missing value for --thinking.")
            thinking = rest[i+1].strip()
            i += 2
            continue
        if part.startswith("--thinking="):
            value = part[len("--thinking="):].strip()
            if not value:
                raise ValueError("Missing value for --thinking.")
            thinking = value
            i += 1
            continue
        prompt_parts.append(part)
        i += 1
    prompt = " ".join(prompt_parts).strip()
    if not prompt:
        raise ValueError("usage")
    return Parsed(target, prompt, model, thinking)

cases = [
    (["agt_1", "--model", "gpt-5.1", "follow up"], Parsed("agt_1", "follow up", "gpt-5.1", None)),
    (["agt_1", "--thinking=high", "follow up"], Parsed("agt_1", "follow up", None, "high")),
    (["agt_1", "--model", "gpt-5.1", "--thinking", "high", "follow", "up"],
     Parsed("agt_1", "follow up", "gpt-5.1", "high")),
]
for argv, expected in cases:
    actual = parse_continue(argv)
    assert actual == expected, (argv, actual, expected)
print("continuation override forms parse as model/thinking values and preserve the prompt")
PY

Repository: Waishnav/devspace

Length of output: 50373


Document continuation option overrides.

Because devspace agents continue accepts and forwards --model and --thinking, add examples for both options to the continuation section.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/subagent-delegation/SKILL.md` around lines 37 - 38, Update the
continuation section describing `continue <id> "<prompt>"` to include examples
demonstrating both supported overrides, `--model` and `--thinking`, while
preserving the existing guidance not to use `run <id>` for continuation.

Comment on lines 82 to 97
function codexAvailability(env: NodeJS.ProcessEnv): LocalAgentProviderAvailability {
const command = resolveCodexCommand(env);
if (!command) {
return { name: "codex", available: false, reason: "codex executable not found" };
}
if (!isCodexAppServerSupported(command.executable, codexCommandEnvironment(env))) {
return { name: "codex", available: false, reason: "codex app-server is not supported" };
}
return { name: "codex", available: true };
return commandAvailability("codex", env.CODEX_COMMAND ?? "codex", env);
}

function commandAvailability(
provider: LocalAgentProvider,
command: string,
options: { env?: NodeJS.ProcessEnv } = {},
env: NodeJS.ProcessEnv,
): LocalAgentProviderAvailability {
const executable = resolveCommand(command, options.env);
if (!executable) {
return {
name: provider,
available: false,
reason: `${command} executable not found`,
};
}

return { name: provider, available: true };
if (resolveCommand(command, env)) return { name: provider, available: true };
return {
name: provider,
available: false,
reason: `${command} executable not found`,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Trace availability reporting surfaces and remaining app-server checks.
set -euo pipefail

rg -nP --type=ts -C4 '\b(getLocalAgentProviderAvailabilitySnapshot|formatLocalAgentProviderAvailabilitySummary)\s*\('
echo "---- app-server support checks ----"
rg -nP --type=ts -C4 'app-server|isCodexAppServerSupported'
echo "---- docs mentioning codex availability ----"
rg -n --glob '*.md' -C3 'app-server|codex executable'

Repository: Waishnav/devspace

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- candidate files ----"
git ls-files | grep -E '(^|/)(local-agent|server|cli|daemon|README|AGENTS).*' | head -200

echo "---- availability identifiers and app-server terms ----"
rg -n -C3 'getLocalAgentProviderAvailabilitySnapshot|formatLocalAgentProviderAvailabilitySummary|codexAvailability|commandAvailability|isCodexAppServerSupported|app-server|Codex provider is not available' . --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' || true

echo "---- local-agent file outlines ----"
for f in $(git ls-files 'src/local-agent*.ts'); do
  echo "### $f"
  ast-grep outline "$f" 2>/dev/null || true
done

Repository: Waishnav/devspace

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- availability implementation ----"
cat -n src/local-agent-availability.ts | sed -n '1,135p'

echo "---- CLI status output ----"
cat -n src/cli.ts | sed -n '180,230p'

echo "---- server startup output and snapshot ----"
cat -n src/server.ts | sed -n '1675,1710p'
cat -n src/server.ts | sed -n '1895,1932p'

echo "---- manager runtime creation and error propagation ----"
cat -n src/local-agent-manager.ts | sed -n '45,125p'
cat -n src/local-agent-manager.ts | sed -n '200,335p'

echo "---- daemon protocol/client error surfaces ----"
cat -n src/local-agent-daemon.ts | sed -n '100,300p'
cat -n src/local-agent-client.ts | sed -n '40,180p'
cat -n src/local-agent-daemon-protocol.ts | sed -n '1,80p'

echo "---- relevant docs ----"
rg -n -C3 'app-server|subagent providers|codex' README.md docs .agents/plan AGENTS.md --glob '*.md' | head -240

echo "---- relevant tests ----"
rg -n -C4 'app-server|availability|subagent providers|codex executable|does not support' src/*.test.ts | head -260

Repository: Waishnav/devspace

Length of output: 49902


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- manager begin/start lifecycle ----"
cat -n src/local-agent-manager.ts | sed -n '120,205p'

echo "---- server local-agent handlers and client calls ----"
rg -n -C5 'localAgentClient|agent\.start|agent\.continue|agent\.get|agent\.list|subagent' src/server.ts src/*.ts --glob '!src/local-agent-manager.ts' | head -320

echo "---- availability and Codex tests ----"
cat -n src/local-agent-availability.test.ts | sed -n '1,100p'
cat -n src/local-agent-codex.test.ts | sed -n '1,85p'
cat -n src/local-agent-codex.ts | sed -n '186,220p'

echo "---- focused read-only invariant verifier ----"
python3 - <<'PY'
from pathlib import Path

availability = Path("src/local-agent-availability.ts").read_text()
cli = Path("src/cli.ts").read_text()
server = Path("src/server.ts").read_text()
codex = Path("src/local-agent-codex.ts").read_text()
docs = Path("docs/agent-profile-schema.md").read_text()

checks = {
    "codex availability delegates to executable-only command check":
        'return commandAvailability("codex", env.CODEX_COMMAND ?? "codex", env);' in availability,
    "summary has no app-server wording":
        "app-server" not in availability,
    "cli prints availability summary":
        "formatLocalAgentProviderAvailabilitySummary(localAgentProviders)" in cli,
    "server snapshots availability":
        "getLocalAgentProviderAvailabilitySnapshot()" in server,
    "server prints availability summary":
        "formatLocalAgentProviderAvailabilitySummary(localAgentProviders)" in server,
    "runtime checks app-server support":
        "isCodexAppServerSupported(command.executable, this.env)" in codex,
    "runtime error names app-server requirement":
        "the installed codex does not support app-server." in codex,
    "docs name codex app-server command":
        "`codex app-server` command" in docs,
}
for name, result in checks.items():
    print(f"{'PASS' if result else 'FAIL'}: {name}")
PY

Repository: Waishnav/devspace

Length of output: 30322


Explain the Codex app-server requirement in availability output.

The CLI and server summaries report Codex as available when only the executable exists. createRuntime then rejects installations without app-server support, and the daemon initially returns a running record before storing the later failure. Keep the side-effect-free check, but label it as executable-only and show the app-server requirement in these user-facing summaries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-availability.ts` around lines 82 - 97, Update
codexAvailability and the related CLI/server availability summaries to
distinguish executable-only detection from full Codex availability, explicitly
stating that app-server support is required. Keep commandAvailability
side-effect-free and preserve its executable resolution check, but ensure
user-facing output does not report Codex as fully available when app-server
support is absent.

Comment on lines 105 to +115
const extensions = process.platform === "win32"
? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD")
.split(";")
.filter(Boolean)
? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)
: [""];
const candidates: string[] = [];
for (const directory of path.split(delimiter)) {
if (!directory) continue;
for (const extension of extensions) {
candidates.push(resolve(directory, `${command}${extension}`));
const candidate = resolve(directory, `${command}${extension}`);
if (executableExists(candidate)) return candidate;
}
}
return candidates;
return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Windows lookup misses a command that already carries an extension.

On win32, extensions contains only PATHEXT entries. It never contains an empty string. So resolveCommand probes codex.exe.COM, codex.exe.EXE, and so on, and it never probes codex.exe itself. A user who sets CODEX_COMMAND=codex.exe, CURSOR_COMMAND=cursor-agent.cmd, or COPILOT_COMMAND=copilot.exe without a path separator gets available: false even when the executable is on PATH.

Probe the bare name first, then the PATHEXT variants.

🐛 Proposed fix
   const extensions = process.platform === "win32"
-    ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)
+    ? ["", ...(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)]
     : [""];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const extensions = process.platform === "win32"
? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD")
.split(";")
.filter(Boolean)
? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)
: [""];
const candidates: string[] = [];
for (const directory of path.split(delimiter)) {
if (!directory) continue;
for (const extension of extensions) {
candidates.push(resolve(directory, `${command}${extension}`));
const candidate = resolve(directory, `${command}${extension}`);
if (executableExists(candidate)) return candidate;
}
}
return candidates;
return undefined;
const extensions = process.platform === "win32"
? ["", ...(env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)]
: [""];
for (const directory of path.split(delimiter)) {
if (!directory) continue;
for (const extension of extensions) {
const candidate = resolve(directory, `${command}${extension}`);
if (executableExists(candidate)) return candidate;
}
}
return undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-availability.ts` around lines 105 - 115, Update
resolveCommand so the Windows extensions list always probes the bare command
name before PATHEXT variants by including an empty extension first. Preserve the
existing PATHEXT fallback and path iteration behavior, ensuring commands already
ending in extensions such as .exe or .cmd are checked directly.

Comment thread src/local-agent-claude.ts
Comment on lines +166 to +168
runtimeKey(context: LocalAgentRuntimeContext): string {
return `claude:${context.agentId}`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does the Claude Agent SDK query object apply sandbox settings changes to a running session at runtime?

💡 Result:

The Claude Agent SDK does not support applying changes to sandbox settings at runtime for a running session [1][2]. While the SDK provides an applyFlagSettings() method (available in the Query object) to update certain settings on a running session, this method is intended for general settings like permissions or models [1][3]. The sandbox configuration is defined within the ClaudeAgentOptions object at the time the session (or query instance) is initialized [2][4]. If you need to change sandbox settings mid-session, you must start a new session or reinitialize the query [3]. Additionally, some configurations, such as system prompt options, are resolved once at startup and also require a new session to modify [3]. For dynamic control over tool operations (such as redirecting file access) while a session is running, you can use hooks, which allow you to intercept and modify tool inputs programmatically [5][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target file outline ---'
ast-grep outline src/local-agent-claude.ts
printf '%s\n' '--- relevant Claude implementation ---'
sed -n '130,240p' src/local-agent-claude.ts
printf '%s\n' '--- runtime key and write-mode references ---'
rg -n -C 4 'runtimeKey|writeMode|applyFlagSettings|claudeQueryOptions|sandbox' src/local-agent-claude.ts src/local-agent-acp.ts src

Repository: Waishnav/devspace

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- Claude runtime turn behavior ---'
sed -n '60,115p' src/local-agent-claude.ts
printf '%s\n' '--- Claude authority and environment helpers ---'
sed -n '241,318p' src/local-agent-claude.ts
printf '%s\n' '--- runtime-pool reuse path ---'
sed -n '175,235p' src/local-agent-runtime-pool.ts
printf '%s\n' '--- Claude tests around reuse and mode transitions ---'
sed -n '1,155p' src/local-agent-claude.test.ts
printf '%s\n' '--- context and runtime-key contracts ---'
rg -n -C 3 'interface LocalAgentRuntimeContext|type LocalAgentRuntimeContext|agentId:|workspaceRoot:|runtimeKey\\(' src --glob '*.ts'

Repository: Waishnav/devspace

Length of output: 12159


🌐 Web query:

site:code.claude.com/docs/en/agent-sdk/typescript applyFlagSettings sandbox options query runtime

💡 Result:

The applyFlagSettings function is a method within the Agent SDK (specifically documented for TypeScript) used to update configuration settings during an active session [1]. It allows developers to modify settings for a running session without needing to restart the query [1]. This method is intended for use cases where a specific setting lacks a dedicated setter function, enabling dynamic configuration changes mid-runtime [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- official Claude SDK documentation references ---'
curl -L --fail --silent --show-error 'https://code.claude.com/docs/en/agent-sdk/typescript' |
  rg -n -i -C 4 'applyFlagSettings|sandbox|ClaudeAgentOptions|query' | head -n 160
printf '%s\n' '--- runtime context declarations and driver call sites ---'
rg -n -F -C 4 'LocalAgentRuntimeContext' src --glob '*.ts'
rg -n -F -C 4 'driver.runtimeKey' src --glob '*.ts'

Repository: Waishnav/devspace

Length of output: 50374


🌐 Web query:

Claude Agent SDK TypeScript applyFlagSettings sandbox setting change running query implementation

💡 Result:

In the Claude Agent SDK for TypeScript, the applyFlagSettings function is designed to modify specific settings on a running session without requiring a restart [1][2]. While applyFlagSettings allows for runtime adjustments of various configuration keys (such as model, agent, or effortLevel), it is primarily used for general settings keys [1]. According to official documentation and SDK behavior, sandbox settings are typically defined during session initialization via the options object passed to query or other session creation functions [1][3]. Key points regarding the use of applyFlagSettings and sandbox configuration: 1. Runtime Adjustments: applyFlagSettings is specifically intended for scenarios where a setting has no dedicated setter and needs to be updated mid-session [1]. While it accepts a subset of settings keys, sandbox-specific configuration is generally managed through the immutable initialization options for a session to ensure the integrity of the isolated environment [4][3]. 2. Sandbox Configuration: Sandbox settings are defined using the SandboxSettings interface within the session options, which includes parameters like enabled, excludedCommands, and allowUnsandboxedCommands [3]. These configurations are established when the agent loop starts [1][5]. 3. Precedence: The SDK uses a hierarchical precedence model for settings, where programmatic options (passed to query) take precedence over filesystem-based settings [2]. Settings applied via applyFlagSettings fall into the "flag-settings" layer, which is designed for programmatic overrides [1][2]. 4. Implementation: If you need to change sandbox behavior, you should generally instantiate a new session with the desired SandboxSettings, as changing fundamental security and isolation boundaries (like the sandbox) on a running process is often restricted to maintain environment stability [1][4]. For authoritative guidance, refer to the TypeScript SDK documentation, which highlights that applyFlagSettings is best used for non-structural settings like model switching or adjusting effort levels while an agent loop is active [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
from urllib.request import urlopen

source = Path("src/local-agent-claude.ts").read_text()
key = re.search(r'return `([^`]*context\.agentId[^`]*)`;', source)
assert key, "Claude runtime key not found"
print("runtime_key_template:", key.group(1))
print("same_key_for_modes:", all(
    key.group(1).replace("${context.agentId}", "agt")
    == key.group(1).replace("${context.agentId}", "agt")
    for _ in ["read_only", "allowed", "full_access"]
))

def authority(mode):
    if mode == "full_access":
        return {"enabled": False, "allowUnsandboxedCommands": True}
    return {"enabled": True, "allowUnsandboxedCommands": False}

print("sandbox_by_mode:", {mode: authority(mode) for mode in ["read_only", "allowed", "full_access"]})
assert len({repr(authority(mode)) for mode in ["read_only", "allowed", "full_access"]}) == 3

html = urlopen("https://code.claude.com/docs/en/agent-sdk/typescript").read().decode()
for term in ["applyFlagSettings", "SandboxSettings", "sandbox configuration"]:
    positions = [m.start() for m in re.finditer(term, html, re.I)]
    print(f"{term}_occurrences:", len(positions))
    for pos in positions[:2]:
        excerpt = re.sub(r"<[^>]+>", " ", html[max(0, pos-500):pos+900])
        excerpt = re.sub(r"\s+", " ", excerpt)
        print(excerpt[:1200])
PY

Repository: Waishnav/devspace

Length of output: 536


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re

source = Path("src/local-agent-claude.ts").read_text()
key = re.search(r'return `([^`]*context\.agentId[^`]*)`;', source)
assert key, "Claude runtime key not found"
template = key.group(1)
print("runtime_key_template:", template)
print("all_write_modes_share_key: True")

assert "context.writeMode" not in template

modes = ["read_only", "allowed", "full_access"]
sandbox = {
    "read_only": {"enabled": True, "allowUnsandboxedCommands": False},
    "allowed": {"enabled": True, "allowUnsandboxedCommands": False},
    "full_access": {"enabled": False, "allowUnsandboxedCommands": True},
}
assert sandbox["read_only"] == sandbox["allowed"]
assert sandbox["full_access"] != sandbox["read_only"]
print("full_access_differs_from_restricted_modes: True")
print("sandbox_by_mode:", sandbox)
PY

Repository: Waishnav/devspace

Length of output: 502


Include the write mode in the Claude runtime key.

The runtime pool reuses the same Claude query for every write mode. A full_access query disables the sandbox and can serve a later restricted turn. Include context.writeMode in the key, as the ACP driver does.

🧰 Tools
🪛 ast-grep (0.45.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-claude.ts` around lines 166 - 168, Update
LocalAgentClaude.runtimeKey to include context.writeMode alongside
context.agentId, ensuring runtime pool entries remain distinct for each write
mode while preserving the existing Claude key prefix.

Source: Coding guidelines

Comment on lines +318 to 320
if (record.workspaceId !== scope.workspaceId) {
throw new Error(`Subagent ${record.id} belongs to a different workspace.`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the scope type and every caller of the workspace-scoped manager methods.
ast-grep outline src --items all --match 'LocalAgentWorkspaceScope|LocalAgentListScope'
rg -nP --type=ts -C4 'LocalAgentWorkspaceScope\b'
rg -nP --type=ts -C4 '\.(continue|get)\(\s*[^)]*(workspaceRoot|scope)' -g '!**/*.test.ts'

Repository: Waishnav/devspace

Length of output: 527


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- scope and manager definitions ---'
rg -n -C8 'interface LocalAgentWorkspaceScope|type LocalAgentWorkspaceScope|class LocalAgentManager|async? (get|continue)|\b(get|continue)\(' src/local-agent-store.ts src/local-agent-manager.ts src -g '*.ts' | head -n 500

printf '%s\n' '--- all LocalAgentManager method calls and scope construction ---'
rg -n -C5 --type=ts 'localAgentManager|agentManager|LocalAgentWorkspaceScope|decodeWorkspaceScope|workspaceRoot:|workspaceId:' src | head -n 800

printf '%s\n' '--- protocol decoders ---'
cat -n src/local-agent-daemon-protocol.ts | sed -n '1,260p'

Repository: Waishnav/devspace

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- manager workspace assertion and nearby methods ---'
cat -n src/local-agent-manager.ts | sed -n '90,155p;300,345p'

printf '%s\n' '--- protocol scope decoders and request schemas ---'
rg -n -C12 'decodeWorkspaceScope|decodeContinueInput|decodeAgent.*Input|agent\.continue|agent\.get' src/local-agent-daemon-protocol.ts src/local-agent-daemon.ts

printf '%s\n' '--- direct client and manager call sites ---'
rg -n -C3 --type=ts '\.(continue|get)\([^;]*\b(scope|workspaceRoot|workspaceId)|\.continue\(|\.get\(' src/local-agent-client.ts src/local-agent-daemon.ts src/server.ts src -g '!local-agent-codex.ts' -g '!local-agent-acp.ts' | rg -v 'sqlite|Map|pending|turns|registry|database|\.get\(' | head -n 300

Repository: Waishnav/devspace

Length of output: 31544


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- manager tests and record shapes ---'
cat -n src/local-agent-manager.test.ts | sed -n '1,190p'

printf '%s\n' '--- all local-agent client method invocations ---'
rg -n -C4 --type=ts '\b(localAgentClient|agentClient|client)\.(start|run|continue|get|list)\(' src | head -n 400

printf '%s\n' '--- all workspaceId assignments in local-agent inputs and records ---'
rg -n -C4 --type=ts 'workspaceId' src/local-agent-*.ts | head -n 600

Repository: Waishnav/devspace

Length of output: 24527


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- current workspace scope resolution ---'
rg -n -C12 'function resolveCurrentWorkspaceScope|const resolveCurrentWorkspaceScope|resolveCurrentWorkspaceScope' src/cli.ts src -g '*.ts' | head -n 240

printf '%s\n' '--- daemon client scope signatures and protocol tests ---'
cat -n src/local-agent-client.ts | sed -n '45,105p'
rg -n -C5 --type=ts 'workspaceId.*DEVSPACE_WORKSPACE_ID|DEVSPACE_WORKSPACE_ID.*workspaceId|workspaceId.*scope|scope.*workspaceId' src/cli.ts src/local-agent-*.test.ts src | head -n 300

printf '%s\n' '--- behavioral probe of the assertion condition ---'
python3 - <<'PY'
cases = [
    ("stored workspace ID, root-only scope", "ws_1", None, "/workspace", "/workspace"),
    ("legacy record, matching root-only scope", None, None, "/workspace", "/workspace"),
    ("stored workspace ID, matching full scope", "ws_1", "ws_1", "/workspace", "/workspace"),
    ("stored workspace ID, wrong ID", "ws_1", "ws_2", "/workspace", "/workspace"),
]
for label, record_id, scope_id, record_root, scope_root in cases:
    root_mismatch = scope_root != record_root
    id_mismatch = record_id != scope_id
    print(f"{label}: root_mismatch={root_mismatch}, id_mismatch={id_mismatch}, rejects={root_mismatch or id_mismatch}")
PY

Repository: Waishnav/devspace

Length of output: 19284


Make workspace scope mandatory for get and continue.

resolveCurrentWorkspaceScope() can omit workspaceId, so matching root-only scopes reject records with a stored ID. The optional manager scope also lets direct callers skip assertAgentWorkspace. Require workspaceId and scope, then use distinct errors for ID and root mismatches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-manager.ts` around lines 318 - 320, Update the get and
continue flows in the agent manager to require both a workspace scope and its
workspaceId, and ensure direct callers invoke assertAgentWorkspace before
accessing records. In the workspace validation around record.workspaceId and
scope, distinguish mismatched workspace IDs from mismatched workspace roots with
separate errors, while preserving validation for matching scopes.

@Waishnav
Waishnav force-pushed the codex/subagent-runtime-overhaul branch from da68ab3 to aca2f57 Compare August 13, 2026 07:56
@Waishnav Waishnav closed this Aug 13, 2026
@Waishnav Waishnav reopened this Aug 13, 2026
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