From b77dd56adccde0226703fa4c0a08406361d95cf9 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 17:40:47 -0400 Subject: [PATCH 1/7] fix: bind edit_file writes to a verified workspace file --- .memory/edit-file-symlink-race-20261015.md | 9 ++ main/services/coding-tools.test.ts | 72 +++++++++ main/services/coding-tools.ts | 100 ++++++++++-- native/subagent-file-mutator/main.c | 142 ++++++++++++++++++ package.json | 5 +- .../aiden-subagent-file-mutator | Bin 86120 -> 102808 bytes .../native/darwin-universal/manifest.json | 6 +- .../linux-arm64/aiden-subagent-file-mutator | Bin 73672 -> 73896 bytes prebuilt/native/linux-arm64/manifest.json | 6 +- .../linux-x64/aiden-subagent-file-mutator | Bin 35552 -> 43976 bytes prebuilt/native/linux-x64/manifest.json | 6 +- scripts/ci-test-registry.json | 4 +- scripts/subagent-file-mutator.test.mjs | 57 +++++++ 13 files changed, 386 insertions(+), 21 deletions(-) create mode 100644 .memory/edit-file-symlink-race-20261015.md diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md new file mode 100644 index 000000000..52c7cc8ff --- /dev/null +++ b/.memory/edit-file-symlink-race-20261015.md @@ -0,0 +1,9 @@ +# Parent edit_file symlink-swap fix — 2026-10-15 + +Issue #423: the parent tool previously checked a canonical path, then used path-based `fs.writeFile` after a test seam. A target or ancestor symlink swap could redirect the final write outside the pinned workspace. + +The parent tool now reads an opened regular file, records its exact device/inode and SHA-256, and passes the canonical root-relative path to the existing packaged cross-platform file-mutator helper. Its new `edit-file` operation opens the pinned root and every relative directory component with no-follow descriptors, checks the opened file's identity and content, then truncates and writes only through that descriptor. The ordinary in-root symlink policy remains: TypeScript resolves a stable link to its in-root target before the native traversal. Multiply linked files fail closed so an outside hard link cannot be changed. The helper accepts up to 256 MiB, beyond the existing subagent protocol's 200 KB cap. + +The high-level tool test swaps the target or an ancestor between validation and commit and checks that the outside file is unchanged. Native tests exercise the same no-follow rule on macOS and Linux, and a separate test covers a stable in-root link and a file above 200 KB. The root test lane now builds the helper before coding-tools tests; the checked-in helper binaries and manifests were refreshed for macOS universal, Linux x64, and Linux arm64. No shared server protocol, native client, UI, onboarding, or plan status changed. + +Local verification: native mutator suite, CI test registry, native prebuilt manifest suite, scoped ESLint, and TypeScript type-check pass. The full coding-tools suite had one independent glob/native-glob comparison failure on `**/*`; the new edit tests passed. Hosted exact-head checks and review remain PR delivery gates. diff --git a/main/services/coding-tools.test.ts b/main/services/coding-tools.test.ts index 14a841840..441b9d205 100644 --- a/main/services/coding-tools.test.ts +++ b/main/services/coding-tools.test.ts @@ -13,6 +13,7 @@ import { promisify } from "node:util"; import test from "node:test"; import { buildCodingTools, + buildPinnedCodingTools, buildSubagentCodingTools, DISCLOSURE_APPROVAL_TOOL_NAMES, runCommandEnv, @@ -1330,6 +1331,77 @@ test("filesystem tools do not follow workspace symlinks outside the root", async } }); +test("pinned edit_file cannot overwrite an outside file after a target or ancestor swap", async () => { + for (const swappedPart of ["target", "ancestor"] as const) { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-edit-root-")); + const outside = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-edit-outside-")); + try { + const directory = path.join(root, "nested"); + await fs.mkdir(directory); + const target = path.join(directory, "edit.txt"); + const outsideFile = path.join(outside, "edit.txt"); + await fs.writeFile(target, "before\n"); + await fs.writeFile(outsideFile, "outside\n"); + const edit = buildPinnedCodingTools(root, { + beforeWriteCommit: async () => { + if (swappedPart === "target") { + await fs.rename(target, path.join(directory, "original.txt")); + await fs.symlink(outsideFile, target); + } else { + await fs.rename(directory, path.join(root, "original-directory")); + await fs.symlink(outside, directory); + } + }, + }).find((tool) => tool.name === "edit_file"); + assert.ok(edit); + await assert.rejects( + edit.execute("swap", { path: "nested/edit.txt", old_string: "before", new_string: "after" }), + /changed before the edit could be committed/u, + ); + assert.equal(await fs.readFile(outsideFile, "utf8"), "outside\n"); + const original = swappedPart === "target" + ? path.join(directory, "original.txt") + : path.join(root, "original-directory", "edit.txt"); + assert.equal(await fs.readFile(original, "utf8"), "before\n"); + } finally { + await fs.rm(root, { recursive: true, force: true }); + await fs.rm(outside, { recursive: true, force: true }); + } + } +}); + +test("pinned edit_file follows a stable in-root file symlink", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-edit-in-root-")); + try { + const target = path.join(root, "actual.txt"); + const alias = path.join(root, "alias.txt"); + await fs.writeFile(target, "before\n"); + await fs.symlink(target, alias); + const edit = buildPinnedCodingTools(root).find((tool) => tool.name === "edit_file"); + assert.ok(edit); + await edit.execute("alias", { path: "alias.txt", old_string: "before", new_string: "after" }); + assert.equal(await fs.readFile(target, "utf8"), "after\n"); + assert.equal((await fs.lstat(alias)).isSymbolicLink(), true); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } +}); + +test("pinned edit_file keeps ordinary files above the subagent content cap editable", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-edit-large-")); + try { + const target = path.join(root, "large.txt"); + const original = `${"x".repeat(220_000)}before\n`; + await fs.writeFile(target, original); + const edit = buildPinnedCodingTools(root).find((tool) => tool.name === "edit_file"); + assert.ok(edit); + await edit.execute("large", { path: "large.txt", old_string: "before", new_string: "after" }); + assert.equal(await fs.readFile(target, "utf8"), original.replace("before", "after")); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } +}); + test("run_command reports real exit failures through Pi and codemode without re-executing effects", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "aiden-command-outcome-")); try { diff --git a/main/services/coding-tools.ts b/main/services/coding-tools.ts index 40bd281cd..ee9ca3a6c 100644 --- a/main/services/coding-tools.ts +++ b/main/services/coding-tools.ts @@ -6,7 +6,8 @@ // Tool inputs use typebox schemas (pi's AgentTool.parameters), matching tools.ts. import { isImageAspectRatio } from "../../renderer/shared/image-generation/aspect-ratio.js"; -import { spawn } from "node:child_process"; +import { execFile, spawn } from "node:child_process"; +import { createHash } from "node:crypto"; import { ForegroundReadOperations, ForegroundReadCleanupError, closeForegroundResource } from "./foreground-read-scope.js"; import { StringDecoder } from "node:string_decoder"; import { CodingToolMatchTimeout, withCodingToolGlob, withCodingToolMatcher } from "./coding-tool-matcher.js"; @@ -28,6 +29,7 @@ import type { RE2 as RE2Matcher } from "re2-wasm"; import { declarePiRuntimeReplay } from "./pi-runtime-tool.js"; import { boundedToolOutput } from "./tool-output-context.js"; import { parseProducedFile, type ProducedFile } from "../../renderer/shared/produced-file.js"; +import { resolveSubagentFileMutatorBinary } from "./subagents/subagent-file-mutator-io.js"; const MAX_READ_BYTES = 200_000; const MAX_OUTPUT_CHARS = 20_000; @@ -42,6 +44,7 @@ const MAX_GREP_ENTRIES = 10_000; const MAX_GREP_BYTES = 10 * 1024 * 1024; const MAX_GREP_DURATION_MS = 5_000; const MAX_SEARCH_PATTERN_CHARS = 1_000; +const MAX_PARENT_EDIT_BYTES = 256 * 1024 * 1024; const SKIP_DIRS = new Set([".git", "node_modules", "dist", "build", ".next", ".cache"]); let re2Constructor: typeof import("re2-wasm").RE2 | undefined; @@ -256,6 +259,7 @@ interface WorkspaceRootGuard { lexical: string; canonical: string | null; identity: Stats | null; + exactIdentity: PinnedWorkspaceRootIdentity | null; testObserver?: { beforeDirectoryOpen?(directoryPath: string): void | Promise; afterDirectoryOpen?(directoryPath: string): void | Promise; @@ -281,6 +285,7 @@ function createParentWorkspaceRoot( lexical: path.resolve(root), canonical: null, identity: null, + exactIdentity: null, testObserver, }; } @@ -293,17 +298,21 @@ function pinWorkspaceRoot( const lexical = path.resolve(root); const canonical = realpathSync(lexical); const identity = statSync(canonical); + const exactStat = statSync(canonical, { bigint: true }); + const exactIdentity = { + device: exactStat.dev.toString(), + inode: exactStat.ino.toString(), + }; if (!identity.isDirectory()) throw new Error("The workspace root is not a directory."); if (expectedIdentity) { - const exactIdentity = statSync(canonical, { bigint: true }); if ( - exactIdentity.dev.toString() !== expectedIdentity.device || - exactIdentity.ino.toString() !== expectedIdentity.inode + exactIdentity.device !== expectedIdentity.device || + exactIdentity.inode !== expectedIdentity.inode ) { throw new Error("The authorized workspace root changed before this generation started."); } } - return { lexical, canonical, identity, testObserver }; + return { lexical, canonical, identity, exactIdentity, testObserver }; } async function verifyWorkspaceRoot( @@ -1076,6 +1085,48 @@ function makeWriteFile(workspace: WorkspaceRootGuard): AgentTool { }; } +async function commitPinnedEdit(input: { + root: string; + rootIdentity: PinnedWorkspaceRootIdentity; + relativePath: string; + fileIdentity: PinnedWorkspaceRootIdentity; + originalSha256: string; + updated: Buffer; +}): Promise { + if (input.updated.length > MAX_PARENT_EDIT_BYTES) { + throw new Error("The edited file exceeds the safe write limit."); + } + const args = [ + "edit-file", + input.root, + input.rootIdentity.device, + input.rootIdentity.inode, + input.relativePath, + input.fileIdentity.device, + input.fileIdentity.inode, + input.originalSha256, + String(input.updated.length), + ]; + await new Promise((resolve, reject) => { + const child = execFile(resolveSubagentFileMutatorBinary(), args, { + encoding: "utf8", + maxBuffer: 1024, + timeout: 120_000, + }, (error, stdout, stderr) => { + if (error || stdout !== "edited\n") { + reject(new Error(stderr.trim() === "conflict" + ? "The workspace file changed before the edit could be committed." + : "The workspace edit could not be completed safely.")); + } else { + resolve(); + } + }); + // The callback owns child errors, including an early native rejection. + child.stdin?.on("error", () => undefined); + child.stdin?.end(input.updated); + }); +} + function makeEditFile(workspace: WorkspaceRootGuard): AgentTool { return { name: "edit_file", @@ -1099,8 +1150,24 @@ function makeEditFile(workspace: WorkspaceRootGuard): AgentTool { old_string: string; new_string: string; }; - const { full } = await resolveExistingInRoot(workspace, p, signal); - const original = await fs.readFile(full, "utf-8"); + const { root, full } = await resolveExistingInRoot(workspace, p, signal); + const handle = await fs.open( + full, + fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0) | (fsConstants.O_NONBLOCK ?? 0), + ); + let source: Buffer; + let fileIdentity: PinnedWorkspaceRootIdentity; + try { + const stat = await handle.stat({ bigint: true }); + if (!stat.isFile() || stat.nlink !== 1n) { + throw new Error(`Path "${p}" is not an exclusively linked regular file.`); + } + source = await handle.readFile(); + fileIdentity = { device: stat.dev.toString(), inode: stat.ino.toString() }; + } finally { + await handle.close(); + } + const original = source.toString("utf8"); throwIfAborted(signal, "File edit cancelled."); const count = original.split(old_string).length - 1; if (count === 0) throw new Error(`old_string not found in ${p}.`); @@ -1111,8 +1178,23 @@ function makeEditFile(workspace: WorkspaceRootGuard): AgentTool { const updated = original.replace(old_string, new_string); const details = fileMutationDetails(original, updated); throwIfAborted(signal, "File edit cancelled."); - details.producedFile = parseProducedFile({ relativePath: path.relative(await fs.realpath(workspace.lexical), path.join(await fs.realpath(path.dirname(full)), path.basename(full))).split(path.sep).join("/"), operation: "edited", bytes: Buffer.byteLength(updated) }, "edit_file"); - await fs.writeFile(full, updated, "utf-8"); + const relativePath = path.relative(root, full).split(path.sep).join("/"); + const updatedBytes = Buffer.from(updated, "utf8"); + details.producedFile = parseProducedFile({ relativePath, operation: "edited", bytes: updatedBytes.length }, "edit_file"); + const rootStat = workspace.exactIdentity === null + ? await fs.stat(root, { bigint: true }) + : null; + await commitPinnedEdit({ + root, + rootIdentity: workspace.exactIdentity ?? { + device: rootStat!.dev.toString(), + inode: rootStat!.ino.toString(), + }, + relativePath, + fileIdentity, + originalSha256: createHash("sha256").update(source).digest("hex"), + updated: updatedBytes, + }); return fileMutationResult(`Edited ${p}.`, details); }, }; diff --git a/native/subagent-file-mutator/main.c b/native/subagent-file-mutator/main.c index d9cc80630..73484bd43 100644 --- a/native/subagent-file-mutator/main.c +++ b/native/subagent-file-mutator/main.c @@ -1790,7 +1790,149 @@ static int parse_identity(const char *value, uint64_t *result) { return 0; } +/* + * Parent edit_file keeps its in-place write semantics. Resolve the already + * canonicalized relative path below an exact root descriptor. Every component + * is opened with O_NOFOLLOW, so a late symlink cannot redirect the final open. + */ +static int open_edit_parent(int root_fd, const char *relative, + char leaf[NAME_MAX + 1]) { + size_t length = strlen(relative); + if (length == 0 || length > MAX_PATH_BYTES || relative[0] == '/' || + relative[length - 1] == '/') + return -1; + char parts[MAX_PATH_BYTES + 1]; + memcpy(parts, relative, length + 1); + int current = + openat(root_fd, ".", O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (current < 0) + return -1; + char *component = parts; + for (;;) { + char *slash = strchr(component, '/'); + if (slash != NULL) + *slash = '\0'; + size_t size = strlen(component); + if (size == 0 || size > NAME_MAX || strcmp(component, ".") == 0 || + strcmp(component, "..") == 0) { + close(current); + return -1; + } + if (slash == NULL) { + memcpy(leaf, component, size + 1); + return current; + } + int next = openat(current, component, + O_RDONLY | O_DIRECTORY | O_NONBLOCK | O_NOFOLLOW | + O_CLOEXEC); + close(current); + if (next < 0) + return -1; + current = next; + component = slash + 1; + } +} + +#define MAX_PARENT_EDIT_BYTES (256U * 1024U * 1024U) + +static int parent_edit_error(const char *reason) { + fprintf(stderr, "%s\n", reason); + return EXIT_FAILURE; +} + +static int parent_edit_file(int argc, char **argv) { + uint64_t root_device, root_inode, file_device, file_inode, bytes; + if (argc != 10 || argv[2][0] != '/' || + parse_identity(argv[3], &root_device) != 0 || + parse_identity(argv[4], &root_inode) != 0 || + parse_identity(argv[6], &file_device) != 0 || + parse_identity(argv[7], &file_inode) != 0 || + parse_identity(argv[9], &bytes) != 0 || + bytes > MAX_PARENT_EDIT_BYTES || !valid_hex_digest(argv[8])) + return parent_edit_error("invalid_input"); + + unsigned char *contents = malloc(bytes == 0 ? 1 : (size_t)bytes); + if (contents == NULL) + return parent_edit_error("io_failed"); + size_t received = 0; + while (received < (size_t)bytes) { + ssize_t count = read(STDIN_FILENO, contents + received, + (size_t)bytes - received); + if (count < 0 && errno == EINTR) + continue; + if (count <= 0) { + free(contents); + return parent_edit_error("invalid_input"); + } + received += (size_t)count; + } + unsigned char extra; + if (read(STDIN_FILENO, &extra, 1) != 0) { + free(contents); + return parent_edit_error("invalid_input"); + } + + int root_fd = + open(argv[2], O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + struct stat root_identity; + if (root_fd < 0 || fstat(root_fd, &root_identity) != 0 || + !S_ISDIR(root_identity.st_mode) || + (uint64_t)root_identity.st_dev != root_device || + (uint64_t)root_identity.st_ino != root_inode) { + if (root_fd >= 0) + close(root_fd); + free(contents); + return parent_edit_error("conflict"); + } + char leaf[NAME_MAX + 1]; + int parent_fd = open_edit_parent(root_fd, argv[5], leaf); + close(root_fd); + if (parent_fd < 0) { + free(contents); + return parent_edit_error("conflict"); + } + int file_fd = openat(parent_fd, leaf, + O_RDWR | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC); + struct stat before, after, named; + if (file_fd < 0 || fstat(file_fd, &before) != 0 || + !exclusive_regular(&before) || + (uint64_t)before.st_dev != file_device || + (uint64_t)before.st_ino != file_inode || + before.st_size < 0 || before.st_size > MAX_PARENT_EDIT_BYTES) { + if (file_fd >= 0) + close(file_fd); + close(parent_fd); + free(contents); + return parent_edit_error("conflict"); + } + char actual_sha256[SHA256_HEX_BYTES + 1]; + if (sha256_descriptor(file_fd, MAX_PARENT_EDIT_BYTES, actual_sha256, + &after) != 0 || + !same_read_identity(&before, &after) || + strcmp(actual_sha256, argv[8]) != 0 || + fstatat(parent_fd, leaf, &named, AT_SYMLINK_NOFOLLOW) != 0 || + !same_read_identity(&before, &named)) { + close(file_fd); + close(parent_fd); + free(contents); + return parent_edit_error("conflict"); + } + /* The pathname is never used for the write. A replacement after this + * check cannot cause an unrelated file to be opened or truncated. */ + int good = ftruncate(file_fd, 0) == 0 && + write_all(file_fd, contents, (size_t)bytes) == 0; + free(contents); + close(file_fd); + close(parent_fd); + if (!good) + return parent_edit_error("io_failed"); + puts("edited"); + return ferror(stdout) ? parent_edit_error("io_failed") : 0; +} + int main(int argc, char **argv) { + if (argc >= 2 && strcmp(argv[1], "edit-file") == 0) + return parent_edit_file(argc, argv); if (argc != 8 || strcmp(argv[1], "serve") != 0 || strcmp(argv[2], "--root") != 0 || strcmp(argv[4], "--device") != 0 || strcmp(argv[6], "--inode") != 0 || argv[3][0] != '/') diff --git a/package.json b/package.json index fe3dd059a..eb6f01e83 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,8 @@ "test:aiden-remote-speech": "tsx --test main/services/aiden-remote-speech.test.ts", "test:stt-smoke": "AIDEN_STT_SMOKE=1 tsx --test scripts/stt-smoke.test.ts", "test": "node scripts/run-ci-tests.mjs --parallel", - "pretest:serial": "npm run build:worktree-remover && npm run build:worktree-file-io && npm run test:mcp && npm run test:browser && npm run test:aiden-remote-speech && npm run test:aiden-remote && npm run test:aiden-service-boundary && npm run test:memory-policy && npm run test:ios-release && npm run test:terminal:coverage && npm run test:ghostty-terminal && npm run test:onboarding && npm run test:assistant-automations && npm run test:slash-commands && npm run test:rich-links && npm run test:display-image && npm run test:ask-user-question && npm run test:todo && npm run test:btw && npm run test:advisor && npm run test:generative-ui && npm run test:provider-failure && npm run test:web-search && npm run test:compaction && npm run test:gemini-live && npm run test:subagents && tsx --test main/services/pi-remote-catalog.test.ts main/services/provider-model-info-core.test.ts main/services/aiden-remote-models.test.ts renderer/shared/provider-thinking.test.ts && npm run test:bots && npm run test:voice && npm run test:tts && npm run test:sidebar && npm run test:transcript-polish && npm run test:acp", - "pretest:coverage": "npm run build:worktree-remover && npm run build:worktree-file-io && npm run build:subagent-run-store && npm run test:preflight && npm run test:ghostty-terminal && npm run test:scheduled && npm run test:memory-policy && npm run test:google-provider && npm run test:config-recovery && npm run test:command-system && npm run test:slash-commands && npm run test:display-image && npm run test:generative-ui && npm run test:compaction && npm run test:gemini-live && npm run test:subagents && npm run test:bots:coverage", + "pretest:serial": "npm run build:worktree-remover && npm run build:worktree-file-io && npm run build:subagent-file-mutator && npm run test:mcp && npm run test:browser && npm run test:aiden-remote-speech && npm run test:aiden-remote && npm run test:aiden-service-boundary && npm run test:memory-policy && npm run test:ios-release && npm run test:terminal:coverage && npm run test:ghostty-terminal && npm run test:onboarding && npm run test:assistant-automations && npm run test:slash-commands && npm run test:rich-links && npm run test:display-image && npm run test:ask-user-question && npm run test:todo && npm run test:btw && npm run test:advisor && npm run test:generative-ui && npm run test:provider-failure && npm run test:web-search && npm run test:compaction && npm run test:gemini-live && npm run test:subagents && tsx --test main/services/pi-remote-catalog.test.ts main/services/provider-model-info-core.test.ts main/services/aiden-remote-models.test.ts renderer/shared/provider-thinking.test.ts && npm run test:bots && npm run test:voice && npm run test:tts && npm run test:sidebar && npm run test:transcript-polish && npm run test:acp", + "pretest:coverage": "npm run build:worktree-remover && npm run build:worktree-file-io && npm run build:subagent-file-mutator && npm run build:subagent-run-store && npm run test:preflight && npm run test:ghostty-terminal && npm run test:scheduled && npm run test:memory-policy && npm run test:google-provider && npm run test:config-recovery && npm run test:command-system && npm run test:slash-commands && npm run test:display-image && npm run test:generative-ui && npm run test:compaction && npm run test:gemini-live && npm run test:subagents && npm run test:bots:coverage", "test:preflight": "npm run test:mcp && npm run test:artificial-analysis && npm run test:model-pad && tsx --test main/services/appearance-preview-core.test.ts main/services/generation-timeline.test.ts main/services/local-runtime-status.test.ts main/services/mcp-tool-result.test.ts main/services/pi-thinking-disclosure.integration.test.ts renderer/components/activity-feed.test.tsx renderer/components/context-meter.test.tsx renderer/components/chat-sidebar.test.tsx renderer/components/composer.test.tsx renderer/components/composer-slash-palette.test.tsx renderer/components/settings/providers-settings.test.tsx renderer/main/chat-transition.test.tsx renderer/components/reasoning-block.test.tsx renderer/components/reasoning-visibility-control.test.tsx renderer/components/thinking-control.test.tsx renderer/lib/agent-steps.test.ts renderer/lib/button-appearance-contract.test.ts renderer/lib/dialog-motion-contract.test.ts renderer/lib/scrollbar-gutter-contract.test.ts renderer/lib/scroll-follow.test.ts renderer/lib/text-entry-focus-contract.test.ts renderer/lib/pill-appearance.test.ts renderer/lib/reasoning-disclosure.test.ts renderer/lib/streaming-motion-contract.test.ts renderer/lib/streaming-reveal.test.ts renderer/components/streaming-markdown-reveal.test.tsx renderer/lib/voice-recorder-core.test.ts renderer/lib/media-recorder-stop.test.ts renderer/lib/dictation-vad.test.ts renderer/lib/dictation-sounds.test.ts renderer/pill-preload-channels.test.ts renderer/shared/anthropic-thinking.test.ts renderer/shared/app-update.test.ts renderer/shared/claim-check.test.ts renderer/shared/codex-thinking.test.ts renderer/shared/context-pressure.test.ts renderer/shared/google-thinking.test.ts renderer/shared/provider-deployment.test.ts renderer/shared/assistant-turn-stats.test.ts renderer/lib/sticky-section.test.ts", "test:chat-row-states": "tsx --test renderer/shared/chat-row-state.test.ts renderer/components/chat-row-status.test.tsx renderer/components/chat-row-context.test.tsx renderer/lib/chat-row-context.test.ts renderer/lib/use-mark-chat-read.test.ts main/services/chat-read-markers.test.ts", "test:sidebar": "tsx --test renderer/components/chat-sidebar.test.tsx renderer/lib/sidebar-workspace-groups.test.ts renderer/lib/sidebar-chat-shortcuts.test.ts renderer/lib/held-modifier-reveal.test.ts renderer/lib/sidebar-organization.test.ts renderer/components/sidebar-organize-menu.test.tsx renderer/components/sidebar-remote.test.tsx renderer/lib/sidebar-remote-groups.test.ts renderer/lib/hosts/peer-host-feed-state.test.ts renderer/lib/hosts/use-peer-host-sidebar.test.tsx", @@ -194,6 +194,7 @@ "test:studio-foundation": "tsx --test main/services/studio/feature-flags.test.ts renderer/lib/app-capabilities.test.ts renderer/shared/studio-routes.test.ts renderer/main/studio-capability-route.test.tsx renderer/components/sidebar-primary-nav.test.tsx main/services/custom-schemes-core.test.ts main/services/studio-assets/image-validation-core.test.ts main/services/studio-assets/store.test.ts main/services/studio-assets/delivery-core.test.ts main/services/studio-assets/request-handler.test.ts main/services/studio-assets/startup-core.test.ts renderer/canvas/canvas-keymap-core.test.ts renderer/canvas/canvas-controls.test.tsx", "test:custom-model-options": "tsx --test main/services/custom-model-options.test.ts renderer/components/settings/custom-model-options.test.tsx", "test:foreground-file-tools:electron": "node scripts/run-foreground-file-tools-smoke.mjs", + "pretest:tool-outputs": "npm run build:subagent-file-mutator", "test:tool-outputs": "tsx --test main/services/tool-output-store.test.ts main/services/coding-tools.test.ts main/services/mcp-tool-result.test.ts main/services/generation-timeline.test.ts main/services/chat-application-service.test.ts main/services/aiden-remote-protocol.test.ts renderer/components/activity-feed.test.tsx renderer/components/onboarding-flow.test.tsx", "test:chat-pull-requests": "tsx --test renderer/shared/chat-pull-requests.test.ts main/services/chat-pull-request-store.test.ts main/services/chat-pull-request-service.test.ts main/services/pull-request-current-resolver.test.ts main/services/pull-request-create-reconciliation.test.ts main/services/github-pull-request.test.ts main/services/github/github-credentials.test.ts main/services/github/github-api.test.ts main/services/github/github-request-gate.test.ts main/services/github/github-local-repository.test.ts main/services/github/github-pull-request-reader.test.ts main/services/github/github-replay.test.ts", "test:ci": "node scripts/run-ci-tests.mjs", diff --git a/prebuilt/native/darwin-universal/aiden-subagent-file-mutator b/prebuilt/native/darwin-universal/aiden-subagent-file-mutator index b5060c2157d91f983cf6d75ca7279eef16c33550..3f16859097ca367269edc032813a827f9160d6a0 100755 GIT binary patch delta 24365 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 B#oYh^ diff --git a/prebuilt/native/linux-arm64/manifest.json b/prebuilt/native/linux-arm64/manifest.json index e85b46f15..a118f7c9c 100644 --- a/prebuilt/native/linux-arm64/manifest.json +++ b/prebuilt/native/linux-arm64/manifest.json @@ -1,6 +1,6 @@ { "target": "linux-arm64", - "builtAt": "2026-09-26T10:12:57.490Z", + "builtAt": "2026-10-15T21:37:14.255Z", "helpers": { "worktree-remover": { "sha256": "1832482e431808d4fad7d2ce9f27b554caca716ffc44cdc43679976f367142e4", @@ -15,8 +15,8 @@ "source": "6679af2569fb7fea609dce1eedf99059481256f0e57c2bd7946aa59194d70575" }, "subagent-file-mutator": { - "sha256": "901257685634ec526eca8ec83a1ddd5836feaea4110abc74b6396a0e7c9c2536", - "source": "9abc3f74bdaadf60c2e8f4921b3ad8ad12401308184cc84db092e9f072caf6cf" + "sha256": "3b0895a1027028906bc3be3ad8f75e3aebb57a7a54769865e0b3259bf90be1c2", + "source": "8bccc0ac0397463d78b48e488af89f738bfe056dc5241071408074c2c80836d6" } } } diff --git a/prebuilt/native/linux-x64/aiden-subagent-file-mutator b/prebuilt/native/linux-x64/aiden-subagent-file-mutator index dd1e94732ee22f57e2a6f23fb95c3b84582d009e..af8e0b83e89c8a59eb0d7555b34b1a2002fccc7f 100755 GIT binary patch delta 15784 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 Y(5q^X_%7_pr-yaD_@+um!*Fr`4>DLH^#A|> delta 13800 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 P{>wDBlZnmQ*!BMba$!M8 diff --git a/prebuilt/native/linux-x64/manifest.json b/prebuilt/native/linux-x64/manifest.json index 5d70f1a3e..908f9f9ff 100644 --- a/prebuilt/native/linux-x64/manifest.json +++ b/prebuilt/native/linux-x64/manifest.json @@ -1,6 +1,6 @@ { "target": "linux-x64", - "builtAt": "2026-09-26T10:12:54.204Z", + "builtAt": "2026-10-15T21:37:05.941Z", "helpers": { "worktree-remover": { "sha256": "0a26da641b1f74f1af4c40dce7d2405eff8771a705eb888a8d6366aac917d95a", @@ -15,8 +15,8 @@ "source": "6679af2569fb7fea609dce1eedf99059481256f0e57c2bd7946aa59194d70575" }, "subagent-file-mutator": { - "sha256": "2eeb42a53758ae10dd3803190b351a89b47c937a9368cdc39018907a0bbafc58", - "source": "9abc3f74bdaadf60c2e8f4921b3ad8ad12401308184cc84db092e9f072caf6cf" + "sha256": "115fcec8b87752860935ac48a306cac17dd8b23b641e9e9481f2171bd5868133", + "source": "8bccc0ac0397463d78b48e488af89f738bfe056dc5241071408074c2c80836d6" } } } diff --git a/scripts/ci-test-registry.json b/scripts/ci-test-registry.json index 560ef8044..9d4cba4ef 100644 --- a/scripts/ci-test-registry.json +++ b/scripts/ci-test-registry.json @@ -12,7 +12,8 @@ "worktree-remover-build", "vcc-build", "worktree-file-io-build", - "worktree-file-io-test-build" + "worktree-file-io-test-build", + "subagent-file-mutator-build" ], "files": [ "main/handlers/assistant-parse.test.ts", @@ -1123,6 +1124,7 @@ { "id": "subagent-file-mutator-build", "for": [ + "core-git", "runtime-subagents", "subagent-file-mutator-native" ], diff --git a/scripts/subagent-file-mutator.test.mjs b/scripts/subagent-file-mutator.test.mjs index 7f9c57d59..131365c8e 100644 --- a/scripts/subagent-file-mutator.test.mjs +++ b/scripts/subagent-file-mutator.test.mjs @@ -81,6 +81,30 @@ async function rootIdentity(root) { return { device: identity.dev.toString(), inode: identity.ino.toString() }; } +async function parentEdit(root, relativePath, fileIdentity, original, updated) { + const rootStat = await rootIdentity(root); + const args = [ + "edit-file", root, rootStat.device, rootStat.inode, relativePath, + fileIdentity.device, fileIdentity.inode, sha256(original), + String(Buffer.byteLength(updated)), + ]; + return new Promise((resolve, reject) => { + const child = spawn(productionBinary, args, { stdio: ["pipe", "pipe", "pipe"] }); + const output = []; + const errors = []; + child.stdout.on("data", (chunk) => output.push(chunk)); + child.stderr.on("data", (chunk) => errors.push(chunk)); + child.on("error", reject); + child.on("close", (code) => resolve({ + code, + stdout: Buffer.concat(output).toString("utf8"), + stderr: Buffer.concat(errors).toString("utf8"), + })); + child.stdin.on("error", () => undefined); + child.stdin.end(updated); + }); +} + function startHelper( t, root, @@ -631,6 +655,39 @@ test("a path replacement after prepare is reported as a conflict and preserved", await helper.close(); }); +test("parent edit opens below the pinned root and refuses swapped symlinks", async (t) => { + if (process.platform !== "darwin" && process.platform !== "linux") return; + const root = await fixture(t, "aiden-parent-edit-root-"); + const outside = await fixture(t, "aiden-parent-edit-outside-"); + const directory = path.join(root, "nested"); + const target = path.join(directory, "edit.txt"); + const outsideFile = path.join(outside, "edit.txt"); + await mkdir(directory); + await writeFile(target, "before\n"); + await writeFile(outsideFile, "outside\n"); + const first = await stat(target, { bigint: true }); + const identity = { device: first.dev.toString(), inode: first.ino.toString() }; + const edited = await parentEdit(root, "nested/edit.txt", identity, "before\n", "after\n"); + assert.deepEqual(edited, { code: 0, stdout: "edited\n", stderr: "" }); + assert.equal(await readFile(target, "utf8"), "after\n"); + + await rename(target, path.join(directory, "original.txt")); + await symlink(outsideFile, target); + const rejectedTarget = await parentEdit(root, "nested/edit.txt", identity, "after\n", "changed\n"); + assert.equal(rejectedTarget.code, 1); + assert.equal(rejectedTarget.stderr, "conflict\n"); + assert.equal(await readFile(outsideFile, "utf8"), "outside\n"); + + await rm(target); + await rename(directory, path.join(root, "original-directory")); + await symlink(outside, directory); + const rejectedAncestor = await parentEdit(root, "nested/original.txt", identity, "after\n", "changed\n"); + assert.equal(rejectedAncestor.code, 1); + assert.equal(rejectedAncestor.stderr, "conflict\n"); + assert.equal(await readFile(outsideFile, "utf8"), "outside\n"); + assert.equal(await readFile(path.join(root, "original-directory", "original.txt"), "utf8"), "after\n"); +}); + test("a replacement in the final install race is atomically rolled back", async (t) => { if (process.platform !== "darwin" && process.platform !== "linux") return; const root = await fixture(t, "aiden-file-mutator-install-race-"); From e4655822d819b19e4a1720903f11fb141d1af3da Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 17:43:39 -0400 Subject: [PATCH 2/7] docs: state edit_file inode confinement boundary --- .memory/edit-file-symlink-race-20261015.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index 52c7cc8ff..9242e6eb8 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -4,6 +4,8 @@ Issue #423: the parent tool previously checked a canonical path, then used path- The parent tool now reads an opened regular file, records its exact device/inode and SHA-256, and passes the canonical root-relative path to the existing packaged cross-platform file-mutator helper. Its new `edit-file` operation opens the pinned root and every relative directory component with no-follow descriptors, checks the opened file's identity and content, then truncates and writes only through that descriptor. The ordinary in-root symlink policy remains: TypeScript resolves a stable link to its in-root target before the native traversal. Multiply linked files fail closed so an outside hard link cannot be changed. The helper accepts up to 256 MiB, beyond the existing subagent protocol's 200 KB cap. +The security claim is limited to preventing a symlink/path replacement from redirecting the write to a different inode. A local actor can rename the already-open file or its parent outside the root after the native name check; the descriptor then still writes that original inode at its new location. This is not a guarantee that every inode modified by the tool remains reachable inside the workspace throughout the write. Such a stronger guarantee would require control of concurrent namespace mutation, not another path recheck or a held parent descriptor. + The high-level tool test swaps the target or an ancestor between validation and commit and checks that the outside file is unchanged. Native tests exercise the same no-follow rule on macOS and Linux, and a separate test covers a stable in-root link and a file above 200 KB. The root test lane now builds the helper before coding-tools tests; the checked-in helper binaries and manifests were refreshed for macOS universal, Linux x64, and Linux arm64. No shared server protocol, native client, UI, onboarding, or plan status changed. Local verification: native mutator suite, CI test registry, native prebuilt manifest suite, scoped ESLint, and TypeScript type-check pass. The full coding-tools suite had one independent glob/native-glob comparison failure on `**/*`; the new edit tests passed. Hosted exact-head checks and review remain PR delivery gates. From a4535dfcb036ecd96c7929bc7d47833d6dd33861 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 17:53:58 -0400 Subject: [PATCH 3/7] Resolve CLI Bot edit helper from packaged installation --- .memory/edit-file-symlink-race-20261015.md | 3 +++ main/services/bot-file-tool-router.ts | 7 +++++-- main/services/coding-tools.ts | 13 +++++++++---- packages/cli/src/bots.ts | 8 ++++++-- packages/cli/tests/parity.test.mjs | 18 +++++++++++++++++- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index 9242e6eb8..588f18642 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -9,3 +9,6 @@ The security claim is limited to preventing a symlink/path replacement from redi The high-level tool test swaps the target or an ancestor between validation and commit and checks that the outside file is unchanged. Native tests exercise the same no-follow rule on macOS and Linux, and a separate test covers a stable in-root link and a file above 200 KB. The root test lane now builds the helper before coding-tools tests; the checked-in helper binaries and manifests were refreshed for macOS universal, Linux x64, and Linux arm64. No shared server protocol, native client, UI, onboarding, or plan status changed. Local verification: native mutator suite, CI test registry, native prebuilt manifest suite, scoped ESLint, and TypeScript type-check pass. The full coding-tools suite had one independent glob/native-glob comparison failure on `**/*`; the new edit tests passed. Hosted exact-head checks and review remain PR delivery gates. + +A review of PR #501 found the standalone CLI Bot route used the desktop development fallback (`/build/native/…`) for the new native edit operation. The CLI now passes the helper staged beside `dist/app/cli.js` through the Bot file router and pinned tool factory. The packaged parity test changes into an external temporary project with an executable decoy at the old fallback path, edits a Bot file successfully, and verifies that the decoy did not run. CLI build, CLI and root type checks, scoped lint, and focused desktop and Bot tests passed. +The full CLI suite passed 99/99 tests after the helper lookup fix. The Bot file router suite passed 8/8, and the focused edit tests passed 4/4. PR #492 remained open when this update was made. diff --git a/main/services/bot-file-tool-router.ts b/main/services/bot-file-tool-router.ts index 6e6b0bbaa..5a652d105 100644 --- a/main/services/bot-file-tool-router.ts +++ b/main/services/bot-file-tool-router.ts @@ -38,6 +38,8 @@ export interface BotFileToolRouterOptions { * Defaults to the process-wide roots (`setBotFileToolProtectedRoots`). */ readonly protectedRoots?: () => readonly string[]; + /** Installation-owned native helper used for edit_file. */ + readonly fileMutatorBinary?: string; } /** File tools that change a file; only these are fenced from protected roots. */ @@ -146,11 +148,12 @@ function checkedLocation(location: BotFileToolLocation, role: string): BotFileTo }); } -function underlyingTools(location: BotFileToolLocation): ReadonlyMap { +function underlyingTools(location: BotFileToolLocation, fileMutatorBinary?: string): ReadonlyMap { const selected = buildPinnedCodingTools( location.root, undefined, location.expectedIdentity, + { fileMutatorBinary }, ).filter((tool) => TOOL_NAMES.has(tool.name)); const map = new Map(); for (const tool of selected) { @@ -252,7 +255,7 @@ export function buildBotFileTools(options: BotFileToolRouterOptions): AgentTool[ const prepared = locations.map((location) => ({ ...location, - tools: underlyingTools(location), + tools: underlyingTools(location, options.fileMutatorBinary), })); const byId = new Map(prepared.map((location) => [location.id, location] as const)); const schemaLocations = prepared.map(({ id, label }) => ({ id, label })); diff --git a/main/services/coding-tools.ts b/main/services/coding-tools.ts index ee9ca3a6c..b87b0800a 100644 --- a/main/services/coding-tools.ts +++ b/main/services/coding-tools.ts @@ -1086,6 +1086,7 @@ function makeWriteFile(workspace: WorkspaceRootGuard): AgentTool { } async function commitPinnedEdit(input: { + binary?: string; root: string; rootIdentity: PinnedWorkspaceRootIdentity; relativePath: string; @@ -1108,7 +1109,7 @@ async function commitPinnedEdit(input: { String(input.updated.length), ]; await new Promise((resolve, reject) => { - const child = execFile(resolveSubagentFileMutatorBinary(), args, { + const child = execFile(input.binary ?? resolveSubagentFileMutatorBinary(), args, { encoding: "utf8", maxBuffer: 1024, timeout: 120_000, @@ -1127,7 +1128,7 @@ async function commitPinnedEdit(input: { }); } -function makeEditFile(workspace: WorkspaceRootGuard): AgentTool { +function makeEditFile(workspace: WorkspaceRootGuard, binary?: string): AgentTool { return { name: "edit_file", label: "Edit File", @@ -1185,6 +1186,7 @@ function makeEditFile(workspace: WorkspaceRootGuard): AgentTool { ? await fs.stat(root, { bigint: true }) : null; await commitPinnedEdit({ + binary, root, rootIdentity: workspace.exactIdentity ?? { device: rootStat!.dev.toString(), @@ -1874,6 +1876,8 @@ function makeParentGrep(workspace: WorkspaceRootGuard): AgentTool { */ export interface CodingToolOptions { pathPrefix?: string | (() => string | null | undefined); + /** Trusted native helper from the application installation, if packaged outside Electron. */ + fileMutatorBinary?: string; } export function runCommandEnv( @@ -2043,7 +2047,7 @@ function buildParentCodingToolSet(workspace: WorkspaceRootGuard, options: Coding declarePiRuntimeReplay(withForegroundReadScope(makeParentListDir(workspace)), "safe"), declarePiRuntimeReplay(withForegroundReadScope(makeParentGlob(workspace)), "safe"), declarePiRuntimeReplay(withForegroundReadScope(makeParentGrep(workspace)), "safe"), - declarePiRuntimeReplay(makeEditFile(workspace), "never"), + declarePiRuntimeReplay(makeEditFile(workspace, options.fileMutatorBinary), "never"), declarePiRuntimeReplay(makeWriteFile(workspace), "never"), declarePiRuntimeReplay(makeRunCommand(workspace, options), "never"), ]; @@ -2069,8 +2073,9 @@ export function buildPinnedCodingTools( testObserver?: WorkspaceRootGuard["testObserver"], /** Optional authority-proven identity captured before the tool set is built. */ expectedIdentity?: PinnedWorkspaceRootIdentity, + options: CodingToolOptions = {}, ): AgentTool[] { - return buildParentCodingToolSet(pinWorkspaceRoot(root, testObserver, expectedIdentity)); + return buildParentCodingToolSet(pinWorkspaceRoot(root, testObserver, expectedIdentity), options); } /** diff --git a/packages/cli/src/bots.ts b/packages/cli/src/bots.ts index 412965145..448fd49a6 100644 --- a/packages/cli/src/bots.ts +++ b/packages/cli/src/bots.ts @@ -15,7 +15,7 @@ import { botMcpLiveToolMatchesGrant, exactBotMcpToolNames } from "../../../main/ import { mcpAgentToolDescriptor } from "../../../main/services/mcp-agent-tool-descriptor.js"; import { createCliMcpPool, mcpCredentialSignature } from "./mcp.ts"; import { createCliWebSearch } from "./extensions/web-search.ts"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; import { mkdir, lstat, chmod, rm } from "node:fs/promises"; import { watch } from "node:fs"; import { createHmac } from "node:crypto"; @@ -66,6 +66,10 @@ function checkpointFile(agentDir: string, name: string) { } export async function createCliBots(agentDir: string, daemon: ReturnType) { + const cliEntry = process.env.AIDEN_CLI_ENTRY; + const fileMutatorBinary = cliEntry + ? join(dirname(cliEntry), "native", "aiden-subagent-file-mutator") + : undefined; const directory = join(agentDir, "bot-service"), root = () => directory; await mkdir(directory, { recursive: true, mode: 0o700 }); const info = await lstat(directory); @@ -159,7 +163,7 @@ export async function createCliBots(agentDir: string, daemon: ReturnType tool.name === "run_command")!); const retained = await capabilities.getBotBinding(authority.botId); const snapshot = await catalog.snapshotForRuntime({ botId: authority.botId, signal: admission.signal, diff --git a/packages/cli/tests/parity.test.mjs b/packages/cli/tests/parity.test.mjs index 3dc13ce9b..8eca0f2eb 100644 --- a/packages/cli/tests/parity.test.mjs +++ b/packages/cli/tests/parity.test.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { mkdtempSync, mkdirSync, existsSync, readFileSync, writeFileSync, rmSync, statSync, utimesSync, renameSync, readdirSync, openSync, closeSync, ftruncateSync } from "node:fs"; +import { mkdtempSync, mkdirSync, existsSync, readFileSync, writeFileSync, chmodSync, rmSync, statSync, utimesSync, renameSync, readdirSync, openSync, closeSync, ftruncateSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve, dirname } from "node:path"; import { fileURLToPath } from "node:url"; @@ -441,6 +441,9 @@ test("Remote HTTPS serves private workspaces, persists turns once, replays SSE a test("Bots retain one-shot notice, exact Custom tools, revocation and independent rollback anchors", async (t) => { const dir = temporary(t), daemon = api.createDaemonChats(dir), requests = []; + const originalEntry = process.env.AIDEN_CLI_ENTRY; + process.env.AIDEN_CLI_ENTRY = join(pkg, "dist/app/cli.js"); + t.after(() => { if (originalEntry === undefined) delete process.env.AIDEN_CLI_ENTRY; else process.env.AIDEN_CLI_ENTRY = originalEntry; }); const server = createServer(async (request, response) => { let body = ""; for await (const chunk of request) body += chunk; const parsed = JSON.parse(body); requests.push(parsed); @@ -471,6 +474,19 @@ test("Bots retain one-shot notice, exact Custom tools, revocation and independen const write = tools.find((tool) => tool.name === "write_file"); const result = await write.execute("one", { path: "note.txt", content: "Scoped" }); assert.ok(!result.isError); assert.equal(readFileSync(join(lease.authority.workingDirectory, "note.txt"), "utf8"), "Scoped"); + const decoyDirectory = join(dir, "build", "native"), decoyMarker = join(dir, "decoy-ran"); + mkdirSync(decoyDirectory, { recursive: true }); + const decoy = join(decoyDirectory, "aiden-subagent-file-mutator"); + writeFileSync(decoy, `#!/bin/sh\ntouch '${decoyMarker}'\nexit 1\n`); chmodSync(decoy, 0o755); + const originalCwd = process.cwd(); + process.chdir(dir); + try { + const edit = tools.find((tool) => tool.name === "edit_file"); + const edited = await edit.execute("edit-outside-repo", { path: "note.txt", old_string: "Scoped", new_string: "Edited" }); + assert.ok(!edited.isError); + } finally { process.chdir(originalCwd); } + assert.equal(readFileSync(join(lease.authority.workingDirectory, "note.txt"), "utf8"), "Edited"); + assert.equal(existsSync(decoyMarker), false); await assert.rejects(write.execute("escape", { path: "../escape.txt", content: "Denied" })); const turn = daemon.llmClient.beginChatTurn(chat.id, "bot-stream", "bot-owner"); assert.ok(turn); const generationEvents = []; From 2f04c476914c9a38e131917f0188970a3a5a28e0 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 19:34:03 -0400 Subject: [PATCH 4/7] Record merged edit fixes and local verification --- .memory/edit-file-symlink-race-20261015.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index 588f18642..d8eedece0 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -12,3 +12,5 @@ Local verification: native mutator suite, CI test registry, native prebuilt mani A review of PR #501 found the standalone CLI Bot route used the desktop development fallback (`/build/native/…`) for the new native edit operation. The CLI now passes the helper staged beside `dist/app/cli.js` through the Bot file router and pinned tool factory. The packaged parity test changes into an external temporary project with an executable decoy at the old fallback path, edits a Bot file successfully, and verifies that the decoy did not run. CLI build, CLI and root type checks, scoped lint, and focused desktop and Bot tests passed. The full CLI suite passed 99/99 tests after the helper lookup fix. The Bot file router suite passed 8/8, and the focused edit tests passed 4/4. PR #492 remained open when this update was made. + +After PR #492 merged as c68462b97, merged origin/main into PR #501 as 46f30b2ce. The combined `edit_file` uses `indexOf`/slices for literal replacement while retaining the opened-file identity/hash checks and descriptor-bound native commit. On the merge head, focused edit/literal tests passed 5/5, Bot router tests 8/8, the CLI build/type-check and full CLI suite passed 99/99, and root type-check and scoped lint passed. The complete coding-tools suite passed 61/62; its sole failure is the previously documented independent foreground/native glob `**/*` mismatch. From b5a6ba37e5937f47c1ad893795cb088700e40612 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 19:48:37 -0400 Subject: [PATCH 5/7] Record latest-main edit verification --- .memory/edit-file-symlink-race-20261015.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index d8eedece0..6c354ab9e 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -14,3 +14,5 @@ A review of PR #501 found the standalone CLI Bot route used the desktop developm The full CLI suite passed 99/99 tests after the helper lookup fix. The Bot file router suite passed 8/8, and the focused edit tests passed 4/4. PR #492 remained open when this update was made. After PR #492 merged as c68462b97, merged origin/main into PR #501 as 46f30b2ce. The combined `edit_file` uses `indexOf`/slices for literal replacement while retaining the opened-file identity/hash checks and descriptor-bound native commit. On the merge head, focused edit/literal tests passed 5/5, Bot router tests 8/8, the CLI build/type-check and full CLI suite passed 99/99, and root type-check and scoped lint passed. The complete coding-tools suite passed 61/62; its sole failure is the previously documented independent foreground/native glob `**/*` mismatch. + +Main advanced again through unrelated PR #491 to dd2c0d03c. Merged it into PR #501 as 6fff24413 without conflicts. The shared `package.json` test chain retained both branches' scripts; CI registry policy tests passed 62/62. The descriptor-bound edit and literal replacement remained intact. Focused edit/literal tests passed 5/5, Bot router tests 8/8, the rebuilt CLI suite passed 99/99, both root and CLI type checks passed, and scoped lint passed. The earlier full coding-tools glob mismatch remains documented above; no edit test failed. From 52674bb0caed2b704365c6f0f99620b8cc73d8e6 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 21:04:04 -0400 Subject: [PATCH 6/7] Record E2E baseline triage and latest-main merge --- .memory/edit-file-symlink-race-20261015.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index 6c354ab9e..0f53bfc7c 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -16,3 +16,5 @@ The full CLI suite passed 99/99 tests after the helper lookup fix. The Bot file After PR #492 merged as c68462b97, merged origin/main into PR #501 as 46f30b2ce. The combined `edit_file` uses `indexOf`/slices for literal replacement while retaining the opened-file identity/hash checks and descriptor-bound native commit. On the merge head, focused edit/literal tests passed 5/5, Bot router tests 8/8, the CLI build/type-check and full CLI suite passed 99/99, and root type-check and scoped lint passed. The complete coding-tools suite passed 61/62; its sole failure is the previously documented independent foreground/native glob `**/*` mismatch. Main advanced again through unrelated PR #491 to dd2c0d03c. Merged it into PR #501 as 6fff24413 without conflicts. The shared `package.json` test chain retained both branches' scripts; CI registry policy tests passed 62/62. The descriptor-bound edit and literal replacement remained intact. Focused edit/literal tests passed 5/5, Bot router tests 8/8, the rebuilt CLI suite passed 99/99, both root and CLI type checks passed, and scoped lint passed. The earlier full coding-tools glob mismatch remains documented above; no edit test failed. + +The exact-head CI run 38825795960 for b5a6ba37 had Electron E2E shard 3 fail on three #491-origin tests: draft-agent-chats.spec.ts:205 failed twice because the workspace header remained keyboard focused and the plus button's intentional group-focus-within style kept opacity at 1, while chat-top-bar.spec.ts:236 and :327 failed once waiting for the worktree-send failure UI and passed retry. Both test and UI sources match main; this is classified as inherited baseline behavior, not an edit-path integration defect. The PR body records exact specs, symptoms, and run link. The run was still active when main advanced to #497, so no failed-job rerun was used on the stale head. Merged latest main 17d51d472 into PR #501 as dee27de64; this merge brought only Android simulator changes. Focused edit/literal tests passed 5/5, CLI Bot parity passed 1/1, and root type-check passed. From a616fc4bda6ea409ca4c5f991e1bbbe098772d87 Mon Sep 17 00:00:00 2001 From: Sambit Biswas Date: Thu, 15 Oct 2026 23:04:19 -0400 Subject: [PATCH 7/7] Record baseline E2E fix integration --- .memory/edit-file-symlink-race-20261015.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.memory/edit-file-symlink-race-20261015.md b/.memory/edit-file-symlink-race-20261015.md index 0f53bfc7c..ae76778d2 100644 --- a/.memory/edit-file-symlink-race-20261015.md +++ b/.memory/edit-file-symlink-race-20261015.md @@ -18,3 +18,5 @@ After PR #492 merged as c68462b97, merged origin/main into PR #501 as 46f30b2ce. Main advanced again through unrelated PR #491 to dd2c0d03c. Merged it into PR #501 as 6fff24413 without conflicts. The shared `package.json` test chain retained both branches' scripts; CI registry policy tests passed 62/62. The descriptor-bound edit and literal replacement remained intact. Focused edit/literal tests passed 5/5, Bot router tests 8/8, the rebuilt CLI suite passed 99/99, both root and CLI type checks passed, and scoped lint passed. The earlier full coding-tools glob mismatch remains documented above; no edit test failed. The exact-head CI run 38825795960 for b5a6ba37 had Electron E2E shard 3 fail on three #491-origin tests: draft-agent-chats.spec.ts:205 failed twice because the workspace header remained keyboard focused and the plus button's intentional group-focus-within style kept opacity at 1, while chat-top-bar.spec.ts:236 and :327 failed once waiting for the worktree-send failure UI and passed retry. Both test and UI sources match main; this is classified as inherited baseline behavior, not an edit-path integration defect. The PR body records exact specs, symptoms, and run link. The run was still active when main advanced to #497, so no failed-job rerun was used on the stale head. Merged latest main 17d51d472 into PR #501 as dee27de64; this merge brought only Android simulator changes. Focused edit/literal tests passed 5/5, CLI Bot parity passed 1/1, and root type-check passed. + +The separate #491 baseline test fix PR #506 merged to main as 896f31ae1. Merged that current main into PR #501 as b93b0b1e1 without conflicts; it changed the E2E focus expectation and native-client stream recovery, not the edit path or package scripts. On this merged checkout, the production build, root and E2E type checks, focused edit/literal tests (5/5), CLI Bot parity (1/1), and the corrected sidebar plus two worktree-send E2E specs repeated twice each (6/6 with fail-on-flaky-tests) passed.