fix(web): address the review of the workspace-file route

Isolation is restored on the premise the review corrected: a workspace file
need not be agent-authored — a read row makes every file in a cloned
repository openable — and a same-origin active document was measured driving
/api/settings.describe to a 200 with full data. Script-capable documents go
back into an opaque origin; the preview's lost localStorage is the known cost,
and a separate serving origin is the way to retire it.

- confine(): a workspace rooted at a filesystem root has a realpath already
  ending in the separator, and the doubled prefix 403'd every child.
- turnDeliverables(): reset on the turn boundary, not only at a closing
  assistant, so an interrupted turn cannot spill into the next turn's row;
  and recognize a mutation by render intent (diff card, or generic with
  kind 'edit') so str_replace_editor's insert counts.
- 405 answers name the methods it allows.
- The e2e now cold-seeds a recorded WRITE turn, so the assembled application
  covers the Produced row, its chip's served URL, and the isolation header.
- Agent Note matched to what shipped (the row is in this PR, not deferred);
  ui-conversation README documents the new destination and the row; the
  fixture lane's dead-tab quirk and the cold-path listing cost are recorded.
This commit is contained in:
ZiyaZhang
2026-08-01 01:08:16 -07:00
parent f5d53f04b7
commit dcf485ac5c
17 changed files with 205 additions and 81 deletions

View File

@@ -34,11 +34,15 @@ function fakeRequest(headers: Record<string, string>, url = `${API_PATH}/session
}
/** Response recorder compatible with both the fence's short-circuit and the bridge. */
function fakeResponse(): { response: ServerResponse; state: { status?: number; body?: unknown } } {
const state: { status?: number; body?: unknown } = {}
function fakeResponse(): { response: ServerResponse; state: { status?: number; body?: unknown; headers?: Record<string, string> } } {
const state: { status?: number; body?: unknown; headers?: Record<string, string> } = {}
const response = Object.assign(new EventEmitter(), {
writableEnded: false,
writeHead(value: number) { state.status = value; return this },
writeHead(value: number, headers?: Record<string, string>) {
state.status = value
if (headers !== undefined) state.headers = headers
return this
},
write() { return true },
end(this: { writableEnded: boolean }, value?: unknown) {
if (value !== undefined) state.body = value
@@ -177,6 +181,7 @@ describe('connection node half: the /f workspace-file route', () => {
Object.assign(post, { method: 'POST' })
await filesRoute(routes).handler(post, written.response)
expect(written.state.status).toBe(405)
expect(written.state.headers).toMatchObject({ allow: 'GET, HEAD' })
await dispose()
})