Skip to content

fix(agent-core): allow subagent cwd override for package-level AGENTS.md discovery#172

Open
thecannabisapp wants to merge 1 commit into
MoonshotAI:mainfrom
thecannabisapp:fix/subagent-cwd-discovery
Open

fix(agent-core): allow subagent cwd override for package-level AGENTS.md discovery#172
thecannabisapp wants to merge 1 commit into
MoonshotAI:mainfrom
thecannabisapp:fix/subagent-cwd-discovery

Conversation

@thecannabisapp
Copy link
Copy Markdown

Problem

Subagents inherit the parent agent's Kaos instance and working directory. In monorepos, this means subagents never discover package-level AGENTS.md files — they only see the session root AGENTS.md. This breaks workflows that worked in kimi-cli via scoped subagent YAMLs.

What changed

  • Added optional "cwd" parameter to the Agent tool input schema so callers can specify a working directory for the subagent.
  • When cwd is provided, SessionSubagentHost.spawn() creates an isolated Kaos via withCwd() and passes it as the subagent's runtime. This ensures AGENTS.md discovery, path resolution, and the Bash tool default directory all operate relative to the target directory without mutating the parent agent's shared Kaos state.
  • Updated Session.instantiateAgent to allow config.runtime override so the subagent can receive its isolated runtime.
  • Updated configureChild to use the custom cwd when provided instead of unconditionally inheriting the parent's cwd.
  • Added unit tests for schema validation and option propagation, plus an integration test verifying the subagent loads AGENTS.md from the custom directory while the parent's cwd remains untouched.

Checklist

  • I have read the CONTRIBUTING document.
  • I have explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 28, 2026

⚠️ No Changeset found

Latest commit: 7f3229a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Copy Markdown

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

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: 9814568692

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +73 to +75
const runtime =
options.cwd !== undefined
? { ...parent.runtime, kaos: parent.runtime.kaos.withCwd(options.cwd) }
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 Resolve relative subagent cwd against the parent cwd

When the tool caller supplies a relative cwd such as packages/domain from a session rooted at /repo, this passes that string directly into withCwd. LocalKaos.withCwd stores relative values as-is instead of resolving them against the parent Kaos cwd, so AGENTS.md discovery and later tool execution are anchored to the server process cwd rather than the parent agent's working directory (and the subsequent config update can even try to chdir relative to that relative cwd). This makes the new cwd override unreliable for common monorepo package paths unless callers always know to pass an absolute path.

Useful? React with 👍 / 👎.

