propose(agentic): read agent_workspace_root from request body (wire UI to merged #23) #28

Open
tim.lingo wants to merge 1 commits from propose/agent-workspace-root-read into main
Member

Proposal (for Will's review — please do not merge without sign-off)

Completes the UI↔soul contract for #23 (scope file/command tools to an agent workspace root), which is already merged to main.

The gap

#23 made the path/command tool guards read the root from state_get("agent_workspace_root") (or env NEURON_AGENT_ROOT). But nothing in the desktop UI ever set that state key — WorkspaceStore had deferred the push to "Phase 2 when #23 lands." #23 landed; the wiring didn't. Result: the agent panel's Workspace Folder is cosmetic — the user picks "Documents" expecting confinement, but with no root set the soul takes the default-allow path and file/command tools run unscoped.

The existing /api/neuron/config/tune endpoint can't fix this UI-side: it writes ConfigEntry engram nodes, a different store than the in-memory state state_get reads.

This change (11 lines, chat.el)

At the top of handle_chat_agentic, read agent_workspace_root from the request body and state_set it before tool dispatch. Mirrors the existing json_get + state_set pattern used throughout the file. Empty field ⇒ unscoped (backward-compatible) and still falls through to the NEURON_AGENT_ROOT env.

Pairs with

neuron-ui PR #32ChatRequest.agentWorkspaceRoot → body agent_workspace_root, sent on every agentic send (state.workspaceRoot).

Review questions for you

  1. Ownership model: I set state from the body every turn, so clearing the folder in the UI un-scopes. Alternative is only-set-when-non-empty (sticky root). Flagged inline — which do you want?
  2. Should this also gate on a config/feature flag, or is per-request body the right transport given the soul is the source of truth elsewhere?
  3. Your #23 note already flags that the lexical guard isn't a hard boundary (no symlink resolution, shell can cd out). This PR doesn't change that — it just makes the (soft) scope actually engage. Want the real confinement (cwd-locked exec / sandbox-exec) tracked separately?

No rush / not a blocker for the Windows build — surfacing it because the UI now advertises a workspace boundary that isn't enforced until this lands.

## Proposal (for Will's review — please do not merge without sign-off) Completes the UI↔soul contract for **#23** (scope file/command tools to an agent workspace root), which is already merged to `main`. ### The gap #23 made the path/command tool guards read the root from `state_get("agent_workspace_root")` (or env `NEURON_AGENT_ROOT`). But nothing in the desktop UI ever set that state key — `WorkspaceStore` had deferred the push to "Phase 2 when #23 lands." #23 landed; the wiring didn't. Result: the agent panel's **Workspace Folder is cosmetic** — the user picks "Documents" expecting confinement, but with no root set the soul takes the default-allow path and file/command tools run **unscoped**. The existing `/api/neuron/config/tune` endpoint can't fix this UI-side: it writes `ConfigEntry` engram nodes, a different store than the in-memory state `state_get` reads. ### This change (11 lines, `chat.el`) At the top of `handle_chat_agentic`, read `agent_workspace_root` from the request body and `state_set` it before tool dispatch. Mirrors the existing `json_get` + `state_set` pattern used throughout the file. Empty field ⇒ unscoped (backward-compatible) and still falls through to the `NEURON_AGENT_ROOT` env. ### Pairs with neuron-ui **PR #32** — `ChatRequest.agentWorkspaceRoot` → body `agent_workspace_root`, sent on every agentic send (`state.workspaceRoot`). ### Review questions for you 1. **Ownership model:** I set state from the body *every* turn, so clearing the folder in the UI un-scopes. Alternative is only-set-when-non-empty (sticky root). Flagged inline — which do you want? 2. Should this also gate on a config/feature flag, or is per-request body the right transport given the soul is the source of truth elsewhere? 3. Your #23 note already flags that the lexical guard isn't a hard boundary (no symlink resolution, shell can `cd` out). This PR doesn't change that — it just makes the (soft) scope actually engage. Want the real confinement (`cwd`-locked exec / `sandbox-exec`) tracked separately? No rush / not a blocker for the Windows build — surfacing it because the UI now advertises a workspace boundary that isn't enforced until this lands.
tim.lingo added 1 commit 2026-06-20 00:57:12 +00:00
Completes the UI<->soul contract for #23 (scope file/command tools to an agent
workspace root). #23 made the tools read state_get("agent_workspace_root"), but
nothing set that key from the desktop UI, so the agent panel's Workspace Folder
was cosmetic and tools ran unscoped (default-allow). This reads the root the UI
now sends on each agentic request and state_sets it before tool dispatch, so
agent_workspace_root() picks it up for the turn.

Minimal + pattern-matching (same json_get/state_set shape used throughout chat.el).
Empty body field => unscoped (backward-compatible) and preserves the env fallback.

FOR WILL'S REVIEW — do not merge without sign-off:
- Ownership model: set state from body each turn (so clearing the folder un-scopes)
  vs. only-when-nonempty. Flagged inline.
- Pairs with neuron-ui PR #32 (ChatRequest.agentWorkspaceRoot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All checks were successful
Neuron Soul CI / build (pull_request) Successful in 7m45s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin propose/agent-workspace-root-read:propose/agent-workspace-root-read
git checkout propose/agent-workspace-root-read
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: neuron-technologies/neuron#28