From 6b47e95d36fbfa2376c235bb6be1d0db06ba8f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 20 Mar 2026 00:12:38 -0400 Subject: [PATCH] update --- build/electron-builder.runtime.json | 9 + build/release/builder-debug.yml | 2 +- build/release/latest.yml | 14 +- docs/2026-03-17-architecture-freeze.md | 264 -------------- docs/2026-03-17-audit-and-rebuild-plan.md | 307 ---------------- docs/2026-03-17-runtime-security-tranche.md | 31 -- docs/2026-03-18-adversarial-review-verdict.md | 49 --- ...026-03-18-cross-feature-integrity-audit.md | 203 ----------- ...026-03-18-full-system-audit-remediation.md | 167 --------- docs/2026-03-18-integrated-audit-matrix.md | 333 ------------------ docs/README.md | 13 - package.json | 11 +- packages/adapter-utils/src/server-utils.ts | 51 ++- src/main/api-server.ts | 28 +- src/main/database.ts | 37 +- src/main/desktop-shell-service.ts | 24 +- src/main/prompt-builder.ts | 45 ++- src/renderer/App.tsx | 4 + src/renderer/styles.css | 12 +- 19 files changed, 195 insertions(+), 1409 deletions(-) create mode 100644 build/electron-builder.runtime.json delete mode 100644 docs/2026-03-17-architecture-freeze.md delete mode 100644 docs/2026-03-17-audit-and-rebuild-plan.md delete mode 100644 docs/2026-03-17-runtime-security-tranche.md delete mode 100644 docs/2026-03-18-adversarial-review-verdict.md delete mode 100644 docs/2026-03-18-cross-feature-integrity-audit.md delete mode 100644 docs/2026-03-18-full-system-audit-remediation.md delete mode 100644 docs/2026-03-18-integrated-audit-matrix.md delete mode 100644 docs/README.md diff --git a/build/electron-builder.runtime.json b/build/electron-builder.runtime.json new file mode 100644 index 00000000..5b25931f --- /dev/null +++ b/build/electron-builder.runtime.json @@ -0,0 +1,9 @@ +{ + "extends": "C:\\Users\\zhang\\Documents\\GitHub\\AgentCompany\\electron-builder.yml", + "directories": { + "output": "C:\\Users\\zhang\\Documents\\GitHub\\AgentCompany\\build\\release" + }, + "extraMetadata": { + "version": "0.4.52" + } +} diff --git a/build/release/builder-debug.yml b/build/release/builder-debug.yml index 4476bdb5..3c383754 100644 --- a/build/release/builder-debug.yml +++ b/build/release/builder-debug.yml @@ -17,4 +17,4 @@ x64: - out/** - package.json nsis: - script: "!include \"C:\\Users\\yang\\Documents\\Github\\AgentCompany\\node_modules\\.pnpm\\app-builder-lib@26.8.1_dmg-_62d31da0b96a3ea7b7d51a6bbe3b3b6e\\node_modules\\app-builder-lib\\templates\\nsis\\include\\StdUtils.nsh\"\n!addincludedir \"C:\\Users\\yang\\Documents\\Github\\AgentCompany\\node_modules\\.pnpm\\app-builder-lib@26.8.1_dmg-_62d31da0b96a3ea7b7d51a6bbe3b3b6e\\node_modules\\app-builder-lib\\templates\\nsis\\include\"\n!macro _isUpdated _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"updated\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isUpdated `\"\" isUpdated \"\"`\n\n!macro _isForceRun _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"force-run\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForceRun `\"\" isForceRun \"\"`\n\n!macro _isKeepShortcuts _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"keep-shortcuts\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isKeepShortcuts `\"\" isKeepShortcuts \"\"`\n\n!macro _isNoDesktopShortcut _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"no-desktop-shortcut\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isNoDesktopShortcut `\"\" isNoDesktopShortcut \"\"`\n\n!macro _isDeleteAppData _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"delete-app-data\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isDeleteAppData `\"\" isDeleteAppData \"\"`\n\n!macro _isForAllUsers _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"allusers\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForAllUsers `\"\" isForAllUsers \"\"`\n\n!macro _isForCurrentUser _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"currentuser\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForCurrentUser `\"\" isForCurrentUser \"\"`\n\n!macro addLangs\n !insertmacro MUI_LANGUAGE \"English\"\n !insertmacro MUI_LANGUAGE \"German\"\n !insertmacro MUI_LANGUAGE \"French\"\n !insertmacro MUI_LANGUAGE \"SpanishInternational\"\n !insertmacro MUI_LANGUAGE \"SimpChinese\"\n !insertmacro MUI_LANGUAGE \"TradChinese\"\n !insertmacro MUI_LANGUAGE \"Japanese\"\n !insertmacro MUI_LANGUAGE \"Korean\"\n !insertmacro MUI_LANGUAGE \"Italian\"\n !insertmacro MUI_LANGUAGE \"Dutch\"\n !insertmacro MUI_LANGUAGE \"Danish\"\n !insertmacro MUI_LANGUAGE \"Swedish\"\n !insertmacro MUI_LANGUAGE \"Norwegian\"\n !insertmacro MUI_LANGUAGE \"Finnish\"\n !insertmacro MUI_LANGUAGE \"Russian\"\n !insertmacro MUI_LANGUAGE \"Portuguese\"\n !insertmacro MUI_LANGUAGE \"PortugueseBR\"\n !insertmacro MUI_LANGUAGE \"Polish\"\n !insertmacro MUI_LANGUAGE \"Ukrainian\"\n !insertmacro MUI_LANGUAGE \"Czech\"\n !insertmacro MUI_LANGUAGE \"Slovak\"\n !insertmacro MUI_LANGUAGE \"Hungarian\"\n !insertmacro MUI_LANGUAGE \"Arabic\"\n !insertmacro MUI_LANGUAGE \"Turkish\"\n !insertmacro MUI_LANGUAGE \"Thai\"\n !insertmacro MUI_LANGUAGE \"Vietnamese\"\n!macroend\n\n!addplugindir /x86-unicode \"C:\\Users\\yang\\AppData\\Local\\electron-builder\\Cache\\nsis\\nsis-resources-3.4.1-nsis-resources-3.4.1\\plugins\\x86-unicode\"\n!include \"C:\\Users\\yang\\AppData\\Local\\Temp\\t-BM1w1t\\4-messages.nsh\"\n\n!include \"common.nsh\"\n!include \"extractAppPackage.nsh\"\n\n# https://github.com/electron-userland/electron-builder/issues/3972#issuecomment-505171582\nCRCCheck off\nWindowIcon Off\nAutoCloseWindow True\nRequestExecutionLevel ${REQUEST_EXECUTION_LEVEL}\n\nFunction .onInit\n !ifndef SPLASH_IMAGE\n SetSilent silent\n !endif\n\n !insertmacro check64BitAndSetRegView\nFunctionEnd\n\nFunction .onGUIInit\n InitPluginsDir\n\n !ifdef SPLASH_IMAGE\n File /oname=$PLUGINSDIR\\splash.bmp \"${SPLASH_IMAGE}\"\n BgImage::SetBg $PLUGINSDIR\\splash.bmp\n BgImage::Redraw\n !endif\nFunctionEnd\n\nSection\n !ifdef SPLASH_IMAGE\n HideWindow\n !endif\n\n StrCpy $INSTDIR \"$PLUGINSDIR\\app\"\n !ifdef UNPACK_DIR_NAME\n StrCpy $INSTDIR \"$TEMP\\${UNPACK_DIR_NAME}\"\n !endif\n\n RMDir /r $INSTDIR\n SetOutPath $INSTDIR\n\n !ifdef APP_DIR_64\n !ifdef APP_DIR_ARM64\n !ifdef APP_DIR_32\n ${if} ${IsNativeARM64}\n File /r \"${APP_DIR_ARM64}\\*.*\"\n ${elseif} ${RunningX64}\n File /r \"${APP_DIR_64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_32}\\*.*\"\n ${endIf}\n !else\n ${if} ${IsNativeARM64}\n File /r \"${APP_DIR_ARM64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_64}\\*.*\"\n {endIf}\n !endif\n !else\n !ifdef APP_DIR_32\n ${if} ${RunningX64}\n File /r \"${APP_DIR_64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_32}\\*.*\"\n ${endIf}\n !else\n File /r \"${APP_DIR_64}\\*.*\"\n !endif\n !endif\n !else\n !ifdef APP_DIR_32\n File /r \"${APP_DIR_32}\\*.*\"\n !else\n !insertmacro extractEmbeddedAppPackage\n !endif\n !endif\n\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_DIR\", \"$EXEDIR\").r0'\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_FILE\", \"$EXEPATH\").r0'\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_APP_FILENAME\", \"${APP_FILENAME}\").r0'\n ${StdUtils.GetAllParameters} $R0 0\n\n !ifdef SPLASH_IMAGE\n BgImage::Destroy\n !endif\n\n\tExecWait \"$INSTDIR\\${APP_EXECUTABLE_FILENAME} $R0\" $0\n SetErrorLevel $0\n\n SetOutPath $EXEDIR\n\tRMDir /r $INSTDIR\nSectionEnd\n" + script: "!include \"C:\\Users\\zhang\\Documents\\GitHub\\AgentCompany\\node_modules\\.pnpm\\app-builder-lib@26.8.1_dmg-_62d31da0b96a3ea7b7d51a6bbe3b3b6e\\node_modules\\app-builder-lib\\templates\\nsis\\include\\StdUtils.nsh\"\n!addincludedir \"C:\\Users\\zhang\\Documents\\GitHub\\AgentCompany\\node_modules\\.pnpm\\app-builder-lib@26.8.1_dmg-_62d31da0b96a3ea7b7d51a6bbe3b3b6e\\node_modules\\app-builder-lib\\templates\\nsis\\include\"\n!macro _isUpdated _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"updated\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isUpdated `\"\" isUpdated \"\"`\n\n!macro _isForceRun _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"force-run\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForceRun `\"\" isForceRun \"\"`\n\n!macro _isKeepShortcuts _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"keep-shortcuts\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isKeepShortcuts `\"\" isKeepShortcuts \"\"`\n\n!macro _isNoDesktopShortcut _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"no-desktop-shortcut\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isNoDesktopShortcut `\"\" isNoDesktopShortcut \"\"`\n\n!macro _isDeleteAppData _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"delete-app-data\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isDeleteAppData `\"\" isDeleteAppData \"\"`\n\n!macro _isForAllUsers _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"allusers\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForAllUsers `\"\" isForAllUsers \"\"`\n\n!macro _isForCurrentUser _a _b _t _f\n ${StdUtils.TestParameter} $R9 \"currentuser\"\n StrCmp \"$R9\" \"true\" `${_t}` `${_f}`\n!macroend\n!define isForCurrentUser `\"\" isForCurrentUser \"\"`\n\n!macro addLangs\n !insertmacro MUI_LANGUAGE \"English\"\n !insertmacro MUI_LANGUAGE \"German\"\n !insertmacro MUI_LANGUAGE \"French\"\n !insertmacro MUI_LANGUAGE \"SpanishInternational\"\n !insertmacro MUI_LANGUAGE \"SimpChinese\"\n !insertmacro MUI_LANGUAGE \"TradChinese\"\n !insertmacro MUI_LANGUAGE \"Japanese\"\n !insertmacro MUI_LANGUAGE \"Korean\"\n !insertmacro MUI_LANGUAGE \"Italian\"\n !insertmacro MUI_LANGUAGE \"Dutch\"\n !insertmacro MUI_LANGUAGE \"Danish\"\n !insertmacro MUI_LANGUAGE \"Swedish\"\n !insertmacro MUI_LANGUAGE \"Norwegian\"\n !insertmacro MUI_LANGUAGE \"Finnish\"\n !insertmacro MUI_LANGUAGE \"Russian\"\n !insertmacro MUI_LANGUAGE \"Portuguese\"\n !insertmacro MUI_LANGUAGE \"PortugueseBR\"\n !insertmacro MUI_LANGUAGE \"Polish\"\n !insertmacro MUI_LANGUAGE \"Ukrainian\"\n !insertmacro MUI_LANGUAGE \"Czech\"\n !insertmacro MUI_LANGUAGE \"Slovak\"\n !insertmacro MUI_LANGUAGE \"Hungarian\"\n !insertmacro MUI_LANGUAGE \"Arabic\"\n !insertmacro MUI_LANGUAGE \"Turkish\"\n !insertmacro MUI_LANGUAGE \"Thai\"\n !insertmacro MUI_LANGUAGE \"Vietnamese\"\n!macroend\n\n!addplugindir /x86-unicode \"C:\\Users\\zhang\\AppData\\Local\\electron-builder\\Cache\\nsis\\nsis-resources-3.4.1-nsis-resources-3.4.1\\plugins\\x86-unicode\"\n!include \"C:\\Users\\zhang\\AppData\\Local\\Temp\\t-9Kaqeq\\4-messages.nsh\"\n\n!include \"common.nsh\"\n!include \"extractAppPackage.nsh\"\n\n# https://github.com/electron-userland/electron-builder/issues/3972#issuecomment-505171582\nCRCCheck off\nWindowIcon Off\nAutoCloseWindow True\nRequestExecutionLevel ${REQUEST_EXECUTION_LEVEL}\n\nFunction .onInit\n !ifndef SPLASH_IMAGE\n SetSilent silent\n !endif\n\n !insertmacro check64BitAndSetRegView\nFunctionEnd\n\nFunction .onGUIInit\n InitPluginsDir\n\n !ifdef SPLASH_IMAGE\n File /oname=$PLUGINSDIR\\splash.bmp \"${SPLASH_IMAGE}\"\n BgImage::SetBg $PLUGINSDIR\\splash.bmp\n BgImage::Redraw\n !endif\nFunctionEnd\n\nSection\n !ifdef SPLASH_IMAGE\n HideWindow\n !endif\n\n StrCpy $INSTDIR \"$PLUGINSDIR\\app\"\n !ifdef UNPACK_DIR_NAME\n StrCpy $INSTDIR \"$TEMP\\${UNPACK_DIR_NAME}\"\n !endif\n\n RMDir /r $INSTDIR\n SetOutPath $INSTDIR\n\n !ifdef APP_DIR_64\n !ifdef APP_DIR_ARM64\n !ifdef APP_DIR_32\n ${if} ${IsNativeARM64}\n File /r \"${APP_DIR_ARM64}\\*.*\"\n ${elseif} ${RunningX64}\n File /r \"${APP_DIR_64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_32}\\*.*\"\n ${endIf}\n !else\n ${if} ${IsNativeARM64}\n File /r \"${APP_DIR_ARM64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_64}\\*.*\"\n {endIf}\n !endif\n !else\n !ifdef APP_DIR_32\n ${if} ${RunningX64}\n File /r \"${APP_DIR_64}\\*.*\"\n ${else}\n File /r \"${APP_DIR_32}\\*.*\"\n ${endIf}\n !else\n File /r \"${APP_DIR_64}\\*.*\"\n !endif\n !endif\n !else\n !ifdef APP_DIR_32\n File /r \"${APP_DIR_32}\\*.*\"\n !else\n !insertmacro extractEmbeddedAppPackage\n !endif\n !endif\n\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_DIR\", \"$EXEDIR\").r0'\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_FILE\", \"$EXEPATH\").r0'\n System::Call 'Kernel32::SetEnvironmentVariable(t, t)i (\"PORTABLE_EXECUTABLE_APP_FILENAME\", \"${APP_FILENAME}\").r0'\n ${StdUtils.GetAllParameters} $R0 0\n\n !ifdef SPLASH_IMAGE\n BgImage::Destroy\n !endif\n\n\tExecWait \"$INSTDIR\\${APP_EXECUTABLE_FILENAME} $R0\" $0\n SetErrorLevel $0\n\n SetOutPath $EXEDIR\n\tRMDir /r $INSTDIR\nSectionEnd\n" diff --git a/build/release/latest.yml b/build/release/latest.yml index 04b30193..564fd8f2 100644 --- a/build/release/latest.yml +++ b/build/release/latest.yml @@ -1,8 +1,8 @@ -version: 0.4.51 +version: 0.4.52 files: - - url: AgentCompany-Setup-0.4.51.exe - sha512: B1hxeWw8AZhXNYxPJrT3AA3E7GMGuGrxM+tijoHEbxjJdtpEx47Cfw7iZA+LpzLABCVPE8JskvM8M7a2035ANg== - size: 101558338 -path: AgentCompany-Setup-0.4.51.exe -sha512: B1hxeWw8AZhXNYxPJrT3AA3E7GMGuGrxM+tijoHEbxjJdtpEx47Cfw7iZA+LpzLABCVPE8JskvM8M7a2035ANg== -releaseDate: '2026-03-19T21:04:17.730Z' + - url: AgentCompany-Setup-0.4.52.exe + sha512: mwXuxahjwBeStj+5gPa8F6Ny6DZg1utectcrjLEt8Wx0IStZUVYA8aZrrx3OsYBsfGG9bJHq3/rZCFPRRoSkJw== + size: 101559599 +path: AgentCompany-Setup-0.4.52.exe +sha512: mwXuxahjwBeStj+5gPa8F6Ny6DZg1utectcrjLEt8Wx0IStZUVYA8aZrrx3OsYBsfGG9bJHq3/rZCFPRRoSkJw== +releaseDate: '2026-03-20T01:27:11.948Z' diff --git a/docs/2026-03-17-architecture-freeze.md b/docs/2026-03-17-architecture-freeze.md deleted file mode 100644 index ed76709a..00000000 --- a/docs/2026-03-17-architecture-freeze.md +++ /dev/null @@ -1,264 +0,0 @@ -# 2026-03-17 Architecture Freeze - -## Supported Platform Target - -The product target remains cross-platform: - -- macOS -- Windows -- Linux - -The current codebase can build on the local host, but release readiness is not equivalent across targets yet. - -### Platform-specific constraints to preserve - -- macOS - - hidden inset title bar - - signing and notarization required for public release - - `safeStorage` may block on keychain access -- Windows - - App User Model ID is required for correct notifications and taskbar integration - - NSIS is the release target - - code signing required for updater trust -- Linux - - safe storage backend can fall back to weaker semantics when no secret store exists - - Wayland window behavior is more limited than X11 - - AppImage is the primary packaged target today - -## External Research Decisions - -### Electron security - -Based on the current Electron security checklist: - -- keep `nodeIntegration: false` -- keep `contextIsolation: true` -- keep `sandbox: true` -- keep the custom `app://` protocol instead of `file://` -- validate sender URLs on every IPC entry point -- do not expose raw IPC objects in preload -- define a restrictive CSP in local renderer content - -### Utility process - -The current choice of `utilityProcess.fork()` is still the right worker primitive. - -Reasoning: - -- it keeps heavy connector execution off the renderer -- it is more aligned with Electron security guidance than broad use of `ELECTRON_RUN_AS_NODE` -- it is compatible with future fuse hardening around `runAsNode` - -### Secret storage - -`safeStorage` remains the primary storage contract. - -Fallback rules: - -- use OS-backed `safeStorage` when real encryption is available -- reject insecure runtime environments by default -- allow AES vault fallback only in explicitly allowed test/insecure modes - -### Updater - -`electron-updater` remains acceptable, but only if release lanes are made real. - -Required implications: - -- real publish endpoint -- signed macOS app -- Windows signing -- explicit metadata handling for the generic provider -- download/apply UX in-app, not just “check available” - -## Process Boundary Freeze - -### Main process - -Owns: - -- app lifecycle -- BrowserWindow creation -- custom protocol registration -- native menus / tray / dialogs / notifications -- database initialization -- worker process lifecycle -- updater lifecycle -- browser automation windows -- privileged IPC handlers - -Must not: - -- become the place where all query, mutation, aggregation, and orchestration code accumulates - -### Preload - -Owns: - -- typed, minimal bridge from renderer to approved use-cases - -Must not: - -- expose raw `ipcRenderer` -- expose generic file-system or shell primitives -- become a mirror of every DB method - -### Renderer - -Owns: - -- operator workflows -- presentation logic -- optimistic local UI state -- command palette / shell navigation -- empty / loading / error / review states - -Must not: - -- talk in raw CRUD terms when the user is performing operator flows -- own privileged state -- load whole-world snapshots forever as the only data pattern - -### Utility process - -Owns: - -- connector execution -- per-run stream forwarding -- cancellation -- run queue scheduling local to worker - -Must not: - -- become the persistence layer -- own security policy decisions that belong in main - -## State Ownership Model - -### Renderer-local state - -- selected entities -- open panels / modals -- search queries -- console UI state -- transient filter/sort state - -### Persisted domain state - -- companies -- connectors -- workspaces -- agents -- goals / projects / tasks -- approvals -- runs -- cost entries -- activity -- secrets -- automation rules -- workflows -- browser actions - -### Runtime execution state - -- active worker jobs -- agent concurrency map -- workspace lock ownership -- pending wakes -- update progress -- browser action queue - -### Derived read models - -These should be separated from write repositories: - -- overview metrics -- inbox -- standup -- run queue / attention views -- workload and review summaries - -## Storage Decision - -### Current decision - -Keep SQLite + WAL for this tranche. - -### Constraints - -- single-writer semantics remain acceptable if heavy aggregations are not kept in the same giant repository -- crash recovery must preserve unfinished run semantics -- backup / restore must remain profile-local and reversible - -### Required follow-up - -- split read models from write repositories -- add offset-based log readers -- keep artifacts and logs on disk, indexed in SQLite - -## IPC Contract Strategy - -### Contract rules - -- every mutating IPC call must stay company-scoped when relevant -- large payloads should use typed request objects, not positional args -- contract names stay explicit and descriptive -- result envelopes stay structured - -### Next contract change - -The next contract slice will add: - -- dedicated run listing / filtering reads -- chunked log reads -- connector selection in onboarding - -## Renderer Information Architecture Freeze - -Primary desktop sections: - -1. Overview -2. Runs -3. Tasks -4. Approvals -5. Messages -6. Agents -7. Workspaces -8. Connectors -9. Costs -10. Activity -11. Settings - -Secondary knowledge/operations surfaces can remain, but they should not dominate the shell. - -## Milestone Mapping - -### Architecture Freeze - -- this document accepted - -### Interactive Prototype - -- shell reordered around operator workflows -- run console and runs list coherent - -### Internal Alpha - -- onboarding, connector health, task run, approval, message routing, and workspace inspection work together - -### Cross-Platform Validation - -- menu, tray, notification, and package behavior documented per OS - -### Signed Beta - -- updater feed real -- signing path documented and testable - -### Release Candidate - -- all repo quality gates green - -### Production Ready - -- security audit, release hardening, and operator workflow polish complete diff --git a/docs/2026-03-17-audit-and-rebuild-plan.md b/docs/2026-03-17-audit-and-rebuild-plan.md deleted file mode 100644 index 5c35722b..00000000 --- a/docs/2026-03-17-audit-and-rebuild-plan.md +++ /dev/null @@ -1,307 +0,0 @@ -# 2026-03-17 Audit And Rebuild Plan - -## Execution Order - -This document is the Phase 1-4 artifact required by `AGENTS.md`. - -1. ProjectManager: read the full PRD and define milestone gates. -2. ResearchAnalyst: verify Electron runtime, security, packaging, updater, and OS constraints. -3. TechnicalImplementationResearcher: freeze main/preload/renderer/storage/runtime boundaries. -4. EnterpriseArchitect: validate dependency direction, module seams, IPC contracts, and naming rules. - -Implementation work starts only after the decisions below are recorded. - -## Scope - -The audit covers: - -- PRD alignment -- Electron main / preload / renderer boundaries -- IPC contracts, sender validation, and company scoping -- connector runtime execution, orchestration, and background work -- local persistence, backup / restore, workspace locking, and crash recovery -- renderer information architecture, onboarding, and desktop UX -- packaging, updater, release, and verification posture -- adapter package quality and test coverage - -## Baseline Evidence - -### Repository state - -- The working tree is already in a large, incomplete refactor state. -- The repo is no longer a prototype shell; it already contains a local data model, automation engine, workflow engine, browser automation surface, and an Electron shell. -- The implementation breadth is now large enough that coherence, not feature count, is the main problem. - -### Verification results on 2026-03-17 - -- `pnpm typecheck`: pass -- `pnpm test:unit`: pass -- `pnpm test:integration`: pass -- `pnpm test:packages`: pass -- `pnpm test:e2e`: pass -- `pnpm build:verify`: pass -- `pnpm build:dir`: pass, producing `build/release/AgentCompany-0.4.26-arm64-mac.zip` -- `pnpm lint`: fail - -### Current build posture - -- Main bundle: `out/main/index.js` about `592 kB` -- Preload bundle: `out/preload/index.js` about `151 kB` -- Renderer bundle: `out/renderer/assets/index-*.js` about `1.69 MB` -- Packaging currently succeeds only on the host platform tested locally. - -## Research Notes - -The following external references were re-checked during this audit: - -- Electron security checklist: https://www.electronjs.org/docs/latest/tutorial/security -- BrowserWindow API and platform notices: https://www.electronjs.org/docs/latest/api/browser-window -- Utility process API: https://www.electronjs.org/docs/latest/api/utility-process -- safeStorage API: https://www.electronjs.org/docs/latest/api/safe-storage -- Electron fuses: https://www.electronjs.org/docs/latest/tutorial/fuses -- electron-builder auto-update guide: https://www.electron.build/auto-update -- electron-builder publish guide: https://www.electron.build/publish - -These references matter because the PRD explicitly requires secure preload exposure, custom protocol usage, sandboxing, OS-backed secret storage, release signing, and update integrity. - -## Current State Summary - -The application already has the following real subsystems: - -- Electron shell with `contextIsolation`, `nodeIntegration: false`, `sandbox: true`, and a custom `app://` protocol -- local `DatabaseSync` SQLite store with WAL mode -- local API server for agent-driven actions -- utility-process worker for connector execution -- run orchestration, automation rules, workflow pipelines, messages, approvals, documents, meetings, sprints, and browser actions - -The main issue is not “missing a single feature”. - -The issue is that the system surface is broader than the stability and architecture boundaries that currently support it. - -## Findings - -### High severity - -1. Main-process data access is concentrated in a single synchronous repository that is too large to remain reliable. - - Evidence: `src/main/database.ts` is `3292` lines and owns schema setup, migrations, snapshots, orchestration state, cost accounting, approvals, messaging, workflow persistence, backup/restore, workspace locking, and dashboard aggregation. - - Impact: - - blocks maintainability - - encourages cross-domain coupling - - increases renderer-visible latency because many IPC calls read the same monolith - -2. The renderer shell is still a god component and does not match the desktop-native operating model in the PRD. - - Evidence: `src/renderer/App.tsx` is `1520` lines and owns navigation, data loading, company switching, command palette, modal orchestration, run log buffering, and page composition for every domain. - - Impact: - - feature interaction is hard to reason about - - state ownership is blurred - - operator workflows such as run monitoring are buried behind a console drawer instead of having first-class surfaces - -3. The verification gate defined by the project itself is currently broken because lint does not run. - - Evidence: `pnpm lint` fails with `ENOENT ... electron.vite.config..mjs`. - - Impact: - - the release checklist in `AGENTS.md` is not met - - CI or local quality enforcement is unreliable - -4. Update and release readiness are not production-grade. - - Evidence: - - `electron-builder.yml` still defaults to a generic placeholder publish URL - - `src/main/updater-service.ts` only checks availability; it does not implement a complete download/apply flow or pre-update backup - - local mac packaging falls back to ad-hoc signing and skips notarization - - Impact: - - no shippable trust chain for public release - - updater behavior is not aligned with PRD requirements - -5. Automatic internal coordination is still mostly emergent behavior from prompts and heuristics, not a hardened orchestration contract. - - Evidence: - - `src/main/prompt-builder.ts` contains the majority of “real company behavior” instructions - - `src/main/automation-service.ts` uses rule-triggered wakeups and message dispatch to simulate organization flow - - no dedicated coordination state model exists for “conversation pending response”, “delegation awaiting acceptance”, or “review SLA breached” - - Impact: - - the system can appear intelligent in happy paths while remaining hard to debug in stalled paths - - message flow, approvals, and delegation are not deterministic enough for a production control plane - -### Medium severity - -1. Snapshot loading is coarse-grained and expensive. - - Evidence: - - `db.listSnapshot()` reads nearly every table every time - - `db.listRendererSnapshot()` still calls `listSnapshot()` first and only then filters by company - - `App.refresh()` reloads the entire current company snapshot on most domain events - - Impact: - - unnecessary main-process work - - unnecessary renderer re-renders - - future scale problems once run/message/activity volume increases - -2. Run log access is full-file based, not incremental. - - Evidence: - - `src/main/database.ts#readRunLog` returns the entire log file - - renderer keeps in-memory log buffers in `App.tsx` - - PRD mismatch: - - PRD requires offset-based incremental log reading - - Impact: - - degraded performance on large logs - - poor long-running run observability - -3. The preload bridge is typed but too broad for the current architecture maturity. - - Evidence: `src/preload/index.ts` exposes dozens of direct CRUD and orchestration methods. - - Impact: - - large privileged surface area - - difficult contract governance - - weak module boundaries between renderer and domain actions - -4. Workflow and automation configuration crosses boundaries as opaque JSON strings. - - Evidence: - - `conditionsJson`, `actionConfigJson`, `stepsJson`, `triggerConfigJson`, `metadataJson`, `attachmentsJson` are passed across IPC and persistence as raw strings - - Impact: - - weak compile-time guarantees - - more runtime parsing failures - - harder migration and refactoring path - -5. Onboarding still optimizes for spectacle more than operator clarity. - - Evidence: - - `src/renderer/components/CompanyOnboarding.tsx` strongly promises “autonomous company” behavior - - connector choice and capability differences are not explicit enough - - initial run review is not operator-first - - Impact: - - weak first-use trust - - harder diagnosis when the selected connector is degraded or capability-limited - -6. Browser/social automation is fragile and outside the core PRD center of gravity. - - Evidence: - - `src/main/browser-manager.ts` relies on direct DOM selectors, timed delays, and third-party site structure - - Impact: - - brittle surface - - expands support burden - - competes with higher-priority core orchestration work - -7. The app supports many domains, but there is no dedicated Runs page. - - Evidence: - - run monitoring exists only through dashboard widgets and the bottom console drawer - - PRD mismatch: - - PRD requires live runs and log console as first-class operating surfaces - -### Low severity - -1. Renderer design language is coherent but still too web-dashboard-like for a desktop control plane. - - Evidence: - - system font stack in `src/renderer/styles.css` - - single-column content area on many dense surfaces - - heavy modal use for editing - -2. Package-level runtime verification is narrow. - - Evidence: - - `test:packages` covers only `adapter-opencode-local` and `adapter-kimi-local` - - `codex`, `claude`, and `gemini` packages do not have equivalent package test commands here - -## Detailed Gap List By Domain - -### Main / preload / IPC - -- Sender validation exists and the custom protocol is correct, which is a good baseline. -- Contracts are typed, but not versioned and not yet narrowed by capability groups. -- `ipc-core-common.ts` and `ipc-domain-common.ts` still use `any`-heavy dependency wiring. -- The privileged bridge still mirrors domain CRUD too directly instead of exposing operator use-cases. - -### Data / orchestration - -- Workspace lock handling exists, but it is still embedded inside the database class. -- Queued-run recovery exists, but recovery semantics are still best-effort. -- Workflow execution is inline, stateful, and sync-heavy in the main process service layer. -- Aggregation views such as inbox, standup, and metrics are built inside the same DB class. - -### Connectors - -- Codex / Claude / Gemini / OpenCode have deeper execution paths than Kimi. -- Kimi remains generic CLI mode with explicit capability downgrade. -- Connector health checks use command probes and status translation, but operator feedback is still too shallow. -- Connector discovery and operator selection are still secondary in first-launch UX. - -### Renderer - -- The UI covers a large domain surface, but the operating hierarchy is inverted: - - overview first - - CRUD pages second - - runs/logs/workspaces hidden as secondary tools -- The app does not yet “scream” workspace, run, and coordination as the primary desktop tasks. - -### Testing / release - -- Current automated coverage is better than zero, but still not close to the PRD completion criteria. -- E2E currently proves only first-launch onboarding and console toggle. -- No E2E covers: - - task run lifecycle - - approval resolution - - message routing - - workflow execution - - connector health state changes - - backup/restore - - workspace locking / recovery -- Release docs do not yet define signed beta, RC, and production lanes. - -## Rebuild Direction - -### Tranche A: operator shell and observability - -- add first-class Runs navigation and run triage workflow -- move run logs to chunked readers instead of whole-file loads -- make onboarding explicitly choose connector and workspace intent -- fix lint so the repo’s minimum quality gate works again - -### Tranche B: boundary extraction - -- split `AppDatabase` into read models, command repositories, and orchestration state store -- reduce preload surface to bounded capability groups -- split renderer shell from feature pages and detail inspectors - -### Tranche C: release hardening - -- define real update lanes and signing rules -- add updater backup/install flow -- document mac notarization, Windows signing, Linux packaging behavior -- flip relevant Electron fuses during packaging - -## Milestone Gates - -### Architecture Freeze - -- audit findings accepted -- platform target confirmed -- process boundaries frozen -- core run / workspace / connector model frozen - -### Interactive Prototype - -- onboarding, connectors, agents, tasks, runs, console, workspaces all coherent - -### Internal Alpha - -- connector lifecycle, approvals, message routing, and crash recovery usable end-to-end - -### Cross-Platform Validation - -- macOS, Windows, Linux behavior documented and smoke-validated - -### Signed Beta Build - -- signing path defined -- updater feed real -- rollback / backup strategy documented - -### Release Candidate - -- lint, typecheck, unit, integration, E2E, build, and packaging gates all green - -### Production Readiness - -- security audit complete -- no placeholder release config remains -- operator workflows are deterministic and observable - -## Next Implementation Slice - -The next code tranche will target: - -1. repair the lint gate -2. add first-class Runs navigation and incremental log loading -3. make onboarding force an explicit connector choice -4. keep build packaging green while making these changes diff --git a/docs/2026-03-17-runtime-security-tranche.md b/docs/2026-03-17-runtime-security-tranche.md deleted file mode 100644 index 6c49e09f..00000000 --- a/docs/2026-03-17-runtime-security-tranche.md +++ /dev/null @@ -1,31 +0,0 @@ -# 2026-03-17 Runtime/Security Tranche - -This tranche locks the first production-boundary fixes before larger module extraction. - -## Scope - -- tighten agent run-token authority in the local API server -- stop default connector execution from bypassing runtime permission/sandbox flags -- harden renderer IPC sender validation -- prevent secret bindings from overriding process-critical environment keys -- make browser-action cancellation affect real execution, not only database state - -## Why this tranche comes first - -These issues sit underneath every feature: - -1. A compromised or overpowered run token could mutate company structure, approvals, and browser automation. -2. A globally bypassed connector runtime defeats approval and sandbox posture for all task runs. -3. Loose origin matching weakens the only boundary between renderer and privileged main-process handlers. -4. Fake cancellation creates visible state drift between the UI and external side effects. -5. Unrestricted env injection allows privilege escalation through runtime process configuration. - -## Intended outcome - -After this tranche: - -- agent runs can no longer approve approvals or request arbitrary organization changes without role-based checks -- connector child processes run without implicit dangerous bypass flags -- only exact trusted renderer origins can invoke privileged IPC handlers -- reserved env keys are blocked from connector secret bindings -- cancelling a browser action cancels queued or active execution, not just the row in SQLite diff --git a/docs/2026-03-18-adversarial-review-verdict.md b/docs/2026-03-18-adversarial-review-verdict.md deleted file mode 100644 index 188bf1f1..00000000 --- a/docs/2026-03-18-adversarial-review-verdict.md +++ /dev/null @@ -1,49 +0,0 @@ -## Intent -Unify and harden cross-process message flow in AgentCompany so API/IPC/automation/renderer behavior is consistent, secure, and production-safe. - -## Verdict: CONTESTED (pre-fix) -> PASS (post-fix) -Pre-fix review surfaced multiple high-severity issues; after remediation in this cycle, no remaining high-severity blocker is accepted. - -## Findings -1. **[high] Unsafe connector bypass inconsistency** (`src/main/orchestrator/worker.ts:114-119`) - - Lens: Skeptic, Architect - - Principle: `fix-root-causes`, `boundary-discipline` - - Recommendation: gate `dangerouslySkipPermissions` by explicit env flag. - -2. **[high] API message path missing real-time message event parity** (`src/main/api-server.ts:1456-1465`) - - Lens: Architect, Minimalist - - Principle: `prove-it-works`, `foundational-thinking` - - Recommendation: wire `emitEvent` through API callbacks and dispatch dependencies. - -3. **[high] Mark-read visibility check false negatives under message volume** (`src/main/ipc-agent-coordination-handlers.ts:82-87`) - - Lens: Skeptic - - Principle: `prove-it-works` - - Recommendation: replace bounded list scan with targeted lookup by `messageId`. - -4. **[medium] Message validation drift risk (schema vs dispatch normalization)** (`src/shared/contracts.ts:413-436`, `src/main/message-dispatch.ts:99-110`) - - Lens: Skeptic, Architect, Minimalist - - Principle: `foundational-thinking` - - Recommendation: enforce same recipient/channel invariants in dispatch layer (implemented). - -5. **[medium] Wake target resolution snapshot scaling risk** (`src/main/message-dispatch.ts:177-223`) - - Lens: Skeptic, Architect - - Principle: `cost-aware-delegation` - - Recommendation: follow-up optimization to avoid full snapshot traversal per message. - -6. **[low] Direct-channel avatar identity ambiguity** (`src/renderer/components/communication.tsx:134-140`, `:288-304`) - - Lens: Skeptic, Minimalist - - Principle: `outcome-oriented-execution` - - Recommendation: bind avatar identity to active viewer context (deferred). - -## What Went Well -- Message flow now has one dispatch path shared by API, IPC, and automation. -- Quality gates (typecheck/lint/unit/integration/e2e/build) all pass after remediation. -- Build pipeline now emits cleaned release artifacts to `/build/release` with explicit version. - -## Lead Judgment -1. Accept. Fixed in this cycle. -2. Accept. Fixed in this cycle. -3. Accept. Fixed in this cycle. -4. Accept. Partially fixed by dispatch-layer invariant enforcement; future refactor can further reduce duplication. -5. Accept as valid risk. Deferred to next optimization wave because correctness/security was prioritized first. -6. Accept as low-priority UX consistency issue. Deferred. diff --git a/docs/2026-03-18-cross-feature-integrity-audit.md b/docs/2026-03-18-cross-feature-integrity-audit.md deleted file mode 100644 index f8bba9a5..00000000 --- a/docs/2026-03-18-cross-feature-integrity-audit.md +++ /dev/null @@ -1,203 +0,0 @@ -# AgentCompany Cross-Feature Integrity Audit (2026-03-18) - -## Scope - -- `PRD.md` -- Electron runtime code under `src/main`, `src/preload`, `src/renderer`, `src/shared` -- Connector adapters under `packages/*` -- Build and packaging config: `package.json`, `electron-builder.yml`, `electron.vite.config.ts`, `scripts/build-desktop.mjs` -- Existing validation assets under `tests/unit`, `tests/integration`, `tests/e2e` -- Current Electron guidance and packaging guidance from official Electron and electron-builder documentation - -## Pipeline Mapping - -### Phase 1: ProjectManager - -- Confirmed product target remains a local-first Electron desktop control plane, not a browser-shaped admin shell. -- Current milestone for this remediation wave: cross-feature integrity, autonomous execution gating, and release validation. - -### Phase 2: ResearchAnalyst - -- Checked current Electron guidance for: - - context isolation and preload boundaries - - sandbox and navigation restrictions - - utility-process usage - - packaging and update prerequisites - -### Phase 3: TechnicalImplementationResearcher - -- Traced how connector health, onboarding, hiring, orchestration, snapshot loading, and renderer forms interact. -- Focused on defects where one subsystem looked valid locally but corrupted another subsystem at runtime. - -### Phase 4: EnterpriseArchitect - -- Validated that the highest-severity current issues were policy drift and hidden truncation, not absence of feature surfaces. -- Chose to fix shared policy and shared data integrity before any cosmetic refactor. - -## What Is Already Real - -- Main / preload / renderer separation exists. -- Renderer does not depend on direct Node access. -- SQLite-backed local-first persistence exists. -- The app already contains real systems for onboarding, companies, agents, goals, projects, tasks, approvals, runs, costs, meetings, knowledge, documents, social accounts, automation, messaging, and packaging. -- Worker execution already runs outside the renderer. - -## Highest-Severity Findings From This Audit - -### 1. Global snapshot truncation polluted company-scoped behavior - -Source: - -- `src/main/database.ts` - -Problem: - -- `listSnapshot()` hard-capped `runs`, `cost_entries`, `agent_messages`, and `activity`. -- `listRendererSnapshot()` then filtered that already-truncated global result down to the current company. -- This allowed newer rows from other companies to displace current-company runs, messages, costs, and activity from the renderer snapshot. - -Why this matters: - -- Inbox severity, board unread counts, standup summaries, cost totals, run history, and communication history could silently diverge from real persisted state. -- The defect was cross-feature by construction because multiple views derived from the same truncated snapshot. - -### 2. Connector policy drift allowed non-production runtimes onto the main execution path - -Sources: - -- `src/main/onboarding-service.ts` -- `src/main/orchestration-service.ts` -- `src/main/ipc-core-work-graph-handlers.ts` -- `src/main/api-server.ts` -- `src/renderer/components/CompanyOnboarding.tsx` -- `src/renderer/components/organization.tsx` -- `src/main/connectors.ts` -- `packages/adapter-kimi-local/src/server/test.ts` - -Problem: - -- Health checks marked generic or partial runtimes as `degraded`. -- Onboarding and orchestration still treated `degraded` as runnable. -- Hiring and agent editing could still bind agents to connectors that were not suitable for autonomous execution. -- `kimi_local` was explicitly documented as generic downgraded mode but was still able to enter product-critical flows. - -Why this matters: - -- The app could create or keep agents on runtimes that do not guarantee session resume or structured transcripts. -- That breaks the internal coordination model because messaging, run tracking, recovery, and auditability depend on a stable execution contract. - -### 3. Policy enforcement was inconsistent across desktop UI, IPC, and local agent API - -Problem: - -- Even if the renderer restricted a path, API or IPC handlers could still write invalid connector assignments unless the same rule existed server-side. - -Why this matters: - -- The system would appear correct in one surface and remain invalid in another. -- That is an integrity failure, not a pure UI bug. - -### 4. Architecture remains operationally real but still too concentrated - -Largest modules remain heavy: - -- `src/main/database.ts` -- `src/main/api-server.ts` -- `src/main/automation-service.ts` -- `src/main/prompt-builder.ts` -- `src/renderer/App.tsx` -- `src/renderer/components/communication.tsx` -- `src/renderer/components/execution.tsx` - -Interpretation: - -- The app is no longer a blank prototype, but some boundaries are still broader than a stable long-term production shape. -- This is a structural follow-up item, not a reason to leave integrity bugs unfixed. - -## Remediation Implemented In This Wave - -### Shared connector execution policy - -- Added `src/shared/connector-policy.ts`. -- Defined a single execution-readiness rule used by main and renderer. -- Autonomous execution now requires: - - connector status `ready` - - session resume support - - structured transcript support - -### Snapshot integrity correction - -- Kept protective limits on the global snapshot path used by broad system scans. -- Moved renderer and company analytics onto company-scoped SQL reads instead of filtering a global snapshot in memory. -- Added company + `created_at` indexes so the scoped history reads can stay predictable as data grows. - -### Main-side enforcement - -- Onboarding now rejects non-execution-ready connectors instead of silently falling back. -- Orchestration now blocks autonomous runs when the connector is not execution-ready. -- IPC agent save / hire request paths now reject non-execution-ready connectors. -- Local agent API hire requests now reject non-execution-ready connectors. - -### Renderer alignment - -- Onboarding only offers execution-ready connectors and explains why others are blocked. -- Agent creation only offers execution-ready connectors. -- Agent editing keeps blocked current connectors visible for repair, but prevents saving or waking until the agent is moved onto a ready connector. -- Dashboard connector health now uses the same execution-readiness rule as onboarding and orchestration. - -## Verification Executed - -- `pnpm typecheck` -- `pnpm lint` -- `pnpm test:unit` -- `pnpm test:integration` -- `pnpm test:e2e` -- `AGENTCOMPANY_BUILD_VERSION=0.4.39 pnpm build` - -Build artifact produced in this session: - -- `build/release/AgentCompany-0.4.39-arm64-mac.zip` - -## Adversarial Review Outcome - -- Reviewer CLI used: `claude -p` -- Lenses run: skeptic, architect, minimalist -- Accepted findings from that pass: - - global LIMIT removal was the wrong fix; replaced with company-scoped SQL snapshot reads while keeping global caps - - connector lookup failure needed structured `CONNECTOR_NOT_FOUND` handling instead of uncaught throws - - dashboard readiness needed to use the same connector policy as onboarding and orchestration -- Rejected findings from that pass: - - version bump concerns were procedural, not product risk - - pure shared readiness helpers used by renderer were acceptable because they remain data-only and build-safe in the current architecture - -## Residual Risks Still Present After This Wave - -### 1. Preload surface is still broad - -- It is typed, but still larger than ideal for a final hardened desktop app. - -### 2. Several modules are still too large - -- The codebase still needs deeper boundary extraction in `database`, `api-server`, `automation-service`, renderer shell composition, and some feature panels. - -### 3. Cross-platform packaging has not yet been runtime-verified on all target OSes in this session - -- Packaging config exists for macOS, Windows, and Linux. -- This session validated macOS packaging only. - -### 4. Signing / updater production readiness still depends on release credentials and environment - -- The code and config exist, but release trust still depends on actual signing/notarization/update infrastructure material. - -## External References Used - -- Electron security checklist: - - https://www.electronjs.org/docs/latest/tutorial/security -- Electron context bridge: - - https://www.electronjs.org/docs/latest/api/context-bridge -- Electron utility process: - - https://www.electronjs.org/docs/latest/api/utility-process -- electron-builder auto-update: - - https://www.electron.build/auto-update -- electron-builder macOS code signing: - - https://www.electron.build/code-signing-mac diff --git a/docs/2026-03-18-full-system-audit-remediation.md b/docs/2026-03-18-full-system-audit-remediation.md deleted file mode 100644 index fc1c4931..00000000 --- a/docs/2026-03-18-full-system-audit-remediation.md +++ /dev/null @@ -1,167 +0,0 @@ -# AgentCompany Full System Audit + Cross-Feature Remediation (2026-03-18) - -## Scope and Method - -- Repository scan scope: `src/main`, `src/preload`, `src/renderer`, `src/shared`, `tests`, `scripts`, build config. -- File inventory baseline: `173` tracked files (`rg --files | wc -l`). -- Validation gates executed after remediation: - - `pnpm typecheck` - - `pnpm lint` - - `pnpm typecheck:all` - - `pnpm test:unit` - - `pnpm test:integration` - - `pnpm test:packages` - - `pnpm test:e2e` - - `AGENTCOMPANY_BUILD_VERSION=0.4.28 pnpm build` - -## Mandatory Pipeline Mapping - -### Phase 1 (ProjectManager): Scope + Milestones - -- Current working milestone interpreted as: stabilize autonomous coordination + message workflow + desktop shell release readiness. -- Release artifact target confirmed: macOS arm64 zip output in `/build/release`. - -### Phase 2 (ResearchAnalyst): Electron + Desktop Constraints - -- Security baseline validated against Electron guidance: - - renderer with `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true` in desktop shell and browser windows. - - webview attach blocked; external navigation constrained in shell manager. -- Packaging/update baseline validated against `electron-builder.yml` and runtime build script. - -### Phase 3 (TechnicalImplementationResearcher): Integration Model - -- High-friction subsystem identified: message pipeline drift across API/IPC/automation/renderer. -- Root cause: duplicated validation + duplicated wake/emit logic + per-entrypoint special behavior. -- Strategy used: unify dispatch semantics in one `main` module and route all entrypoints through it. - -### Phase 4 (EnterpriseArchitect): Boundary Enforcement - -- Main/preload/renderer boundary retained. -- IPC contract hardened for message semantics (`direct`/`department`/`project` requirements). -- Connector execution defaults tightened to avoid implicit unsafe permission bypass. - -### Phase 5-8 (Implementation + Verification) - -Implemented and validated in this cycle (details below). - -## Cross-Feature Coupling Audit (Before Fix) - -### Critical coupling defects - -1. Message contract drift between API / IPC / automation - - Different validation, wake-up targeting, and event emission behavior. -2. Direct-message send path in renderer could lose recipient context outside reply mode. -3. Mark-read visibility check used bounded list (`limit=500`), causing false negatives at scale. -4. API message write path did not emit `new-message` event, unlike IPC/automation. -5. Connector execution safety policy inconsistent (`claude_local` bypass always on). -6. Console visibility control removed from header, breaking e2e contract and operator UX. - -## Remediation Implemented - -### 1) Unified message dispatch (single source of truth) - -- Added: `src/main/message-dispatch.ts` -- Centralized: - - input normalization - - company/agent/project/parent validation - - send + event emission - - domain refresh publication - - wake target resolution by channel - -### 2) API, IPC, automation now use same dispatch behavior - -- API send route now uses `dispatchAgentMessage` and shared schema parse. -- IPC send handler now uses `dispatchAgentMessage`. -- Automation actions (`send_message`, escalation, cross-department notify) now use `dispatchAgentMessage`. - -### 3) IPC + API mark-read visibility correction - -- Added `messageId` filter support to `db.listAgentMessages(...)`. -- Replaced broad visibility scans with targeted message visibility lookup (`limit: 1` + `messageId`). -- Removed over-restrictive direct-recipient-only mark-read branch to avoid false denial when sender marks visible thread entries. - -### 4) API event parity restored - -- `AgentApiServer` now receives and uses `emitEvent` callback. -- API-originated message writes now emit `new-message` event consistently with IPC/automation. - -### 5) Connector security tightening - -- `claude_local` execution config now ties `dangerouslySkipPermissions` to `AGENTCOMPANY_ALLOW_UNSAFE_CONNECTOR_BYPASS` (default false). -- `.env.example` documents this guard variable. - -### 6) Renderer communication channel correctness - -- DM channels now store participant sets and resolve recipient deterministically for composer send path. -- Compose modal enforces target requirements per channel (department/project require target; non-target channels clear target state). - -### 7) Contract hardening - -- `agentMessageInputSchema.superRefine(...)` enforces: - - direct must provide `toAgentId` - - department/project must provide `channelTargetId` - - non-direct must not provide `toAgentId` -- Dispatch layer now enforces the same non-direct recipient prohibition, eliminating API/automation semantic mismatch. - -### 8) Desktop UX/e2e compatibility restore - -- Restored header `Show console / Hide console` control in `App.tsx` with stable ARIA/title semantics used by e2e. - -## Adversarial Review (Opposite Model CLI) - -- Ran `claude -p` in three lenses: `skeptic`, `architect`, `minimalist`. -- Output artifacts stored under `/tmp/adversarial-review-agentcompany-20260318-120225/`. -- Key accepted findings addressed in this cycle: - - unsafe connector bypass inconsistency - - API event parity gap - - bounded mark-read visibility check correctness gap -- Remaining contested/medium findings (deferred) tracked in “Open Risks”. - -## Open Risks / Remaining Work - -1. `resolveMessageWakeTargets` is snapshot-based and can be heavy at larger org scale. -2. Incident/company channel wake fan-out can cause high wake volume without throttling. -3. Message attachment JSON is stored as raw string; no structural JSON validation yet. -4. Some renderer DM channel avatar semantics still depend on ambiguous channel-level `agentId` for two-party threads. -5. Update/notarization production credential flow still depends on release environment material availability. - -## Verification Evidence - -- Type safety: pass (`pnpm typecheck`, `pnpm typecheck:all`). -- Lint: pass (`pnpm lint`). -- Unit/integration/package tests: pass. -- E2E smoke: pass. -- Build packaging: - - build folder cleaned by script - - version used: `0.4.28` - - artifact produced: `build/release/AgentCompany-0.4.28-arm64-mac.zip` - -## External Research References - -- Electron security checklist and architecture guidance: - - https://www.electronjs.org/docs/latest/tutorial/security -- Preload and contextBridge guidance: - - https://www.electronjs.org/docs/latest/tutorial/tutorial-preload - - https://www.electronjs.org/docs/latest/api/context-bridge -- BrowserWindow/webPreferences hardening: - - https://www.electronjs.org/docs/latest/api/browser-window -- Utility/background process model: - - https://www.electronjs.org/docs/latest/api/utility-process -- Packaging and auto-update config: - - https://www.electron.build/ - - https://www.electron.build/auto-update - -## Changed Files (this remediation wave) - -- `src/main/message-dispatch.ts` (new) -- `src/main/api-server.ts` -- `src/main/ipc-agent-coordination-handlers.ts` -- `src/main/database.ts` -- `src/main/automation-service.ts` -- `src/main/bootstrap-application.ts` -- `src/main/orchestrator/worker.ts` -- `src/shared/contracts.ts` -- `src/renderer/components/communication.tsx` -- `src/renderer/App.tsx` -- `.env.example` -- `package.json` diff --git a/docs/2026-03-18-integrated-audit-matrix.md b/docs/2026-03-18-integrated-audit-matrix.md deleted file mode 100644 index a0e23e89..00000000 --- a/docs/2026-03-18-integrated-audit-matrix.md +++ /dev/null @@ -1,333 +0,0 @@ -# AgentCompany Integrated Audit Matrix (2026-03-18) - -## Scope - -This audit covered: - -- `PRD.md` -- Electron main / preload / renderer source under `src/main`, `src/preload`, `src/renderer`, `src/shared` -- Packaging and runtime config: `electron-builder.yml`, `electron.vite.config.ts`, `scripts/build-desktop.mjs` -- Existing validation assets under `tests/unit`, `tests/integration`, `tests/e2e` -- Runtime validation through a live Electron launch with real IPC, onboarding, org creation, approvals, and run orchestration - -## Evidence Collected - -Validation commands executed in this session: - -- `pnpm typecheck` -- `pnpm lint` -- `pnpm test:unit` -- `pnpm test:integration` -- `pnpm test:e2e` -- `pnpm electron-vite build` - -Additional runtime scenarios executed against the built Electron app: - -- full first-launch onboarding to desktop shell -- navigation sweep across desktop sections -- live hire request flow with `autoApproveHires = true` -- live hire request flow with `autoApproveHires = false` -- negative-path validation that legacy `saveAgent` creation and `pending_approval -> idle` status bypasses are rejected - -## Current Architecture Reality - -What already exists and is materially real: - -- main / preload / renderer separation is implemented -- renderer uses typed preload APIs instead of raw Node access -- `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true` are used for the main desktop shell -- orchestration worker runs in an Electron utility process -- SQLite-backed local-first state model exists -- connectors for `codex_local`, `claude_local`, `gemini_local`, `opencode_local`, `kimi_local` exist -- approvals, runs, costs, activity, automation, meetings, knowledge, documents, sprints, social, messages, workspaces, and onboarding all exist in code and UI - -What is still structurally weak: - -- several modules are still too large to be considered stable long-term coordination boundaries -- cross-feature side effects still live in a few orchestration-heavy modules instead of a smaller explicit event-policy layer -- some release/security/product rules exist in PRD and code, but not all are enforced uniformly in every write path - -Largest modules by line count at audit time: - -- `src/main/database.ts`: 3348 -- `src/renderer/components/execution.tsx`: 1726 -- `src/renderer/components/communication.tsx`: 1726 -- `src/main/api-server.ts`: 1697 -- `src/renderer/components/automation.tsx`: 1693 -- `src/renderer/App.tsx`: 1606 -- `src/main/prompt-builder.ts`: 1541 - -## PRD Matrix - -### 1. Local Profile / Company - -Status: implemented, with one policy caveat - -Evidence: - -- local profile bootstrap, backup/restore, company switching, settings persistence all exist -- onboarding successfully creates company, workspace, goal, initial run, and opens desktop shell - -Remaining risk: - -- company policy toggles are present, but some governance behavior is still distributed across UI, IPC, API, and automation rather than centralized in a single policy service - -### 2. Connector Management - -Status: implemented - -Evidence: - -- connector registry UI exists -- connector health service exists -- model listing and auth terminal support exist -- command/env binding handling exists - -Remaining risk: - -- connector execution policy is still sensitive to environment-based behavior and deserves a dedicated policy surface, not scattered guard clauses - -### 3. Agent Registry / Org Structure - -Status: implemented, previously inconsistent, partially hardened in this wave - -Evidence: - -- org chart, agent roster, hiring pipeline, manager/report relationships, and budget fields exist -- pending-approval lifecycle exists in data model and approval resolution logic - -Critical finding fixed in this wave: - -- renderer previously allowed direct agent creation from the Agents page, bypassing `hire_agent` approval and breaking synchronization between Hiring, Approvals, onboarding, and org history -- renderer also allowed direct status edits that could move a pending hire into an active state without an approval decision - -Remediation added: - -- dedicated `requestHire` IPC contract -- transactional `createHireRequest(...)` persistence path -- renderer Agent creation now submits hire requests instead of active agents -- IPC `saveAgent` now rejects new-agent creation and rejects direct pending-hire activation - -Runtime proof: - -- with `autoApproveHires = false`, a new hire lands in `pending_approval` -- direct `saveAgent` creation returns `AGENT_CREATION_REQUIRES_HIRE_REQUEST` -- direct status bypass returns `AGENT_APPROVAL_REQUIRED` -- formal approval transitions the same agent record to `idle` - -### 4. Goals / Projects / Tasks - -Status: implemented - -Evidence: - -- onboarding creates goal and project structure -- task creation, comments, assignment, review states, kanban/list views, and run linkage exist -- task status changes feed automation/orchestration paths - -Remaining risk: - -- feature is functionally present, but task + review + message + automation interactions are still distributed across large renderer and main modules - -### 5. Run Orchestration / Session Continuity - -Status: implemented, with scaling caveats - -Evidence: - -- run creation, queueing, worker dispatch, recovery, logs, cancellation, and run status events exist -- utility-process worker is used instead of running orchestration in the renderer -- integration tests cover autonomous chain and recovery cases - -Remaining risk: - -- orchestration and wake logic remain heavily centralized -- cross-feature triggers are correct at current scope, but scaling behavior and event fan-out still need a slimmer event-policy boundary - -### 6. Approvals / Governance - -Status: implemented, materially improved in this wave - -Evidence: - -- approvals UI, decision handling, related task linkage, and automation callbacks exist -- board-side approval decisions update state and trigger follow-up automations - -Critical finding fixed in this wave: - -- agent creation governance could be bypassed from the desktop UI and IPC write path - -Remaining risk: - -- other approval-governed domains, especially termination and production-deploy style flows, still deserve the same hard policy treatment across all entrypoints - -### 7. Cost / Budget - -Status: implemented - -Evidence: - -- budget fields, cost entries, cost dashboard, thresholds, and budget-related automation logic exist - -Remaining risk: - -- budgets are enforced operationally, but not all escalation policy is centralized; some cost behavior is still advisory rather than hard-stopping privileged actions - -### 8. Activity / Audit / Logs - -Status: implemented - -Evidence: - -- activity timeline exists -- many core mutations add activity entries -- run logs exist and are streamable into renderer console panels - -Remaining risk: - -- structured logging exists, but the audit trail is still assembled from multiple write sites instead of passing through a smaller domain-event backbone - -### 9. Secrets / Config / Environment - -Status: implemented, hardened in this wave - -Evidence: - -- Electron `safeStorage` is used where available -- encrypted fallback vault exists behind explicit env opt-in -- env binding resolution exists for connector runs - -Security hardening added in this wave: - -- `NODE_OPTIONS` was removed from the runtime environment allowlist so spawned agent runtimes do not inherit host-side Node injection flags by default - -Remaining risk: - -- preload API surface is still large, even if typed -- env policy should eventually be expressed as explicit runtime capability profiles per connector, not only a static allowlist - -### 10. Workspace / Repo / Artifact Management - -Status: implemented - -Evidence: - -- workspace registry, path selection, repo URL/ref fields, primary workspace logic, and lock metadata exist -- orchestration resolves workspace context into runtime env - -Remaining risk: - -- workspace locking and stale-lock cleanup exist, but deserve more explicit operator-facing conflict states in UI for complex multi-run contention - -### 11. Notifications / State Awareness - -Status: implemented - -Evidence: - -- desktop notifications exist -- tray/menu/window controls exist -- sidebar badges, inbox, and console surfacing exist - -Remaining risk: - -- notification strategy is present but not yet tuned as a full release-quality desktop attention model across macOS, Windows, and Linux - -## Electron / Security / Release Audit - -### Security Baseline - -Status: mostly aligned - -Confirmed: - -- secure BrowserWindow defaults are in place -- renderer does not receive raw Node primitives -- preload uses typed IPC contract calls -- utility process is used for background orchestration work -- vault prefers OS-backed secret storage - -New hardening applied: - -- runtime env no longer forwards host `NODE_OPTIONS` -- pending-hire governance cannot be bypassed from renderer IPC - -Still open: - -- preload surface is broader than ideal for a production app of this size -- some domain rules are enforced in handlers rather than at a smaller dedicated policy layer - -### Packaging / Release - -Status: partially complete - -Confirmed: - -- `electron-builder.yml` targets macOS, Windows, Linux -- build script cleans `/build`, stages a non-mutating build version, and outputs into `/build/release` -- e2e and build use the current Electron/Vite packaging path correctly - -Still open: - -- this audit validated runtime on the current local platform only -- cross-platform runtime differences and signing/notarization behavior still need explicit validation evidence per target platform -- updater flow currently checks availability but does not yet provide a fully operator-visible download/install lifecycle - -### Performance - -Status: acceptable for current prototype depth, not yet release-optimized - -Evidence: - -- app launches and is usable in live runtime checks -- worker isolation prevents heavy agent runs from living in renderer - -Remaining risk: - -- renderer build currently produces an approximately `1.7 MB` main JS chunk, which is too large for a polished long-term desktop shell -- mega-components still make fine-grained performance work harder than it should be - -## External Research Used - -Primary references used for this audit: - -- Electron security checklist: https://www.electronjs.org/docs/latest/tutorial/security -- Electron process model / utility process guidance: https://www.electronjs.org/docs/latest/tutorial/process-model -- Electron safe storage API: https://www.electronjs.org/docs/latest/api/safe-storage -- electron-builder auto-update guidance: https://www.electron.build/auto-update - -These references were used to validate: - -- secure BrowserWindow / preload expectations -- utility-process suitability for background work -- safe secret storage expectations -- updater/build configuration direction - -## Remediation Implemented In This Wave - -Files changed in this wave: - -- `src/shared/contracts.ts` -- `src/preload/index.ts` -- `src/main/runtime-env.ts` -- `src/main/database.ts` -- `src/main/ipc-core-work-graph-handlers.ts` -- `src/renderer/components/organization.tsx` -- `src/renderer/App.tsx` - -Concrete changes: - -- added typed `requestHire` contract and preload API -- added transactional `createHireRequest(...)` database path -- rerouted renderer new-agent flow into hiring / approvals pipeline -- blocked direct renderer IPC creation of active agents -- blocked direct renderer status promotion of pending hires -- tightened runtime env inheritance by removing `NODE_OPTIONS` - -## Remaining High-Value Follow-Ups - -1. Split `App.tsx`, `database.ts`, `api-server.ts`, `automation-service.ts`, `communication.tsx`, and `execution.tsx` into smaller domain slices. -2. Pull wake / publish / automation side effects into an explicit domain-event policy layer instead of leaving them distributed in orchestration-heavy modules. -3. Shrink the renderer entry chunk with section-level lazy loading for heavyweight desktop panels. -4. Make updater lifecycle operator-visible beyond availability checks. -5. Validate runtime behavior on Windows and Linux rather than relying only on configured targets. diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index f9117136..00000000 --- a/docs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Docs Index - -Active documents only. - -- [2026-03-17-audit-and-rebuild-plan.md](./2026-03-17-audit-and-rebuild-plan.md) -- [2026-03-17-architecture-freeze.md](./2026-03-17-architecture-freeze.md) -- [2026-03-17-runtime-security-tranche.md](./2026-03-17-runtime-security-tranche.md) - -Removed: - -- duplicate audit snapshots -- duplicate rebuild plans -- stale one-off implementation deltas that no longer matched the working tree diff --git a/package.json b/package.json index f15c163b..0cf78b0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-company", - "version": "0.4.47", + "version": "0.4.52", "private": true, "type": "module", "description": "Cross-platform Electron desktop control plane for local-first AI agent companies.", @@ -66,5 +66,12 @@ "node": ">=22.0.0", "pnpm": ">=10.0.0" }, - "packageManager": "pnpm@10.24.0" + "packageManager": "pnpm@10.24.0", + "pnpm": { + "onlyBuiltDependencies": [ + "electron", + "electron-winstaller", + "esbuild" + ] + } } diff --git a/packages/adapter-utils/src/server-utils.ts b/packages/adapter-utils/src/server-utils.ts index e2dce599..d4d4ff69 100644 --- a/packages/adapter-utils/src/server-utils.ts +++ b/packages/adapter-utils/src/server-utils.ts @@ -3,6 +3,19 @@ import { constants as fsConstants, promises as fs } from "node:fs"; import path from "node:path"; import { StringDecoder } from "node:string_decoder"; +/** + * Decode a Buffer as GBK (CP936). Used as a fallback when UTF-8 decoding + * produces replacement characters on Chinese Windows systems where the + * system ANSI code page is GBK and chcp 65001 is ineffective for piped I/O. + */ +function decodeGbkBuffer(buf: Buffer): string { + try { + return new TextDecoder("gbk").decode(buf); + } catch { + return buf.toString("utf8"); + } +} + export interface RunProcessResult { exitCode: number | null; signal: string | null; @@ -331,10 +344,14 @@ export async function runChildProcess( let stderr = ""; let logChain: Promise = Promise.resolve(); - // Use StringDecoder to correctly handle multi-byte UTF-8 characters - // that may be split across chunk boundaries (e.g. Chinese characters). + // Use StringDecoder to handle multi-byte characters split across chunks. + // On Windows, child process stdout may arrive in GBK (CP936) instead + // of UTF-8 when chcp 65001 is ineffective (piped stdout has no console). + // We detect this per-stream and fall back to GBK decoding if needed. const stdoutDecoder = new StringDecoder("utf8"); const stderrDecoder = new StringDecoder("utf8"); + let stdoutIsGbk = false; + let stderrIsGbk = false; const timeout = opts.timeoutSec > 0 @@ -350,7 +367,20 @@ export async function runChildProcess( : null; child.stdout?.on("data", (chunk: unknown) => { - const text = chunk instanceof Buffer ? stdoutDecoder.write(chunk) : String(chunk); + let text: string; + if (chunk instanceof Buffer) { + if (stdoutIsGbk) { + text = decodeGbkBuffer(chunk); + } else { + text = stdoutDecoder.write(chunk); + if (text.includes("\ufffd") && chunk.length > 0) { + stdoutIsGbk = true; + text = decodeGbkBuffer(chunk); + } + } + } else { + text = String(chunk); + } stdout = appendWithCap(stdout, text); logChain = logChain .then(() => opts.onLog("stdout", text)) @@ -358,7 +388,20 @@ export async function runChildProcess( }); child.stderr?.on("data", (chunk: unknown) => { - const text = chunk instanceof Buffer ? stderrDecoder.write(chunk) : String(chunk); + let text: string; + if (chunk instanceof Buffer) { + if (stderrIsGbk) { + text = decodeGbkBuffer(chunk); + } else { + text = stderrDecoder.write(chunk); + if (text.includes("\ufffd") && chunk.length > 0) { + stderrIsGbk = true; + text = decodeGbkBuffer(chunk); + } + } + } else { + text = String(chunk); + } stderr = appendWithCap(stderr, text); logChain = logChain .then(() => opts.onLog("stderr", text)) diff --git a/src/main/api-server.ts b/src/main/api-server.ts index 9198f559..01af6b4b 100644 --- a/src/main/api-server.ts +++ b/src/main/api-server.ts @@ -10,6 +10,29 @@ import type { AppDatabase } from "./database"; import { dispatchAgentMessage } from "./message-dispatch"; import type { DesktopEvent } from "@shared/types"; +/** + * Decode an HTTP request body to a UTF-8 string, handling the common Windows + * encoding issue where Chinese/CJK characters arrive in GBK (CP936) instead + * of UTF-8. On Chinese Windows the system ANSI code page is GBK; tools like + * curl encode command-line arguments using that code page when stdout is piped + * (chcp 65001 does not help because there is no real console). We try strict + * UTF-8 first and, only if the bytes are invalid UTF-8, fall back to GBK. + */ +function decodeBodyToUtf8(raw: Buffer): string { + if (raw.length === 0) return ""; + try { + return new TextDecoder("utf-8", { fatal: true }).decode(raw); + } catch { + // Bytes are not valid UTF-8 — most likely GBK from Chinese Windows. + try { + return new TextDecoder("gbk").decode(raw); + } catch { + // Last resort: lossy UTF-8 (replaces invalid bytes with U+FFFD). + return raw.toString("utf8"); + } + } +} + type RequestHandler = ( req: IncomingMessage, res: ServerResponse, @@ -234,7 +257,8 @@ export class AgentApiServer { }); req.on("end", () => { try { - const data = Buffer.concat(chunks).toString("utf8"); + const raw = Buffer.concat(chunks); + const data = decodeBodyToUtf8(raw); resolve(data ? JSON.parse(data) : null); } catch { reject(new Error("Invalid JSON")); } @@ -244,7 +268,7 @@ export class AgentApiServer { } private json(res: ServerResponse, status: number, data: unknown) { - res.writeHead(status, { "Content-Type": "application/json" }); + res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" }); res.end(JSON.stringify(data)); } diff --git a/src/main/database.ts b/src/main/database.ts index 4c0d9e72..cad06710 100644 --- a/src/main/database.ts +++ b/src/main/database.ts @@ -76,6 +76,32 @@ function safeRealpath(targetPath: string) { } } +/** + * Find the last complete UTF-8 character boundary in a buffer. + * Returns the number of bytes up to (and including) the last complete character. + * This prevents corrupting multi-byte CJK characters when slicing at chunk edges. + */ +function findUtf8Boundary(buffer: Buffer, bytesRead: number): number { + if (bytesRead === 0) return 0; + // Walk backwards past continuation bytes (10xxxxxx) + let pos = bytesRead - 1; + while (pos >= 0 && (buffer[pos] & 0xc0) === 0x80) { + pos--; + } + if (pos < 0) return bytesRead; + const lead = buffer[pos]; + let charLen: number; + if ((lead & 0x80) === 0) charLen = 1; + else if ((lead & 0xe0) === 0xc0) charLen = 2; + else if ((lead & 0xf0) === 0xe0) charLen = 3; + else if ((lead & 0xf8) === 0xf0) charLen = 4; + else return bytesRead; + // If the character is complete within the buffer, keep all bytes + if (pos + charLen <= bytesRead) return bytesRead; + // Otherwise truncate before the incomplete character + return pos; +} + function taskPriorityOrderSql(columnName: string) { return `case ${columnName} when 'critical' then 4 @@ -1156,14 +1182,14 @@ export class AppDatabase { .run(id, input.companyId, input.taskId, input.agentId, input.workspaceId, input.connectorId, logPath, ts, ts); this.db.prepare("update tasks set active_run_id = ?, updated_at = ? where id = ?").run(id, ts, input.taskId); }); - writeFileSync(logPath, ""); + writeFileSync(logPath, "", "utf8"); return { id, logPath }; } appendRunLog(runId: string, stream: "stdout" | "stderr", chunk: string) { const run = this.db.prepare("select log_path as logPath from runs where id = ?").get(runId) as { logPath: string | null } | undefined; if (!run?.logPath) return; - return appendFile(run.logPath, `[${new Date().toISOString()}][${stream}] ${chunk}`); + return appendFile(run.logPath, `[${new Date().toISOString()}][${stream}] ${chunk}`, "utf8"); } readRunLog(runId: string) { @@ -1207,9 +1233,12 @@ export class AppDatabase { try { const buffer = Buffer.allocUnsafe(bytesToRead); const bytesRead = readSync(fileDescriptor, buffer, 0, bytesToRead, safeOffset); - const nextOffset = safeOffset + bytesRead; + // Find the last complete UTF-8 character boundary to avoid corrupting + // multi-byte characters (e.g. Chinese/CJK 3-byte sequences) at the edge. + const safeBytesRead = findUtf8Boundary(buffer, bytesRead); + const nextOffset = safeOffset + safeBytesRead; return { - content: buffer.subarray(0, bytesRead).toString("utf8"), + content: buffer.subarray(0, safeBytesRead).toString("utf8"), offset: safeOffset, nextOffset, totalBytes, diff --git a/src/main/desktop-shell-service.ts b/src/main/desktop-shell-service.ts index ca99e547..3d99549c 100644 --- a/src/main/desktop-shell-service.ts +++ b/src/main/desktop-shell-service.ts @@ -1,4 +1,5 @@ import { existsSync } from "node:fs"; +import { extname } from "node:path"; import { pathToFileURL } from "node:url"; import { resolve, sep } from "node:path"; import { @@ -81,14 +82,33 @@ export function createDesktopShellService({ } function registerProtocol() { - protocol.handle("app", (request) => { + const textMimeTypes: Record = { + ".html": "text/html; charset=utf-8", + ".js": "text/javascript; charset=utf-8", + ".mjs": "text/javascript; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".svg": "image/svg+xml; charset=utf-8", + }; + protocol.handle("app", async (request) => { const url = new URL(request.url); const relativePath = decodeURIComponent(url.pathname === "/" ? "index.html" : url.pathname.replace(/^\/+/, "")); const targetPath = resolve(rendererDist, relativePath); if (!isWithinDirectory(rendererDist, targetPath) || !existsSync(targetPath)) { return new Response("Not found", { status: 404 }); } - return net.fetch(pathToFileURL(targetPath).toString()); + const response = await net.fetch(pathToFileURL(targetPath).toString()); + const mime = textMimeTypes[extname(targetPath).toLowerCase()]; + if (mime) { + const headers = new Headers(response.headers); + headers.set("Content-Type", mime); + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers, + }); + } + return response; }); } diff --git a/src/main/prompt-builder.ts b/src/main/prompt-builder.ts index 960d8aa6..78ee2da6 100644 --- a/src/main/prompt-builder.ts +++ b/src/main/prompt-builder.ts @@ -451,38 +451,47 @@ Do not print or echo AGENT_COMPANY_API_KEY into comments, summaries, or external ## Authentication & Request Format Every API call MUST include these headers: Authorization: Bearer $AGENT_COMPANY_API_KEY - Content-Type: application/json + Content-Type: application/json; charset=utf-8 X-Agent-Company-Run-Id: ${ctx.runId} -Example using curl: - curl -s -X GET "$AGENT_COMPANY_API_URL/api/agents/me" \\ - -H "Authorization: Bearer $AGENT_COMPANY_API_KEY" \\ - -H "Content-Type: application/json" \\ - -H "X-Agent-Company-Run-Id: ${ctx.runId}" +IMPORTANT: On Windows, prefer using Node.js fetch() for API calls instead of curl, +because curl on Windows may corrupt non-ASCII characters (Chinese, Japanese, etc.). +If you must use curl, write the JSON body to a temp file first and use curl -d @file. -Example using fetch (Node.js): +Example using fetch (Node.js) — PREFERRED: const res = await fetch(\`\${process.env.AGENT_COMPANY_API_URL}/api/agents/me\`, { headers: { "Authorization": \`Bearer \${process.env.AGENT_COMPANY_API_KEY}\`, - "Content-Type": "application/json", + "Content-Type": "application/json; charset=utf-8", "X-Agent-Company-Run-Id": "${ctx.runId}" } }); const data = await res.json(); -Example POST using curl: +Example POST using fetch (Node.js) — PREFERRED for non-ASCII content: + const res = await fetch(\`\${process.env.AGENT_COMPANY_API_URL}/api/companies/${ctx.agent.companyId}/issues\`, { + method: "POST", + headers: { + "Authorization": \`Bearer \${process.env.AGENT_COMPANY_API_KEY}\`, + "Content-Type": "application/json; charset=utf-8", + "X-Agent-Company-Run-Id": "${ctx.runId}" + }, + body: JSON.stringify({title:"Task title",description:"Details",assigneeAgentId:"agent-id",priority:"medium",status:"todo"}) + }); + +Example GET using curl: + curl -s -X GET "$AGENT_COMPANY_API_URL/api/agents/me" \\ + -H "Authorization: Bearer $AGENT_COMPANY_API_KEY" \\ + -H "Content-Type: application/json; charset=utf-8" \\ + -H "X-Agent-Company-Run-Id: ${ctx.runId}" + +Example POST using curl (write body to temp file to avoid encoding issues): + echo '{"title":"Task title","description":"Details","priority":"medium","status":"todo"}' > /tmp/body.json curl -s -X POST "$AGENT_COMPANY_API_URL/api/companies/${ctx.agent.companyId}/issues" \\ -H "Authorization: Bearer $AGENT_COMPANY_API_KEY" \\ - -H "Content-Type: application/json" \\ + -H "Content-Type: application/json; charset=utf-8" \\ -H "X-Agent-Company-Run-Id: ${ctx.runId}" \\ - -d '{"title":"Task title","description":"Details","assigneeAgentId":"agent-id","priority":"medium","status":"todo"}' - -Example PATCH using curl: - curl -s -X PATCH "$AGENT_COMPANY_API_URL/api/issues/ISSUE_ID" \\ - -H "Authorization: Bearer $AGENT_COMPANY_API_KEY" \\ - -H "Content-Type: application/json" \\ - -H "X-Agent-Company-Run-Id: ${ctx.runId}" \\ - -d '{"status":"in_review","comment":"Work completed. Summary of changes: ..."}' + -d @/tmp/body.json IMPORTANT: Always check the HTTP response status. 200/201 = success, 4xx = client error (read the error message), 5xx = server error (retry once then report). diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 2a5249f1..b90f6124 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -323,6 +323,10 @@ export function App() { } }, [snapshot, locale]); + useEffect(() => { + document.documentElement.lang = locale === "zh" ? "zh-CN" : "en"; + }, [locale]); + const handleLocaleChange = useCallback(async (newLocale: AppLocale) => { setLocale(newLocale); try { diff --git a/src/renderer/styles.css b/src/renderer/styles.css index 950351d8..4a10c1f7 100644 --- a/src/renderer/styles.css +++ b/src/renderer/styles.css @@ -1,5 +1,13 @@ @import "tailwindcss"; +@theme { + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", + "Noto Sans CJK SC", Helvetica, Arial, sans-serif; + --font-mono: "JetBrains Mono Variable", "Microsoft YaHei", ui-monospace, + SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + :root { color-scheme: light; --bg: #fbfbfb; @@ -82,7 +90,7 @@ body { margin: 0; background: var(--bg); color: var(--text); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", Helvetica, Arial, sans-serif; + font-family: var(--font-sans); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } @@ -120,7 +128,7 @@ select { } .mono { - font-family: "JetBrains Mono Variable", "Microsoft YaHei", monospace; + font-family: var(--font-mono); } .surface {