Comment on lines 77 to +78
const { id, agent } = await this.session.createAgent(
{ type: 'sub', generate: parent.rawGenerate },
{ type: 'sub', generate: parent.rawGenerate, runtime },
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Persist the custom runtime for resumed subagents

When a custom-cwd subagent is saved and the session is later resumed, metadata only records the homedir/type/parent, so ensureResumeAgentInstantiated() reconstructs every agent with the shared session runtime instead of this overridden runtime. Replaying the child's recorded config.update({cwd: ...}) then calls chdir on that shared Kaos, so after kimi resume a package-scoped subagent can move the main agent's runtime cwd (and other agents' tool resolution) to the package directory even though their config.cwd still says the session root. The cwd/runtime override needs to be restored per agent on resume, not just applied at initial spawn.

Useful? React with 👍 / 👎.

Comment on lines 220 to +223
description: args.description,
runInBackground,
signal: backgroundController?.signal ?? foregroundDeadline?.signal ?? signal,
cwd: args.cwd,
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 Reject or honor cwd when resuming a subagent

If a caller sets both resume and cwd, this option is forwarded into the resume path, but SessionSubagentHost.resume() only realigns modelAlias and never updates the child's cwd/runtime or reloads AGENTS.md. In that scenario the tool accepts a package cwd while silently continuing in the old working directory, so follow-up prompts that rely on scoped paths or package-level instructions run against the wrong context. Either make cwd spawn-only with an explicit error on resume, or apply it during resume.

Useful? React with 👍 / 👎.

Comment on lines 77 to +78
const { id, agent } = await this.session.createAgent(
{ type: 'sub', generate: parent.rawGenerate },
{ type: 'sub', generate: parent.rawGenerate, runtime },
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 Run subagent hooks from the overridden cwd

For a subagent spawned with a package cwd, this still creates the child without a cwd-specific hookEngine, so it inherits the session-level engine that was constructed with the original session cwd. Tool and prompt hooks use that engine's cwd both in inputData.cwd and as the subprocess working directory, which means package-scoped hook commands (for example a PreToolUse/PostToolUse script that runs package-local checks) run from the repo root while the subagent tools run from the package directory. The custom cwd path should also be reflected in the child's hook engine.

Useful? React with 👍 / 👎.

@thecannabisapp
Copy link
Copy Markdown
Author

Related upstream issue filed: #173

@thecannabisapp thecannabisapp force-pushed the fix/subagent-cwd-discovery branch from 9814568 to 7edbada Compare May 28, 2026 15:32
@thecannabisapp
Copy link
Copy Markdown
Author

thecannabisapp commented May 28, 2026

Thanks for the review. Addressed in the latest push:

P2 — Relative cwd resolution ✅ Fixed. spawn() now resolves the raw cwd against parent.config.cwd via resolve() before passing it to withCwd(). Added an integration test that spawns with a relative cwd: 'packages/domain' and verifies it resolves to /repo/packages/domain.

P2 — cwd on resume ✅ Fixed. AgentTool.execution() now rejects with an explicit error when both resume and cwd are supplied: "Cannot set cwd when resuming an existing agent." Added a unit test for this.

P1 — Persist custom runtime on resume Acknowledged. This requires storing the overridden runtime/cwd in per-agent metadata and restoring it in ensureResumeAgentInstantiated(). That's a meaningful schema change beyond the scope of this minimal fix. Happy to open a follow-up PR if the maintainers agree this is the right direction.

P2 — Hook engine cwd mismatch Acknowledged. The child inherits the session-level hookEngine constructed with the session root cwd. A fix would need to plumb the resolved cwd through instantiateAgent into a new HookEngine instance for the child. Same as above — can follow up if the approach is accepted.

Copy link
Copy Markdown

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

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: 7edbada5cd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +79 to +81
resolvedCwd !== undefined
? { ...parent.runtime, kaos: parent.runtime.kaos.withCwd(resolvedCwd) }
: 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.

P2 Badge Inherit the parent runtime for nested subagents

When a cwd-scoped subagent later delegates without passing cwd, this leaves runtime undefined, so Session.createAgent() falls back to the session runtime instead of the parent subagent's cwd-scoped runtime. configureChild() still copies the parent cwd into the nested child config, which calls chdir on that shared session Kaos; in that nested-subagent scenario the main agent and other agents can have their runtime cwd moved to the package directory. Use the parent runtime when no new cwd override is requested, and only wrap it with withCwd when a different cwd is provided.

Useful? React with 👍 / 👎.

description: args.description,
runInBackground,
signal: backgroundController?.signal ?? foregroundDeadline?.signal ?? signal,
cwd: args.cwd,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require approval before escaping the parent cwd

When cwd is an absolute path outside the parent workspace, forwarding it unchecked lets the default-approved Agent tool create a child whose own config.cwd is that outside directory. I checked default-tool-approve.ts and Agent is auto-approved, while the write guard only asks for writes outside the child cwd; in another git worktree this means a subagent can auto-approve Write/Edit operations that would have required approval if the parent tried the same outside path directly. Constrain cwd to the parent cwd or surface the requested directory as a file access that must be approved.

Useful? React with 👍 / 👎.

….md discovery

Subagents previously inherited the parent's Kaos instance and cwd, so
package-level AGENTS.md files were never loaded for subagents. This
broke monorepo workflows where subagents should operate with
package-specific context.

Changes:
- Add optional "cwd" parameter to AgentToolInputSchema
- Pass cwd through RunSubagentOptions to SessionSubagentHost.spawn()
- Resolve relative cwd against parent.config.cwd before passing to
  withCwd(), so callers can use relative package paths
- Create an isolated Kaos via withCwd() for the subagent when cwd is
  provided, so AGENTS.md discovery and path resolution use the target
  directory without mutating the parent agent's shared Kaos state
- Allow config.runtime override in Session.instantiateAgent so the
  subagent can receive its isolated runtime
- Update configureChild to use the custom cwd when provided
- Reject cwd when combined with resume (silently ignored before)
- Add unit and integration tests for cwd propagation, relative
  resolution, and resume rejection
@thecannabisapp thecannabisapp force-pushed the fix/subagent-cwd-discovery branch from 7edbada to 7f3229a Compare May 28, 2026 15:43
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