Root promotion: request_directory gains primary; granted folder can become the workspace

One-way, once, scratch-primary sessions only; consent card says workspace.
Live roots + shell repoint, persisted; engine rebuilds fully anchored after the turn.
This commit is contained in:
Rohit C Prasad
2026-08-20 21:37:39 -07:00
parent 65c568cd44
commit ebb97d7643
9 changed files with 272 additions and 22 deletions
+1 -1
View File
@@ -751,7 +751,7 @@ export function App() {
if (unattendedRef.current) break;
setItems((p) => [
...p,
{ kind: "dirreq", reason: d.reason || "", path: d.path || "", writable: !!d.writable },
{ kind: "dirreq", reason: d.reason || "", path: d.path || "", writable: !!d.writable, primary: !!d.primary },
]);
break;
case "tool_requested":
@@ -26,9 +26,19 @@ export function DirectoryRequestCard({
<div className="dirreq-card">
<div className="dirreq-head">
<Icon name="folderPlus" size={16} className="ico" />
<span>The agent is requesting access to a folder</span>
<span>
{item.primary
? "The agent asks to make a folder this session's workspace"
: "The agent is requesting access to a folder"}
</span>
</div>
{item.reason && <div className="dirreq-reason">{item.reason}</div>}
{item.primary && (
<div className="dirreq-reason">
Granting makes this folder the session's primary working directory (read-write);
the scratch directory stays available for temporary files and artifacts.
</div>
)}
<div className="dirreq-pathrow">
<input
className="dirreq-path"
@@ -41,16 +51,22 @@ export function DirectoryRequestCard({
</button>
</div>
<div className="dirreq-actions">
<label className="dirreq-access">
<input type="checkbox" checked={writable} onChange={(e) => setWritable(e.target.checked)} />
Allow writing (read-write)
</label>
{!item.primary && (
<label className="dirreq-access">
<input type="checkbox" checked={writable} onChange={(e) => setWritable(e.target.checked)} />
Allow writing (read-write)
</label>
)}
<span className="spacer" />
<button className="btn" onClick={() => onRespond(false)}>
Decline
</button>
<button className="btn primary" disabled={!path.trim()} onClick={() => onRespond(true, path.trim(), writable)}>
Grant access
<button
className="btn primary"
disabled={!path.trim()}
onClick={() => onRespond(true, path.trim(), item.primary ? true : writable)}
>
{item.primary ? "Make workspace" : "Grant access"}
</button>
</div>
</div>
+10 -2
View File
@@ -378,11 +378,19 @@ export function InboxItemCard({
onClick={() =>
onResolve(
item.id,
JSON.stringify({ granted: true, path: item.data?.path || "", writable: !!item.data?.writable }),
JSON.stringify({
granted: true,
path: item.data?.path || "",
writable: item.data?.primary ? true : !!item.data?.writable,
}),
)
}
>
{item.data?.path ? "Grant" : "Grant (no folder)"}
{item.data?.path
? item.data?.primary
? "Make workspace"
: "Grant"
: "Grant (no folder)"}
</button>
<button className={BTN_BORDERED} onClick={() => onResolve(item.id, JSON.stringify({ granted: false }))}>
Deny
+1
View File
@@ -144,6 +144,7 @@ export type Item =
reason: string;
path?: string;
writable?: boolean;
primary?: boolean; // root promotion: the folder becomes the session's workspace
resolved?: "granted" | "denied";
}
| {