rowboat/apps/x/pnpm-workspace.yaml

28 lines
440 B
YAML
Raw Permalink Normal View History

2025-12-29 15:30:57 +05:30
packages:
- apps/*
- packages/*
allowBuilds:
Code mode: make packaged builds work via managed engine provisioning (#625) * fix(code-mode): make packaged code mode work via on-demand engine provisioning Packaged builds could never run code mode: the Claude/Codex ACP adapters are spawned as separate `node <entry>` processes resolved at runtime, but esbuild can't inline a dynamic spawn target and Forge strips the workspace node_modules, so every release threw `Cannot find module '@agentclientprotocol/...'`. Dev worked only because of the pnpm symlink. Rather than bundle the ~400 MB of native engines (one claude + one codex binary per OS), provision them on demand: - forge.config.cjs: stage the two ACP adapters + their JS dependency closure into .package/acp/node_modules (npm-style nested layout, native engines skipped), exempt .package from the node_modules ignore rule, and only sign/notarize when APPLE_ID is set so unsigned local/CI builds can package. - agents.ts: resolve the adapter from the staged location first (node_modules fallback in dev); provision the pinned engine and point the adapter at it via CLAUDE_CODE_EXECUTABLE / CODEX_PATH. No dependence on a user's global install. - engine-provisioner.ts: ensureEngine() downloads the per-platform engine package from npm AT THE EXACT VERSION THE ADAPTER WAS BUILT AGAINST, verifies its sha512 integrity, extracts atomically into ~/.rowboat/engines/<agent>/<version>/, and caches it. Version-pinning keeps the ACP handshake compatible. - engine-manifest.ts + scripts/gen-engine-manifest.mjs: committed manifest of tarball URLs + integrity for all platforms, regenerated from the adapters' pinned versions on a bump. Verified on macOS arm64: both engines provision and run, and both adapters complete the ACP initialize handshake from the packaged .app against the provisioned engines. Installer drops from ~790 MB to 390 MB. * feat(code-mode): explicit per-agent Enable in Settings; no silent chat download Code mode now requires the user to explicitly enable an agent before use, instead of silently downloading a ~200 MB engine on the first chat message. - Settings → Code Mode: each agent shows "Not enabled" + an Enable button that downloads its engine with a live progress indicator (download % → verify → install), then flips to "Engine ready". Driven by a new codeMode:provisionEngine IPC call + a codeMode:engineProgress push channel. The section now states the prerequisite explicitly: the agent must be installed (Enable) and logged in (claude login / codex login — code mode reuses that saved credential). - Chat path no longer auto-downloads: getProvisionedEnginePath() returns the enabled engine or throws a clear "enable it in Settings → Code Mode" error, so there's never a surprise mid-conversation download. getAgentLaunchSpec is sync again. - Agent status: `installed` now means "engine provisioned" (downloaded), driving the Enable/Ready state; the new-session dialog shows "Enable in Settings" and disables un-enabled agents. Dropped the dead PATH-probing for a global CLI. Verified: empty cache -> status installed=false and the chat path throws the enable-in-Settings error (no download); core, renderer, and main typecheck/build; no new lint errors. * fix(code-mode): show only percentage during engine download in Settings * feat(code-mode): prune superseded engine versions after install After a successful provision, remove any other version dirs (and their .meta) for that agent so old ~200 MB engines don't accumulate across version bumps. Best-effort; never fails a good install. Verified: a planted stale version dir + meta are both removed after provisioning the current version. * fix(code-mode): keep showing engine download % after reopening Settings Provisioning state lived in the row component, which unmounts when the Settings dialog closes — so reopening mid-download showed the Enable button again even though the download was still running in the main process. Move provisioning state to a module-level store with one persistent listener on codeMode:engineProgress, so a row remounting (dialog reopened) reflects the live % and resolves to Ready on completion. * fix(code-mode): flip Enable row straight to Ready after install (no Enable flash) On successful provision the in-flight flag was cleared before the async status refresh completed, so the row briefly (or until reopen) showed the Enable button again. Await the status refresh before clearing the flag so it transitions directly to Ready. * fix(code-mode): optimistically show Ready right after Enable completes Awaiting the status refresh wasn't enough — setStatus re-renders the parent separately from the row, leaving a window where the in-flight flag was cleared but the status prop was stale, so the row flashed/stuck on the Enable button until reopen. Track just-enabled agents in a module-level set and treat them as installed immediately; loadStatus still syncs the real status in the background. * fix(code-mode): graft login-shell PATH + add startup deadline #1 (the gh/git "command not found" in packaged builds): GUI/Finder launches inherit launchd's stripped PATH (/usr/bin:/bin:...), so tools the engine spawns — gh, git, rg, bash — fail even though they work from a terminal (e.g. Homebrew's /opt/homebrew/bin/gh). Probe the user's login-shell PATH and graft it onto the engine's env before spawn (shell-env.ts; no-op on Windows / probe failure). #2: add a 60s startup deadline (initialize / session create+load) so a wedged engine fails with a clear, stderr-enriched error instead of an infinite "(pending...)". Overridable via ROWBOAT_ACP_STARTUP_TIMEOUT_MS. Manager now disposes the client on startup failure so the spawned adapter doesn't leak. Verified: getAgentLaunchSpec's env.PATH now includes /opt/homebrew/bin (where gh lives); core builds; no new lint errors. * chore(code-mode): comment out signing/notarization for local builds Revert to the explicit comment-out approach for osxSign/osxNotarize: uncomment them (with APPLE_ID/APPLE_PASSWORD/APPLE_TEAM_ID) for a signed release build. * chore(code-mode): keep signing/notarization active in committed config The repo's forge.config ships with osxSign/osxNotarize enabled (release-ready). Developers comment them out locally for unsigned test builds and don't commit that. * chore: approve workspace build scripts so packaging runs non-interactively The allowBuilds entries were left as "set this to true or false" placeholders, so `pnpm install` / the pre-build deps check aborted with ERR_PNPM_IGNORED_BUILDS and `npm run package` failed. Set them to true (and add node-pty, used by the code-mode embedded terminal) so build scripts are approved and packaging works without a manual `pnpm approve-builds`.
2026-06-17 09:23:15 -07:00
core-js: true
electron: true
electron-winstaller: true
esbuild: true
fs-xattr: true
macos-alias: true
node-pty: true
protobufjs: true
catalog:
vitest: 4.1.7
2025-12-29 15:30:57 +05:30
onlyBuiltDependencies:
2026-01-21 10:47:44 +05:30
- core-js
2025-12-29 15:30:57 +05:30
- electron
2026-01-17 10:27:30 +05:30
- electron-winstaller
2025-12-29 15:30:57 +05:30
- esbuild
2026-01-17 10:27:30 +05:30
- fs-xattr
2026-01-21 10:47:44 +05:30
- macos-alias
- protobufjs
feat: run code mode on an in-app ACP client with live approvals (#593) * feat(code-mode): add ACP client engine (Layer 2 core) Own the Agent Client Protocol client instead of shelling out to `acpx`, so code mode can stream structured events (tool calls, diffs, plan) and surface live permission requests. Headless acpx can't do live approvals (it only supports --approve-all), which is why we drive the agent adapters ourselves. - code-mode/acp/{agents,client,permission-broker,session-store,manager,types}.ts: headless engine driving the Claude/Codex ACP adapters; one warm session per chat with create-or-resume via session/load; approval policy (ask | auto-approve-reads | yolo) in the broker. - claude-exec.ts: cross-platform claude resolver (Windows .cmd EINVAL fix + macOS/Linux GUI-PATH safety net) shared with the legacy acpx path in builtin-tools.ts. - add @agentclientprotocol/sdk + claude/codex adapters to core. * feat(code-mode): route code mode through code_agent_run tool + live approvals Replace the acpx shell-out with a structured code_agent_run tool that drives the ACP engine directly, streaming the agent's tool calls / diffs / plan into the chat and surfacing permission requests inline. - shared: code-mode.ts zod schemas; add code-run-event + code-run-permission-request RunEvent variants (stream to the renderer over the existing runs:events channel); codeRun:resolvePermission IPC channel. - core: CodePermissionRegistry (promise-based mid-run approvals — the LLM tool-loop's pre-call gate can't model a mid-execution wait); register codeModeManager + codePermissionRegistry in awilix. - core: code_agent_run builtin tool (streams via ctx.publish, asks via the registry, cancels on ctx.signal, returns the agent summary). CodeModeConfig.approvalPolicy (ask | auto-approve-reads | yolo; default ask). Exclude the tool from the headless background-task / live-note / inline-task agents so they can't block on an approval. - main: codeRun:resolvePermission handler -> registry.resolve. - rewrite the code-with-agents skill and the runtime "Code Mode (Active)" block to call code_agent_run instead of emitting npx acpx commands. * feat(code-mode): render coding runs inline (live timeline + permission card) Render a code_agent_run tool call as a live CodingRun block instead of generic tool output: the agent's text, tool-call rows (kind icon + status + changed-file names from diffs), a plan checklist, and resolved-permission lines — plus an inline Allow / Always-allow / Deny card wired to codeRun:resolvePermission. - chat-conversation.ts: ToolCall carries codeRunEvents + pendingCodePermission; code_agent_run is excluded from tool-grouping so it renders standalone. - App.tsx: handle code-run-event / code-run-permission-request, clear the pending card on tool-result, handleCodePermissionResponse, render via CodingRunBlock. * fix(code-mode): run the ACP adapter as Node under Electron + resolve it from main Two runtime failures that only surfaced inside the packaged/bundled Electron app (the headless harness used real node, so neither showed there): - "ACP connection closed": the main process spawns the adapter via process.execPath, which inside Electron is the Electron binary, not node — so the child never ran as Node and its ACP stdio stream closed immediately. Set ELECTRON_RUN_AS_NODE=1 on the adapter env (a no-op under real node). - "Cannot find module '@agentclientprotocol/claude-agent-acp'": the adapters were transitive (core) deps, unreachable from the esbuild-bundled main.cjs. Add them as direct deps of the main app so require.resolve finds them at runtime (and so they ship when packaged). Also capture the adapter's stderr + exit code and enrich connection errors, so a future failure reports the real cause instead of the opaque "ACP connection closed". * chore(code-mode): remove dead acpx code paths and stale copy Code mode now runs through the code_agent_run tool (owning the ACP client), so the legacy acpx shell-out paths are dead. Remove them: - core: envForCommand (acpx-only CLAUDE_CODE_EXECUTABLE injection) from executeCommand; getCodeModeCommandLabel (acpx run-status label). - renderer: the acpx-detection "switch agent / auto-flip the code-mode chip" flow — App.tsx executeCommand detection, the permission-request onSwitchAgent button + badge, and the composer's code-mode-detected listener. - copy: Settings -> Code Mode and the code-with-agents skill summary no longer mention acpx; tidy stale comments (claude-exec, command-executor). No behavior change for code mode; the general executeCommand tool is unaffected. * feat(code-mode): approval-policy selector in Settings Surface the approval policy (Ask every time / Auto-approve reads / YOLO) in Settings -> Code Mode, instead of being config-file only. The broker already reads CodeModeConfig.approvalPolicy; this plumbs it through the codeMode:getConfig / setConfig IPC + main handlers and adds the picker UI (with a one-line explanation of each level). Defaults to "ask". * fix(code-mode): harden ACP engine — turn-scoped connections, chip-authoritative agent, reliable stop Three robustness fixes that co-modify manager.runPrompt and the code_agent_run tool, so they land together: - Lifecycle: scope each ACP adapter connection to the agent turn. Dispose it a short grace (60s) after the turn ends instead of holding it for the app's life; the next turn resumes via session/load (both agents support it). Wire disposeAll() on app quit (was dead code). Fixes the unbounded per-chat leak of booted agent processes. - Agent selection: make the composer chip the source of truth. Thread codeMode into ToolContext; code_agent_run uses it instead of the model's guessed `agent` arg, which anchored on the thread's earlier agent and ignored a chip change. Prompts updated to match; the run is labelled by the agent that actually ran. - Stop/abort: guarantee a stopped turn unwinds. On abort the manager sends ACP session/cancel, then force-kills the adapter after a 2s grace and resolves the turn as cancelled — a wedged adapter can no longer hang the run and lock the chat. code_agent_run returns a clean cancelled result. * fix(code-mode): hide Codex's native console window on Windows Codex's engine ships as a native console-subsystem binary (codex.exe). Launched from our console-less Electron process tree, Windows allocated a fresh *visible* console window for it; closing that window wedged the run in a pending state. (Claude Code is a Node CLI, so it never triggers this.) The window is created by @openai/codex's launcher (bin/codex.js), which spawns codex.exe with no windowsHide. Patch it via pnpm to pass windowsHide: true (CREATE_NO_WINDOW) so the console stays hidden — no window, nothing to close. * refactor(code-mode): move ACP session files out of WorkDir/config Per-run ACP session state is runtime state that accumulates one file per chat run, not user/app config. Relocate it from WorkDir/config to a dedicated WorkDir/code-mode/sessions/ so it can be listed, cleaned up, and managed on its own without crowding config. Drop the now-redundant codesession- filename prefix (the directory conveys it).
2026-06-05 14:45:08 +05:30
patchedDependencies:
'@openai/codex@0.128.0': patches/@openai__codex@0.128.0.patch